diff --git a/.bandit.yml b/.bandit.yml index 4824c564..56f7a83b 100644 --- a/.bandit.yml +++ b/.bandit.yml @@ -1,7 +1,6 @@ --- +skips: [] # No need to check for security issues in the test scripts! exclude_dirs: - - "./nautobot_chatops/tests/" - -skips: - - "B404" + - "./tests/" + - "./.venv/" diff --git a/.cookiecutter.json b/.cookiecutter.json new file mode 100644 index 00000000..c67312da --- /dev/null +++ b/.cookiecutter.json @@ -0,0 +1,35 @@ +{ + "cookiecutter": { + "codeowner_github_usernames": "@nautobot/plugin-chatops", + "full_name": "Network to Code, LLC", + "email": "opensource@networktocode.com", + "github_org": "nautobot", + "plugin_name": "nautobot_chatops", + "verbose_name": "Nautobot ChatOps App", + "plugin_slug": "nautobot-chatops", + "project_slug": "nautobot-plugin-chatops", + "repo_url": "https://github.com/nautobot/nautobot-plugin-chatops/", + "base_url": "chatops", + "min_nautobot_version": "2.0.0", + "max_nautobot_version": "2.9999", + "camel_name": "NautobotChatOpsPlugin", + "project_short_description": "Nautobot ChatOps App", + "model_class_name": "None", + "open_source_license": "Apache-2.0", + "docs_base_url": "https://docs.nautobot.com", + "docs_app_url": "https://docs.nautobot.com/projects/chatops/en/latest", + "_drift_manager": { + "template": "https://github.com/nautobot/cookiecutter-nautobot-app.git", + "template_dir": "nautobot-app", + "template_ref": "develop", + "cookie_dir": "", + "branch_prefix": "drift-manager", + "pull_request_strategy": "create", + "post_actions": [ + "black" + ], + "draft": true, + "baked_commit_ref": "f7199da166a77ef00af4347535a1e09f939a711a" + } + } +} diff --git a/.dockerignore b/.dockerignore index aacfd4e3..2270f496 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,11 +1,27 @@ +# Docker related +development/Dockerfile +development/docker-compose*.yml +development/*.env +*.env +environments/ + +# Python **/*.pyc **/*.pyo -**/*.log +**/__pycache__/ +**/.pytest_cache/ +**/.venv/ + + +# Other +docs/_build +FAQ.md .git/ -.github/ .gitignore -Dockerfile -docker-compose.yml -.env -docs/_build -__pycache__/ \ No newline at end of file +.github +tasks.py +LICENSE +**/*.log +**/.vscode/ +invoke*.yml +tasks.py diff --git a/.flake8 b/.flake8 index 8193a5d0..c9f5e84d 100644 --- a/.flake8 +++ b/.flake8 @@ -1,4 +1,10 @@ [flake8] -# E501: Line length is enforced by Black, so flake8 doesn't need to check it -# W503: Black disagrees with this rule, as does PEP 8; Black wins -ignore = E501, W503, F811, F401 \ No newline at end of file +ignore = + E501, # Line length is enforced by Black, so flake8 doesn't need to check it + W503 # Black disagrees with this rule, as does PEP 8; Black wins +exclude = + migrations, + __pycache__, + manage.py, + settings.py, + .venv diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 5f4290b0..6592e35a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,2 +1,2 @@ -# Default owners for all files in this repository -* @glennmatthews @jvanderaa @smk4664 @whitej6 +# Default owner(s) of all files in this repository +* @nautobot/plugin-chatops diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 2a57e1af..93878502 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,12 +1,19 @@ --- name: 🐛 Bug Report -about: Report a reproducible bug in the current release of nautobot-plugin-chatops +about: Report a reproducible bug in the current release of nautobot-chatops --- ### Environment -* Python version: -* Nautobot version: -* nautobot-plugin-chatops version: +* Python version: +* Nautobot version: +* nautobot-chatops version: + + +### Expected Behavior + + + +### Observed Behavior -### Expected Behavior - - - -### Observed Behavior \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbeb5de2..2324a93a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -5,8 +5,8 @@ about: Propose a new feature or enhancement --- ### Environment -* Nautobot version: -* nautobot-plugin-chatops version: +* Nautobot version: +* nautobot-chatops version: + +# Closes: # + +## What's Changed + + + +## To Do + + +- [ ] Explanation of Change(s) +- [ ] Added change log fragment(s) (for more information see [the documentation](https://docs.nautobot.com/projects/core/en/stable/development/#creating-changelog-fragments)) +- [ ] Attached Screenshots, Payload Example +- [ ] Unit, Integration Tests +- [ ] Documentation Updates (when adding/changing features) +- [ ] Example Plugin Updates (when adding/changing features) +- [ ] Outline Remaining Work, Constraints from Design diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59d01289..03e02e6d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,195 +3,269 @@ name: "CI" concurrency: # Cancel any existing runs of this workflow for this same PR group: "${{ github.workflow }}-${{ github.ref }}" cancel-in-progress: true -on: # yamllint disable - - "push" - - "pull_request" +on: # yamllint disable-line rule:truthy rule:comments + push: + branches: + - "main" + - "develop" + tags: + - "v*" + pull_request: ~ + +env: + PLUGIN_NAME: "nautobot-plugin-chatops" jobs: black: - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" env: INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True" steps: - name: "Check out repository code" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" - with: - python-version: "3.10" + uses: "networktocode/gh-action-setup-poetry-environment@v4" - name: "Linting: black" run: "poetry run invoke black" bandit: - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" env: INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True" steps: - name: "Check out repository code" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" - with: - python-version: "3.10" + uses: "networktocode/gh-action-setup-poetry-environment@v4" - name: "Linting: bandit" run: "poetry run invoke bandit" - needs: - - "black" pydocstyle: - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" env: INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True" steps: - name: "Check out repository code" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" - with: - python-version: "3.10" + uses: "networktocode/gh-action-setup-poetry-environment@v4" - name: "Linting: pydocstyle" run: "poetry run invoke pydocstyle" - needs: - - "black" flake8: - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" env: INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True" steps: - name: "Check out repository code" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" - with: - python-version: "3.10" + uses: "networktocode/gh-action-setup-poetry-environment@v4" - name: "Linting: flake8" run: "poetry run invoke flake8" - needs: - - "black" + poetry: + 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@v4" + - name: "Checking: poetry lock file" + run: "poetry run invoke lock --check" yamllint: - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" env: INVOKE_NAUTOBOT_CHATOPS_LOCAL: "True" steps: - name: "Check out repository code" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" - with: - python-version: "3.10" + uses: "networktocode/gh-action-setup-poetry-environment@v4" - name: "Linting: yamllint" run: "poetry run invoke yamllint" + pylint: needs: + - "bandit" + - "pydocstyle" + - "flake8" + - "poetry" + - "yamllint" - "black" - pylint: - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" + strategy: + fail-fast: true + matrix: + python-version: ["3.11"] + nautobot-version: ["2.0.0"] + env: + INVOKE_NAUTOBOT_CHATOPS_PYTHON_VER: "${{ matrix.python-version }}" + INVOKE_NAUTOBOT_CHATOPS_NAUTOBOT_VER: "${{ matrix.nautobot-version }}" steps: - name: "Check out repository code" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" + uses: "networktocode/gh-action-setup-poetry-environment@v4" + - name: "Set up Docker Buildx" + id: "buildx" + uses: "docker/setup-buildx-action@v3" + - name: "Build" + uses: "docker/build-push-action@v5" with: - python-version: "3.10" + builder: "${{ steps.buildx.outputs.name }}" + context: "./" + push: false + load: true + tags: "${{ env.PLUGIN_NAME }}/nautobot:${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + file: "./development/Dockerfile" + cache-from: "type=gha,scope=${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + cache-to: "type=gha,scope=${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + build-args: | + NAUTOBOT_VER=${{ matrix.nautobot-version }} + PYTHON_VER=${{ matrix.python-version }} - name: "Copy credentials" run: "cp development/creds.example.env development/creds.env" - - name: "Linting: Pylint" + - name: "Linting: pylint" run: "poetry run invoke pylint" + check-migrations: needs: - "bandit" - "pydocstyle" - "flake8" + - "poetry" - "yamllint" - unittest: + - "black" + runs-on: "ubuntu-22.04" strategy: fail-fast: true matrix: - python-version: ["3.8"] - db-backend: ["postgresql", "mysql"] + python-version: ["3.11"] nautobot-version: ["2.0.0"] - # The include is a method to limit the amount of jobs ran. This essentially - # means that in addition to standard postgres and stable, also the lowest - # supported version and with mysql + env: + INVOKE_NAUTOBOT_CHATOPS_PYTHON_VER: "${{ matrix.python-version }}" + INVOKE_NAUTOBOT_CHATOPS_NAUTOBOT_VER: "${{ matrix.nautobot-version }}" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v4" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v4" + - name: "Set up Docker Buildx" + id: "buildx" + uses: "docker/setup-buildx-action@v3" + - name: "Build" + uses: "docker/build-push-action@v5" + with: + builder: "${{ steps.buildx.outputs.name }}" + context: "./" + push: false + load: true + tags: "${{ env.PLUGIN_NAME }}/nautobot:${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + file: "./development/Dockerfile" + cache-from: "type=gha,scope=${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + cache-to: "type=gha,scope=${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + build-args: | + NAUTOBOT_VER=${{ matrix.nautobot-version }} + PYTHON_VER=${{ matrix.python-version }} + - name: "Copy credentials" + run: "cp development/creds.example.env development/creds.env" + - name: "Checking: migrations" + run: "poetry run invoke check-migrations" + unittest: + needs: + - "pylint" + - "check-migrations" + strategy: + fail-fast: true + matrix: + python-version: ["3.8", "3.11"] + db-backend: ["postgresql"] + nautobot-version: ["stable"] include: - python-version: "3.11" db-backend: "postgresql" nautobot-version: "2.0.0" - - python-version: "3.11" - db-backend: "postgresql" - nautobot-version: "stable" - python-version: "3.11" db-backend: "mysql" nautobot-version: "stable" - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" env: INVOKE_NAUTOBOT_CHATOPS_PYTHON_VER: "${{ matrix.python-version }}" INVOKE_NAUTOBOT_CHATOPS_NAUTOBOT_VER: "${{ matrix.nautobot-version }}" steps: - name: "Check out repository code" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" + uses: "networktocode/gh-action-setup-poetry-environment@v4" + - name: "Set up Docker Buildx" + id: "buildx" + uses: "docker/setup-buildx-action@v3" + - name: "Build" + uses: "docker/build-push-action@v5" with: - python-version: "${{ matrix.python-version }}" + builder: "${{ steps.buildx.outputs.name }}" + context: "./" + push: false + load: true + tags: "${{ env.PLUGIN_NAME }}/nautobot:${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + file: "./development/Dockerfile" + cache-from: "type=gha,scope=${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + cache-to: "type=gha,scope=${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + build-args: | + NAUTOBOT_VER=${{ matrix.nautobot-version }} + PYTHON_VER=${{ matrix.python-version }} - name: "Copy credentials" run: "cp development/creds.example.env development/creds.env" - - name: "Build Container" - run: "poetry run invoke build" + - name: "Use Mysql invoke settings when needed" + run: "cp invoke.mysql.yml invoke.yml" + if: "matrix.db-backend == 'mysql'" - name: "Run Tests" run: "poetry run invoke unittest" - needs: - - "pylint" publish_gh: + needs: + - "unittest" name: "Publish to GitHub" - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" if: "startsWith(github.ref, 'refs/tags/v')" steps: - name: "Check out repository code" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Set up Python" - uses: "actions/setup-python@v2" + uses: "actions/setup-python@v4" with: - python-version: "3.9" + python-version: "3.11" - name: "Install Python Packages" run: "pip install poetry" - name: "Set env" run: "echo RELEASE_VERSION=${GITHUB_REF:10} >> $GITHUB_ENV" - name: "Run Poetry Version" run: "poetry version $RELEASE_VERSION" - - name: "Install Dependencies (needed for mkdocs)" - run: "poetry install --no-root" - - name: "Build Documentation" - run: "poetry run mkdocs build --no-directory-urls --strict" - name: "Run Poetry Build" run: "poetry build" - name: "Upload binaries to release" uses: "svenstaro/upload-release-action@v2" with: - repo_token: "${{ secrets.GH_NAUTOBOT_BOT_TOKEN }}" + repo_token: "${{ secrets.NTC_GITHUB_TOKEN }}" # use GH_NAUTOBOT_BOT_TOKEN for Nautobot Org repos. file: "dist/*" tag: "${{ github.ref }}" overwrite: true file_glob: true + publish_pypi: needs: - "unittest" - publish_pypi: name: "Push Package to PyPI" - runs-on: "ubuntu-20.04" + runs-on: "ubuntu-22.04" if: "startsWith(github.ref, 'refs/tags/v')" steps: - name: "Check out repository code" - uses: "actions/checkout@v2" + uses: "actions/checkout@v4" - name: "Set up Python" - uses: "actions/setup-python@v2" + uses: "actions/setup-python@v4" with: - python-version: "3.9" + python-version: "3.11" - name: "Install Python Packages" run: "pip install poetry" - name: "Set env" run: "echo RELEASE_VERSION=${GITHUB_REF:10} >> $GITHUB_ENV" - name: "Run Poetry Version" run: "poetry version $RELEASE_VERSION" - - name: "Install Dependencies (needed for mkdocs)" - run: "poetry install --no-root" - - name: "Build Documentation" - run: "poetry run mkdocs build --no-directory-urls --strict" - name: "Run Poetry Build" run: "poetry build" - name: "Push to PyPI" @@ -199,20 +273,38 @@ jobs: with: user: "__token__" password: "${{ secrets.PYPI_API_TOKEN }}" + slack-notify: needs: - - "unittest" - - changelog: - if: github.base_ref == 'develop' || github.base_ref == 'next' - runs-on: "ubuntu-20.04" + - "publish_gh" + - "publish_pypi" + runs-on: "ubuntu-22.04" + env: + SLACK_WEBHOOK_URL: "${{ secrets.SLACK_WEBHOOK_URL }}" + SLACK_MESSAGE: >- + *NOTIFICATION: NEW-RELEASE-PUBLISHED*\n + Repository: <${{ github.server_url }}/${{ github.repository }}|${{ github.repository }}>\n + Release: <${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ github.ref_name }}|${{ github.ref_name }}>\n + Published by: <${{ github.server_url }}/${{ github.actor }}|${{ github.actor }}> steps: - - name: "Check out repository code" - uses: "actions/checkout@v2" + - name: "Send a notification to Slack" + # ENVs cannot be used directly in job.if. This is a workaround to check + # if SLACK_WEBHOOK_URL is present. + if: "env.SLACK_WEBHOOK_URL != ''" + uses: "slackapi/slack-github-action@v1" with: - fetch-depth: "0" - - name: "Setup environment" - uses: "networktocode/gh-action-setup-poetry-environment@v2" - - name: "Check for changelog entry" - run: | - git fetch --no-tags origin +refs/heads/${{ github.base_ref }}:refs/remotes/origin/${{ github.base_ref }} - poetry run towncrier check --compare-with origin/${{ github.base_ref }} + payload: | + { + "text": "${{ env.SLACK_MESSAGE }}", + "blocks": [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "${{ env.SLACK_MESSAGE }}" + } + } + ] + } + env: + SLACK_WEBHOOK_URL: "${{ secrets.SLACK_WEBHOOK_URL }}" + SLACK_WEBHOOK_TYPE: "INCOMING_WEBHOOK" diff --git a/.github/workflows/rebake.yml b/.github/workflows/rebake.yml new file mode 100644 index 00000000..13d1e3a0 --- /dev/null +++ b/.github/workflows/rebake.yml @@ -0,0 +1,118 @@ +--- +name: "Rebake Cookie" +on: # yamllint disable-line rule:truthy + workflow_call: + inputs: + cookie: + description: "The cookie to rebake" + type: "string" + default: "" + draft: + description: "Whether to create the pull request as a draft" + type: "string" + default: "" + pull-request: + description: "The pull request strategy" + type: "string" + default: "" + template: + description: "The template repository URL" + type: "string" + default: "" + template-dir: + description: "The directory within the template repository to use as the template" + type: "string" + default: "" + template-ref: + description: "The branch or tag to use for the template" + type: "string" + default: "" + drift-manager-tag: + description: "The drift manager Docker image tag to use" + type: "string" + default: "latest" + workflow_dispatch: + inputs: + cookie: + description: "The cookie to rebake" + type: "string" + default: "" + draft: + description: "Whether to create the pull request as a draft" + type: "string" + default: "" + pull-request: + description: "The pull request strategy" + type: "string" + default: "" + template: + description: "The template repository URL" + type: "string" + default: "" + template-dir: + description: "The directory within the template repository to use as the template" + type: "string" + default: "" + template-ref: + description: "The branch or tag to use for the template" + type: "string" + default: "" + drift-manager-tag: + description: "The drift manager Docker image tag to use" + type: "string" + default: "latest" +jobs: + rebake: + runs-on: "ubuntu-22.04" + permissions: + actions: "write" + contents: "write" + packages: "read" + pull-requests: "write" + container: "ghcr.io/nautobot/cookiecutter-nautobot-app-drift-manager/prod:${{ github.event.inputs.drift-manager-tag }}" + env: + GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}" + steps: + - name: "Configure Rebake Arguments" + id: "config" + shell: "bash" + run: | + ARGS='--push' + + if [[ '${{ github.event.inputs.draft }}' == 'true' ]]; then + ARGS="$ARGS --draft" + elif [[ '${{ github.event.inputs.draft }}' == 'false' ]]; then + ARGS="$ARGS --no-draft" + elif [[ '${{ github.event.inputs.draft }}' == '' ]]; then + echo "Using repo default value for --draft" + else + echo "ERROR: Invalid value for draft: '${{ github.event.inputs.draft }}'" + exit 1 + fi + + if [[ '${{ github.event.inputs.pull-request }}' != '' ]]; then + ARGS="$ARGS --pull-request='${{ github.event.inputs.pull-request }}'" + fi + + if [[ '${{ github.event.inputs.template }}' != '' ]]; then + ARGS="$ARGS --template='${{ github.event.inputs.template }}'" + fi + + if [[ '${{ github.event.inputs.template-dir }}' != '' ]]; then + ARGS="$ARGS --template-dir='${{ github.event.inputs.template-dir }}'" + fi + + if [[ '${{ github.event.inputs.template-ref }}' != '' ]]; then + ARGS="$ARGS --template-ref='${{ github.event.inputs.template-ref }}'" + fi + + if [[ '${{ github.event.inputs.cookie }}' == '' ]]; then + ARGS="$ARGS '${{ github.repositoryUrl }}'" + else + ARGS="$ARGS '${{ github.event.inputs.cookie }}'" + fi + + echo "args=$ARGS" >> $GITHUB_OUTPUT + - name: "Rebake" + run: | + python -m ntc_cookie_drift_manager rebake ${{ steps.config.outputs.args }} diff --git a/.github/workflows/upstream_testing.yml b/.github/workflows/upstream_testing.yml index f6177d89..b23adc45 100644 --- a/.github/workflows/upstream_testing.yml +++ b/.github/workflows/upstream_testing.yml @@ -1,5 +1,5 @@ --- -name: "Nautobot Upstream Testing" +name: "Nautobot Upstream Monitor" on: # yamllint disable-line rule:truthy rule:comments schedule: @@ -10,4 +10,4 @@ jobs: uses: "nautobot/nautobot/.github/workflows/plugin_upstream_testing_base.yml@develop" with: # Below could potentially be collapsed into a single argument if a concrete relationship between both is enforced invoke_context_name: "NAUTOBOT_CHATOPS" - plugin_name: "nautobot-chatops-plugin" + plugin_name: "nautobot-plugin-chatops" diff --git a/.gitignore b/.gitignore index d1cbef39..ccca6f0a 100644 --- a/.gitignore +++ b/.gitignore @@ -171,7 +171,6 @@ ehthumbs_vista.db # Dump file *.stackdump -dumps/ # Folder config file [Dd]esktop.ini @@ -193,8 +192,28 @@ $RECYCLE.BIN/ # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 -# Jetbrains IDE configs -.idea/ +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries # Gradle and Maven with auto-import # When using Gradle or Maven with auto-import, you should exclude module files, @@ -278,11 +297,13 @@ fabric.properties # Rando creds.env +development/*.txt # Invoke overrides invoke.yml -# Static docs -/nautobot_chatops/static/nautobot_chatops/docs/ +# Docs +public /compose.yaml /dump.sql +/nautobot_chatops/static/nautobot_chatops/docs diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 882d19e9..9a0a64c7 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -8,14 +8,13 @@ version: 2 # Set the version of Python in the build environment. build: - os: "ubuntu-20.04" + os: "ubuntu-22.04" tools: - python: "3.9" + python: "3.10" -# MKDocs configuration file mkdocs: configuration: "mkdocs.yml" - # fail_on_warning: true + fail_on_warning: true # Use our docs/requirements.txt during installation. python: diff --git a/.yamllint.yml b/.yamllint.yml index 1cc1bcec..8cc3e9a9 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -1,13 +1,13 @@ --- extends: "default" -ignore: | - setup_files/ - compose.yaml rules: comments: "enable" - empty-values: "enable" + empty-values: "disable" indentation: indent-sequences: "consistent" line-length: "disable" quoted-strings: quote-type: "double" +ignore: | + .venv/ + compose.yaml diff --git a/LICENSE b/LICENSE index 2a07d287..d46cc975 100644 --- a/LICENSE +++ b/LICENSE @@ -1,15 +1,15 @@ -Copyright 2020 Network to Code -Network to Code, LLC +Apache Software License 2.0 + +Copyright (c) 2023, Network to Code, LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. - You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 +http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and -limitations under the License. \ No newline at end of file +limitations under the License. diff --git a/README.md b/README.md index 36c0a150..d25078f1 100644 --- a/README.md +++ b/README.md @@ -1,128 +1,66 @@ -# Nautobot ChatOps +# Nautobot ChatOps App + +

