diff --git a/.cookiecutter.json b/.cookiecutter.json
index d1e2df97..c5f93035 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": "cfd9475f3ea601572d0f3b23d4bb6d9655bc2721"
+ "baked_commit_ref": "76744d296d468b03fb528c902f68c3dc230a9a3f"
}
}
}
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 17bd2cfa..24473bd9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ env:
APP_NAME: "nautobot-app-chatops"
jobs:
- black:
+ ruff-format:
runs-on: "ubuntu-22.04"
env:
INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True"
@@ -25,20 +25,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_CHATOPS_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_CHATOPS_LOCAL: "True"
@@ -60,17 +49,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_CHATOPS_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:
@@ -95,12 +73,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/README.md b/README.md
index 43a0c439..2988b69f 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@
- A multi-platform ChatOps bot App for Nautobot.
+ An App for Nautobot.
## Overview
diff --git a/changes/321.housekeeping b/changes/321.housekeeping
new file mode 100644
index 00000000..996d400a
--- /dev/null
+++ b/changes/321.housekeeping
@@ -0,0 +1 @@
+Rebake with 2.3.0 Cookiecutter.
\ No newline at end of file
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 9e17c4ad..5d558da2 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 106be230..a6d0dfc8 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 2f1103da..dbe31cba 100644
--- a/development/docker-compose.mysql.yml
+++ b/development/docker-compose.mysql.yml
@@ -1,6 +1,4 @@
---
-version: "3.8"
-
services:
nautobot:
environment:
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/mattermost/nautobot_bootstrap.py b/development/mattermost/nautobot_bootstrap.py
index bdd6462c..3fae7744 100644
--- a/development/mattermost/nautobot_bootstrap.py
+++ b/development/mattermost/nautobot_bootstrap.py
@@ -1,19 +1,18 @@
"""Bootstrap script for Nautobot to allow Mattermost integration."""
-
import contextlib
+
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
from nautobot_chatops.models import (
- AccessGrantTypeChoices,
- PlatformChoices,
AccessGrant,
- CommandToken,
+ AccessGrantTypeChoices,
ChatOpsAccountLink,
+ CommandToken,
+ PlatformChoices,
)
-
User = get_user_model()
for grant_type in AccessGrantTypeChoices.values():
diff --git a/development/nautobot_config.py b/development/nautobot_config.py
index 825ea553..31cc69c5 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,
}
}
@@ -171,7 +173,7 @@
"tower_password": os.getenv("NAUTOBOT_TOWER_PASSWORD"),
"tower_uri": os.getenv("NAUTOBOT_TOWER_URI"),
"tower_username": os.getenv("NAUTOBOT_TOWER_USERNAME"),
- "tower_verify_ssl": is_truthy(os.getenv("NAUTOBOT_TOWER_VERIFY_SSL", True)),
+ "tower_verify_ssl": is_truthy(os.getenv("NAUTOBOT_TOWER_VERIFY_SSL", "true")),
# - Arista CloudVision ---------------
"enable_aristacv": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_ARISTACV")),
"aristacv_cvaas_url": os.environ.get("ARISTACV_CVAAS_URL"),
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 d0875111..e6fc5d35 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 621c4ea2..abee82ce 100644
--- a/docs/dev/dev_environment.md
+++ b/docs/dev/dev_environment.md
@@ -131,10 +131,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.
@@ -462,7 +459,7 @@ This is the same as running:
### Tests
-To run tests against your code, you can run all the tests that GitHub CI 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
@@ -472,9 +469,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 e3d2c7ec..ca55a90d 100644
--- a/docs/requirements.txt
+++ b/docs/requirements.txt
@@ -1,6 +1,6 @@
mkdocs==1.5.2
mkdocs-material==9.1.15
-mkdocs-version-annotations==1.0.0
+markdown-version-annotations==1.0.1
mkdocstrings-python==1.5.2
mkdocstrings==0.22.0
mkdocs-include-markdown-plugin==6.0.3
diff --git a/mkdocs.yml b/mkdocs.yml
index 25aec33f..c806a12c 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_chatops/__init__.py b/nautobot_chatops/__init__.py
index efa39a7b..c8e89ce8 100644
--- a/nautobot_chatops/__init__.py
+++ b/nautobot_chatops/__init__.py
@@ -1,4 +1,5 @@
"""App declaration for nautobot_chatops."""
+
# Metadata is inherited from Nautobot. If not including Nautobot in the environment, this should be added
from importlib import metadata
@@ -158,6 +159,7 @@ def ready(self):
super().ready()
# pylint: disable=import-outside-toplevel
from nautobot_capacity_metrics import register_metric_func
+
from .metrics_app import metric_commands
register_metric_func(metric_commands)
diff --git a/nautobot_chatops/admin.py b/nautobot_chatops/admin.py
index 53b3f889..02fa2123 100644
--- a/nautobot_chatops/admin.py
+++ b/nautobot_chatops/admin.py
@@ -1,6 +1,7 @@
"""Administrative capabilities for nautobot_chatops app."""
from django.contrib import admin
+
from .models import ChatOpsAccountLink, CommandLog
diff --git a/nautobot_chatops/api/serializers.py b/nautobot_chatops/api/serializers.py
index d0cac19d..616ab373 100644
--- a/nautobot_chatops/api/serializers.py
+++ b/nautobot_chatops/api/serializers.py
@@ -1,8 +1,7 @@
"""API Serializers for ChatOps App."""
-from rest_framework import serializers
-
from nautobot.core.api import NautobotModelSerializer
+from rest_framework import serializers
from nautobot_chatops.models import AccessGrant, CommandLog, CommandToken
diff --git a/nautobot_chatops/api/urls.py b/nautobot_chatops/api/urls.py
index 9443a98d..f470beb1 100644
--- a/nautobot_chatops/api/urls.py
+++ b/nautobot_chatops/api/urls.py
@@ -5,6 +5,7 @@
from django.urls import include, path
from nautobot.apps.api import OrderedDefaultRouter
from nautobot.apps.config import get_app_settings_or_config
+
from nautobot_chatops.api.views.generic import (
AccessGrantViewSet,
CommandLogViewSet,
@@ -13,7 +14,6 @@
)
from nautobot_chatops.api.views.lookup import AccessLookupView, UserEmailLookupView
-
logger = logging.getLogger(__name__)
urlpatterns = [
path("lookup/", AccessLookupView.as_view(), name="access_lookup"),
@@ -21,7 +21,7 @@
]
if get_app_settings_or_config("nautobot_chatops", "enable_slack"):
- from nautobot_chatops.api.views.slack import SlackSlashCommandView, SlackInteractionView, SlackEventAPIView
+ from nautobot_chatops.api.views.slack import SlackEventAPIView, SlackInteractionView, SlackSlashCommandView
urlpatterns += [
path("slack/slash_command/", SlackSlashCommandView.as_view(), name="slack_slash_command"),
@@ -44,7 +44,7 @@
]
if get_app_settings_or_config("nautobot_chatops", "enable_mattermost"):
- from nautobot_chatops.api.views.mattermost import MattermostSlashCommandView, MattermostInteractionView
+ from nautobot_chatops.api.views.mattermost import MattermostInteractionView, MattermostSlashCommandView
urlpatterns += [
path("mattermost/slash_command/", MattermostSlashCommandView.as_view(), name="mattermost_slash_command"),
diff --git a/nautobot_chatops/api/views/generic.py b/nautobot_chatops/api/views/generic.py
index fb26d10e..0cbd85f9 100644
--- a/nautobot_chatops/api/views/generic.py
+++ b/nautobot_chatops/api/views/generic.py
@@ -1,10 +1,11 @@
"""API Views for Nautobot Chatops."""
-from rest_framework.routers import APIRootView
+
from nautobot.apps.api import NautobotModelViewSet
+from rest_framework.routers import APIRootView
from nautobot_chatops.api.serializers import AccessGrantSerializer, CommandLogSerializer, CommandTokenSerializer
-from nautobot_chatops.models import AccessGrant, CommandLog, CommandToken
from nautobot_chatops.filters import AccessGrantFilterSet, CommandLogFilterSet, CommandTokenFilterSet
+from nautobot_chatops.models import AccessGrant, CommandLog, CommandToken
class NautobotChatopsRootView(APIRootView):
diff --git a/nautobot_chatops/api/views/lookup.py b/nautobot_chatops/api/views/lookup.py
index 2c93668d..41453bcd 100644
--- a/nautobot_chatops/api/views/lookup.py
+++ b/nautobot_chatops/api/views/lookup.py
@@ -1,7 +1,8 @@
"""API views for dynamic lookup of platform-specific data."""
import contextlib
-from django.http import JsonResponse, HttpResponseBadRequest, HttpResponseNotFound
+
+from django.http import HttpResponseBadRequest, HttpResponseNotFound, JsonResponse
from django.views import View
from nautobot_chatops.dispatchers import Dispatcher
diff --git a/nautobot_chatops/api/views/mattermost.py b/nautobot_chatops/api/views/mattermost.py
index 05ab58f7..25e0013f 100644
--- a/nautobot_chatops/api/views/mattermost.py
+++ b/nautobot_chatops/api/views/mattermost.py
@@ -10,12 +10,12 @@
from django.views import View
from django.views.decorators.csrf import csrf_exempt
-from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string
-from nautobot_chatops.dispatchers.mattermost import MattermostDispatcher, Driver
-from nautobot_chatops.utils import check_and_enqueue_command
+from nautobot_chatops.choices import PlatformChoices
+from nautobot_chatops.dispatchers.mattermost import Driver, MattermostDispatcher
from nautobot_chatops.metrics import signature_error_cntr
from nautobot_chatops.models import CommandToken
-from nautobot_chatops.choices import PlatformChoices
+from nautobot_chatops.utils import check_and_enqueue_command
+from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string
# pylint: disable=logging-fstring-interpolation
diff --git a/nautobot_chatops/api/views/ms_teams.py b/nautobot_chatops/api/views/ms_teams.py
index abb75bcc..a2305db9 100644
--- a/nautobot_chatops/api/views/ms_teams.py
+++ b/nautobot_chatops/api/views/ms_teams.py
@@ -1,22 +1,20 @@
"""Views to receive inbound notifications from Microsoft Teams, parse them, and enqueue worker actions."""
import json
-import re
import logging
+import re
-import requests
import jwt
-
+import requests
from django.conf import settings
from django.http import HttpResponse
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
-from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string
from nautobot_chatops.dispatchers.ms_teams import MSTeamsDispatcher
from nautobot_chatops.utils import check_and_enqueue_command
-
+from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string
logger = logging.getLogger(__name__)
diff --git a/nautobot_chatops/api/views/slack.py b/nautobot_chatops/api/views/slack.py
index 9110bbdb..b390e4b6 100644
--- a/nautobot_chatops/api/views/slack.py
+++ b/nautobot_chatops/api/views/slack.py
@@ -13,10 +13,10 @@
from django.views.decorators.csrf import csrf_exempt
from slack_sdk import WebClient
-from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string
from nautobot_chatops.dispatchers.slack import SlackDispatcher
-from nautobot_chatops.utils import check_and_enqueue_command
from nautobot_chatops.metrics import signature_error_cntr
+from nautobot_chatops.utils import check_and_enqueue_command
+from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string
# pylint: disable=logging-fstring-interpolation
@@ -194,10 +194,12 @@ def post(self, request, *args, **kwargs):
except ValueError as err:
logger.error("%s", err)
return HttpResponse(f"Error: {err} encountered when processing {callback_id}")
+ # If more than 2 arguments are provided, we will need to format the selected value
+ argument_check = 2
for i, cmd in enumerate(cmds):
- if i == 2:
+ if i == argument_check:
selected_value += f"'{cmd}'"
- elif i > 2:
+ elif i > argument_check:
selected_value += f" '{cmd}'"
action_id = f"{cmds[0]} {cmds[1]}"
diff --git a/nautobot_chatops/api/views/webex.py b/nautobot_chatops/api/views/webex.py
index 0e014e52..bf8e7b5d 100644
--- a/nautobot_chatops/api/views/webex.py
+++ b/nautobot_chatops/api/views/webex.py
@@ -11,14 +11,12 @@
from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt
-
from webexteamssdk import WebexTeamsAPI
from webexteamssdk.exceptions import AccessTokenError, ApiError
-from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string
-from nautobot_chatops.dispatchers.webex import WebexDispatcher
-from nautobot_chatops.dispatchers.webex import WEBEX_CONFIG
+from nautobot_chatops.dispatchers.webex import WEBEX_CONFIG, WebexDispatcher
from nautobot_chatops.utils import check_and_enqueue_command
+from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string
logger = logging.getLogger(__name__)
diff --git a/nautobot_chatops/banner.py b/nautobot_chatops/banner.py
index 9d59de72..83daa7ca 100644
--- a/nautobot_chatops/banner.py
+++ b/nautobot_chatops/banner.py
@@ -1,9 +1,9 @@
"""Banner to alert Staff to use Admin site if trying to add/edit Account Links for other Users."""
+
from typing import Optional
from django.urls import reverse
from django.utils.html import format_html
-
from nautobot.extras.choices import BannerClassChoices
from nautobot.extras.plugins import PluginBanner
diff --git a/nautobot_chatops/constants.py b/nautobot_chatops/constants.py
index 22f46662..0b5cf354 100644
--- a/nautobot_chatops/constants.py
+++ b/nautobot_chatops/constants.py
@@ -13,8 +13,6 @@
# pylint: disable=line-too-long
ACCESS_GRANT_VALUE_HELP_TEXT = "Corresponding ID value to grant access to.
Enter * to grant access to all organizations, channels, or users"
-COMMAND_TOKEN_COMMENT_HELP_TEXT = "Optional: Enter description of token" # nosec - skips Bandit B105 error
-COMMAND_TOKEN_TOKEN_HELP_TEXT = (
- "Token given by chat platform for signing or command validation" # nosec - skips Bandit B105 error
-)
+COMMAND_TOKEN_COMMENT_HELP_TEXT = "Optional: Enter description of token" # noqa S105 - skips Ruff S105 error
+COMMAND_TOKEN_TOKEN_HELP_TEXT = "Token given by chat platform for signing or command validation" # noqa S105 - skips Ruff S105 error
CHATOPS_USER_ID_HELP_TEXT = "Enter the chat platform's User ID you want to link."
diff --git a/nautobot_chatops/dispatchers/adaptive_cards.py b/nautobot_chatops/dispatchers/adaptive_cards.py
index e1dd5ff3..acefefc1 100644
--- a/nautobot_chatops/dispatchers/adaptive_cards.py
+++ b/nautobot_chatops/dispatchers/adaptive_cards.py
@@ -1,4 +1,5 @@
"""Dispatcher subclass for chat platforms that use Adaptive Cards (https://adaptivecards.io/)."""
+
from .base import Dispatcher
# pylint: disable=abstract-method
@@ -152,9 +153,7 @@ def prompt_for_text(self, action_id, help_text, label, title="Your attention ple
blocks = [self.markdown_block(help_text), self.actions_block("TODO", [textentry, buttons])]
return self.send_blocks(blocks, ephemeral=True, title=title)
- def prompt_from_menu(
- self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0
- ): # pylint: disable=too-many-arguments
+ def prompt_from_menu(self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0): # noqa: PLR0913, pylint: disable=too-many-arguments
"""Prompt the user to make a selection from a menu of choices.
Args:
diff --git a/nautobot_chatops/dispatchers/base.py b/nautobot_chatops/dispatchers/base.py
index 2bbf4836..905e7244 100644
--- a/nautobot_chatops/dispatchers/base.py
+++ b/nautobot_chatops/dispatchers/base.py
@@ -1,17 +1,18 @@
"""Generic base class modeling the API for sending messages to a generic chat platform."""
+
import logging
from typing import Dict, Optional
-from django.templatetags.static import static
+
+from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist
-from django.conf import settings
+from django.templatetags.static import static
from nautobot.apps.config import get_app_settings_or_config
from texttable import Texttable
from nautobot_chatops.models import ChatOpsAccountLink
-
logger = logging.getLogger(__name__)
_APP_CONFIG: Dict = settings.PLUGINS_CONFIG["nautobot_chatops"]
@@ -118,16 +119,16 @@ def subclasses(cls):
# TODO: this should be dynamic using entry_points
# pylint: disable=import-outside-toplevel, unused-import, cyclic-import
if get_app_settings_or_config("nautobot_chatops", "enable_slack"):
- from .slack import SlackDispatcher
+ from .slack import SlackDispatcher # noqa: F401
if get_app_settings_or_config("nautobot_chatops", "enable_ms_teams"):
- from .ms_teams import MSTeamsDispatcher
+ from .ms_teams import MSTeamsDispatcher # noqa: F401
if get_app_settings_or_config("nautobot_chatops", "enable_webex"):
- from .webex import WebexDispatcher
+ from .webex import WebexDispatcher # noqa: F401
if get_app_settings_or_config("nautobot_chatops", "enable_mattermost"):
- from .mattermost import MattermostDispatcher
+ from .mattermost import MattermostDispatcher # noqa: F401
subclasses = set()
classes = [cls]
@@ -304,9 +305,7 @@ def prompt_for_text(self, action_id, help_text, label, title="Your attention ple
"""
raise NotImplementedError
- def prompt_from_menu(
- self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0
- ): # pylint: disable=too-many-arguments
+ def prompt_from_menu(self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0): # pylint: disable=too-many-arguments
"""Prompt the user to make a selection from a menu of choices.
Args:
diff --git a/nautobot_chatops/dispatchers/mattermost.py b/nautobot_chatops/dispatchers/mattermost.py
index 5303f2a1..12d752cb 100644
--- a/nautobot_chatops/dispatchers/mattermost.py
+++ b/nautobot_chatops/dispatchers/mattermost.py
@@ -3,13 +3,15 @@
import json
import logging
import time
+from http import HTTPStatus
from typing import Dict, Optional
-import requests
-from requests.exceptions import HTTPError
+import requests
from django.conf import settings
+from requests.exceptions import HTTPError
from nautobot_chatops.metrics import backend_action_sum
+
from .base import Dispatcher
logger = logging.getLogger(__name__)
@@ -77,33 +79,33 @@ def inner(*args, **kwargs): # pylint: disable=inconsistent-return-statements
try:
return function(*args, **kwargs)
except HTTPError as err:
- if err.response.status_code == 400:
+ if err.response.status_code == HTTPStatus.BAD_REQUEST:
raise BadRequestException(f"Malformatted requests: {err.response.text}")
- if err.response.status_code == 401:
+ if err.response.status_code == HTTPStatus.UNAUTHORIZED:
raise UnauthorizedException(f"Invalid credentials provided or account is locked: {err.response.text}")
- if err.response.status_code == 403:
+ if err.response.status_code == HTTPStatus.FORBIDDEN:
raise ForbiddenException(
f"Insufficient permissions to execute request (ie, any POST method as a regular user): {err.response.text}"
)
- if err.response.status_code == 404:
+ if err.response.status_code == HTTPStatus.NOT_FOUND:
raise NotFoundException(f"Attempting to access an endpoint that does not exist: {err.response.text}")
- if err.response.status_code == 405:
+ if err.response.status_code == HTTPStatus.METHOD_NOT_ALLOWED:
raise MethodNotAllowedException(
f"Wrong request type for target endpoint (ie, POSTing data to a GET endpoint): {err.response.text}"
)
- if err.response.status_code == 406:
+ if err.response.status_code == HTTPStatus.NOT_ACCEPTABLE:
raise NotAcceptableException(
f"Content Type of the data returned does not match the Accept header of the request: {err.response.text}"
)
- if err.response.status_code == 415:
+ if err.response.status_code == HTTPStatus.UNSUPPORTED_MEDIA_TYPE:
raise UnsupportedMediaTypeException(f"Attempting to POST data in incorrect format: {err.response.text}")
- if err.response.status_code == 429:
+ if err.response.status_code == HTTPStatus.TOO_MANY_REQUESTS:
raise MMRateLimit(
f"You have exceeded the max number of requests per 1-minute period: {err.response.text}"
)
- if err.response.status_code == 500:
+ if err.response.status_code == HTTPStatus.INTERNAL_SERVER_ERROR:
raise InternalServerErrorException(f"Contact support if you see this error type: {err.response.text}")
- if err.response.status_code == 503:
+ if err.response.status_code == HTTPStatus.SERVICE_UNAVAILABLE:
raise ServiceUnavailableException(
f"The Mattermost API is currently in maintenance mode: {err.response.text}"
)
@@ -493,9 +495,7 @@ def prompt_for_text(self, action_id, help_text, label, title="Your attention ple
# In Mattermost, a textentry element can ONLY be sent in a modal Interactive dialog
return self.send_blocks(blocks, callback_id=action_id, ephemeral=False, modal=True, title=title)
- def prompt_from_menu(
- self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0
- ): # pylint: disable=too-many-arguments
+ def prompt_from_menu(self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0): # pylint: disable=too-many-arguments
"""Prompt the user for a selection from a menu.
Args:
@@ -659,7 +659,7 @@ def select_element_interactive(self, action_id, choices, default=(None, None), c
Args:
action_id (str): Identifying string to associate with this element
choices (list): List of (display, value) tuples
- default (tuple: Default (display, value) to preselect
+ default (tuple): Default (display, value) to preselect
confirm (bool): If true (and the platform supports it), prompt the user to confirm their selection
optional (bool): If set to True, the field will return NoneType is not specified.
diff --git a/nautobot_chatops/dispatchers/ms_teams.py b/nautobot_chatops/dispatchers/ms_teams.py
index 563461fb..96a510a2 100644
--- a/nautobot_chatops/dispatchers/ms_teams.py
+++ b/nautobot_chatops/dispatchers/ms_teams.py
@@ -1,9 +1,10 @@
"""Dispatcher implementation for sending content to Microsoft Teams."""
-import os
+
import logging
+import os
from typing import Optional
-import requests
+import requests
from django.conf import settings
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
@@ -11,6 +12,7 @@
from nautobot_chatops.metrics import backend_action_sum
from nautobot_chatops.models import ChatOpsAccountLink
+
from .adaptive_cards import AdaptiveCardsDispatcher
logger = logging.getLogger(__name__)
diff --git a/nautobot_chatops/dispatchers/slack.py b/nautobot_chatops/dispatchers/slack.py
index 82ce52fc..b2507f61 100644
--- a/nautobot_chatops/dispatchers/slack.py
+++ b/nautobot_chatops/dispatchers/slack.py
@@ -4,15 +4,16 @@
import logging
import os
import time
-
from typing import Optional
+
from django.conf import settings
from django.templatetags.static import static
from slack_sdk import WebClient
-from slack_sdk.webhook.client import WebhookClient
from slack_sdk.errors import SlackApiError, SlackClientError
+from slack_sdk.webhook.client import WebhookClient
from nautobot_chatops.metrics import backend_action_sum
+
from .base import Dispatcher
logger = logging.getLogger(__name__)
@@ -371,9 +372,7 @@ def get_prompt_from_menu_choices(self, choices, offset=0):
choices.append(("Next...", f"menu_offset-{new_offset}"))
return choices
- def prompt_from_menu(
- self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0
- ): # pylint: disable=too-many-arguments
+ def prompt_from_menu(self, action_id, help_text, choices, default=(None, None), confirm=False, offset=0): # pylint: disable=too-many-arguments
"""Prompt the user for a selection from a menu.
Args:
diff --git a/nautobot_chatops/dispatchers/webex.py b/nautobot_chatops/dispatchers/webex.py
index 3bc7eb9e..34904b2a 100644
--- a/nautobot_chatops/dispatchers/webex.py
+++ b/nautobot_chatops/dispatchers/webex.py
@@ -1,10 +1,11 @@
"""Dispatcher implementation for sending content to Webex."""
-import logging
+import logging
from typing import Optional
+
+from texttable import Texttable
from webexteamssdk import WebexTeamsAPI
from webexteamssdk.exceptions import ApiError
-from texttable import Texttable
from nautobot_chatops.metrics import backend_action_sum
from nautobot_chatops.utils import get_app_config_part
diff --git a/nautobot_chatops/filters.py b/nautobot_chatops/filters.py
index 9ebd5f4f..040783ab 100644
--- a/nautobot_chatops/filters.py
+++ b/nautobot_chatops/filters.py
@@ -4,7 +4,7 @@
from nautobot.apps.filters import BaseFilterSet, NautobotFilterSet, SearchFilter
from nautobot_chatops.choices import PlatformChoices
-from nautobot_chatops.models import CommandLog, AccessGrant, ChatOpsAccountLink, CommandToken
+from nautobot_chatops.models import AccessGrant, ChatOpsAccountLink, CommandLog, CommandToken
class CommandLogFilterSet(BaseFilterSet):
diff --git a/nautobot_chatops/forms.py b/nautobot_chatops/forms.py
index e055dae8..0f1028e3 100644
--- a/nautobot_chatops/forms.py
+++ b/nautobot_chatops/forms.py
@@ -1,13 +1,12 @@
"""Forms for Nautobot."""
from django import forms
-
from nautobot.core.forms import BootstrapMixin, StaticSelect2Multiple
from nautobot.extras.forms import NautobotFilterForm
-from .models import AccessGrant, CommandLog, CommandToken, ChatOpsAccountLink
from .choices import AccessGrantTypeChoices, PlatformChoices
from .constants import ACCESS_GRANT_COMMAND_HELP_TEXT, COMMAND_TOKEN_TOKEN_HELP_TEXT
+from .models import AccessGrant, ChatOpsAccountLink, CommandLog, CommandToken
BLANK_CHOICE = (("", "--------"),)
diff --git a/nautobot_chatops/integrations/aci/aci.py b/nautobot_chatops/integrations/aci/aci.py
index 572401fa..adb5493b 100644
--- a/nautobot_chatops/integrations/aci/aci.py
+++ b/nautobot_chatops/integrations/aci/aci.py
@@ -1,14 +1,14 @@
"""All interactions with aci."""
-import sys
import logging
-from datetime import datetime
-from datetime import timedelta
import re
+import sys
+from datetime import datetime, timedelta
+
import requests
import urllib3
-from .utils import tenant_from_dn, ap_from_dn
+from .utils import ap_from_dn, tenant_from_dn
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
diff --git a/nautobot_chatops/integrations/aci/utils.py b/nautobot_chatops/integrations/aci/utils.py
index 64869bdb..07a83049 100644
--- a/nautobot_chatops/integrations/aci/utils.py
+++ b/nautobot_chatops/integrations/aci/utils.py
@@ -1,6 +1,8 @@
"""ACI ChatOps Utilities."""
+
import logging
import re
+
from prettytable import PrettyTable
logger = logging.getLogger("nautobot")
diff --git a/nautobot_chatops/integrations/aci/worker.py b/nautobot_chatops/integrations/aci/worker.py
index cb2652bf..bb74ca15 100644
--- a/nautobot_chatops/integrations/aci/worker.py
+++ b/nautobot_chatops/integrations/aci/worker.py
@@ -5,9 +5,10 @@
from nautobot.core.settings_funcs import is_truthy
from nautobot_chatops.choices import CommandStatusChoices
-from nautobot_chatops.workers import subcommand_of, handle_subcommands
-from .aci import RequestConnectError, RequestHTTPError, NautobotPluginChatopsAci
-from .utils import logger, send_logo, build_table, send_wait_msg
+from nautobot_chatops.workers import handle_subcommands, subcommand_of
+
+from .aci import NautobotPluginChatopsAci, RequestConnectError, RequestHTTPError
+from .utils import build_table, logger, send_logo, send_wait_msg
PLUGIN_SETTINGS = settings.PLUGINS_CONFIG["nautobot_chatops"]
diff --git a/nautobot_chatops/integrations/ansible/tower.py b/nautobot_chatops/integrations/ansible/tower.py
index a8d07914..02052ebd 100644
--- a/nautobot_chatops/integrations/ansible/tower.py
+++ b/nautobot_chatops/integrations/ansible/tower.py
@@ -1,10 +1,11 @@
"""All interactions with Ansible AWX/Tower."""
+
import json
import logging
from urllib.parse import urlparse
-from django.conf import settings
import requests
+from django.conf import settings
logger = logging.getLogger(__name__)
@@ -92,6 +93,7 @@ def _get_tower(self, api_path, **kwargs):
Args:
api_path (str): API path to get data from
+ **kwargs: Additional Keyword Arguments
Returns:
(JSON): JSON data for the response
@@ -154,7 +156,7 @@ def get_tower_inventory_hosts(self, group_id):
"""Gets hosts for a given Tower inventory.
Args:
- group (str): Group Name
+ group_id (str): Group Name
Returns:
(json): JSON data of the Tower hosts
diff --git a/nautobot_chatops/integrations/ansible/worker.py b/nautobot_chatops/integrations/ansible/worker.py
index 61c4509c..cd2af942 100644
--- a/nautobot_chatops/integrations/ansible/worker.py
+++ b/nautobot_chatops/integrations/ansible/worker.py
@@ -1,10 +1,12 @@
"""Worker functions implementing Nautobot "ansible" command and subcommands."""
+
import json
import logging
from collections import namedtuple
import yaml
from django.conf import settings
+
from nautobot_chatops.workers import handle_subcommands, subcommand_of
from .tower import Tower
diff --git a/nautobot_chatops/integrations/cloudvision/cvpgrpcutils.py b/nautobot_chatops/integrations/cloudvision/cvpgrpcutils.py
index 7a0ca77d..a53a9bf9 100644
--- a/nautobot_chatops/integrations/cloudvision/cvpgrpcutils.py
+++ b/nautobot_chatops/integrations/cloudvision/cvpgrpcutils.py
@@ -1,12 +1,13 @@
"""Utilities for using GRPC with Cloudvision Chatbot."""
+
import ssl
-import requests
-import grpc
import arista.tag.v1 as tag
+import grpc
+import requests
from google.protobuf import wrappers_pb2 as wrappers
-from .utils import CVAAS_ADDR
-from .utils import DEFAULT_TIMEOUT
+
+from .utils import CVAAS_ADDR, DEFAULT_TIMEOUT
def connect_cv(settings):
@@ -27,7 +28,7 @@ def connect_cv(settings):
response = requests.post(
f"https://{cvp_host}/cvpservice/login/authenticate.do",
auth=(username, password),
- verify=False, # nosec
+ verify=False, # noqa: S501
timeout=DEFAULT_TIMEOUT,
)
# Otherwise, the server is expected to have a valid certificate signed by a well-known CA.
diff --git a/nautobot_chatops/integrations/cloudvision/utils.py b/nautobot_chatops/integrations/cloudvision/utils.py
index 456987f3..38f5b28f 100644
--- a/nautobot_chatops/integrations/cloudvision/utils.py
+++ b/nautobot_chatops/integrations/cloudvision/utils.py
@@ -1,12 +1,14 @@
"""Utilities for cloudvision chatbot."""
+
import os
import ssl
from datetime import datetime
+
import requests
-from google.protobuf.timestamp_pb2 import Timestamp # pylint: disable=no-name-in-module
from cloudvision.Connector.grpc_client import GRPCClient, create_query
from cvprac.cvp_client import CvpClient
from django.conf import settings
+from google.protobuf.timestamp_pb2 import Timestamp # pylint: disable=no-name-in-module
fullpath = os.path.abspath(__file__)
directory = os.path.dirname(fullpath)
@@ -43,7 +45,7 @@ def get(name):
CVP_HOST = CONFIG["cvp_host"]
CVP_INSECURE = CONFIG["cvp_insecure"]
ON_PREM = CONFIG["on_prem"]
-CVP_TOKEN_PATH = "token.txt" # nosec
+CVP_TOKEN_PATH = "token.txt" # noqa: S105
CRT_FILE_PATH = "cvp.crt"
@@ -559,7 +561,7 @@ def get_token_crt():
request = requests.post(
f"https://{CVP_HOST}/cvpservice/login/authenticate.do",
auth=(CVP_USERNAME, CVP_PASSWORD),
- verify=False, # nosec
+ verify=False, # noqa: S501
timeout=DEFAULT_TIMEOUT,
)
else:
diff --git a/nautobot_chatops/integrations/cloudvision/worker.py b/nautobot_chatops/integrations/cloudvision/worker.py
index 27b50ddf..1d033cde 100644
--- a/nautobot_chatops/integrations/cloudvision/worker.py
+++ b/nautobot_chatops/integrations/cloudvision/worker.py
@@ -1,38 +1,40 @@
"""Cloudvision chatops."""
+
import logging
-from datetime import datetime, timedelta
import os
-from nautobot_chatops.workers import subcommand_of, handle_subcommands # pylint: disable=import-error
+from datetime import datetime, timedelta
+
from nautobot_chatops.choices import CommandStatusChoices # pylint: disable=import-error
+from nautobot_chatops.workers import handle_subcommands, subcommand_of # pylint: disable=import-error
+
from .cvpgrpcutils import get_device_tags
from .utils import (
CONFIG,
- prompt_for_events_filter,
- prompt_for_device_or_container,
+ get_active_events_data,
+ get_active_events_data_filter,
+ get_active_severity_types,
+ get_applied_configlets_container_id,
+ get_applied_configlets_device_id,
+ get_bug_device_report,
+ get_bug_info,
+ get_cloudvision_configlets_names,
get_cloudvision_container_devices,
get_cloudvision_containers,
- get_cloudvision_configlets_names,
- get_configlet_config,
get_cloudvision_devices_all,
get_cloudvision_devices_all_resource,
get_cloudvision_devices_by_sn,
- get_device_id_from_hostname,
- get_device_running_configuration,
- get_cloudvision_tasks,
get_cloudvision_task_logs,
+ get_cloudvision_tasks,
+ get_configlet_config,
get_container_id_by_name,
- get_applied_configlets_container_id,
- get_applied_configlets_device_id,
- get_severity_choices,
- get_active_events_data,
- get_active_events_data_filter,
- get_active_severity_types,
get_device_bugs_data,
- get_bug_info,
- get_bug_device_report,
+ get_device_id_from_hostname,
+ get_device_running_configuration,
+ get_severity_choices,
+ prompt_for_device_or_container,
+ prompt_for_events_filter,
)
-
logger = logging.getLogger(__name__)
dir_path = os.path.dirname(os.path.realpath(__file__))
CLOUDVISION_LOGO_PATH = "nautobot_cloudvision/cloudvision_logo.png"
@@ -53,13 +55,12 @@ def check_credentials(dispatcher):
"and ARISTACV_CVP_URL are set and your nautobot config file is updated."
)
return False
- else:
- if not CONFIG.get("cvaas_token"):
- dispatcher.send_warning(
- "Please ensure environment variable ARISTACV_CVAAS_TOKEN "
- "is set and your nautobot config file is updated."
- )
- return False
+ elif not CONFIG.get("cvaas_token"):
+ dispatcher.send_warning(
+ "Please ensure environment variable ARISTACV_CVAAS_TOKEN "
+ "is set and your nautobot config file is updated."
+ )
+ return False
return True
diff --git a/nautobot_chatops/integrations/grafana/api/urls.py b/nautobot_chatops/integrations/grafana/api/urls.py
index 2ccef219..ef337c21 100644
--- a/nautobot_chatops/integrations/grafana/api/urls.py
+++ b/nautobot_chatops/integrations/grafana/api/urls.py
@@ -5,7 +5,6 @@
from nautobot_chatops.integrations.grafana.api.views.generic import NautobotPluginChatopsGrafanaRootView
-
urlpatterns = []
if get_app_settings_or_config("nautobot_chatops", "enable_grafana"):
router = OrderedDefaultRouter()
diff --git a/nautobot_chatops/integrations/grafana/api/views/generic.py b/nautobot_chatops/integrations/grafana/api/views/generic.py
index da24c2cb..62d47b05 100644
--- a/nautobot_chatops/integrations/grafana/api/views/generic.py
+++ b/nautobot_chatops/integrations/grafana/api/views/generic.py
@@ -1,4 +1,5 @@
"""API Views for Nautobot App Chatops Grafana."""
+
from rest_framework.routers import APIRootView
diff --git a/nautobot_chatops/integrations/grafana/diffsync/models.py b/nautobot_chatops/integrations/grafana/diffsync/models.py
index 772d7f08..7f0180ec 100644
--- a/nautobot_chatops/integrations/grafana/diffsync/models.py
+++ b/nautobot_chatops/integrations/grafana/diffsync/models.py
@@ -1,8 +1,11 @@
"""DiffSync model definitions for Grafana Dashboards."""
-from typing import Optional, List
+
+from typing import List, Optional
+
from diffsync import DiffSync, DiffSyncModel
-from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
+
from nautobot_chatops.integrations.grafana.helpers import format_command
+from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
class DashboardModel(DiffSyncModel):
diff --git a/nautobot_chatops/integrations/grafana/diffsync/sync.py b/nautobot_chatops/integrations/grafana/diffsync/sync.py
index 45c1fc68..b7b00149 100644
--- a/nautobot_chatops/integrations/grafana/diffsync/sync.py
+++ b/nautobot_chatops/integrations/grafana/diffsync/sync.py
@@ -1,16 +1,19 @@
"""Synchronization functions for the implemented DiffSync models."""
+
from typing import Union
+
from diffsync import DiffSyncFlags
-from nautobot_chatops.integrations.grafana.grafana import handler
-from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
+
from nautobot_chatops.integrations.grafana.diffsync.models import (
- NautobotDashboard,
GrafanaDashboard,
- NautobotPanel,
GrafanaPanel,
- NautobotVariable,
GrafanaVariable,
+ NautobotDashboard,
+ NautobotPanel,
+ NautobotVariable,
)
+from nautobot_chatops.integrations.grafana.grafana import handler
+from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
def run_dashboard_sync(overwrite: bool = False) -> Union[str, None]:
diff --git a/nautobot_chatops/integrations/grafana/exceptions.py b/nautobot_chatops/integrations/grafana/exceptions.py
index 38975e7d..e3af5c9c 100644
--- a/nautobot_chatops/integrations/grafana/exceptions.py
+++ b/nautobot_chatops/integrations/grafana/exceptions.py
@@ -1,6 +1,7 @@
"""Nautobot App ChatOps Grafana Exceptions."""
-from pydantic import ValidationError
+
from isodate import ISO8601Error
+from pydantic import ValidationError
class DefaultArgsError(BaseException):
diff --git a/nautobot_chatops/integrations/grafana/filters.py b/nautobot_chatops/integrations/grafana/filters.py
index 51e4aa6b..d6fbb685 100644
--- a/nautobot_chatops/integrations/grafana/filters.py
+++ b/nautobot_chatops/integrations/grafana/filters.py
@@ -1,7 +1,7 @@
"""Filtering for nautobot_plugin_device_lifecycle_mgmt UI."""
-from django_filters import FilterSet, CharFilter
from django.db.models import Q
+from django_filters import CharFilter, FilterSet
from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
diff --git a/nautobot_chatops/integrations/grafana/forms.py b/nautobot_chatops/integrations/grafana/forms.py
index 0afcacf6..84f82b45 100644
--- a/nautobot_chatops/integrations/grafana/forms.py
+++ b/nautobot_chatops/integrations/grafana/forms.py
@@ -1,17 +1,18 @@
"""Forms for Nautobot."""
+from django.core.serializers.json import DjangoJSONEncoder
from django.forms import (
- ModelForm,
+ BooleanField,
CharField,
IntegerField,
- BooleanField,
JSONField,
ModelChoiceField,
+ ModelForm,
ModelMultipleChoiceField,
MultipleHiddenInput,
)
-from django.core.serializers.json import DjangoJSONEncoder
from nautobot.core.forms import BootstrapMixin, BulkEditForm
+
from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
diff --git a/nautobot_chatops/integrations/grafana/grafana.py b/nautobot_chatops/integrations/grafana/grafana.py
index 36dd280a..f7e8dee5 100644
--- a/nautobot_chatops/integrations/grafana/grafana.py
+++ b/nautobot_chatops/integrations/grafana/grafana.py
@@ -1,15 +1,18 @@
"""This module is intended to handle grafana requests generically perhaps outside of nautobot."""
+
import datetime
import logging
import urllib.parse
-from typing import Union, Tuple, List
-import requests
-import isodate
+from http import HTTPStatus
+from typing import List, Tuple, Union
+import isodate
+import requests
from django.conf import settings
from pydantic import BaseModel # pylint: disable=no-name-in-module
from requests.exceptions import RequestException
from typing_extensions import Literal
+
from nautobot_chatops.integrations.grafana.models import Panel, PanelVariable
LOGGER = logging.getLogger("nautobot.plugin.grafana")
@@ -203,7 +206,7 @@ def get_png(self, panel: Panel, panel_vars: List[PanelVariable]) -> Union[bytes,
LOGGER.error("An error occurred while accessing the url: %s Exception: %s", url, exc)
return None
- if results.status_code == 200:
+ if results.status_code == HTTPStatus.OK:
LOGGER.debug("Request returned %s", results.status_code)
return results.content
@@ -264,7 +267,7 @@ def get_dashboards(self) -> List[dict]:
LOGGER.error("An error occurred while accessing the url: %s Exception: %s", url, exc)
return []
- if results.status_code == 200:
+ if results.status_code == HTTPStatus.OK:
LOGGER.debug("Request returned %s", results.status_code)
return results.json()
@@ -289,7 +292,7 @@ def get_panels(self, dashboard_uid: str) -> List[dict]:
LOGGER.error("An error occurred while accessing the url: %s Exception: %s", url, exc)
return []
- if results.status_code != 200:
+ if results.status_code != HTTPStatus.OK:
LOGGER.error("Request returned %s for %s", results.status_code, url)
return []
@@ -323,7 +326,7 @@ def get_variables(self, dashboard_uid: str) -> List[dict]:
LOGGER.error("An error occurred while accessing the url: %s Exception: %s", url, exc)
return []
- if results.status_code != 200:
+ if results.status_code != HTTPStatus.OK:
LOGGER.error("Request returned %s for %s", results.status_code, url)
return []
diff --git a/nautobot_chatops/integrations/grafana/helpers.py b/nautobot_chatops/integrations/grafana/helpers.py
index 67e3f551..08d551f3 100644
--- a/nautobot_chatops/integrations/grafana/helpers.py
+++ b/nautobot_chatops/integrations/grafana/helpers.py
@@ -1,11 +1,12 @@
"""Schema Enforcer wrapper used to mimic the validate cli functionality."""
+
from typing import List
-from termcolor import colored
from schema_enforcer import config
-from schema_enforcer.schemas.manager import SchemaManager
-from schema_enforcer.instances.file import InstanceFileManager
from schema_enforcer.exceptions import InvalidJSONSchema
+from schema_enforcer.instances.file import InstanceFileManager
+from schema_enforcer.schemas.manager import SchemaManager
+from termcolor import colored
SPECIAL_CHAR = {
"%": "percent",
diff --git a/nautobot_chatops/integrations/grafana/management/commands/gen_panels_from_dashboard.py b/nautobot_chatops/integrations/grafana/management/commands/gen_panels_from_dashboard.py
index 1600cf6c..c3c7d573 100644
--- a/nautobot_chatops/integrations/grafana/management/commands/gen_panels_from_dashboard.py
+++ b/nautobot_chatops/integrations/grafana/management/commands/gen_panels_from_dashboard.py
@@ -1,9 +1,10 @@
"""Generate a panels.yml file using the json file retrieved from Grafana."""
+
import json
-import yaml
-from termcolor import colored
+import yaml
from django.core.management.base import BaseCommand
+from termcolor import colored
class Command(BaseCommand):
diff --git a/nautobot_chatops/integrations/grafana/management/commands/import_panels.py b/nautobot_chatops/integrations/grafana/management/commands/import_panels.py
index 77307798..ecbc7a2a 100644
--- a/nautobot_chatops/integrations/grafana/management/commands/import_panels.py
+++ b/nautobot_chatops/integrations/grafana/management/commands/import_panels.py
@@ -1,9 +1,10 @@
"""Import a panels.yml file into the Grafana object models in Nautobot."""
-import yaml
-from termcolor import colored
+import yaml
from django.core.management.base import BaseCommand
from pydantic import ValidationError
+from termcolor import colored
+
from nautobot_chatops.integrations.grafana.helpers import validate
from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
diff --git a/nautobot_chatops/integrations/grafana/management/commands/validate_schema.py b/nautobot_chatops/integrations/grafana/management/commands/validate_schema.py
index 02ada2ef..e50080dd 100644
--- a/nautobot_chatops/integrations/grafana/management/commands/validate_schema.py
+++ b/nautobot_chatops/integrations/grafana/management/commands/validate_schema.py
@@ -1,5 +1,7 @@
"""Nautobot Server CLI extension for Grafana ChatOps."""
+
from django.core.management.base import BaseCommand
+
from nautobot_chatops.integrations.grafana.helpers import validate
diff --git a/nautobot_chatops/integrations/grafana/models.py b/nautobot_chatops/integrations/grafana/models.py
index c16f462e..63261ec1 100644
--- a/nautobot_chatops/integrations/grafana/models.py
+++ b/nautobot_chatops/integrations/grafana/models.py
@@ -1,10 +1,11 @@
"""Models for Grafana Plugin."""
-from django.db import models
+
from django.core.exceptions import ValidationError
from django.core.serializers.json import DjangoJSONEncoder
+from django.db import models
from django.utils.translation import gettext_lazy as _
from nautobot.circuits import models as circuit_models
-from nautobot.core.models.generics import PrimaryModel, OrganizationalModel
+from nautobot.core.models.generics import OrganizationalModel, PrimaryModel
from nautobot.dcim import models as dcim_models
from nautobot.extras import models as extra_models
from nautobot.extras.utils import extras_features
@@ -12,7 +13,6 @@
from nautobot.tenancy import models as tenancy_models
from nautobot.virtualization import models as virtualization_models
-
# Valid models to be used in Panel Variables as query options. If a model doesn't exist in
# this list, you cannot set or use the `query` field in a panel variable.
VALID_MODELS = (
diff --git a/nautobot_chatops/integrations/grafana/navigation.py b/nautobot_chatops/integrations/grafana/navigation.py
index bb757d2f..922fbf3a 100644
--- a/nautobot_chatops/integrations/grafana/navigation.py
+++ b/nautobot_chatops/integrations/grafana/navigation.py
@@ -1,4 +1,5 @@
"""Navigation for Circuit Maintenance."""
+
from nautobot.apps.ui import NavMenuAddButton, NavMenuItem
items = [
diff --git a/nautobot_chatops/integrations/grafana/tables.py b/nautobot_chatops/integrations/grafana/tables.py
index a8f9ec67..7234f848 100644
--- a/nautobot_chatops/integrations/grafana/tables.py
+++ b/nautobot_chatops/integrations/grafana/tables.py
@@ -1,8 +1,9 @@
"""Django table classes for Nautobot."""
-from django_tables2 import TemplateColumn, Column, BooleanColumn
-from nautobot.core.tables import BaseTable, ToggleColumn, ButtonsColumn
-from nautobot_chatops.integrations.grafana.models import Panel, Dashboard, PanelVariable
+from django_tables2 import BooleanColumn, Column, TemplateColumn
+from nautobot.core.tables import BaseTable, ButtonsColumn, ToggleColumn
+
+from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
class DashboardViewTable(BaseTable):
diff --git a/nautobot_chatops/integrations/grafana/urls.py b/nautobot_chatops/integrations/grafana/urls.py
index 1f1f7b25..65a44008 100644
--- a/nautobot_chatops/integrations/grafana/urls.py
+++ b/nautobot_chatops/integrations/grafana/urls.py
@@ -1,32 +1,34 @@
"""Django urlpatterns declaration for nautobot_chatops.integrations.grafana app."""
+
from django.urls import path
from nautobot.extras.views import ObjectChangeLogView
-from nautobot_chatops.integrations.grafana.models import Dashboard, PanelVariable, Panel
+
+from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
from nautobot_chatops.integrations.grafana.views import (
+ DashboardBulkEditView,
Dashboards,
+ DashboardsBulkDeleteView,
+ DashboardsBulkImportView,
DashboardsCreate,
DashboardsDelete,
DashboardsEdit,
DashboardsSync,
- DashboardsBulkImportView,
- DashboardsBulkDeleteView,
- DashboardBulkEditView,
Panels,
+ PanelsBulkDeleteView,
+ PanelsBulkEditView,
+ PanelsBulkImportView,
PanelsCreate,
+ PanelsDelete,
PanelsEdit,
PanelsSync,
- PanelsDelete,
- PanelsBulkImportView,
- PanelsBulkDeleteView,
- PanelsBulkEditView,
Variables,
- VariablesCreate,
- VariablesSync,
- VariablesEdit,
- VariablesDelete,
- VariablesBulkImportView,
VariablesBulkDeleteView,
VariablesBulkEditView,
+ VariablesBulkImportView,
+ VariablesCreate,
+ VariablesDelete,
+ VariablesEdit,
+ VariablesSync,
)
urlpatterns = [
diff --git a/nautobot_chatops/integrations/grafana/views.py b/nautobot_chatops/integrations/grafana/views.py
index 09fbc5bc..8ebeb6a1 100644
--- a/nautobot_chatops/integrations/grafana/views.py
+++ b/nautobot_chatops/integrations/grafana/views.py
@@ -4,36 +4,37 @@
to send requests and notifications to.
"""
-from django.shortcuts import render, reverse, redirect
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
+from django.shortcuts import redirect, render, reverse
+from nautobot.core.forms import ConfirmationForm
from nautobot.core.views.generic import (
- ObjectEditView,
- ObjectDeleteView,
- ObjectListView,
- BulkImportView,
BulkDeleteView,
BulkEditView,
+ BulkImportView,
+ ObjectDeleteView,
+ ObjectEditView,
+ ObjectListView,
)
-from nautobot.core.forms import ConfirmationForm
+
from nautobot_chatops.integrations.grafana.diffsync.sync import run_dashboard_sync, run_panels_sync, run_variables_sync
-from nautobot_chatops.integrations.grafana.tables import PanelViewTable, DashboardViewTable, PanelVariableViewTable
-from nautobot_chatops.integrations.grafana.models import Panel, Dashboard, PanelVariable
-from nautobot_chatops.integrations.grafana.grafana import handler
from nautobot_chatops.integrations.grafana.filters import DashboardFilter, PanelFilter, VariableFilter
from nautobot_chatops.integrations.grafana.forms import (
- DashboardsForm,
- DashboardsFilterForm,
DashboardBulkEditForm,
+ DashboardsFilterForm,
+ DashboardsForm,
+ PanelsBulkEditForm,
+ PanelsFilterForm,
PanelsForm,
PanelsSyncForm,
- PanelsFilterForm,
- PanelsBulkEditForm,
+ PanelVariablesBulkEditForm,
+ PanelVariablesFilterForm,
PanelVariablesForm,
PanelVariablesSyncForm,
- PanelVariablesFilterForm,
- PanelVariablesBulkEditForm,
)
+from nautobot_chatops.integrations.grafana.grafana import handler
+from nautobot_chatops.integrations.grafana.models import Dashboard, Panel, PanelVariable
+from nautobot_chatops.integrations.grafana.tables import DashboardViewTable, PanelVariableViewTable, PanelViewTable
# -------------------------------------------------------------------------------------
# Dashboard Specific Views
diff --git a/nautobot_chatops/integrations/grafana/worker.py b/nautobot_chatops/integrations/grafana/worker.py
index 0becc1d6..2901df44 100644
--- a/nautobot_chatops/integrations/grafana/worker.py
+++ b/nautobot_chatops/integrations/grafana/worker.py
@@ -1,26 +1,29 @@
"""Worker function for /net commands in Slack."""
-import tempfile
+
import argparse
import os
+import tempfile
from datetime import datetime
-from typing import NoReturn, List, Union, Dict
+from typing import Dict, List, NoReturn, Union
+
+from django.core.exceptions import FieldError, MultipleObjectsReturned, ObjectDoesNotExist
from isodate import ISO8601Error, parse_duration
from jinja2 import Template
-from django.core.exceptions import FieldError, ObjectDoesNotExist, MultipleObjectsReturned
-from pydantic.error_wrappers import ValidationError # pylint: disable=no-name-in-module
from nautobot.core.models.querysets import RestrictedQuerySet
+from pydantic.error_wrappers import ValidationError # pylint: disable=no-name-in-module
+
from nautobot_chatops.dispatchers import Dispatcher
-from nautobot_chatops.workers import handle_subcommands, add_subcommand
-from nautobot_chatops.integrations.grafana.models import Panel, PanelVariable, VALID_MODELS
+from nautobot_chatops.integrations.grafana.exceptions import DefaultArgsError, MultipleOptionsError, PanelError
from nautobot_chatops.integrations.grafana.grafana import (
- SLASH_COMMAND,
- LOGGER,
- GRAFANA_LOGO_PATH,
GRAFANA_LOGO_ALT,
+ GRAFANA_LOGO_PATH,
+ LOGGER,
REQUEST_TIMEOUT_SEC,
+ SLASH_COMMAND,
handler,
)
-from nautobot_chatops.integrations.grafana.exceptions import DefaultArgsError, PanelError, MultipleOptionsError
+from nautobot_chatops.integrations.grafana.models import VALID_MODELS, Panel, PanelVariable
+from nautobot_chatops.workers import add_subcommand, handle_subcommands
def grafana_logo(dispatcher):
@@ -68,6 +71,7 @@ def chat_get_panel(dispatcher: Dispatcher, *args) -> bool: # pylint: disable=to
Args:
dispatcher (nautobot_chatops.dispatchers.Dispatcher): Abstracted dispatcher class for chat-ops.
+ *args: Grafana Panel Arguments.
Returns:
bool: ChatOps response pass or fail.
@@ -119,6 +123,7 @@ def chat_parse_args(panel_vars: List[PanelVariable], *args) -> Union[dict, bool]
Args:
panel_vars (List[nautobot_chatops.models.GrafanaPanelVariable]): List of PanelVariable objects.
+ *args: Grafana Panel arguments.
Returns:
parsed_args: dict of the arguments from the user's raw input
diff --git a/nautobot_chatops/integrations/ipfabric/context.py b/nautobot_chatops/integrations/ipfabric/context.py
index b8e2f38b..c99f715a 100644
--- a/nautobot_chatops/integrations/ipfabric/context.py
+++ b/nautobot_chatops/integrations/ipfabric/context.py
@@ -1,13 +1,17 @@
"""Functions for caching per-user context."""
+
import hashlib
+
from django.core.cache import cache
+
from nautobot_chatops import NautobotChatOpsConfig
def _get_cache_key(user: str) -> str:
"""Key generator for the cache, adding the app prefix name."""
key_string = "-".join([NautobotChatOpsConfig.name, user])
- return hashlib.md5(key_string.encode("utf-8")).hexdigest() # nosec
+ # Since Hashlib is only used for caching, this does not pose a security risk.
+ return hashlib.md5(key_string.encode("utf-8")).hexdigest() # noqa: S324
def get_context(user: str) -> dict:
diff --git a/nautobot_chatops/integrations/ipfabric/ipfabric_wrapper.py b/nautobot_chatops/integrations/ipfabric/ipfabric_wrapper.py
index d8194dcd..f34a4c81 100644
--- a/nautobot_chatops/integrations/ipfabric/ipfabric_wrapper.py
+++ b/nautobot_chatops/integrations/ipfabric/ipfabric_wrapper.py
@@ -2,9 +2,8 @@
import logging
-from ipfabric_diagrams import IPFDiagram
from ipfabric import IPFClient
-
+from ipfabric_diagrams import IPFDiagram
logger = logging.getLogger("nautobot")
diff --git a/nautobot_chatops/integrations/ipfabric/worker.py b/nautobot_chatops/integrations/ipfabric/worker.py
index 4957336d..59f3645a 100644
--- a/nautobot_chatops/integrations/ipfabric/worker.py
+++ b/nautobot_chatops/integrations/ipfabric/worker.py
@@ -1,7 +1,8 @@
"""Worker functions implementing Nautobot "ipfabric" command and subcommands.""" # pylint: disable=too-many-lines
+
import logging
-import tempfile
import os
+import tempfile
from datetime import datetime
from django.conf import settings
@@ -11,11 +12,10 @@
from pkg_resources import parse_version
from nautobot_chatops.choices import CommandStatusChoices
-from nautobot_chatops.workers import subcommand_of, handle_subcommands
-
-from .ipfabric_wrapper import IpFabric
+from nautobot_chatops.workers import handle_subcommands, subcommand_of
from .context import get_context, set_context
+from .ipfabric_wrapper import IpFabric
from .utils import parse_hosts
BASE_CMD = "ipfabric"
@@ -429,9 +429,7 @@ def get_int_drops(dispatcher, device, snapshot_id):
# PATH LOOKUP COMMMAND
-def submit_pathlookup(
- dispatcher, sub_cmd, src_ip, dst_ip, protocol, src_port=None, dst_port=None, icmp_type=None
-): # pylint: disable=too-many-arguments, too-many-locals
+def submit_pathlookup(dispatcher, sub_cmd, src_ip, dst_ip, protocol, src_port=None, dst_port=None, icmp_type=None): # pylint: disable=too-many-arguments, too-many-locals
"""Path simulation diagram lookup between source and target IP address."""
snapshot_id = get_user_snapshot(dispatcher)
# diagrams for 4.0 - 4.2 are not supported due to attribute changes in 4.3+
@@ -486,9 +484,7 @@ def submit_pathlookup(
@subcommand_of("ipfabric")
-def pathlookup(
- dispatcher, src_ip, dst_ip, src_port, dst_port, protocol
-): # pylint: disable=too-many-arguments, too-many-locals
+def pathlookup(dispatcher, src_ip, dst_ip, src_port, dst_port, protocol): # pylint: disable=too-many-arguments, too-many-locals
"""Path simulation diagram lookup between source and target IP address."""
sub_cmd = "pathlookup"
supported_protocols = ["tcp", "udp"]
@@ -976,9 +972,7 @@ def find_host(dispatcher, filter_key=None, filter_value=None):
@subcommand_of("ipfabric")
-def table_diff(
- dispatcher, category, table, view, snapshot
-): # pylint: disable=too-many-return-statements, too-many-branches
+def table_diff(dispatcher, category, table, view, snapshot): # pylint: disable=too-many-return-statements, too-many-branches
"""Get difference of a table between the current snapshot and the specified snapshot."""
sub_cmd = "table-diff"
diff --git a/nautobot_chatops/integrations/meraki/worker.py b/nautobot_chatops/integrations/meraki/worker.py
index 6d08f2c2..6c2133b8 100644
--- a/nautobot_chatops/integrations/meraki/worker.py
+++ b/nautobot_chatops/integrations/meraki/worker.py
@@ -1,14 +1,15 @@
"""Demo meraki addition to Nautobot."""
-import os
+
import logging
+import os
from django.conf import settings
-from nautobot_chatops.workers import subcommand_of, handle_subcommands
+
from nautobot_chatops.choices import CommandStatusChoices
+from nautobot_chatops.workers import handle_subcommands, subcommand_of
from .utils import MerakiClient
-
MERAKI_LOGO_PATH = "nautobot_meraki/meraki.png"
MERAKI_LOGO_ALT = "Meraki Logo"
diff --git a/nautobot_chatops/integrations/nso/jinja_filters.py b/nautobot_chatops/integrations/nso/jinja_filters.py
index 50688bf0..cf8cbfb5 100644
--- a/nautobot_chatops/integrations/nso/jinja_filters.py
+++ b/nautobot_chatops/integrations/nso/jinja_filters.py
@@ -1,5 +1,7 @@
"""Custom filters for nautobot_chatops.integrations.nso."""
+
from django_jinja import library
+
from nautobot_chatops.integrations.nso.nso import NSOClient
diff --git a/nautobot_chatops/integrations/nso/nso.py b/nautobot_chatops/integrations/nso/nso.py
index a4a9b77c..d67b4556 100644
--- a/nautobot_chatops/integrations/nso/nso.py
+++ b/nautobot_chatops/integrations/nso/nso.py
@@ -1,18 +1,18 @@
"""All interactions with nso."""
import logging
+
import requests
from django.conf import settings
from rest_framework import status
from nautobot_chatops.integrations.nso.exceptions import (
CommunicationError,
+ DeviceLocked,
DeviceNotFound,
DeviceNotSupported,
- DeviceLocked,
)
-
logger = logging.getLogger("nautobot.plugin.nso")
# Import config vars from nautobot_config.py
diff --git a/nautobot_chatops/integrations/nso/worker.py b/nautobot_chatops/integrations/nso/worker.py
index 2da09bb2..291afd76 100644
--- a/nautobot_chatops/integrations/nso/worker.py
+++ b/nautobot_chatops/integrations/nso/worker.py
@@ -1,11 +1,12 @@
"""Worker functions implementing Nautobot "nso" command and subcommands."""
+
from django.core.exceptions import ObjectDoesNotExist
-from nautobot.dcim.models import Device
from nautobot.core.settings_funcs import is_truthy
+from nautobot.dcim.models import Device
from nautobot_chatops.choices import CommandStatusChoices
+from nautobot_chatops.integrations.nso.nso import REQUEST_TIMEOUT_SEC, SLASH_COMMAND, NSOClient
from nautobot_chatops.workers import handle_subcommands, subcommand_of
-from nautobot_chatops.integrations.nso.nso import NSOClient, REQUEST_TIMEOUT_SEC, SLASH_COMMAND
def nso_logo(dispatcher):
diff --git a/nautobot_chatops/integrations/panorama/constant.py b/nautobot_chatops/integrations/panorama/constant.py
index 1748f8f1..aa269da7 100644
--- a/nautobot_chatops/integrations/panorama/constant.py
+++ b/nautobot_chatops/integrations/panorama/constant.py
@@ -1,4 +1,5 @@
"""Storage of data that will not change throughout the life cycle of application."""
+
from django.conf import settings
PLUGIN_CFG = settings.PLUGINS_CONFIG["nautobot_chatops"]
diff --git a/nautobot_chatops/integrations/panorama/jinja_filters.py b/nautobot_chatops/integrations/panorama/jinja_filters.py
index ac7798c9..c3d0df3f 100644
--- a/nautobot_chatops/integrations/panorama/jinja_filters.py
+++ b/nautobot_chatops/integrations/panorama/jinja_filters.py
@@ -1,4 +1,5 @@
"""Custom Django Jinja filters."""
+
from django_jinja import library
diff --git a/nautobot_chatops/integrations/panorama/utils.py b/nautobot_chatops/integrations/panorama/utils.py
index 4d82f8b3..a5aba962 100644
--- a/nautobot_chatops/integrations/panorama/utils.py
+++ b/nautobot_chatops/integrations/panorama/utils.py
@@ -1,20 +1,20 @@
"""Functions used for interacting with Panroama."""
+
import logging
import time
from typing import List
+
import defusedxml.ElementTree as ET
import requests
-
from netmiko import ConnectHandler
from panos.errors import PanDeviceXapiError
from panos.firewall import Firewall
-from panos.panorama import Panorama, DeviceGroup, PanoramaDeviceGroupHierarchy
+from panos.panorama import DeviceGroup, Panorama, PanoramaDeviceGroupHierarchy
from panos.policies import PostRulebase, PreRulebase, Rulebase, SecurityRule
from requests.exceptions import RequestException
from .constant import DEFAULT_TIMEOUT, PLUGIN_CFG
-
logger = logging.getLogger(__name__)
@@ -31,10 +31,11 @@ def get_api_key_api(url: str = PLUGIN_CFG["panorama_host"]) -> str:
params = {"type": "keygen", "user": PLUGIN_CFG["panorama_user"], "password": PLUGIN_CFG["panorama_password"]}
+ # TODO: The Verify option should be configurable.
response = requests.get(
f"https://{url}/api/",
params=params,
- verify=False, # nosec
+ verify=False, # noqa: S501
timeout=DEFAULT_TIMEOUT,
)
if response.status_code != 200:
@@ -82,6 +83,8 @@ def get_from_pano(connection: Panorama, devices: bool = False, groups: bool = Fa
Args:
connection (Panorama): Connection object to Panorama.
+ devices (bool): Get Devices from Panorama.
+ groups (bool): Get Groups from Panorama.
Returns:
dict: Dictionary of all devices attached to Panorama.
@@ -181,12 +184,12 @@ def start_packet_capture(capture_filename: str, ip_address: str, filters: dict):
if filters["dport"] and filters["dport"] != "any":
command += f" destination-port {filters['dport']}"
- if filters["dnet"] != "0.0.0.0": # nosec
+ if filters["dnet"] != "0.0.0.0": # noqa: S104
command += f" destination {filters['dnet']}"
if filters["dcidr"] != "0":
command += f" destination-netmask {filters['dcidr']}"
- if filters["snet"] != "0.0.0.0": # nosec
+ if filters["snet"] != "0.0.0.0": # noqa: S104
command += f" source {filters['snet']}"
if filters["scidr"] != "0":
command += f" source-netmask {filters['scidr']}"
@@ -223,10 +226,11 @@ def _get_pcap(capture_filename: str, ip_address: str):
params = {"key": get_api_key_api(), "type": "export", "category": "filters-pcap", "from": "1.pcap"}
+ # TODO: The Verify option should be configurable.
respone = requests.get(
url,
params=params,
- verify=False, # nosec
+ verify=False, # noqa S501 # nosec
timeout=DEFAULT_TIMEOUT,
)
diff --git a/nautobot_chatops/integrations/panorama/worker.py b/nautobot_chatops/integrations/panorama/worker.py
index 799dfa60..879ef69e 100644
--- a/nautobot_chatops/integrations/panorama/worker.py
+++ b/nautobot_chatops/integrations/panorama/worker.py
@@ -1,4 +1,5 @@
"""Example worker to handle /panorama chat commands with 1 subcommand addition."""
+
import logging
import os
import re
@@ -15,13 +16,13 @@
from nautobot_chatops.choices import CommandStatusChoices
from nautobot_chatops.integrations.panorama.utils import (
connect_panorama,
- get_from_pano,
- get_rule_match,
- start_packet_capture,
get_all_rules,
- split_rules,
+ get_from_pano,
get_object,
get_panorama_device_group_hierarchy,
+ get_rule_match,
+ split_rules,
+ start_packet_capture,
)
from nautobot_chatops.workers import handle_subcommands, subcommand_of
@@ -174,9 +175,7 @@ def get_devicegroups(dispatcher, **kwargs):
@subcommand_of("panorama")
-def validate_rule_exists(
- dispatcher, device, src_ip, dst_ip, protocol, dst_port
-): # pylint:disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
+def validate_rule_exists(dispatcher, device, src_ip, dst_ip, protocol, dst_port): # pylint:disable=too-many-arguments,too-many-locals,too-many-branches,too-many-statements
"""Verify that the rule exists within a device, via Panorama."""
dialog_list = [
{
@@ -574,6 +573,7 @@ def capture_traffic(
ip_proto (str): Protocol for destination port
stage (str): Stage to use
capture_seconds (str): Number of seconds to run packet capture
+ **kwargs: Any additional args
"""
logger.info("Starting capture_traffic()")
diff --git a/nautobot_chatops/integrations/utils.py b/nautobot_chatops/integrations/utils.py
index 0ec22c93..32b49c21 100644
--- a/nautobot_chatops/integrations/utils.py
+++ b/nautobot_chatops/integrations/utils.py
@@ -1,4 +1,5 @@
"""Utility functions for nautobot_chatops integrations."""
+
from importlib import import_module
from pathlib import Path
from types import ModuleType
diff --git a/nautobot_chatops/management/commands/start_slack_socket.py b/nautobot_chatops/management/commands/start_slack_socket.py
index 0c8fb3f2..5a029488 100644
--- a/nautobot_chatops/management/commands/start_slack_socket.py
+++ b/nautobot_chatops/management/commands/start_slack_socket.py
@@ -1,7 +1,9 @@
"""Command to start a slack socket."""
import asyncio
+
from django.core.management.base import BaseCommand
+
from nautobot_chatops.sockets.slack import main
diff --git a/nautobot_chatops/migrations/0001_initial.py b/nautobot_chatops/migrations/0001_initial.py
index e2d836c3..e6b79eee 100644
--- a/nautobot_chatops/migrations/0001_initial.py
+++ b/nautobot_chatops/migrations/0001_initial.py
@@ -1,9 +1,10 @@
# Generated by Django 3.1.3 on 2021-02-22 02:18
+import uuid
+
import django.contrib.postgres.fields
-from django.db import migrations, models, connection
import nautobot.core.models.fields
-import uuid
+from django.db import connection, migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_chatops/migrations/0002_commandlog_params1.py b/nautobot_chatops/migrations/0002_commandlog_params1.py
index 9e8789ed..ae7e5fbd 100644
--- a/nautobot_chatops/migrations/0002_commandlog_params1.py
+++ b/nautobot_chatops/migrations/0002_commandlog_params1.py
@@ -1,7 +1,6 @@
# Generated by Django 3.1.12 on 2021-08-14 02:29
from django.db import migrations, models
-import nautobot_chatops.models
class Migration(migrations.Migration):
diff --git a/nautobot_chatops/migrations/0003_params_to_params1.py b/nautobot_chatops/migrations/0003_params_to_params1.py
index aae448b0..5001056d 100644
--- a/nautobot_chatops/migrations/0003_params_to_params1.py
+++ b/nautobot_chatops/migrations/0003_params_to_params1.py
@@ -1,7 +1,6 @@
import copy
-import json
-from django.db import migrations, connection
+from django.db import connection, migrations
def migrate_params(apps, schema_editor):
diff --git a/nautobot_chatops/migrations/0004_remove_params_rename_params1.py b/nautobot_chatops/migrations/0004_remove_params_rename_params1.py
index 6d9d36a8..f8e2d17e 100644
--- a/nautobot_chatops/migrations/0004_remove_params_rename_params1.py
+++ b/nautobot_chatops/migrations/0004_remove_params_rename_params1.py
@@ -1,4 +1,4 @@
-from django.db import migrations, connection
+from django.db import connection, migrations
class Migration(migrations.Migration):
diff --git a/nautobot_chatops/migrations/0005_grafana.py b/nautobot_chatops/migrations/0005_grafana.py
index 96479648..db389724 100644
--- a/nautobot_chatops/migrations/0005_grafana.py
+++ b/nautobot_chatops/migrations/0005_grafana.py
@@ -1,13 +1,14 @@
# Generated by Django 3.2.15 on 2023-05-19 11:57
+import uuid
+
import django.core.serializers.json
-from django.db import migrations, models
import django.db.models.deletion
import nautobot.extras.models.mixins
import taggit.managers
-import uuid
-from django.db.migrations.recorder import MigrationRecorder
from django.contrib.contenttypes.models import ContentType
+from django.db import migrations, models
+from django.db.migrations.recorder import MigrationRecorder
_APP_LABEL = "nautobot_chatops"
_OLD_APP_LABEL = "nautobot_plugin_chatops_grafana"
@@ -22,7 +23,7 @@ def _copy(model_name, apps, connection):
with connection.cursor() as cursor:
# Table names are from trusted source (this script)
- cursor.execute(f"INSERT INTO {new_table_name} SELECT * FROM {old_table_name};") # nosec
+ cursor.execute(f"INSERT INTO {new_table_name} SELECT * FROM {old_table_name};") # noqa: S608
# Update the content type to point to the new model
old_content_type = ContentType.objects.get(app_label=_OLD_APP_LABEL, model=model_name.lower())
diff --git a/nautobot_chatops/migrations/0006_nautobot_2.py b/nautobot_chatops/migrations/0006_nautobot_2.py
index ce19a15c..fd253bcc 100644
--- a/nautobot_chatops/migrations/0006_nautobot_2.py
+++ b/nautobot_chatops/migrations/0006_nautobot_2.py
@@ -1,8 +1,8 @@
# Generated by Django 3.2.20 on 2023-08-25 16:40
import django.core.serializers.json
-from django.db import migrations, models
import nautobot.core.models.fields
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_chatops/migrations/0007_account_link.py b/nautobot_chatops/migrations/0007_account_link.py
index ca7f06bd..f3f5e380 100644
--- a/nautobot_chatops/migrations/0007_account_link.py
+++ b/nautobot_chatops/migrations/0007_account_link.py
@@ -1,12 +1,13 @@
# Generated by Django 3.2.20 on 2023-09-14 17:34
-from django.conf import settings
+import uuid
+
import django.core.serializers.json
-from django.db import migrations, models
import django.db.models.deletion
import nautobot.core.models.fields
import nautobot.extras.models.mixins
-import uuid
+from django.conf import settings
+from django.db import migrations, models
class Migration(migrations.Migration):
diff --git a/nautobot_chatops/models.py b/nautobot_chatops/models.py
index b6b557b9..d9d29ffc 100644
--- a/nautobot_chatops/models.py
+++ b/nautobot_chatops/models.py
@@ -4,30 +4,28 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
-
from nautobot.core.models.fields import ColorField
from nautobot.core.models.generics import PrimaryModel
-from .choices import AccessGrantTypeChoices, CommandStatusChoices, PlatformChoices
-
-from .integrations.grafana.models import Dashboard as GrafanaDashboard
-from .integrations.grafana.models import Panel as GrafanaPanel
-from .integrations.grafana.models import PanelVariable as GrafanaPanelVariable
+from .choices import AccessGrantTypeChoices, CommandStatusChoices, PlatformChoices
from .constants import (
- COMMAND_LOG_USER_NAME_HELP_TEXT,
- COMMAND_LOG_USER_ID_HELP_TEXT,
- COMMAND_LOG_PLATFORM_HELP_TEXT,
- COMMAND_LOG_COMMAND_TEXT,
- COMMAND_LOG_SUBCOMMAND_HELP_TEXT,
- COMMAND_LOG_PARAMS_HELP_TEXT,
ACCESS_GRANT_COMMAND_HELP_TEXT,
- ACCESS_GRANT_SUBCOMMAND_HELP_TEXT,
ACCESS_GRANT_NAME_HELP_TEXT,
+ ACCESS_GRANT_SUBCOMMAND_HELP_TEXT,
ACCESS_GRANT_VALUE_HELP_TEXT,
+ CHATOPS_USER_ID_HELP_TEXT,
+ COMMAND_LOG_COMMAND_TEXT,
+ COMMAND_LOG_PARAMS_HELP_TEXT,
+ COMMAND_LOG_PLATFORM_HELP_TEXT,
+ COMMAND_LOG_SUBCOMMAND_HELP_TEXT,
+ COMMAND_LOG_USER_ID_HELP_TEXT,
+ COMMAND_LOG_USER_NAME_HELP_TEXT,
COMMAND_TOKEN_COMMENT_HELP_TEXT,
COMMAND_TOKEN_TOKEN_HELP_TEXT,
- CHATOPS_USER_ID_HELP_TEXT,
)
+from .integrations.grafana.models import Dashboard as GrafanaDashboard
+from .integrations.grafana.models import Panel as GrafanaPanel
+from .integrations.grafana.models import PanelVariable as GrafanaPanelVariable
class CommandLog(PrimaryModel): # pylint: disable=nb-string-field-blank-null
diff --git a/nautobot_chatops/sockets/slack.py b/nautobot_chatops/sockets/slack.py
index 6d8dd1bb..e4482db7 100644
--- a/nautobot_chatops/sockets/slack.py
+++ b/nautobot_chatops/sockets/slack.py
@@ -6,13 +6,13 @@
from django.conf import settings
from slack_sdk.socket_mode.aiohttp import SocketModeClient
-from slack_sdk.socket_mode.response import SocketModeResponse
from slack_sdk.socket_mode.request import SocketModeRequest
+from slack_sdk.socket_mode.response import SocketModeResponse
from slack_sdk.web.async_client import AsyncWebClient
-from nautobot_chatops.workers import get_commands_registry, commands_help, parse_command_string
from nautobot_chatops.dispatchers.slack import SlackDispatcher
from nautobot_chatops.utils import socket_check_and_enqueue_command
+from nautobot_chatops.workers import commands_help, get_commands_registry, parse_command_string
# pylint: disable-next=too-many-statements
diff --git a/nautobot_chatops/tables.py b/nautobot_chatops/tables.py
index 18c20097..efdcd0fa 100644
--- a/nautobot_chatops/tables.py
+++ b/nautobot_chatops/tables.py
@@ -1,10 +1,9 @@
"""Django table classes for Nautobot."""
-from django_tables2 import TemplateColumn, LinkColumn
-
+from django_tables2 import LinkColumn, TemplateColumn
from nautobot.core.tables import BaseTable, ButtonsColumn, ToggleColumn
-from .models import CommandLog, AccessGrant, CommandToken, ChatOpsAccountLink
+from .models import AccessGrant, ChatOpsAccountLink, CommandLog, CommandToken
class CommandLogTable(BaseTable):
diff --git a/nautobot_chatops/tests/aci/test_aci.py b/nautobot_chatops/tests/aci/test_aci.py
index 1385399a..2f1dfb12 100644
--- a/nautobot_chatops/tests/aci/test_aci.py
+++ b/nautobot_chatops/tests/aci/test_aci.py
@@ -1,7 +1,9 @@
"""Tests for integrations.aci.aci."""
+
# pylint: disable=invalid-name
import unittest
-from unittest.mock import patch, Mock
+from unittest.mock import Mock, patch
+
from nautobot_chatops.integrations.aci.aci import NautobotPluginChatopsAci, RequestHTTPError
@@ -312,9 +314,7 @@ def test_get_static_path_negative(self, mocked_login, mocked_handle_request):
mock_response.ok = False
mocked_login.return_value = self.mock_login
mocked_handle_request.return_value = mock_response
- self.assertRaises(
- RequestHTTPError, self.aci_obj.get_static_path, "test-tenant-1", "test-ap", "test-epg"
- ) # nosec
+ self.assertRaises(RequestHTTPError, self.aci_obj.get_static_path, "test-tenant-1", "test-ap", "test-epg") # nosec
@patch.object(NautobotPluginChatopsAci, "get_static_path")
@patch.object(NautobotPluginChatopsAci, "get_contract_filters")
diff --git a/nautobot_chatops/tests/ansible/test_tower.py b/nautobot_chatops/tests/ansible/test_tower.py
index f8c28f0a..a28aeeb9 100644
--- a/nautobot_chatops/tests/ansible/test_tower.py
+++ b/nautobot_chatops/tests/ansible/test_tower.py
@@ -1,4 +1,5 @@
"""Test of tower.py."""
+
from collections import namedtuple
from os import path
diff --git a/nautobot_chatops/tests/aristacv/test_api.py b/nautobot_chatops/tests/aristacv/test_api.py
index a09083d2..4b58e0c2 100644
--- a/nautobot_chatops/tests/aristacv/test_api.py
+++ b/nautobot_chatops/tests/aristacv/test_api.py
@@ -1,12 +1,12 @@
"""Unit tests for Arista CloudVision integration API."""
+
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
+from nautobot.users.models import Token
from rest_framework import status
from rest_framework.test import APIClient
-from nautobot.users.models import Token
-
User = get_user_model()
diff --git a/nautobot_chatops/tests/grafana/test_api.py b/nautobot_chatops/tests/grafana/test_api.py
index 759571a0..4a5af59c 100644
--- a/nautobot_chatops/tests/grafana/test_api.py
+++ b/nautobot_chatops/tests/grafana/test_api.py
@@ -1,12 +1,12 @@
"""Unit tests for nautobot_chatops.integrations.grafana."""
+
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
+from nautobot.users.models import Token
from rest_framework import status
from rest_framework.test import APIClient
-from nautobot.users.models import Token
-
User = get_user_model()
diff --git a/nautobot_chatops/tests/grafana/test_workers.py b/nautobot_chatops/tests/grafana/test_workers.py
index 2707c037..1deef81a 100644
--- a/nautobot_chatops/tests/grafana/test_workers.py
+++ b/nautobot_chatops/tests/grafana/test_workers.py
@@ -1,13 +1,13 @@
"""Test cases for the Nautobot workers module."""
-from django.test import TestCase
+from django.test import TestCase
from prybar import dynamic_entrypoint
-from nautobot_chatops.workers import parse_command_string, get_commands_registry, add_subcommand
-from nautobot_chatops.tests.workers.dynamic_commands import dynamic_command, dynamic_subcommand
import nautobot_chatops.workers
+from nautobot_chatops.integrations.grafana.models import Dashboard, Panel
from nautobot_chatops.integrations.grafana.worker import initialize_subcommands
-from nautobot_chatops.integrations.grafana.models import Panel, Dashboard
+from nautobot_chatops.tests.workers.dynamic_commands import dynamic_command, dynamic_subcommand
+from nautobot_chatops.workers import add_subcommand, get_commands_registry, parse_command_string
class TestGrafana(TestCase):
diff --git a/nautobot_chatops/tests/ipfabric/test_api.py b/nautobot_chatops/tests/ipfabric/test_api.py
index d6638d9d..ca5b63ad 100644
--- a/nautobot_chatops/tests/ipfabric/test_api.py
+++ b/nautobot_chatops/tests/ipfabric/test_api.py
@@ -1,12 +1,12 @@
"""Unit tests for IP Fabric Integration."""
+
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse
+from nautobot.users.models import Token
from rest_framework import status
from rest_framework.test import APIClient
-from nautobot.users.models import Token
-
User = get_user_model()
diff --git a/nautobot_chatops/tests/meraki/test_utils.py b/nautobot_chatops/tests/meraki/test_utils.py
index 3a14a80b..32409eed 100644
--- a/nautobot_chatops/tests/meraki/test_utils.py
+++ b/nautobot_chatops/tests/meraki/test_utils.py
@@ -1,4 +1,5 @@
"""Test of utils.py."""
+
import unittest
from unittest.mock import patch
diff --git a/nautobot_chatops/tests/nso/test_nso.py b/nautobot_chatops/tests/nso/test_nso.py
index 995a787f..08c89d09 100644
--- a/nautobot_chatops/tests/nso/test_nso.py
+++ b/nautobot_chatops/tests/nso/test_nso.py
@@ -1,19 +1,19 @@
"""Test rundeck client."""
# Disable protected access, since..ya know. We need to test them. # pylint: disable=protected-access
-from os import path
import json
+from os import path
+
import responses
from django.test import SimpleTestCase
-from nautobot_chatops.integrations.nso.nso import NSOClient as nso
from nautobot_chatops.integrations.nso.exceptions import (
CommunicationError,
+ DeviceLocked,
DeviceNotFound,
DeviceNotSupported,
- DeviceLocked,
)
-
+from nautobot_chatops.integrations.nso.nso import NSOClient as nso
HERE = path.abspath(path.dirname(__file__))
diff --git a/nautobot_chatops/tests/panorama/test_utils_panorama.py b/nautobot_chatops/tests/panorama/test_utils_panorama.py
index 87416272..f68b8358 100644
--- a/nautobot_chatops/tests/panorama/test_utils_panorama.py
+++ b/nautobot_chatops/tests/panorama/test_utils_panorama.py
@@ -1,6 +1,9 @@
"""Unit tests for Panorama utility functions."""
+
from unittest.mock import MagicMock
+
from nautobot.core.testing import TestCase
+
from nautobot_chatops.integrations.panorama.utils import get_from_pano
diff --git a/nautobot_chatops/tests/test_api.py b/nautobot_chatops/tests/test_api.py
index 3113ce4e..f79f6294 100644
--- a/nautobot_chatops/tests/test_api.py
+++ b/nautobot_chatops/tests/test_api.py
@@ -1,11 +1,15 @@
-"""Test cases for Nautobot Chatops API."""
+"""Unit tests for nautobot_chatops."""
+
from importlib import metadata
+from django.contrib.auth import get_user_model
from django.urls import reverse
-
from nautobot.core.testing import APITestCase, APIViewTestCases
+
from nautobot_chatops.models import AccessGrant, CommandToken
+User = get_user_model()
+
nautobot_version = metadata.version("nautobot")
diff --git a/nautobot_chatops/tests/test_basic.py b/nautobot_chatops/tests/test_basic.py
index bcf7d12d..d72f2d02 100644
--- a/nautobot_chatops/tests/test_basic.py
+++ b/nautobot_chatops/tests/test_basic.py
@@ -1,6 +1,8 @@
"""Basic tests that do not require Django."""
-import unittest
+
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_chatops/tests/test_dispatchers.py b/nautobot_chatops/tests/test_dispatchers.py
index efea9bfb..d4c032ea 100644
--- a/nautobot_chatops/tests/test_dispatchers.py
+++ b/nautobot_chatops/tests/test_dispatchers.py
@@ -1,15 +1,15 @@
"""Tests for Nautobot dispatcher class implementations."""
-from unittest.mock import patch, MagicMock
+
+from unittest.mock import MagicMock, patch
from django.conf import settings
from django.test import TestCase
from slack_sdk.errors import SlackApiError
+from nautobot_chatops.dispatchers.mattermost import MattermostDispatcher
from nautobot_chatops.dispatchers.ms_teams import MSTeamsDispatcher
from nautobot_chatops.dispatchers.slack import SlackDispatcher
from nautobot_chatops.dispatchers.webex import WebexDispatcher
-from nautobot_chatops.dispatchers.mattermost import MattermostDispatcher
-
# pylint: disable=unnecessary-pass
diff --git a/nautobot_chatops/tests/test_utils.py b/nautobot_chatops/tests/test_utils.py
index 8d5276e2..66c592f7 100644
--- a/nautobot_chatops/tests/test_utils.py
+++ b/nautobot_chatops/tests/test_utils.py
@@ -9,7 +9,6 @@
from nautobot_chatops.models import AccessGrant
from nautobot_chatops.utils import check_and_enqueue_command
-
User = get_user_model()
diff --git a/nautobot_chatops/tests/test_views.py b/nautobot_chatops/tests/test_views.py
index 2297990a..8db45e02 100644
--- a/nautobot_chatops/tests/test_views.py
+++ b/nautobot_chatops/tests/test_views.py
@@ -5,21 +5,23 @@
from django.conf import settings
from django.test import TestCase
from django.test.client import RequestFactory
-
from nautobot.core.testing import ViewTestCases
+
+from nautobot_chatops.api.views.mattermost import verify_signature as mattermost_verify_signature
from nautobot_chatops.api.views.slack import (
- verify_signature as slack_verify_signature,
generate_signature as slack_generate_signature,
)
+from nautobot_chatops.api.views.slack import (
+ verify_signature as slack_verify_signature,
+)
from nautobot_chatops.api.views.webex import (
- verify_signature as webex_verify_signature,
generate_signature as webex_generate_signature,
)
-from nautobot_chatops.api.views.mattermost import verify_signature as mattermost_verify_signature
-from nautobot_chatops.choices import PlatformChoices
-from nautobot_chatops.models import CommandToken
-from nautobot_chatops.choices import CommandStatusChoices
-from nautobot_chatops.models import CommandLog
+from nautobot_chatops.api.views.webex import (
+ verify_signature as webex_verify_signature,
+)
+from nautobot_chatops.choices import CommandStatusChoices, PlatformChoices
+from nautobot_chatops.models import CommandLog, CommandToken
class TestSignatureVerification(TestCase):
diff --git a/nautobot_chatops/tests/test_workers.py b/nautobot_chatops/tests/test_workers.py
index 65120bfb..56bae1f7 100644
--- a/nautobot_chatops/tests/test_workers.py
+++ b/nautobot_chatops/tests/test_workers.py
@@ -1,11 +1,11 @@
"""Test cases for the Nautobot workers module."""
-from django.test import SimpleTestCase
+from django.test import SimpleTestCase
from prybar import dynamic_entrypoint
-from nautobot_chatops.workers import convert_smart_quotes, parse_command_string, get_commands_registry, add_subcommand
-from nautobot_chatops.tests.workers.dynamic_commands import dynamic_command, dynamic_subcommand
import nautobot_chatops.workers
+from nautobot_chatops.tests.workers.dynamic_commands import dynamic_command, dynamic_subcommand
+from nautobot_chatops.workers import add_subcommand, convert_smart_quotes, get_commands_registry, parse_command_string
class TestFunctions(SimpleTestCase):
@@ -128,11 +128,11 @@ def test_convert_smart_quotes(self):
"""Verify Convert Smart Quotes."""
self.assertEqual(convert_smart_quotes("''"), "''")
self.assertEqual(convert_smart_quotes(""), "")
- self.assertEqual(convert_smart_quotes("\u201C\u201D"), "''")
+ self.assertEqual(convert_smart_quotes("\u201c\u201d"), "''")
self.assertEqual(convert_smart_quotes("\u2018\u2019"), "''")
- self.assertEqual(convert_smart_quotes("\u201C"), "'")
- self.assertEqual(convert_smart_quotes("\u201D"), "'")
+ self.assertEqual(convert_smart_quotes("\u201c"), "'")
+ self.assertEqual(convert_smart_quotes("\u201d"), "'")
self.assertEqual(convert_smart_quotes("\u2018"), "'")
self.assertEqual(convert_smart_quotes("\u2019"), "'")
- self.assertEqual(convert_smart_quotes("\u201CLas Vegas\u201D"), "'Las Vegas'")
+ self.assertEqual(convert_smart_quotes("\u201cLas Vegas\u201d"), "'Las Vegas'")
self.assertEqual(convert_smart_quotes("\u2018Las Vegas\u2019"), "'Las Vegas'")
diff --git a/nautobot_chatops/tests/workers/dynamic_commands.py b/nautobot_chatops/tests/workers/dynamic_commands.py
index e2e286fc..433b046f 100644
--- a/nautobot_chatops/tests/workers/dynamic_commands.py
+++ b/nautobot_chatops/tests/workers/dynamic_commands.py
@@ -1,6 +1,6 @@
"""Test file defining a dynamic subcommand (issue #54)."""
-from nautobot_chatops.workers import subcommand_of, handle_subcommands
+from nautobot_chatops.workers import handle_subcommands, subcommand_of
def dynamic_command(subcommand, **kwargs):
diff --git a/nautobot_chatops/tests/workers/test_nautobot.py b/nautobot_chatops/tests/workers/test_nautobot.py
index f215a917..05e0cd40 100644
--- a/nautobot_chatops/tests/workers/test_nautobot.py
+++ b/nautobot_chatops/tests/workers/test_nautobot.py
@@ -1,13 +1,14 @@
"""Tests for the /nautobot chatops commands."""
+
from unittest.mock import MagicMock
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from nautobot.dcim.models import Location, LocationType
-from nautobot.ipam.models import VLAN
from nautobot.extras.models import Status
-from nautobot_chatops.choices import CommandStatusChoices
+from nautobot.ipam.models import VLAN
+from nautobot_chatops.choices import CommandStatusChoices
from nautobot_chatops.dispatchers import Dispatcher
from nautobot_chatops.workers.nautobot import get_vlans
diff --git a/nautobot_chatops/tests/workers/two_commands.py b/nautobot_chatops/tests/workers/two_commands.py
index 7d08e147..1e3b7e16 100644
--- a/nautobot_chatops/tests/workers/two_commands.py
+++ b/nautobot_chatops/tests/workers/two_commands.py
@@ -1,6 +1,6 @@
"""Test file defining two commands and their subcommands (issue #20)."""
-from nautobot_chatops.workers import subcommand_of, handle_subcommands
+from nautobot_chatops.workers import handle_subcommands, subcommand_of
def first_command(subcommand, **kwargs):
diff --git a/nautobot_chatops/urls.py b/nautobot_chatops/urls.py
index 53d9db14..a3e5a559 100644
--- a/nautobot_chatops/urls.py
+++ b/nautobot_chatops/urls.py
@@ -1,28 +1,28 @@
"""Django urlpatterns declaration for nautobot_chatops app."""
+
import logging
-from django.urls import path
from django.templatetags.static import static
+from django.urls import path
from django.views.generic import RedirectView
-
from nautobot.apps.config import get_app_settings_or_config
from nautobot.extras.views import ObjectChangeLogView, ObjectNotesView
from nautobot_chatops.models import AccessGrant, ChatOpsAccountLink, CommandLog, CommandToken
from nautobot_chatops.views import (
- CommandTokenBulkDeleteView,
- CommandTokenCreateView,
- CommandTokenListView,
- CommandTokenView,
- CommandLogListView,
+ AccessGrantBulkDeleteView,
+ AccessGrantCreateView,
AccessGrantListView,
AccessGrantView,
- AccessGrantCreateView,
- AccessGrantBulkDeleteView,
- ChatOpsAccountLinkView,
+ ChatOpsAccountLinkDeleteView,
ChatOpsAccountLinkEditView,
ChatOpsAccountLinkListView,
- ChatOpsAccountLinkDeleteView,
+ ChatOpsAccountLinkView,
+ CommandLogListView,
+ CommandTokenBulkDeleteView,
+ CommandTokenCreateView,
+ CommandTokenListView,
+ CommandTokenView,
)
if get_app_settings_or_config("nautobot_chatops", "enable_grafana"):
diff --git a/nautobot_chatops/utils.py b/nautobot_chatops/utils.py
index 4ad812f0..19d99915 100644
--- a/nautobot_chatops/utils.py
+++ b/nautobot_chatops/utils.py
@@ -1,8 +1,8 @@
"""Utility functions for API view implementations."""
-from datetime import datetime, timezone
import logging
import sys
+from datetime import datetime, timezone
from asgiref.sync import sync_to_async
from django.conf import settings
@@ -11,8 +11,8 @@
from nautobot.core.celery import nautobot_task
from nautobot_chatops.choices import AccessGrantTypeChoices, CommandStatusChoices
-from nautobot_chatops.models import AccessGrant, CommandLog
from nautobot_chatops.metrics import request_command_cntr
+from nautobot_chatops.models import AccessGrant, CommandLog
logger = logging.getLogger(__name__)
@@ -118,9 +118,7 @@ def socket_check_and_enqueue_command(*args, **kwargs):
return check_and_enqueue_command(*args, **kwargs)
-def check_and_enqueue_command(
- registry, command, subcommand, params, context, dispatcher_class
-): # pylint:disable=too-many-statements
+def check_and_enqueue_command(registry, command, subcommand, params, context, dispatcher_class): # noqa: PLR0915 pylint:disable=too-many-statements
"""Check whether the given command is permitted by any access grants, and enqueue it if permitted.
For a command/subcommand to be permitted, we must have:
diff --git a/nautobot_chatops/views.py b/nautobot_chatops/views.py
index b629d4c9..0bd8d947 100644
--- a/nautobot_chatops/views.py
+++ b/nautobot_chatops/views.py
@@ -6,20 +6,19 @@
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.shortcuts import render
-
from nautobot.core.forms import restrict_form_fields
from nautobot.core.utils.requests import normalize_querydict
-from nautobot.core.views.generic import BulkDeleteView, ObjectDeleteView, ObjectListView, ObjectEditView, ObjectView
+from nautobot.core.views.generic import BulkDeleteView, ObjectDeleteView, ObjectEditView, ObjectListView, ObjectView
-from nautobot_chatops.models import CommandLog, AccessGrant, CommandToken, ChatOpsAccountLink
+from nautobot_chatops import forms
from nautobot_chatops.filters import (
- CommandLogFilterSet,
AccessGrantFilterSet,
- CommandTokenFilterSet,
ChatOpsAccountLinkFilterSet,
+ CommandLogFilterSet,
+ CommandTokenFilterSet,
)
-from nautobot_chatops import forms
-from nautobot_chatops.tables import CommandLogTable, AccessGrantTable, CommandTokenTable, ChatOpsAccountLinkTable
+from nautobot_chatops.models import AccessGrant, ChatOpsAccountLink, CommandLog, CommandToken
+from nautobot_chatops.tables import AccessGrantTable, ChatOpsAccountLinkTable, CommandLogTable, CommandTokenTable
class CommandLogListView(PermissionRequiredMixin, ObjectListView):
diff --git a/nautobot_chatops/workers/__init__.py b/nautobot_chatops/workers/__init__.py
index 19bc4322..0685d3f9 100644
--- a/nautobot_chatops/workers/__init__.py
+++ b/nautobot_chatops/workers/__init__.py
@@ -6,21 +6,21 @@
back to the chat using the provided ``dispatchers`` instance's generic API.
"""
-from datetime import datetime, timezone
import inspect
-from functools import wraps
import logging
import shlex
-import pkg_resources
+from datetime import datetime, timezone
+from functools import wraps
+import pkg_resources
from django.conf import settings
from django.db.models import Q
from nautobot_chatops.choices import AccessGrantTypeChoices, CommandStatusChoices
from nautobot_chatops.integrations.utils import ALL_INTEGRATIONS, DISABLED_INTEGRATIONS
+from nautobot_chatops.metrics import command_histogram, request_command_cntr
from nautobot_chatops.models import AccessGrant
from nautobot_chatops.utils import create_command_log
-from nautobot_chatops.metrics import request_command_cntr, command_histogram
logger = logging.getLogger(__name__)
@@ -57,8 +57,8 @@
def get_commands_registry():
"""Populate and return the _commands_registry dictionary with all known commands, subcommands, and workers."""
- global _commands_registry # pylint: disable=global-statement,global-variable-not-assigned,invalid-name
- global _registry_initialized # pylint: disable=global-statement,invalid-name
+ global _commands_registry # pylint: disable=global-variable-not-assigned
+ global _registry_initialized # pylint: disable=global-statement
if _registry_initialized:
# Already populated, don't regenerate it
return _commands_registry
diff --git a/nautobot_chatops/workers/clear.py b/nautobot_chatops/workers/clear.py
index e7a34d55..70abf26e 100644
--- a/nautobot_chatops/workers/clear.py
+++ b/nautobot_chatops/workers/clear.py
@@ -2,7 +2,6 @@
from datetime import datetime, timezone
-
from nautobot_chatops.choices import CommandStatusChoices
from nautobot_chatops.dispatchers.slack import SlackDispatcher
from nautobot_chatops.utils import create_command_log
diff --git a/nautobot_chatops/workers/helper_functions.py b/nautobot_chatops/workers/helper_functions.py
index cf298103..d246af44 100644
--- a/nautobot_chatops/workers/helper_functions.py
+++ b/nautobot_chatops/workers/helper_functions.py
@@ -1,6 +1,5 @@
"""Helper functions for nautobot.py worker."""
-
NAUTOBOT_LOGO_PATH = "nautobot/NautobotLogoSquare.png"
NAUTOBOT_LOGO_ALT = "Nautobot Logo"
diff --git a/nautobot_chatops/workers/nautobot.py b/nautobot_chatops/workers/nautobot.py
index 11fc74c1..bc5e43ae 100644
--- a/nautobot_chatops/workers/nautobot.py
+++ b/nautobot_chatops/workers/nautobot.py
@@ -3,28 +3,26 @@
import json
import time
-
+from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db.models import Count
-from django.contrib.contenttypes.models import ContentType
from django.utils.text import slugify
-
-from nautobot.dcim.models.device_components import Interface, FrontPort, RearPort
-from nautobot.circuits.models import Circuit, CircuitType, Provider, CircuitTermination
-from nautobot.dcim.models import Device, DeviceType, Location, LocationType, Manufacturer, Rack, Cable
-from nautobot.ipam.models import VLAN, Prefix, VLANGroup
-from nautobot.tenancy.models import Tenant
+from nautobot.circuits.models import Circuit, CircuitTermination, CircuitType, Provider
+from nautobot.dcim.models import Cable, Device, DeviceType, Location, LocationType, Manufacturer, Rack
+from nautobot.dcim.models.device_components import FrontPort, Interface, RearPort
from nautobot.extras.choices import JobResultStatusChoices
-from nautobot.extras.models import Job, JobResult, Role, Status
from nautobot.extras.jobs import get_job
+from nautobot.extras.models import Job, JobResult, Role, Status
+from nautobot.ipam.models import VLAN, Prefix, VLANGroup
+from nautobot.tenancy.models import Tenant
from nautobot_chatops.choices import CommandStatusChoices
-from nautobot_chatops.workers import subcommand_of, handle_subcommands
+from nautobot_chatops.workers import handle_subcommands, subcommand_of
from nautobot_chatops.workers.helper_functions import (
add_asterisk,
+ menu_item_check,
menu_offset_value,
nautobot_logo,
- menu_item_check,
prompt_for_circuit_filter_type,
prompt_for_device_filter_type,
prompt_for_interface_filter_type,
@@ -135,11 +133,7 @@ def get_filtered_connections(device, interface):
status__name="Connected",
termination_a_type=interface.pk,
termination_b_type=interface.pk,
- ).exclude(
- _termination_b_device=None
- ).exclude(
- _termination_a_device=None
- )
+ ).exclude(_termination_b_device=None).exclude(_termination_a_device=None)
def analyze_circuit_endpoints(endpoint):
@@ -1056,6 +1050,7 @@ def filter_jobs(dispatcher, job_filters: str = ""): # We can use a Literal["ena
"""Get a filtered list of jobs from Nautobot that the request user have view permissions for.
Args:
+ dispatcher (object): Chatops app dispatcher object
job_filters (str): Filter job results by literals in a comma-separated string.
Available filters are: enabled, installed.
"""
@@ -1088,6 +1083,7 @@ def get_jobs(dispatcher, kwargs: str = ""):
"""Get all jobs from Nautobot that the requesting user have view permissions for.
Args:
+ dispatcher (object): Chatops app dispatcher object
kwargs (str): JSON-string array of header items to be exported. (Optional, default export is: name, id, enabled)
"""
# Confirm kwargs is valid JSON
@@ -1127,6 +1123,7 @@ def run_job(dispatcher, *args, job_name: str = "", json_string_kwargs: str = "")
"""Initiate a job in Nautobot by job name.
Args:
+ dispatcher (object): Chatops app dispatcher object
*args (tuple): Dispatcher form will pass job args as tuple.
job_name (str): Name of Nautobot job to run.
json_string_kwargs (str): JSON-string dictionary for input keyword arguments for job run.
@@ -1241,6 +1238,7 @@ def run_job_form(dispatcher, job_name: str = ""):
"""Send job form as a multi-input dialog. On form submit it initiates the job with the form arguments.
Args:
+ dispatcher (object): Chatops app dispatcher object
job_name (str): Name of Nautobot job to run.
"""
# Prompt the user to pick a job if they did not specify one
diff --git a/poetry.lock b/poetry.lock
index 892f8b60..97d1f07c 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -332,29 +332,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"
@@ -400,52 +377,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 = "bracex"
version = "2.4"
@@ -1420,22 +1351,6 @@ files = [
[package.extras]
tests = ["asttokens (>=2.1.0)", "coverage", "coverage-enable-subprocess", "ipython", "littleutils", "pytest", "rich"]
-[[package]]
-name = "flake8"
-version = "3.9.2"
-description = "the modular source code checker: pep8 pyflakes and co"
-optional = false
-python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
-files = [
- {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
- {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
-]
-
-[package.dependencies]
-mccabe = ">=0.6.0,<0.7.0"
-pycodestyle = ">=2.7.0,<2.8.0"
-pyflakes = ">=2.3.0,<2.4.0"
-
[[package]]
name = "fqdn"
version = "1.5.1"
@@ -2239,7 +2154,7 @@ testing = ["coverage", "pyyaml"]
name = "markdown-it-py"
version = "3.0.0"
description = "Python port of markdown-it. Markdown parsing, done right!"
-optional = false
+optional = true
python-versions = ">=3.8"
files = [
{file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"},
@@ -2259,6 +2174,20 @@ 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"]
+[[package]]
+name = "markdown-version-annotations"
+version = "1.0.1"
+description = "Markdown plugin to add custom admonitions for documenting version differences"
+optional = false
+python-versions = "<4.0,>=3.7"
+files = [
+ {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]
+markdown = ">=3.3.7,<4.0.0"
+
[[package]]
name = "markupsafe"
version = "2.1.3"
@@ -2357,7 +2286,7 @@ files = [
name = "mdurl"
version = "0.1.2"
description = "Markdown URL utilities"
-optional = false
+optional = true
python-versions = ">=3.7"
files = [
{file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"},
@@ -2487,17 +2416,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"
@@ -2687,17 +2605,6 @@ files = [
{file = "multidict-6.0.4.tar.gz", hash = "sha256:3666906492efb76453c0e7b97f2cf459b0682e7402c0489a95484965dbc1da49"},
]
-[[package]]
-name = "mypy-extensions"
-version = "1.0.0"
-description = "Type system extensions for programs checked with the mypy type checker."
-optional = false
-python-versions = ">=3.5"
-files = [
- {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"},
- {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"},
-]
-
[[package]]
name = "nautobot"
version = "2.0.0"
@@ -2945,17 +2852,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"
@@ -3345,17 +3241,6 @@ cffi = ">=1.5.0"
[package.extras]
idna = ["idna (>=2.1)"]
-[[package]]
-name = "pycodestyle"
-version = "2.7.0"
-description = "Python style guide checker"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"},
- {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"},
-]
-
[[package]]
name = "pycparser"
version = "2.21"
@@ -3419,17 +3304,6 @@ typing-extensions = ">=4.2.0"
dotenv = ["python-dotenv (>=0.10.4)"]
email = ["email-validator (>=1.0.3)"]
-[[package]]
-name = "pyflakes"
-version = "2.3.1"
-description = "passive checker of Python programs"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-files = [
- {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
- {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
-]
-
[[package]]
name = "pygments"
version = "2.16.1"
@@ -4117,7 +3991,7 @@ files = [
name = "rich"
version = "13.5.3"
description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal"
-optional = false
+optional = true
python-versions = ">=3.7.0"
files = [
{file = "rich-13.5.3-py3-none-any.whl", hash = "sha256:9257b468badc3d347e146a4faa268ff229039d4c2d176ab0cffb4c4fbc73d5d9"},
@@ -4198,28 +4072,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]]
@@ -4414,20 +4289,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 = "structlog"
version = "22.3.0"
@@ -4900,6 +4761,24 @@ files = [
{file = "wrapt-1.15.0.tar.gz", hash = "sha256:d06730c6aed78cee4126234cf2d071e01b44b915e725a6cb439a879ec9754a3a"},
]
+[[package]]
+name = "yamllint"
+version = "1.35.1"
+description = "A linter for YAML files."
+optional = false
+python-versions = ">=3.8"
+files = [
+ {file = "yamllint-1.35.1-py3-none-any.whl", hash = "sha256:2e16e504bb129ff515b37823b472750b36b6de07963bd74b307341ef5ad8bdc3"},
+ {file = "yamllint-1.35.1.tar.gz", hash = "sha256:7a003809f88324fd2c877734f2d575ee7881dd9043360657cc8049c809eba6cd"},
+]
+
+[package.dependencies]
+pathspec = ">=0.5.3"
+pyyaml = "*"
+
+[package.extras]
+dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"]
+
[[package]]
name = "yarl"
version = "1.9.2"
@@ -5016,4 +4895,4 @@ panorama = ["defusedxml", "ipaddr", "netmiko", "netutils", "pan-os-python"]
[metadata]
lock-version = "2.0"
python-versions = ">=3.8,<3.12"
-content-hash = "4a0e18446998244cdef542b78277503106fb706c3b2b7320d94ef011aa6c22cd"
+content-hash = "1dbdcfb76c1d1939ff17053c850c91e5386b606991c506de6414fcfc6a2eb459"
diff --git a/pyproject.toml b/pyproject.toml
index 58a6a322..b57ff6f2 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -72,18 +72,19 @@ texttable = "^1.6.2"
webexteamssdk = "^1.3"
[tool.poetry.group.dev.dependencies]
-Markdown = "*"
-bandit = "*"
-black = "*"
-coverage = "~5.4"
+coverage = "*"
django-debug-toolbar = "*"
-flake8 = "*"
invoke = "*"
ipython = "*"
+Markdown = "*"
+# Render custom markdown for version added/changed/remove notes
+markdown-version-annotations = "1.0.1"
+# Rendering docs to HTML
mkdocs = "1.5.2"
mkdocs-include-markdown-plugin = "6.0.3"
+# Material for MkDocs theme
mkdocs-material = "9.1.15"
-mkdocs-version-annotations = "1.0.0"
+# Automatic documentation from sources, for MkDocs
mkdocstrings = "0.22.0"
mkdocstrings-python = "1.5.2"
prybar = "*"
@@ -91,7 +92,8 @@ pylint = "*"
pylint-django = "*"
pylint-nautobot = "*"
requests-mock = "^1.9.3"
-ruff = "*"
+ruff = "0.5.5"
+yamllint = "*"
toml = "*"
towncrier = "~23.6.0"
to-json-schema = "*"
@@ -156,29 +158,6 @@ panorama = [
]
nautobot = ["nautobot"]
-[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
load-plugins="pylint_django, pylint_nautobot"
@@ -189,8 +168,6 @@ ignore=".venv"
no-docstring-rgx="^(_|test_|Meta$)"
[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",
"nb-incorrect-base-class",
@@ -223,6 +200,9 @@ target-version = "py38"
[tool.ruff.lint]
select = [
"D", # pydocstyle
+ "F", "E", "W", # flake8
+ "S", # bandit
+ "I", # isort
]
ignore = [
# warning: `one-blank-line-before-class` (D203) and `no-blank-line-before-class` (D211) are incompatible.
@@ -240,20 +220,19 @@ ignore = [
"D401", # First line of docstring should be in imperative mood
"D407", # Missing dashed underline after section
"D416", # Section name ends in colon
-
- # Package specific
- "D417", # Missing argument descriptions in Docstrings
+ "E501", # Line too long
]
[tool.ruff.lint.pydocstyle]
convention = "google"
-[tool.ruff.per-file-ignores]
+[tool.ruff.lint.per-file-ignores]
"nautobot_chatops/migrations/*" = [
- "D", # pydocstyle
+ "D",
]
"nautobot_chatops/tests/*" = [
- "D", # pydocstyle
+ "D",
+ "S"
]
[build-system]
diff --git a/tasks.py b/tasks.py
index 62dad57a..1d34c5e2 100644
--- a/tasks.py
+++ b/tasks.py
@@ -161,17 +161,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)
@@ -496,7 +496,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(
@@ -521,9 +526,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",
@@ -649,28 +656,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
@@ -690,38 +675,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
@@ -753,7 +739,7 @@ def check_migrations(context):
"verbose": "Enable verbose test output.",
}
)
-def unittest(
+def unittest( # noqa: PLR0913
context,
keepdb=False,
label="nautobot_chatops",
@@ -801,14 +787,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...")
@@ -841,14 +821,23 @@ 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"},
+ )
# ------------------------------------------------------------------------------
@@ -888,13 +877,10 @@ def connect_awx_container(context, container_name="tools_awx_1"):
"""Connect nautobot and celery containers to awx container.
Bridge network is defined in `development/ansible/docker-compose.yaml`.
-
To run testing awx instance, follow [instructions]
(https://github.com/ansible/awx/tree/devel/tools/docker-compose#getting-started)
-
Before running `make docker-compose` comment out `- 8080:8080` port mapping in file
`tools/docker-compose/ansible/roles/sources/templates/docker-compose.yml.j2` to avoid port conflict with nautobot.
-
After setting up awx, cd back to chatops repo and run `invoke connect-awx-container`.
"""
bridge_network = f"{context.nautobot_chatops.project_name}_awx"