From 2e39e7d50793429d1d4fad140e2b3742d86eafc2 Mon Sep 17 00:00:00 2001
From: Nautobot-Bot <79372327+nautobot-bot@users.noreply.github.com>
Date: Wed, 14 Aug 2024 16:49:37 -0400
Subject: [PATCH] Cookie updated by NetworkToCode Cookie Drift Manager Tool
(#788)
* Cookie updated by NetworkToCode Cookie Drift Manager Tool
Template:
```
{
"template": "https://github.com/nautobot/cookiecutter-nautobot-app.git",
"dir": "nautobot-app",
"ref": "refs/tags/nautobot-app-v2.3.0",
"path": null
}
```
Cookie:
```
{
"remote": "https://github.com/nautobot/nautobot-app-golden-config.git",
"path": "/tmp/tmpl8jjmwv0/nautobot-app-golden-config",
"repository_path": "/tmp/tmpl8jjmwv0/nautobot-app-golden-config",
"dir": "",
"branch_prefix": "drift-manager",
"context": {
"codeowner_github_usernames": "@itdependsnetworks @jeffkala @nkallergis",
"full_name": "Network to Code, LLC",
"email": "opensource@networktocode.com",
"github_org": "nautobot",
"app_name": "nautobot_golden_config",
"verbose_name": "Golden Config",
"app_slug": "nautobot-golden-config",
"project_slug": "nautobot-app-golden-config",
"repo_url": "https://github.com/nautobot/nautobot-app-golden-config",
"base_url": "golden-config",
"min_nautobot_version": "2.0.0",
"max_nautobot_version": "2.9999",
"camel_name": "NautobotGoldenConfig",
"project_short_description": "An app for configuration on nautobot",
"model_class_name": "None",
"open_source_license": "Apache-2.0",
"docs_base_url": "https://docs.nautobot.com",
"docs_app_url": "https://docs.nautobot.com/projects/golden-config/en/latest",
"_template": "https://github.com/nautobot/cookiecutter-nautobot-app.git",
"_output_dir": "/tmp/tmpl8jjmwv0",
"_repo_dir": "/github/home/.cookiecutters/cookiecutter-nautobot-app/nautobot-app",
"_checkout": "refs/tags/nautobot-app-v2.3.0"
},
"base_branch": "develop",
"remote_name": "origin",
"pull_request_strategy": "PullRequestStrategy.CREATE",
"post_actions": [
"PostAction.BLACK"
],
"baked_commit_ref": "78686cacfb51540281444ff112705b91304f797c",
"draft": true
}
```
CLI Arguments:
```
{
"cookie_dir": "",
"input": false,
"json_filename": "",
"output_dir": "",
"push": true,
"template": "",
"template_dir": "",
"template_ref": "refs/tags/nautobot-app-v2.3.0",
"pull_request": null,
"post_action": [],
"disable_post_actions": false,
"draft": null
}
```
* changelog, remove bandit, fix some ruff failures
* pylint
* revert ruff rule ignore removal
* fix tests
* fix a bunch of failing tests
* Update docs/requirements.txt
* Update pyproject.toml
* update lockfile
---------
Co-authored-by: bakebot
Co-authored-by: Gary Snider <75227981+gsnider2195@users.noreply.github.com>
---
.bandit.yml | 7 -
.cookiecutter.json | 4 +-
.dockerignore | 1 -
.github/workflows/ci.yml | 36 +--
.pydocstyle.ini | 11 -
README.md | 2 +-
changes/788.housekeeping | 1 +
development/app_config_schema.py | 1 +
development/docker-compose.base.yml | 1 -
development/docker-compose.dev.yml | 1 -
development/docker-compose.mysql.yml | 4 +-
development/docker-compose.postgres.yml | 2 -
development/docker-compose.redis.yml | 1 -
development/nautobot_config.py | 12 +-
docs/assets/extra.css | 2 +-
docs/dev/contributing.md | 2 +-
docs/dev/dev_environment.md | 10 +-
docs/requirements.txt | 2 +-
mkdocs.yml | 3 +-
nautobot_golden_config/__init__.py | 3 +-
nautobot_golden_config/api/serializers.py | 8 +-
nautobot_golden_config/api/views.py | 23 +-
nautobot_golden_config/choices.py | 1 +
nautobot_golden_config/datasources.py | 1 +
nautobot_golden_config/filters.py | 3 +-
nautobot_golden_config/forms.py | 1 -
nautobot_golden_config/jobs.py | 2 +
nautobot_golden_config/metrics.py | 1 +
.../migrations/0001_initial.py | 5 +-
.../migrations/0003_auto_20210510_2356.py | 2 +-
.../migrations/0004_auto_20210616_2234.py | 2 +-
.../migrations/0005_json_compliance_rule.py | 3 +-
.../0009_multiple_gc_settings_part_1.py | 2 +-
.../0011_multiple_gc_settings_part_3.py | 2 +-
.../0019_convert_dynamicgroup_part_1.py | 2 +-
.../0020_convert_dynamicgroup_part_2.py | 1 -
.../0024_convert_custom_compliance_rules.py | 1 +
.../migrations/0027_auto_20230915_1657.py | 2 +-
.../0028_auto_20230916_1712_part2.py | 2 +-
.../0030_alter_goldenconfig_device.py | 2 +-
nautobot_golden_config/models.py | 11 +-
.../nornir_plays/config_backup.py | 1 +
.../nornir_plays/config_compliance.py | 9 +-
.../nornir_plays/config_deployment.py | 21 +-
.../nornir_plays/config_intended.py | 8 +-
.../nornir_plays/processor.py | 1 +
nautobot_golden_config/signals.py | 3 +-
nautobot_golden_config/tables.py | 3 +-
nautobot_golden_config/template_content.py | 2 +
.../templatetags/json_helpers.py | 1 +
nautobot_golden_config/tests/conftest.py | 14 +-
.../forms/test_golden_config_settings.py | 9 +-
nautobot_golden_config/tests/test_api.py | 7 +-
nautobot_golden_config/tests/test_basic.py | 9 +-
.../tests/test_datasources.py | 3 +-
nautobot_golden_config/tests/test_filters.py | 92 +++---
nautobot_golden_config/tests/test_graphql.py | 12 +-
nautobot_golden_config/tests/test_helpers.py | 13 +-
nautobot_golden_config/tests/test_jobs.py | 9 +-
.../test_config_compliance.py | 7 +-
.../tests/test_utilities/test_config_plan.py | 5 +-
.../tests/test_utilities/test_git.py | 2 +-
.../tests/test_utilities/test_graphql.py | 3 +-
.../tests/test_utilities/test_helpers.py | 5 +-
nautobot_golden_config/tests/test_views.py | 84 +++---
nautobot_golden_config/urls.py | 5 +-
.../utilities/config_plan.py | 1 +
.../utilities/config_postprocessing.py | 2 +
nautobot_golden_config/utilities/constant.py | 1 +
.../utilities/db_management.py | 2 +-
nautobot_golden_config/utilities/git.py | 1 +
nautobot_golden_config/utilities/helper.py | 22 +-
nautobot_golden_config/utilities/logger.py | 2 +-
nautobot_golden_config/utilities/mat_plot.py | 6 +-
nautobot_golden_config/utilities/utils.py | 3 +-
nautobot_golden_config/views.py | 4 +-
poetry.lock | 275 ++++--------------
pyproject.toml | 48 +--
tasks.py | 117 ++++----
79 files changed, 374 insertions(+), 621 deletions(-)
delete mode 100644 .bandit.yml
delete mode 100644 .pydocstyle.ini
create mode 100644 changes/788.housekeeping
diff --git a/.bandit.yml b/.bandit.yml
deleted file mode 100644
index 78140801..00000000
--- a/.bandit.yml
+++ /dev/null
@@ -1,7 +0,0 @@
----
-# No need to check for security issues in the test scripts!
-exclude_dirs:
- - "./nautobot_golden_config/tests/"
- - "./.venv/"
-skips:
- - "B404"
diff --git a/.cookiecutter.json b/.cookiecutter.json
index f3383d34..f378a64e 100644
--- a/.cookiecutter.json
+++ b/.cookiecutter.json
@@ -21,7 +21,7 @@
"_drift_manager": {
"template": "https://github.com/nautobot/cookiecutter-nautobot-app.git",
"template_dir": "nautobot-app",
- "template_ref": "refs/tags/nautobot-app-v2.2.1",
+ "template_ref": "refs/tags/nautobot-app-v2.3.0",
"cookie_dir": "",
"branch_prefix": "drift-manager",
"pull_request_strategy": "create",
@@ -29,7 +29,7 @@
"black"
],
"draft": true,
- "baked_commit_ref": "78686cacfb51540281444ff112705b91304f797c"
+ "baked_commit_ref": "4eb0e2da920ce7f21715b4dd11549ac433eba529"
}
}
}
diff --git a/.dockerignore b/.dockerignore
index 2270f496..a0bf06f4 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -19,7 +19,6 @@ FAQ.md
.git/
.gitignore
.github
-tasks.py
LICENSE
**/*.log
**/.vscode/
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index a02a72ee..24b94334 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,7 +17,7 @@ env:
APP_NAME: "nautobot-app-golden-config"
jobs:
- black:
+ ruff-format:
runs-on: "ubuntu-22.04"
env:
INVOKE_NAUTOBOT_GOLDEN_CONFIG_LOCAL: "True"
@@ -26,20 +26,9 @@ jobs:
uses: "actions/checkout@v4"
- name: "Setup environment"
uses: "networktocode/gh-action-setup-poetry-environment@v6"
- - name: "Linting: black"
- run: "poetry run invoke black"
- bandit:
- runs-on: "ubuntu-22.04"
- env:
- INVOKE_NAUTOBOT_GOLDEN_CONFIG_LOCAL: "True"
- steps:
- - name: "Check out repository code"
- uses: "actions/checkout@v4"
- - name: "Setup environment"
- uses: "networktocode/gh-action-setup-poetry-environment@v6"
- - name: "Linting: bandit"
- run: "poetry run invoke bandit"
- ruff:
+ - name: "Linting: ruff format"
+ run: "poetry run invoke ruff --action format"
+ ruff-lint:
runs-on: "ubuntu-22.04"
env:
INVOKE_NAUTOBOT_GOLDEN_CONFIG_LOCAL: "True"
@@ -61,17 +50,6 @@ jobs:
uses: "networktocode/gh-action-setup-poetry-environment@v6"
- name: "Check Docs Build"
run: "poetry run invoke build-and-check-docs"
- flake8:
- runs-on: "ubuntu-22.04"
- env:
- INVOKE_NAUTOBOT_GOLDEN_CONFIG_LOCAL: "True"
- steps:
- - name: "Check out repository code"
- uses: "actions/checkout@v4"
- - name: "Setup environment"
- uses: "networktocode/gh-action-setup-poetry-environment@v6"
- - name: "Linting: flake8"
- run: "poetry run invoke flake8"
poetry:
runs-on: "ubuntu-22.04"
env:
@@ -96,12 +74,10 @@ jobs:
run: "poetry run invoke yamllint"
check-in-docker:
needs:
- - "bandit"
- - "ruff"
- - "flake8"
+ - "ruff-format"
+ - "ruff-lint"
- "poetry"
- "yamllint"
- - "black"
runs-on: "ubuntu-22.04"
strategy:
fail-fast: true
diff --git a/.pydocstyle.ini b/.pydocstyle.ini
deleted file mode 100644
index 951011dd..00000000
--- a/.pydocstyle.ini
+++ /dev/null
@@ -1,11 +0,0 @@
-[pydocstyle]
-convention = google
-inherit = false
-match = (?!__init__).*\.py
-match-dir = (?!tests|migrations)[^\.].*
-# D212 is enabled by default in google convention, and complains if we have a docstring like:
-# """
-# My docstring is on the line after the opening quotes instead of on the same line as them.
-# """
-# We've discussed and concluded that we consider this to be a valid style choice.
-add_ignore = D212, D417
\ No newline at end of file
diff --git a/README.md b/README.md
index f71cd292..e15eba61 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
- An App for Nautobot.
+ An App for Nautobot.
## Overview
diff --git a/changes/788.housekeeping b/changes/788.housekeeping
new file mode 100644
index 00000000..321e1e26
--- /dev/null
+++ b/changes/788.housekeeping
@@ -0,0 +1 @@
+Rebaked from the cookie `nautobot-app-v2.3.0`.
diff --git a/development/app_config_schema.py b/development/app_config_schema.py
index 47009954..a779b14e 100644
--- a/development/app_config_schema.py
+++ b/development/app_config_schema.py
@@ -1,4 +1,5 @@
"""App Config Schema Generator and Validator."""
+
import json
from importlib import import_module
from os import getenv
diff --git a/development/docker-compose.base.yml b/development/docker-compose.base.yml
index e13ccec3..0b6ac1a2 100644
--- a/development/docker-compose.base.yml
+++ b/development/docker-compose.base.yml
@@ -13,7 +13,6 @@ x-nautobot-base: &nautobot-base
- "creds.env"
tty: true
-version: "3.8"
services:
nautobot:
depends_on:
diff --git a/development/docker-compose.dev.yml b/development/docker-compose.dev.yml
index 0e4b0386..3a7ec4ff 100644
--- a/development/docker-compose.dev.yml
+++ b/development/docker-compose.dev.yml
@@ -3,7 +3,6 @@
# any override will need to include these volumes to use them.
# see: https://github.com/docker/compose/issues/3729
---
-version: "3.8"
services:
nautobot:
command: "nautobot-server runserver 0.0.0.0:8080"
diff --git a/development/docker-compose.mysql.yml b/development/docker-compose.mysql.yml
index e1494749..dbe31cba 100644
--- a/development/docker-compose.mysql.yml
+++ b/development/docker-compose.mysql.yml
@@ -1,6 +1,4 @@
---
-version: "3.8"
-
services:
nautobot:
environment:
@@ -18,6 +16,8 @@ services:
- "development_mysql.env"
db:
image: "mysql:8"
+ command:
+ - "--max_connections=1000"
env_file:
- "development.env"
- "creds.env"
diff --git a/development/docker-compose.postgres.yml b/development/docker-compose.postgres.yml
index 12d1de31..8d96fdba 100644
--- a/development/docker-compose.postgres.yml
+++ b/development/docker-compose.postgres.yml
@@ -1,6 +1,4 @@
---
-version: "3.8"
-
services:
nautobot:
environment:
diff --git a/development/docker-compose.redis.yml b/development/docker-compose.redis.yml
index 6da9fa01..b5e266a3 100644
--- a/development/docker-compose.redis.yml
+++ b/development/docker-compose.redis.yml
@@ -1,5 +1,4 @@
---
-version: "3.8"
services:
redis:
image: "redis:6-alpine"
diff --git a/development/nautobot_config.py b/development/nautobot_config.py
index 776bcd4e..49a529dc 100644
--- a/development/nautobot_config.py
+++ b/development/nautobot_config.py
@@ -1,4 +1,5 @@
"""Nautobot development configuration file."""
+
import os
import sys
@@ -9,7 +10,7 @@
# Debug
#
-DEBUG = is_truthy(os.getenv("NAUTOBOT_DEBUG", False))
+DEBUG = is_truthy(os.getenv("NAUTOBOT_DEBUG", "false"))
_TESTING = len(sys.argv) > 1 and sys.argv[1] == "test"
if DEBUG and not _TESTING:
@@ -47,9 +48,10 @@
"PASSWORD": os.getenv("NAUTOBOT_DB_PASSWORD", ""), # Database password
"HOST": os.getenv("NAUTOBOT_DB_HOST", "localhost"), # Database server
"PORT": os.getenv(
- "NAUTOBOT_DB_PORT", default_db_settings[nautobot_db_engine]["NAUTOBOT_DB_PORT"]
+ "NAUTOBOT_DB_PORT",
+ default_db_settings[nautobot_db_engine]["NAUTOBOT_DB_PORT"],
), # Database port, default to postgres
- "CONN_MAX_AGE": int(os.getenv("NAUTOBOT_DB_TIMEOUT", 300)), # Database timeout
+ "CONN_MAX_AGE": int(os.getenv("NAUTOBOT_DB_TIMEOUT", "300")), # Database timeout
"ENGINE": nautobot_db_engine,
}
}
@@ -158,8 +160,8 @@
"postprocessing_subscribed": os.environ.get("POSTPROCESSING_SUBSCRIBED", []),
"jinja_env": {
"undefined": "jinja2.StrictUndefined",
- "trim_blocks": is_truthy(os.getenv("NAUTOBOT_JINJA_ENV_TRIM_BLOCKS", True)),
- "lstrip_blocks": is_truthy(os.getenv("NAUTOBOT_JINJA_ENV_LSTRIP_BLOCKS", False)),
+ "trim_blocks": is_truthy(os.getenv("NAUTOBOT_JINJA_ENV_TRIM_BLOCKS", "true")),
+ "lstrip_blocks": is_truthy(os.getenv("NAUTOBOT_JINJA_ENV_LSTRIP_BLOCKS", "false")),
},
# "get_custom_compliance": "my.custom_compliance.func",
# "default_deploy_status": "Not Approved",
diff --git a/docs/assets/extra.css b/docs/assets/extra.css
index 1eff1192..3f3931a0 100644
--- a/docs/assets/extra.css
+++ b/docs/assets/extra.css
@@ -96,7 +96,7 @@ a.autorefs-external:hover::after {
}
-/* Customization for mkdocs-version-annotations */
+/* Customization for markdown-version-annotations */
:root {
/* Icon for "version-added" admonition: Material Design Icons "plus-box-outline" */
--md-admonition-icon--version-added: url('data:image/svg+xml;charset=utf-8,');
diff --git a/docs/dev/contributing.md b/docs/dev/contributing.md
index ccd3a5a6..5bf960b8 100644
--- a/docs/dev/contributing.md
+++ b/docs/dev/contributing.md
@@ -4,7 +4,7 @@ The project is packaged with a light [development environment](dev_environment.m
The project is following Network to Code software development guidelines and is leveraging the following:
-- Python linting and formatting: `black`, `pylint`, `bandit`, `flake8`, and `ruff`.
+- Python linting and formatting: `pylint` and `ruff`.
- YAML linting is done with `yamllint`.
- Django unit test to ensure the app is working properly.
diff --git a/docs/dev/dev_environment.md b/docs/dev/dev_environment.md
index 95837814..50408bcd 100644
--- a/docs/dev/dev_environment.md
+++ b/docs/dev/dev_environment.md
@@ -123,10 +123,7 @@ Each command can be executed with `invoke `. All commands support the a
#### Testing
```
- bandit Run bandit to validate basic static code security analysis.
- black Run black to check that Python files adhere to its style standards.
- flake8 Run flake8 to check that Python files adhere to its style standards.
- ruff Run ruff to validate docstring formatting adheres to NTC defined standards.
+ ruff Run ruff to perform code formatting and/or linting.
pylint Run pylint code analysis.
tests Run all tests for this app.
unittest Run Django unit tests for the app.
@@ -454,7 +451,7 @@ This is the same as running:
### Tests
-To run tests against your code, you can run all of the tests that TravisCI runs against any new PR with:
+To run tests against your code, you can run all of the tests that the CI runs against any new PR with:
```bash
➜ invoke tests
@@ -464,9 +461,6 @@ To run an individual test, you can run any or all of the following:
```bash
➜ invoke unittest
-➜ invoke bandit
-➜ invoke black
-➜ invoke flake8
➜ invoke ruff
➜ invoke pylint
```
diff --git a/docs/requirements.txt b/docs/requirements.txt
index 4a079cd3..3359c083 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,5 +1,5 @@
mkdocs==1.5.2
mkdocs-material==9.2.4
-mkdocs-version-annotations==1.0.0
+markdown-version-annotations==1.0.1
mkdocstrings-python==1.5.2
mkdocstrings==0.22.0
diff --git a/mkdocs.yml b/mkdocs.yml
index 078b57dd..8e2e188e 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -72,6 +72,8 @@ extra:
link: "https://twitter.com/networktocode"
name: "Network to Code Twitter"
markdown_extensions:
+ - "markdown_version_annotations":
+ admonition_tag: "???"
- "admonition"
- "toc":
permalink: true
@@ -89,7 +91,6 @@ markdown_extensions:
- "footnotes"
plugins:
- "search"
- - "mkdocs-version-annotations"
- "mkdocstrings":
default_handler: "python"
handlers:
diff --git a/nautobot_golden_config/__init__.py b/nautobot_golden_config/__init__.py
index 537cf109..43c6532d 100644
--- a/nautobot_golden_config/__init__.py
+++ b/nautobot_golden_config/__init__.py
@@ -1,4 +1,5 @@
"""App declaration for nautobot_golden_config."""
+
# Metadata is inherited from Nautobot. If not including Nautobot in the environment, this should be added
from importlib import metadata
@@ -71,8 +72,8 @@ def ready(self):
# pylint: disable=import-outside-toplevel
from .signals import (
config_compliance_platform_cleanup,
- post_migrate_create_statuses,
post_migrate_create_job_button,
+ post_migrate_create_statuses,
)
nautobot_database_ready.connect(post_migrate_create_statuses, sender=self)
diff --git a/nautobot_golden_config/api/serializers.py b/nautobot_golden_config/api/serializers.py
index 6e3a8f3e..b9787afb 100644
--- a/nautobot_golden_config/api/serializers.py
+++ b/nautobot_golden_config/api/serializers.py
@@ -1,13 +1,11 @@
"""REST API serializer capabilities for graphql app."""
# pylint: disable=too-many-ancestors
-from rest_framework import serializers
-
-from nautobot.extras.api.mixins import TaggedModelSerializerMixin
+from nautobot.core.api.serializers import NautobotModelSerializer
from nautobot.dcim.api.serializers import DeviceSerializer
from nautobot.dcim.models import Device
-from nautobot.core.api.serializers import NautobotModelSerializer
-
+from nautobot.extras.api.mixins import TaggedModelSerializerMixin
+from rest_framework import serializers
from nautobot_golden_config import models
from nautobot_golden_config.utilities.config_postprocessing import get_config_postprocessing
diff --git a/nautobot_golden_config/api/views.py b/nautobot_golden_config/api/views.py
index 101dce49..1a7910c9 100644
--- a/nautobot_golden_config/api/views.py
+++ b/nautobot_golden_config/api/views.py
@@ -1,29 +1,26 @@
"""View for Golden Config APIs."""
+
import json
from django.contrib.contenttypes.models import ContentType
-
-from rest_framework.mixins import DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
-from rest_framework.views import APIView
-from rest_framework.viewsets import GenericViewSet
-from rest_framework.response import Response
-from rest_framework.routers import APIRootView
-from rest_framework.permissions import AllowAny, IsAuthenticated, BasePermission
-from rest_framework import mixins, viewsets
-
from nautobot.core.api.views import (
BulkDestroyModelMixin,
BulkUpdateModelMixin,
ModelViewSetMixin,
NautobotAPIVersionMixin,
)
-from nautobot.extras.api.views import NautobotModelViewSet, NotesViewSetMixin
from nautobot.dcim.models import Device
+from nautobot.extras.api.views import NautobotModelViewSet, NotesViewSetMixin
+from rest_framework import mixins, viewsets
+from rest_framework.mixins import DestroyModelMixin, ListModelMixin, RetrieveModelMixin, UpdateModelMixin
+from rest_framework.permissions import AllowAny, BasePermission, IsAuthenticated
+from rest_framework.response import Response
+from rest_framework.routers import APIRootView
+from rest_framework.views import APIView
+from rest_framework.viewsets import GenericViewSet
-
+from nautobot_golden_config import filters, models
from nautobot_golden_config.api import serializers
-from nautobot_golden_config import models
-from nautobot_golden_config import filters
from nautobot_golden_config.utilities.graphql import graph_ql_query
from nautobot_golden_config.utilities.helper import get_device_to_settings_map
diff --git a/nautobot_golden_config/choices.py b/nautobot_golden_config/choices.py
index 7531b279..0d7f1137 100644
--- a/nautobot_golden_config/choices.py
+++ b/nautobot_golden_config/choices.py
@@ -1,4 +1,5 @@
"""Choicesets for golden config."""
+
from nautobot.core.choices import ChoiceSet
diff --git a/nautobot_golden_config/datasources.py b/nautobot_golden_config/datasources.py
index c8953736..07b2e837 100644
--- a/nautobot_golden_config/datasources.py
+++ b/nautobot_golden_config/datasources.py
@@ -1,4 +1,5 @@
"""Data source app extension to register additional git repo types."""
+
import os
import yaml
diff --git a/nautobot_golden_config/filters.py b/nautobot_golden_config/filters.py
index 45717400..6e785954 100644
--- a/nautobot_golden_config/filters.py
+++ b/nautobot_golden_config/filters.py
@@ -1,8 +1,7 @@
"""Filters for UI and API Views."""
import django_filters
-
-from nautobot.core.filters import MultiValueDateTimeFilter, TreeNodeMultipleChoiceFilter, SearchFilter
+from nautobot.core.filters import MultiValueDateTimeFilter, SearchFilter, TreeNodeMultipleChoiceFilter
from nautobot.dcim.models import Device, DeviceType, Location, Manufacturer, Platform, Rack, RackGroup
from nautobot.extras.filters import NaturalKeyOrPKMultipleChoiceFilter, NautobotFilterSet, StatusFilter
from nautobot.extras.models import JobResult, Role, Status
diff --git a/nautobot_golden_config/forms.py b/nautobot_golden_config/forms.py
index f8e8ff19..0bc639ab 100644
--- a/nautobot_golden_config/forms.py
+++ b/nautobot_golden_config/forms.py
@@ -4,7 +4,6 @@
import json
import django.forms as django_forms
-
from nautobot.apps import forms
from nautobot.dcim.models import Device, DeviceType, Location, Manufacturer, Platform, Rack, RackGroup
from nautobot.extras.forms import NautobotBulkEditForm, NautobotFilterForm, NautobotModelForm
diff --git a/nautobot_golden_config/jobs.py b/nautobot_golden_config/jobs.py
index 2a2335b7..34727cd4 100644
--- a/nautobot_golden_config/jobs.py
+++ b/nautobot_golden_config/jobs.py
@@ -5,6 +5,7 @@
# pylint: disable=arguments-differ
from datetime import datetime
+
from django.utils.timezone import make_aware
from nautobot.core.celery import register_jobs
from nautobot.dcim.models import Device, DeviceType, Location, Manufacturer, Platform, Rack, RackGroup
@@ -24,6 +25,7 @@
from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory
from nornir.core.plugins.inventory import InventoryPluginRegister
from nornir_nautobot.exceptions import NornirNautobotException
+
from nautobot_golden_config.choices import ConfigPlanTypeChoice
from nautobot_golden_config.exceptions import BackupFailure, ComplianceFailure, IntendedGenerationFailure
from nautobot_golden_config.models import ComplianceFeature, ConfigPlan, GoldenConfig
diff --git a/nautobot_golden_config/metrics.py b/nautobot_golden_config/metrics.py
index 80ced35f..f5b2815a 100644
--- a/nautobot_golden_config/metrics.py
+++ b/nautobot_golden_config/metrics.py
@@ -1,4 +1,5 @@
"""Nautobot Golden Config app application level metrics ."""
+
from django.conf import settings
from django.db.models import Count, F, Q
from nautobot.dcim.models import Device
diff --git a/nautobot_golden_config/migrations/0001_initial.py b/nautobot_golden_config/migrations/0001_initial.py
index 2d926bf8..96a09c5f 100644
--- a/nautobot_golden_config/migrations/0001_initial.py
+++ b/nautobot_golden_config/migrations/0001_initial.py
@@ -1,10 +1,11 @@
# Generated by Django 3.1.8 on 2021-05-09 18:33
+import uuid
+
import django.core.serializers.json
-from django.db import migrations, models
import django.db.models.deletion
import taggit.managers
-import uuid
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_golden_config/migrations/0003_auto_20210510_2356.py b/nautobot_golden_config/migrations/0003_auto_20210510_2356.py
index 1e9500c3..07336567 100644
--- a/nautobot_golden_config/migrations/0003_auto_20210510_2356.py
+++ b/nautobot_golden_config/migrations/0003_auto_20210510_2356.py
@@ -1,7 +1,7 @@
# Generated by Django 3.1.8 on 2021-05-10 23:56
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_golden_config/migrations/0004_auto_20210616_2234.py b/nautobot_golden_config/migrations/0004_auto_20210616_2234.py
index 2d01e8bc..fc8f04f4 100644
--- a/nautobot_golden_config/migrations/0004_auto_20210616_2234.py
+++ b/nautobot_golden_config/migrations/0004_auto_20210616_2234.py
@@ -1,7 +1,7 @@
# Generated by Django 3.1.8 on 2021-06-16 22:34
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_golden_config/migrations/0005_json_compliance_rule.py b/nautobot_golden_config/migrations/0005_json_compliance_rule.py
index 6ec713c7..8d388b58 100644
--- a/nautobot_golden_config/migrations/0005_json_compliance_rule.py
+++ b/nautobot_golden_config/migrations/0005_json_compliance_rule.py
@@ -1,6 +1,7 @@
-from django.db import migrations, models
import json
+from django.db import migrations, models
+
def jsonify(apps, schedma_editor):
"""Converts textfield to json in preparation for migration."""
diff --git a/nautobot_golden_config/migrations/0009_multiple_gc_settings_part_1.py b/nautobot_golden_config/migrations/0009_multiple_gc_settings_part_1.py
index f3a47d2d..b728fefc 100644
--- a/nautobot_golden_config/migrations/0009_multiple_gc_settings_part_1.py
+++ b/nautobot_golden_config/migrations/0009_multiple_gc_settings_part_1.py
@@ -1,5 +1,5 @@
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_golden_config/migrations/0011_multiple_gc_settings_part_3.py b/nautobot_golden_config/migrations/0011_multiple_gc_settings_part_3.py
index c30b2744..538859fd 100644
--- a/nautobot_golden_config/migrations/0011_multiple_gc_settings_part_3.py
+++ b/nautobot_golden_config/migrations/0011_multiple_gc_settings_part_3.py
@@ -1,5 +1,5 @@
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_golden_config/migrations/0019_convert_dynamicgroup_part_1.py b/nautobot_golden_config/migrations/0019_convert_dynamicgroup_part_1.py
index 985f4a30..290d9f17 100644
--- a/nautobot_golden_config/migrations/0019_convert_dynamicgroup_part_1.py
+++ b/nautobot_golden_config/migrations/0019_convert_dynamicgroup_part_1.py
@@ -1,7 +1,7 @@
# Generated by Django 3.2.14 on 2022-07-11 14:18
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_golden_config/migrations/0020_convert_dynamicgroup_part_2.py b/nautobot_golden_config/migrations/0020_convert_dynamicgroup_part_2.py
index 5de3e82b..14881220 100644
--- a/nautobot_golden_config/migrations/0020_convert_dynamicgroup_part_2.py
+++ b/nautobot_golden_config/migrations/0020_convert_dynamicgroup_part_2.py
@@ -1,7 +1,6 @@
# Generated by Django 3.2.14 on 2022-07-11 14:18
from django.db import migrations
-from django.utils.text import slugify
def create_dynamic_groups(apps, schedma_editor):
diff --git a/nautobot_golden_config/migrations/0024_convert_custom_compliance_rules.py b/nautobot_golden_config/migrations/0024_convert_custom_compliance_rules.py
index 9128b6bd..79d41ef2 100644
--- a/nautobot_golden_config/migrations/0024_convert_custom_compliance_rules.py
+++ b/nautobot_golden_config/migrations/0024_convert_custom_compliance_rules.py
@@ -1,4 +1,5 @@
from django.db import migrations
+
from nautobot_golden_config.choices import ComplianceRuleConfigTypeChoice
diff --git a/nautobot_golden_config/migrations/0027_auto_20230915_1657.py b/nautobot_golden_config/migrations/0027_auto_20230915_1657.py
index aa5308e4..0f22796d 100644
--- a/nautobot_golden_config/migrations/0027_auto_20230915_1657.py
+++ b/nautobot_golden_config/migrations/0027_auto_20230915_1657.py
@@ -1,7 +1,7 @@
# Generated by Django 3.2.20 on 2023-09-15 16:57
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_golden_config/migrations/0028_auto_20230916_1712_part2.py b/nautobot_golden_config/migrations/0028_auto_20230916_1712_part2.py
index 033ce076..f943803d 100644
--- a/nautobot_golden_config/migrations/0028_auto_20230916_1712_part2.py
+++ b/nautobot_golden_config/migrations/0028_auto_20230916_1712_part2.py
@@ -1,8 +1,8 @@
# Generated by Django 3.2.20 on 2023-09-16 17:12
-from django.db import migrations, models
import django.db.models.deletion
import nautobot.core.models.fields
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_golden_config/migrations/0030_alter_goldenconfig_device.py b/nautobot_golden_config/migrations/0030_alter_goldenconfig_device.py
index 4637bc91..823076c7 100644
--- a/nautobot_golden_config/migrations/0030_alter_goldenconfig_device.py
+++ b/nautobot_golden_config/migrations/0030_alter_goldenconfig_device.py
@@ -1,7 +1,7 @@
# Generated by Django 3.2.21 on 2023-09-25 12:36
-from django.db import migrations, models
import django.db.models.deletion
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_golden_config/models.py b/nautobot_golden_config/models.py
index 924f5158..3e31debd 100644
--- a/nautobot_golden_config/models.py
+++ b/nautobot_golden_config/models.py
@@ -15,8 +15,7 @@
from nautobot.extras.models.statuses import StatusField
from nautobot.extras.utils import extras_features
from netutils.config.compliance import feature_compliance
-from xmldiff import main, actions
-
+from xmldiff import actions, main
from nautobot_golden_config.choices import ComplianceRuleConfigTypeChoice, ConfigPlanTypeChoice, RemediationTypeChoice
from nautobot_golden_config.utilities.constant import ENABLE_SOTAGG, PLUGIN_CFG
@@ -348,9 +347,7 @@ class ConfigCompliance(PrimaryModel): # pylint: disable=too-many-ancestors
# Used for django-pivot, both compliance and compliance_int should be set.
compliance_int = models.IntegerField(blank=True)
- def to_objectchange(
- self, action, *, related_object=None, object_data_extra=None, object_data_exclude=None
- ): # pylint: disable=arguments-differ
+ def to_objectchange(self, action, *, related_object=None, object_data_extra=None, object_data_exclude=None): # pylint: disable=arguments-differ
"""Remove actual and intended configuration from changelog."""
fields_to_exclude = ["actual", "intended"]
if not object_data_exclude:
@@ -451,9 +448,7 @@ class GoldenConfig(PrimaryModel): # pylint: disable=too-many-ancestors
compliance_last_attempt_date = models.DateTimeField(null=True, blank=True)
compliance_last_success_date = models.DateTimeField(null=True, blank=True)
- def to_objectchange(
- self, action, *, related_object=None, object_data_extra=None, object_data_exclude=None
- ): # pylint: disable=arguments-differ
+ def to_objectchange(self, action, *, related_object=None, object_data_extra=None, object_data_exclude=None): # pylint: disable=arguments-differ
"""Remove actual and intended configuration from changelog."""
fields_to_exclude = ["backup_config", "intended_config", "compliance_config"]
if not object_data_exclude:
diff --git a/nautobot_golden_config/nornir_plays/config_backup.py b/nautobot_golden_config/nornir_plays/config_backup.py
index 952d1424..01390428 100644
--- a/nautobot_golden_config/nornir_plays/config_backup.py
+++ b/nautobot_golden_config/nornir_plays/config_backup.py
@@ -13,6 +13,7 @@
from nornir.core.task import Result, Task
from nornir_nautobot.exceptions import NornirNautobotException
from nornir_nautobot.plugins.tasks.dispatcher import dispatcher
+
from nautobot_golden_config.exceptions import BackupFailure
from nautobot_golden_config.models import ConfigRemove, ConfigReplace, GoldenConfig
from nautobot_golden_config.nornir_plays.processor import ProcessGoldenConfig
diff --git a/nautobot_golden_config/nornir_plays/config_compliance.py b/nautobot_golden_config/nornir_plays/config_compliance.py
index 4f44bf76..5a2531ef 100644
--- a/nautobot_golden_config/nornir_plays/config_compliance.py
+++ b/nautobot_golden_config/nornir_plays/config_compliance.py
@@ -6,9 +6,9 @@
import os
from collections import defaultdict
from datetime import datetime
-from django.utils.timezone import make_aware
-from lxml import etree # nosec
+from django.utils.timezone import make_aware
+from lxml import etree
from nautobot_plugin_nornir.constants import NORNIR_SETTINGS
from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory
from netutils.config.compliance import _open_file_config, parser_map, section_config
@@ -16,19 +16,20 @@
from nornir.core.plugins.inventory import InventoryPluginRegister
from nornir.core.task import Result, Task
from nornir_nautobot.exceptions import NornirNautobotException
+
from nautobot_golden_config.choices import ComplianceRuleConfigTypeChoice
from nautobot_golden_config.exceptions import ComplianceFailure
from nautobot_golden_config.models import ComplianceRule, ConfigCompliance, GoldenConfig
from nautobot_golden_config.nornir_plays.processor import ProcessGoldenConfig
from nautobot_golden_config.utilities.db_management import close_threaded_db_connections
-from nautobot_golden_config.utilities.logger import NornirLogger
from nautobot_golden_config.utilities.helper import (
get_json_config,
get_xml_config,
+ get_xml_subtree_with_full_path,
render_jinja_template,
verify_settings,
- get_xml_subtree_with_full_path,
)
+from nautobot_golden_config.utilities.logger import NornirLogger
InventoryPluginRegister.register("nautobot-inventory", NautobotORMInventory)
LOGGER = logging.getLogger(__name__)
diff --git a/nautobot_golden_config/nornir_plays/config_deployment.py b/nautobot_golden_config/nornir_plays/config_deployment.py
index 2f5480ca..d0eac642 100644
--- a/nautobot_golden_config/nornir_plays/config_deployment.py
+++ b/nautobot_golden_config/nornir_plays/config_deployment.py
@@ -1,29 +1,25 @@
"""Nornir job for deploying configurations."""
-from datetime import datetime
import logging
+from datetime import datetime
from django.utils.timezone import make_aware
-
from nautobot.dcim.models import Device
from nautobot.extras.models import Status
-
+from nautobot_plugin_nornir.constants import NORNIR_SETTINGS
+from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory
from nornir import InitNornir
from nornir.core.exceptions import NornirSubTaskError
from nornir.core.plugins.inventory import InventoryPluginRegister
from nornir.core.task import Result, Task
-
from nornir_nautobot.exceptions import NornirNautobotException
from nornir_nautobot.plugins.tasks.dispatcher import dispatcher
-from nautobot_plugin_nornir.constants import NORNIR_SETTINGS
-from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory
-
from nautobot_golden_config.nornir_plays.processor import ProcessGoldenConfig
+from nautobot_golden_config.utilities.constant import DEFAULT_DEPLOY_STATUS
+from nautobot_golden_config.utilities.db_management import close_threaded_db_connections
from nautobot_golden_config.utilities.helper import dispatch_params
from nautobot_golden_config.utilities.logger import NornirLogger
-from nautobot_golden_config.utilities.db_management import close_threaded_db_connections
-from nautobot_golden_config.utilities.constant import DEFAULT_DEPLOY_STATUS
InventoryPluginRegister.register("nautobot-inventory", NautobotORMInventory)
@@ -60,10 +56,9 @@ def run_deployment(task: Task, logger: logging.Logger, config_plan_qs, deploy_jo
if not task_changed and not task_failed:
plans_to_deploy.update(status=Status.objects.get(name="Completed"))
logger.info("Nothing was deployed to the device.", extra={"object": obj})
- else:
- if not task_failed:
- logger.info("Successfully deployed configuration to device.", extra={"object": obj})
- plans_to_deploy.update(status=Status.objects.get(name="Completed"))
+ elif not task_failed:
+ logger.info("Successfully deployed configuration to device.", extra={"object": obj})
+ plans_to_deploy.update(status=Status.objects.get(name="Completed"))
except NornirSubTaskError as error:
task_result = None
plans_to_deploy.update(status=Status.objects.get(name="Failed"))
diff --git a/nautobot_golden_config/nornir_plays/config_intended.py b/nautobot_golden_config/nornir_plays/config_intended.py
index 60c89d18..dc97f3a3 100644
--- a/nautobot_golden_config/nornir_plays/config_intended.py
+++ b/nautobot_golden_config/nornir_plays/config_intended.py
@@ -6,13 +6,14 @@
from datetime import datetime
from django.utils.timezone import make_aware
+from nautobot_plugin_nornir.constants import NORNIR_SETTINGS
+from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory
from nornir import InitNornir
from nornir.core.plugins.inventory import InventoryPluginRegister
from nornir.core.task import Result, Task
from nornir_nautobot.exceptions import NornirNautobotException
from nornir_nautobot.plugins.tasks.dispatcher import dispatcher
-from nautobot_plugin_nornir.constants import NORNIR_SETTINGS
-from nautobot_plugin_nornir.plugins.inventory.nautobot_orm import NautobotORMInventory
+
from nautobot_golden_config.exceptions import IntendedGenerationFailure
from nautobot_golden_config.models import GoldenConfig
from nautobot_golden_config.nornir_plays.processor import ProcessGoldenConfig
@@ -26,7 +27,6 @@
)
from nautobot_golden_config.utilities.logger import NornirLogger
-
InventoryPluginRegister.register("nautobot-inventory", NautobotORMInventory)
LOGGER = logging.getLogger(__name__)
@@ -64,7 +64,7 @@ def run_template( # pylint: disable=too-many-arguments,too-many-locals
jinja_template = render_jinja_template(obj, logger, settings.jinja_path_template)
job_class_instance.request.user = job_class_instance.user
status, device_data = graph_ql_query(job_class_instance.request, obj, settings.sot_agg_query.query)
- if status != 200:
+ if status != 200: # noqa: PLR2004
error_msg = f"`E3012:` The GraphQL query return a status of {str(status)} with error of {str(device_data)}"
logger.error(error_msg, extra={"object": obj})
raise NornirNautobotException(error_msg)
diff --git a/nautobot_golden_config/nornir_plays/processor.py b/nautobot_golden_config/nornir_plays/processor.py
index 4043dcaa..956e3560 100644
--- a/nautobot_golden_config/nornir_plays/processor.py
+++ b/nautobot_golden_config/nornir_plays/processor.py
@@ -1,4 +1,5 @@
"""Processor used by Golden Config to catch unknown errors."""
+
from nornir.core.inventory import Host
from nornir.core.task import MultiResult, Result, Task
from nornir_nautobot.exceptions import NornirNautobotException
diff --git a/nautobot_golden_config/signals.py b/nautobot_golden_config/signals.py
index 1706bddb..d5966c04 100755
--- a/nautobot_golden_config/signals.py
+++ b/nautobot_golden_config/signals.py
@@ -1,10 +1,11 @@
"""Signal helpers."""
+
from django.apps import apps as global_apps
from django.db.models.signals import post_save
from django.dispatch import receiver
+from nautobot.core.choices import ColorChoices
from nautobot.dcim.models import Platform
-from nautobot.core.choices import ColorChoices
from nautobot_golden_config import models
diff --git a/nautobot_golden_config/tables.py b/nautobot_golden_config/tables.py
index 9031c6d1..c4588815 100644
--- a/nautobot_golden_config/tables.py
+++ b/nautobot_golden_config/tables.py
@@ -1,11 +1,12 @@
"""Django Tables2 classes for golden_config app."""
+
import copy
from django.utils.html import format_html
from django_tables2 import Column, LinkColumn, TemplateColumn
from django_tables2.utils import A
-from nautobot.extras.tables import StatusTableMixin
from nautobot.apps.tables import BaseTable, BooleanColumn, TagColumn, ToggleColumn
+from nautobot.extras.tables import StatusTableMixin
from nautobot_golden_config import models
from nautobot_golden_config.utilities.constant import CONFIG_FEATURES, ENABLE_BACKUP, ENABLE_COMPLIANCE, ENABLE_INTENDED
diff --git a/nautobot_golden_config/template_content.py b/nautobot_golden_config/template_content.py
index ff97ce05..fb92b0fd 100644
--- a/nautobot_golden_config/template_content.py
+++ b/nautobot_golden_config/template_content.py
@@ -1,8 +1,10 @@
"""Added content to the device model view for config compliance."""
+
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Count, Q
from django.urls import reverse
from nautobot.extras.plugins import PluginTemplateExtension
+
from nautobot_golden_config.models import ConfigCompliance, GoldenConfig
from nautobot_golden_config.utilities.constant import CONFIG_FEATURES, ENABLE_COMPLIANCE
diff --git a/nautobot_golden_config/templatetags/json_helpers.py b/nautobot_golden_config/templatetags/json_helpers.py
index 5b3df9c3..9b2dd7d5 100644
--- a/nautobot_golden_config/templatetags/json_helpers.py
+++ b/nautobot_golden_config/templatetags/json_helpers.py
@@ -1,4 +1,5 @@
"""Helper for JSON rendering that extends what Nautobot Core provides."""
+
import json
from django import template
diff --git a/nautobot_golden_config/tests/conftest.py b/nautobot_golden_config/tests/conftest.py
index ec552b24..97eefadb 100644
--- a/nautobot_golden_config/tests/conftest.py
+++ b/nautobot_golden_config/tests/conftest.py
@@ -1,18 +1,18 @@
"""Params for testing."""
-from datetime import datetime
+
+from datetime import datetime, timezone
+
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.utils.text import slugify
-
from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Manufacturer, Platform, Rack, RackGroup
from nautobot.extras.choices import JobResultStatusChoices
from nautobot.extras.datasources.registry import get_datasource_contents
-from nautobot.extras.models import GitRepository, GraphQLQuery, JobResult, Role, Status, Tag, DynamicGroup
+from nautobot.extras.models import DynamicGroup, GitRepository, GraphQLQuery, JobResult, Role, Status, Tag
from nautobot.tenancy.models import Tenant, TenantGroup
-import pytz
-from nautobot_golden_config.models import GoldenConfigSetting
+
from nautobot_golden_config.choices import ComplianceRuleConfigTypeChoice
-from nautobot_golden_config.models import ComplianceFeature, ComplianceRule, ConfigCompliance
+from nautobot_golden_config.models import ComplianceFeature, ComplianceRule, ConfigCompliance, GoldenConfigSetting
User = get_user_model()
@@ -545,7 +545,7 @@ def create_job_result() -> None:
user=user,
)
result.status = JobResultStatusChoices.STATUS_SUCCESS
- result.completed = datetime.now(pytz.UTC)
+ result.completed = datetime.now(timezone.utc)
result.validated_save()
return result
diff --git a/nautobot_golden_config/tests/forms/test_golden_config_settings.py b/nautobot_golden_config/tests/forms/test_golden_config_settings.py
index a2da7695..6a583975 100644
--- a/nautobot_golden_config/tests/forms/test_golden_config_settings.py
+++ b/nautobot_golden_config/tests/forms/test_golden_config_settings.py
@@ -1,12 +1,13 @@
"""Tests for Golden Configuration Settings Form."""
+
from unittest import mock
from django.test import TestCase
+from nautobot.extras.models import DynamicGroup, GitRepository
-from nautobot.extras.models import GitRepository, DynamicGroup
from nautobot_golden_config.forms import GoldenConfigSettingForm
from nautobot_golden_config.models import GoldenConfigSetting
-from nautobot_golden_config.tests.conftest import create_git_repos, create_device_data
+from nautobot_golden_config.tests.conftest import create_device_data, create_git_repos
class GoldenConfigSettingFormTest(TestCase):
@@ -33,7 +34,7 @@ def test_no_query_no_scope_success(self):
"intended_repository": GitRepository.objects.get(name="test-intended-repo-1"),
"intended_path_template": "{{ obj.location.name }}/{{ obj.name }}.cfg",
"backup_test_connectivity": True,
- "dynamic_group": DynamicGroup.objects.first()
+ "dynamic_group": DynamicGroup.objects.first(),
}
)
self.assertTrue(form.is_valid())
@@ -53,7 +54,7 @@ def test_no_query_fail(self):
"intended_repository": GitRepository.objects.get(name="test-intended-repo-1"),
"intended_path_template": "{{ obj.location.name }}/{{ obj.name }}.cfg",
"backup_test_connectivity": True,
- "dynamic_group": DynamicGroup.objects.first()
+ "dynamic_group": DynamicGroup.objects.first(),
}
)
self.assertFalse(form.is_valid())
diff --git a/nautobot_golden_config/tests/test_api.py b/nautobot_golden_config/tests/test_api.py
index 08104fad..1e7e6b7d 100644
--- a/nautobot_golden_config/tests/test_api.py
+++ b/nautobot_golden_config/tests/test_api.py
@@ -1,15 +1,14 @@
"""Unit tests for nautobot_golden_config."""
+
from copy import deepcopy
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
-
-from rest_framework import status
-
+from nautobot.core.testing import APITestCase, APIViewTestCases
from nautobot.dcim.models import Device, Platform
from nautobot.extras.models import DynamicGroup, GitRepository, GraphQLQuery, Status
-from nautobot.core.testing import APITestCase, APIViewTestCases
+from rest_framework import status
from nautobot_golden_config.choices import RemediationTypeChoice
from nautobot_golden_config.models import ConfigPlan, GoldenConfigSetting, RemediationSetting
diff --git a/nautobot_golden_config/tests/test_basic.py b/nautobot_golden_config/tests/test_basic.py
index 260e90e8..d72f2d02 100644
--- a/nautobot_golden_config/tests/test_basic.py
+++ b/nautobot_golden_config/tests/test_basic.py
@@ -1,6 +1,8 @@
"""Basic tests that do not require Django."""
+
import os
import unittest
+
import toml
@@ -15,8 +17,9 @@ def test_version(self):
with open(f"{parent_path}/docs/requirements.txt", "r", encoding="utf-8") as file:
requirements = [line for line in file.read().splitlines() if (len(line) > 0 and not line.startswith("#"))]
for pkg in requirements:
- if len(pkg.split("==")) == 2:
- pkg, version = pkg.split("==")
+ package_name = pkg
+ if len(pkg.split("==")) == 2: # noqa: PLR2004
+ package_name, version = pkg.split("==")
else:
version = "*"
- self.assertEqual(poetry_details[pkg], version)
+ self.assertEqual(poetry_details[package_name], version)
diff --git a/nautobot_golden_config/tests/test_datasources.py b/nautobot_golden_config/tests/test_datasources.py
index 26e0bbc2..135ff505 100644
--- a/nautobot_golden_config/tests/test_datasources.py
+++ b/nautobot_golden_config/tests/test_datasources.py
@@ -1,11 +1,12 @@
"""Unit tests for nautobot_golden_config datasources."""
+
from unittest import skip
from unittest.mock import Mock
from django.test import TestCase
from nautobot.dcim.models import Platform
-from nautobot_golden_config.datasources import get_id_kwargs, MissingReference
+from nautobot_golden_config.datasources import MissingReference, get_id_kwargs
from nautobot_golden_config.models import ComplianceFeature
diff --git a/nautobot_golden_config/tests/test_filters.py b/nautobot_golden_config/tests/test_filters.py
index c9eb46d4..41f506d5 100644
--- a/nautobot_golden_config/tests/test_filters.py
+++ b/nautobot_golden_config/tests/test_filters.py
@@ -2,9 +2,10 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
+from nautobot.core.testing import FilterTestCases
from nautobot.dcim.models import Device, Platform
from nautobot.extras.models import Status, Tag
-from nautobot.core.testing import FilterTestCases
+
from nautobot_golden_config import filters, models
from .conftest import create_device_data, create_feature_rule_cli, create_feature_rule_json, create_job_result
@@ -390,72 +391,73 @@ class ConfigPlanFilterTestCase(FilterTestCases.FilterTestCase):
queryset = models.ConfigPlan.objects.all()
filterset = filters.ConfigPlanFilterSet
- def setUp(self):
+ @classmethod
+ def setUpTestData(cls):
"""Setup Object."""
create_device_data()
- self.device1 = Device.objects.get(name="Device 1")
- self.device2 = Device.objects.get(name="Device 2")
- self.rule1 = create_feature_rule_cli(self.device1, feature="Feature 1")
- self.feature1 = self.rule1.feature
- self.rule2 = create_feature_rule_cli(self.device2, feature="Feature 2")
- self.feature2 = self.rule2.feature
- self.rule3 = create_feature_rule_cli(self.device1, feature="Feature 3")
- self.feature3 = self.rule3.feature
- self.status1 = Status.objects.get(name="Not Approved")
- self.status2 = Status.objects.get(name="Approved")
- self.tag1, _ = Tag.objects.get_or_create(name="Tag 1")
- self.tag2, _ = Tag.objects.get_or_create(name="Tag 2")
- self.tag1.content_types.set([ContentType.objects.get_for_model(models.ConfigPlan)])
- self.tag2.content_types.set([ContentType.objects.get_for_model(models.ConfigPlan)])
- self.job_result1 = create_job_result()
- self.job_result2 = create_job_result()
- self.config_plan1 = models.ConfigPlan.objects.create(
- device=self.device1,
+ cls.device1 = Device.objects.get(name="Device 1")
+ cls.device2 = Device.objects.get(name="Device 2")
+ cls.rule1 = create_feature_rule_cli(cls.device1, feature="Feature 1")
+ cls.feature1 = cls.rule1.feature
+ cls.rule2 = create_feature_rule_cli(cls.device2, feature="Feature 2")
+ cls.feature2 = cls.rule2.feature
+ cls.rule3 = create_feature_rule_cli(cls.device1, feature="Feature 3")
+ cls.feature3 = cls.rule3.feature
+ cls.status1 = Status.objects.get(name="Not Approved")
+ cls.status2 = Status.objects.get(name="Approved")
+ cls.tag1, _ = Tag.objects.get_or_create(name="Tag 1")
+ cls.tag2, _ = Tag.objects.get_or_create(name="Tag 2")
+ cls.tag1.content_types.set([ContentType.objects.get_for_model(models.ConfigPlan)])
+ cls.tag2.content_types.set([ContentType.objects.get_for_model(models.ConfigPlan)])
+ cls.job_result1 = create_job_result()
+ cls.job_result2 = create_job_result()
+ cls.config_plan1 = models.ConfigPlan.objects.create(
+ device=cls.device1,
plan_type="intended",
created="2020-01-01",
config_set="intended test",
change_control_id="12345",
- status=self.status2,
- plan_result_id=self.job_result1.id,
+ status=cls.status2,
+ plan_result_id=cls.job_result1.id,
)
- self.config_plan1.tags.add(self.tag1)
- self.config_plan1.feature.add(self.feature1)
- self.config_plan1.validated_save()
- self.config_plan2 = models.ConfigPlan.objects.create(
- device=self.device1,
+ cls.config_plan1.tags.add(cls.tag1)
+ cls.config_plan1.feature.add(cls.feature1)
+ cls.config_plan1.validated_save()
+ cls.config_plan2 = models.ConfigPlan.objects.create(
+ device=cls.device1,
plan_type="missing",
created="2020-01-02",
config_set="missing test",
change_control_id="23456",
- status=self.status1,
- plan_result_id=self.job_result1.id,
+ status=cls.status1,
+ plan_result_id=cls.job_result1.id,
)
- self.config_plan2.tags.add(self.tag2)
- self.config_plan2.feature.add(self.feature2)
- self.config_plan2.validated_save()
- self.config_plan3 = models.ConfigPlan.objects.create(
- device=self.device2,
+ cls.config_plan2.tags.add(cls.tag2)
+ cls.config_plan2.feature.add(cls.feature2)
+ cls.config_plan2.validated_save()
+ cls.config_plan3 = models.ConfigPlan.objects.create(
+ device=cls.device2,
plan_type="remediation",
created="2020-01-03",
config_set="remediation test",
change_control_id="34567",
- status=self.status2,
- plan_result_id=self.job_result2.id,
+ status=cls.status2,
+ plan_result_id=cls.job_result2.id,
)
- self.config_plan3.tags.add(self.tag2)
- self.config_plan3.feature.set([self.feature1, self.feature3])
- self.config_plan3.validated_save()
- self.config_plan4 = models.ConfigPlan.objects.create(
- device=self.device2,
+ cls.config_plan3.tags.add(cls.tag2)
+ cls.config_plan3.feature.set([cls.feature1, cls.feature3])
+ cls.config_plan3.validated_save()
+ cls.config_plan4 = models.ConfigPlan.objects.create(
+ device=cls.device2,
plan_type="manual",
created="2020-01-04",
config_set="manual test",
change_control_id="45678",
- status=self.status1,
- plan_result_id=self.job_result1.id,
+ status=cls.status1,
+ plan_result_id=cls.job_result1.id,
)
- self.config_plan4.tags.add(self.tag1)
- self.config_plan4.validated_save()
+ cls.config_plan4.tags.add(cls.tag1)
+ cls.config_plan4.validated_save()
def test_full(self):
"""Test without filtering to ensure all have been added."""
diff --git a/nautobot_golden_config/tests/test_graphql.py b/nautobot_golden_config/tests/test_graphql.py
index 386f0756..8c9e6960 100644
--- a/nautobot_golden_config/tests/test_graphql.py
+++ b/nautobot_golden_config/tests/test_graphql.py
@@ -6,21 +6,19 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from django.test.client import RequestFactory
-
-from graphql import get_default_backend
from graphene_django.settings import graphene_settings
-
-from nautobot.dcim.models import Platform, LocationType, Location, Device, Manufacturer, DeviceType
-from nautobot.extras.models import GitRepository, GraphQLQuery, DynamicGroup, Role, Status
+from graphql import get_default_backend
+from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Manufacturer, Platform
+from nautobot.extras.models import DynamicGroup, GitRepository, GraphQLQuery, Role, Status
from nautobot_golden_config.models import (
ComplianceFeature,
ComplianceRule,
ConfigCompliance,
- GoldenConfig,
- GoldenConfigSetting,
ConfigRemove,
ConfigReplace,
+ GoldenConfig,
+ GoldenConfigSetting,
)
from .conftest import create_saved_queries
diff --git a/nautobot_golden_config/tests/test_helpers.py b/nautobot_golden_config/tests/test_helpers.py
index 917299f7..2bec5a92 100644
--- a/nautobot_golden_config/tests/test_helpers.py
+++ b/nautobot_golden_config/tests/test_helpers.py
@@ -1,27 +1,26 @@
"""Unit tests for nautobot_golden_config helpers."""
+
import os
from unittest import mock
-import jinja2
+import jinja2
from django.contrib.auth import get_user_model
-from django.test import TestCase
from django.contrib.contenttypes.models import ContentType
-
-from nautobot.extras.models import SecretsGroup, Secret, SecretsGroupAssociation
+from django.test import TestCase
from nautobot.extras.choices import SecretsGroupAccessTypeChoices, SecretsGroupSecretTypeChoices
+from nautobot.extras.models import Secret, SecretsGroup, SecretsGroupAssociation
from nautobot.users.models import ObjectPermission
+from nautobot_golden_config.models import GoldenConfig
from nautobot_golden_config.utilities.config_postprocessing import (
+ get_config_postprocessing,
get_secret_by_secret_group_name,
render_secrets,
- get_config_postprocessing,
)
-from nautobot_golden_config.models import GoldenConfig
from nautobot_golden_config.utilities.constant import PLUGIN_CFG
from .conftest import create_device
-
# Use the proper swappable User model
User = get_user_model()
diff --git a/nautobot_golden_config/tests/test_jobs.py b/nautobot_golden_config/tests/test_jobs.py
index fcf099a5..2debfd8b 100755
--- a/nautobot_golden_config/tests/test_jobs.py
+++ b/nautobot_golden_config/tests/test_jobs.py
@@ -1,15 +1,18 @@
"""Basic Job Test."""
-from unittest.mock import patch, MagicMock
+
+from unittest.mock import MagicMock, patch
+
from nautobot.apps.testing import TransactionTestCase, create_job_result_and_run_job
-from nautobot.extras.models import JobLogEntry
from nautobot.dcim.models import Device
+from nautobot.extras.models import JobLogEntry
+
+from nautobot_golden_config import jobs
from nautobot_golden_config.tests.conftest import (
create_device,
create_orphan_device,
dgs_gc_settings_and_job_repo_objects,
)
from nautobot_golden_config.utilities import constant
-from nautobot_golden_config import jobs
@patch("nautobot_golden_config.nornir_plays.config_backup.run_backup", MagicMock(return_value="foo"))
diff --git a/nautobot_golden_config/tests/test_nornir_plays/test_config_compliance.py b/nautobot_golden_config/tests/test_nornir_plays/test_config_compliance.py
index d6125395..36c05a15 100644
--- a/nautobot_golden_config/tests/test_nornir_plays/test_config_compliance.py
+++ b/nautobot_golden_config/tests/test_nornir_plays/test_config_compliance.py
@@ -1,10 +1,11 @@
"""Unit tests for nautobot_golden_config nornir compliance."""
-import unittest
import json
-from unittest.mock import patch, Mock, MagicMock
+import unittest
+from unittest.mock import MagicMock, Mock, patch
+
from nautobot_golden_config.choices import ComplianceRuleConfigTypeChoice
-from nautobot_golden_config.nornir_plays.config_compliance import get_rules, get_config_element
+from nautobot_golden_config.nornir_plays.config_compliance import get_config_element, get_rules
class ConfigComplianceTest(unittest.TestCase):
diff --git a/nautobot_golden_config/tests/test_utilities/test_config_plan.py b/nautobot_golden_config/tests/test_utilities/test_config_plan.py
index 5ed1bf40..b83fb5b2 100644
--- a/nautobot_golden_config/tests/test_utilities/test_config_plan.py
+++ b/nautobot_golden_config/tests/test_utilities/test_config_plan.py
@@ -1,13 +1,14 @@
"""Unit tests for the nautobot_golden_config utilities config_plan."""
+
import unittest
from unittest.mock import Mock, patch
+from nautobot_golden_config.tests.conftest import create_config_compliance, create_device, create_feature_rule_cli
from nautobot_golden_config.utilities.config_plan import (
+ config_plan_default_status,
generate_config_set_from_compliance_feature,
generate_config_set_from_manual,
- config_plan_default_status,
)
-from nautobot_golden_config.tests.conftest import create_device, create_feature_rule_cli, create_config_compliance
class ConfigPlanTest(unittest.TestCase):
diff --git a/nautobot_golden_config/tests/test_utilities/test_git.py b/nautobot_golden_config/tests/test_utilities/test_git.py
index cae1f760..4188fe30 100644
--- a/nautobot_golden_config/tests/test_utilities/test_git.py
+++ b/nautobot_golden_config/tests/test_utilities/test_git.py
@@ -27,7 +27,7 @@ def mock_get_secret_value( # pylint: disable=unused-argument,inconsistent-retur
mock_obj.filesystem_path = "/fake/path"
mock_obj.remote_url = "https://fake.git/org/repository.git"
- mock_obj._token = "fake token" # nosec pylint: disable=protected-access
+ mock_obj._token = "fake token" # pylint: disable=protected-access
mock_obj.username = None
mock_obj.secrets_group = Mock(get_secret_value=mock_get_secret_value)
self.mock_obj = mock_obj
diff --git a/nautobot_golden_config/tests/test_utilities/test_graphql.py b/nautobot_golden_config/tests/test_utilities/test_graphql.py
index 0728064b..3dc55372 100644
--- a/nautobot_golden_config/tests/test_utilities/test_graphql.py
+++ b/nautobot_golden_config/tests/test_utilities/test_graphql.py
@@ -1,10 +1,11 @@
"""Unit tests for nautobot_golden_config utilities graphql."""
-from unittest.mock import patch
from unittest import skip
+from unittest.mock import patch
from nautobot.core.testing import TestCase
from nautobot.dcim.models import Device
+
from nautobot_golden_config.utilities.graphql import graph_ql_query
diff --git a/nautobot_golden_config/tests/test_utilities/test_helpers.py b/nautobot_golden_config/tests/test_utilities/test_helpers.py
index b39776da..b0903240 100644
--- a/nautobot_golden_config/tests/test_utilities/test_helpers.py
+++ b/nautobot_golden_config/tests/test_utilities/test_helpers.py
@@ -4,12 +4,13 @@
from unittest.mock import MagicMock, patch
from django.contrib.contenttypes.models import ContentType
-from django.test import TestCase
from django.template import engines
+from django.test import TestCase
from jinja2 import exceptions as jinja_errors
-from nautobot.dcim.models import Device, Platform, Location, LocationType
+from nautobot.dcim.models import Device, Location, LocationType, Platform
from nautobot.extras.models import DynamicGroup, GitRepository, GraphQLQuery, Status, Tag
from nornir_nautobot.exceptions import NornirNautobotException
+
from nautobot_golden_config.models import GoldenConfigSetting
from nautobot_golden_config.tests.conftest import create_device, create_helper_repo, create_orphan_device
from nautobot_golden_config.utilities.helper import (
diff --git a/nautobot_golden_config/tests/test_views.py b/nautobot_golden_config/tests/test_views.py
index cf49e3f4..51eca381 100644
--- a/nautobot_golden_config/tests/test_views.py
+++ b/nautobot_golden_config/tests/test_views.py
@@ -1,19 +1,18 @@
"""Unit tests for nautobot_golden_config views."""
-from unittest import mock, skip
import datetime
+from unittest import mock, skip
-from lxml import html
-
+import nautobot
from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
-from django.test import override_settings, RequestFactory, TestCase
+from django.test import RequestFactory, override_settings
from django.urls import reverse
-
+from lxml import html
from nautobot.core.models.querysets import RestrictedQuerySet
+from nautobot.core.testing import TestCase, ViewTestCases
from nautobot.dcim.models import Device
from nautobot.extras.models import Relationship, RelationshipAssociation, Status
-from nautobot.core.testing import ViewTestCases
from nautobot_golden_config import models, views
@@ -22,10 +21,12 @@
User = get_user_model()
+@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
class ConfigComplianceOverviewHelperTestCase(TestCase):
"""Test ConfigComplianceOverviewHelper."""
- def setUp(self):
+ @classmethod
+ def setUpTestData(cls):
"""Set up base objects."""
create_device_data()
dev01 = Device.objects.get(name="Device 1")
@@ -52,9 +53,7 @@ def setUp(self):
)
# TODO: 2.0 turn this back on.
- # self.ccoh = views.ConfigComplianceOverviewOverviewHelper
- User.objects.create_superuser(username="views", password="incredible")
- self.client.login(username="views", password="incredible")
+ # cls.ccoh = views.ConfigComplianceOverviewOverviewHelper
def test_plot_visual_no_devices(self):
# TODO: 2.0 turn this back on.
@@ -103,56 +102,44 @@ def test_config_compliance_details_sotagg_no_error(
mock_graph_ql_query.assert_called()
+@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
class ConfigReplaceListViewTestCase(TestCase):
"""Test ConfigReplaceListView."""
- def setUp(self):
+ _csv_headers = "name,platform,description,regex,replace"
+ _entry_name = "test name"
+ _entry_description = "test description"
+ _entry_regex = "^startswiththeend$"
+ _entry_replace = ""
+
+ @classmethod
+ def setUpTestData(cls):
"""Set up base objects."""
create_device_data()
- User.objects.create_superuser(username="views", password="incredible")
- self.client.login(username="views", password="incredible")
- self._delete_test_entry()
+ cls._delete_test_entry()
models.ConfigReplace.objects.create(
- name=self._entry_name,
+ name=cls._entry_name,
platform=Device.objects.first().platform,
- description=self._entry_description,
- regex=self._entry_regex,
- replace=self._entry_replace,
+ description=cls._entry_description,
+ regex=cls._entry_regex,
+ replace=cls._entry_replace,
)
@property
def _url(self):
return reverse("plugins:nautobot_golden_config:configreplace_list")
- def _delete_test_entry(self):
+ @classmethod
+ def _delete_test_entry(cls):
try:
- entry = models.ConfigReplace.objects.get(name=self._entry_name)
+ entry = models.ConfigReplace.objects.get(name=cls._entry_name)
entry.delete()
except models.ConfigReplace.DoesNotExist:
pass
- @property
- def _csv_headers(self):
- return "name,platform,description,regex,replace"
-
- @property
- def _entry_name(self):
- return "test name"
-
- @property
- def _entry_description(self):
- return "test description"
-
- @property
- def _entry_regex(self):
- return "^startswiththeend$"
-
- @property
- def _entry_replace(self):
- return ""
-
def test_configreplace_import(self):
self._delete_test_entry()
+ self.add_permissions("nautobot_golden_config.add_configreplace")
platform = Device.objects.first().platform
import_entry = (
f"{self._entry_name},{platform.id},{self._entry_description},{self._entry_regex},{self._entry_replace}"
@@ -171,15 +158,16 @@ def test_configreplace_import(self):
class GoldenConfigListViewTestCase(TestCase):
"""Test GoldenConfigListView."""
- def setUp(self):
+ user_permissions = ["nautobot_golden_config.view_goldenconfig", "nautobot_golden_config.change_goldenconfig"]
+
+ @classmethod
+ def setUpTestData(cls):
"""Set up base objects."""
create_device_data()
- User.objects.create_superuser(username="views", password="incredible")
- self.client.login(username="views", password="incredible")
- self.gc_settings = models.GoldenConfigSetting.objects.first()
- self.gc_dynamic_group = self.gc_settings.dynamic_group
- self.gc_dynamic_group.filter = {"name": [dev.name for dev in Device.objects.all()]}
- self.gc_dynamic_group.validated_save()
+ cls.gc_settings = models.GoldenConfigSetting.objects.first()
+ cls.gc_dynamic_group = cls.gc_settings.dynamic_group
+ cls.gc_dynamic_group.filter = {"name": [dev.name for dev in Device.objects.all()]}
+ cls.gc_dynamic_group.validated_save()
models.GoldenConfig.objects.create(device=Device.objects.first())
def _get_golden_config_table_header(self):
@@ -190,6 +178,8 @@ def _get_golden_config_table_header(self):
@property
def _text_table_headers(self):
+ if nautobot.__version__ >= "2.3.0":
+ return ["Device", "Backup Status", "Intended Status", "Compliance Status", "Dynamic Groups", "Actions"]
return ["Device", "Backup Status", "Intended Status", "Compliance Status", "Actions"]
@property
diff --git a/nautobot_golden_config/urls.py b/nautobot_golden_config/urls.py
index ee2043d3..f6b55835 100644
--- a/nautobot_golden_config/urls.py
+++ b/nautobot_golden_config/urls.py
@@ -1,9 +1,10 @@
"""Django urlpatterns declaration for config compliance app."""
-from django.urls import path
+
from django.templatetags.static import static
+from django.urls import path
from django.views.generic import RedirectView
-
from nautobot.core.views.routers import NautobotUIViewSetRouter
+
from nautobot_golden_config import views
app_name = "nautobot_golden_config"
diff --git a/nautobot_golden_config/utilities/config_plan.py b/nautobot_golden_config/utilities/config_plan.py
index 228597e9..dc34657a 100644
--- a/nautobot_golden_config/utilities/config_plan.py
+++ b/nautobot_golden_config/utilities/config_plan.py
@@ -1,4 +1,5 @@
"""Functions to support config plan."""
+
from nautobot.core.utils.data import render_jinja2
from nautobot.dcim.models import Device
from nautobot.extras.models import Status
diff --git a/nautobot_golden_config/utilities/config_postprocessing.py b/nautobot_golden_config/utilities/config_postprocessing.py
index 4254f882..92421530 100644
--- a/nautobot_golden_config/utilities/config_postprocessing.py
+++ b/nautobot_golden_config/utilities/config_postprocessing.py
@@ -1,4 +1,5 @@
"""Functions related to prepare configuration with postprocessing."""
+
from functools import partial
from typing import Optional
@@ -12,6 +13,7 @@
from nautobot.extras.models.secrets import SecretsGroup
from nautobot.users.models import User
from netutils.utils import jinja2_convenience_function
+
from nautobot_golden_config import models
from nautobot_golden_config.exceptions import RenderConfigToPushError
from nautobot_golden_config.utilities.constant import ENABLE_POSTPROCESSING, PLUGIN_CFG
diff --git a/nautobot_golden_config/utilities/constant.py b/nautobot_golden_config/utilities/constant.py
index a13703c0..f1f5db28 100644
--- a/nautobot_golden_config/utilities/constant.py
+++ b/nautobot_golden_config/utilities/constant.py
@@ -1,4 +1,5 @@
"""Storage of data that will not change throughout the life cycle of application."""
+
from django.conf import settings
from django.utils.module_loading import import_string
diff --git a/nautobot_golden_config/utilities/db_management.py b/nautobot_golden_config/utilities/db_management.py
index b1fb9e32..09433be3 100644
--- a/nautobot_golden_config/utilities/db_management.py
+++ b/nautobot_golden_config/utilities/db_management.py
@@ -1,8 +1,8 @@
"""Functions to manage DB related tasks."""
+
from django.db import connections
from nautobot_plugin_nornir.constants import NORNIR_SETTINGS
-
RUNNER_SETTINGS = NORNIR_SETTINGS.get("runner", {})
diff --git a/nautobot_golden_config/utilities/git.py b/nautobot_golden_config/utilities/git.py
index f1cabacc..fda680e9 100644
--- a/nautobot_golden_config/utilities/git.py
+++ b/nautobot_golden_config/utilities/git.py
@@ -1,6 +1,7 @@
"""Git helper methods and class."""
import logging
+
from nautobot.core.utils.git import GitRepo as _GitRepo
LOGGER = logging.getLogger(__name__)
diff --git a/nautobot_golden_config/utilities/helper.py b/nautobot_golden_config/utilities/helper.py
index 77c0f494..f894ec9a 100644
--- a/nautobot_golden_config/utilities/helper.py
+++ b/nautobot_golden_config/utilities/helper.py
@@ -1,29 +1,28 @@
"""Helper functions."""
+
# pylint: disable=raise-missing-from
import json
from copy import deepcopy
-from lxml import etree # nosec
from django.conf import settings
from django.contrib import messages
from django.db.models import Q
from django.template import engines
-from django.utils.html import format_html
from django.urls import reverse
-
+from django.utils.html import format_html
from jinja2 import exceptions as jinja_errors
from jinja2.sandbox import SandboxedEnvironment
+from lxml import etree
+from nautobot.core.utils.data import render_jinja2
from nautobot.dcim.filters import DeviceFilterSet
from nautobot.dcim.models import Device
-from nautobot.core.utils.data import render_jinja2
from nautobot.extras.models import Job
from nornir_nautobot.exceptions import NornirNautobotException
+from nautobot_golden_config import config as app_config
from nautobot_golden_config import models
from nautobot_golden_config.utilities import utils
from nautobot_golden_config.utilities.constant import JINJA_ENV
-from nautobot_golden_config import config as app_config
-
FRAMEWORK_METHODS = {
"default": utils.default_framework,
@@ -173,6 +172,7 @@ def render_jinja_template(obj, logger, template):
def get_device_to_settings_map(queryset):
"""Helper function to map settings to devices."""
device_to_settings_map = {}
+ update_dynamic_groups_cache()
for device in queryset:
dynamic_group = device.dynamic_groups.exclude(golden_config_setting__isnull=True).order_by(
"-golden_config_setting__weight"
@@ -194,7 +194,7 @@ def get_xml_config(config):
"""Helper to parse XML config files."""
try:
parser = etree.XMLParser(remove_blank_text=True)
- return etree.fromstring(config, parser=parser) # nosec
+ return etree.fromstring(config, parser=parser) # noqa: S320
except etree.ParseError:
return None
@@ -203,7 +203,7 @@ def list_to_string(items):
"""Helper function to set the proper list of items sentence."""
if len(items) == 1:
return items[0]
- if len(items) == 2:
+ if len(items) == 2: # noqa: PLR2004
return " and ".join(items)
return ", ".join(items[:-1]) + " and " + items[-1]
@@ -278,3 +278,9 @@ def get_xml_subtree_with_full_path(config_xml, match_config):
current_element = copied_parent
current_element.append(deepcopy(element))
return etree.tostring(new_root, encoding="unicode", pretty_print=True)
+
+
+def update_dynamic_groups_cache():
+ """Update dynamic group cache for all golden config dynamic groups."""
+ for setting in models.GoldenConfigSetting.objects.all():
+ setting.dynamic_group.update_cached_members()
diff --git a/nautobot_golden_config/utilities/logger.py b/nautobot_golden_config/utilities/logger.py
index 4563315d..3ff8dfad 100644
--- a/nautobot_golden_config/utilities/logger.py
+++ b/nautobot_golden_config/utilities/logger.py
@@ -1,7 +1,7 @@
"""Customer logger to support writing to console and db."""
-from typing import Any
import logging
+from typing import Any
LOGGER = logging.getLogger("NORNIR_LOGGER")
diff --git a/nautobot_golden_config/utilities/mat_plot.py b/nautobot_golden_config/utilities/mat_plot.py
index 4b6b23d6..3397a3e4 100644
--- a/nautobot_golden_config/utilities/mat_plot.py
+++ b/nautobot_golden_config/utilities/mat_plot.py
@@ -1,15 +1,15 @@
"""Utility functions for working with mathplotlib."""
+
import base64
import io
import logging
import urllib
-from django.db.models import Count, Q
-
import matplotlib.pyplot as plt
import numpy as np
-
+from django.db.models import Count, Q
from nautobot.core.choices import ColorChoices
+
from nautobot_golden_config.utilities import constant
GREEN = "#" + ColorChoices.COLOR_GREEN
diff --git a/nautobot_golden_config/utilities/utils.py b/nautobot_golden_config/utilities/utils.py
index 63be40b3..a45ca6eb 100644
--- a/nautobot_golden_config/utilities/utils.py
+++ b/nautobot_golden_config/utilities/utils.py
@@ -1,8 +1,7 @@
"""Utility functions."""
-from django.conf import settings
from constance import config as constance_name
-
+from django.conf import settings
from nautobot.extras.choices import SecretsGroupAccessTypeChoices
from nautobot.extras.models.secrets import SecretsGroupAssociation
diff --git a/nautobot_golden_config/views.py b/nautobot_golden_config/views.py
index ad1f601e..76e812e3 100644
--- a/nautobot_golden_config/views.py
+++ b/nautobot_golden_config/views.py
@@ -1,4 +1,5 @@
"""Django views for Nautobot Golden Configuration.""" # pylint: disable=too-many-lines
+
import json
import logging
from datetime import datetime
@@ -9,14 +10,13 @@
from django.db.models import Count, ExpressionWrapper, F, FloatField, Max, Q
from django.shortcuts import redirect, render
from django.urls import reverse
-
from django.utils.html import format_html
from django.utils.timezone import make_aware
from django.views.generic import View
from django_pivot.pivot import pivot
from nautobot.apps import views
from nautobot.core.views import generic
-from nautobot.core.views.mixins import ObjectPermissionRequiredMixin, PERMISSIONS_ACTION_MAP
+from nautobot.core.views.mixins import PERMISSIONS_ACTION_MAP, ObjectPermissionRequiredMixin
from nautobot.dcim.models import Device
from nautobot.extras.models import Job, JobResult
from rest_framework.decorators import action
diff --git a/poetry.lock b/poetry.lock
index 616bc924..5ae165a2 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -1,4 +1,4 @@
-# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand.
+# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand.
[[package]]
name = "amqp"
@@ -141,18 +141,21 @@ tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pyte
[[package]]
name = "babel"
-version = "2.12.1"
+version = "2.16.0"
description = "Internationalization utilities"
optional = false
-python-versions = ">=3.7"
+python-versions = ">=3.8"
files = [
- {file = "Babel-2.12.1-py3-none-any.whl", hash = "sha256:b4246fb7677d3b98f501a39d43396d3cafdc8eadb045f4a31be01863f655c610"},
- {file = "Babel-2.12.1.tar.gz", hash = "sha256:cc2d99999cd01d44420ae725a21c9e3711b3aadc7976d6147f622d8581963455"},
+ {file = "babel-2.16.0-py3-none-any.whl", hash = "sha256:368b5b98b37c06b7daf6696391c3240c938b37767d4584413e8438c5c435fa8b"},
+ {file = "babel-2.16.0.tar.gz", hash = "sha256:d1f3554ca26605fe173f3de0c65f750f5a42f924499bf134de6423582298e316"},
]
[package.dependencies]
pytz = {version = ">=2015.7", markers = "python_version < \"3.9\""}
+[package.extras]
+dev = ["freezegun (>=1.0,<2.0)", "pytest (>=6.0)", "pytest-cov"]
+
[[package]]
name = "backcall"
version = "0.2.0"
@@ -195,29 +198,6 @@ tzdata = {version = "*", optional = true, markers = "extra == \"tzdata\""}
[package.extras]
tzdata = ["tzdata"]
-[[package]]
-name = "bandit"
-version = "1.7.5"
-description = "Security oriented static analyser for python code."
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "bandit-1.7.5-py3-none-any.whl", hash = "sha256:75665181dc1e0096369112541a056c59d1c5f66f9bb74a8d686c3c362b83f549"},
- {file = "bandit-1.7.5.tar.gz", hash = "sha256:bdfc739baa03b880c2d15d0431b31c658ffc348e907fe197e54e0389dd59e11e"},
-]
-
-[package.dependencies]
-colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""}
-GitPython = ">=1.0.1"
-PyYAML = ">=5.3.1"
-rich = "*"
-stevedore = ">=1.20.0"
-
-[package.extras]
-test = ["beautifulsoup4 (>=4.8.0)", "coverage (>=4.5.4)", "fixtures (>=3.0.0)", "flake8 (>=4.0.0)", "pylint (==1.9.4)", "stestr (>=2.5.0)", "testscenarios (>=0.5.0)", "testtools (>=2.3.0)", "tomli (>=1.1.0)"]
-toml = ["tomli (>=1.1.0)"]
-yaml = ["PyYAML"]
-
[[package]]
name = "bcrypt"
version = "4.0.1"
@@ -254,19 +234,22 @@ typecheck = ["mypy"]
[[package]]
name = "beautifulsoup4"
-version = "4.12.2"
+version = "4.12.3"
description = "Screen-scraping library"
optional = false
python-versions = ">=3.6.0"
files = [
- {file = "beautifulsoup4-4.12.2-py3-none-any.whl", hash = "sha256:bd2520ca0d9d7d12694a53d44ac482d181b4ec1888909b035a3dbf40d0f57d4a"},
- {file = "beautifulsoup4-4.12.2.tar.gz", hash = "sha256:492bbc69dca35d12daac71c4db1bfff0c876c00ef4a2ffacce226d4638eb72da"},
+ {file = "beautifulsoup4-4.12.3-py3-none-any.whl", hash = "sha256:b80878c9f40111313e55da8ba20bdba06d8fa3969fc68304167741bbf9e082ed"},
+ {file = "beautifulsoup4-4.12.3.tar.gz", hash = "sha256:74e3d1928edc070d21748185c46e3fb33490f22f52a3addee9aee0f4f7781051"},
]
[package.dependencies]
soupsieve = ">1.2"
[package.extras]
+cchardet = ["cchardet"]
+chardet = ["chardet"]
+charset-normalizer = ["charset-normalizer"]
html5lib = ["html5lib"]
lxml = ["lxml"]
@@ -281,52 +264,6 @@ files = [
{file = "billiard-4.1.0.tar.gz", hash = "sha256:1ad2eeae8e28053d729ba3373d34d9d6e210f6e4d8bf0a9c64f92bd053f1edf5"},
]
-[[package]]
-name = "black"
-version = "23.9.1"
-description = "The uncompromising code formatter."
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "black-23.9.1-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:d6bc09188020c9ac2555a498949401ab35bb6bf76d4e0f8ee251694664df6301"},
- {file = "black-23.9.1-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:13ef033794029b85dfea8032c9d3b92b42b526f1ff4bf13b2182ce4e917f5100"},
- {file = "black-23.9.1-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:75a2dc41b183d4872d3a500d2b9c9016e67ed95738a3624f4751a0cb4818fe71"},
- {file = "black-23.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13a2e4a93bb8ca74a749b6974925c27219bb3df4d42fc45e948a5d9feb5122b7"},
- {file = "black-23.9.1-cp310-cp310-win_amd64.whl", hash = "sha256:adc3e4442eef57f99b5590b245a328aad19c99552e0bdc7f0b04db6656debd80"},
- {file = "black-23.9.1-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:8431445bf62d2a914b541da7ab3e2b4f3bc052d2ccbf157ebad18ea126efb91f"},
- {file = "black-23.9.1-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:8fc1ddcf83f996247505db6b715294eba56ea9372e107fd54963c7553f2b6dfe"},
- {file = "black-23.9.1-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:7d30ec46de88091e4316b17ae58bbbfc12b2de05e069030f6b747dfc649ad186"},
- {file = "black-23.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:031e8c69f3d3b09e1aa471a926a1eeb0b9071f80b17689a655f7885ac9325a6f"},
- {file = "black-23.9.1-cp311-cp311-win_amd64.whl", hash = "sha256:538efb451cd50f43aba394e9ec7ad55a37598faae3348d723b59ea8e91616300"},
- {file = "black-23.9.1-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:638619a559280de0c2aa4d76f504891c9860bb8fa214267358f0a20f27c12948"},
- {file = "black-23.9.1-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:a732b82747235e0542c03bf352c126052c0fbc458d8a239a94701175b17d4855"},
- {file = "black-23.9.1-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:cf3a4d00e4cdb6734b64bf23cd4341421e8953615cba6b3670453737a72ec204"},
- {file = "black-23.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf99f3de8b3273a8317681d8194ea222f10e0133a24a7548c73ce44ea1679377"},
- {file = "black-23.9.1-cp38-cp38-win_amd64.whl", hash = "sha256:14f04c990259576acd093871e7e9b14918eb28f1866f91968ff5524293f9c573"},
- {file = "black-23.9.1-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:c619f063c2d68f19b2d7270f4cf3192cb81c9ec5bc5ba02df91471d0b88c4c5c"},
- {file = "black-23.9.1-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:6a3b50e4b93f43b34a9d3ef00d9b6728b4a722c997c99ab09102fd5efdb88325"},
- {file = "black-23.9.1-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:c46767e8df1b7beefb0899c4a95fb43058fa8500b6db144f4ff3ca38eb2f6393"},
- {file = "black-23.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50254ebfa56aa46a9fdd5d651f9637485068a1adf42270148cd101cdf56e0ad9"},
- {file = "black-23.9.1-cp39-cp39-win_amd64.whl", hash = "sha256:403397c033adbc45c2bd41747da1f7fc7eaa44efbee256b53842470d4ac5a70f"},
- {file = "black-23.9.1-py3-none-any.whl", hash = "sha256:6ccd59584cc834b6d127628713e4b6b968e5f79572da66284532525a042549f9"},
- {file = "black-23.9.1.tar.gz", hash = "sha256:24b6b3ff5c6d9ea08a8888f6977eae858e1f340d7260cf56d70a49823236b62d"},
-]
-
-[package.dependencies]
-click = ">=8.0.0"
-mypy-extensions = ">=0.4.3"
-packaging = ">=22.0"
-pathspec = ">=0.9.0"
-platformdirs = ">=2"
-tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
-typing-extensions = {version = ">=4.0.1", markers = "python_version < \"3.11\""}
-
-[package.extras]
-colorama = ["colorama (>=0.4.3)"]
-d = ["aiohttp (>=3.7.4)"]
-jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
-uvloop = ["uvloop (>=0.15.2)"]
-
[[package]]
name = "celery"
version = "5.3.4"
@@ -1361,22 +1298,6 @@ files = [
[package.extras]
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
-[[package]]
-name = "flake8"
-version = "5.0.4"
-description = "the modular source code checker: pep8 pyflakes and co"
-optional = false
-python-versions = ">=3.6.1"
-files = [
- {file = "flake8-5.0.4-py2.py3-none-any.whl", hash = "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248"},
- {file = "flake8-5.0.4.tar.gz", hash = "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db"},
-]
-
-[package.dependencies]
-mccabe = ">=0.7.0,<0.8.0"
-pycodestyle = ">=2.9.0,<2.10.0"
-pyflakes = ">=2.5.0,<2.6.0"
-
[[package]]
name = "fonttools"
version = "4.43.0"
@@ -2232,43 +2153,34 @@ importlib-metadata = {version = ">=4.4", markers = "python_version < \"3.10\""}
testing = ["coverage", "pyyaml"]
[[package]]
-name = "markdown-it-py"
-version = "3.0.0"
-description = "Python port of markdown-it. Markdown parsing, done right!"
+name = "markdown-version-annotations"
+version = "1.0.1"
+description = "Markdown plugin to add custom admonitions for documenting version differences"
optional = false
-python-versions = ">=3.8"
+python-versions = "<4.0,>=3.7"
files = [
- {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
- {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"},
+ {file = "markdown_version_annotations-1.0.1-py3-none-any.whl", hash = "sha256:6df0b2ac08bab906c8baa425f59fc0fe342fbe8b3917c144fb75914266b33200"},
+ {file = "markdown_version_annotations-1.0.1.tar.gz", hash = "sha256:620aade507ef175ccfb2059db152a34c6a1d2add28c2be16ea4de38d742e6132"},
]
[package.dependencies]
-mdurl = ">=0.1,<1.0"
-
-[package.extras]
-benchmarking = ["psutil", "pytest", "pytest-benchmark"]
-code-style = ["pre-commit (>=3.0,<4.0)"]
-compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"]
-linkify = ["linkify-it-py (>=1,<3)"]
-plugins = ["mdit-py-plugins"]
-profiling = ["gprof2dot"]
-rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
-testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
+markdown = ">=3.3.7,<4.0.0"
[[package]]
name = "markdown2"
-version = "2.4.10"
+version = "2.5.0"
description = "A fast and complete Python implementation of Markdown"
optional = false
-python-versions = ">=3.5, <4"
+python-versions = "<4,>=3.8"
files = [
- {file = "markdown2-2.4.10-py2.py3-none-any.whl", hash = "sha256:e6105800483783831f5dc54f827aa5b44eb137ecef5a70293d8ecfbb4109ecc6"},
- {file = "markdown2-2.4.10.tar.gz", hash = "sha256:cdba126d90dc3aef6f4070ac342f974d63f415678959329cc7909f96cc235d72"},
+ {file = "markdown2-2.5.0-py2.py3-none-any.whl", hash = "sha256:300d4429b620ebc974ef512339a9e08bc080473f95135a91f33906e24e8280c1"},
+ {file = "markdown2-2.5.0.tar.gz", hash = "sha256:9bff02911f8b617b61eb269c4c1a5f9b2087d7ff051604f66a61b63cab30adc2"},
]
[package.extras]
-all = ["pygments (>=2.7.3)", "wavedrom"]
+all = ["latex2mathml", "pygments (>=2.7.3)", "wavedrom"]
code-syntax-highlighting = ["pygments (>=2.7.3)"]
+latex = ["latex2mathml"]
wavedrom = ["wavedrom"]
[[package]]
@@ -2434,17 +2346,6 @@ files = [
{file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"},
]
-[[package]]
-name = "mdurl"
-version = "0.1.2"
-description = "Markdown URL utilities"
-optional = false
-python-versions = ">=3.7"
-files = [
- {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
- {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"},
-]
-
[[package]]
name = "mergedeep"
version = "1.3.4"
@@ -2539,17 +2440,6 @@ files = [
{file = "mkdocs_material_extensions-1.2.tar.gz", hash = "sha256:27e2d1ed2d031426a6e10d5ea06989d67e90bb02acd588bc5673106b5ee5eedf"},
]
-[[package]]
-name = "mkdocs-version-annotations"
-version = "1.0.0"
-description = "MkDocs plugin to add custom admonitions for documenting version differences"
-optional = false
-python-versions = ">=3.7,<4.0"
-files = [
- {file = "mkdocs-version-annotations-1.0.0.tar.gz", hash = "sha256:6786024b37d27b330fda240b76ebec8e7ce48bd5a9d7a66e99804559d088dffa"},
- {file = "mkdocs_version_annotations-1.0.0-py3-none-any.whl", hash = "sha256:385004eb4a7530dd87a227e08cd907ce7a8fe21fdf297720a4149c511bcf05f5"},
-]
-
[[package]]
name = "mkdocstrings"
version = "0.22.0"
@@ -3040,17 +2930,6 @@ files = [
{file = "pathspec-0.11.2.tar.gz", hash = "sha256:e0d8d0ac2f12da61956eb2306b69f9469b42f4deb0f3cb6ed47b9cce9996ced3"},
]
-[[package]]
-name = "pbr"
-version = "5.11.1"
-description = "Python Build Reasonableness"
-optional = false
-python-versions = ">=2.6"
-files = [
- {file = "pbr-5.11.1-py2.py3-none-any.whl", hash = "sha256:567f09558bae2b3ab53cb3c1e2e33e726ff3338e7bae3db5dc954b3a44eef12b"},
- {file = "pbr-5.11.1.tar.gz", hash = "sha256:aefc51675b0b533d56bb5fd1c8c6c0522fe31896679882e1c4c63d5e4a0fccb3"},
-]
-
[[package]]
name = "pexpect"
version = "4.8.0"
@@ -3307,17 +3186,6 @@ files = [
[package.extras]
tests = ["pytest"]
-[[package]]
-name = "pycodestyle"
-version = "2.9.1"
-description = "Python style guide checker"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "pycodestyle-2.9.1-py2.py3-none-any.whl", hash = "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b"},
- {file = "pycodestyle-2.9.1.tar.gz", hash = "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785"},
-]
-
[[package]]
name = "pycparser"
version = "2.21"
@@ -3346,17 +3214,6 @@ netaddr = "*"
dev = ["check-manifest", "pep8", "pyflakes", "twine"]
test = ["coverage", "mock"]
-[[package]]
-name = "pyflakes"
-version = "2.5.0"
-description = "passive checker of Python programs"
-optional = false
-python-versions = ">=3.6"
-files = [
- {file = "pyflakes-2.5.0-py2.py3-none-any.whl", hash = "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2"},
- {file = "pyflakes-2.5.0.tar.gz", hash = "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3"},
-]
-
[[package]]
name = "pygments"
version = "2.16.1"
@@ -3960,25 +3817,6 @@ requests = ">=2.0.0"
[package.extras]
rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
-[[package]]
-name = "rich"
-version = "13.6.0"
-description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
-optional = false
-python-versions = ">=3.7.0"
-files = [
- {file = "rich-13.6.0-py3-none-any.whl", hash = "sha256:2b38e2fe9ca72c9a00170a1a2d20c63c790d0e10ef1fe35eba76e1e7b1d7d245"},
- {file = "rich-13.6.0.tar.gz", hash = "sha256:5c14d22737e6d5084ef4771b62d5d4363165b403455a30a1c8ca39dc7b644bef"},
-]
-
-[package.dependencies]
-markdown-it-py = ">=2.2.0"
-pygments = ">=2.13.0,<3.0.0"
-typing-extensions = {version = ">=4.0.0,<5.0", markers = "python_version < \"3.9\""}
-
-[package.extras]
-jupyter = ["ipywidgets (>=7.5.1,<9)"]
-
[[package]]
name = "rpds-py"
version = "0.10.3"
@@ -4151,28 +3989,29 @@ files = [
[[package]]
name = "ruff"
-version = "0.1.15"
+version = "0.5.5"
description = "An extremely fast Python linter and code formatter, written in Rust."
optional = false
python-versions = ">=3.7"
files = [
- {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5fe8d54df166ecc24106db7dd6a68d44852d14eb0729ea4672bb4d96c320b7df"},
- {file = "ruff-0.1.15-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6f0bfbb53c4b4de117ac4d6ddfd33aa5fc31beeaa21d23c45c6dd249faf9126f"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0d432aec35bfc0d800d4f70eba26e23a352386be3a6cf157083d18f6f5881c8"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9405fa9ac0e97f35aaddf185a1be194a589424b8713e3b97b762336ec79ff807"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c66ec24fe36841636e814b8f90f572a8c0cb0e54d8b5c2d0e300d28a0d7bffec"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:6f8ad828f01e8dd32cc58bc28375150171d198491fc901f6f98d2a39ba8e3ff5"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:86811954eec63e9ea162af0ffa9f8d09088bab51b7438e8b6488b9401863c25e"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fd4025ac5e87d9b80e1f300207eb2fd099ff8200fa2320d7dc066a3f4622dc6b"},
- {file = "ruff-0.1.15-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b17b93c02cdb6aeb696effecea1095ac93f3884a49a554a9afa76bb125c114c1"},
- {file = "ruff-0.1.15-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ddb87643be40f034e97e97f5bc2ef7ce39de20e34608f3f829db727a93fb82c5"},
- {file = "ruff-0.1.15-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:abf4822129ed3a5ce54383d5f0e964e7fef74a41e48eb1dfad404151efc130a2"},
- {file = "ruff-0.1.15-py3-none-musllinux_1_2_i686.whl", hash = "sha256:6c629cf64bacfd136c07c78ac10a54578ec9d1bd2a9d395efbee0935868bf852"},
- {file = "ruff-0.1.15-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:1bab866aafb53da39c2cadfb8e1c4550ac5340bb40300083eb8967ba25481447"},
- {file = "ruff-0.1.15-py3-none-win32.whl", hash = "sha256:2417e1cb6e2068389b07e6fa74c306b2810fe3ee3476d5b8a96616633f40d14f"},
- {file = "ruff-0.1.15-py3-none-win_amd64.whl", hash = "sha256:3837ac73d869efc4182d9036b1405ef4c73d9b1f88da2413875e34e0d6919587"},
- {file = "ruff-0.1.15-py3-none-win_arm64.whl", hash = "sha256:9a933dfb1c14ec7a33cceb1e49ec4a16b51ce3c20fd42663198746efc0427360"},
- {file = "ruff-0.1.15.tar.gz", hash = "sha256:f6dfa8c1b21c913c326919056c390966648b680966febcb796cc9d1aaab8564e"},
+ {file = "ruff-0.5.5-py3-none-linux_armv6l.whl", hash = "sha256:605d589ec35d1da9213a9d4d7e7a9c761d90bba78fc8790d1c5e65026c1b9eaf"},
+ {file = "ruff-0.5.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:00817603822a3e42b80f7c3298c8269e09f889ee94640cd1fc7f9329788d7bf8"},
+ {file = "ruff-0.5.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:187a60f555e9f865a2ff2c6984b9afeffa7158ba6e1eab56cb830404c942b0f3"},
+ {file = "ruff-0.5.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fe26fc46fa8c6e0ae3f47ddccfbb136253c831c3289bba044befe68f467bfb16"},
+ {file = "ruff-0.5.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:4ad25dd9c5faac95c8e9efb13e15803cd8bbf7f4600645a60ffe17c73f60779b"},
+ {file = "ruff-0.5.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f70737c157d7edf749bcb952d13854e8f745cec695a01bdc6e29c29c288fc36e"},
+ {file = "ruff-0.5.5-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:cfd7de17cef6ab559e9f5ab859f0d3296393bc78f69030967ca4d87a541b97a0"},
+ {file = "ruff-0.5.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a09b43e02f76ac0145f86a08e045e2ea452066f7ba064fd6b0cdccb486f7c3e7"},
+ {file = "ruff-0.5.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d0b856cb19c60cd40198be5d8d4b556228e3dcd545b4f423d1ad812bfdca5884"},
+ {file = "ruff-0.5.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3687d002f911e8a5faf977e619a034d159a8373514a587249cc00f211c67a091"},
+ {file = "ruff-0.5.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ac9dc814e510436e30d0ba535f435a7f3dc97f895f844f5b3f347ec8c228a523"},
+ {file = "ruff-0.5.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:af9bdf6c389b5add40d89b201425b531e0a5cceb3cfdcc69f04d3d531c6be74f"},
+ {file = "ruff-0.5.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d40a8533ed545390ef8315b8e25c4bb85739b90bd0f3fe1280a29ae364cc55d8"},
+ {file = "ruff-0.5.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:cab904683bf9e2ecbbe9ff235bfe056f0eba754d0168ad5407832928d579e7ab"},
+ {file = "ruff-0.5.5-py3-none-win32.whl", hash = "sha256:696f18463b47a94575db635ebb4c178188645636f05e934fdf361b74edf1bb2d"},
+ {file = "ruff-0.5.5-py3-none-win_amd64.whl", hash = "sha256:50f36d77f52d4c9c2f1361ccbfbd09099a1b2ea5d2b2222c586ab08885cf3445"},
+ {file = "ruff-0.5.5-py3-none-win_arm64.whl", hash = "sha256:3191317d967af701f1b73a31ed5788795936e423b7acce82a2b63e26eb3e89d6"},
+ {file = "ruff-0.5.5.tar.gz", hash = "sha256:cc5516bdb4858d972fbc31d246bdb390eab8df1a26e2353be2dbc0c2d7f5421a"},
]
[[package]]
@@ -4329,13 +4168,13 @@ saml = ["python3-saml (>=1.5.0)"]
[[package]]
name = "soupsieve"
-version = "2.5"
+version = "2.6"
description = "A modern CSS selector implementation for Beautiful Soup."
optional = false
python-versions = ">=3.8"
files = [
- {file = "soupsieve-2.5-py3-none-any.whl", hash = "sha256:eaa337ff55a1579b6549dc679565eac1e3d000563bcb1c8ab0d0fefbc0c2cdc7"},
- {file = "soupsieve-2.5.tar.gz", hash = "sha256:5663d5a7b3bfaeee0bc4372e7fc48f9cff4940b3eec54a6451cc5299f1097690"},
+ {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"},
+ {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"},
]
[[package]]
@@ -4373,20 +4212,6 @@ pure-eval = "*"
[package.extras]
tests = ["cython", "littleutils", "pygments", "pytest", "typeguard"]
-[[package]]
-name = "stevedore"
-version = "5.1.0"
-description = "Manage dynamic plugins for Python applications"
-optional = false
-python-versions = ">=3.8"
-files = [
- {file = "stevedore-5.1.0-py3-none-any.whl", hash = "sha256:8cc040628f3cea5d7128f2e76cf486b2251a4e543c7b938f58d9a377f6694a2d"},
- {file = "stevedore-5.1.0.tar.gz", hash = "sha256:a54534acf9b89bc7ed264807013b505bf07f74dbe4bcfa37d32bd063870b087c"},
-]
-
-[package.dependencies]
-pbr = ">=2.0.0,<2.1.0 || >2.1.0"
-
[[package]]
name = "svgwrite"
version = "1.4.3"
@@ -4837,4 +4662,4 @@ all = []
[metadata]
lock-version = "2.0"
python-versions = ">=3.8,<3.12"
-content-hash = "3320a0e8fced27e6dac2b0239c9067d73b8e91fe394da3c482543b5beb8ebade"
+content-hash = "d3d8429fc0b3c1628cb046a7e153701cf19d6d2ca6b71c8993afefcb764a402a"
diff --git a/pyproject.toml b/pyproject.toml
index 52410407..5d97e79c 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -44,25 +44,22 @@ nautobot-capacity-metrics = "^3.0.1"
xmldiff = "^2.6.3"
[tool.poetry.group.dev.dependencies]
-bandit = "*"
-black = "*"
coverage = "*"
django-debug-toolbar = "*"
-flake8 = "*"
invoke = "*"
ipython = "*"
pylint = "*"
pylint-django = "*"
pylint-nautobot = "*"
-ruff = "*"
+ruff = "0.5.5"
yamllint = "*"
Markdown = "*"
+# Render custom markdown for version added/changed/remove notes
+markdown-version-annotations = "1.0.1"
# Rendering docs to HTML
mkdocs = "1.5.2"
# Material for MkDocs theme
mkdocs-material = "9.2.4"
-# Render custom markdown for version added/changed/remove notes
-mkdocs-version-annotations = "1.0.0"
# Automatic documentation from sources, for MkDocs
mkdocstrings = "0.22.0"
mkdocstrings-python = "1.5.2"
@@ -74,29 +71,6 @@ jsonschema = "*"
all = [
]
-[tool.black]
-line-length = 120
-target-version = ['py38', 'py39', 'py310', 'py311']
-include = '\.pyi?$'
-exclude = '''
-(
- /(
- \.eggs # exclude a few common directories in the
- | \.git # root of the project
- | \.hg
- | \.mypy_cache
- | \.tox
- | \.venv
- | _build
- | buck-out
- | build
- | dist
- )/
- | settings.py # This is where you define files that should not be stylized by black
- # the root of the project
-)
-'''
-
[tool.pylint.master]
# Include the pylint_django plugin to avoid spurious warnings about Django patterns
extension-pkg-allow-list = ["lxml"]
@@ -110,13 +84,11 @@ no-docstring-rgx="^(_|test_|Meta$)"
good-names = ["pk"]
[tool.pylint.messages_control]
-# Line length is enforced by Black, so pylint doesn't need to check it.
-# Pylint and Black disagree about how to format multi-line arrays; Black wins.
disable = """,
line-too-long,
too-few-public-methods,
duplicate-code,
- """
+"""
[tool.pylint.miscellaneous]
# Don't flag TODO as a failure, let us commit with things that still need to be done in the code
@@ -137,6 +109,10 @@ target-version = "py38"
[tool.ruff.lint]
select = [
"D", # pydocstyle
+ "F", "E", "W", # flake8
+ "PL", # pylint
+ "S", # bandit
+ "I", # isort
]
ignore = [
# warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible.
@@ -154,6 +130,7 @@ ignore = [
"D401", # First line of docstring should be in imperative mood
"D407", # Missing dashed underline after section
"D416", # Section name ends in colon
+ "E501", # Line too long
# Package specific ignores for backwards compatibility
"D417", # Missing argument descriptions in Docstrings
@@ -162,12 +139,13 @@ ignore = [
[tool.ruff.lint.pydocstyle]
convention = "google"
-[tool.ruff.per-file-ignores]
+[tool.ruff.lint.per-file-ignores]
"nautobot_golden_config/migrations/*" = [
- "D", # pydocstyle
+ "D",
]
"nautobot_golden_config/tests/*" = [
- "D", # pydocstyle
+ "D",
+ "S"
]
[build-system]
diff --git a/tasks.py b/tasks.py
index 09bc977f..3b01950e 100644
--- a/tasks.py
+++ b/tasks.py
@@ -159,17 +159,17 @@ def run_command(context, command, **kwargs):
# Check if nautobot is running, no need to start another nautobot container to run a command
docker_compose_status = "ps --services --filter status=running"
results = docker_compose(context, docker_compose_status, hide="out")
- if "nautobot" in results.stdout:
- compose_command = "exec"
- else:
- compose_command = "run --rm --entrypoint=''"
+ command_env_args = ""
if "command_env" in kwargs:
command_env = kwargs.pop("command_env")
for key, value in command_env.items():
- compose_command += f' --env="{key}={value}"'
+ command_env_args += f' --env="{key}={value}"'
- compose_command += f" -- nautobot {command}"
+ if "nautobot" in results.stdout:
+ compose_command = f"exec{command_env_args} nautobot {command}"
+ else:
+ compose_command = f"run{command_env_args} --rm --entrypoint='{command}' nautobot"
pty = kwargs.pop("pty", True)
@@ -494,7 +494,12 @@ def dbshell(context, db_name="", input_file="", output_file="", query=""):
f"> '{output_file}'" if output_file else "",
]
- docker_compose(context, " ".join(command), env=env, pty=not (input_file or output_file or query))
+ docker_compose(
+ context,
+ " ".join(command),
+ env=env,
+ pty=not (input_file or output_file or query),
+ )
@task(
@@ -519,9 +524,11 @@ def import_db(context, db_name="", input_file="dump.sql"):
'--execute="',
f"DROP DATABASE IF EXISTS {db_name};",
f"CREATE DATABASE {db_name};",
- ""
- if db_name == "$MYSQL_DATABASE"
- else f"GRANT ALL PRIVILEGES ON {db_name}.* TO $MYSQL_USER; FLUSH PRIVILEGES;",
+ (
+ ""
+ if db_name == "$MYSQL_DATABASE"
+ else f"GRANT ALL PRIVILEGES ON {db_name}.* TO $MYSQL_USER; FLUSH PRIVILEGES;"
+ ),
'"',
"&&",
"mysql",
@@ -647,28 +654,6 @@ def generate_release_notes(context, version=""):
# ------------------------------------------------------------------------------
# TESTS
# ------------------------------------------------------------------------------
-@task(
- help={
- "autoformat": "Apply formatting recommendations automatically, rather than failing if formatting is incorrect.",
- }
-)
-def black(context, autoformat=False):
- """Check Python code style with Black."""
- if autoformat:
- black_command = "black"
- else:
- black_command = "black --check --diff"
-
- command = f"{black_command} ."
-
- run_command(context, command)
-
-
-@task
-def flake8(context):
- """Check for PEP8 compliance and other style issues."""
- command = "flake8 . --config .flake8"
- run_command(context, command)
@task
@@ -688,38 +673,39 @@ def pylint(context):
@task(aliases=("a",))
def autoformat(context):
"""Run code autoformatting."""
- black(context, autoformat=True)
- ruff(context, fix=True)
+ ruff(context, action=["format"], fix=True)
@task(
help={
- "action": "One of 'lint', 'format', or 'both'",
- "fix": "Automatically fix selected action. May not be able to fix all.",
- "output_format": "see https://docs.astral.sh/ruff/settings/#output-format",
+ "action": "Available values are `['lint', 'format']`. Can be used multiple times. (default: `['lint', 'format']`)",
+ "target": "File or directory to inspect, repeatable (default: all files in the project will be inspected)",
+ "fix": "Automatically fix selected actions. May not be able to fix all issues found. (default: False)",
+ "output_format": "See https://docs.astral.sh/ruff/settings/#output-format for details. (default: `concise`)",
},
+ iterable=["action", "target"],
)
-def ruff(context, action="lint", fix=False, output_format="text"):
+def ruff(context, action=None, target=None, fix=False, output_format="concise"):
"""Run ruff to perform code formatting and/or linting."""
- if action != "lint":
- command = "ruff format"
- if not fix:
- command += " --check"
- command += " ."
- run_command(context, command)
- if action != "format":
- command = "ruff check"
- if fix:
- command += " --fix"
- command += f" --output-format {output_format} ."
- run_command(context, command)
+ if not action:
+ action = ["lint", "format"]
+ if not target:
+ target = ["."]
+ if "format" in action:
+ command = "ruff format "
+ if not fix:
+ command += "--check "
+ command += " ".join(target)
+ run_command(context, command, warn=True)
-@task
-def bandit(context):
- """Run bandit to validate basic static code security analysis."""
- command = "bandit --recursive . --configfile .bandit.yml"
- run_command(context, command)
+ if "lint" in action:
+ command = "ruff check "
+ if fix:
+ command += "--fix "
+ command += f"--output-format {output_format} "
+ command += " ".join(target)
+ run_command(context, command, warn=True)
@task
@@ -751,7 +737,7 @@ def check_migrations(context):
"verbose": "Enable verbose test output.",
}
)
-def unittest(
+def unittest( # noqa: PLR0913
context,
keepdb=False,
label="nautobot_golden_config",
@@ -799,14 +785,8 @@ def tests(context, failfast=False, keepdb=False, lint_only=False):
print("Starting Docker Containers...")
start(context)
# Sorted loosely from fastest to slowest
- print("Running black...")
- black(context)
print("Running ruff...")
ruff(context)
- print("Running flake8...")
- flake8(context)
- print("Running bandit...")
- bandit(context)
print("Running yamllint...")
yamllint(context)
print("Running poetry check...")
@@ -839,11 +819,20 @@ def generate_app_config_schema(context):
- `NautobotAppConfig.required_settings`
"""
start(context, service="nautobot")
- nbshell(context, file="development/app_config_schema.py", env={"APP_CONFIG_SCHEMA_COMMAND": "generate"})
+ nbshell(
+ context,
+ file="development/app_config_schema.py",
+ env={"APP_CONFIG_SCHEMA_COMMAND": "generate"},
+ )
@task
def validate_app_config(context):
"""Validate the app config based on the app config schema."""
start(context, service="nautobot")
- nbshell(context, plain=True, file="development/app_config_schema.py", env={"APP_CONFIG_SCHEMA_COMMAND": "validate"})
+ nbshell(
+ context,
+ plain=True,
+ file="development/app_config_schema.py",
+ env={"APP_CONFIG_SCHEMA_COMMAND": "validate"},
+ )