diff --git a/hyperglass/exceptions/private.py b/hyperglass/exceptions/private.py index 255bc17..27d0285 100644 --- a/hyperglass/exceptions/private.py +++ b/hyperglass/exceptions/private.py @@ -17,9 +17,9 @@ class ExternalError(PrivateHyperglassError): class UnsupportedDevice(PrivateHyperglassError): - """Raised when an input NOS is not in the supported NOS list.""" + """Raised when an input platform is not in the supported platform list.""" - def __init__(self, device_type: str) -> None: + def __init__(self, platform: str) -> None: """Show the unsupported device type and a list of supported drivers.""" # Third Party from netmiko.ssh_dispatcher import CLASS_MAPPER # type: ignore @@ -29,7 +29,7 @@ class UnsupportedDevice(PrivateHyperglassError): drivers = ("", *[*DRIVER_MAP.keys(), *CLASS_MAPPER.keys()].sort()) driver_list = "\n - ".join(drivers) - super().__init__(message=f"'{device_type}' is not supported. Must be one of:{driver_list}") + super().__init__(message=f"'{platform}' is not supported. Must be one of:{driver_list}") class InputValidationError(PrivateHyperglassError): diff --git a/hyperglass/execution/drivers/_construct.py b/hyperglass/execution/drivers/_construct.py index 42fbebf..0327776 100644 --- a/hyperglass/execution/drivers/_construct.py +++ b/hyperglass/execution/drivers/_construct.py @@ -45,14 +45,14 @@ class Construct: # Set transport method based on NOS type self.transport = "scrape" - if self.device.type in TRANSPORT_REST: + if self.device.platform in TRANSPORT_REST: self.transport = "rest" # Remove slashes from target for required platforms - if self.device.type in TARGET_FORMAT_SPACE: + if self.device.platform in TARGET_FORMAT_SPACE: self.target = re.sub(r"\/", r" ", str(self.query.query_target)) - with Formatter(self.device.type, self.query.query_type) as formatter: + with Formatter(self.device.platform, self.query.query_type) as formatter: self.target = formatter(self.query.query_target) def json(self, afi): @@ -105,9 +105,9 @@ class Construct: class Formatter: """Modify query target based on the device's NOS requirements and the query type.""" - def __init__(self, device_type: str, query_type: str) -> None: + def __init__(self, platform: str, query_type: str) -> None: """Initialize target formatting.""" - self.device_type = device_type + self.platform = platform self.query_type = query_type def __enter__(self): @@ -121,10 +121,10 @@ class Formatter: pass def _get_formatter(self): - if self.device_type in ("juniper", "juniper_junos"): + if self.platform in ("juniper", "juniper_junos"): if self.query_type == "bgp_aspath": return self._juniper_bgp_aspath - if self.device_type in ("bird", "bird_ssh"): + if self.platform in ("bird", "bird_ssh"): if self.query_type == "bgp_aspath": return self._bird_bgp_aspath elif self.query_type == "bgp_community": diff --git a/hyperglass/execution/drivers/ssh_netmiko.py b/hyperglass/execution/drivers/ssh_netmiko.py index c79ca55..57ca5f7 100644 --- a/hyperglass/execution/drivers/ssh_netmiko.py +++ b/hyperglass/execution/drivers/ssh_netmiko.py @@ -57,14 +57,14 @@ class NetmikoConnection(SSHConnection): else: log.debug("Connecting directly to {}", self.device.name) - global_args = netmiko_device_globals.get(self.device.type, {}) + global_args = netmiko_device_globals.get(self.device.platform, {}) - send_args = netmiko_device_send_args.get(self.device.type, {}) + send_args = netmiko_device_send_args.get(self.device.platform, {}) driver_kwargs = { "host": host or self.device._target, "port": port or self.device.port, - "device_type": self.device.type, + "device_type": self.device.platform, "username": self.device.credential.username, "global_delay_factor": params.netmiko_delay_factor, "timeout": math.floor(params.request_timeout * 1.25), @@ -72,7 +72,7 @@ class NetmikoConnection(SSHConnection): **global_args, } - if "_telnet" in self.device.type: + if "_telnet" in self.device.platform: # Telnet devices with a low delay factor (default) tend to # throw login errors. driver_kwargs["global_delay_factor"] = 2 diff --git a/hyperglass/execution/drivers/ssh_scrapli.py b/hyperglass/execution/drivers/ssh_scrapli.py index ae5455b..9c9540e 100644 --- a/hyperglass/execution/drivers/ssh_scrapli.py +++ b/hyperglass/execution/drivers/ssh_scrapli.py @@ -72,7 +72,7 @@ class ScrapliConnection(SSHConnection): command output. """ params = use_state("params") - driver = _map_driver(self.device.type) + driver = _map_driver(self.device.platform) if host is not None: log.debug( @@ -84,7 +84,7 @@ class ScrapliConnection(SSHConnection): else: log.debug("Connecting directly to {}", self.device.name) - global_args = driver_global_args.get(self.device.type, {}) + global_args = driver_global_args.get(self.device.platform, {}) driver_kwargs = { "host": host or self.device._target, diff --git a/hyperglass/models/commands/__init__.py b/hyperglass/models/commands/__init__.py index ef9487c..4e1ef4d 100644 --- a/hyperglass/models/commands/__init__.py +++ b/hyperglass/models/commands/__init__.py @@ -55,8 +55,8 @@ class Commands(HyperglassModel, extra="allow", validate_all=False): def import_params(cls, **input_params): """Import loaded YAML, initialize per-command definitions.""" obj = Commands() - for device_type, cmds in input_params.items(): - cmd_set = _DEVICE_TYPE_MAP.get(device_type, CommandGroup) + for platform, cmds in input_params.items(): + cmd_set = _DEVICE_TYPE_MAP.get(platform, CommandGroup) cmds = cmd_set(**cmds) - setattr(obj, device_type, cmds) + setattr(obj, platform, cmds) return obj diff --git a/hyperglass/models/config/devices.py b/hyperglass/models/config/devices.py index 4f3b939..9913603 100644 --- a/hyperglass/models/config/devices.py +++ b/hyperglass/models/config/devices.py @@ -15,7 +15,7 @@ from hyperglass.util import ( get_driver, get_fmt_keys, resolve_hostname, - validate_device_type, + validate_platform, ) from hyperglass.settings import Settings from hyperglass.constants import SCRAPE_HELPERS, SUPPORTED_STRUCTURED_OUTPUT @@ -45,7 +45,7 @@ class Device(HyperglassModelWithId, extra="allow"): display_name: Optional[StrictStr] port: StrictInt = 22 ssl: Optional[Ssl] - type: StrictStr + platform: StrictStr commands: List[Directive] structured_output: Optional[StrictBool] driver: Optional[SupportedDriver] @@ -156,15 +156,15 @@ class Device(HyperglassModelWithId, extra="allow"): """Validate structured output is supported on the device & set a default.""" if value is True: - if values["type"] not in SUPPORTED_STRUCTURED_OUTPUT: + if values["platform"] not in SUPPORTED_STRUCTURED_OUTPUT: raise ConfigError( "The 'structured_output' field is set to 'true' on device '{d}' with " - + "NOS '{n}', which does not support structured output", + + "platform '{p}', which does not support structured output", d=values["name"], - n=values["type"], + p=values["platform"], ) return value - elif value is None and values["type"] in SUPPORTED_STRUCTURED_OUTPUT: + elif value is None and values["platform"] in SUPPORTED_STRUCTURED_OUTPUT: value = True else: value = False @@ -185,31 +185,32 @@ class Device(HyperglassModelWithId, extra="allow"): @root_validator(pre=True) def validate_device_commands(cls, values: Dict) -> Dict: - """Validate & rewrite device type, set default commands.""" + """Validate & rewrite device platform, set default commands.""" - _type = values.get("type") - if _type is None: - # Ensure device type is defined. - raise ValueError( - f"Device {values['name']} is missing a 'type' (Network Operating System) property." + platform = values.get("platform") + if platform is None: + # Ensure device platform is defined. + raise ConfigError( + "Device '{device}' is missing a 'platform' (Network Operating System) property", + device={values["name"]}, ) - if _type in SCRAPE_HELPERS.keys(): + if platform in SCRAPE_HELPERS.keys(): # Rewrite NOS to helper value if needed. - _type = SCRAPE_HELPERS[_type] + platform = SCRAPE_HELPERS[platform] - # Verify device type is supported by hyperglass. - supported, _ = validate_device_type(_type) + # Verify device platform is supported by hyperglass. + supported, _ = validate_platform(platform) if not supported: - raise UnsupportedDevice(_type) + raise UnsupportedDevice(platform) - values["type"] = _type + values["platform"] = platform commands = values.get("commands") if commands is None: # If no commands are defined, set commands to the NOS. - inferred = values["type"] + inferred = values["platform"] # If the _telnet prefix is added, remove it from the command # profile so the commands are the same regardless of @@ -224,7 +225,7 @@ class Device(HyperglassModelWithId, extra="allow"): @validator("driver") def validate_driver(cls, value: Optional[str], values: Dict) -> Dict: """Set the correct driver and override if supported.""" - return get_driver(values["type"], value) + return get_driver(values["platform"], value) class Devices(HyperglassModel, extra="allow"): diff --git a/hyperglass/models/config/proxy.py b/hyperglass/models/config/proxy.py index 764631a..4c7ddb0 100644 --- a/hyperglass/models/config/proxy.py +++ b/hyperglass/models/config/proxy.py @@ -24,7 +24,7 @@ class Proxy(HyperglassModel): address: Union[IPv4Address, IPv6Address, StrictStr] port: StrictInt = 22 credential: Credential - type: StrictStr = "linux_ssh" + platform: StrictStr = "linux_ssh" def __init__(self: "Proxy", **kwargs: Any) -> None: """Check for legacy fields.""" @@ -48,13 +48,13 @@ class Proxy(HyperglassModel): ) return value - @validator("type", pre=True, always=True) + @validator("platform", pre=True, always=True) def validate_type(cls: "Proxy", value: Any, values: Dict[str, Any]) -> str: """Validate device type.""" if value != "linux_ssh": raise UnsupportedDevice( - "Proxy '{p}' uses type '{t}', which is currently unsupported.", + "Proxy '{p}' uses platform '{t}', which is currently unsupported.", p=values["name"], t=value, ) diff --git a/hyperglass/models/util.py b/hyperglass/models/util.py index ec43e90..73832e5 100644 --- a/hyperglass/models/util.py +++ b/hyperglass/models/util.py @@ -7,8 +7,8 @@ from typing import Any, Dict, Tuple from hyperglass.log import log LEGACY_FIELDS: Dict[str, Tuple[Tuple[str, str], ...]] = { - "Device": (("nos", "type"),), - "Proxy": (("nos", "type"),), + "Device": (("nos", "platform"),), + "Proxy": (("nos", "platform"),), } diff --git a/hyperglass/plugins/_base.py b/hyperglass/plugins/_base.py index a3b782b..c46ad15 100644 --- a/hyperglass/plugins/_base.py +++ b/hyperglass/plugins/_base.py @@ -63,10 +63,10 @@ class DirectivePlugin(BaseModel): directives: Sequence[str] = () -class DeviceTypePlugin(BaseModel): - """Plugin associated with specific device types. +class PlatformPlugin(BaseModel): + """Plugin associated with specific device platform. Should always be subclassed with `HyperglassPlugin`. """ - device_types: Sequence[str] = () + platforms: Sequence[str] = () diff --git a/hyperglass/plugins/_builtin/bgp_route_juniper.py b/hyperglass/plugins/_builtin/bgp_route_juniper.py index 7850971..369424a 100644 --- a/hyperglass/plugins/_builtin/bgp_route_juniper.py +++ b/hyperglass/plugins/_builtin/bgp_route_juniper.py @@ -120,7 +120,7 @@ class BGPRoutePluginJuniper(OutputPlugin): """Coerce a Juniper route table in XML format to a standard BGP Table structure.""" __hyperglass_builtin__: bool = PrivateAttr(True) - device_types: Sequence[str] = ("juniper",) + platforms: Sequence[str] = ("juniper",) directives: Sequence[str] = ( "__hyperglass_juniper_bgp_route__", "__hyperglass_juniper_bgp_aspath__", @@ -132,7 +132,7 @@ class BGPRoutePluginJuniper(OutputPlugin): should_process = all( ( isinstance(output, (list, tuple)), - device.type in self.device_types, + device.platform in self.platforms, device.structured_output is True, device.has_directives(*self.directives), ) diff --git a/hyperglass/plugins/_output.py b/hyperglass/plugins/_output.py index 9efee14..42e0a8e 100644 --- a/hyperglass/plugins/_output.py +++ b/hyperglass/plugins/_output.py @@ -8,7 +8,7 @@ from hyperglass.log import log from hyperglass.types import Series # Local -from ._base import DirectivePlugin, DeviceTypePlugin, HyperglassPlugin +from ._base import DirectivePlugin, PlatformPlugin, HyperglassPlugin if TYPE_CHECKING: # Project @@ -18,7 +18,7 @@ if TYPE_CHECKING: OutputType = Union["OutputDataModel", Series[str]] -class OutputPlugin(HyperglassPlugin, DirectivePlugin, DeviceTypePlugin): +class OutputPlugin(HyperglassPlugin, DirectivePlugin, PlatformPlugin): """Plugin to interact with device command output.""" def process(self, output: OutputType, device: "Device") -> OutputType: diff --git a/hyperglass/util/__init__.py b/hyperglass/util/__init__.py index d49978f..5a4aa1a 100644 --- a/hyperglass/util/__init__.py +++ b/hyperglass/util/__init__.py @@ -220,7 +220,7 @@ def repr_from_attrs(obj: object, attrs: Series[str]) -> str: return f"{obj.__class__.__name__}({','.join(pairs)})" -def validate_device_type(_type: str) -> t.Tuple[bool, t.Union[None, str]]: +def validate_platform(_type: str) -> t.Tuple[bool, t.Union[None, str]]: """Validate device type is supported.""" result = (False, None)