From d5d3fe8b87abafcf98f5ef8f8b6d0a43a51d6ba5 Mon Sep 17 00:00:00 2001 From: Artem Kotik Date: Mon, 1 Jul 2024 01:39:54 +0800 Subject: [PATCH] Version 2.5.0 (#73) * Closes #67: Add option default_desired_privilege_level (#68) * Closes #69: Fix logger in SecretsMixin (#71) * Closes #70: Add escapejs filter to diff template (#72) Co-authored-by: Clemens Knost --- docs/changelog.md | 17 ++++++++++++++ docs/secrets.md | 6 +++-- netbox_config_diff/__init__.py | 2 +- netbox_config_diff/compliance/base.py | 6 ++++- netbox_config_diff/compliance/secrets.py | 23 +++++++++++++++---- netbox_config_diff/configurator/base.py | 3 ++- netbox_config_diff/models/data_models.py | 6 +++-- .../configcompliance/data.html | 2 +- .../netbox_config_diff/inc/diff.html | 2 +- requirements/base.txt | 6 ++--- tests/test_compliance.py | 1 + 11 files changed, 58 insertions(+), 16 deletions(-) diff --git a/docs/changelog.md b/docs/changelog.md index 07af73b..f9ff8fd 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -1,5 +1,22 @@ # Changelog + +## 2.5.0 (2024-06-30) + +* [#67](https://github.com/miaow2/netbox-config-diff/issues/67) Add option `default_desired_privilege_level` to plugins variables (thanks to [@cknost](https://github.com/cknost)) +* [#69](https://github.com/miaow2/netbox-config-diff/issues/69) Fix logger in SecretsMixin +* [#70](https://github.com/miaow2/netbox-config-diff/issues/80) Add `escapejs` filter to diff templates + +## 2.4.0 (2024-05-12) + +* [#63](https://github.com/miaow2/netbox-config-diff/issues/63) Generate patch commands necessary to bring a device into its intended configuration with [hier_config](https://github.com/netdevops/hier_config) + +## 2.3.0 (2024-04-11) + +* [#49](https://github.com/miaow2/netbox-config-diff/issues/49) Handle junipers templates with set commands +* [#56](https://github.com/miaow2/netbox-config-diff/issues/56) Add support for NetBox 3.7 +* [#57](https://github.com/miaow2/netbox-config-diff/issues/57) Reverse columns in compliance diff + ## 2.2.0 (2024-02-06) * [#47](https://github.com/miaow2/netbox-config-diff/issues/47) Move plugin to separete menu item in navbar and add tab for devices with compliance result diff --git a/docs/secrets.md b/docs/secrets.md index 978f0f7..131be84 100644 --- a/docs/secrets.md +++ b/docs/secrets.md @@ -1,8 +1,7 @@ # Integration with NetBox secrets plugin You can store credentials for devices authentification in NetBox secrets [plugin](https://github.com/Onemind-Services-LLC/netbox-secrets). - -Read NetBox secrets docs for more info. + Read NetBox secrets docs for more info. In plugin variables define secrets roles for username (`USER_SECRET_ROLE`), password (`PASSWORD_SECRET_ROLE`) and password (`SECOND_AUTH_SECRET_ROLE`) for Privileged EXEC mode. @@ -21,4 +20,7 @@ PLUGINS_CONFIG = { Script will find secrets with these roles attached to the device and use them as credentials. +Also you can define secret role for desired privilege level in plugins variable `DEFAULT_DESIRED_PRIVILEGE_LEVEL_ROLE` + or can specify the desired privilege level itself in variable `DEFAULT_DESIRED_PRIVILEGE_LEVEL`. + If something goes wrong, then credentials from `PLUGINS_CONFIG` will be used. diff --git a/netbox_config_diff/__init__.py b/netbox_config_diff/__init__.py index ab3ab9f..cd81cd8 100644 --- a/netbox_config_diff/__init__.py +++ b/netbox_config_diff/__init__.py @@ -2,7 +2,7 @@ __author__ = "Artem Kotik" __email__ = "miaow2@yandex.ru" -__version__ = "2.4.0" +__version__ = "2.5.0" class ConfigDiffConfig(PluginConfig): diff --git a/netbox_config_diff/compliance/base.py b/netbox_config_diff/compliance/base.py index 48a945c..f4fee83 100644 --- a/netbox_config_diff/compliance/base.py +++ b/netbox_config_diff/compliance/base.py @@ -130,7 +130,10 @@ def get_devices_with_rendered_configs(self, devices: Iterable[Device]) -> Iterat self.check_netbox_secrets() self.substitutes = {} for device in devices: - username, password, auth_secondary = self.get_credentials(device) + if self.data["data_source"]: + username, password, auth_secondary, default_desired_privilege_level = None, None, None, None + else: + username, password, auth_secondary, default_desired_privilege_level = self.get_credentials(device) rendered_config = None error = None context_data = device.get_config_context() @@ -159,6 +162,7 @@ def get_devices_with_rendered_configs(self, devices: Iterable[Device]) -> Iterat username=username, password=password, auth_secondary=auth_secondary, + default_desired_privilege_level=default_desired_privilege_level, rendered_config=rendered_config, error=error, device=device, diff --git a/netbox_config_diff/compliance/secrets.py b/netbox_config_diff/compliance/secrets.py index 862adae..a7df185 100644 --- a/netbox_config_diff/compliance/secrets.py +++ b/netbox_config_diff/compliance/secrets.py @@ -34,7 +34,10 @@ def get_master_key(self) -> None: self.master_key = sk.get_master_key(self.session_key) except Exception as e: if getattr(self, "logger"): - self.logger.log_failure(f"Can't fetch master_key: {str(e)}") + if getattr(self.logger, "log_failure"): + self.logger.log_failure(f"Can't fetch master_key: {str(e)}") + else: + self.logger.error(f"Can't fetch master_key: {str(e)}") else: self.log_failure(f"Can't fetch master_key: {str(e)}") @@ -45,9 +48,9 @@ def get_secret(self, secret: "Secret") -> str | None: return None return secret.plaintext - def get_credentials(self, device: Device) -> tuple[str, str, str]: + def get_credentials(self, device: Device) -> tuple[str, str, str, str]: if not self.netbox_secrets_installed: - return self.username, self.password, self.auth_secondary + return self.username, self.password, self.auth_secondary, self.default_desired_privilege_level if secret := device.secrets.filter(role__name=self.user_role).first(): username = value if (value := self.get_secret(secret)) else self.username @@ -61,8 +64,14 @@ def get_credentials(self, device: Device) -> tuple[str, str, str]: auth_secondary = value if (value := self.get_secret(secret)) else self.auth_secondary else: auth_secondary = self.auth_secondary + if secret := device.secrets.filter(role__name=self.default_desired_privilege_level_role).first(): + default_desired_privilege_level = ( + value if (value := self.get_secret(secret)) else self.default_desired_privilege_level + ) + else: + default_desired_privilege_level = self.default_desired_privilege_level - return username, password, auth_secondary + return username, password, auth_secondary, default_desired_privilege_level def check_netbox_secrets(self) -> None: if "netbox_secrets" in get_installed_plugins(): @@ -70,8 +79,14 @@ def check_netbox_secrets(self) -> None: self.user_role = get_plugin_config("netbox_config_diff", "USER_SECRET_ROLE") self.password_role = get_plugin_config("netbox_config_diff", "PASSWORD_SECRET_ROLE") self.auth_secondary_role = get_plugin_config("netbox_config_diff", "SECOND_AUTH_SECRET_ROLE") + self.default_desired_privilege_level_role = get_plugin_config( + "netbox_config_diff", "DEFAULT_DESIRED_PRIVILEGE_LEVEL_ROLE" + ) self.netbox_secrets_installed = True self.username = get_plugin_config("netbox_config_diff", "USERNAME") self.password = get_plugin_config("netbox_config_diff", "PASSWORD") self.auth_secondary = get_plugin_config("netbox_config_diff", "AUTH_SECONDARY") + self.default_desired_privilege_level = get_plugin_config( + "netbox_config_diff", "DEFAULT_DESIRED_PRIVILEGE_LEVEL" + ) diff --git a/netbox_config_diff/configurator/base.py b/netbox_config_diff/configurator/base.py index 21c81df..537517e 100644 --- a/netbox_config_diff/configurator/base.py +++ b/netbox_config_diff/configurator/base.py @@ -37,7 +37,7 @@ def __init__(self, devices: Iterable[Device], request: NetBoxFakeRequest) -> Non def validate_devices(self) -> None: self.check_netbox_secrets() for device in self.devices: - username, password, auth_secondary = self.get_credentials(device) + username, password, auth_secondary, default_desired_privilege_level = self.get_credentials(device) if device.platform.platform_setting is None: self.logger.log_warning(f"Skipping {device}, add PlatformSetting for {device.platform} platform") elif device.platform.platform_setting.driver not in ACCEPTABLE_DRIVERS: @@ -67,6 +67,7 @@ def validate_devices(self) -> None: username=username, password=password, auth_secondary=auth_secondary, + default_desired_privilege_level=default_desired_privilege_level, rendered_config=rendered_config, error=error, ) diff --git a/netbox_config_diff/models/data_models.py b/netbox_config_diff/models/data_models.py index 99539a4..5a2bc22 100644 --- a/netbox_config_diff/models/data_models.py +++ b/netbox_config_diff/models/data_models.py @@ -15,8 +15,8 @@ class BaseDeviceDataClass: name: str mgmt_ip: str platform: str - username: str - password: str + username: str | None + password: str | None exclude_regex: str | None = None rendered_config: str | None = None actual_config: str | None = None @@ -28,6 +28,7 @@ class BaseDeviceDataClass: config_error: str | None = None auth_strict_key: bool = False auth_secondary: str | None = None + default_desired_privilege_level: str | None = None transport: str = "asyncssh" def __str__(self) -> str: @@ -41,6 +42,7 @@ def to_scrapli(self) -> dict: "platform": self.platform, "auth_strict_key": self.auth_strict_key, "auth_secondary": self.auth_secondary, + "default_desired_privilege_level": self.default_desired_privilege_level, "transport": self.transport, "transport_options": { "asyncssh": { diff --git a/netbox_config_diff/templates/netbox_config_diff/configcompliance/data.html b/netbox_config_diff/templates/netbox_config_diff/configcompliance/data.html index 5c20e42..e845774 100644 --- a/netbox_config_diff/templates/netbox_config_diff/configcompliance/data.html +++ b/netbox_config_diff/templates/netbox_config_diff/configcompliance/data.html @@ -70,7 +70,7 @@
Diff
stickyFileHeaders: false, drawFileList: false, }; - const jsonDiff = `{{ instance.diff|safe }}`; + const jsonDiff = `{{ instance.diff|escapejs|safe }}`; var targetElement = document.getElementById('diffElement'); var diff2htmlUi = new Diff2HtmlUI(targetElement, jsonDiff, configuration); diff2htmlUi.draw(); diff --git a/netbox_config_diff/templates/netbox_config_diff/inc/diff.html b/netbox_config_diff/templates/netbox_config_diff/inc/diff.html index bc1864a..7f5130e 100644 --- a/netbox_config_diff/templates/netbox_config_diff/inc/diff.html +++ b/netbox_config_diff/templates/netbox_config_diff/inc/diff.html @@ -17,7 +17,7 @@
{{ device_name }} - Diff
stickyFileHeaders: false, drawFileList: false, }; - const jsonDiff = `{{ data|safe }}`; + const jsonDiff = `{{ data|escapejs|safe }}`; var targetElement = document.getElementById('{{ device_name }}-diffElement'); var diff2htmlUi = new Diff2HtmlUI(targetElement, jsonDiff, configuration); diff2htmlUi.draw(); diff --git a/requirements/base.txt b/requirements/base.txt index 770e18b..1a27887 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -1,5 +1,5 @@ hier-config==2.2.3 netutils==1.5.0 -scrapli[asyncssh]==2023.07.30 -scrapli-cfg==2023.07.30 -scrapli-community==2023.07.30 +scrapli[asyncssh]==2024.01.30 +scrapli-cfg==2024.01.30 +scrapli-community==2024.01.30 diff --git a/tests/test_compliance.py b/tests/test_compliance.py index fdbc376..88efe26 100644 --- a/tests/test_compliance.py +++ b/tests/test_compliance.py @@ -91,6 +91,7 @@ def test_devicedataclass_to_scrapli(devicedataclass_data: "DeviceDataClassData") "platform": devicedataclass_data.platform, "auth_strict_key": devicedataclass_data.auth_strict_key, "auth_secondary": devicedataclass_data.auth_secondary, + "default_desired_privilege_level": devicedataclass_data.default_desired_privilege_level, "transport": devicedataclass_data.transport, "transport_options": { "asyncssh": {