From b75b68caa26240f5ec7af92312a92196bc07f3a8 Mon Sep 17 00:00:00 2001 From: Carl Montanari <8515611+carlmontanari@users.noreply.github.com> Date: Fri, 30 Jul 2021 13:44:03 -0700 Subject: [PATCH] Prepare Release (#13) prepare release --- .github/workflows/commit.yaml | 2 +- .github/workflows/weekly.yaml | 2 +- docs/api_docs/diff.md | 8 +++--- docs/api_docs/factory.md | 28 ++++++++++++++++--- docs/api_docs/platform/base/async_platform.md | 4 +++ docs/api_docs/platform/base/sync_platform.md | 4 +++ .../platform/core/juniper_junos/patterns.md | 5 +++- docs/changelog.md | 2 +- examples/basic_usage/config | 8 +++--- examples/basic_usage/config_replace.py | 7 +++-- mkdocs.yml | 1 + requirements-dev.txt | 14 +++++----- requirements-docs.txt | 4 +-- scrapli_cfg/diff.py | 4 +-- scrapli_cfg/factory.py | 20 +++++++++++-- scrapli_cfg/platform/base/async_platform.py | 2 ++ scrapli_cfg/platform/base/sync_platform.py | 2 ++ .../platform/core/juniper_junos/patterns.py | 5 +++- setup.py | 2 +- tests/unit/test_diff.py | 8 +++--- tests/unit/test_logging.py | 5 ---- 21 files changed, 94 insertions(+), 43 deletions(-) diff --git a/.github/workflows/commit.yaml b/.github/workflows/commit.yaml index 276d3bf..e90a53a 100644 --- a/.github/workflows/commit.yaml +++ b/.github/workflows/commit.yaml @@ -30,7 +30,7 @@ jobs: max-parallel: 12 matrix: os: [ubuntu-latest, macos-latest] - version: [3.6, 3.7, 3.8, 3.9, 3.10.0-beta.1] + version: [3.6, 3.7, 3.8, 3.9, 3.10.0-beta.4] steps: - uses: actions/checkout@v2 - name: set up python ${{ matrix.version }} diff --git a/.github/workflows/weekly.yaml b/.github/workflows/weekly.yaml index 244210f..adeddd4 100644 --- a/.github/workflows/weekly.yaml +++ b/.github/workflows/weekly.yaml @@ -34,7 +34,7 @@ jobs: max-parallel: 12 matrix: os: [ubuntu-latest, macos-latest] - version: [3.6, 3.7, 3.8, 3.9, 3.10.0-beta.1] + version: [3.6, 3.7, 3.8, 3.9, 3.10.0-beta.4] steps: - uses: actions/checkout@v2 - name: set up python ${{ matrix.version }} diff --git a/docs/api_docs/diff.md b/docs/api_docs/diff.md index 7fc74c1..d1b1fcb 100644 --- a/docs/api_docs/diff.md +++ b/docs/api_docs/diff.md @@ -159,7 +159,7 @@ class ScrapliCfgDiffResponse(ScrapliCfgResponse): side_by_side_diff_lines = [] for line in self._difflines: - if line[:2] == " ?": + if line[:2] == "? ": current = ( yellow + f"{line[2:][:diff_side_width].rstrip() : <{half_term_width}}" + end ) @@ -204,7 +204,7 @@ class ScrapliCfgDiffResponse(ScrapliCfgResponse): unified_diff = [ yellow + line[2:] + end - if line[:2] == " ?" + if line[:2] == "? " else red + line[2:] + end if line[:2] == "- " else green + line[2:] + end @@ -369,7 +369,7 @@ class ScrapliCfgDiffResponse(ScrapliCfgResponse): side_by_side_diff_lines = [] for line in self._difflines: - if line[:2] == " ?": + if line[:2] == "? ": current = ( yellow + f"{line[2:][:diff_side_width].rstrip() : <{half_term_width}}" + end ) @@ -414,7 +414,7 @@ class ScrapliCfgDiffResponse(ScrapliCfgResponse): unified_diff = [ yellow + line[2:] + end - if line[:2] == " ?" + if line[:2] == "? " else red + line[2:] + end if line[:2] == "- " else green + line[2:] + end diff --git a/docs/api_docs/factory.md b/docs/api_docs/factory.md index d37f833..f9a60d4 100644 --- a/docs/api_docs/factory.md +++ b/docs/api_docs/factory.md @@ -111,11 +111,19 @@ def ScrapliCfg( ScrapliCfg: sync scrapli cfg object Raises: - ScrapliCfgException: if platform is not a string + ScrapliCfgException: if provided connection object is async + ScrapliCfgException: if provided connection object is sync but is not a supported ("core") + platform type """ logger.debug("ScrapliCfg factory initialized") + if isinstance(conn, AsyncNetworkDriver): + raise ScrapliCfgException( + "provided scrapli connection is sync but using 'AsyncScrapliCfg' -- you must use an " + "async connection with 'AsyncScrapliCfg'!" + ) + platform_class = SYNC_CORE_PLATFORM_MAP.get(type(conn)) if not platform_class: raise ScrapliCfgException( @@ -173,11 +181,19 @@ def AsyncScrapliCfg( AsyncScrapliCfg: async scrapli cfg object Raises: - ScrapliCfgException: if platform is not a string + ScrapliCfgException: if provided connection object is sync + ScrapliCfgException: if provided connection object is async but is not a supported ("core") + platform type """ logger.debug("AsyncScrapliCfg factory initialized") + if isinstance(conn, NetworkDriver): + raise ScrapliCfgException( + "provided scrapli connection is sync but using 'AsyncScrapliCfg' -- you must use an " + "async connection with 'AsyncScrapliCfg'!" + ) + platform_class = ASYNC_CORE_PLATFORM_MAP.get(type(conn)) if not platform_class: raise ScrapliCfgException( @@ -237,7 +253,9 @@ Returns: AsyncScrapliCfg: async scrapli cfg object Raises: - ScrapliCfgException: if platform is not a string + ScrapliCfgException: if provided connection object is sync + ScrapliCfgException: if provided connection object is async but is not a supported ("core") + platform type ``` @@ -278,5 +296,7 @@ Returns: ScrapliCfg: sync scrapli cfg object Raises: - ScrapliCfgException: if platform is not a string + ScrapliCfgException: if provided connection object is async + ScrapliCfgException: if provided connection object is sync but is not a supported ("core") + platform type ``` \ No newline at end of file diff --git a/docs/api_docs/platform/base/async_platform.md b/docs/api_docs/platform/base/async_platform.md index b17106f..2412ba5 100644 --- a/docs/api_docs/platform/base/async_platform.md +++ b/docs/api_docs/platform/base/async_platform.md @@ -196,6 +196,8 @@ class AsyncScrapliCfgPlatform(ABC, ScrapliCfgBase): self.logger.debug("on_prepare provided, executing now") await self.on_prepare(self) + self._prepared = True + async def cleanup(self) -> None: """ Cleanup after scrapli-cfg operations @@ -575,6 +577,8 @@ class AsyncScrapliCfgPlatform(ABC, ScrapliCfgBase): self.logger.debug("on_prepare provided, executing now") await self.on_prepare(self) + self._prepared = True + async def cleanup(self) -> None: """ Cleanup after scrapli-cfg operations diff --git a/docs/api_docs/platform/base/sync_platform.md b/docs/api_docs/platform/base/sync_platform.md index b568ccd..5a13619 100644 --- a/docs/api_docs/platform/base/sync_platform.md +++ b/docs/api_docs/platform/base/sync_platform.md @@ -196,6 +196,8 @@ class ScrapliCfgPlatform(ABC, ScrapliCfgBase): self.logger.debug("on_prepare provided, executing now") self.on_prepare(self) + self._prepared = True + def cleanup(self) -> None: """ Cleanup after scrapli-cfg operations @@ -574,6 +576,8 @@ class ScrapliCfgPlatform(ABC, ScrapliCfgBase): self.logger.debug("on_prepare provided, executing now") self.on_prepare(self) + self._prepared = True + def cleanup(self) -> None: """ Cleanup after scrapli-cfg operations diff --git a/docs/api_docs/platform/core/juniper_junos/patterns.md b/docs/api_docs/platform/core/juniper_junos/patterns.md index 77debfd..be680b4 100644 --- a/docs/api_docs/platform/core/juniper_junos/patterns.md +++ b/docs/api_docs/platform/core/juniper_junos/patterns.md @@ -32,7 +32,10 @@ scrapli_cfg.platform.core.juniper_junos.patterns import re VERSION_PATTERN = re.compile( - pattern=r"\d+\.\w+\.\w+", + # should match at least versions looking like: + # 17.3R2.10 + # 18.1R3-S2.5 + pattern=r"\d+\.[\w-]+\.\w+", ) OUTPUT_HEADER_PATTERN = re.compile(pattern=r"^## last commit.*$\nversion.*$", flags=re.M | re.I) EDIT_PATTERN = re.compile(pattern=r"^\[edit\]$", flags=re.M) diff --git a/docs/changelog.md b/docs/changelog.md index 7c928cd..b76fd75 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,6 +1,6 @@ Changelog ========= -## (in development) 2021.07.30 +## 2021.07.30 - Initial release! diff --git a/examples/basic_usage/config b/examples/basic_usage/config index d051835..e7a5bcc 100644 --- a/examples/basic_usage/config +++ b/examples/basic_usage/config @@ -1,6 +1,7 @@ version 16.12 service timestamps debug datetime msec service timestamps log datetime msec +! Call-home is enabled by Smart-Licensing. service call-home platform qfp utilization monitor load 80 platform punt-keepalive disable-kernel-core @@ -12,8 +13,7 @@ boot-start-marker boot-end-marker ! ! -no logging monitor -enable secret 9 $9$h6Ayg86tb/EImk$2T6Ns.ke08cAlZ2TbMf3YRCYr7ngDGzgAxZB0YMe7lQ +enable secret 9 $9$xvWnx8Fe35f8xE$E9ijp7GM/V48P5y1Uz3IEPtotXgwkJKYJmN0q3q2E92 ! no aaa new-model call-home @@ -147,7 +147,7 @@ memory free low-watermark processor 72329 ! spanning-tree extend system-id ! -username vrnetlab privilege 15 password 0 VR-netlab9 +username boxen privilege 15 pass 0 b0x3N-b0x3N ! redundancy ! @@ -255,7 +255,7 @@ no ip http server no ip http secure-server ! ip ssh pubkey-chain - username vrnetlab + username boxen key-hash ssh-rsa 5CC74A68B18B026A1709FB09D1F44E2F ip scp server enable ! diff --git a/examples/basic_usage/config_replace.py b/examples/basic_usage/config_replace.py index 9336ea8..d78728a 100644 --- a/examples/basic_usage/config_replace.py +++ b/examples/basic_usage/config_replace.py @@ -3,9 +3,10 @@ from scrapli_cfg import ScrapliCfg DEVICE = { - "host": "172.18.0.11", - "auth_username": "vrnetlab", - "auth_password": "VR-netlab9", + "host": "localhost", + "port": 21022, + "auth_username": "boxen", + "auth_password": "b0x3N-b0x3N", "auth_strict_key": False, "platform": "cisco_iosxe", } diff --git a/mkdocs.yml b/mkdocs.yml index 50a23a8..f1272b5 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,6 +2,7 @@ site_name: Scrapli Cfg site_description: Network device configuration management with scrapli site_author: Carl Montanari +site_url: https://scrapli.github.io/ repo_name: scrapli/scrapli_cfg repo_url: https://github.com/scrapli/scrapli_cfg diff --git a/requirements-dev.txt b/requirements-dev.txt index 9b5362a..7fae4c6 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -1,15 +1,15 @@ -black==21.5b1 +black==21.7b0 darglint==1.8.0 -isort==5.8.0 -mypy==0.812 -nox==2020.12.31 +isort==5.9.3 +mypy==0.910 +nox==2021.6.12 pycodestyle==2.7.0 pydocstyle==6.1.1 -pyfakefs==4.4.0 +pyfakefs==4.5.0 pylama==7.7.1 -pylint==2.8.2 +pylint==2.9.6 pytest-asyncio==0.15.1 -pytest-cov==2.12.0 +pytest-cov==2.12.1 pytest==6.2.4 scrapli-replay==2021.7.30a5 -r requirements-asyncssh.txt diff --git a/requirements-docs.txt b/requirements-docs.txt index efc1994..183e6bb 100644 --- a/requirements-docs.txt +++ b/requirements-docs.txt @@ -1,5 +1,5 @@ mdx-gh-links==0.2 -mkdocs==1.1.2 -mkdocs-material==7.1.5 +mkdocs==1.2.2 +mkdocs-material==7.2.1 mkdocs-material-extensions==1.0.1 pdoc3==0.9.2 ; sys_platform != "win32" \ No newline at end of file diff --git a/scrapli_cfg/diff.py b/scrapli_cfg/diff.py index 19fb97b..00079a1 100644 --- a/scrapli_cfg/diff.py +++ b/scrapli_cfg/diff.py @@ -129,7 +129,7 @@ def side_by_side_diff(self) -> str: side_by_side_diff_lines = [] for line in self._difflines: - if line[:2] == " ?": + if line[:2] == "? ": current = ( yellow + f"{line[2:][:diff_side_width].rstrip() : <{half_term_width}}" + end ) @@ -174,7 +174,7 @@ def unified_diff(self) -> str: unified_diff = [ yellow + line[2:] + end - if line[:2] == " ?" + if line[:2] == "? " else red + line[2:] + end if line[:2] == "- " else green + line[2:] + end diff --git a/scrapli_cfg/factory.py b/scrapli_cfg/factory.py index 91a5e09..bc7a6ae 100644 --- a/scrapli_cfg/factory.py +++ b/scrapli_cfg/factory.py @@ -81,11 +81,19 @@ def ScrapliCfg( ScrapliCfg: sync scrapli cfg object Raises: - ScrapliCfgException: if platform is not a string + ScrapliCfgException: if provided connection object is async + ScrapliCfgException: if provided connection object is sync but is not a supported ("core") + platform type """ logger.debug("ScrapliCfg factory initialized") + if isinstance(conn, AsyncNetworkDriver): + raise ScrapliCfgException( + "provided scrapli connection is sync but using 'AsyncScrapliCfg' -- you must use an " + "async connection with 'AsyncScrapliCfg'!" + ) + platform_class = SYNC_CORE_PLATFORM_MAP.get(type(conn)) if not platform_class: raise ScrapliCfgException( @@ -143,11 +151,19 @@ def AsyncScrapliCfg( AsyncScrapliCfg: async scrapli cfg object Raises: - ScrapliCfgException: if platform is not a string + ScrapliCfgException: if provided connection object is sync + ScrapliCfgException: if provided connection object is async but is not a supported ("core") + platform type """ logger.debug("AsyncScrapliCfg factory initialized") + if isinstance(conn, NetworkDriver): + raise ScrapliCfgException( + "provided scrapli connection is sync but using 'AsyncScrapliCfg' -- you must use an " + "async connection with 'AsyncScrapliCfg'!" + ) + platform_class = ASYNC_CORE_PLATFORM_MAP.get(type(conn)) if not platform_class: raise ScrapliCfgException( diff --git a/scrapli_cfg/platform/base/async_platform.py b/scrapli_cfg/platform/base/async_platform.py index 84428ad..8ebb1a9 100644 --- a/scrapli_cfg/platform/base/async_platform.py +++ b/scrapli_cfg/platform/base/async_platform.py @@ -166,6 +166,8 @@ async def prepare(self) -> None: self.logger.debug("on_prepare provided, executing now") await self.on_prepare(self) + self._prepared = True + async def cleanup(self) -> None: """ Cleanup after scrapli-cfg operations diff --git a/scrapli_cfg/platform/base/sync_platform.py b/scrapli_cfg/platform/base/sync_platform.py index c6a581d..344398e 100644 --- a/scrapli_cfg/platform/base/sync_platform.py +++ b/scrapli_cfg/platform/base/sync_platform.py @@ -166,6 +166,8 @@ def prepare(self) -> None: self.logger.debug("on_prepare provided, executing now") self.on_prepare(self) + self._prepared = True + def cleanup(self) -> None: """ Cleanup after scrapli-cfg operations diff --git a/scrapli_cfg/platform/core/juniper_junos/patterns.py b/scrapli_cfg/platform/core/juniper_junos/patterns.py index 514c21c..ff5fa9a 100644 --- a/scrapli_cfg/platform/core/juniper_junos/patterns.py +++ b/scrapli_cfg/platform/core/juniper_junos/patterns.py @@ -2,7 +2,10 @@ import re VERSION_PATTERN = re.compile( - pattern=r"\d+\.\w+\.\w+", + # should match at least versions looking like: + # 17.3R2.10 + # 18.1R3-S2.5 + pattern=r"\d+\.[\w-]+\.\w+", ) OUTPUT_HEADER_PATTERN = re.compile(pattern=r"^## last commit.*$\nversion.*$", flags=re.M | re.I) EDIT_PATTERN = re.compile(pattern=r"^\[edit\]$", flags=re.M) diff --git a/setup.py b/setup.py index 3866c16..b37f1ea 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ import setuptools -__version__ = "2021.07.30a9" +__version__ = "2021.07.30" __author__ = "Carl Montanari" with open("README.md", "r", encoding="utf-8") as f: diff --git a/tests/unit/test_diff.py b/tests/unit/test_diff.py index 3f69600..5aefc08 100644 --- a/tests/unit/test_diff.py +++ b/tests/unit/test_diff.py @@ -19,10 +19,10 @@ description racecar end""" -COLORIZED_SIDE_BY_SIDE_DIFF = "! !\n\x1b[91minterface loopback123 \x1b[0m\n ^^^ ^^^\n \x1b[92minterface loopback456\x1b[0m\n ^^^ ^^^\n\x1b[91m description tacocat \x1b[0m\n ^ ^ ^ ^ ^ ^\n \x1b[92m description racecar\x1b[0m\n ^ ^ ^ ^ ^ ^\n! !" -SIDE_BY_SIDE_DIFF = "! !\n- interface loopback123 \n ^^^ ^^^\n + interface loopback456\n ^^^ ^^^\n- description tacocat \n ^ ^ ^ ^ ^ ^\n + description racecar\n ^ ^ ^ ^ ^ ^\n! !" -COLORIZED_UNIFIED_DIFF = "!\n\x1b[91minterface loopback123\n\x1b[0m ^^^\n\x1b[92minterface loopback456\n\x1b[0m ^^^\n\x1b[91m description tacocat\n\x1b[0m ^ ^ ^\n\x1b[92m description racecar\n\x1b[0m ^ ^ ^\n!\n" -UNIFIED_DIFF = "!\n- interface loopback123\n ^^^\n+ interface loopback456\n ^^^\n- description tacocat\n ^ ^ ^\n+ description racecar\n ^ ^ ^\n!\n" +COLORIZED_SIDE_BY_SIDE_DIFF = "! !\n\x1b[91minterface loopback123 \x1b[0m\n\x1b[93m ^^^ \x1b[0m\x1b[93m ^^^\x1b[0m\n \x1b[92minterface loopback456\x1b[0m\n\x1b[93m ^^^ \x1b[0m\x1b[93m ^^^\x1b[0m\n\x1b[91m description tacocat \x1b[0m\n\x1b[93m ^ ^ ^ \x1b[0m\x1b[93m ^ ^ ^\x1b[0m\n \x1b[92m description racecar\x1b[0m\n\x1b[93m ^ ^ ^ \x1b[0m\x1b[93m ^ ^ ^\x1b[0m\n! !" +SIDE_BY_SIDE_DIFF = "! !\n- interface loopback123 \n? ^^^ ? ^^^\n + interface loopback456\n? ^^^ ? ^^^\n- description tacocat \n? ^ ^ ^ ? ^ ^ ^\n + description racecar\n? ^ ^ ^ ? ^ ^ ^\n! !" +COLORIZED_UNIFIED_DIFF = "!\n\x1b[91minterface loopback123\n\x1b[0m\x1b[93m ^^^\n\x1b[0m\x1b[92minterface loopback456\n\x1b[0m\x1b[93m ^^^\n\x1b[0m\x1b[91m description tacocat\n\x1b[0m\x1b[93m ^ ^ ^\n\x1b[0m\x1b[92m description racecar\n\x1b[0m\x1b[93m ^ ^ ^\n\x1b[0m!\n" +UNIFIED_DIFF = "!\n- interface loopback123\n? ^^^\n+ interface loopback456\n? ^^^\n- description tacocat\n? ^ ^ ^\n+ description racecar\n? ^ ^ ^\n!\n" def test_record_diff_response(diff_obj): diff --git a/tests/unit/test_logging.py b/tests/unit/test_logging.py index 3b2d3d7..90689bc 100644 --- a/tests/unit/test_logging.py +++ b/tests/unit/test_logging.py @@ -1,13 +1,9 @@ import logging -import sys from pathlib import Path -import pytest - from scrapli_cfg.logging import ScrapliFileHandler, ScrapliFormatter, enable_basic_logging, logger -@pytest.mark.skipif(sys.version_info > (3, 9), reason="skipping pending pyfakefs 3.10 support") def test_enable_basic_logging(fs): assert Path("scrapli_cfg.log").is_file() is False enable_basic_logging(file=True, level="debug") @@ -25,7 +21,6 @@ def test_enable_basic_logging(fs): del logger.handlers[1] -@pytest.mark.skipif(sys.version_info > (3, 9), reason="skipping pending pyfakefs 3.10 support") def test_enable_basic_logging_no_buffer(fs): assert Path("mylog.log").is_file() is False