- +
- +
- A multi-platform ChatOps bot App for Nautobot. + An App for Nautobot.

-- Support for multiple chat platforms: - - Mattermost - - Microsoft Teams - - Slack - - Cisco Webex -- Support for multiple integrations: - - Cisco ACI - - AWX / Ansible Tower - - Arista CloudVision - - Grafana - - IPFabric - - Cisco Meraki - - Palo Alto Panorama -- Write a command once and run it on every supported platform, including rich content formatting. -- Extensible - other Nautobot plugins can provide additional commands which will be dynamically discovered. -- Automatic generation of basic help menus (accessed via `help`, `/command help`, or `/command sub-command help`). -- Metrics of command usage via the `nautobot_capacity_metrics` plugin. +## Overview -## Documentation +> Developer Note: Add a long (2-3 paragraphs) description of what the App does, what problems it solves, what functionality it adds to Nautobot, what external systems it works with etc. -Full web-based HTML documentation for this app can be found over on the [Nautobot Docs](https://docs.nautobot.com/projects/chatops/en/latest/) website: +### Screenshots -- [User Guide](https://docs.nautobot.com/projects/chatops/en/latest/user/app_overview/) - Overview, Using the App, Getting Started -- [Administrator Guide](https://docs.nautobot.com/projects/chatops/en/latest/admin/install/) - How to Install, Configure, Upgrade, or Uninstall the App. -- [Developer Guide](https://docs.nautobot.com/projects/chatops/en/latest/dev/contributing/) - Extending the App, Code Reference, Contribution Guide. -- [Release Notes / Changelog](https://docs.nautobot.com/projects/chatops/en/latest/admin/release_notes/) -- [Frequently Asked Questions](https://docs.nautobot.com/projects/chatops/en/latest/user/app_faq/) -- [Glossary](https://docs.nautobot.com/projects/chatops/en/latest/glossary/) +> Developer Note: Add any representative screenshots of the App in action. These images should also be added to the `docs/user/app_use_cases.md` section. + +> Developer Note: Place the files in the `docs/images/` folder and link them using only full URLs from GitHub, for example: `![Overview](https://raw.githubusercontent.com/nautobot/nautobot-plugin-chatops/develop/docs/images/plugin-overview.png)`. This absolute static linking is required to ensure the README renders properly in GitHub, the docs site, and any other external sites like PyPI. -## Try it Out +More screenshots can be found in the [Using the App](https://docs.nautobot.com/projects/chatops/en/latest/user/app_use_cases/) page in the documentation. Here's a quick overview of some of the plugin's added functionality: -Interested to see Nautobot ChatOps in action? It's currently setup on the [Demo Instance](https://demo.nautobot.com/) and integrated into [NTC Slack](https://slack.networktocode.com). You can sign up for that Slack workspace and join the `#nautobot-chat` channel to understand what this bot can do and try it for yourself. You can try these exact chat commands and many more: +![](https://raw.githubusercontent.com/nautobot/nautobot-plugin-chatops/develop/docs/images/placeholder.png) -### Command: `/nautobot` +## Try it out! -![image](https://user-images.githubusercontent.com/6332586/118281576-5db4e980-b49b-11eb-8574-1332ed4b9757.png) +> Developer Note: Only keep this section if appropriate. Update link to correct sandbox. + +This App is installed in the Nautobot Community Sandbox found over at [demo.nautobot.com](https://demo.nautobot.com/)! + +> For a full list of all the available always-on sandbox environments, head over to the main page on [networktocode.com](https://www.networktocode.com/nautobot/sandbox-environments/). + +## Documentation + +Full documentation for this App can be found over on the [Nautobot Docs](https://docs.nautobot.com) website: + +- [User Guide](https://docs.nautobot.com/projects/chatops/en/latest/user/app_overview/) - Overview, Using the App, Getting Started. +- [Administrator Guide](https://docs.nautobot.com/projects/chatops/en/latest/admin/install/) - How to Install, Configure, Upgrade, or Uninstall the App. +- [Developer Guide](https://docs.nautobot.com/projects/chatops/en/latest/dev/contributing/) - Extending the App, Code Reference, Contribution Guide. +- [Release Notes / Changelog](https://docs.nautobot.com/projects/chatops/en/latest/admin/release_notes/). +- [Frequently Asked Questions](https://docs.nautobot.com/projects/chatops/en/latest/user/faq/). -### Command: `/nautobot get-devices` +### Contributing to the Documentation -![image](https://user-images.githubusercontent.com/6332586/118281772-95239600-b49b-11eb-9c79-e2040dc4a982.png) +You can find all the Markdown source for the App documentation under the [`docs`](https://github.com/nautobot/nautobot-plugin-chatops//tree/develop/docs) folder in this repository. For simple edits, a Markdown capable editor is sufficient: clone the repository and edit away. -### Command: `/nautobot get-interface-connections` +If you need to view the fully-generated documentation site, you can build it with [MkDocs](https://www.mkdocs.org/). A container hosting the documentation can be started using the `invoke` commands (details in the [Development Environment Guide](https://docs.nautobot.com/projects/chatops/en/latest/dev/dev_environment/#docker-development-environment)) on [http://localhost:8001](http://localhost:8001). Using this container, as your changes to the documentation are saved, they will be automatically rebuilt and any pages currently being viewed will be reloaded in your browser. -![image](https://user-images.githubusercontent.com/6332586/118281976-ca2fe880-b49b-11eb-87ad-2a41eaa168ed.png) +Any PRs with fixes or improvements are very welcome! ## Questions -For any questions or comments, please check the [FAQ](https://docs.nautobot.com/projects/chatops/en/latest/user/app_faq/) first and feel free to swing by the [Network to Code slack channel](https://networktocode.slack.com/) (channel #nautobot). -Sign up [here](https://slack.networktocode.com/) - -## Acknowledgements - -This project includes code originally written in separate plugins, which have been merged into this project: - -- [nautobot-plugin-chatops-aci](https://github.com/nautobot/nautobot-plugin-chatops-aci): - Thanks - [@mamullen13316](https://github.com/mamullen13316), - [@smk4664](https://github.com/smk4664), - [@ubajze](https://github.com/ubajze), - [@whitej6](https://github.com/whitej6), -- [nautobot-plugin-chatops-ansible](https://github.com/nautobot/nautobot-plugin-chatops-ansible): - Thanks - [@chipn](https://github.com/chipn), - [@dgjustice](https://github.com/dgjustice), - [@jeffkala](https://github.com/jeffkala), - [@jvanderaa](https://github.com/jvanderaa), - [@matt852](https://github.com/matt852), - [@smk4664](https://github.com/smk4664), - [@ubajze](https://github.com/ubajze), - [@whitej6](https://github.com/whitej6), -- [nautobot-plugin-chatops-arista-cloudvision](https://github.com/nautobot/nautobot-plugin-chatops-arista-cloudvision): - Thanks - [@qduk](https://github.com/qduk), - [@ubajze](https://github.com/ubajze), - [@whitej6](https://github.com/whitej6), -- [nautobot-plugin-chatops-grafana](https://github.com/nautobot/nautobot-plugin-chatops-grafana): - Thanks - [@jedelman8](https://github.com/jedelman8), - [@josh-silvas](https://github.com/josh-silvas), - [@nniehoff](https://github.com/nniehoff), - [@tim-fiola](https://github.com/tim-fiola), - [@ubajze](https://github.com/ubajze), - [@whitej6](https://github.com/whitej6), -- [nautobot-plugin-chatops-ipfabric](https://github.com/nautobot/nautobot-plugin-chatops-ipfabric): - Thanks - [@alhogan](https://github.com/alhogan), - [@chadell](https://github.com/chadell), - [@chipn](https://github.com/chipn), - [@justinjeffery-ipf](https://github.com/justinjeffery-ipf), - [@nniehoff](https://github.com/nniehoff), - [@pke11y](https://github.com/pke11y), - [@scetron](https://github.com/scetron), - [@smk4664](https://github.com/smk4664), - [@ubajze](https://github.com/ubajze), - [@whitej6](https://github.com/whitej6), -- [nautobot-plugin-chatops-meraki](https://github.com/nautobot/nautobot-plugin-chatops-meraki): - Thanks - [@jedelman8](https://github.com/jedelman8), - [@jeffkala](https://github.com/jeffkala), - [@qduk](https://github.com/qduk), - [@tim-fiola](https://github.com/tim-fiola), - [@ubajze](https://github.com/ubajze), - [@whitej6](https://github.com/whitej6), -- [nautobot-plugin-chatops-panorama](https://github.com/nautobot/nautobot-plugin-chatops-panorama): - Thanks - [@FragmentedPacket](https://github.com/FragmentedPacket), - [@PhillSimonds](https://github.com/PhillSimonds), - [@armartirosyan](https://github.com/armartirosyan), - [@itdependsnetworks](https://github.com/itdependsnetworks), - [@jamesholland-uk](https://github.com/jamesholland-uk), - [@jdrew82](https://github.com/jdrew82), - [@matt852](https://github.com/matt852), - [@qduk](https://github.com/qduk), - [@ubajze](https://github.com/ubajze), - [@whitej6](https://github.com/whitej6), +For any questions or comments, please check the [FAQ](https://docs.nautobot.com/projects/chatops/en/latest/user/faq/) first. Feel free to also swing by the [Network to Code Slack](https://networktocode.slack.com/) (channel `#nautobot`), sign up [here](http://slack.networktocode.com/) if you don't have an account. diff --git a/development/Dockerfile b/development/Dockerfile index a5931cf7..de25f815 100644 --- a/development/Dockerfile +++ b/development/Dockerfile @@ -6,11 +6,11 @@ # ------------------------------------------------------------------------------------- # !!! USE CAUTION WHEN MODIFYING LINES BELOW -# Accepts a desired Nautobot version as build argument, default to 1.5.4 -ARG NAUTOBOT_VER="1.5.4" +# Accepts a desired Nautobot version as build argument, default to 2.0.0 +ARG NAUTOBOT_VER="2.0.0" -# Accepts a desired Python version as build argument, default to 3.8 -ARG PYTHON_VER="3.8" +# Accepts a desired Python version as build argument, default to 3.11 +ARG PYTHON_VER="3.11" # Retrieve published development image of Nautobot base which should include most CI dependencies FROM ghcr.io/nautobot/nautobot-dev:${NAUTOBOT_VER}-py${PYTHON_VER} @@ -19,20 +19,21 @@ FROM ghcr.io/nautobot/nautobot-dev:${NAUTOBOT_VER}-py${PYTHON_VER} ARG NAUTOBOT_ROOT=/opt/nautobot ENV prometheus_multiproc_dir=/prom_cache -ENV NAUTOBOT_ROOT ${NAUTOBOT_ROOT} +ENV NAUTOBOT_ROOT=${NAUTOBOT_ROOT} +ENV INVOKE_NAUTOBOT_CHATOPS_LOCAL=true # Install Poetry manually via its installer script; # We might be using an older version of Nautobot that includes an older version of Poetry # and CI and local development may have a newer version of Poetry # Since this is only used for development and we don't ship this container, pinning Poetry back is not expressly necessary # We also don't need virtual environments in container -RUN curl -sSL https://install.python-poetry.org | python3 - && \ +RUN which poetry || curl -sSL https://install.python-poetry.org | python3 - && \ poetry config virtualenvs.create false # !!! USE CAUTION WHEN MODIFYING LINES ABOVE # ------------------------------------------------------------------------------------- # App-specifc system build/test dependencies. -# +# # Example: LDAP requires `libldap2-dev` to be apt-installed before the Python package. # ------------------------------------------------------------------------------------- # --> Start safe to modify section @@ -68,11 +69,13 @@ RUN sort poetry_freeze_base.txt poetry_freeze_all.txt | uniq -u > poetry_freeze_ # Install all local project as editable, constrained on Nautobot version, to get any additional # direct dependencies of the app -RUN pip install -c constraints.txt -e .[all] +RUN --mount=type=cache,target="/root/.cache/pip",sharing=locked \ + pip install -c constraints.txt -e .[all] # Install any dev dependencies frozen from Poetry # Can be improved in Poetry 1.2 which allows `poetry install --only dev` -RUN pip install -c constraints.txt -r poetry_freeze_dev.txt +RUN --mount=type=cache,target="/root/.cache/pip",sharing=locked \ + pip install -c constraints.txt -r poetry_freeze_dev.txt COPY development/nautobot_config.py ${NAUTOBOT_ROOT}/nautobot_config.py # !!! USE CAUTION WHEN MODIFYING LINES ABOVE diff --git a/development/creds.example.env b/development/creds.example.env index 34f27de1..26e24fad 100644 --- a/development/creds.example.env +++ b/development/creds.example.env @@ -25,55 +25,3 @@ MYSQL_PASSWORD=${NAUTOBOT_DB_PASSWORD} # NAUTOBOT_DB_HOST=localhost # NAUTOBOT_REDIS_HOST=localhost # NAUTOBOT_CONFIG=development/nautobot_config.py - -# = Chat Platforms =================== - -# - Mattermost ----------------------- -MATTERMOST_API_TOKEN="5qsffxoapt883qfdygbdgf17jy" - -# - Microsoft Teams ------------------ -# MICROSOFT_APP_ID="changeme" -# MICROSOFT_APP_PASSWORD="changeme" - -# - Slack ---------------------------- -# SLACK_API_TOKEN="xoxb-changeme" -# SLACK_APP_TOKEN="changeme" -# SLACK_SIGNING_SECRET="changeme" - -# - Cisco Webex ---------------------- -# WEBEX_ACCESS_TOKEN="changeme" -# WEBEX_SIGNING_SECRET="changeme" - -# = ChatOps Integrations ============= - -# - Cisco ACI ------------------------ -# First APIC -# APIC_USERNAME_NTCAPIC=admin -# APIC_PASSWORD_NTCAPIC="changeme" -# APIC_URI_NTCAPIC=https://aci.demo.networktocode.com -# APIC_VERIFY_NTCAPIC=false -# Second APIC -# APIC_USERNAME_LAB=admin -# APIC_PASSWORD_LAB="changeme" -# APIC_URI_LAB=https://10.100.1.10 -# APIC_VERIFY_LAB=false -# Repeat for as many as you need... - -# - AWX / Ansible Tower -------------- -NAUTOBOT_TOWER_PASSWORD="admin" - -# - Arista CloudVision --------------- -# ARISTACV_CVAAS_TOKEN="changeme" -# ARISTACV_CVP_PASSWORD="changeme" - -# - Grafana -------------------------- -# GRAFANA_API_KEY="changeme" - -# - IPFabric --------------------- -# IPFABRIC_API_TOKEN="changeme" - -# - Cisco Meraki --------------------- -# MERAKI_API_KEY="changeme" - -# - Palo Alto Panorama --------------- -# PANORAMA_PASSWORD="changeme" diff --git a/development/development.env b/development/development.env index b395947b..54f0b870 100644 --- a/development/development.env +++ b/development/development.env @@ -7,8 +7,6 @@ NAUTOBOT_BANNER_TOP="Local" NAUTOBOT_CHANGELOG_RETENTION=0 NAUTOBOT_DEBUG=True -NAUTOBOT_DJANGO_EXTENSIONS_ENABLED=True -NAUTOBOT_DJANGO_TOOLBAR_ENABLED=True NAUTOBOT_LOG_LEVEL=DEBUG NAUTOBOT_METRICS_ENABLED=True NAUTOBOT_NAPALM_TIMEOUT=5 @@ -38,61 +36,3 @@ POSTGRES_DB=${NAUTOBOT_DB_NAME} MYSQL_USER=${NAUTOBOT_DB_USER} MYSQL_DATABASE=${NAUTOBOT_DB_NAME} MYSQL_ROOT_HOST=% - -# = ChatOps Common Settings ========== -NAUTOBOT_CHATOPS_RESTRICT_HELP="False" -NAUTOBOT_CHATOPS_FALLBACK_CHATOPS_USER="chatbot" - -# = Chat Platforms =================== - -# - Mattermost ----------------------- -NAUTOBOT_CHATOPS_ENABLE_MATTERMOST="True" -MATTERMOST_URL="http://mattermost:8065" - -# - Microsoft Teams ------------------ -NAUTOBOT_CHATOPS_ENABLE_MS_TEAMS="False" - -# - Slack ---------------------------- -NAUTOBOT_CHATOPS_ENABLE_SLACK="False" -# SLACK_SLASH_COMMAND_PREFIX="/" - -# - Cisco Webex ---------------------- -NAUTOBOT_CHATOPS_ENABLE_WEBEX="False" -# WEBEX_MSG_CHAR_LIMIT=7439 - -# = ChatOps Integrations ============= - -# - Cisco ACI ------------------------ -NAUTOBOT_CHATOPS_ENABLE_ACI="False" - -# - AWX / Ansible Tower -------------- -NAUTOBOT_CHATOPS_ENABLE_ANSIBLE="False" -NAUTOBOT_TOWER_URI="https://awx:8043/" -NAUTOBOT_TOWER_USERNAME="awx" -NAUTOBOT_TOWER_VERIFY_SSL="False" - -# - Arista CloudVision --------------- -NAUTOBOT_CHATOPS_ENABLE_ARISTACV="False" -# ARISTACV_CVAAS_URL="https://cloudvision.arista.com" -# ARISTACV_CVP_HOST="cloudvision.arista" -ARISTACV_CVP_INSECURE="False" -# ARISTACV_CVP_USERNAME="arista" -ARISTACV_ON_PREM="False" - -# - Grafana -------------------------- -NAUTOBOT_CHATOPS_ENABLE_GRAFANA="False" -# GRAFANA_URL="http://grafana:3000" - -# - IPFabric --------------------- -NAUTOBOT_CHATOPS_ENABLE_IPFABRIC="False" -# IPFABRIC_HOST="https://ipfabric.example.com" -# IPFABRIC_TIMEOUT=15 -IPFABRIC_VERIFY="True" - -# - Cisco Meraki --------------------- -NAUTOBOT_CHATOPS_ENABLE_MERAKI="False" - -# - Palo Alto Panorama --------------- -NAUTOBOT_CHATOPS_ENABLE_PANORAMA="False" -PANORAMA_HOST="https://panorama.example.com" -PANORAMA_USER="admin" diff --git a/development/docker-compose.base.yml b/development/docker-compose.base.yml index 680e3786..36ec4b5e 100644 --- a/development/docker-compose.base.yml +++ b/development/docker-compose.base.yml @@ -7,7 +7,7 @@ x-nautobot-build: &nautobot-build context: "../" dockerfile: "development/Dockerfile" x-nautobot-base: &nautobot-base - image: "nautobot-chatops-plugin/nautobot:${NAUTOBOT_VER}-py${PYTHON_VER}" + image: "nautobot-chatops/nautobot:${NAUTOBOT_VER}-py${PYTHON_VER}" env_file: - "development.env" - "creds.env" @@ -28,7 +28,7 @@ services: entrypoint: - "sh" - "-c" # this is to evaluate the $NAUTOBOT_LOG_LEVEL from the env - - "watchmedo auto-restart --directory './' --pattern '*.py' --recursive -- nautobot-server celery worker -l $$NAUTOBOT_LOG_LEVEL --events" ## $$ because of docker-compose + - "nautobot-server celery worker -l $$NAUTOBOT_LOG_LEVEL --events" ## $$ because of docker-compose depends_on: - "nautobot" healthcheck: diff --git a/development/docker-compose.dev.yml b/development/docker-compose.dev.yml index 1c74cb19..be064971 100644 --- a/development/docker-compose.dev.yml +++ b/development/docker-compose.dev.yml @@ -12,21 +12,28 @@ services: volumes: - "./nautobot_config.py:/opt/nautobot/nautobot_config.py" - "../:/source" + healthcheck: + test: ["CMD", "true"] # Due to layering, disable: true won't work. Instead, change the test docs: entrypoint: "mkdocs serve -v -a 0.0.0.0:8080" ports: - "8001:8080" volumes: - - "../docs:/source/docs:ro" - - "../mkdocs.yml:/source/mkdocs.yml:ro" - image: "nautobot-chatops-plugin/nautobot:${NAUTOBOT_VER}-py${PYTHON_VER}" + - "../:/source" + image: "nautobot-chatops/nautobot:${NAUTOBOT_VER}-py${PYTHON_VER}" healthcheck: disable: true tty: true worker: + entrypoint: + - "sh" + - "-c" # this is to evaluate the $NAUTOBOT_LOG_LEVEL from the env + - "watchmedo auto-restart --directory './' --pattern '*.py' --recursive -- nautobot-server celery worker -l $$NAUTOBOT_LOG_LEVEL --events" ## $$ because of docker-compose volumes: - "./nautobot_config.py:/opt/nautobot/nautobot_config.py" - "../:/source" + healthcheck: + test: ["CMD", "true"] # Due to layering, disable: true won't work. Instead, change the test # To expose postgres or redis to the host uncomment the following # postgres: # ports: diff --git a/development/docker-compose.mysql.yml b/development/docker-compose.mysql.yml index 32f1f945..062ada94 100644 --- a/development/docker-compose.mysql.yml +++ b/development/docker-compose.mysql.yml @@ -20,7 +20,7 @@ services: image: "mysql:8" command: - "--default-authentication-plugin=mysql_native_password" - - "--max_connections=200" + - "--max_connections=1000" env_file: - "development.env" - "creds.env" diff --git a/development/docker-compose.postgres.yml b/development/docker-compose.postgres.yml index 8582412b..12d1de31 100644 --- a/development/docker-compose.postgres.yml +++ b/development/docker-compose.postgres.yml @@ -14,7 +14,6 @@ services: - "development.env" - "creds.env" volumes: - # - "./nautobot.sql:/tmp/nautobot.sql" - "postgres_data:/var/lib/postgresql/data" healthcheck: test: "pg_isready --username=$$POSTGRES_USER --dbname=$$POSTGRES_DB" diff --git a/development/nautobot_config.py b/development/nautobot_config.py index c913a18f..ba1b52ee 100644 --- a/development/nautobot_config.py +++ b/development/nautobot_config.py @@ -2,9 +2,24 @@ import os import sys -from nautobot.core.settings import * # noqa: F401,F403 pylint: disable=wildcard-import,unused-wildcard-import +from nautobot.core.settings import * # noqa: F403 # pylint: disable=wildcard-import,unused-wildcard-import from nautobot.core.settings_funcs import is_truthy, parse_redis_connection +# +# Debug +# + +DEBUG = is_truthy(os.getenv("NAUTOBOT_DEBUG", False)) +_TESTING = len(sys.argv) > 1 and sys.argv[1] == "test" + +if DEBUG and not _TESTING: + DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": lambda _request: True} + + if "debug_toolbar" not in INSTALLED_APPS: # noqa: F405 + INSTALLED_APPS.append("debug_toolbar") # noqa: F405 + if "debug_toolbar.middleware.DebugToolbarMiddleware" not in MIDDLEWARE: # noqa: F405 + MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware") # noqa: F405 + # # Misc. settings # @@ -12,6 +27,9 @@ ALLOWED_HOSTS = os.getenv("NAUTOBOT_ALLOWED_HOSTS", "").split(" ") SECRET_KEY = os.getenv("NAUTOBOT_SECRET_KEY", "") +# +# Database +# nautobot_db_engine = os.getenv("NAUTOBOT_DB_ENGINE", "django.db.backends.postgresql") default_db_settings = { @@ -41,18 +59,28 @@ DATABASES["default"]["OPTIONS"] = {"charset": "utf8mb4"} # -# Debug +# Redis # -DEBUG = True +# The django-redis cache is used to establish concurrent locks using Redis. +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": parse_redis_connection(redis_database=0), + "TIMEOUT": 300, + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + }, + } +} -# Django Debug Toolbar -DEBUG_TOOLBAR_CONFIG = {"SHOW_TOOLBAR_CALLBACK": lambda _request: DEBUG and not TESTING} +# Redis Cacheops +CACHEOPS_REDIS = parse_redis_connection(redis_database=1) -if DEBUG and "debug_toolbar" not in INSTALLED_APPS: # noqa: F405 - INSTALLED_APPS.append("debug_toolbar") # noqa: F405 -if DEBUG and "debug_toolbar.middleware.DebugToolbarMiddleware" not in MIDDLEWARE: # noqa: F405 - MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware") # noqa: F405 +# +# Celery settings are not defined here because they can be overloaded with +# environment variables. By default they use `CACHES["default"]["LOCATION"]`. +# # # Logging @@ -60,10 +88,8 @@ LOG_LEVEL = "DEBUG" if DEBUG else "INFO" -TESTING = len(sys.argv) > 1 and sys.argv[1] == "test" - # Verbose logging during normal development operation, but quiet logging during unit test execution -if not TESTING: +if not _TESTING: LOGGING = { "version": 1, "disable_existing_loggers": False, @@ -99,117 +125,17 @@ } # -# Redis -# - -# The django-redis cache is used to establish concurrent locks using Redis. The -# django-rq settings will use the same instance/database by default. -# -# This "default" server is now used by RQ_QUEUES. -# >> See: nautobot.core.settings.RQ_QUEUES -CACHES = { - "default": { - "BACKEND": "django_redis.cache.RedisCache", - "LOCATION": parse_redis_connection(redis_database=0), - "TIMEOUT": 300, - "OPTIONS": { - "CLIENT_CLASS": "django_redis.client.DefaultClient", - }, - } -} - -# RQ_QUEUES is not set here because it just uses the default that gets imported -# up top via `from nautobot.core.settings import *`. - -# Redis Cacheops -CACHEOPS_REDIS = parse_redis_connection(redis_database=1) - -# -# Celery settings are not defined here because they can be overloaded with -# environment variables. By default they use `CACHES["default"]["LOCATION"]`. +# Apps # -# Enable installed plugins. Add the name of each plugin to the list. -PLUGINS = [ - "nautobot_capacity_metrics", - "nautobot_chatops", -] - -# Plugins configuration settings. These settings are used by various plugins that the user may have installed. -# Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. -PLUGINS_CONFIG = { - "nautobot_chatops": { - # = Common Settings ================== - "restrict_help": is_truthy(os.getenv("NAUTOBOT_CHATOPS_RESTRICT_HELP")), - "fallback_chatops_user": os.environ.get("NAUTOBOT_CHATOPS_FALLBACK_CHATOPS_USER"), - # TODO: Add following settings - # | `delete_input_on_submission` | Removes the input prompt from the chat history after user input | No | `False` | - # | `send_all_messages_private` | Ensures only the person interacting with the bot sees the responses | No | `False` | - # | `session_cache_timeout` | Controls session cache | No | `86400` | - # = Chat Platforms =================== - # - Mattermost ----------------------- - "enable_mattermost": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_MATTERMOST")), - "mattermost_api_token": os.environ.get("MATTERMOST_API_TOKEN"), - "mattermost_url": os.environ.get("MATTERMOST_URL"), - # - Microsoft Teams ------------------ - "enable_ms_teams": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_MS_TEAMS")), - "microsoft_app_id": os.environ.get("MICROSOFT_APP_ID"), - "microsoft_app_password": os.environ.get("MICROSOFT_APP_PASSWORD"), - # - Slack ---------------------------- - "enable_slack": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_SLACK")), - "slack_api_token": os.environ.get("SLACK_API_TOKEN"), - "slack_app_token": os.environ.get("SLACK_APP_TOKEN"), - "slack_signing_secret": os.environ.get("SLACK_SIGNING_SECRET"), - "slack_slash_command_prefix": os.environ.get("SLACK_SLASH_COMMAND_PREFIX", "/"), - # - Cisco Webex ---------------------- - "enable_webex": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_WEBEX")), - "webex_msg_char_limit": int(os.getenv("WEBEX_MSG_CHAR_LIMIT", "7439")), - "webex_signing_secret": os.environ.get("WEBEX_SIGNING_SECRET"), - "webex_token": os.environ.get("WEBEX_ACCESS_TOKEN"), - # = Integrations ===================== - # - Cisco ACI ------------------------ - "enable_aci": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_ACI")), - "aci_creds": {x: os.environ[x] for x in os.environ if "APIC" in x}, - # - AWX / Ansible Tower -------------- - "enable_ansible": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_ANSIBLE")), - "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)), - # - Arista CloudVision --------------- - "enable_aristacv": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_ARISTACV")), - "aristacv_cvaas_url": os.environ.get("ARISTACV_CVAAS_URL"), - "aristacv_cvaas_token": os.environ.get("ARISTACV_CVAAS_TOKEN"), - "aristacv_cvp_host": os.environ.get("ARISTACV_CVP_HOST"), - "aristacv_cvp_insecure": is_truthy(os.environ.get("ARISTACV_CVP_INSECURE")), - "aristacv_cvp_password": os.environ.get("ARISTACV_CVP_PASSWORD"), - "aristacv_cvp_username": os.environ.get("ARISTACV_CVP_USERNAME"), - "aristacv_on_prem": is_truthy(os.environ.get("ARISTACV_ON_PREM")), - # - Grafana -------------------------- - "enable_grafana": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_GRAFANA")), - "grafana_url": os.environ.get("GRAFANA_URL", ""), - "grafana_api_key": os.environ.get("GRAFANA_API_KEY", ""), - "grafana_default_width": 0, - "grafana_default_height": 0, - "grafana_default_theme": "dark", - "grafana_default_timespan": "0", - "grafana_org_id": 1, - "grafana_default_tz": "America/Denver", - # - IPFabric -------------------------- - "enable_ipfabric": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_IPFABRIC")), - "ipfabric_api_token": os.environ.get("IPFABRIC_API_TOKEN"), - "ipfabric_host": os.environ.get("IPFABRIC_HOST"), - "ipfabric_timeout": os.environ.get("IPFABRIC_TIMEOUT", 15), - "ipfabric_verify": is_truthy(os.environ.get("IPFABRIC_VERIFY", True)), - # - Cisco Meraki --------------------- - "enable_meraki": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_MERAKI")), - "meraki_dashboard_api_key": os.environ.get("MERAKI_API_KEY"), - # - Palo Alto Panorama --------------- - "enable_panorama": is_truthy(os.getenv("NAUTOBOT_CHATOPS_ENABLE_PANORAMA")), - "panorama_host": os.environ.get("PANORAMA_HOST"), - "panorama_password": os.environ.get("PANORAMA_PASSWORD"), - "panorama_user": os.environ.get("PANORAMA_USER"), - }, -} - -METRICS_ENABLED = is_truthy(os.getenv("NAUTOBOT_METRICS_ENABLED")) +# Enable installed Apps. Add the name of each App to the list. +PLUGINS = ["nautobot_chatops"] + +# Apps configuration settings. These settings are used by various Apps that the user may have installed. +# Each key in the dictionary is the name of an installed App and its value is a dictionary of settings. +# PLUGINS_CONFIG = { +# 'nautobot_chatops': { +# 'foo': 'bar', +# 'buzz': 'bazz' +# } +# } diff --git a/docs/admin/compatibility_matrix.md b/docs/admin/compatibility_matrix.md index bdea453b..2be4c56d 100644 --- a/docs/admin/compatibility_matrix.md +++ b/docs/admin/compatibility_matrix.md @@ -1,24 +1,8 @@ # Compatibility Matrix -Changes to the support of upstream Nautobot releases will be announced 1 minor or major version ahead. +!!! warning "Developer Note - Remove Me!" + Explain how the release models of the plugin and of Nautobot work together, how releases are supported, how features and older releases are deprecated etc. -The **deprecation policy** will be announced within the [release notes](release_notes/index.md), and updated in the table below. There will be a `stable-.` branch that will be minimally maintained. Any security enhancements or major bugs in that branch will be supported for a limited time. - -While that last supported version will not be strictly enforced via the `max_version` setting, any issues with an updated Nautobot supported version in a minor release will require raising a bug and fixing it in Nautobot core, with no fixes expected in this plugin. This allows the Chatops plugin the ability to quickly take advantage of the latest features in Nautobot. - -| Chatops Version | Nautobot First Support Version | Nautobot Last Support Version | -| --------------- | ------------------------------ | ----------------------------- | -| 1.0.X | 1.0.0 | 1.2.99 [Official] | -| 1.1.X | 1.0.0 | 1.2.99 [Official] | -| 1.2.X | 1.0.0 | 1.2.99 [Official] | -| 1.3.X | 1.0.0 | 1.2.99 [Official] | -| 1.4.X | 1.0.0 | 1.2.99 [Official] | -| 1.5.X | 1.0.0 | 1.2.99 [Official] | -| 1.6.X | 1.0.0 | 1.2.99 [Official] | -| 1.7.X | 1.0.0 | 1.2.99 [Official] | -| 1.8.X | 1.1.0 | 1.4.99 [Official] | -| 1.9.X | 1.2.0 | 1.5.99 [Official] | -| 1.10.X | 1.3.0 | 1.5.99 [Official] | -| 2.0.X | 1.5.4 | 1.6.99 [Official] | -| 2.1.X | 1.6.2 | 1.6.99 [Official] | -| 3.0.X | 2.0.0 | 2.2.99 [Official] | +| Nautobot ChatOps App Version | Nautobot First Support Version | Nautobot Last Support Version | +| ------------- | -------------------- | ------------- | +| 1.0.X | 2.0.0 | 1.99.99 | diff --git a/docs/admin/install.md b/docs/admin/install.md new file mode 100644 index 00000000..c3463adf --- /dev/null +++ b/docs/admin/install.md @@ -0,0 +1,81 @@ +# Installing the App in Nautobot + +Here you will find detailed instructions on how to **install** and **configure** the App within your Nautobot environment. + +!!! warning "Developer Note - Remove Me!" + Detailed instructions on installing the App. You will need to update this section based on any additional dependencies or prerequisites. + +## Prerequisites + +- The plugin is compatible with Nautobot 2.0.0 and higher. +- Databases supported: PostgreSQL, MySQL + +!!! note + Please check the [dedicated page](compatibility_matrix.md) for a full compatibility matrix and the deprecation policy. + +### Access Requirements + +!!! warning "Developer Note - Remove Me!" + What external systems (if any) it needs access to in order to work. + +## Install Guide + +!!! note + Plugins can be installed manually or using Python's `pip`. See the [nautobot documentation](https://nautobot.readthedocs.io/en/latest/plugins/#install-the-package) for more details. The pip package name for this plugin is [`nautobot-chatops`](https://pypi.org/project/nautobot-chatops/). + +The plugin is available as a Python package via PyPI and can be installed with `pip`: + +```shell +pip install nautobot-chatops +``` + +To ensure Nautobot ChatOps App is automatically re-installed during future upgrades, create a file named `local_requirements.txt` (if not already existing) in the Nautobot root directory (alongside `requirements.txt`) and list the `nautobot-chatops` package: + +```shell +echo nautobot-chatops >> local_requirements.txt +``` + +Once installed, the plugin needs to be enabled in your Nautobot configuration. The following block of code below shows the additional configuration required to be added to your `nautobot_config.py` file: + +- Append `"nautobot_chatops"` to the `PLUGINS` list. +- Append the `"nautobot_chatops"` dictionary to the `PLUGINS_CONFIG` dictionary and override any defaults. + +```python +# In your nautobot_config.py +PLUGINS = ["nautobot_chatops"] + +# PLUGINS_CONFIG = { +# "nautobot_chatops": { +# ADD YOUR SETTINGS HERE +# } +# } +``` + +Once the Nautobot configuration is updated, run the Post Upgrade command (`nautobot-server post_upgrade`) to run migrations and clear any cache: + +```shell +nautobot-server post_upgrade +``` + +Then restart (if necessary) the Nautobot services which may include: + +- Nautobot +- Nautobot Workers +- Nautobot Scheduler + +```shell +sudo systemctl restart nautobot nautobot-worker nautobot-scheduler +``` + +## App Configuration + +!!! warning "Developer Note - Remove Me!" + Any configuration required to get the App set up. Edit the table below as per the examples provided. + +The plugin behavior can be controlled with the following list of settings: + +| Key | Example | Default | Description | +| ------- | ------ | -------- | ------------------------------------- | +| `enable_backup` | `True` | `True` | A boolean to represent whether or not to run backup configurations within the plugin. | +| `platform_slug_map` | `{"cisco_wlc": "cisco_aireos"}` | `None` | A dictionary in which the key is the platform slug and the value is what netutils uses in any "network_os" parameter. | +| `per_feature_bar_width` | `0.15` | `0.15` | The width of the table bar within the overview report | diff --git a/docs/admin/release_notes/version_1.0.md b/docs/admin/release_notes/version_1.0.md new file mode 100644 index 00000000..5bdc1a9e --- /dev/null +++ b/docs/admin/release_notes/version_1.0.md @@ -0,0 +1,48 @@ +# v1.0 Release Notes + +!!! warning "Developer Note - Remove Me!" + Guiding Principles: + + - Changelogs are for humans, not machines. + - There should be an entry for every single version. + - The same types of changes should be grouped. + - Versions and sections should be linkable. + - The latest version comes first. + - The release date of each version is displayed. + - Mention whether you follow Semantic Versioning. + + Types of changes: + + - `Added` for new features. + - `Changed` for changes in existing functionality. + - `Deprecated` for soon-to-be removed features. + - `Removed` for now removed features. + - `Fixed` for any bug fixes. + - `Security` in case of vulnerabilities. + + +This document describes all new features and changes in the release `1.0`. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## Release Overview + +- Major features or milestones +- Achieved in this `x.y` release +- Changes to compatibility with Nautobot and/or other plugins, libraries etc. + +## [v1.0.1] - 2021-09-08 + +### Added + +### Changed + +### Fixed + +- [#123](https://github.com/nautobot/nautobot-plugin-chatops//issues/123) Fixed Tag filtering not working in job launch form + +## [v1.0.0] - 2021-08-03 + +### Added + +### Changed + +### Fixed diff --git a/docs/admin/uninstall.md b/docs/admin/uninstall.md index d1a18925..7ba42875 100644 --- a/docs/admin/uninstall.md +++ b/docs/admin/uninstall.md @@ -1,18 +1,18 @@ # Uninstall the App from Nautobot -## Uninstall Guide +Here you will find any steps necessary to cleanly remove the App from your Nautobot environment. -1. Remove Database migrations for ChatOps: +## Database Cleanup - ```bash - nautobot-server migrate nautobot-chatops zero - ``` +Prior to removing the plugin from the `nautobot_config.py`, run the following command to roll back any migration specific to this plugin. -2. Remove the configuration you added in `nautobot_config.py` from `PLUGINS` & `PLUGINS_CONFIG`. -3. Run Nautobot-server post_upgrade +```shell +nautobot-server migrate nautobot_plugin_chatops zero +``` - ```bash - nautobot-server post_ugprade - ``` +!!! warning "Developer Note - Remove Me!" + Any other cleanup operations to ensure the database is clean after the app is removed. Is there anything else that needs cleaning up, such as CFs, relationships, etc. if they're no longer desired? -4. Restart Nautobot Services +## Remove App configuration + +Remove the configuration you added in `nautobot_config.py` from `PLUGINS` & `PLUGINS_CONFIG`. diff --git a/docs/admin/upgrade.md b/docs/admin/upgrade.md index 31dde8bb..858c34d8 100644 --- a/docs/admin/upgrade.md +++ b/docs/admin/upgrade.md @@ -1,9 +1,10 @@ # Upgrading the App -## Upgrade Guide +Here you will find any steps necessary to upgrade the App in your Nautobot environment. -When a new release comes out it may be necessary to run a migration of the database to account for any changes in the data models used by this plugin. Execute the command `nautobot-server post_upgrade` from the Nautobot install nautobot/ directory after updating the package. +## Upgrade Guide -### Upgrading to ChatOps 3.0 +!!! warning "Developer Note - Remove Me!" + Add more detailed steps on how the app is upgraded in an existing Nautobot setup and any version specifics (such as upgrading between major versions with breaking changes). -Introduced in 3.0.0 is [Account Linking](../models/chatopsaccountlink.md), users will now need to link their Chat Platform User with their Nautobot User. Until this is done, the `fallback_chatops_user` setting controls the default Nautobot User and should have proper Nautobot Permissions applied. +When a new release comes out it may be necessary to run a migration of the database to account for any changes in the data models used by this plugin. Execute the command `nautobot-server post-upgrade` within the runtime environment of your Nautobot installation after updating the `nautobot-chatops` package via `pip`. diff --git a/docs/assets/extra.css b/docs/assets/extra.css index ce9ea9c8..dfe2e4b1 100644 --- a/docs/assets/extra.css +++ b/docs/assets/extra.css @@ -18,6 +18,15 @@ font-size: 0.7rem; } +/* +* The default max-width is 61rem which does not provide nearly enough space to present code examples or larger tables +*/ +.md-grid { + margin-left: auto; + margin-right: auto; + max-width: 95%; +} + .md-tabs__link { font-size: 0.8rem; } @@ -38,13 +47,8 @@ font-size: 1.2rem; } -/* Keep images in tables at 50px */ -.md-typeset table:not([class]) :is(img) { - height: 50px; -} - img.logo { - height: 100px; + height: 200px; } img.copyright-logo { @@ -91,7 +95,67 @@ a.autorefs-external:hover::after { background-color: var(--md-accent-fg-color); } -/* Do not wrap code blocks in markdown tables. */ -div.md-typeset__table>table>tbody>tr>td>code { - white-space: nowrap; + +/* Customization for mkdocs-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,'); + /* Icon for "version-changed" admonition: Material Design Icons "delta" */ + --md-admonition-icon--version-changed: url('data:image/svg+xml;charset=utf-8,'); + /* Icon for "version-removed" admonition: Material Design Icons "minus-circle-outline" */ + --md-admonition-icon--version-removed: url('data:image/svg+xml;charset=utf-8,'); +} + +/* "version-added" admonition in green */ +.md-typeset .admonition.version-added, +.md-typeset details.version-added { + border-color: rgb(0, 200, 83); +} + +.md-typeset .version-added>.admonition-title, +.md-typeset .version-added>summary { + background-color: rgba(0, 200, 83, .1); +} + +.md-typeset .version-added>.admonition-title::before, +.md-typeset .version-added>summary::before { + background-color: rgb(0, 200, 83); + -webkit-mask-image: var(--md-admonition-icon--version-added); + mask-image: var(--md-admonition-icon--version-added); +} + +/* "version-changed" admonition in orange */ +.md-typeset .admonition.version-changed, +.md-typeset details.version-changed { + border-color: rgb(255, 145, 0); +} + +.md-typeset .version-changed>.admonition-title, +.md-typeset .version-changed>summary { + background-color: rgba(255, 145, 0, .1); +} + +.md-typeset .version-changed>.admonition-title::before, +.md-typeset .version-changed>summary::before { + background-color: rgb(255, 145, 0); + -webkit-mask-image: var(--md-admonition-icon--version-changed); + mask-image: var(--md-admonition-icon--version-changed); +} + +/* "version-removed" admonition in red */ +.md-typeset .admonition.version-removed, +.md-typeset details.version-removed { + border-color: rgb(255, 82, 82); +} + +.md-typeset .version-removed>.admonition-title, +.md-typeset .version-removed>summary { + background-color: rgba(255, 82, 82, .1); +} + +.md-typeset .version-removed>.admonition-title::before, +.md-typeset .version-removed>summary::before { + background-color: rgb(255, 82, 82); + -webkit-mask-image: var(--md-admonition-icon--version-removed); + mask-image: var(--md-admonition-icon--version-removed); } diff --git a/docs/assets/overrides/partials/copyright.html b/docs/assets/overrides/partials/copyright.html index e2c55d12..b92cf5e3 100644 --- a/docs/assets/overrides/partials/copyright.html +++ b/docs/assets/overrides/partials/copyright.html @@ -1,9 +1,10 @@ + @@ -17,4 +18,5 @@ -
\ No newline at end of file +
+ diff --git a/docs/dev/arch_decision.md b/docs/dev/arch_decision.md new file mode 100644 index 00000000..e7bcbbe4 --- /dev/null +++ b/docs/dev/arch_decision.md @@ -0,0 +1,7 @@ +# Architecture Decision Records + +The intention is to document deviations from a standard Model View Controller (MVC) design. + +!!! warning "Developer Note - Remove Me!" + Optional page, remove if not applicable. + For examples see [Golden Config](https://github.com/nautobot/nautobot-plugin-golden-config/tree/develop/docs/dev/dev_adr.md) and [nautobot-plugin-reservation](https://github.com/networktocode/nautobot-plugin-reservation/blob/develop/docs/dev/dev_adr.md). diff --git a/docs/dev/code_reference/api.md b/docs/dev/code_reference/api.md index 01e7ce0a..f2bdd255 100644 --- a/docs/dev/code_reference/api.md +++ b/docs/dev/code_reference/api.md @@ -1,4 +1,4 @@ -# Nautobot Plugin ChatOps API Package +# Nautobot ChatOps App API Package ::: nautobot_chatops.api options: diff --git a/docs/dev/code_reference/index.md b/docs/dev/code_reference/index.md index 473f2c40..ebe9ff7d 100644 --- a/docs/dev/code_reference/index.md +++ b/docs/dev/code_reference/index.md @@ -1,3 +1,6 @@ # Code Reference Auto-generated code reference documentation from docstrings. + +!!! warning "Developer Note - Remove Me!" + Uses [mkdocstrings](https://mkdocstrings.github.io/) syntax to auto-generate code documentation from docstrings. Two example pages are provided ([api](api.md) and [package](package.md)), add new stubs for each module or package that you think has relevant documentation. diff --git a/docs/dev/code_reference/package.md b/docs/dev/code_reference/package.md new file mode 100644 index 00000000..664696ae --- /dev/null +++ b/docs/dev/code_reference/package.md @@ -0,0 +1 @@ +::: nautobot_chatops diff --git a/docs/dev/contributing.md b/docs/dev/contributing.md index 57cd6cb3..2337f740 100644 --- a/docs/dev/contributing.md +++ b/docs/dev/contributing.md @@ -1,4 +1,7 @@ -# Contributing to Nautobot ChatOps +# Contributing to the App + +!!! warning "Developer Note - Remove Me!" + Information on how to contribute fixes, functionality, or documentation changes back to the project. The project is packaged with a light [development environment](dev_environment.md) based on `docker-compose` to help with the local development of the project and to run tests. @@ -8,207 +11,14 @@ The project is following Network to Code software development guidelines and is - YAML linting is done with `yamllint`. - Django unit test to ensure the plugin is working properly. -Documentation is built using [mkdocs](https://www.mkdocs.org/). -The [Docker based development environment](dev_environment.md#docker-development-environment) automatically starts a container hosting a live version of the documentation website on [http://localhost:8001](http://localhost:8001) that auto-refreshes when you make any changes to your local files. - -## Creating Changelog Fragments - -All pull requests to `next` or `develop` must include a changelog fragment file in the `./changes` directory. To create a fragment, use your GitHub issue number and fragment type as the filename. For example, `2362.added`. Valid fragment types are `added`, `changed`, `deprecated`, `fixed`, `removed`, and `security`. The change summary is added to the file in plain text. Change summaries should be complete sentences, starting with a capital letter and ending with a period, and be in past tense. Each line of the change fragment will generate a single change entry in the release notes. Use multiple lines in the same file if your change needs to generate multiple release notes in the same category. If the change needs to create multiple entries in separate categories, create multiple files. - -!!! example - - **Wrong** - ```plaintext title="changes/1234.fixed" - fix critical bug in documentation - ``` - - **Right** - ```plaintext title="changes/1234.fixed" - Fixed critical bug in documentation. - ``` - -!!! example "Multiple Entry Example" - - This will generate 2 entries in the `fixed` category and one entry in the `changed` category. - - ```plaintext title="changes/1234.fixed" - Fixed critical bug in documentation. - Fixed release notes generation. - ``` - - ```plaintext title="changes/1234.changed" - Changed release notes generation. - ``` - -## Adding a new top-level command - -First, you should be familiar with the design goals and constraints involved in Nautobot (`design.md`). -Be sure that this is really what you want to do, versus adding a sub-command instead. - -We recommend that each command exist as its own submodule under `nautobot_chatops/workers/` (or, as a separate package -entirely, such as `nautobot_chatops_mycommand/worker.py`, using the `entrypoint/plugin` capability described in `design.md`) -to keep code files to a reasonable size and complexity. This submodule or package should implement a -`celery` worker function(s). In general this worker function shouldn't need to do much more than call -the `handle_subcommands` helper function provided: - -```python -# nautobot_chatops/workers/mycommand.py - -from nautobot_chatops.workers import handle_subcommands, subcommand_of - - -def mycommand(subcommand, **kwargs) - """Perform mycommand and its subcommands.""" - return handle_subcommands("mycommand", subcommand, **kwargs) -``` - -By using `handle_subcommands`, the top-level command worker will automatically recognize the sub-command "help", -as well as any sub-commands registered using the `@subcommand_of` decorator. - -You shouldn't need to make any changes to the `views` or `dispatchers` modules in this scenario. - -For usability, you should use the App Studio app in the Microsoft Teams client to update the bot settings -(`Nautobot_ms_teams.zip`) to include this new top-level command as a documented command supported by the bot. -You will probably then need to delete the bot deployment from your team and re-deploy it for the new command to appear. - -You will also need to log in to api.slack.com and add the new slash-command to your bot's configuration. - -## Adding a new sub-command - -First, you should be familiar with the design goals and constraints involved in Nautobot (`design.md`). - -To register a sub-command, write a function whose name matches the sub-command's name (any `_` in the function name -will be automatically converted to `-` for the sub-command name), and decorate it with the `@subcommand_of` decorator. -This function must take `dispatcher` (an instance of any `Dispatcher` subclass) as its first argument; any additional -positional arguments become arguments in the chat app UI. The docstring of this function will become the help text -displayed for this sub-command when a user invokes ` help`, so it should be concise and to the point. - -```python -from nautobot_chatops.workers import subcommand_of - -# ... - -@subcommand_of("mycommand") -def do_something(dispatcher, first_arg, second_arg): - """Do something with two arguments.""" - # ... -``` - -With the above code, the command `mycommand do_something [first_arg] [second_arg]` will now be available. - -You shouldn't need to make any changes to the `views` or `dispatchers` modules in this scenario. - -A sub-command worker function should always return one of the following: - -### `return False` - -This indicates that the function did not do anything meaningful, and it so should not be logged in Nautobot's -command log. Typically, this is only returned when not all required parameters have been provided by the user -and so the function needs to prompt the user for additional inputs, for example: - -```python -@subcommand_of("nautobot") -def get_rack(dispatcher, site_key, rack_id): - """Get information about a specific rack from Nautobot.""" - site_lt = LocationType.objects.get(name="Site") - if not site_key: - site_options = [(site.name, site.composite_key) for site in Location.objects.filter(location_type=site_lt)] - dispatcher.prompt_from_menu("nautobot get-rack", "Select a site (location)", site_options) - return False # command did not run to completion and therefore should not be logged - ... -``` - -### `return CommandStatusChoices.STATUS_SUCCEEDED` - -This indicates that the command was successful, and no further details are necessary in the logging. -You *could* return another status code besides `STATUS_SUCCEEDED` in this pattern, but in general any other status -code should be accompanied by an explanatory message: - -### `return (CommandStatusChoices.STATUS_FAILED, details_str)` - -This indicates that the command failed for some reason, which is provided for logging purposes. -You could also use other status codes (including `STATUS_SUCCEEDED`) for any other outcome that also requires -explanation. - -The provided `details_str` will be stored in the Nautobot command log history. - -## Adding support for a new chat platform (Webhooks) - -First, you should be familiar with the design goals and constraints involved in Nautobot (`design.md`). - -You'll need to add a new `nautobot_chatops.views.` submodule that provides any necessary API endpoints. - -You'll also need to add a new `nautobot_chatops.dispatchers.` submodule that implements an appropriate -subclass of `Dispatcher`. This new dispatcher class will need to implement any abstract methods of the base class -and override any other methods where platform-specific behavior is required (which will probably be most of them). - -You shouldn't need to make any changes to the `workers` module in this scenario. - -## Adding support for a new chat platform (WebSockets) - -First, you should be familiar with the design goals and constraints involved in Nautobot (`design.md`). - -You'll need to add a new `nautobot_chatops.sockets.` submodule that provides the necessary WebSockets connection to the Platform. - -You'll also need to add a new `nautobot_chatops.dispatchers.` submodule that implements an appropriate -subclass of `Dispatcher`. This new dispatcher class will need to implement any abstract methods of the base class -and override any other methods where platform-specific behavior is required (which will probably be most of them). - -Finally, you will need to add a new `nautobot_chatops.management.start__socket` management command that will start the WebSockets asynchronous loop. -In 2.0 these will likely be condensed to use a single base command with arguments to select the platform. - -You shouldn't need to make any changes to the `workers` module in this scenario. - -## Submitting Pull Requests - -- It is recommended to open an issue **before** starting work on a pull request, and discuss your idea with the Nautobot maintainers before beginning work. This will help prevent wasting time on something that we might not be able to implement. When suggesting a new feature, also make sure it won't conflict with any work that's already in progress. - -- Once you've opened or identified an issue you'd like to work on, ask that it - be assigned to you so that others are aware it's being worked on. A maintainer - will then mark the issue as "accepted." - -- If you followed the project guidelines, have ample tests, code quality, you will first be acknowledged for your work. So, thank you in advance! After that, the PR will be quickly reviewed to ensure that it makes sense as a contribution to the project, and to gauge the work effort or issues with merging into *current*. If the effort required by the core team isn’t trivial, it’ll likely still be a few weeks before it gets thoroughly reviewed and merged, thus it won't be uncommon to move it to *near term* with a `near-term` label. It will just depend on the current backlog. - -- All code submissions should meet the following criteria (CI will enforce -these checks): - - Python syntax is valid - - All unit tests pass successfully - - PEP 8 compliance is enforced, with the exception that lines may be - greater than 80 characters in length - - At least one [changelog fragment](#creating-changelog-fragments) has - been included in the feature branch +Documentation is built using [mkdocs](https://www.mkdocs.org/). The [Docker based development environment](dev_environment.md#docker-development-environment) automatically starts a container hosting a live version of the documentation website on [http://localhost:8001](http://localhost:8001) that auto-refreshes when you make any changes to your local files. ## Branching Policy -The branching policy includes the following tenets: - -- The `develop` branch is the primary branch to develop off of. -- PRs intended to add new features should be sourced from the `develop` branch. -- PRs intended to address bug fixes and security patches should be sourced from the `develop` branch. -- PRs intended to add new features that break backward compatibility should be discussed before a PR is created. - -Nautobot ChatOps app will observe semantic versioning, as of 1.0. This may result in a quick turn around in minor versions to keep pace with an ever-growing feature set. +!!! warning "Developer Note - Remove Me!" + What branching policy is used for this project and where contributions should be made. ## Release Policy -Nautobot ChatOps currently has no intended scheduled release schedule, and will release new features in minor versions. - -When a new release of any kind (e.g. from `develop` to `main`, or a release of a `stable-.`) is created the following should happen. - -- A release PR is created: - - Add and/or update to the changelog in `docs/admin/release_notes/version_..md` file to reflect the changes. - - Update the mkdocs.yml file to include updates when adding a new release_notes version file. - - Change the version from `..-beta` to `..` in `pyproject.toml`. - - Set the PR to the proper branch, e.g. either `main` or `stable-.`. -- Ensure the tests for the PR pass. -- Merge the PR. -- Create a new tag: - - The tag should be in the form of `v..`. - - The title should be in the form of `v..`. - - The description should be the changes that were added to the `version_..md` document. -- If merged into `main`, then push from `main` to `develop`, in order to retain the merge commit created when the PR was merged. -- If there is a new `.`, create a `stable-.` for the **previous** version, so that security updates to old versions may be applied more easily. -- A post release PR is created: - - Change the version from `..` to `..-beta` in `pyproject.toml`. - - Set the PR to the proper branch, e.g. either `develop` or `stable-.`. - - Once tests pass, merge. +!!! warning "Developer Note - Remove Me!" + How new versions are released. diff --git a/docs/dev/dev_environment.md b/docs/dev/dev_environment.md index 73f88702..2e1300e1 100644 --- a/docs/dev/dev_environment.md +++ b/docs/dev/dev_environment.md @@ -4,7 +4,7 @@ The development environment can be used in two ways: -1. **(Recommended)** All services, including Nautobot, are spun up using Docker containers and a volume mount, so you can develop locally. +1. **(Recommended)** All services, including Nautobot, are spun up using Docker containers and a volume mount so you can develop locally. 2. With a local Poetry environment if you wish to develop outside of Docker, with the caveat of using external services provided by Docker for the database (PostgreSQL by default, MySQL optionally) and Redis services. This is a quick reference guide if you're already familiar with the development environment provided, which you can read more about later in this document. @@ -13,9 +13,9 @@ This is a quick reference guide if you're already familiar with the development The [Invoke](http://www.pyinvoke.org/) library is used to provide some helper commands based on the environment. There are a few configuration parameters which can be passed to Invoke to override the default configuration: -- `nautobot_ver`: the version of Nautobot to use as a base for any built docker containers (default: latest) -- `project_name`: the default docker compose project name (default: `nautobot_chatops`) -- `python_ver`: the version of Python to use as a base for any built docker containers (default: 3.8) +- `nautobot_ver`: the version of Nautobot to use as a base for any built docker containers (default: 2.0.0) +- `project_name`: the default docker compose project name (default: `nautobot-chatops`) +- `python_ver`: the version of Python to use as a base for any built docker containers (default: 3.11) - `local`: a boolean flag indicating if invoke tasks should be run on the host or inside the docker containers (default: False, commands will be run in docker containers) - `compose_dir`: the full path to a directory containing the project compose files - `compose_files`: a list of compose files applied in order (see [Multiple Compose files](https://docs.docker.com/compose/extends/#multiple-compose-files) for more information) @@ -27,7 +27,7 @@ Using **Invoke** these configuration options can be overridden using [several me !!! tip This is the recommended option for development. -This project is managed by [Python Poetry](https://python-poetry.org/) and has a few requirements to set up your development environment: +This project is managed by [Python Poetry](https://python-poetry.org/) and has a few requirements to setup your development environment: 1. Install Poetry, see the [Poetry Documentation](https://python-poetry.org/docs/#installation) for your operating system. 2. Install Docker, see the [Docker documentation](https://docs.docker.com/get-docker/) for your operating system. @@ -35,21 +35,11 @@ This project is managed by [Python Poetry](https://python-poetry.org/) and has a Once you have Poetry and Docker installed you can run the following commands (in the root of the repository) to install all other development dependencies in an isolated Python virtual environment: ```shell -git clone git@github.com:nautobot/nautobot-plugin-chatops.git -cd nautobot-plugin-chatops poetry shell poetry install cp development/creds.example.env development/creds.env invoke build invoke start - -# Nautobot available as http://127.0.0.1:8080 admin / admin -# Mattermost available at http://127.0.0.1:8065 admin / admin - -# To allow Mattermost integration run the following after Nautobot starts: -invoke bootstrap-mattermost -``` - ``` The Nautobot server can now be accessed at [http://localhost:8080](http://localhost:8080) and the live documentation at [http://localhost:8001](http://localhost:8001). @@ -67,8 +57,6 @@ To either stop or destroy the development environment use the following options. --- nautobot_chatops: local: true - compose_files: - - "docker-compose.requirements.yml" ``` Run the following commands: @@ -76,7 +64,7 @@ Run the following commands: ```shell poetry shell poetry install --extras nautobot -export $(cat development/dev.env | xargs) +export $(cat development/development.env | xargs) export $(cat development/creds.env | xargs) invoke start && sleep 5 nautobot-server migrate @@ -93,7 +81,7 @@ nautobot-server runserver 0.0.0.0:8080 --insecure Nautobot server can now be accessed at [http://localhost:8080](http://localhost:8080). -It is typically recommended to launch the Nautobot `runserver` command in a separate shell, so you can keep developing and manage the web server separately. +It is typically recommended to launch the Nautobot **runserver** command in a separate shell so you can keep developing and manage the webserver separately. ### Updating the Documentation @@ -103,7 +91,7 @@ If you need to update any of the documentation dependencies to a newer version, ### CLI Helper Commands -The project features a CLI helper based on [Invoke](https://www.pyinvoke.org/) to help set up the development environment. The commands are listed below in 3 categories: +The project features a CLI helper based on [Invoke](https://www.pyinvoke.org/) to help setup the development environment. The commands are listed below in 3 categories: - `dev environment` - `utility` @@ -111,9 +99,6 @@ The project features a CLI helper based on [Invoke](https://www.pyinvoke.org/) t Each command can be executed with `invoke `. All commands support the arguments `--nautobot-ver` and `--python-ver` if you want to manually define the version of Python and Nautobot to use. Each command also has its own help `invoke --help` -!!! note - To run the MySQL (MariaDB) development environment, set the environment variable as such `export NAUTOBOT_USE_MYSQL=1`. - #### Local Development Environment ``` @@ -146,10 +131,9 @@ Each command can be executed with `invoke `. All commands support the a unittest Run Django unit tests for the plugin. ``` - ## Project Overview -This project provides the ability to develop and manage the Nautobot server locally (with supporting services being dockerized) or by using only Docker containers to manage Nautobot. The main difference between the two environments is the ability to debug and use **pdb** when developing locally. Debugging with **pdb** within the Docker container is more complicated, but can still be accomplished by either entering into the container (via `docker exec`) or attaching your IDE to the container and running the Nautobot service manually within the container. +This project provides the ability to develop and manage the Nautobot server locally (with supporting services being *Dockerized*) or by using only Docker containers to manage Nautobot. The main difference between the two environments is the ability to debug and use **pdb** when developing locally. Debugging with **pdb** within the Docker container is more complicated, but can still be accomplished by either entering into the container (via `docker exec`) or attaching your IDE to the container and running the Nautobot service manually within the container. The upside to having the Nautobot service handled by Docker rather than locally is that you do not have to manage the Nautobot server. The [Docker logs](#docker-logs) provide the majority of the information you will need to help troubleshoot, while getting started quickly and not requiring you to perform several manual steps and remembering to have the Nautobot server running in a separate terminal while you develop. @@ -160,14 +144,14 @@ Follow the directions below for the specific development environment that you ch ## Poetry -Poetry is used in lieu of the `virtualenv` commands and is leveraged in both environments. The virtual environment will provide all the Python packages required to manage the development environment such as **Invoke**. See the [Local Development Environment](#local-poetry-development-environment) section to see how to install Nautobot if you're going to be developing locally (i.e. not using the Docker container). +Poetry is used in lieu of the "virtualenv" commands and is leveraged in both environments. The virtual environment will provide all of the Python packages required to manage the development environment such as **Invoke**. See the [Local Development Environment](#local-poetry-development-environment) section to see how to install Nautobot if you're going to be developing locally (i.e. not using the Docker container). -The `pyproject.toml` file outlines all the relevant dependencies for the project: +The `pyproject.toml` file outlines all of the relevant dependencies for the project: - `tool.poetry.dependencies` - the main list of dependencies. -- `tool.poetry.dev-dependencies` - development dependencies, to facilitate linting, testing, and documentation building. +- `tool.poetry.group.dev.dependencies` - development dependencies, to facilitate linting, testing, and documentation building. -The `poetry shell` command is used to create and enable a virtual environment managed by Poetry, so all commands ran going forward are executed within the virtual environment. This is similar to running the `source venv/bin/activate` command with `virtualenv`. To install project dependencies in the virtual environment, you should run `poetry install` - this will install **both** project and development dependencies. +The `poetry shell` command is used to create and enable a virtual environment managed by Poetry, so all commands ran going forward are executed within the virtual environment. This is similar to running the `source venv/bin/activate` command with virtualenvs. To install project dependencies in the virtual environment, you should run `poetry install` - this will install **both** project and development dependencies. For more details about Poetry and its commands please check out its [online documentation](https://python-poetry.org/docs/). @@ -195,7 +179,7 @@ The first thing you need to do is build the necessary Docker image for Nautobot #14 exporting layers #14 exporting layers 1.2s done #14 writing image sha256:2d524bc1665327faa0d34001b0a9d2ccf450612bf8feeb969312e96a2d3e3503 done -#14 naming to docker.io/nautobot-chatops/nautobot:latest-py3.7 done +#14 naming to docker.io/nautobot-chatops/nautobot:2.0.0-py3.11 done ``` ### Invoke - Starting the Development Environment @@ -221,14 +205,14 @@ Creating nautobot_chatops_worker_1 ... done Docker Compose is now in the Docker CLI, try `docker compose up` ``` -This will start all containers used for hosting Nautobot. You should see the following containers running after `invoke start` is finished. +This will start all of the Docker containers used for hosting Nautobot. You should see the following containers running after `invoke start` is finished. ```bash ➜ docker ps ****CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -ee90fbfabd77 nautobot-chatops/nautobot:latest-py3.7 "nautobot-server rqw…" 16 seconds ago Up 13 seconds nautobot_chatops_worker_1 -b8adb781d013 nautobot-chatops/nautobot:latest-py3.7 "/docker-entrypoint.…" 20 seconds ago Up 15 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp nautobot_chatops_nautobot_1 -d64ebd60675d nautobot-chatops/nautobot:latest-py3.7 "mkdocs serve -v -a …" 25 seconds ago Up 18 seconds 0.0.0.0:8001->8080/tcp, :::8001->8080/tcp nautobot_chatops_docs_1 +ee90fbfabd77 nautobot-chatops/nautobot:2.0.0-py3.11 "nautobot-server rqw…" 16 seconds ago Up 13 seconds nautobot_chatops_worker_1 +b8adb781d013 nautobot-chatops/nautobot:2.0.0-py3.11 "/docker-entrypoint.…" 20 seconds ago Up 15 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp nautobot_chatops_nautobot_1 +d64ebd60675d nautobot-chatops/nautobot:2.0.0-py3.11 "mkdocs serve -v -a …" 25 seconds ago Up 18 seconds 0.0.0.0:8001->8080/tcp, :::8001->8080/tcp nautobot_chatops_docs_1 e72d63129b36 postgres:13-alpine "docker-entrypoint.s…" 25 seconds ago Up 19 seconds 0.0.0.0:5432->5432/tcp, :::5432->5432/tcp nautobot_chatops_postgres_1 96c6ff66997c redis:6-alpine "docker-entrypoint.s…" 25 seconds ago Up 21 seconds 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp nautobot_chatops_redis_1 ``` @@ -243,14 +227,14 @@ Once the containers are fully up, you should be able to open up a web browser, a ### Invoke - Creating a Superuser -The Nautobot development image will automatically provision a super-user when specifying the following variables within `creds.env` which is the default when copying `creds.example.env` to `creds.env`. +The Nautobot development image will automatically provision a super user when specifying the following variables within `creds.env` which is the default when copying `creds.example.env` to `creds.env`. - `NAUTOBOT_CREATE_SUPERUSER=true` - `NAUTOBOT_SUPERUSER_API_TOKEN=0123456789abcdef0123456789abcdef01234567` - `NAUTOBOT_SUPERUSER_PASSWORD=admin` !!! note - The default username is **admin**, but can be overridden by specifying `NAUTOBOT_SUPERUSER_USERNAME`. + The default username is **admin**, but can be overridden by specifying **NAUTOBOT_SUPERUSER_USERNAME**. If you need to create additional superusers, run the follow commands. @@ -313,7 +297,7 @@ The magic here is the root directory is mounted inside your Docker containers wh !!! warning There are a few exceptions to this, as outlined in the section [To Rebuild or Not To Rebuild](#to-rebuild-or-not-to-rebuild). -The back-end Django process is set up to automatically reload itself (it only takes a couple of seconds) every time a file is updated (saved). So for example, if you were to update one of the files like `tables.py`, then save it, the changes will be visible right away in the web browser! +The back-end Django process is setup to automatically reload itself (it only takes a couple of seconds) every time a file is updated (saved). So for example, if you were to update one of the files like `tables.py`, then save it, the changes will be visible right away in the web browser! !!! note You may get connection refused while Django reloads, but it should be refreshed fairly quickly. @@ -323,13 +307,16 @@ The back-end Django process is set up to automatically reload itself (it only ta When trying to debug an issue, one helpful thing you can look at are the logs within the Docker containers. ```bash -➜ invoke logs --follow +➜ docker logs -f ``` !!! note - The `--follow` tag will keep the logs open, and output them in real-time as they are generated. + The `-f` tag will keep the logs open, and output them in realtime as they are generated. + +!!! info + Want to limit the log output even further? Use the `--tail <#>` command line argument in conjunction with `-f`. -So for example, `invoke logs --service nautobot --follow` will follow logs from `nautobot` docker compose service. You can find all running services via `invoke ps`. +So for example, our plugin is named `nautobot-chatops`, the command would most likely be `docker logs nautobot_chatops_nautobot_1 -f`. You can find the name of all running containers via `docker ps`. If you want to view the logs specific to the worker container, simply use the name of that container instead. @@ -337,13 +324,13 @@ If you want to view the logs specific to the worker container, simply use the na Most of the time, you will not need to rebuild your images. Simply running `invoke start` and `invoke stop` is enough to keep your environment going. -However, there are a couple of instances when you will want to. +However there are a couple of instances when you will want to. ### Updating Environment Variables To add environment variables to your containers, thus allowing Nautobot to use them, you will update/add them in the `development/development.env` file. However, doing so is considered updating the underlying container shell, instead of Django (which auto restarts itself on changes). -To get new environment variables to take effect, you will need to stop any running images, rebuild the images, then restart them. This can easily be done with 3 commands: +To get new environment variables to take effect, you will need stop any running images, rebuild the images, then restart them. This can easily be done with 3 commands: ```bash ➜ invoke stop @@ -372,7 +359,7 @@ Once the dependencies are resolved, stop the existing containers, rebuild the Do ### Installing Additional Nautobot Plugins -Let's say for example you want the new plugin you're creating to integrate into Nautobot ChatOps. To do this, you will want to integrate into the existing Nautobot ChatOps Plugin. +Let's say for example you want the new plugin you're creating to integrate into Slack. To do this, you will want to integrate into the existing Nautobot ChatOps Plugin. ```bash ➜ poetry shell @@ -392,7 +379,7 @@ Before you continue, you'll need to update the file `development/nautobot_config Once the containers are up and running, you should now see the new plugin installed in your Nautobot instance. !!! note - You can even launch `ngrok` service locally on your laptop, pointing to port 8080 (such as for ChatOps development), and it will point traffic directly to your Docker images. + You can even launch an `ngrok` service locally on your laptop, pointing to port 8080 (such as for chatops development), and it will point traffic directly to your Docker images. ### Updating Python Version @@ -404,14 +391,14 @@ namespace.configure( { "nautobot_chatops": { ... - "python_ver": "3.7", + "python_ver": "3.11", ... } } ) ``` -Or set the `INVOKE_NAUTOBOT_GOLDEN_CONFIG_PYTHON_VER` variable. +Or set the `INVOKE_NAUTOBOT_CHATOPS_PYTHON_VER` variable. ### Updating Nautobot Version @@ -423,7 +410,7 @@ namespace.configure( { "nautobot_chatops": { ... - "nautobot_ver": "1.0.2", + "nautobot_ver": "2.0.0", ... } } @@ -466,7 +453,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 TravisCI runs against any new PR with: ```bash ➜ invoke tests diff --git a/docs/dev/extending.md b/docs/dev/extending.md new file mode 100644 index 00000000..49b89f46 --- /dev/null +++ b/docs/dev/extending.md @@ -0,0 +1,6 @@ +# Extending the App + +!!! warning "Developer Note - Remove Me!" + Information on how to extend the App functionality. + +Extending the application is welcome, however it is best to open an issue first, to ensure that a PR would be accepted and makes sense in terms of features and design. diff --git a/docs/images/icon-nautobot-chatops.png b/docs/images/icon-nautobot-chatops.png new file mode 100644 index 00000000..7e00cf6a Binary files /dev/null and b/docs/images/icon-nautobot-chatops.png differ diff --git a/docs/index.md b/docs/index.md index f8e5c92f..f32fd72b 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,4 +3,4 @@ hide: - navigation --- ---8<-- "README.md" \ No newline at end of file +--8<-- "README.md" diff --git a/docs/requirements.txt b/docs/requirements.txt index 0da7e270..d168c88f 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,8 +1,5 @@ -griffe==0.30.1 -mkdocs==1.3.1 -mkdocs-material==8.4.2 +mkdocs==1.5.2 +mkdocs-material==9.1.15 mkdocs-version-annotations==1.0.0 +mkdocstrings-python==1.5.2 mkdocstrings==0.22.0 -mkdocstrings-python==1.1.2 -Jinja2==3.0.3 -mkdocs-include-markdown-plugin==3.6.1 \ No newline at end of file diff --git a/docs/user/app_getting_started.md b/docs/user/app_getting_started.md index c2da634b..24ea0099 100644 --- a/docs/user/app_getting_started.md +++ b/docs/user/app_getting_started.md @@ -1,61 +1,19 @@ # Getting Started with the App -A step-by-step tutorial on how to get the App going and how to use it. +This document provides a step-by-step tutorial on how to get the App going and how to use it. ## Install the App -To install the App, please follow the instructions detailed in the [Administrator Guide](../admin/install/index.md). +To install the App, please follow the instructions detailed in the [Installation Guide](../admin/install.md). -## Link Nautobot Account +## First steps with the App -+++3.0.0 +!!! warning "Developer Note - Remove Me!" + What (with screenshots preferably) does it look like to perform the simplest workflow within the App once installed? -Nautobot ChatOps now uses the built-in Nautobot permissions for Nautobot Objects (Devices, Locations, Racks, etc.). Each user will need to link their Nautobot Account with their Chat Platform User Account. Login to Nautobot then access the Link ChatOps Account within the Plugins menu. Here you can provide your email address and select the ChatOps Platform you are using, then click the Look up User ID from Email to get your Chat User ID. +## What are the next steps? -![Link Accounts](../images/account_link.png) +!!! warning "Developer Note - Remove Me!" + After taking the first steps, what else could the users look at doing. -## Built-in Commands - -Each command can be invoked with `help` sub-command to display all sub-commands with the description. - -### `/clear` Command - -Scroll the chat history out of view. This command has no sub-commands. - -### `/nautobot` Command - -Interact with Nautobot by utilizing the following sub-commands: - -| Command | Arguments | Description | -| ------- | --------- | ----------- | -| `about` || Provide a link for more information on Nautobot Apps. | -| `change-device-status` | `[device-name]` `[status]` | Set the status of a device in Nautobot. | -| `get-circuit-connections` | `[provider-slug]` `[circuit-id]` | For a given circuit, find the objects the circuit connects to. | -| `get-circuit-providers` || Get a list of circuit providers. | -| `get-circuits` | `[filter-type]` `[filter-value]` | Get a filtered list of circuits from Nautobot. | -| `get-device-facts` | `[device-name]` | Get detailed facts about a device from Nautobot in YAML format. | -| `get-device-status` | `[device-name]` | Get the status of a device in Nautobot. | -| `get-devices` | `[filter-type]` `[filter-value]` | Get a filtered list of devices from Nautobot. | -| `get-interface-connections` | `[filter-type]` `[filter-value-1]` `[filter-value-2]` | Return a filtered list of interface connections based on filter type, `filter_value_1` and/or `filter_value_2`. | -| `get-manufacturer-summary` || Provides a summary of each manufacturer and how many devices have that manufacturer. | -| `get-rack` | `[site-slug]` `[rack-id]` | Get information about a specific rack from Nautobot. | -| `get-vlans` | `[filter-type]` `[filter-value-1]` | Return a filtered list of VLANs based on filter type and/or `filter_value_1`. | - -!!! note - All sub-commands are intended to be used with the `nautobot` prefix. For example, to retrieve a filtered list of VLANs, use the command `/nautobot get-vlans`. - -+/- 3.0.0 - Due to the removal of slug in Nautobot 2.0, the command shortcuts will use the PK value of an object. This will be - changed to the Natural Key or PK in an upcoming release. - -### Integrations Commands - -The `nautobot-chatops` package includes multiple integrations. Each integration adds chat commands described here: - -- [Cisco ACI](./aci_commands.md) -- [AWX / Ansible Tower](./ansible_commands.md) -- [Arista CloudVision](./aristacv_commands.md) -- [Grafana](./grafana_commands.md) -- [IPFabric](./ipfabric_commands.md) -- [Cisco Meraki](./meraki_commands.md) -- [Palo Alto Panorama](./panorama_commands.md) +You can check out the [Use Cases](app_use_cases.md) section for more examples. diff --git a/docs/user/app_overview.md b/docs/user/app_overview.md index b2af3cca..06ff5d32 100644 --- a/docs/user/app_overview.md +++ b/docs/user/app_overview.md @@ -1,26 +1,30 @@ # App Overview -The ChatOps plugin is a Nautobot Plugin that provides a Chatbot framework for Nautobot. +This document provides an overview of the App including critical information and import considerations when applying it to your Nautobot environment. -## Description/Overview +!!! note + Throughout this documentation, the terms "app" and "plugin" will be used interchangeably. -The ChatOps framework provides Network Engineers power to query Nautobot or their Network (through ChatOps plugins) while staying within their preferred Chat application. The goal of ChatOps is to bring people, processes and Automation together. +## Description -{% - include-markdown '../glossary.md' - heading-offset=1 -%} ## Audience (User Personas) - Who should use this App? -- Network Engineers -- Network Automation Engineers -- Site Reliability Engineers -- Network Operations +!!! warning "Developer Note - Remove Me!" + Who is this meant for/ who is the common user of this app? ## Authors and Maintainers -- Glenn Matthews (@glennmatthews) -- Josh VanDeraa (@jvanderaa) -- Jeremy White (@whitej6) -- Stephen Kiely (@smk4664) +!!! warning "Developer Note - Remove Me!" + Add the team and/or the main individuals maintaining this project. Include historical maintainers as well. + +## Nautobot Features Used + +!!! warning "Developer Note - Remove Me!" + What is shown today in the Installed Plugins page in Nautobot. What parts of Nautobot does it interact with, what does it add etc. ? + +### Extras + +!!! warning "Developer Note - Remove Me!" + Custom Fields - things like which CFs are created by this app? + Jobs - are jobs, if so, which ones, installed by this app? diff --git a/docs/user/app_use_cases.md b/docs/user/app_use_cases.md new file mode 100644 index 00000000..dc06944f --- /dev/null +++ b/docs/user/app_use_cases.md @@ -0,0 +1,12 @@ +# Using the App + +This document describes common use-cases and scenarios for this App. + +## General Usage + +## Use-cases and common workflows + +## Screenshots + +!!! warning "Developer Note - Remove Me!" + Ideally captures every view exposed by the App. Should include a relevant dataset. diff --git a/docs/user/external_interactions.md b/docs/user/external_interactions.md new file mode 100644 index 00000000..eaba5b56 --- /dev/null +++ b/docs/user/external_interactions.md @@ -0,0 +1,17 @@ +# External Interactions + +This document describes external dependencies and prerequisites for this App to operate, including system requirements, API endpoints, interconnection or integrations to other applications or services, and similar topics. + +!!! warning "Developer Note - Remove Me!" + Optional page, remove if not applicable. + +## External System Integrations + +### From the App to Other Systems + +### From Other Systems to the App + +## Nautobot REST API endpoints + +!!! warning "Developer Note - Remove Me!" + API documentation in this doc - including python request examples, curl examples, postman collections referred etc. diff --git a/docs/user/faq.md b/docs/user/faq.md new file mode 100644 index 00000000..318b08dc --- /dev/null +++ b/docs/user/faq.md @@ -0,0 +1 @@ +# Frequently Asked Questions diff --git a/invoke.example.yml b/invoke.example.yml index 90872991..df0a8ebc 100644 --- a/invoke.example.yml +++ b/invoke.example.yml @@ -1,14 +1,12 @@ --- nautobot_chatops: project_name: "nautobot-chatops" - nautobot_ver: "1.5.4" + nautobot_ver: "2.0.0" local: false - python_ver: "3.8" + python_ver: "3.11" compose_dir: "development" compose_files: - "docker-compose.base.yml" - "docker-compose.redis.yml" - "docker-compose.postgres.yml" - - "mattermost/docker-compose.yml" - - "ansible/docker-compose.yml" - "docker-compose.dev.yml" diff --git a/invoke.mysql.yml b/invoke.mysql.yml index 20cebb59..e7372544 100644 --- a/invoke.mysql.yml +++ b/invoke.mysql.yml @@ -1,14 +1,12 @@ --- nautobot_chatops: project_name: "nautobot-chatops" - nautobot_ver: "1.5.4" + nautobot_ver: "2.0.0" local: false - python_ver: "3.8" + python_ver: "3.11" compose_dir: "development" compose_files: - "docker-compose.base.yml" - "docker-compose.redis.yml" - "docker-compose.mysql.yml" - - "mattermost/docker-compose.yml" - - "ansible/docker-compose.yml" - "docker-compose.dev.yml" diff --git a/mkdocs.yml b/mkdocs.yml index 0f8d2034..b0cf8820 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,11 +1,11 @@ --- dev_addr: "127.0.0.1:8001" -edit_uri: "edit/develop/docs" +edit_uri: "edit/main/nautobot-plugin-chatops/docs" site_dir: "nautobot_chatops/static/nautobot_chatops/docs" -site_name: "Nautobot ChatOps Documentation" -site_url: "https://docs.nautobot.com/projects/chatops/en/stable/" -repo_url: "https://github.com/nautobot/nautobot-plugin-chatops" -copyright: "Copyright © 2020 Network to Code" +site_name: "Nautobot ChatOps App Documentation" +site_url: "https://docs.nautobot.com/projects/chatops/en/latest/" +repo_url: "https://github.com/nautobot/nautobot-plugin-chatops/" +copyright: "Copyright © The Authors" theme: name: "material" navigation_depth: 4 @@ -14,16 +14,17 @@ theme: - "django" - "yaml" features: - - "navigation.tracking" + - "content.action.edit" + - "content.action.view" + - "content.code.copy" + - "navigation.footer" + - "navigation.indexes" - "navigation.tabs" - "navigation.tabs.sticky" - - "navigation.footer" - - "search.suggest" + - "navigation.tracking" - "search.highlight" - "search.share" - - "navigation.indexes" - - "content.action.edit" - - "content.action.view" + - "search.suggest" favicon: "assets/favicon.ico" logo: "assets/nautobot_logo.svg" palette: @@ -84,7 +85,6 @@ markdown_extensions: - "footnotes" plugins: - "search" - - "include-markdown" - "mkdocs-version-annotations" - "mkdocstrings": default_handler: "python" @@ -101,64 +101,24 @@ nav: - User Guide: - App Overview: "user/app_overview.md" - Getting Started: "user/app_getting_started.md" - - Frequently Asked Questions: "user/app_faq.md" - - Integrations: - - "user/aci_commands.md" - - "user/ansible_commands.md" - - "user/aristacv_commands.md" - - "user/grafana_commands.md" - - "user/ipfabric_commands.md" - - "user/meraki_commands.md" - - "user/panorama_commands.md" + - Using the App: "user/app_use_cases.md" + - Frequently Asked Questions: "user/faq.md" + - External Interactions: "user/external_interactions.md" - Administrator Guide: - - Install and Configure: - - "admin/install/index.md" - - "admin/install/slack_setup.md" - - "admin/install/mattermost_setup.md" - - "admin/install/webex_setup.md" - - "admin/install/microsoft_teams_setup.md" - - "admin/install/aci_setup.md" - - "admin/install/ansible_setup.md" - - "admin/install/aristacv_setup.md" - - "admin/install/grafana_setup.md" - - "admin/install/ipfabric_setup.md" - - "admin/install/meraki_setup.md" - - "admin/install/panorama_setup.md" + - Install and Configure: "admin/install.md" - Upgrade: "admin/upgrade.md" - Uninstall: "admin/uninstall.md" - Compatibility Matrix: "admin/compatibility_matrix.md" - Release Notes: - "admin/release_notes/index.md" - - v3.0: "admin/release_notes/version_3.0.md" - - v2.1: "admin/release_notes/version_2.1.md" - - v2.0: "admin/release_notes/version_2.0.md" - - v1.11: "admin/release_notes/version_1.11.md" - - v1.10: "admin/release_notes/version_1.10.md" - - v1.9: "admin/release_notes/version_1.9.md" - - v1.8: "admin/release_notes/version_1.8.md" - - v1.7: "admin/release_notes/version_1.7.md" - - v1.6: "admin/release_notes/version_1.6.md" - - v1.5: "admin/release_notes/version_1.5.md" - - v1.4: "admin/release_notes/version_1.4.md" - - v1.3: "admin/release_notes/version_1.3.md" - - v1.2: "admin/release_notes/version_1.2.md" - - v1.1: "admin/release_notes/version_1.1.md" + - v1.0: "admin/release_notes/version_1.0.md" - Developer Guide: + - Extending the App: "dev/extending.md" - Contributing to the App: "dev/contributing.md" - - Design Documentation: "dev/design.md" - Development Environment: "dev/dev_environment.md" - - Release Checklist: "dev/release_checklist.md" - - Glossary: "glossary.md" + - Architecture Decision Records: "dev/arch_decision.md" - Code Reference: - "dev/code_reference/index.md" - - Models: "dev/code_reference/models.md" + - Package: "dev/code_reference/package.md" - API: "dev/code_reference/api.md" - - Dispatchers: - - "dev/code_reference/base.md" - - "dev/code_reference/slack.md" - - "dev/code_reference/webex.md" - - "dev/code_reference/ms_teams.md" - - "dev/code_reference/mattermost.md" - - "models/accessgrant.md" - - "models/commandtoken.md" - Nautobot Docs Home ↗︎: "https://docs.nautobot.com" diff --git a/nautobot_chatops/__init__.py b/nautobot_chatops/__init__.py index dd1c8c6f..0ef2d619 100644 --- a/nautobot_chatops/__init__.py +++ b/nautobot_chatops/__init__.py @@ -1,162 +1,26 @@ -"""Nautobot plugin implementing a chatbot.""" -try: - from importlib import metadata -except ImportError: - # Python version < 3.8 - import importlib_metadata as metadata +"""Plugin declaration for nautobot_chatops.""" +# Metadata is inherited from Nautobot. If not including Nautobot in the environment, this should be added +from importlib import metadata __version__ = metadata.version(__name__) -from django.conf import settings -from nautobot.apps import ConstanceConfigItem, NautobotAppConfig +from nautobot.extras.plugins import NautobotAppConfig -_CONFLICTING_APP_NAMES = [ - # App names that conflict with nautobot_chatops - "nautobot_plugin_chatops_aci", - "nautobot_plugin_chatops_ansible", - "nautobot_plugin_chatops_aristacv", - "nautobot_plugin_chatops_grafana", - "nautobot_plugin_chatops_ipfabric", - "nautobot_plugin_chatops_meraki", - "nautobot_plugin_chatops_panorama", -] - - -def _check_for_conflicting_apps(): - intersection = set(_CONFLICTING_APP_NAMES).intersection(set(settings.PLUGINS)) - if intersection: - raise RuntimeError( - f"The following apps are installed and conflict with `nautobot-chatops`: {', '.join(intersection)}." - ) - - -_check_for_conflicting_apps() - - -class NautobotChatOpsConfig(NautobotAppConfig): +class NautobotChatOpsPluginConfig(NautobotAppConfig): """Plugin configuration for the nautobot_chatops plugin.""" name = "nautobot_chatops" - verbose_name = "Nautobot ChatOps" + verbose_name = "Nautobot ChatOps App" version = __version__ - author = "Network to Code" - author_email = "opensource@networktocode.com" - description = """ - Nautobot App that is a multi-platform chatbot supporting Slack, MS Teams, Cisco Webex, - and Mattermost that simplifies creating chat commands with pre-defined design patterns. - Includes the 'nautobot' command that simplifies fetching and updating data in Nautobot. - """ + author = "Network to Code, LLC" + description = "Nautobot ChatOps App." base_url = "chatops" required_settings = [] - default_settings = { - # = Common Settings ================== - # Should menus, text input fields, etc. be deleted from the chat history after the user makes a selection? - "delete_input_on_submission": False, - "restrict_help": False, - # As requested on https://github.com/nautobot/nautobot-plugin-chatops/issues/114 this setting is used for - # sending all messages as an ephemeral message, meaning only the person interacting with the bot will see the - # responses. - "send_all_messages_private": False, - # Session Cache - "session_cache_timeout": 86400, - # = Chat Platforms =================== - # - Mattermost ----------------------- - "mattermost_api_token": "", - "mattermost_url": "", - # - Microsoft Teams ------------------ - "microsoft_app_id": "", - "microsoft_app_password": "", - # - Slack ---------------------------- - "slack_api_token": "", # for example, "xoxb-123456" - "slack_signing_secret": "", - "slack_ephemeral_message_size_limit": 3000, - # Any prefix that's prepended to all slash-commands for this bot and should be stripped away - # in order to identify the actual command name to be invoked, eg "/nautobot-" - "slack_slash_command_prefix": "/", - # Since Slack Socket is meant keep Nautobot server out of public access, slack needs to know - # where to find Static images. If Django Storages is configured with an External server like S3, - # this can be ignored. - # If neither option is provided, then no static images (like Nautobot Logo) will be shown. - "slack_socket_static_host": "", - # - Cisco Webex ---------------------- - "webex_token": "", - "webex_signing_secret": "", - "webex_msg_char_limit": 7439, - # = Integrations ===================== - # - Cisco ACI ------------------------ - "aci_creds": "", - # - AWX / Ansible Tower -------------- - "tower_password": "", - "tower_uri": "", - "tower_username": "", - "tower_verify_ssl": True, - # - Arista CloudVision --------------- - "aristacv_cvaas_url": "www.arista.io:443", - "aristacv_cvaas_token": "", - "aristacv_cvp_host": "", - "aristacv_cvp_insecure": False, - "aristacv_cvp_password": "", - "aristacv_cvp_username": "", - "aristacv_on_prem": False, - # - Grafana -------------------------- - "grafana_url": "", - "grafana_api_key": "", - "grafana_default_width": 0, - "grafana_default_height": 0, - "grafana_default_theme": "dark", - "grafana_default_timespan": "", - "grafana_org_id": 1, - "grafana_default_tz": "", - # - IPFabric --------------------- - "ipfabric_api_token": "", - "ipfabric_host": "", - "ipfabric_timeout": "", - "ipfabric_verify": False, - # - Cisco Meraki --------------------- - "meraki_dashboard_api_key": "", - # - Palo Alto Panorama --------------- - "panorama_host": "", - "panorama_password": "", - "panorama_user": "", - } - constance_config = { - "fallback_chatops_user": ConstanceConfigItem(default="chatbot", help_text="Enable Mattermost Chat Platform."), - "enable_mattermost": ConstanceConfigItem( - default=False, help_text="Enable Mattermost Chat Platform.", field_type=bool - ), - "enable_ms_teams": ConstanceConfigItem( - default=False, help_text="Enable Microsoft Teams Chat Platform.", field_type=bool - ), - "enable_slack": ConstanceConfigItem(default=False, help_text="Enable Slack Chat Platform.", field_type=bool), - "enable_webex": ConstanceConfigItem(default=False, help_text="Enable Webex Chat Platform.", field_type=bool), - "enable_aci": ConstanceConfigItem(default=False, help_text="Enable Cisco ACI Integration.", field_type=bool), - "enable_ansible": ConstanceConfigItem(default=False, help_text="Enable Ansible Integration.", field_type=bool), - "enable_aristacv": ConstanceConfigItem( - default=False, help_text="Enable Arista CloudVision Integration.", field_type=bool - ), - "enable_grafana": ConstanceConfigItem(default=False, help_text="Enable Grafana Integration.", field_type=bool), - "enable_ipfabric": ConstanceConfigItem( - default=False, help_text="Enable IP Fabric Integration.", field_type=bool - ), - "enable_meraki": ConstanceConfigItem( - default=False, help_text="Enable Cisco Meraki Integration.", field_type=bool - ), - "enable_panorama": ConstanceConfigItem( - default=False, help_text="Enable Panorama Integration.", field_type=bool - ), - } - + min_version = "2.0.0" + max_version = "2.9999" + default_settings = {} caching_config = {} - def ready(self): - """Function invoked after all plugins have been loaded.""" - 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) - -config = NautobotChatOpsConfig # pylint:disable=invalid-name +config = NautobotChatOpsPluginConfig # pylint:disable=invalid-name diff --git a/nautobot_chatops/api/__init__.py b/nautobot_chatops/api/__init__.py index e69de29b..81763b58 100644 --- a/nautobot_chatops/api/__init__.py +++ b/nautobot_chatops/api/__init__.py @@ -0,0 +1 @@ +"""REST API module for nautobot_chatops plugin.""" diff --git a/nautobot_chatops/tests/__init__.py b/nautobot_chatops/tests/__init__.py index 90a42e97..102ef4fd 100644 --- a/nautobot_chatops/tests/__init__.py +++ b/nautobot_chatops/tests/__init__.py @@ -1 +1 @@ -"""Test for the nautobot_chatops plugin.""" +"""Unit tests for nautobot_chatops plugin.""" diff --git a/nautobot_chatops/tests/test_api.py b/nautobot_chatops/tests/test_api.py index eb319a70..4c3e56ad 100644 --- a/nautobot_chatops/tests/test_api.py +++ b/nautobot_chatops/tests/test_api.py @@ -1,84 +1,28 @@ -"""Test cases for Nautobot Chatops API.""" -try: - from importlib import metadata -except ImportError: - # Python version < 3.8 - import importlib_metadata as metadata - +"""Unit tests for nautobot_chatops.""" +from django.contrib.auth import get_user_model +from django.test import TestCase from django.urls import reverse +from rest_framework import status +from rest_framework.test import APIClient -from nautobot.core.testing import APITestCase, APIViewTestCases -from nautobot_chatops.models import AccessGrant, CommandToken - - -nautobot_version = metadata.version("nautobot") - - -class AppTest(APITestCase): # pylint: disable=too-many-ancestors - """Test cases for the Nautobot_chatops App.""" - - def test_root(self): - """Validate the root for Nautobot Chatops API.""" - url = reverse("plugins-api:nautobot_chatops-api:api-root") - response = self.client.get(f"{url}?format=api", **self.header) - - self.assertEqual(response.status_code, 200) - - -class CommandTokenTest(APIViewTestCases.APIViewTestCase): # pylint: disable=too-many-ancestors - """Tests for the CommandToken Endpoint.""" - - model = CommandToken - brief_fields = ["comment", "display", "id", "platform", "token", "url"] - # Nautobot 1.4.0 added created/last_updated to builtin serializers. - if nautobot_version >= "1.4.0": - brief_fields = ["comment", "created", "display", "id", "last_updated", "platform", "token", "url"] - create_data = [ - {"comment": "Test 4", "platform": "mattermost", "token": "token4"}, - {"comment": "Test 5", "platform": "mattermost", "token": "token5"}, - {"comment": "Test 6", "platform": "mattermost", "token": "token6"}, - ] - bulk_update_data = {"comment": "Testing"} - choices_fields = ["platform"] +from nautobot.users.models import Token - @classmethod - def setUpTestData(cls): - """Generate test data for the CommandToken Endpoint.""" - CommandToken.objects.create(comment="Test 1", platform="mattermost", token="token1") - CommandToken.objects.create(comment="Test 2", platform="mattermost", token="token2") - CommandToken.objects.create(comment="Test 3", platform="mattermost", token="token3") +User = get_user_model() -class AccessGrantTest(APIViewTestCases.APIViewTestCase): # pylint: disable=too-many-ancestors - """Tests for the AccessGrant Endpoint.""" +class PlaceholderAPITest(TestCase): + """Test the NautobotChatOpsPlugin API.""" - model = AccessGrant - brief_fields = ["command", "display", "grant_type", "id", "name", "subcommand", "url", "value"] - # Nautobot 1.4.0 added created/last_updated to builtin serializers. - if nautobot_version >= "1.4.0": - brief_fields = [ - "command", - "created", - "display", - "grant_type", - "id", - "last_updated", - "name", - "subcommand", - "url", - "value", - ] - create_data = [ - {"command": "*", "subcommand": "*", "grant_type": "organization", "name": "test4", "value": "*"}, - {"command": "*", "subcommand": "*", "grant_type": "channel", "name": "test5", "value": "*"}, - {"command": "*", "subcommand": "*", "grant_type": "user", "name": "test6", "value": "*"}, - ] - bulk_update_data = {"command": "nautobot"} - choices_fields = ["grant_type"] + def setUp(self): + """Create a superuser and token for API calls.""" + self.user = User.objects.create(username="testuser", is_superuser=True) + self.token = Token.objects.create(user=self.user) + self.client = APIClient() + self.client.credentials(HTTP_AUTHORIZATION=f"Token {self.token.key}") - @classmethod - def setUpTestData(cls): - """Generate test data for the AccessGrant Endpoint.""" - AccessGrant.objects.create(command="*", subcommand="*", grant_type="organization", name="test1", value="test1") - AccessGrant.objects.create(command="*", subcommand="*", grant_type="channel", name="test2", value="test2") - AccessGrant.objects.create(command="*", subcommand="*", grant_type="user", name="test3", value="test3") + def test_placeholder(self): + """Verify that devices can be listed.""" + url = reverse("dcim-api:device-list") + response = self.client.get(url) + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data["count"], 0) diff --git a/nautobot_chatops/tests/test_basic.py b/nautobot_chatops/tests/test_basic.py new file mode 100644 index 00000000..9a096567 --- /dev/null +++ b/nautobot_chatops/tests/test_basic.py @@ -0,0 +1,34 @@ +"""Basic tests that do not require Django.""" +import unittest +import os +import toml + +from nautobot_chatops import __version__ as project_version + + +class TestVersion(unittest.TestCase): + """Test Version is the same.""" + + def test_version(self): + """Verify that pyproject.toml version is same as version specified in the package.""" + parent_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + poetry_version = toml.load(os.path.join(parent_path, "pyproject.toml"))["tool"]["poetry"]["version"] + self.assertEqual(project_version, poetry_version) + + +class TestDocsPackaging(unittest.TestCase): + """Test Version in doc requirements is the same pyproject.""" + + def test_version(self): + """Verify that pyproject.toml dev dependencies have the same versions as in the docs requirements.txt.""" + parent_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__)))) + poetry_path = os.path.join(parent_path, "pyproject.toml") + poetry_details = toml.load(poetry_path)["tool"]["poetry"]["group"]["dev"]["dependencies"] + 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("==") + else: + version = "*" + self.assertEqual(poetry_details[pkg], version) diff --git a/pyproject.toml b/pyproject.toml index 1cf5dde0..8e294a3e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,157 +1,63 @@ [tool.poetry] name = "nautobot-chatops" -version = "3.0.1" -description = "A plugin providing chatbot capabilities for Nautobot" +version = "0.1.0" +description = "Nautobot ChatOps App" authors = ["Network to Code, LLC "] +license = "Apache-2.0" readme = "README.md" -homepage = "https://github.com/nautobot/nautobot-plugin-chatops" -repository = "https://github.com/nautobot/nautobot-plugin-chatops" -documentation = "https://docs.nautobot.com/projects/chatops/en/stable/" +homepage = "https://github.com/nautobot/nautobot-plugin-chatops/" +repository = "https://github.com/nautobot/nautobot-plugin-chatops/" keywords = ["nautobot", "nautobot-plugin"] +classifiers = [ + "Intended Audience :: Developers", + "Development Status :: 5 - Production/Stable", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", +] include = [ "LICENSE", "README.md", - # Poetry by default will exclude files that are in .gitignore - "nautobot_chatops/static/nautobot_chatops/docs/**/*", ] - -[tool.poetry.plugins."nautobot.workers"] -"aci" = "nautobot_chatops.integrations.aci.worker:aci" -"ansible" = "nautobot_chatops.integrations.ansible.worker:ansible" -"clear" = "nautobot_chatops.workers.clear:clear" -"cloudvision" = "nautobot_chatops.integrations.aristacv.worker:cloudvision" -"grafana" = "nautobot_chatops.integrations.grafana.worker:grafana" -"ipfabric" = "nautobot_chatops.integrations.ipfabric.worker:ipfabric" -"meraki" = "nautobot_chatops.integrations.meraki.worker:cisco_meraki" -"nautobot" = "nautobot_chatops.workers.nautobot:nautobot" -"panorama" = "nautobot_chatops.integrations.panorama.worker:panorama" +packages = [ + { include = "nautobot_chatops" }, +] [tool.poetry.dependencies] -Markdown = "!=3.3.5" -PyJWT = "^2.1.0" -PyYAML = { version = "^6.0", optional = true } -aiodns = "^1.0" -aiohttp = "^3.7.3" -asgiref = "^3.4.1" -certifi = { version = ">=2021.5.30", optional = true } -cloudvision = { version = "^1.1", optional = true } -cvprac = { version = "^1.0.6", optional = true } -defusedxml = { version = "^0.7.1", optional = true } -diffsync = { version = "^1.3.0", optional = true } -ipaddr = { version = "^2.2.0", optional = true } -ipfabric = { version = "~6.0.9", optional = true } -ipfabric-diagrams = { version = "~6.0.2", optional = true } -isodate = { version = "^0.6.1", optional = true } -meraki = { version = "^1.7.2", optional = true } -nautobot = "^2.0.0" -nautobot-capacity-metrics = "^3.0.0" -netmiko = { version = "^4.0.0", optional = true } -netutils = { version = "^1.1.0", optional = true } -pan-os-python = { version = "^1.3.0", optional = true } -prettytable = { version = "^2.1.0", optional = true } -protobuf = { version = "^3.17", optional = true } -pydantic = { version = "^1.8.2", optional = true } python = ">=3.8,<3.12" -requests = ">=2.25.1" -schema-enforcer = { version = "^1.2.1", optional = true } -slack-sdk = "^3.4.2" -termcolor = { version = "1.1.0", optional = true } -texttable = "^1.6.2" -webexteamssdk = "^1.3" +# Used for local development +nautobot = "^2.0.0" -[tool.poetry.dev-dependencies] -black = "*" -yamllint = "*" +[tool.poetry.group.dev.dependencies] bandit = "*" -# Pinning older pylint due to https://github.com/pylint-dev/pylint/issues/7381 -pylint = "2.13.9" -pylint-django = "*" -pydocstyle = "*" -prybar = "*" +black = "*" +coverage = "*" +django-debug-toolbar = "*" +flake8 = "*" invoke = "*" -flake8 = "^3.9.2" -griffe = "0.30.1" +ipython = "*" +pydocstyle = "*" +pylint = "*" +pylint-django = "*" +pylint-nautobot = "*" +yamllint = "*" +toml = "*" +Markdown = "*" # Rendering docs to HTML -mkdocs = "1.3.1" -# Material for mkdocs theme -mkdocs-material = "8.4.2" +mkdocs = "1.5.2" +# Material for MkDocs theme +mkdocs-material = "9.1.15" +# Render custom markdown for version added/changed/remove notes +mkdocs-version-annotations = "1.0.0" # Automatic documentation from sources, for MkDocs mkdocstrings = "0.22.0" -mkdocstrings-python = "1.1.2" -# Render custom markdown for version added/changed/remove notes -mkdocs-version-annotations = "~1.0.0" -# Allow Markdown files to include other files -mkdocs-include-markdown-plugin = "~3.6.1" -python-dotenv = "^0.21.1" -# Change log management and generation -towncrier = "~22.8.0" -coverage = "~5.4" -requests-mock = "^1.9.3" - -[tool.poetry.extras] -all = [ - "PyYAML", - "certifi", - "cloudvision", - "cvprac", - "defusedxml", - "diffsync", - "ipaddr", - "ipfabric", - "ipfabric-diagrams", - "isodate", - "meraki", - "netmiko", - "netutils", - "pan-os-python", - "prettytable", - "protobuf", - "pydantic", - "schema-enforcer", - "termcolor", -] -aci = [ - "prettytable", -] -aristacv = [ - "certifi", - "cloudvision", - "cvprac", - "protobuf" -] -ansible = [ - "PyYAML", -] -grafana = [ - "diffsync", - "isodate", - "pydantic", - "schema-enforcer", - "termcolor", -] -ipfabric = [ - "ipfabric", - "ipfabric-diagrams", - "netutils", -] -meraki = [ - "meraki", -] -panorama = [ - "defusedxml", - "ipaddr", - "netmiko", - "netutils", - "pan-os-python", -] -nautobot = ["nautobot"] - -[tool.poetry.group.dev.dependencies] -pylint-nautobot = "^0.2.0" +mkdocstrings-python = "1.5.2" [tool.black] line-length = 120 -target-version = ['py36'] +target-version = ['py38', 'py39', 'py310', 'py311'] include = '\.pyi?$' exclude = ''' ( @@ -173,53 +79,45 @@ exclude = ''' ''' [tool.pylint.master] -# Including the pylint_django plugin -load-plugins=["pylint_nautobot", "pylint_django"] - -[tool.pylint.message_control] -disable=""", - django-not-configured, - too-few-public-methods, - too-many-lines, - too-many-ancestors, - nb-incorrect-base-class, +# Include the pylint_django plugin to avoid spurious warnings about Django patterns +load-plugins="pylint_django, pylint_nautobot" +ignore=".venv" + +[tool.pylint.basic] +# No docstrings required for private methods (Pylint default), or for test_ functions, or for inner Meta classes. +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 """ [tool.pylint.miscellaneous] -notes=""", +# Don't flag TODO as a failure, let us commit with things that still need to be done in the code +notes = """, FIXME, XXX, """ -[tool.pylint.design] -max-args=6 -max-public-methods=22 - -[tool.pylint.similarities] -ignore-imports= true -min-similarity-lines=0 - -[tool.pylint.format] -max-line-length=120 - [tool.pylint-nautobot] -supported_nautobot_versions = ["2",] +supported_nautobot_versions = [ + "2.0.0" +] + +[tool.pydocstyle] +convention = "google" +inherit = false +match = "(?!__init__).*\\.py" +match-dir = "(?!tests|migrations|development)[^\\.].*" +# D212 is enabled by default in google convention, and complains if we have a docstring like: +# """ +# My docstring is on the line after the opening quotes instead of on the same line as them. +# """ +# We've discussed and concluded that we consider this to be a valid style choice. +add_ignore = "D212" [build-system] -requires = ["poetry-core>=1.0.0"] +requires = ["poetry_core>=1.0.0"] build-backend = "poetry.core.masonry.api" - -[tool.towncrier] -package = "nautobot_chatops" -directory = "changes" -filename = "docs/admin/release_notes/version_3.0.md" -template = "development/towncrier_template.j2" -start_string = "" -issue_format = "[#{issue}](https://github.com/nautobot/nautobot-plugin-chatops/issues/{issue})" - -[tool.towncrier.fragment.added] -[tool.towncrier.fragment.changed] -[tool.towncrier.fragment.deprecated] -[tool.towncrier.fragment.fixed] -[tool.towncrier.fragment.removed] -[tool.towncrier.fragment.security] diff --git a/tasks.py b/tasks.py index e8674a6a..2a786b44 100644 --- a/tasks.py +++ b/tasks.py @@ -1,6 +1,6 @@ """Tasks for use with Invoke. -(c) 2020-2021 Network To Code +Copyright (c) 2023, Network to Code, LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -12,16 +12,10 @@ limitations under the License. """ -from distutils.util import strtobool -from invoke import Collection, task as invoke_task import os -from dotenv import load_dotenv - - -def _load_dotenv(): - load_dotenv("./development/development.env") - load_dotenv("./development/creds.env") +from invoke.collection import Collection +from invoke.tasks import task as invoke_task def is_truthy(arg): @@ -36,7 +30,14 @@ def is_truthy(arg): """ if isinstance(arg, bool): return arg - return bool(strtobool(arg)) + + val = str(arg).lower() + if val in ("y", "yes", "t", "true", "on", "1"): + return True + elif val in ("n", "no", "f", "false", "off", "0"): + return False + else: + raise ValueError(f"Invalid truthy value: `{arg}`") # Use pyinvoke configuration for default values, see http://docs.pyinvoke.org/en/stable/concepts/configuration.html @@ -47,15 +48,13 @@ def is_truthy(arg): "nautobot_chatops": { "nautobot_ver": "2.0.0", "project_name": "nautobot-chatops", - "python_ver": "3.10", + "python_ver": "3.11", "local": False, "compose_dir": os.path.join(os.path.dirname(__file__), "development"), "compose_files": [ "docker-compose.base.yml", "docker-compose.redis.yml", "docker-compose.postgres.yml", - "mattermost/docker-compose.yml", - "ansible/docker-compose.yml", "docker-compose.dev.yml", ], "compose_http_timeout": "86400", @@ -64,6 +63,10 @@ def is_truthy(arg): ) +def _is_compose_included(context, name): + return f"docker-compose.{name}.yml" in context.nautobot_chatops.compose_files + + def task(function=None, *args, **kwargs): """Task decorator to override the default Invoke task decorator and add each task to the invoke namespace.""" @@ -169,10 +172,17 @@ def generate_packages(context): run_command(context, command) -@task -def lock(context): +@task( + help={ + "check": ( + "If enabled, check for outdated dependencies in the poetry.lock file, " + "instead of generating a new one. (default: disabled)" + ) + } +) +def lock(context, check=False): """Generate poetry.lock inside the Nautobot container.""" - run_command(context, "poetry lock --no-update") + run_command(context, f"poetry {'check' if check else 'lock --no-update'}") # ------------------------------------------------------------------------------ @@ -360,160 +370,166 @@ def exec(context, service="nautobot", command="bash", file=""): @task( help={ + "db-name": "Database name (default: Nautobot database)", + "input-file": "SQL file to execute and quit (default: empty, start interactive CLI)", + "output-file": "Ouput file, overwrite if exists (default: empty, output to stdout)", "query": "SQL command to execute and quit (default: empty)", - "input": "SQL file to execute and quit (default: empty)", - "output": "Ouput file, overwrite if exists (default: empty)", } ) -def dbshell(context, query="", input="", output=""): +def dbshell(context, db_name="", input_file="", output_file="", query=""): """Start database CLI inside the running `db` container. Doesn't use `nautobot-server dbshell`, using started `db` service container only. """ - if input and query: - raise ValueError("Cannot specify both, `input` and `query` arguments") - if output and not (input or query): - raise ValueError("`output` argument requires `input` or `query` argument") + if input_file and query: + raise ValueError("Cannot specify both, `input_file` and `query` arguments") + if output_file and not (input_file or query): + raise ValueError("`output_file` argument requires `input_file` or `query` argument") - _load_dotenv() + env = {} + if query: + env["_SQL_QUERY"] = query - service = "db" - env_vars = {} - command = ["exec"] + command = [ + "exec", + "--env=_SQL_QUERY" if query else "", + "-- db sh -c '", + ] - if "docker-compose.mysql.yml" in context.nautobot_chatops.compose_files: - env_vars["MYSQL_PWD"] = os.getenv("MYSQL_PASSWORD") + if _is_compose_included(context, "mysql"): command += [ - "--env=MYSQL_PWD", - "--", - service, "mysql", - f"--user='{os.getenv('MYSQL_USER')}'", - f"--database='{os.getenv('MYSQL_DATABASE')}'", + "--user=$MYSQL_USER", + "--password=$MYSQL_PASSWORD", + f"--database={db_name or '$MYSQL_DATABASE'}", ] - if query: - command += [f"--execute='{query}'"] - elif "docker-compose.postgres.yml" in context.nautobot_chatops.compose_files: + elif _is_compose_included(context, "postgres"): command += [ - "--", - service, "psql", - f"--username='{os.getenv('POSTGRES_USER')}'", - f"--dbname='{os.getenv('POSTGRES_DB')}'", + "--username=$POSTGRES_USER", + f"--dbname={db_name or '$POSTGRES_DB'}", ] - if query: - command += [f"--command='{query}'"] else: raise ValueError("Unsupported database backend.") - if input: - command += [f"< '{input}'"] - if output: - command += [f"> '{output}'"] + command += [ + "'", + '<<<"$_SQL_QUERY"' if query else "", + f"< '{input_file}'" if input_file else "", + f"> '{output_file}'" if output_file else "", + ] - docker_compose(context, " ".join(command), env=env_vars, pty=not (input or output or query)) + docker_compose(context, " ".join(command), env=env, pty=not (input_file or output_file or query)) @task( help={ - "input": "SQL dump file to replace the existing database with. This can be generated using `invoke backup-db` (default: `dump.sql`).", + "input-file": "SQL dump file to replace the existing database with. This can be generated using `invoke backup-db` (default: `dump.sql`).", } ) -def import_db(context, input="dump.sql"): +def import_db(context, input_file="dump.sql"): """Stop Nautobot containers and replace the current database with the dump into the running `db` container.""" docker_compose(context, "stop -- nautobot worker") - _load_dotenv() + command = ["exec -- db sh -c '"] - service = "db" - env_vars = {} - command = ["exec"] - - if "docker-compose.mysql.yml" in context.nautobot_chatops.compose_files: - env_vars["MYSQL_PWD"] = os.getenv("MYSQL_PASSWORD") + if _is_compose_included(context, "mysql"): command += [ - "--env=MYSQL_PWD", - "--", - service, "mysql", - f"--user='{os.getenv('MYSQL_USER')}'", - f"--database='{os.getenv('MYSQL_DATABASE')}'", + "--database=$MYSQL_DATABASE", + "--user=$MYSQL_USER", + "--password=$MYSQL_PASSWORD", ] - elif "docker-compose.postgres.yml" in context.nautobot_chatops.compose_files: + elif _is_compose_included(context, "postgres"): command += [ - "--", - service, "psql", - f"--username='{os.getenv('POSTGRES_USER')}'", + "--username=$POSTGRES_USER", "postgres", ] else: raise ValueError("Unsupported database backend.") - command += [f"< '{input}'"] + command += [ + "'", + f"< '{input_file}'", + ] - docker_compose(context, " ".join(command), env=env_vars, pty=False) + docker_compose(context, " ".join(command), pty=False) print("Database import complete, you can start Nautobot now: `invoke start`") @task( help={ - "output": "Ouput file, overwrite if exists (default: `dump.sql`)", + "db-name": "Database name to backup (default: Nautobot database)", + "output-file": "Ouput file, overwrite if exists (default: `dump.sql`)", "readable": "Flag to dump database data in more readable format (default: `True`)", } ) -def backup_db(context, output="dump.sql", readable=True): - """Dump database into `output` file from running `db` container.""" - _load_dotenv() +def backup_db(context, db_name="", output_file="dump.sql", readable=True): + """Dump database into `output_file` file from running `db` container.""" + command = ["exec -- db sh -c '"] - service = "db" - env_vars = {} - command = ["exec"] - - if "docker-compose.mysql.yml" in context.nautobot_chatops.compose_files: - env_vars["MYSQL_PWD"] = os.getenv("MYSQL_ROOT_PASSWORD") + if _is_compose_included(context, "mysql"): command += [ - "--env=MYSQL_PWD", - "--", - service, "mysqldump", "--user=root", + "--password=$MYSQL_ROOT_PASSWORD", "--add-drop-database", "--skip-extended-insert" if readable else "", "--databases", - os.getenv("MYSQL_DATABASE", ""), + db_name if db_name else "$MYSQL_DATABASE", ] - elif "docker-compose.postgres.yml" in context.nautobot_chatops.compose_files: + elif _is_compose_included(context, "postgres"): command += [ - "--", - service, "pg_dump", "--clean", "--create", "--if-exists", - f"--username='{os.getenv('POSTGRES_USER')}'", - f"--dbname='{os.getenv('POSTGRES_DB')}'", + "--username=$POSTGRES_USER", + f"--dbname={db_name or '$POSTGRES_DB'}", + "--inserts" if readable else "", ] - - if readable: - command += ["--inserts"] else: raise ValueError("Unsupported database backend.") - if output: - command += [f"> '{output}'"] + command += [ + "'", + f"> '{output_file}'", + ] - docker_compose(context, " ".join(command), env=env_vars, pty=False) + docker_compose(context, " ".join(command), pty=False) print(50 * "=") - print("The database backup has been successfully completed and saved to the file:") - print(output) - print("If you want to import this database backup, please execute the following command:") - print(f"invoke import-db --input '{output}'") + print("The database backup has been successfully completed and saved to the following file:") + print(output_file) + print("You can import this database backup with the following command:") + print(f"invoke import-db --input-file '{output_file}'") print(50 * "=") +# ------------------------------------------------------------------------------ +# DOCS +# ------------------------------------------------------------------------------ +@task +def docs(context): + """Build and serve docs locally for development.""" + command = "mkdocs serve -v" + + if is_truthy(context.nautobot_chatops.local): + print(">>> Serving Documentation at http://localhost:8001") + run_command(context, command) + else: + start(context, service="docs") + + +@task +def build_and_check_docs(context): + """Build documentation to be available within Nautobot.""" + command = "mkdocs build --no-directory-urls --strict" + run_command(context, command) + + @task(name="help") def help_task(context): """Print the help of available tasks.""" @@ -571,7 +587,7 @@ def pylint(context): def pydocstyle(context): """Run pydocstyle to validate docstring formatting adheres to NTC defined standards.""" # We exclude the /migrations/ directory since it is autogenerated code - command = 'pydocstyle --config=.pydocstyle.ini --match-dir="^(?!migrations).*"' + command = "pydocstyle ." run_command(context, command) @@ -584,7 +600,7 @@ def bandit(context): @task def yamllint(context): - """Run yamllint to validate formating adheres to NTC defined YAML standards. + """Run yamllint to validate formatting adheres to NTC defined YAML standards. Args: context (obj): Used to run specific commands @@ -596,18 +612,11 @@ def yamllint(context): @task def check_migrations(context): """Check for missing migrations.""" - command = "nautobot-server --config=nautobot/core/tests/nautobot_config.py makemigrations --dry-run --check" + command = "nautobot-server makemigrations --dry-run --check" run_command(context, command) -@task() -def build_and_check_docs(context): - """Build docs for use within Nautobot.""" - command = "mkdocs build --no-directory-urls --strict" - run_command(context, command) - - @task( help={ "keepdb": "save and re-use test database between test runs for faster re-testing.", @@ -615,9 +624,18 @@ def build_and_check_docs(context): "failfast": "fail as soon as a single test fails don't run the entire test suite", "buffer": "Discard output from passing tests", "pattern": "Run specific test methods, classes, or modules instead of all tests", + "verbose": "Enable verbose test output.", } ) -def unittest(context, keepdb=False, label="nautobot_chatops", failfast=False, buffer=True, pattern=""): +def unittest( + context, + keepdb=False, + label="nautobot_chatops", + failfast=False, + buffer=True, + pattern="", + verbose=False, +): """Run Nautobot unit tests.""" command = f"coverage run --module nautobot.core.cli test {label}" @@ -629,6 +647,9 @@ def unittest(context, keepdb=False, label="nautobot_chatops", failfast=False, bu command += " --buffer" if pattern: command += f" -k='{pattern}'" + if verbose: + command += " --verbosity 2" + run_command(context, command) @@ -642,10 +663,12 @@ def unittest_coverage(context): @task( help={ - "failfast": "fail as soon as a single test fails don't run the entire test suite", + "failfast": "fail as soon as a single test fails don't run the entire test suite. (default: False)", + "keepdb": "Save and re-use test database between test runs for faster re-testing. (default: False)", + "lint-only": "Only run linters; unit tests will be excluded. (default: False)", } ) -def tests(context, failfast=False): +def tests(context, failfast=False, keepdb=False, lint_only=False): """Run all tests for this plugin.""" # If we are not running locally, start the docker containers so we don't have to for each test if not is_truthy(context.nautobot_chatops.local): @@ -662,98 +685,16 @@ def tests(context, failfast=False): pydocstyle(context) print("Running yamllint...") yamllint(context) + print("Running poetry check...") + lock(context, check=True) + print("Running migrations check...") + check_migrations(context) print("Running pylint...") pylint(context) - print("Building and checking docs...") + print("Running mkdocs...") build_and_check_docs(context) - print("Running unit tests...") - unittest(context, failfast=failfast) + if not lint_only: + print("Running unit tests...") + unittest(context, failfast=failfast, keepdb=keepdb) + unittest_coverage(context) print("All tests have passed!") - unittest_coverage(context) - - -# ------------------------------------------------------------------------------ -# APP CUSTOM -# ------------------------------------------------------------------------------ -@task -def bootstrap_mattermost(context): - """Bootstrap Nautobot data to be used with Mattermost.""" - nbshell(context, file="development/mattermost/nautobot_bootstrap.py") - - -@task -def backup_mattermost(context): - """Export Mattermost data to the SQL file. Certain tables are ignored.""" - output = "./development/mattermost/dump.sql" - - ignore_tables = [ - "Audits", - "ChannelMemberHistory", - "CommandWebhooks", - "Posts", - "PostsPriority", - "Sessions", - "UploadSessions", - ] - - base_command = [ - "exec", - "--env MYSQL_PWD=mostest", - "--", - "mattermost", - "mysqldump", - "--databases mattermost_test", - "--compact", - "-u root", - ] - - # Dump schema first - command = [ - *base_command, - "--add-drop-database", - "--no-data", - f"> {output}", - ] - docker_compose(context, " ".join(command)) - - # Dump data for all tables except ignored - command = [ - *base_command, - *(f"--ignore-table mattermost_test.{table}" for table in ignore_tables), - "--no-create-info", - "--skip-extended-insert", - f">> {output}", - ] - docker_compose(context, " ".join(command)) - - -@task -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" - context.run(f"docker network connect --alias awx {bridge_network} {container_name}") - print(f"Container {container_name} connected to {bridge_network} network") - - -@task( - help={ - "version": "Version of Nautobot ChatOps to generate the release notes for.", - } -) -def generate_release_notes(context, version=""): - """Generate Release Notes using Towncrier.""" - command = "env DJANGO_SETTINGS_MODULE=nautobot.core.settings towncrier build" - if version: - command += f" --version {version}" - run_command(context, command)