diff --git a/hyperglass/constants.py b/hyperglass/constants.py index ab41373..6d0718f 100644 --- a/hyperglass/constants.py +++ b/hyperglass/constants.py @@ -76,17 +76,6 @@ SCRAPE_HELPERS = { } DRIVER_MAP = { - # TODO: Troubleshoot Arista with Scrapli, broken after upgrading to 2021.1.30. - # "arista_eos": "scrapli", # noqa: E800 - "bird": "scrapli", - "cisco_ios": "scrapli", - "cisco_xe": "scrapli", - "cisco_xr": "scrapli", - "cisco_nxos": "scrapli", - # TODO: Troubleshoot Juniper with Scrapli, broken after upgrading to 2021.7.30. - # "juniper": "scrapli", # noqa: E800 - "tnsr": "scrapli", - "frr": "scrapli", "frr_legacy": "hyperglass_agent", "bird_legacy": "hyperglass_agent", } diff --git a/hyperglass/execution/drivers/__init__.py b/hyperglass/execution/drivers/__init__.py index 93a1f0e..653df2b 100644 --- a/hyperglass/execution/drivers/__init__.py +++ b/hyperglass/execution/drivers/__init__.py @@ -4,11 +4,9 @@ from .agent import AgentConnection from ._common import Connection from .ssh_netmiko import NetmikoConnection -from .ssh_scrapli import ScrapliConnection __all__ = ( "AgentConnection", "Connection", "NetmikoConnection", - "ScrapliConnection", ) diff --git a/hyperglass/execution/drivers/ssh_scrapli.py b/hyperglass/execution/drivers/ssh_scrapli.py deleted file mode 100644 index 9c9540e..0000000 --- a/hyperglass/execution/drivers/ssh_scrapli.py +++ /dev/null @@ -1,136 +0,0 @@ -"""Scrapli-Specific Classes & Utilities. - -https://github.com/carlmontanari/scrapli -""" - -# Standard Library -import math -from typing import Tuple - -# Third Party -from scrapli.driver import AsyncGenericDriver -from scrapli.exceptions import ( - ScrapliTimeout, - ScrapliException, - ScrapliAuthenticationFailed, -) -from scrapli.driver.core import ( - AsyncEOSDriver, - AsyncNXOSDriver, - AsyncIOSXEDriver, - AsyncIOSXRDriver, - AsyncJunosDriver, -) - -# Project -from hyperglass.log import log -from hyperglass.state import use_state -from hyperglass.exceptions.public import ( - AuthError, - ScrapeError, - DeviceTimeout, - ResponseEmpty, -) -from hyperglass.exceptions.private import UnsupportedDevice - -# Local -from .ssh import SSHConnection - -SCRAPLI_DRIVER_MAP = { - "arista_eos": AsyncEOSDriver, - "bird": AsyncGenericDriver, - "cisco_ios": AsyncIOSXEDriver, - "cisco_nxos": AsyncNXOSDriver, - "cisco_xr": AsyncIOSXRDriver, - "frr": AsyncGenericDriver, - "juniper": AsyncJunosDriver, - "tnsr": AsyncGenericDriver, -} - -driver_global_args = { - # Per-NOS driver keyword arguments - "tnsr": {"comms_prompt_pattern": r"\S+\s\S+[\#\>]"}, - "frr": {"comms_ansi": True}, - "bird": {"comms_ansi": True}, -} - - -def _map_driver(_type: str) -> AsyncGenericDriver: - driver = SCRAPLI_DRIVER_MAP.get(_type) - if driver is None: - raise UnsupportedDevice(_type) - return driver - - -class ScrapliConnection(SSHConnection): - """Handle a device connection via Scrapli.""" - - async def collect(self, host: str = None, port: int = None) -> Tuple[str, ...]: - """Connect directly to a device. - - Directly connects to the router via Netmiko library, returns the - command output. - """ - params = use_state("params") - driver = _map_driver(self.device.platform) - - if host is not None: - log.debug( - "Connecting to {} via proxy {} [{}]", - self.device.name, - self.device.proxy.name, - f"{host}:{port}", - ) - else: - log.debug("Connecting directly to {}", self.device.name) - - global_args = driver_global_args.get(self.device.platform, {}) - - driver_kwargs = { - "host": host or self.device._target, - "port": port or self.device.port, - "auth_username": self.device.credential.username, - "timeout_ops": math.floor(params.request_timeout * 1.25), - "transport": "asyncssh", - "auth_strict_key": False, - "ssh_known_hosts_file": False, - **global_args, - } - - if self.device.credential._method == "password": - # Use password auth if no key is defined. - driver_kwargs["auth_password"] = self.device.credential.password.get_secret_value() - else: - # Otherwise, use key auth. - driver_kwargs["auth_private_key"] = self.device.credential.key.as_posix() - if self.device.credential._method == "encrypted_key": - # If the key is encrypted, use the password field as the - # private key password. - driver_kwargs[ - "auth_private_key_passphrase" - ] = self.device.credential.password.get_secret_value() - - driver = driver(**driver_kwargs) - driver.logger = log.bind(logger_name=f"scrapli.{driver.host}:{driver.port}-driver") - try: - responses = () - async with driver as connection: - await connection.get_prompt() - for query in self.query: - raw = await connection.send_command(query) - responses += (raw.result,) - log.debug(f'Raw response for command "{query}":\n{raw.result}') - - except ScrapliTimeout as err: - raise DeviceTimeout(error=err, device=self.device) - - except ScrapliAuthenticationFailed as err: - raise AuthError(error=err, device=self.device) - - except ScrapliException as err: - raise ScrapeError(error=err, device=self.device) - - if not responses: - raise ResponseEmpty(query=self.query_data) - - return responses diff --git a/hyperglass/execution/main.py b/hyperglass/execution/main.py index fc192eb..ecc5297 100644 --- a/hyperglass/execution/main.py +++ b/hyperglass/execution/main.py @@ -22,16 +22,13 @@ if TYPE_CHECKING: from hyperglass.models.data import OutputDataModel # Local -from .drivers import AgentConnection, NetmikoConnection, ScrapliConnection +from .drivers import AgentConnection, NetmikoConnection def map_driver(driver_name: str) -> "Connection": """Get the correct driver class based on the driver name.""" - if driver_name == "scrapli": - return ScrapliConnection - - elif driver_name == "hyperglass_agent": + if driver_name == "hyperglass_agent": return AgentConnection return NetmikoConnection diff --git a/hyperglass/log.py b/hyperglass/log.py index 2cbcce6..30a2487 100644 --- a/hyperglass/log.py +++ b/hyperglass/log.py @@ -118,7 +118,6 @@ def setup_lib_logging(log_level: str) -> None: "uvicorn.asgi", "netmiko", "paramiko", - "scrapli", "httpx", ]: if name not in seen: diff --git a/hyperglass/models/fields.py b/hyperglass/models/fields.py index bb57589..5782958 100644 --- a/hyperglass/models/fields.py +++ b/hyperglass/models/fields.py @@ -9,7 +9,7 @@ from pydantic import StrictInt, StrictFloat IntFloat = t.TypeVar("IntFloat", StrictInt, StrictFloat) -SupportedDriver = t.Literal["scrapli", "netmiko", "hyperglass_agent"] +SupportedDriver = t.Literal["netmiko", "hyperglass_agent"] HttpAuthMode = t.Literal["basic", "api_key"] HttpProvider = t.Literal["msteams", "slack", "generic"] LogFormat = t.Literal["text", "json"] diff --git a/pyproject.toml b/pyproject.toml index e026851..ff781ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,7 +48,6 @@ pydantic = {extras = ["dotenv"], version = "^1.8.2"} python = ">=3.8.1,<4.0" redis = "^3.5.3" rich = "^10.11.0" -scrapli = {version = "2021.07.30", extras = ["asyncssh"]} typer = "^0.4.0" typing-extensions = "^3.7.4" uvicorn = {extras = ["standard"], version = "^0.13.4"}