diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..1f014a5 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,228 @@ +--- +name: "CI" +on: # yamllint disable-line rule:truthy + - "push" + - "pull_request" + +env: + PLUGIN_NAME: "nautobot-plugin-chatops-panorama" + +# Enable jobs as needed +jobs: + black: + runs-on: "ubuntu-20.04" + env: + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: black" + run: "poetry run invoke black" + bandit: + runs-on: "ubuntu-20.04" + env: + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: bandit" + run: "poetry run invoke bandit" + needs: + - "black" + pydocstyle: + runs-on: "ubuntu-20.04" + env: + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: pydocstyle" + run: "poetry run invoke pydocstyle" + needs: + - "black" + flake8: + runs-on: "ubuntu-20.04" + env: + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: flake8" + run: "poetry run invoke flake8" + needs: + - "black" + yamllint: + runs-on: "ubuntu-20.04" + env: + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_LOCAL: "True" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Linting: yamllint" + run: "poetry run invoke yamllint" + needs: + - "black" + build: + runs-on: "ubuntu-20.04" + strategy: + fail-fast: true + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9"] + nautobot-version: ["1.0.3"] + env: + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_PYTHON_VER: "${{ matrix.python-version }}" + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_NAUTOBOT_VER: "${{ matrix.nautobot-version }}" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Set up Docker Buildx" + id: "buildx" + uses: "docker/setup-buildx-action@v1" + - name: "Build" + uses: "docker/build-push-action@v2" + with: + builder: "${{ steps.buildx.outputs.name }}" + context: "./" + push: false + 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 }}" + outputs: "type=docker,dest=/tmp/${{ env.PLUGIN_NAME }}-${{ matrix.nautobot-version }}-py${{ matrix.python-version }}.tar" + build-args: | + NAUTOBOT_VER=${{ matrix.nautobot-version }} + PYTHON_VER=${{ matrix.python-version }} + - name: "Upload the docker image" + uses: "actions/upload-artifact@v2" + with: + name: "${{ env.PLUGIN_NAME }}-${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + path: "/tmp/${{ env.PLUGIN_NAME }}-${{ matrix.nautobot-version }}-py${{ matrix.python-version }}.tar" + retention-days: 1 + needs: + - "bandit" + - "pydocstyle" + - "flake8" + - "yamllint" + pylint: + runs-on: "ubuntu-20.04" + strategy: + fail-fast: true + matrix: + python-version: ["3.7"] + nautobot-version: ["1.0.3"] + env: + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_PYTHON_VER: "${{ matrix.python-version }}" + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_NAUTOBOT_VER: "${{ matrix.nautobot-version }}" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Download the docker image" + uses: "actions/download-artifact@v2" + with: + name: "${{ env.PLUGIN_NAME }}-${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + path: "/tmp" + - name: "Load the image" + run: "docker load < /tmp/${{ env.PLUGIN_NAME }}-${{ matrix.nautobot-version }}-py${{ matrix.python-version }}.tar" + - name: "Copy credentials" + run: "cp development/creds.example.env development/creds.env" + - name: "Linting: pylint" + run: "poetry run invoke pylint" + needs: + - "build" + unittest: + runs-on: "ubuntu-20.04" + strategy: + fail-fast: true + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9"] + nautobot-version: ["1.0.3"] + env: + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_PYTHON_VER: "${{ matrix.python-version }}" + INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_NAUTOBOT_VER: "${{ matrix.nautobot-version }}" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Setup environment" + uses: "networktocode/gh-action-setup-poetry-environment@v2" + - name: "Download the docker image" + uses: "actions/download-artifact@v2" + with: + name: "${{ env.PLUGIN_NAME }}-${{ matrix.nautobot-version }}-py${{ matrix.python-version }}" + path: "/tmp" + - name: "Load the image" + run: "docker load < /tmp/${{ env.PLUGIN_NAME }}-${{ matrix.nautobot-version }}-py${{ matrix.python-version }}.tar" + - name: "Copy credentials" + run: "cp development/creds.example.env development/creds.env" + - name: "Run Tests" + run: "poetry run invoke unittest" + needs: + - "pylint" + publish_gh: + name: "Publish to GitHub" + runs-on: "ubuntu-20.04" + if: "startsWith(github.ref, 'refs/tags/v')" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Set up Python" + uses: "actions/setup-python@v2" + with: + python-version: "3.9" + - 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: "Run Poetry Build" + run: "poetry build" + - name: "Upload binaries to release" + uses: "svenstaro/upload-release-action@v2" + with: + repo_token: "${{ secrets.NTC_GITHUB_TOKEN }}" + file: "dist/*" + tag: "${{ github.ref }}" + overwrite: true + file_glob: true + needs: + - "unittest" + publish_pypi: + name: "Push Package to PyPI" + runs-on: "ubuntu-20.04" + if: "startsWith(github.ref, 'refs/tags/v')" + steps: + - name: "Check out repository code" + uses: "actions/checkout@v2" + - name: "Set up Python" + uses: "actions/setup-python@v2" + with: + python-version: "3.9" + - 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: "Run Poetry Build" + run: "poetry build" + - name: "Push to PyPI" + uses: "pypa/gh-action-pypi-publish@release/v1" + with: + user: "__token__" + password: "${{ secrets.PYPI_API_TOKEN }}" + needs: + - "unittest" diff --git a/.gitignore b/.gitignore index cc42866..cd8fad9 100644 --- a/.gitignore +++ b/.gitignore @@ -301,3 +301,7 @@ creds.env # Invoke overrides invoke.yml development/mattermost_config_docker.json + +# Ignore generated files from chatops commands +*.csv +*.pcap \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index ac65a79..0000000 --- a/.travis.yml +++ /dev/null @@ -1,50 +0,0 @@ ---- -language: "python" -python: - - "3.6" - - "3.7" - - "3.8" -env: - # Each version of Nautobot listed here must have a corresponding directory/configuration file - # under development/nautobot_/configuration.py - matrix: - - "INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_NAUTOBOT_VER=1.0.1" -# Add your encrypted secret below, you can encrypt secret using "travis encrypt" -# https://docs.travis-ci.com/user/environment-variables/#defining-encrypted-variables-in-travisyml -# global: -# secure: -services: - - "docker" -# -------------------------------------------------------------------------- -# Tests -# -------------------------------------------------------------------------- -before_script: - # Login to Docker if password is in the environment - - echo "${DOCKER_HUB_PASSWORD}" | docker login -u "${DOCKER_HUB_USERNAME}" --password-stdin - - "pip install invoke docker-compose" - - "curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py > /tmp/get-poetry.py" - - "python /tmp/get-poetry.py -y --version 1.1.6" - - "source $HOME/.poetry/env" - - "cp development/creds.example.env development/creds.env" - -script: - # If you want to test different versions, it may require updating the poetry definition for Nautobot - # as that is where the version is controlled - # - "poetry add nautobot=$NAUTOBOT_VER" - - "INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_PYTHON_VER=$TRAVIS_PYTHON_VERSION invoke build --no-cache" - - "INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_PYTHON_VER=$TRAVIS_PYTHON_VERSION invoke tests --failfast" -# -------------------------------------------------------------------------- -# Deploy -# Uncomment the section below if you would like to publish a new release to pypi automatically -# when a new tag is created in master. You"ll also need to generate a dedicated API key for this project in pypi -# and encrypt the key with "travis encrypt PYPI_TOKEN= --add env.global --com" -# -------------------------------------------------------------------------- -# deploy: -# provider: script -# script: poetry config pypi-token.pypi $PYPI_TOKEN && poetry publish --build -# skip_cleanup: true -# on: -# tags: true -# branch: master -# condition: $NAUTOBOT_VER = master -# python: 3.7 diff --git a/.yamllint.yml b/.yamllint.yml index 58324ed..a5be16a 100644 --- a/.yamllint.yml +++ b/.yamllint.yml @@ -2,9 +2,12 @@ extends: "default" rules: comments: "enable" - empty-values: "enable" + comments-indentation: + level: "warning" + empty-values: "disable" indentation: indent-sequences: "consistent" line-length: "disable" quoted-strings: quote-type: "double" + truthy: "disable" diff --git a/README.md b/README.md index 7bd647f..0f05684 100644 --- a/README.md +++ b/README.md @@ -1,173 +1,142 @@ -# Nautobot Plugin Chatops Panorama +# Nautobot Panorama ChatOps -A plugin for [Nautobot](https://github.com/nautobot/nautobot). +This is a plugin for [Nautobot](https://github.com/nautobot/nautobot) that extends ChatOps support to Palo Alto Panorama systems. The plugin adds some useful commands into your ChatOps environment that enhance an administrator's and end user's day to day using of Panorama. This framework allows for the quick extension of new ChatOps commands for Panorama. + +Note: While this plugin requires Nautobot and the base Nautobot ChatOps plugin, it does _not_ require the Panorama or Palo Alto inventory to be in Nautobot. It is effectively Nautobot-independent, except for using it as a backend to run the chat bot itself. + +## Usage + +The supported commands are listed below. We welcome any new command or feature requests by submitting an issue or PR. + +| /panorama Command | Description | +| -------------------- | -------------------------------------------------------------------------- | +| capture-traffic | Run a packet capture on PANOS Device for specified IP traffic. | +| export-device-rules | Generate a downloadable list of firewall rules with details in CSV format. | +| get-device-rules | Return a list of all firewall rules on a given device with details. | +| get-version | Obtain software version information for Panorama. | +| install-software | Install software to specified Palo Alto device. | +| upload-software | Upload software to specified Palo Alto device. | +| validate-rule-exists | Verify that a specific ACL rule exists within a device, via Panorama. | + +## Prerequisites + +This plugin requires the [Nautobot ChatOps Plugin](https://github.com/nautobot/nautobot-plugin-chatops) to be installed and configured before using. You can find detailed setup and configuration instructions [here](https://github.com/nautobot/nautobot-plugin-chatops/blob/develop/README.md). ## Installation -The plugin is available as a Python package in pypi and can be installed with pip +The plugin is available as a Python package in pypi and can be installed with pip: ```shell pip install nautobot-plugin-chatops-panorama ``` -> The plugin is compatible with Nautobot 1.0.0 and higher +> The plugin is compatible with Nautobot 1.1.0 and higher -To ensure Nautobot Plugin Chatops Panorama 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-plugin-chatops-panorama` package: +To ensure Nautobot Panorama ChatOps 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-plugin-chatops-panorama` package: ```no-highlight # echo nautobot-plugin-chatops-panorama >> local_requirements.txt ``` -Once installed, the plugin needs to be enabled in your `nautobot_configuration.py` +Once installed, the plugin needs to be enabled in your `nautobot_config.py` ```python # In your configuration.py -PLUGINS = ["nautobot_plugin_chatops_panorama"] - -# PLUGINS_CONFIG = { -# "nautobot_plugin_chatops_panorama": { -# ADD YOUR SETTINGS HERE -# } -# } +PLUGINS = ["nautobot_chatops", "nautobot_plugin_chatops_panorama"] ``` -The plugin behavior can be controlled with the following list of settings - -- TODO - -## Usage - -### API - -TODO - -## Contributing +In addition, add/update the below `PLUGINS_CONFIG` section to `nautobot_config.py`. -Pull requests are welcomed and automatically built and tested against multiple version of Python and multiple version of Nautobot through TravisCI. +> It is only necessary to add the sections from the below snippet for the chat platform you will be using (Slack, Webex, etc.). -The project is packaged with a light development environment based on `docker-compose` to help with the local development of the project and to run the tests within TravisCI. - -The project is following Network to Code software development guideline and is leveraging: - -- Black, Pylint, Bandit and pydocstyle for Python linting and formatting. -- Django unit test to ensure the plugin is working properly. - -### Development Environment - -The development environment can be used in 2 ways. First, with a local poetry environment if you wish to develop outside of Docker. Second, inside of a docker container. - -#### Invoke tasks - -The [PyInvoke](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 PyInvoke to override the default configuration: - -* `nautobot_ver`: the version of Nautobot to use as a base for any built docker containers (default: 1.0.1) -* `project_name`: the default docker compose project name (default: nautobot_plugin_chatops_panorama) -* `python_ver`: the version of Python to use as a base for any built docker containers (default: 3.6) -* `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) - -Using PyInvoke these configuration options can be overridden using [several methods](http://docs.pyinvoke.org/en/stable/concepts/configuration.html). Perhaps the simplest is simply setting an environment variable `INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_VARIABLE_NAME` where `VARIABLE_NAME` is the variable you are trying to override. The only exception is `compose_files`, because it is a list it must be overridden in a yaml file. There is an example `invoke.yml` in this directory which can be used as a starting point. - -#### Local Poetry Development Environment - -1. Copy `development/creds.example.env` to `development/creds.env` (This file will be ignored by git and docker) -2. Uncomment the `POSTGRES_HOST`, `REDIS_HOST`, and `NAUTOBOT_ROOT` variables in `development/creds.env` -3. Create an invoke.yml with the following contents at the root of the repo: - -```shell ---- -nautobot_plugin_chatops_panorama: - local: true - compose_files: - - "docker-compose.requirements.yml" +```python +# Also in nautobot_config.py +PLUGINS_CONFIG = { + "nautobot_chatops": { + # Slack + "enable_slack": os.environ.get("ENABLE_SLACK", False), + "slack_api_token": os.environ.get("SLACK_API_TOKEN"), + "slack_signing_secret": os.environ.get("SLACK_SIGNING_SECRET"), + "slack_slash_command_prefix": os.environ.get("SLACK_SLASH_COMMAND_PREFIX", "/"), + # Webex + "enable_webex": os.environ.get("ENABLE_WEBEX", False), + "webex_token": os.environ.get("WEBEX_TOKEN"), + "webex_signing_secret": os.environ.get("WEBEX_SIGNING_SECRET"), + # Mattermost + "enable_mattermost": os.environ.get("ENABLE_MATTERMOST", False), + "mattermost_api_token": os.environ.get("MATTERMOST_API_TOKEN"), + "mattermost_url": os.environ.get("MATTERMOST_URL"), + # MS Teams + "enable_ms_teams": os.environ.get("ENABLE_MS_TEAMS", False), + "microsoft_app_id": os.environ.get("MICROSOFT_APP_ID"), + "microsoft_app_password": os.environ.get("MICROSOFT_APP_PASSWORD"), + }, + "nautobot_plugin_chatops_panorama": { + "panorama_host": os.environ.get("PANORAMA_HOST"), + "panorama_user": os.environ.get("PANORAMA_USER"), + "panorama_password": os.environ.get("PANORAMA_PASSWORD"), + }, +} ``` -3. Run the following commands: +### Environment Variables -```shell -poetry shell -poetry install --extras nautobot -export $(cat development/dev.env | xargs) -export $(cat development/creds.env | xargs) -invoke start && sleep 5 -nautobot-server migrate -``` - -> If you want to develop on the latest develop branch of Nautobot, run the following command: ``poetry add git+https://github.com/nautobot/nautobot@develop``. After the ``@`` symbol must match either a branch or a tag. +You will need to set the following environment variables for your Nautobot instance, then restart the services for them to take effect. -4. You can now run nautobot-server commands as you would from the [Nautobot documentation](https://nautobot.readthedocs.io/en/latest/) for example to start the development server: +- PANORAMA_HOST - This is the management DNS/IP address used to reach your Panorama instance. +- PANORAMA_USER - A user account with API access to Panorama. +- PANORAMA_PASSWORD - The password that goes with the above user account. -```shell -nautobot-server runserver 0.0.0.0:8080 --insecure +```bash +export PANORAMA_HOST="{{ Panorama DNS/URL }}" +export PANORAMA_USER="{{ Panorama account username }}" +export PANORAMA_PASSWORD="{{ Panorama account password }}" ``` -Nautobot server can now be accessed at [http://localhost:8080](http://localhost:8080). - -#### Docker 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. - -Once you have Poetry and Docker installed you can run the following commands to install all other development dependencies in an isolated python virtual environment: - -```shell -poetry shell -poetry install -invoke start +If the base Nautobot Chatops plugin is not already installed, the following environment variables are required for the chat platform in use. The [Platform-specific Setup](https://github.com/nautobot/nautobot-plugin-chatops/blob/develop/docs/chat_setup/chat_setup.md#platform-specific-setup) document describes how to retrieve the tokens and secrets for each chat platform that will need to be used in the environment variables. + +> It is only necessary to create the environment variables shown below for the chat platform you will be using. To make the environment variables persistent, add them to the ~/.bash_profile for the user running Nautobot. + +```bash +# Slack +export ENABLE_SLACK="true" +export SLACK_API_TOKEN="foobar" +export SLACK_SIGNING_SECRET="foobar" +# Webex +export ENABLE_WEBEX="true" +export WEBEX_TOKEN="foobar" +export WEBEX_SIGNING_SECRET="foobar" +# Mattermost +export ENABLE_MATTERMOST="false" +export MATTERMOST_API_TOKEN="foobar" +export MATTERMOST_URL="foobar" +# Microsoft Teams +export ENABLE_MS_TEAMS="false" +export MICROSOFT_APP_ID="foobar" +export MICROSOFT_APP_PASSWORD="foobar" ``` -Nautobot server can now be accessed at [http://localhost:8080](http://localhost:8080). - -### CLI Helper Commands - -The project is coming with a CLI helper based on [invoke](http://www.pyinvoke.org/) to help setup the development environment. The commands are listed below in 3 categories `dev environment`, `utility` and `testing`. +> When deploying as Docker containers, all of the above environment variables should be defined in the file `development/creds.env`. An example credentials file `creds.env.example` is available in the `development` folder. -Each command can be executed with `invoke `. Environment variables `INVOKE_nautobot_plugin_chatops_panorama_PYTHON_VER` and `INVOKE_nautobot_plugin_chatops_panorama_NAUTOBOT_VER` may be specified to override the default versions. Each command also has its own help `invoke --help` +## Access Control -#### Docker dev environment - -```no-highlight - build Build all docker images. - debug Start Nautobot and its dependencies in debug mode. - destroy Destroy all containers and volumes. - restart Restart Nautobot and its dependencies. - start Start Nautobot and its dependencies in detached mode. - stop Stop Nautobot and its dependencies. -``` +Just like with the regular `/nautobot` command from the base Nautobot ChatOps plugin, the `/panorama` command supports access control through the Access Grants menu in Nautobot. See section [Grant Access to the Chatbot](https://github.com/nautobot/nautobot-plugin-chatops/blob/develop/docs/chat_setup/chat_setup.md#grant-access-to-the-chatbot) in the installation guide for the base Nautobot ChatOps plugin for setting this up. -#### Utility - -```no-highlight - cli Launch a bash shell inside the running Nautobot container. - create-user Create a new user in django (default: admin), will prompt for password. - makemigrations Run Make Migration in Django. - nbshell Launch a nbshell session. -``` +## Questions -#### Testing +For any questions or comments, please check the [FAQ](FAQ.md) first and feel free to swing by the [Network to Code slack channel](https://networktocode.slack.com/) (channel #networktocode). +Sign up [here](http://slack.networktocode.com/) -```no-highlight - bandit Run bandit to validate basic static code security analysis. - black Run black to check that Python files adhere to its style standards. - flake8 This will run flake8 for the specified name and Python version. - pydocstyle Run pydocstyle to validate docstring formatting adheres to NTC defined standards. - pylint Run pylint code analysis. - tests Run all tests for this plugin. - unittest Run Django unit tests for the plugin. -``` +## Screenshots -### Project Documentation +![Help](docs/img/screenshot1.png) -Project documentation is generated by [mkdocs](https://www.mkdocs.org/) from the documentation located in the docs folder. You can configure [readthedocs.io](https://readthedocs.io/) to point at this folder in your repo. For development purposes a `docker-compose.docs.yml` is also included. A container hosting the docs will be started using the invoke commands on [http://localhost:8001](http://localhost:8001), as changes are saved the docs will be automatically reloaded. +![Validate Rule Exists Success](docs/img/screenshot2.png) -## Questions +![Validate Rule Exists Failure](docs/img/screenshot3.png) -For any questions or comments, please check the [FAQ](FAQ.md) first and feel free to swing by the [Network to Code slack channel](https://networktocode.slack.com/) (channel #networktocode). -Sign up [here](http://slack.networktocode.com/) +![Upload Software](docs/img/screenshot4.png) -## Screenshots +![Capture Traffic Filter](docs/img/screenshot5.png) -TODO +![Capture Traffic](docs/img/screenshot6.png) diff --git a/development/Dockerfile b/development/Dockerfile index 1dac3a2..5296af3 100644 --- a/development/Dockerfile +++ b/development/Dockerfile @@ -1,22 +1,9 @@ -ARG NAUTOBOT_VER="1.0.1" -ARG PYTHON_VER=3.8 +ARG PYTHON_VER +ARG NAUTOBOT_VER +# Switching to ghcr.io image for now so we don't hit dockerhub rate limit. FROM ghcr.io/nautobot/nautobot-dev:${NAUTOBOT_VER}-py${PYTHON_VER} +# FROM networktocode/nautobot-dev:${NAUTOBOT_VER}-py${PYTHON_VER} -ENV prometheus_multiproc_dir=/prom_cache - -ARG NAUTOBOT_ROOT=/opt/nautobot - -ENV NAUTOBOT_ROOT ${NAUTOBOT_ROOT} - -WORKDIR $NAUTOBOT_ROOT - -# Configure poetry -RUN poetry config virtualenvs.create false \ - && poetry config installer.parallel false - -# ------------------------------------------------------------------------------------- -# Install Nautobot Plugin -# ------------------------------------------------------------------------------------- WORKDIR /source # Copy in only pyproject.toml/poetry.lock to help with caching this layer if no updates to dependencies @@ -29,4 +16,4 @@ RUN poetry install --no-interaction --no-ansi --no-root COPY . /source RUN poetry install --no-interaction --no-ansi -COPY development/nautobot_config.py ${NAUTOBOT_ROOT}/nautobot_config.py +COPY development/nautobot_config.py /opt/nautobot/nautobot_config.py \ No newline at end of file diff --git a/development/dev.env b/development/dev.env index 01eab5b..a9f4550 100644 --- a/development/dev.env +++ b/development/dev.env @@ -1,19 +1,32 @@ -ALLOWED_HOSTS=* -BANNER_TOP="Local" +NAUTOBOT_ALLOWED_HOSTS=* +BANNER_TOP="Chatops plugin dev" CHANGELOG_RETENTION=0 DEBUG=True -MAX_PAGE_SIZE=0 +NAUTOBOT_MAX_PAGE_SIZE=0 METRICS_ENABLED=True NAPALM_TIMEOUT=5 -NAUTOBOT_CHATOPS_ENABLE_MATTERMOST=True -NAUTOBOT_CHATOPS_ENABLE_SLACK=False -NAUTOBOT_ROOT=/opt/nautobot -POSTGRES_DB=nautobot -POSTGRES_HOST=postgres -POSTGRES_USER=nautobot -REDIS_HOST=redis -REDIS_PORT=6379 +NAUTOBOT_DB_NAME=nautobot +NAUTOBOT_DB_HOST=postgres +NAUTOBOT_DB_PASSWORD=notverysecurepwd +NAUTOBOT_DB_USER=nautobot +NAUTOBOT_DB_TIMEOUT=300 +NAUTOBOT_DB_ENGINE=django.db.backends.postgresql +NAUTOBOT_REDIS_HOST=redis +NAUTOBOT_REDIS_PASSWORD=notverysecurepwd +NAUTOBOT_REDIS_PORT=6379 # REDIS_SSL=True # Uncomment REDIS_SSL if using SSL +NAUTOBOT_SECRET_KEY=bqn8nn4qmjvx4hv2u5qr4pp46s3w9skbb63y +SUPERUSER_API_TOKEN=0123456789abcdef0123456789abcdef01234567 SUPERUSER_EMAIL=admin@example.com SUPERUSER_NAME=admin +SUPERUSER_PASSWORD=admin + +# Needed for Postgres should match the values for Nautobot above +PGPASSWORD=notverysecurepwd +POSTGRES_DB=nautobot +POSTGRES_PASSWORD=notverysecurepwd +POSTGRES_USER=nautobot + +# Needed for Redis should match the values for Nautobot above +REDIS_PASSWORD=notverysecurepwd \ No newline at end of file diff --git a/development/docker-compose.base.yml b/development/docker-compose.base.yml index c8a701b..18d389c 100644 --- a/development/docker-compose.base.yml +++ b/development/docker-compose.base.yml @@ -17,7 +17,7 @@ version: "3.4" services: nautobot: ports: - - "127.0.0.1:8080:8080" + - "8080:8080" depends_on: - "postgres" - "redis" diff --git a/development/docker-compose.celery.yml b/development/docker-compose.celery.yml new file mode 100644 index 0000000..547fd43 --- /dev/null +++ b/development/docker-compose.celery.yml @@ -0,0 +1,17 @@ +--- +version: "3.4" +services: + celery: + image: "nautobot-plugin-chatops-panorama/nautobot:${NAUTOBOT_VER}-py${PYTHON_VER}" + env_file: + - "dev.env" + - "creds.env" + tty: true + entrypoint: "nautobot-server celery worker -l INFO" + depends_on: + - "nautobot" + healthcheck: + disable: true + volumes: + - "./nautobot_config.py:/opt/nautobot/nautobot_config.py" + - "../:/source" diff --git a/development/docker-compose.dev.yml b/development/docker-compose.dev.yml index a8637f8..02e81f6 100644 --- a/development/docker-compose.dev.yml +++ b/development/docker-compose.dev.yml @@ -6,7 +6,7 @@ version: "3.4" services: nautobot: - command: "nautobot-server runserver 0.0.0.0:8080 --nothreading" + command: "nautobot-server runserver 0.0.0.0:8080" volumes: - "./nautobot_config.py:/opt/nautobot/nautobot_config.py" - "../:/source" diff --git a/development/docker-compose.mattermost.yml b/development/docker-compose.mattermost.yml index 4acc16f..1894b22 100644 --- a/development/docker-compose.mattermost.yml +++ b/development/docker-compose.mattermost.yml @@ -3,17 +3,17 @@ version: "3.4" services: mattermost: build: - context: ./ + context: "./" dockerfile: "Dockerfile-mattermost" image: "nautobot-plugin-chatops-panorama/mattermost" - restart: unless-stopped + restart: "unless-stopped" volumes: - - /etc/localtime:/etc/localtime:ro - - mattermost:/mm/mattermost-data:rw - - mattermost:/var/lib/mysql/:rw + - "/etc/localtime:/etc/localtime:ro" + - "mattermost:/mm/mattermost-data:rw" + - "mattermost:/var/lib/mysql/:rw" env_file: "./mattermost.env" ports: - - "8065:8065" + - "8065:8065" volumes: - mattermost: \ No newline at end of file + mattermost: diff --git a/development/docker-compose.requirements.yml b/development/docker-compose.requirements.yml index cc2d79d..175cd29 100644 --- a/development/docker-compose.requirements.yml +++ b/development/docker-compose.requirements.yml @@ -9,7 +9,7 @@ services: volumes: - "postgres_data:/var/lib/postgresql/data" ports: - - "127.0.0.1:5432:5432" + - "5432:5432" redis: image: "redis:6-alpine" command: @@ -20,22 +20,6 @@ services: - "dev.env" - "creds.env" ports: - - "127.0.0.1:6379:6379" - celery_worker: - image: "nautobot-plugin-chatops-panorama/nautobot:${NAUTOBOT_VER}-py${PYTHON_VER}" - entrypoint: "nautobot-server celery worker -B -l INFO" - healthcheck: - interval: 5s - timeout: 5s - start_period: 5s - retries: 3 - test: ["CMD", "nautobot-server", "health_check"] - depends_on: - - nautobot - - redis - env_file: - - ./dev.env - - "creds.env" - tty: true + - "6379:6379" volumes: postgres_data: {} diff --git a/development/nautobot_config.py b/development/nautobot_config.py index 1e85e2b..97d71b4 100644 --- a/development/nautobot_config.py +++ b/development/nautobot_config.py @@ -7,21 +7,18 @@ import os import sys -from distutils.util import strtobool from django.core.exceptions import ImproperlyConfigured from nautobot.core import settings -from nautobot.core.settings_funcs import is_truthy, parse_redis_connection +from nautobot.core.settings_funcs import is_truthy # Enforce required configuration parameters for key in [ - "ALLOWED_HOSTS", - "POSTGRES_DB", - "POSTGRES_USER", - "POSTGRES_HOST", - "POSTGRES_PASSWORD", - "REDIS_HOST", - "REDIS_PASSWORD", - "SECRET_KEY", + "NAUTOBOT_ALLOWED_HOSTS", + "NAUTOBOT_DB_USER", + "NAUTOBOT_DB_PASSWORD", + "NAUTOBOT_REDIS_HOST", + "NAUTOBOT_REDIS_PASSWORD", + "NAUTOBOT_SECRET_KEY", ]: if not os.environ.get(key): raise ImproperlyConfigured(f"Required environment variable {key} is missing.") @@ -33,30 +30,28 @@ # access to the server via any other hostnames. The first FQDN in the list will be treated as the preferred name. # # Example: ALLOWED_HOSTS = ['nautobot.example.com', 'nautobot.internal.local'] -ALLOWED_HOSTS = os.getenv("ALLOWED_HOSTS").split(" ") +ALLOWED_HOSTS = os.getenv("NAUTOBOT_ALLOWED_HOSTS", "").split(" ") -# PostgreSQL database configuration. See the Django documentation for a complete list of available parameters: -# https://docs.djangoproject.com/en/stable/ref/settings/#databases DATABASES = { "default": { - "NAME": os.getenv("POSTGRES_DB", "nautobot"), # Database name - "USER": os.getenv("POSTGRES_USER", ""), # Database username - "PASSWORD": os.getenv("POSTGRES_PASSWORD", ""), # Datbase password - "HOST": os.getenv("POSTGRES_HOST", "localhost"), # Database server - "PORT": os.getenv("POSTGRES_PORT", ""), # Database port (leave blank for default) - "CONN_MAX_AGE": os.getenv("POSTGRES_TIMEOUT", 300), # Database timeout - "ENGINE": "django.db.backends.postgresql", # Database driver (Postgres only supported!) + "NAME": os.getenv("NAUTOBOT_DB_NAME", "nautobot"), # Database name + "USER": os.getenv("NAUTOBOT_DB_USER", ""), # Database username + "PASSWORD": os.getenv("NAUTOBOT_DB_PASSWORD", ""), # Datbase password + "HOST": os.getenv("NAUTOBOT_DB_HOST", "localhost"), # Database server + "PORT": os.getenv("NAUTOBOT_DB_PORT", ""), # Database port (leave blank for default) + "CONN_MAX_AGE": int(os.environ.get("NAUTOBOT_DB_TIMEOUT", 300)), # Database timeout + "ENGINE": os.getenv("NAUTOBOT_DB_ENGINE", "django.db.backends.postgresql"), # Database driver } } # Redis variables -REDIS_HOST = os.getenv("REDIS_HOST", "localhost") -REDIS_PORT = os.getenv("REDIS_PORT", 6379) -REDIS_PASSWORD = os.getenv("REDIS_PASSWORD", "") +REDIS_HOST = os.getenv("NAUTOBOT_REDIS_HOST", "localhost") +REDIS_PORT = os.getenv("NAUTOBOT_REDIS_PORT", "6379") +REDIS_PASSWORD = os.getenv("NAUTOBOT_REDIS_PASSWORD", "") # Check for Redis SSL REDIS_SCHEME = "redis" -REDIS_SSL = is_truthy(os.environ.get("REDIS_SSL", False)) +REDIS_SSL = is_truthy(os.getenv("REDIS_SSL", "False")) if REDIS_SSL: REDIS_SCHEME = "rediss" @@ -236,18 +231,20 @@ # Each key in the dictionary is the name of an installed plugin and its value is a dictionary of settings. PLUGINS_CONFIG = { "nautobot_chatops": { - "enable_slack": is_truthy(os.environ.get("NAUTOBOT_CHATOPS_ENABLE_SLACK", False)), - "slack_api_token": os.environ.get("NAUTOBOT_CHATOPS_SLACK_API_TOKEN", ""), - "slack_signing_secret": os.environ.get("NAUTOBOT_CHATOPS_SIGNING_SECRET", ""), + "enable_slack": True, + "enable_ms_teams": True, + "enable_webex": True, + "slack_api_token": os.environ.get("SLACK_API_TOKEN"), + "slack_signing_secret": os.environ.get("SLACK_SIGNING_SECRET"), "slack_slash_command_prefix": os.environ.get("SLACK_SLASH_COMMAND_PREFIX", "/"), "enable_mattermost": is_truthy(os.environ.get("NAUTOBOT_CHATOPS_ENABLE_MATTERMOST", False)), "mattermost_api_token": os.environ.get("NAUTOBOT_CHATOPS_MATTERMOST_API_TOKEN", ""), "mattermost_url": os.environ.get("NAUTOBOT_CHATOPS_MATTERMOST_URL", ""), }, "nautobot_plugin_chatops_panorama": { - "panorama_host": os.environ.get("PANORAMA_HOST", ""), - "panorama_user": os.environ.get("PANORAMA_USER", ""), - "panorama_password": os.environ.get("PANORAMA_PASSWORD", ""), + "panorama_host": os.environ.get("PANORAMA_HOST"), + "panorama_user": os.environ.get("PANORAMA_USER"), + "panorama_password": os.environ.get("PANORAMA_PASSWORD"), }, } @@ -320,10 +317,10 @@ # # Global Celery task timeout (in seconds) -CELERY_TASK_TIME_LIMIT = int(os.getenv("NAUTOBOT_CELERY_TASK_TIME_LIMIT", 30 * 60)) +# CELERY_TASK_TIME_LIMIT = int(os.getenv("NAUTOBOT_CELERY_TASK_TIME_LIMIT", 30 * 60)) # Celery broker URL used to tell workers where queues are located -CELERY_BROKER_URL = os.getenv("NAUTOBOT_CELERY_BROKER_URL", parse_redis_connection(redis_database=0)) +# CELERY_BROKER_URL = os.getenv("NAUTOBOT_CELERY_BROKER_URL", parse_redis_connection(redis_database=0)) # Celery results backend URL to tell workers where to publish task results -CELERY_RESULT_BACKEND = os.getenv("NAUTOBOT_CELERY_RESULT_BACKEND", parse_redis_connection(redis_database=0)) +# CELERY_RESULT_BACKEND = os.getenv("NAUTOBOT_CELERY_RESULT_BACKEND", parse_redis_connection(redis_database=0)) diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..22e6b27 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,123 @@ +# Contributing + +## Overview + +Pull requests are welcomed and automatically built and tested against multiple version of Python and multiple version of Nautobot through TravisCI. + +The project is packaged with a light development environment based on `docker-compose` to help with the local development of the project and to run the tests within TravisCI. + +The project is following Network to Code software development guideline and is leveraging: + +- Black, Pylint, Bandit and pydocstyle for Python linting and formatting. +- Django unit test to ensure the plugin is working properly. + +### Development Environment + +The development environment can be used in 2 ways. First, with a local poetry environment if you wish to develop outside of Docker. Second, inside of a docker container. + +#### Invoke tasks + +The [PyInvoke](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 PyInvoke to override the default configuration: + +* `nautobot_ver`: the version of Nautobot to use as a base for any built docker containers (default: 1.0.1) +* `project_name`: the default docker compose project name (default: nautobot_plugin_chatops_panorama) +* `python_ver`: the version of Python to use as a base for any built docker containers (default: 3.6) +* `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) + +Using PyInvoke these configuration options can be overridden using [several methods](http://docs.pyinvoke.org/en/stable/concepts/configuration.html). Perhaps the simplest is simply setting an environment variable `INVOKE_NAUTOBOT_PLUGIN_CHATOPS_PANORAMA_VARIABLE_NAME` where `VARIABLE_NAME` is the variable you are trying to override. The only exception is `compose_files`, because it is a list it must be overridden in a yaml file. There is an example `invoke.yml` in this directory which can be used as a starting point. + +#### Local Poetry Development Environment + +1. Copy `development/creds.example.env` to `development/creds.env` (This file will be ignored by git and docker) +2. Uncomment the `POSTGRES_HOST`, `REDIS_HOST`, and `NAUTOBOT_ROOT` variables in `development/creds.env` +3. Create an invoke.yml with the following contents at the root of the repo: + +```shell +--- +nautobot_plugin_chatops_panorama: + local: true + compose_files: + - "docker-compose.requirements.yml" +``` + +3. Run the following commands: + +```shell +poetry shell +poetry install --extras nautobot +export $(cat development/dev.env | xargs) +export $(cat development/creds.env | xargs) +invoke start && sleep 5 +nautobot-server migrate +``` + +> If you want to develop on the latest develop branch of Nautobot, run the following command: ``poetry add git+https://github.com/nautobot/nautobot@develop``. After the ``@`` symbol must match either a branch or a tag. + +4. You can now run nautobot-server commands as you would from the [Nautobot documentation](https://nautobot.readthedocs.io/en/latest/) for example to start the development server: + +```shell +nautobot-server runserver 0.0.0.0:8080 --insecure +``` + +Nautobot server can now be accessed at [http://localhost:8080](http://localhost:8080). + +#### Docker 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. + +Once you have Poetry and Docker installed you can run the following commands to install all other development dependencies in an isolated python virtual environment: + +```shell +poetry shell +poetry install +invoke start +``` + +Nautobot server can now be accessed at [http://localhost:8080](http://localhost:8080). + +### CLI Helper Commands + +The project is coming with a CLI helper based on [invoke](http://www.pyinvoke.org/) to help setup the development environment. The commands are listed below in 3 categories `dev environment`, `utility` and `testing`. + +Each command can be executed with `invoke `. Environment variables `INVOKE_nautobot_plugin_chatops_panorama_PYTHON_VER` and `INVOKE_nautobot_plugin_chatops_panorama_NAUTOBOT_VER` may be specified to override the default versions. Each command also has its own help `invoke --help` + +#### Docker dev environment + +```no-highlight + build Build all docker images. + debug Start Nautobot and its dependencies in debug mode. + destroy Destroy all containers and volumes. + restart Restart Nautobot and its dependencies. + start Start Nautobot and its dependencies in detached mode. + stop Stop Nautobot and its dependencies. +``` + +#### Utility + +```no-highlight + cli Launch a bash shell inside the running Nautobot container. + create-user Create a new user in django (default: admin), will prompt for password. + makemigrations Run Make Migration in Django. + nbshell Launch a nbshell session. +``` + +#### Testing + +```no-highlight + bandit Run bandit to validate basic static code security analysis. + black Run black to check that Python files adhere to its style standards. + flake8 This will run flake8 for the specified name and Python version. + pydocstyle Run pydocstyle to validate docstring formatting adheres to NTC defined standards. + pylint Run pylint code analysis. + tests Run all tests for this plugin. + unittest Run Django unit tests for the plugin. +``` + +### Project Documentation + +Project documentation is generated by [mkdocs](https://www.mkdocs.org/) from the documentation located in the docs folder. You can configure [readthedocs.io](https://readthedocs.io/) to point at this folder in your repo. For development purposes a `docker-compose.docs.yml` is also included. A container hosting the docs will be started using the invoke commands on [http://localhost:8001](http://localhost:8001), as changes are saved the docs will be automatically reloaded. \ No newline at end of file diff --git a/docs/img/screenshot1.png b/docs/img/screenshot1.png new file mode 100644 index 0000000..9423aab Binary files /dev/null and b/docs/img/screenshot1.png differ diff --git a/docs/img/screenshot2.png b/docs/img/screenshot2.png new file mode 100644 index 0000000..f2426dc Binary files /dev/null and b/docs/img/screenshot2.png differ diff --git a/docs/img/screenshot3.png b/docs/img/screenshot3.png new file mode 100644 index 0000000..c7fb1ff Binary files /dev/null and b/docs/img/screenshot3.png differ diff --git a/docs/img/screenshot4.png b/docs/img/screenshot4.png new file mode 100644 index 0000000..d9f8a14 Binary files /dev/null and b/docs/img/screenshot4.png differ diff --git a/docs/img/screenshot5.png b/docs/img/screenshot5.png new file mode 100644 index 0000000..091033b Binary files /dev/null and b/docs/img/screenshot5.png differ diff --git a/docs/img/screenshot6.png b/docs/img/screenshot6.png new file mode 100644 index 0000000..0643207 Binary files /dev/null and b/docs/img/screenshot6.png differ diff --git a/docs/index.md b/docs/index.md index f7a2c0e..0f05684 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,142 @@ -# NautobotPluginChatopsPanorama +# Nautobot Panorama ChatOps -TODO: Write plugin documentation, the outline here is provided as a guide and should be expanded upon. If more detail is required you are encouraged to expand on the table of contents (TOC) in `mkdocs.yml` to add additional pages. +This is a plugin for [Nautobot](https://github.com/nautobot/nautobot) that extends ChatOps support to Palo Alto Panorama systems. The plugin adds some useful commands into your ChatOps environment that enhance an administrator's and end user's day to day using of Panorama. This framework allows for the quick extension of new ChatOps commands for Panorama. -## Description +Note: While this plugin requires Nautobot and the base Nautobot ChatOps plugin, it does _not_ require the Panorama or Palo Alto inventory to be in Nautobot. It is effectively Nautobot-independent, except for using it as a backend to run the chat bot itself. + +## Usage + +The supported commands are listed below. We welcome any new command or feature requests by submitting an issue or PR. + +| /panorama Command | Description | +| -------------------- | -------------------------------------------------------------------------- | +| capture-traffic | Run a packet capture on PANOS Device for specified IP traffic. | +| export-device-rules | Generate a downloadable list of firewall rules with details in CSV format. | +| get-device-rules | Return a list of all firewall rules on a given device with details. | +| get-version | Obtain software version information for Panorama. | +| install-software | Install software to specified Palo Alto device. | +| upload-software | Upload software to specified Palo Alto device. | +| validate-rule-exists | Verify that a specific ACL rule exists within a device, via Panorama. | + +## Prerequisites + +This plugin requires the [Nautobot ChatOps Plugin](https://github.com/nautobot/nautobot-plugin-chatops) to be installed and configured before using. You can find detailed setup and configuration instructions [here](https://github.com/nautobot/nautobot-plugin-chatops/blob/develop/README.md). ## Installation -## Configuration +The plugin is available as a Python package in pypi and can be installed with pip: -## Usage +```shell +pip install nautobot-plugin-chatops-panorama +``` + +> The plugin is compatible with Nautobot 1.1.0 and higher + +To ensure Nautobot Panorama ChatOps 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-plugin-chatops-panorama` package: + +```no-highlight +# echo nautobot-plugin-chatops-panorama >> local_requirements.txt +``` + +Once installed, the plugin needs to be enabled in your `nautobot_config.py` + +```python +# In your configuration.py +PLUGINS = ["nautobot_chatops", "nautobot_plugin_chatops_panorama"] +``` + +In addition, add/update the below `PLUGINS_CONFIG` section to `nautobot_config.py`. + +> It is only necessary to add the sections from the below snippet for the chat platform you will be using (Slack, Webex, etc.). + +```python +# Also in nautobot_config.py +PLUGINS_CONFIG = { + "nautobot_chatops": { + # Slack + "enable_slack": os.environ.get("ENABLE_SLACK", False), + "slack_api_token": os.environ.get("SLACK_API_TOKEN"), + "slack_signing_secret": os.environ.get("SLACK_SIGNING_SECRET"), + "slack_slash_command_prefix": os.environ.get("SLACK_SLASH_COMMAND_PREFIX", "/"), + # Webex + "enable_webex": os.environ.get("ENABLE_WEBEX", False), + "webex_token": os.environ.get("WEBEX_TOKEN"), + "webex_signing_secret": os.environ.get("WEBEX_SIGNING_SECRET"), + # Mattermost + "enable_mattermost": os.environ.get("ENABLE_MATTERMOST", False), + "mattermost_api_token": os.environ.get("MATTERMOST_API_TOKEN"), + "mattermost_url": os.environ.get("MATTERMOST_URL"), + # MS Teams + "enable_ms_teams": os.environ.get("ENABLE_MS_TEAMS", False), + "microsoft_app_id": os.environ.get("MICROSOFT_APP_ID"), + "microsoft_app_password": os.environ.get("MICROSOFT_APP_PASSWORD"), + }, + "nautobot_plugin_chatops_panorama": { + "panorama_host": os.environ.get("PANORAMA_HOST"), + "panorama_user": os.environ.get("PANORAMA_USER"), + "panorama_password": os.environ.get("PANORAMA_PASSWORD"), + }, +} +``` + +### Environment Variables + +You will need to set the following environment variables for your Nautobot instance, then restart the services for them to take effect. + +- PANORAMA_HOST - This is the management DNS/IP address used to reach your Panorama instance. +- PANORAMA_USER - A user account with API access to Panorama. +- PANORAMA_PASSWORD - The password that goes with the above user account. + +```bash +export PANORAMA_HOST="{{ Panorama DNS/URL }}" +export PANORAMA_USER="{{ Panorama account username }}" +export PANORAMA_PASSWORD="{{ Panorama account password }}" +``` + +If the base Nautobot Chatops plugin is not already installed, the following environment variables are required for the chat platform in use. The [Platform-specific Setup](https://github.com/nautobot/nautobot-plugin-chatops/blob/develop/docs/chat_setup/chat_setup.md#platform-specific-setup) document describes how to retrieve the tokens and secrets for each chat platform that will need to be used in the environment variables. + +> It is only necessary to create the environment variables shown below for the chat platform you will be using. To make the environment variables persistent, add them to the ~/.bash_profile for the user running Nautobot. + +```bash +# Slack +export ENABLE_SLACK="true" +export SLACK_API_TOKEN="foobar" +export SLACK_SIGNING_SECRET="foobar" +# Webex +export ENABLE_WEBEX="true" +export WEBEX_TOKEN="foobar" +export WEBEX_SIGNING_SECRET="foobar" +# Mattermost +export ENABLE_MATTERMOST="false" +export MATTERMOST_API_TOKEN="foobar" +export MATTERMOST_URL="foobar" +# Microsoft Teams +export ENABLE_MS_TEAMS="false" +export MICROSOFT_APP_ID="foobar" +export MICROSOFT_APP_PASSWORD="foobar" +``` + +> When deploying as Docker containers, all of the above environment variables should be defined in the file `development/creds.env`. An example credentials file `creds.env.example` is available in the `development` folder. + +## Access Control + +Just like with the regular `/nautobot` command from the base Nautobot ChatOps plugin, the `/panorama` command supports access control through the Access Grants menu in Nautobot. See section [Grant Access to the Chatbot](https://github.com/nautobot/nautobot-plugin-chatops/blob/develop/docs/chat_setup/chat_setup.md#grant-access-to-the-chatbot) in the installation guide for the base Nautobot ChatOps plugin for setting this up. + +## Questions + +For any questions or comments, please check the [FAQ](FAQ.md) first and feel free to swing by the [Network to Code slack channel](https://networktocode.slack.com/) (channel #networktocode). +Sign up [here](http://slack.networktocode.com/) + +## Screenshots + +![Help](docs/img/screenshot1.png) + +![Validate Rule Exists Success](docs/img/screenshot2.png) + +![Validate Rule Exists Failure](docs/img/screenshot3.png) -## API +![Upload Software](docs/img/screenshot4.png) -## Views +![Capture Traffic Filter](docs/img/screenshot5.png) -## Models +![Capture Traffic](docs/img/screenshot6.png) diff --git a/docs/requirements.txt b/docs/requirements.txt index b3667f9..fe9469e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1 @@ -mkdocs==1.1.2 +mkdocs==1.2.3 diff --git a/nautobot_plugin_chatops_panorama/constant.py b/nautobot_plugin_chatops_panorama/constant.py index a46c67c..614a772 100644 --- a/nautobot_plugin_chatops_panorama/constant.py +++ b/nautobot_plugin_chatops_panorama/constant.py @@ -11,9 +11,14 @@ "ethernet1/5", "ethernet1/6", "ethernet1/7", - "ethernet1/8" + "ethernet1/8", ] UNKNOWN_SITE = "Unknown" ALLOWED_OBJECTS = ("all", "address", "service") + +NAPALM_DRIVER = "panos" +PANOS_MANUFACTURER_NAME = "Palo Alto Networks" +PANOS_PLATFORM = "PANOS" +PANOS_DEVICE_ROLE = "Firewall" diff --git a/nautobot_plugin_chatops_panorama/img/image.png b/nautobot_plugin_chatops_panorama/img/image.png deleted file mode 100644 index 9d1fb3a..0000000 Binary files a/nautobot_plugin_chatops_panorama/img/image.png and /dev/null differ diff --git a/nautobot_plugin_chatops_panorama/jinja_filters.py b/nautobot_plugin_chatops_panorama/jinja_filters.py index 06c6240..ac7798c 100644 --- a/nautobot_plugin_chatops_panorama/jinja_filters.py +++ b/nautobot_plugin_chatops_panorama/jinja_filters.py @@ -1,21 +1,24 @@ +"""Custom Django Jinja filters.""" from django_jinja import library @library.filter def build_service_objects(service): + """Build service objects.""" service_objects = [] name = service.name.upper() protocol = service.protocol.upper() - for p in service.ports: - service_objects.append(f"{name}_{protocol}_{p}") + for port in service.ports: + service_objects.append(f"{name}_{protocol}_{port}") return ", ".join(service_objects) @library.filter def build_address_objects(ip_addresses, name): + """Build address objects.""" address_objects = [] - for ip in ip_addresses.all(): - address_objects.append(f"{name.upper()}_{ip.host.replace('.', '_')}_{ip.prefix_length}") + for ip_address in ip_addresses.all(): + address_objects.append(f"{name.upper()}_{ip_address.host.replace('.', '_')}_{ip_address.prefix_length}") return ", ".join(address_objects) diff --git a/nautobot_plugin_chatops_panorama/static/nautobot_palo/palo-alto-networks.png b/nautobot_plugin_chatops_panorama/static/nautobot_palo/palo-alto-networks.png new file mode 100644 index 0000000..2e7472d Binary files /dev/null and b/nautobot_plugin_chatops_panorama/static/nautobot_palo/palo-alto-networks.png differ diff --git a/nautobot_plugin_chatops_panorama/static/nautobot_palo/palo_resized.png b/nautobot_plugin_chatops_panorama/static/nautobot_palo/palo_resized.png new file mode 100644 index 0000000..4665760 Binary files /dev/null and b/nautobot_plugin_chatops_panorama/static/nautobot_palo/palo_resized.png differ diff --git a/nautobot_plugin_chatops_panorama/static/nautobot_palo/palo_transparent.png b/nautobot_plugin_chatops_panorama/static/nautobot_palo/palo_transparent.png new file mode 100644 index 0000000..f5a8410 Binary files /dev/null and b/nautobot_plugin_chatops_panorama/static/nautobot_palo/palo_transparent.png differ diff --git a/nautobot_plugin_chatops_panorama/utils/nautobot.py b/nautobot_plugin_chatops_panorama/utils/nautobot.py deleted file mode 100644 index 50bf028..0000000 --- a/nautobot_plugin_chatops_panorama/utils/nautobot.py +++ /dev/null @@ -1,79 +0,0 @@ -"""Methods for interactions with Panorama.""" -from typing import List - -from django.utils.text import slugify -from nautobot.dcim.models import Site, Platform, Manufacturer, DeviceType, Device, DeviceRole, Interface -from nautobot.extras.models import Status -from nautobot.ipam.models import IPAddress -from nautobot_plugin_chatops_panorama.constant import INTERFACES - - -def _get_or_create_site(site): - active_status = Status.objects.get(name="Active") - site_obj, created = Site.objects.get_or_create(name=site, slug=slugify(site)) - if site_obj.status != active_status: - site_obj.status = active_status - site_obj.save() - return site_obj - - -def _get_or_create_platform(description: str, platform: str = "PANOS") -> Platform: - manufacturer_obj = Manufacturer.objects.get(name="Palo Alto Networks") - platform = Platform.objects.get_or_create( - name=platform, - slug=slugify(platform), - manufacturer=manufacturer_obj, - napalm_driver="panos", - description=description, - )[0] - if platform.description != description: - platform.description = description - platform.save() - - return platform - - -def _get_or_create_device_type(model: str) -> Platform: - manufacturer_obj = Manufacturer.objects.get(name="Palo Alto Networks") - return DeviceType.objects.get_or_create(model=model, slug=slugify(model), manufacturer=manufacturer_obj)[0] - - -def _get_or_create_device(device: str, serial: str, site: Site, device_type: DeviceType, os: str) -> Platform: - manufacturer_obj = Manufacturer.objects.get(name="Palo Alto Networks") - device_role_obj = DeviceRole.objects.get(name="Firewall") - active_status_obj = Status.objects.get(name="Active") - device_platform_obj = _get_or_create_platform(description=os) - - device_obj = Device.objects.get_or_create( - name=device, - device_role=device_role_obj, - status=active_status_obj, - site=site, - platform=device_platform_obj, - device_type=device_type, - serial=serial - )[0] - device_obj.custom_field_data['public_ipv4'] = '3.13.252.97' - device_obj.save() - - return device_obj - - -def _get_or_create_interfaces(device: Device) -> List[Interface]: - """Generate standard interfaces for Palo devices.""" - interfaces = [] - for intf in INTERFACES: - interfaces.append(Interface.objects.get_or_create(name=intf, device=device, type="1000base-t (ge)")[0]) - - return interfaces - - -def _get_or_create_management_ip(device: Device, interface: Interface, ip_address: str): - active_status = Status.objects.get(name="Active") - mgmt_ip = IPAddress.objects.get_or_create( - address=ip_address, status=active_status, assigned_object_id=interface.id - )[0] - device.primary_ip4 = mgmt_ip - device.save() - - return mgmt_ip diff --git a/nautobot_plugin_chatops_panorama/utils/panorama.py b/nautobot_plugin_chatops_panorama/utils/panorama.py index dee0097..21ffd72 100644 --- a/nautobot_plugin_chatops_panorama/utils/panorama.py +++ b/nautobot_plugin_chatops_panorama/utils/panorama.py @@ -1,20 +1,25 @@ -from nautobot_plugin_chatops_panorama.constant import PLUGIN_CFG +"""Functions used for interacting with Panroama.""" + +import time -from panos.panorama import Panorama -from panos.firewall import Firewall -from panos.objects import AddressObject, ServiceObject -from panos.errors import PanObjectMissing -from requests.exceptions import RequestException import defusedxml.ElementTree as ET import requests + from netmiko import ConnectHandler -import time +from panos.firewall import Firewall +from panos.panorama import Panorama from panos.policies import Rulebase, SecurityRule +from requests.exceptions import RequestException + +from nautobot_plugin_chatops_panorama.constant import PLUGIN_CFG + def get_api_key_api(url: str = PLUGIN_CFG["panorama_host"]) -> str: """Returns the API key. + Args: url (str): URL of the device + Returns: The API key. """ @@ -22,7 +27,7 @@ def get_api_key_api(url: str = PLUGIN_CFG["panorama_host"]) -> str: params = {"type": "keygen", "user": PLUGIN_CFG["panorama_user"], "password": PLUGIN_CFG["panorama_password"]} - response = requests.get(f"https://{url}/api/", params=params, verify=False) + response = requests.get(f"https://{url}/api/", params=params, verify=False) # nosec if response.status_code != 200: raise RequestException(f"Something went wrong while making a request. Reason: {response.text}") @@ -40,7 +45,7 @@ def connect_panorama() -> Panorama: return pano -def _get_group(groups, serial): +def _get_group(groups: dict, serial: str) -> str: """Sort through fetched groups and serials and return group. Args: @@ -50,25 +55,33 @@ def _get_group(groups, serial): Returns: group_name (str): Name of group serial is part of or None if serial not in a group """ - for k, v in groups.items(): - if serial in v: - return k + for group_name, serial_numbers in groups.items(): + if serial in serial_numbers: + return group_name + return None -def get_rule_match(connection: Panorama, five_tuple: dict, serial: str) -> dict: +def get_rule_match(five_tuple: dict, serial: str) -> dict: """Method to obtain the devices connected to Panorama. + Args: - connection (Panorama): Connection object to Panorama. + five_tuple (dict): Five tuple dictionary for rule lookup + serial (str): Serial of firewall device to query + Returns: dict: Dictionary of all devices attached to Panorama. """ - - host = PLUGIN_CFG['panorama_host'].rstrip("/") - fw = Firewall(serial=serial) + host = PLUGIN_CFG["panorama_host"].rstrip("/") + firewall = Firewall(serial=serial) pano = Panorama(host, api_key=get_api_key_api()) - pano.add(fw) - return fw.test_security_policy_match(source=five_tuple["src_ip"], destination=five_tuple["dst_ip"], protocol=int(five_tuple["protocol"]), port=int(five_tuple["dst_port"])) - + pano.add(firewall) + match = firewall.test_security_policy_match( + source=five_tuple["src_ip"], + destination=five_tuple["dst_ip"], + protocol=int(five_tuple["protocol"]), + port=int(five_tuple["dst_port"]), + ) + return match def get_devices(connection: Panorama) -> dict: @@ -80,7 +93,7 @@ def get_devices(connection: Panorama) -> dict: Returns: dict: Dictionary of all devices attached to Panorama. """ - dev_list = connection.refresh_devices(expand_vsys=False, include_device_groups=False) + dev_list = connection.refresh_devices(include_device_groups=False) group_names = [device.name for device in connection.refresh_devices()] group_xml_obj = connection.op("show devicegroups") @@ -111,38 +124,39 @@ def get_devices(connection: Panorama) -> dict: return _device_dict -def start_packet_capture(ip: str, filters: dict): +def start_packet_capture(capture_filename: str, ip_address: str, filters: dict): """Starts or stops packet capturing on the Managed FW. Args: - ip (str): IP address of the device + capture_filename (str): Name of packet capture file + ip_address (str): IP address of the device filters (dict): Commands to pass to the device for packet capturing - """ - dev_connect = { - 'device_type': 'paloalto_panos', - 'host': ip, - 'username': PLUGIN_CFG["panorama_user"], - 'password': PLUGIN_CFG["panorama_password"] + "device_type": "paloalto_panos", + "host": ip_address, + "username": PLUGIN_CFG["panorama_user"], + "password": PLUGIN_CFG["panorama_password"], } - command=f"debug dataplane packet-diag set filter index 1 match ingress-interface {filters['intf_name']}" + command = f"debug dataplane packet-diag set filter index 1 match ingress-interface {filters['intf_name']}" - if filters["dport"]: + # Ignore this command if not filtering by port (when user sets port to 'any') + if filters["dport"] and filters["dport"] != "any": command += f" destination-port {filters['dport']}" - if filters['dnet'] != "0.0.0.0": + if filters["dnet"] != "0.0.0.0": # nosec command += f" destination {filters['dnet']}" - if filters['dcidr'] != "0": + if filters["dcidr"] != "0": command += f" destination-netmask {filters['dcidr']}" - if filters['snet'] != "0.0.0.0": + if filters["snet"] != "0.0.0.0": # nosec command += f" source {filters['snet']}" - if filters['scidr'] != "0": + if filters["scidr"] != "0": command += f" source-netmask {filters['scidr']}" - if filters['ip_proto']: + # Ignore this command if not filtering by port (when user sets protocol to 'any') + if filters["ip_proto"] and filters["ip_proto"] != "any": command += f" protocol {filters['ip_proto']}" ssh = ConnectHandler(**dev_connect) @@ -151,119 +165,56 @@ def start_packet_capture(ip: str, filters: dict): ssh.send_command(command) ssh.send_command("debug dataplane packet-diag set filter on") - ssh.send_command(f"debug dataplane packet-diag set capture stage {filters['stage']} byte-count 1024 file python.pcap") + ssh.send_command( + f"debug dataplane packet-diag set capture stage {filters['stage']} byte-count 1024 file python.pcap" + ) ssh.send_command("debug dataplane packet-diag set capture on") - time.sleep(int(filters['capture_seconds'])) + time.sleep(int(filters["capture_seconds"])) ssh.send_command("debug dataplane packet-diag set capture off") ssh.send_command("debug dataplane packet-diag set filter off") ssh.disconnect() - _get_pcap(ip) + _get_pcap(capture_filename, ip_address) -def _get_pcap(ip:str): - """Downloads PCAP file from PANOS device +def _get_pcap(capture_filename: str, ip_address: str): + """Downloads PCAP file from PANOS device. - Args:b - ip (str): IP address of the device + Args: + capture_filename (str): Name of packet capture file + ip_address (str): IP address of the device """ + url = f"https://{ip_address}/api/" - url = f"https://{ip}/api/" - - params = { - "key": get_api_key_api(), - "type": "export", - "category": "filters-pcap", - "from": "1.pcap" - } + params = {"key": get_api_key_api(), "type": "export", "category": "filters-pcap", "from": "1.pcap"} - respone = requests.get(url, params=params, verify=False) + respone = requests.get(url, params=params, verify=False) # nosec - with open("captured.pcap", "wb") as pcap_file: + with open(capture_filename, "wb") as pcap_file: pcap_file.write(respone.content) -def compare_address_objects(address_objects, connection): - results = [] - for addr in address_objects: - # Set initial values to be used in final results (row) - loop_result = [addr, "address"] - - # Parse out the IP address and CIDR - oct1, oct2, oct3, oct4, cidr = addr.split("_")[1:] - ip_address = f"{oct1}.{oct2}.{oct3}.{oct4}/{cidr}" - - # Build Panos Objects to attempt to compare to. - addr_obj = AddressObject(name=addr, value=ip_address) - panos_obj = connection.add(addr_obj) - - # Catch exception if object doesn't already exist to prevent invalid comparison - try: - panos_obj.refresh() - except PanObjectMissing: - loop_result.append("Does not exist") - results.append(loop_result) - continue - - if panos_obj.value != ip_address: - loop_result.append(f"Discrepancy!! Nautobot value: {ip_address}, Panorama value: {panos_obj.value}") - else: - loop_result.append(f"Nautobot and Panorama are in sync for {addr}.") - - results.append(loop_result) - - return results - - -def compare_service_objects(service_objects, connection): - results = [] - for svc in service_objects: - # Set initial values to be used in final results (row) - loop_result = [svc, "service"] - - # Parse out the IP address and CIDR - protocol, port = svc.split("_")[1:] - protocol = protocol.lower() - - # Build Panos Objects to attempt to compare to. - svc_obj = ServiceObject(name=svc, protocol=protocol, destination_port=port) - panos_obj = connection.add(svc_obj) - - # Catch exception if object doesn't already exist to prevent invalid comparison - try: - panos_obj.refresh() - except PanObjectMissing: - loop_result.append("Does not exist") - results.append(loop_result) - continue - - status_msg = "" - if panos_obj.protocol != protocol: - status_msg += f"Incorrect protocol: ({protocol}/{panos_obj.protocol})" - if panos_obj.destination_port != port: - status_msg += f"Incorrect port: ({port}/{panos_obj.destination_port})" - - if not status_msg: - loop_result.append(f"Nautobot and Panorama are in sync for {svc}.") - else: - loop_result.append(status_msg) - - results.append(loop_result) - - return results - def parse_all_rule_names(xml_rules: str) -> list: + """Parse all rules names.""" rule_names = [] root = ET.fromstring(xml_rules) # Get names of rules - for i in root.findall('.//entry'): + for i in root.findall(".//entry"): name = i.attrib.get("name") rule_names.append(name) return rule_names -def get_all_rules(device=None): - pano = connect_panorama() - devices = pano.refresh_devices(expand_vsys=False, include_device_groups=False) +def get_all_rules(device: str, pano: Panorama) -> list: + """Get all currently configured rules. + + Args: + device (str): Name of firewall device in Panorama + pano (Panorama): Panorama connection + + Returns: + list: List of rules + """ + devices = pano.refresh_devices(include_device_groups=False) device = pano.add(devices[0]) # TODO: Future - filter by name input, the query/filter in Nautobot DB and/or Panorama # if not device: @@ -274,7 +225,8 @@ def get_all_rules(device=None): return rules -def split_rules(rules, title=''): +def split_rules(rules, title=""): + """Split rules into CSV format.""" output = title or "Name,Source,Destination,Service,Action,To Zone,From Zone\n" for rule in rules: sources = "" diff --git a/nautobot_plugin_chatops_panorama/worker.py b/nautobot_plugin_chatops_panorama/worker.py index e2bd76a..e7f5e74 100644 --- a/nautobot_plugin_chatops_panorama/worker.py +++ b/nautobot_plugin_chatops_panorama/worker.py @@ -1,52 +1,48 @@ """Example rq worker to handle /panorama chat commands with 1 subcommand addition.""" import logging -import requests +import os +import re +from ipaddress import ip_network +from typing import List, Tuple, Union + +from netmiko import NetMikoTimeoutException +from netutils.protocol_mapper import PROTO_NAME_TO_NUM from django_rq import job from nautobot.dcim.models import Device, Interface -from nautobot.ipam.models import Service from nautobot_chatops.choices import CommandStatusChoices from nautobot_chatops.workers import handle_subcommands, subcommand_of -import json -import defusedxml.ElementTree as ET -import ipaddr -from panos.panorama import DeviceGroup from panos.firewall import Firewall from panos.errors import PanDeviceError -from panos.policies import SecurityRule - -from nautobot_plugin_chatops_panorama.constant import UNKNOWN_SITE, ALLOWED_OBJECTS, PLUGIN_CFG -from nautobot_plugin_chatops_panorama.utils.nautobot import ( - _get_or_create_site, - _get_or_create_device_type, - _get_or_create_device, - _get_or_create_interfaces, - _get_or_create_management_ip, -) + +from nautobot_plugin_chatops_panorama.constant import ALLOWED_OBJECTS from nautobot_plugin_chatops_panorama.utils.panorama import ( connect_panorama, get_devices, - compare_address_objects, - compare_service_objects, - get_api_key_api, get_rule_match, - parse_all_rule_names, start_packet_capture, get_all_rules, - split_rules + split_rules, ) +PALO_LOGO_PATH = "nautobot_palo/palo_transparent.png" +PALO_LOGO_ALT = "Palo Alto Networks Logo" logger = logging.getLogger("rq.worker") +def palo_logo(dispatcher): + """Construct an image_element containing the locally hosted Palo Alto Networks logo.""" + return dispatcher.image_element(dispatcher.static_url(PALO_LOGO_PATH), alt_text=PALO_LOGO_ALT) + + def prompt_for_panos_device_group(dispatcher, command, connection): """Prompt user for panos device group to check for groups from.""" group_names = [device.name for device in connection.refresh_devices()] dispatcher.prompt_from_menu(command, "Select Panorama Device Group", [(grp, grp) for grp in group_names]) - return CommandStatusChoices.STATUS_ERRORED + return CommandStatusChoices.STATUS_SUCCEEDED def prompt_for_object_type(dispatcher, command): @@ -54,29 +50,79 @@ def prompt_for_object_type(dispatcher, command): dispatcher.prompt_from_menu( command, "Select an allowed object type", [(object_type, object_type) for object_type in ALLOWED_OBJECTS] ) - return CommandStatusChoices.STATUS_ERRORED + return CommandStatusChoices.STATUS_SUCCEEDED def prompt_for_nautobot_device(dispatcher, command): """Prompt user for firewall device within Nautobot.""" _devices = Device.objects.all() dispatcher.prompt_from_menu(command, "Select a Nautobot Device", [(dev.name, str(dev.id)) for dev in _devices]) - return CommandStatusChoices.STATUS_ERRORED + return CommandStatusChoices.STATUS_SUCCEEDED def prompt_for_device(dispatcher, command, conn): """Prompt the user to select a Palo Alto device.""" _devices = get_devices(connection=conn) dispatcher.prompt_from_menu(command, "Select a Device", [(dev, dev) for dev in _devices]) - return CommandStatusChoices.STATUS_ERRORED + return CommandStatusChoices.STATUS_SUCCEEDED -def prompt_for_versions(dispatcher, command, conn): +def prompt_for_versions(dispatcher, command, conn, prompt_offset=None): """Prompt the user to select a version.""" conn.software.check() versions = conn.software.versions - dispatcher.prompt_from_menu(command, "Select a Version", [(ver, ver) for ver in versions]) - return CommandStatusChoices.STATUS_ERRORED + if prompt_offset: + prompt_offset = int(prompt_offset) + dispatcher.prompt_from_menu(command, "Select a Version", [(ver, ver) for ver in versions][prompt_offset:]) + return CommandStatusChoices.STATUS_SUCCEEDED + + +def is_valid_cidr(ip_address: str) -> str: + """Checks if string is a valid IPv4 CIDR.""" + try: + return str(ip_network(str(ip_address))) + except ValueError: + return "" + + +def notify_user_of_error(dispatcher: object, error_msg: str) -> str: + """Notify the user of an error, logs error to syslog, and returns FAILED command status for Nautobot logging.""" + logger.error(error_msg) + dispatcher.send_warning(error_msg) + return CommandStatusChoices.STATUS_FAILED + + +def capture_packet_str_validation( + dispatcher: object, + value_to_check: str, + valid_values: List[Tuple[str, Union[str, int]]], + variable_description: str, + not_found_error: str, +) -> Tuple[str, bool]: + """Validates a user string input against list existing of valid values. + + Args: + dispatcher (obj): Dispatcher object from Nautobot/Django + value_to_check (str): Value to check to see if valid + valid_values (list): List of tuples with valid values + variable_description (str): Type of variable being checked (e.g. "Interface") + not_found_error (str): Error to display to user and log to syslog if value given is invalid + + Returns: + tuple: Tuple of string value, and boolean if validation passed + """ + try: + valid_item_found = [ + valid_item for valid_item in valid_values if value_to_check.lower() == valid_item[0].lower() + ] + if valid_item_found: + return valid_item_found[0][1], True + # User supplied an invalid or unsupported value + return notify_user_of_error(dispatcher, not_found_error), False + except AttributeError: + # User may have supplied an invalid value, or there was an error parsing the value given as a string + # Ideally this should not trigger + return notify_user_of_error(dispatcher, f"{variable_description} is invalid."), False @job("default") @@ -86,14 +132,11 @@ def panorama(subcommand, **kwargs): @subcommand_of("panorama") -def validate_rule_exists(dispatcher, device, src_ip, dst_ip, protocol, dst_port): +def validate_rule_exists( + dispatcher, device, src_ip, dst_ip, protocol, dst_port +): # pylint:disable=too-many-arguments,too-many-locals,too-many-branches """Verify that the rule exists within a device, via Panorama.""" - dialog_list = [ - { - "type": "text", - "label": "Device", - }, { "type": "text", "label": "Source IP", @@ -105,8 +148,8 @@ def validate_rule_exists(dispatcher, device, src_ip, dst_ip, protocol, dst_port) { "type": "select", "label": "Dest IP", - "choices": [("TCP", "6"), ("UDP", "17")], - "default": ("TCP", "6"), + "choices": [("TCP", "TCP"), ("UDP", "UDP")], + "default": ("TCP", "TCP"), }, { "type": "text", @@ -114,23 +157,58 @@ def validate_rule_exists(dispatcher, device, src_ip, dst_ip, protocol, dst_port) "default": "443", }, ] - if not all([device, src_ip, dst_ip, protocol, dst_port]): - dispatcher.multi_input_dialog("panorama", "validate-rule-exists", "Verify if rule exists", dialog_list) - return CommandStatusChoices.STATUS_SUCCEEDED + + if all([device, src_ip, dst_ip, protocol, dst_port]): + dispatcher.send_markdown( + f"Standby {dispatcher.user_mention()}, I'm checking the firewall rules now. ", + ephemeral=True, + ) pano = connect_panorama() + if not device: + return prompt_for_device(dispatcher, "panorama validate-rule-exists", pano) + + if not all([src_ip, dst_ip, protocol, dst_port]): + dispatcher.multi_input_dialog( + "panorama", f"validate-rule-exists {device}", "Verify if rule exists", dialog_list + ) + return CommandStatusChoices.STATUS_SUCCEEDED + + # Validate IP addresses are valid or 'any' is used. + # TODO: Add support for hostnames + if not is_valid_cidr(src_ip) and src_ip.lower() != "any": + dispatcher.send_warning( + f"Source IP {src_ip} is not a valid host or CIDR. Please specify a valid host IP address or IP network in CIDR notation." + ) + dispatcher.multi_input_dialog( + "panorama", f"validate-rule-exists {device}", "Verify if rule exists", dialog_list + ) + return CommandStatusChoices.STATUS_ERRORED + + if not is_valid_cidr(dst_ip) and src_ip.lower() != "any": + dispatcher.send_warning() + dispatcher.multi_input_dialog( + "panorama", f"validate-rule-exists {device}", "Verify if rule exists", dialog_list + ) + return CommandStatusChoices.STATUS_ERRORED + serial = get_devices(connection=pano).get(device, {}).get("serial") if not serial: - return dispatcher.send_markdown(f"The device {device} was not found.") + return dispatcher.send_warning(f"The device {device} was not found.") - data = {"src_ip":src_ip, "dst_ip": dst_ip, "protocol": protocol, "dst_port": dst_port} - matching_rules = get_rule_match(connection=pano, five_tuple=data, serial=serial) + data = { + "src_ip": src_ip, + "dst_ip": dst_ip, + "protocol": PROTO_NAME_TO_NUM.get(protocol.upper()), + "dst_port": dst_port, + } + matching_rules = get_rule_match(five_tuple=data, serial=serial) if matching_rules: - all_rules = list() - for rule in get_all_rules(device): + all_rules = [] + for rule in get_all_rules(device, pano): if rule.name == matching_rules[0]["name"]: - rule_list = list() + rule_list = [] rule_list.append(rule.name) sources = "" for src in rule.source: @@ -146,11 +224,49 @@ def validate_rule_exists(dispatcher, device, src_ip, dst_ip, protocol, dst_port) rule_list.append(service[:-2]) rule_list.append(rule.action) all_rules.append(rule_list) + blocks = [ + *dispatcher.command_response_header( + "panorama", + "validate-rule-exists", + [ + ("Device", device), + ("Source IP", src_ip), + ("Destination IP", dst_ip), + ("Protocol", protocol.upper()), + ("Destination Port", dst_port), + ], + "validated rule", + palo_logo(dispatcher), + ), + ] + dispatcher.send_blocks(blocks) dispatcher.send_markdown(f"The Traffic is permitted via a rule named `{matching_rules[0]['name']}`:") dispatcher.send_large_table(("Name", "Source", "Destination", "Service", "Action"), all_rules) else: - dispatcher.send_markdown(f"`No matching rule` found for:") - all_values = [["Device", device], ["Source IP", src_ip],["Destination", dst_ip], ["Protocol", protocol], ["Destination Port", dst_port]] + blocks = [ + *dispatcher.command_response_header( + "panorama", + "validate-rule-exists", + [ + ("Device", device), + ("Source IP", src_ip), + ("Destination IP", dst_ip), + ("Protocol", protocol.upper()), + ("Destination Port", dst_port), + ], + "rule validation", + palo_logo(dispatcher), + ), + ] + dispatcher.send_blocks(blocks) + dispatcher.send_markdown("`No matching rule` found for:") + all_values = [ + ["Device", device], + ["Source IP", src_ip], + ["Destination", dst_ip], + ["Protocol", protocol], + ["Destination Port", dst_port], + ] dispatcher.send_large_table(("Object", "Value"), all_values) return CommandStatusChoices.STATUS_SUCCEEDED @@ -158,15 +274,23 @@ def validate_rule_exists(dispatcher, device, src_ip, dst_ip, protocol, dst_port) @subcommand_of("panorama") def get_version(dispatcher): """Obtain software version information for Panorama.""" + dispatcher.send_markdown( + f"Standby {dispatcher.user_mention()}, I'm getting Panorama's version for you.", + ephemeral=True, + ) pano = connect_panorama() - dispatcher.send_markdown(f"The version of Panorama is {pano.refresh_system_info().version}.") - return CommandStatusChoices.STATUS_SUCCEEDED - - -@subcommand_of("panorama") -def the_best(dispatcher, **kwargs): - """Who is the best?""" - dispatcher.send_image("/source/nautobot_plugin_chatops_panorama/img/image.png") + version = pano.refresh_system_info().version + blocks = [ + *dispatcher.command_response_header( + "panorama", + "get-version", + [], + "Panorama version", + palo_logo(dispatcher), + ) + ] + dispatcher.send_blocks(blocks) + dispatcher.send_markdown(f"The version of Panorama is {version}.") return CommandStatusChoices.STATUS_SUCCEEDED @@ -180,19 +304,45 @@ def upload_software(dispatcher, device, version, **kwargs): return prompt_for_device(dispatcher, "panorama upload-software", pano) if not version: - prompt_for_versions(dispatcher, f"panorama upload-software {device}", pano) - return CommandStatusChoices.STATUS_FAILED + return prompt_for_versions(dispatcher, f"panorama upload-software {device}", pano) + + if "menu_offset" in version: + return prompt_for_versions( + dispatcher, f"panorama upload-software {device}", pano, prompt_offset=re.findall(r"\d+", version)[0] + ) devs = get_devices(connection=pano) - dispatcher.send_markdown(f"Hey {dispatcher.user_mention()}, you've requested to upload {version} to {device}.") + dispatcher.send_markdown( + f"Hey {dispatcher.user_mention()}, you've requested to upload {version} to {device}.", ephemeral=True + ) _firewall = Firewall(serial=devs[device]["serial"]) pano.add(_firewall) - dispatcher.send_markdown("Starting download now...") + dispatcher.send_markdown("Starting download now...", ephemeral=True) try: _firewall.software.download(version) except PanDeviceError as err: - dispatcher.send_markdown(f"There was an issue uploading {version} to {device}. {err}") - return CommandStatusChoices.STATUS_FAILED + blocks = [ + *dispatcher.command_response_header( + "panorama", + "upload-software", + [("Device", device), ("Version", version)], + "information on that upload software task", + palo_logo(dispatcher), + ), + ] + dispatcher.send_blocks(blocks) + dispatcher.send_warning(f"There was an issue uploading {version} to {device}. {err}") + return CommandStatusChoices.STATUS_SUCCEEDED + blocks = [ + *dispatcher.command_response_header( + "panorama", + "upload-software", + [("Device", device), ("Version", version)], + "information on that upload software task", + palo_logo(dispatcher), + ), + ] + dispatcher.send_blocks(blocks) dispatcher.send_markdown(f"As requested, {version} is being uploaded to {device}.") return CommandStatusChoices.STATUS_SUCCEEDED @@ -210,146 +360,63 @@ def install_software(dispatcher, device, version, **kwargs): prompt_for_versions(dispatcher, f"panorama install-software {device}", pano) return False + if "menu_offset" in version: + return prompt_for_versions( + dispatcher, f"panorama upload-software {device}", pano, prompt_offset=re.findall(r"\d+", version)[0] + ) + devs = get_devices(connection=pano) - dispatcher.send_markdown(f"Hey {dispatcher.user_mention()}, you've requested to install {version} to {device}.") + dispatcher.send_markdown( + f"Hey {dispatcher.user_mention()}, you've requested to install {version} to {device}.", ephemeral=True + ) _firewall = Firewall(serial=devs[device]["serial"]) pano.add(_firewall) try: _firewall.software.install(version) except PanDeviceError as err: - dispatcher.send_markdown(f"There was an issue installing {version} on {device}. {err}") + blocks = [ + *dispatcher.command_response_header( + "panorama", + "install-software", + [("Device", device), ("Version", version)], + "information on that install software task", + palo_logo(dispatcher), + ), + ] + dispatcher.send_blocks(blocks) + dispatcher.send_warning(f"There was an issue installing {version} on {device}. {err}") return CommandStatusChoices.STATUS_FAILED + blocks = [ + *dispatcher.command_response_header( + "panorama", + "install-software", + [("Device", device), ("Version", version)], + "information on that install software task", + palo_logo(dispatcher), + ), + ] + dispatcher.send_blocks(blocks) dispatcher.send_markdown(f"As requested, {version} has been installed on {device}.") return CommandStatusChoices.STATUS_SUCCEEDED -@subcommand_of("panorama") -def sync_firewalls(dispatcher): - """Sync firewalls into Nautobot.""" - logger.info("Starting synchronization from Panorama.") - pano = connect_panorama() - devices = get_devices(connection=pano) - device_status = [] - for name, data in devices.items(): - if not data["group_name"]: - data["group_name"] = UNKNOWN_SITE - # logic to create site via group_name - site = _get_or_create_site(data["group_name"]) - # logic to create device type based on model - device_type = _get_or_create_device_type(data["model"]) - # logic to create device - device = _get_or_create_device(name, data["serial"], site, device_type, data["os_version"]) - # logic to create interfaces - interfaces = _get_or_create_interfaces(device) - # logic to assign ip_address to mgmt interface - mgmt_ip = _get_or_create_management_ip(device, interfaces[0], data["ip_address"]) - - # Add info for device creation to be sent to table creation at the end of task - status = (name, site, device_type, mgmt_ip, ", ".join([intf.name for intf in interfaces])) - device_status.append(status) - dispatcher.send_large_table(("Name", "Site", "Type", "Primary IP", "Interfaces"), device_status) - return CommandStatusChoices.STATUS_SUCCEEDED - - -@subcommand_of("panorama") -def validate_objects(dispatcher, device, object_type, device_group): - """Validate Address Objects exist for a device.""" - logger.info("Starting synchronization from Panorama.") - if not device: - return prompt_for_nautobot_device(dispatcher, "panorama validate-objects") - if not object_type: - return prompt_for_object_type(dispatcher, f"panorama validate-objects {device}") - - pano = connect_panorama() - if not device_group: - return prompt_for_panos_device_group(dispatcher, f"panorama validate-objects {device} {object_type}", pano) - - pano = pano.add(DeviceGroup(name=device_group)) - device = Device.objects.get(id=device) - services = Service.objects.filter(device=device) - if not services: - return dispatcher.send_markdown(f"No available services to validate against for {device}") - - object_results = [] - names = set() - for s in services: - computed_fields = s.get_computed_fields() - - if object_type == "address" or object_type == "all": - computed_objects = computed_fields.get("address_objects") - obj_names = set(computed_objects.split(", ")) - current_objs = obj_names.difference(names) - names.update(current_objs) - if computed_objects: - object_results.extend(compare_address_objects(current_objs, pano)) - - if object_type == "service" or object_type == "all": - computed_objects = computed_fields.get("service_objects") - obj_names = set(computed_objects.split(", ")) - current_objs = obj_names.difference(names) - names.update(current_objs) - if computed_objects: - object_results.extend(compare_service_objects(current_objs, pano)) - - dispatcher.send_large_table(("Name", "Object Type", "Status (Nautobot/Panorama)"), object_results) - return CommandStatusChoices.STATUS_SUCCEEDED - - -@subcommand_of("panorama") -def get_pano_rules(dispatcher, **kwargs): - """Get list of firewall rules by name.""" - logger.info("Pulling list of firewall rules by name.") - pano = connect_panorama() - # if not device: - # return prompt_for_nautobot_device(dispatcher, "panorama get-rules") - # device = Device.objects.get(id=device) - api_key = get_api_key_api() - params = { - "key": api_key, - "cmd": """ - - - - - - - - - - - - - - - """, - "type": "op", - } - host = PLUGIN_CFG["panorama_host"].rstrip("/") - url = f"https://{host}/api/" - response = requests.get(url, params=params, verify=False) - if not response.ok: - dispatcher.send_markdown(f"Error retrieving device rules.") - return CommandStatusChoices.STATUS_FAILED - - rule_names = parse_all_rule_names(response.text) - return_str = "" - for idx, name in enumerate(rule_names): - return_str += f"Rule {idx+1}\t\t{name}\n" - dispatcher.send_markdown(return_str) - return CommandStatusChoices.STATUS_SUCCEEDED - - @subcommand_of("panorama") def get_device_rules(dispatcher, device, **kwargs): """Get list of firewall rules with details.""" + pano = connect_panorama() if not device: - return prompt_for_nautobot_device(dispatcher, "panorama get-device-rules") + return prompt_for_device(dispatcher, "panorama get-device-rules", pano) + + dispatcher.send_markdown( + f"Standby {dispatcher.user_mention()}, I'm getting the rules for device {device}.", + ephemeral=True, + ) - rules = get_all_rules(device) + rules = get_all_rules(device, pano) - all_rules = list() + all_rules = [] for rule in rules: - rule_list = list() + rule_list = [] rule_list.append(rule.name) sources = "" for src in rule.source: @@ -366,70 +433,105 @@ def get_device_rules(dispatcher, device, **kwargs): rule_list.append(rule.action) all_rules.append(rule_list) + blocks = [ + *dispatcher.command_response_header( + "panorama", + "get-device-rules", + [("Device", device)], + f"rules for device {device}", + palo_logo(dispatcher), + ), + ] + dispatcher.send_blocks(blocks) dispatcher.send_large_table(("Name", "Source", "Destination", "Service", "Action"), all_rules) return CommandStatusChoices.STATUS_SUCCEEDED @subcommand_of("panorama") def export_device_rules(dispatcher, device, **kwargs): - """Get list of firewall rules with details.""" - if not device: - return prompt_for_nautobot_device(dispatcher, "panorama export-device-rules") - - rules = get_all_rules(device) - - output = split_rules(rules) + """Generate list of firewall rules with details in CSV format.""" + if device: + dispatcher.send_markdown( + f"Standby {dispatcher.user_mention()}, I'm creating the CSV file for the rules on device {device}.", + ephemeral=True, + ) + else: + pano = connect_panorama() + return prompt_for_device(dispatcher, "panorama export-device-rules", pano) + logger.debug("Running /panorama export-device-rules, device=%s", device) - # dispatcher.snippet(output) - dispatcher.send_snippet(output) - return CommandStatusChoices.STATUS_SUCCEEDED + pano = connect_panorama() + rules = get_all_rules(device, pano) + file_name = f"{device}-device-rules.csv" -@subcommand_of("panorama") -def export_device_rules_csv(dispatcher, device, **kwargs): - """Get list of firewall rules with details.""" - if not device: - return prompt_for_nautobot_device(dispatcher, "panorama export-device-rules") - - rules = get_all_rules(device) + output = split_rules(rules) + with open(file_name, "w") as file: # pylint: disable=unspecified-encoding + file.write(output) - file_name = "device_rules.csv" + dispatcher.send_image(file_name) - output = split_rules(rules) - with open(file_name, "w") as f: - f.write(output) + try: + os.remove(file_name) + logger.debug("Deleted generated CSV file %s", file_name) + except FileNotFoundError: + logger.warning("Unable to delete generated CSV file %s", file_name) - # dispatcher.snippet(output) - dispatcher.send_image(file=file_name) return CommandStatusChoices.STATUS_SUCCEEDED @subcommand_of("panorama") -def capture_traffic(dispatcher, device_id, snet, dnet, dport, intf_name, ip_proto, stage, capture_seconds, **kwargs): - """Capture IP traffic on PANOS Device +def capture_traffic( + dispatcher: object, + device: str, + snet: str, + dnet: str, + dport: str, + intf_name: str, + ip_proto: str, + stage: str, + capture_seconds: str, + **kwargs, +): # pylint:disable=too-many-statements,too-many-arguments,too-many-return-statements,too-many-locals,too-many-branches + """Capture IP traffic on PANOS Device. Args: - device_id - snet - dnet - dport - intf_name - ip_proto + dispatcher (object): Chatops plugin dispatcher object + device (str): Device name + snet (str): Source IP/network in IPv4 CIDR notation + dnet (str): Destination IP/network in IPv4 CIDR notation + dport (str): Destination port + intf_name (str): Interface name + ip_proto (str): Protocol for destination port + stage (str): Stage to use + capture_seconds (str): Number of seconds to run packet capture + """ - logger.info("Starting packet capturing.") - _devices = Device.objects.all() + logger.info("Starting capture_traffic()") + + valid_ip_protocols = [("ANY", "any"), ("TCP", "6"), ("UDP", "17")] + valid_stages = [("Receive", "receive"), ("Transmit", "transmit"), ("Drop", "drop"), ("Firewall", "firewall")] + + if all([device, snet, dnet, dport, intf_name, ip_proto, stage, capture_seconds]): + dispatcher.send_markdown( + f"Standby {dispatcher.user_mention()}, I'm starting the packet capture on device {device}.", + ephemeral=True, + ) # --------------------------------------------------- # Get device to execute against # --------------------------------------------------- - if not device_id: - dispatcher.prompt_from_menu("panorama capture-traffic", "Select Palo-Alto Device", [(dev.name, str(dev.id)) for dev in _devices]) - return CommandStatusChoices.STATUS_SUCCEEDED + pano = connect_panorama() + + if not device: + return prompt_for_device(dispatcher, "panorama capture-traffic", pano) # --------------------------------------------------- # Get parameters used to filter packet capture # --------------------------------------------------- - _interfaces = Interface.objects.filter(device__id=device_id) + _interfaces = Interface.objects.filter(device__name=device) + interface_list = [(intf.name, intf.name) for intf in _interfaces] + dialog_list = [ { "type": "text", @@ -449,22 +551,22 @@ def capture_traffic(dispatcher, device_id, snet, dnet, dport, intf_name, ip_prot { "type": "select", "label": "Interface Name", - "choices": [(intf.name, intf.name) for intf in _interfaces], + "choices": interface_list, "confirm": False, }, { "type": "select", "label": "IP Protocol", - "choices": [("TCP", "6"), ("UDP", "17"), ("ANY", "any")], + "choices": valid_ip_protocols, "confirm": False, - "default": ("TCP", "6") + "default": ("ANY", "any"), }, { "type": "select", "label": "Capture Stage", - "choices": [("Receive", "receive"), ("Transmit", "transmit"), ("Drop", "drop"), ("Firewall", "firewall")], + "choices": valid_stages, "confirm": False, - "default": ("Receive", "receive") + "default": ("Receive", "receive"), }, { "type": "text", @@ -474,70 +576,152 @@ def capture_traffic(dispatcher, device_id, snet, dnet, dport, intf_name, ip_prot ] if not all([snet, dnet, dport, intf_name, ip_proto, stage, capture_seconds]): - dispatcher.multi_input_dialog("panorama", f"capture-traffic {device_id}", "Capture Filter", dialog_list) + dispatcher.multi_input_dialog("panorama", f"capture-traffic {device}", "Capture Filter", dialog_list) return CommandStatusChoices.STATUS_SUCCEEDED + logger.debug( + "Running packet capture with the following information:\nDevice - %s\nSource Network - %s\nDestination Network - %s\nDestination Port - %s\nInterface Name - %s\nIP Protocol - %s\nStage - %s\nCapture Seconds - %s", + device, + snet, + dnet, + dport, + intf_name, + ip_proto, + stage, + capture_seconds, + ) + # --------------------------------------------------- # Validate dialog list # --------------------------------------------------- - try: - ipaddr.IPv4Network(snet) - source_network = snet.split("/")[0] - source_cidr = snet.split("/")[1] - except: - dispatcher.send_markdown(f"Source Network {snet} is not a valid CIDR, please specify a valid network in CIDR notation") - return CommandStatusChoices.STATUS_FAILED + # Validate snet try: - ipaddr.IPv4Network(dnet) - dest_network = dnet.split("/")[0] - dest_cidr = dnet.split("/")[1] - except: - dispatcher.send_markdown(f"Destination Network {dnet} is not a valid CIDR, please specify a valid network in CIDR notation") - return CommandStatusChoices.STATUS_FAILED + ip_network(snet) + except ValueError: + return notify_user_of_error( + dispatcher, f"Source Network {snet} is not a valid CIDR, please specify a valid network in CIDR notation." + ) - if dport == "any": - dport = None - else: - try: - dport = int(dport) - if not dport >= 1 or not dport <= 65535: - raise ValueError - except: - dispatcher.send_markdown(f"Destination Port {dport} must be either the string `any` or an integer in the range 1-65535") - return CommandStatusChoices.STATUS_FAILED + # Validate dnet + try: + ip_network(dnet) + except ValueError: + return notify_user_of_error( + dispatcher, + f"Destination Network {dnet} is not a valid CIDR, please specify a valid network in CIDR notation.", + ) - if ip_proto == "any": - ip_proto = None + # Validate dport + try: + dport_error = f"Destination Port {dport} must be either the string `any` or an integer in the range 1-65535." + if not 1 <= int(dport) <= 65535: + raise TypeError + except ValueError: + # Port may be a string, which is still valid + dport = dport.lower() + if dport != "any": + return notify_user_of_error(dispatcher, dport_error) + except (AttributeError, TypeError): + return notify_user_of_error(dispatcher, dport_error) + + # Validate intf_name + # If the user supplied an interface name, this uses the actual one present and ignores case sensitivity + intf_name, validation_result = capture_packet_str_validation( + dispatcher, intf_name, interface_list, "Interface", f"Interface {intf_name} was not found on device {device}." + ) + if not validation_result: + return intf_name + + # Validate ip_proto + # We have to validate here and do some conversion in case the user pastes in the full command + # If valid, change to value needed for actual command. E.g. 'tcp' should be the protocol number '6' + # Invalid because we currently do not support pasting in protocol integers, only the protocol name (e.g. 'tcp') or 'any', or the user supplied an unsupported protocol. + ip_proto, validation_result = capture_packet_str_validation( + dispatcher, + ip_proto, + valid_ip_protocols, + "IP protocol", + f"IP protocol {ip_proto} must be a valid IP protocol `tcp`, `udp`, or the string `any`.", + ) + if not validation_result: + return ip_proto + + # Validate stage + stage, validation_result = capture_packet_str_validation( + dispatcher, + stage, + valid_stages, + "Stage", + f"Stage {stage} must be a valid stage `drop`, `firewall`, `receive`, or `transmit`", + ) + if not validation_result: + return stage + # Validate capture_seconds try: - capture_seconds = int(capture_seconds) - if capture_seconds > 120 or capture_seconds < 1: + if not 1 <= int(capture_seconds) <= 120: raise ValueError except ValueError: - dispatcher.send_markdown(f"Capture Seconds must be specified as a number in the range 1-120") - return CommandStatusChoices.STATUS_FAILED + return notify_user_of_error(dispatcher, "Capture Seconds must be specified as a number in the range 1-120.") # --------------------------------------------------- # Start Packet Capture on Device # --------------------------------------------------- - device_ip = Device.objects.get(id=device_id).custom_field_data["public_ipv4"] - dispatcher.send_markdown(f"Starting {capture_seconds} second packet capture") - start_packet_capture( - device_ip, - { - "snet": snet.split("/")[0], - "scidr": snet.split("/")[1], - "dnet": dnet.split("/")[0], - "dcidr": dnet.split("/")[1], - "dport": dport, - "intf_name": intf_name, - "ip_proto": ip_proto, - "stage": stage, - "capture_seconds": capture_seconds - } - ) + devices = get_devices(connection=pano) + try: + # TODO: This gathers the internal IP address that Panorama sees. However if the firewall is accessible to Nautobot via a different IP address (e.g. external), this fails. + # Add support for multiple possible IP addresses here + device_ip = devices[device]["ip_address"] + logger.info("Attempting packet capture to device %s via IP address %s.", device, device_ip) + except KeyError: + return notify_user_of_error(dispatcher, f"No IP address found assigned to device {device} in Panorama.") - dispatcher.send_markdown("Here is the PCAP file that your requested!") - return dispatcher.send_image('captured.pcap') + # Name of capture file + capture_filename = f"{device}-packet-capture.pcap" + # Begin packet capture on device + try: + start_packet_capture( + capture_filename, + device_ip, + { + "snet": snet.split("/")[0], + "scidr": snet.split("/")[1], + "dnet": dnet.split("/")[0], + "dcidr": dnet.split("/")[1], + "dport": dport, + "intf_name": intf_name, + "ip_proto": ip_proto, + "stage": stage, + "capture_seconds": capture_seconds, + }, + ) + except NetMikoTimeoutException: + return notify_user_of_error(dispatcher, f"Unable to connect to device {device} via IP address {device_ip}.") + + blocks = [ + *dispatcher.command_response_header( + "panorama", + "capture-traffic", + [("Details below:", " ")], + "PCAP file", + palo_logo(dispatcher), + ), + ] + + dispatcher.send_blocks(blocks) + + all_values = [ + ["Device", device], + ["Source Network", snet], + ["Destination Network", dnet], + ["Destination Port", dport], + ["Interface Name", intf_name], + ["IP Protocol", ip_proto], + ["Stage", stage], + ["Capture Seconds", capture_seconds], + ] + dispatcher.send_large_table(("Object", "Value"), all_values) + dispatcher.send_image(capture_filename) + return CommandStatusChoices.STATUS_SUCCEEDED diff --git a/poetry.lock b/poetry.lock index 6bc6836..a9c87cc 100644 --- a/poetry.lock +++ b/poetry.lock @@ -17,14 +17,6 @@ category = "main" optional = false python-versions = "*" -[[package]] -name = "appdirs" -version = "1.4.4" -description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." -category = "dev" -optional = false -python-versions = "*" - [[package]] name = "asgiref" version = "3.4.1" @@ -41,7 +33,7 @@ tests = ["pytest", "pytest-asyncio", "mypy (>=0.800)"] [[package]] name = "astroid" -version = "2.6.2" +version = "2.9.0" description = "An abstract syntax tree for Python with inference support." category = "dev" optional = false @@ -49,9 +41,9 @@ python-versions = "~=3.6" [package.dependencies] lazy-object-proxy = ">=1.4.0" -typed-ast = {version = ">=1.4.0,<1.5", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} -typing-extensions = {version = ">=3.7.4", markers = "python_version < \"3.8\""} -wrapt = ">=1.11,<1.13" +typed-ast = {version = ">=1.4.0,<2.0", markers = "implementation_name == \"cpython\" and python_version < \"3.8\""} +typing-extensions = {version = ">=3.10", markers = "python_version < \"3.10\""} +wrapt = ">=1.11,<1.14" [[package]] name = "attrs" @@ -69,7 +61,7 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (> [[package]] name = "bandit" -version = "1.7.0" +version = "1.7.1" description = "Security oriented static analyser for python code." category = "dev" optional = false @@ -79,7 +71,6 @@ python-versions = ">=3.5" colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} GitPython = ">=1.0.1" PyYAML = ">=5.3.1" -six = ">=1.10.0" stevedore = ">=1.20.0" [[package]] @@ -108,26 +99,31 @@ python-versions = "*" [[package]] name = "black" -version = "20.8b1" +version = "21.12b0" description = "The uncompromising code formatter." category = "dev" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.2" [package.dependencies] -appdirs = "*" click = ">=7.1.2" dataclasses = {version = ">=0.6", markers = "python_version < \"3.7\""} mypy-extensions = ">=0.4.3" -pathspec = ">=0.6,<1" -regex = ">=2020.1.8" -toml = ">=0.10.1" -typed-ast = ">=1.4.0" -typing-extensions = ">=3.7.4" +pathspec = ">=0.9.0,<1" +platformdirs = ">=2" +tomli = ">=0.2.6,<2.0.0" +typed-ast = {version = ">=1.4.2", markers = "python_version < \"3.8\" and implementation_name == \"cpython\""} +typing-extensions = [ + {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, + {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""}, +] [package.extras] colorama = ["colorama (>=0.4.3)"] -d = ["aiohttp (>=3.3.2)", "aiohttp-cors"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +python2 = ["typed-ast (>=1.4.3)"] +uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "cached-property" @@ -190,7 +186,7 @@ zstd = ["zstandard"] [[package]] name = "certifi" -version = "2021.5.30" +version = "2021.10.8" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -198,7 +194,7 @@ python-versions = "*" [[package]] name = "cffi" -version = "1.14.6" +version = "1.15.0" description = "Foreign Function Interface for Python calling C code." category = "main" optional = false @@ -209,7 +205,7 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "2.0.2" +version = "2.0.9" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." category = "main" optional = false @@ -228,14 +224,14 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "click-didyoumean" -version = "0.0.3" -description = "Enable git-like did-you-mean feature in click." +version = "0.3.0" +description = "Enables git-like *did-you-mean* feature in click" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6.2,<4.0.0" [package.dependencies] -click = "*" +click = ">=7" [[package]] name = "click-plugins" @@ -299,18 +295,18 @@ jinja2 = "*" [[package]] name = "coverage" -version = "5.5" +version = "6.2" description = "Code coverage measurement for Python" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" +python-versions = ">=3.6" [package.extras] -toml = ["toml"] +toml = ["tomli"] [[package]] name = "cryptography" -version = "3.4.7" +version = "36.0.0" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." category = "main" optional = false @@ -321,11 +317,11 @@ cffi = ">=1.12" [package.extras] docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] -docstest = ["doc8", "pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] +docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] -sdist = ["setuptools-rust (>=0.11.4)"] +sdist = ["setuptools_rust (>=0.11.4)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["pytest (>=6.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] +test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] [[package]] name = "dataclasses" @@ -343,9 +339,23 @@ category = "main" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +[[package]] +name = "deprecated" +version = "1.2.13" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["tox", "bump2version (<1)", "sphinx (<2)", "importlib-metadata (<3)", "importlib-resources (<4)", "configparser (<5)", "sphinxcontrib-websupport (<2)", "zipp (<2)", "PyTest (<5)", "PyTest-Cov (<2.6)", "pytest", "pytest-cov"] + [[package]] name = "django" -version = "3.1.13" +version = "3.1.14" description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." category = "main" optional = false @@ -362,11 +372,11 @@ bcrypt = ["bcrypt"] [[package]] name = "django-appconf" -version = "1.0.4" +version = "1.0.5" description = "A helper class for handling configuration defaults of packaged apps gracefully." category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" [package.dependencies] django = "*" @@ -408,9 +418,20 @@ python-versions = ">=3.5" cryptography = "*" django-appconf = "*" +[[package]] +name = "django-db-file-storage" +version = "0.5.5" +description = "Custom FILE_STORAGE for Django. Saves files in your database instead of your file system." +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Django = "*" + [[package]] name = "django-debug-toolbar" -version = "3.2.1" +version = "3.2.2" description = "A configurable set of panels that display various debug information about the current request/response." category = "dev" optional = false @@ -444,15 +465,15 @@ django = ">=2.2" [[package]] name = "django-jinja" -version = "2.9.0" +version = "2.7.1" description = "Jinja2 templating language integrated in Django." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.5" [package.dependencies] django = ">=2.2" -jinja2 = ">=3" +jinja2 = ">=2.10" [[package]] name = "django-js-asset" @@ -607,17 +628,17 @@ validation = ["swagger-spec-validator (>=2.1.0)"] [[package]] name = "flake8" -version = "3.9.2" +version = "4.0.1" description = "the modular source code checker: pep8 pyflakes and co" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" +python-versions = ">=3.6" [package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} +importlib-metadata = {version = "<4.3", markers = "python_version < \"3.8\""} mccabe = ">=0.6.0,<0.7.0" -pycodestyle = ">=2.7.0,<2.8.0" -pyflakes = ">=2.3.0,<2.4.0" +pycodestyle = ">=2.8.0,<2.9.0" +pyflakes = ">=2.4.0,<2.5.0" [[package]] name = "funcy" @@ -635,20 +656,34 @@ category = "main" optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +[[package]] +name = "ghp-import" +version = "2.0.2" +description = "Copy your docs directly to the gh-pages branch." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +python-dateutil = ">=2.8.1" + +[package.extras] +dev = ["twine", "markdown", "flake8", "wheel"] + [[package]] name = "gitdb" -version = "4.0.7" +version = "4.0.9" description = "Git Object Database" category = "main" optional = false -python-versions = ">=3.4" +python-versions = ">=3.6" [package.dependencies] -smmap = ">=3.0.1,<5" +smmap = ">=3.0.1,<6" [[package]] name = "gitpython" -version = "3.1.18" +version = "3.1.20" description = "Python Git Library" category = "main" optional = false @@ -656,11 +691,11 @@ python-versions = ">=3.6" [package.dependencies] gitdb = ">=4.0.1,<5" -typing-extensions = {version = ">=3.7.4.0", markers = "python_version < \"3.8\""} +typing-extensions = {version = ">=3.7.4.3", markers = "python_version < \"3.10\""} [[package]] name = "graphene" -version = "2.1.8" +version = "2.1.9" description = "GraphQL Framework for Python" category = "main" optional = false @@ -675,7 +710,7 @@ six = ">=1.10.0,<2" [package.extras] django = ["graphene-django"] sqlalchemy = ["graphene-sqlalchemy"] -test = ["pytest", "pytest-benchmark", "pytest-cov", "pytest-mock", "snapshottest", "coveralls", "promise", "six", "mock", "pytz", "iso8601"] +test = ["pytest", "pytest-benchmark", "pytest-cov", "pytest-mock", "fastdiff (==0.2.0)", "snapshottest", "coveralls", "promise", "six", "mock", "pytz", "iso8601"] [[package]] name = "graphene-django" @@ -731,7 +766,7 @@ six = ">=1.12" [[package]] name = "idna" -version = "3.2" +version = "3.3" description = "Internationalized Domain Names in Applications (IDNA)" category = "main" optional = false @@ -739,7 +774,7 @@ python-versions = ">=3.5" [[package]] name = "importlib-metadata" -version = "3.4.0" +version = "3.10.1" description = "Read metadata from Python packages" category = "main" optional = false @@ -751,11 +786,11 @@ zipp = ">=0.5" [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=3.5,!=3.7.3)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] +testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "packaging", "pep517", "pyfakefs", "flufl.flake8", "pytest-black (>=0.3.7)", "pytest-mypy", "importlib-resources (>=1.3)"] [[package]] name = "importlib-resources" -version = "5.2.0" +version = "5.4.0" description = "Read resources from Python packages" category = "main" optional = false @@ -766,7 +801,7 @@ zipp = {version = ">=3.1.0", markers = "python_version < \"3.10\""} [package.extras] docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"] -testing = ["pytest (>=4.6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "pytest-cov", "pytest-enabler (>=1.0.1)", "pytest-black (>=0.3.7)", "pytest-mypy"] [[package]] name = "inflection" @@ -794,16 +829,17 @@ python-versions = "*" [[package]] name = "isort" -version = "5.8.0" +version = "5.10.1" description = "A Python utility / library to sort Python imports." category = "dev" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.6.1,<4.0" [package.extras] pipfile_deprecated_finder = ["pipreqs", "requirementslib"] requirements_deprecated_finder = ["pipreqs", "pip-api"] colors = ["colorama (>=0.4.3,<0.5.0)"] +plugins = ["setuptools"] [[package]] name = "itypes" @@ -815,25 +851,17 @@ python-versions = "*" [[package]] name = "jinja2" -version = "3.0.1" +version = "2.11.3" description = "A very fast and expressive template engine." category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [package.dependencies] -MarkupSafe = ">=2.0" +MarkupSafe = ">=0.23" [package.extras] -i18n = ["Babel (>=2.7)"] - -[[package]] -name = "joblib" -version = "1.0.1" -description = "Lightweight pipelining with Python functions" -category = "dev" -optional = false -python-versions = ">=3.6" +i18n = ["Babel (>=0.8)"] [[package]] name = "jsonschema" @@ -891,45 +919,14 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" -[[package]] -name = "livereload" -version = "2.6.3" -description = "Python LiveReload is an awesome tool for web developers" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -six = "*" -tornado = {version = "*", markers = "python_version > \"2.7\""} - -[[package]] -name = "lunr" -version = "0.5.8" -description = "A Python implementation of Lunr.js" -category = "dev" -optional = false -python-versions = "*" - -[package.dependencies] -future = ">=0.16.0" -nltk = {version = ">=3.2.5", optional = true, markers = "python_version > \"2.7\" and extra == \"languages\""} -six = ">=1.11.0" - -[package.extras] -languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"] - [[package]] name = "markdown" -version = "3.3.4" +version = "3.3.5" description = "Python implementation of Markdown." category = "main" optional = false python-versions = ">=3.6" -[package.dependencies] -importlib-metadata = {version = "*", markers = "python_version < \"3.8\""} - [package.extras] testing = ["coverage", "pyyaml"] @@ -949,22 +946,36 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "mergedeep" +version = "1.3.4" +description = "A deep merge function for 🐍." +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "mkdocs" -version = "1.1.2" +version = "1.2.3" description = "Project documentation with Markdown." category = "dev" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [package.dependencies] click = ">=3.3" +ghp-import = ">=1.0" +importlib-metadata = ">=3.10" Jinja2 = ">=2.10.1" -livereload = ">=2.5.1" -lunr = {version = "0.5.8", extras = ["languages"]} Markdown = ">=3.2.1" +mergedeep = ">=1.3.4" +packaging = ">=20.5" PyYAML = ">=3.10" -tornado = ">=5.0" +pyyaml-env-tag = ">=0.1" +watchdog = ">=2.0" + +[package.extras] +i18n = ["babel (>=2.9.0)"] [[package]] name = "mypy-extensions" @@ -976,11 +987,11 @@ python-versions = "*" [[package]] name = "nautobot" -version = "1.1.0b2" +version = "1.1.6" description = "Source of truth and network automation platform." category = "main" optional = false -python-versions = ">=3.6,<4.0" +python-versions = ">=3.6.2,<4.0.0" [package.dependencies] celery = ">=5.1.0,<5.2.0" @@ -988,9 +999,10 @@ Django = ">=3.1.12,<3.2.0" django-cacheops = ">=5.1,<5.2" django-cors-headers = ">=3.7.0,<3.8.0" django-cryptography = ">=1.0,<1.1" +django-db-file-storage = ">=0.5.5,<0.6.0" django-filter = ">=2.4.0,<2.5.0" django-health-check = ">=3.16.4,<4.0.0" -django-jinja = ">=2.8.0,<3.0.0" +django-jinja = "<2.8.0" django-mptt = ">=0.11.0,<0.12.0" django-prometheus = ">=2.1.0,<2.2.0" django-redis = ">=4.12.1,<4.13.0" @@ -1003,12 +1015,12 @@ djangorestframework = ">=3.12.4,<3.13.0" drf-yasg = {version = ">=1.20.0,<1.21.0", extras = ["validation"]} GitPython = ">=3.1.15,<3.2.0" graphene-django = ">=2.15.0,<2.16.0" -importlib-metadata = {version = ">=3.4.0,<3.5.0", markers = "python_version < \"3.8\""} -Jinja2 = ">=3.0.1,<3.1.0" +importlib-metadata = {version = ">=3.10.0,<3.11.0", markers = "python_version < \"3.8\""} +Jinja2 = ">=2.11.3,<2.12.0" jsonschema = ">=3.2.0,<3.3.0" Markdown = ">=3.3.4,<3.4.0" netaddr = ">=0.8.0,<0.9.0" -Pillow = ">=8.1.0,<8.2.0" +Pillow = ">=8.3.2,<8.4.0" psycopg2-binary = ">=2.8.6,<2.9.0" pycryptodome = ">=3.10.1,<3.11.0" pyuwsgi = ">=2.0.19.1.post0,<2.1.0.0" @@ -1021,18 +1033,18 @@ mysql = ["mysqlclient (>=2.0.3,<2.1.0)"] [[package]] name = "nautobot-capacity-metrics" -version = "1.0.1" +version = "1.1.0" description = "Plugin to improve the instrumentation of Nautobot and expose additional metrics (Application Metrics, RQ Worker)." category = "main" optional = false python-versions = ">=3.6,<4.0" [package.dependencies] -nautobot = "*" +nautobot = ">=1.0.0,<2.0.0" [[package]] name = "nautobot-chatops" -version = "1.3.1" +version = "1.5.1" description = "A plugin providing chatbot capabilities for Nautobot" category = "main" optional = false @@ -1077,30 +1089,16 @@ tenacity = "*" test = ["pyyaml (>=5.1.2)", "pytest (>=5.1.2)"] [[package]] -name = "nltk" -version = "3.6.2" -description = "Natural Language Toolkit" -category = "dev" +name = "netutils" +version = "0.2.5" +description = "Common helper functions useful in network automation." +category = "main" optional = false -python-versions = ">=3.5.*" - -[package.dependencies] -click = "*" -joblib = "*" -regex = "*" -tqdm = "*" - -[package.extras] -all = ["matplotlib", "twython", "scipy", "numpy", "gensim (<4.0.0)", "python-crfsuite", "pyparsing", "scikit-learn", "requests"] -corenlp = ["requests"] -machine_learning = ["gensim (<4.0.0)", "numpy", "python-crfsuite", "scikit-learn", "scipy"] -plot = ["matplotlib"] -tgrep = ["pyparsing"] -twitter = ["twython"] +python-versions = ">=3.6,<4.0" [[package]] name = "ntc-templates" -version = "2.1.0" +version = "3.0.0" description = "TextFSM Templates for Network Devices, and Python wrapper for TextFSM's CliTable." category = "main" optional = false @@ -1124,18 +1122,18 @@ signedtoken = ["cryptography (>=3.0.0,<4)", "pyjwt (>=2.0.0,<3)"] [[package]] name = "packaging" -version = "21.0" +version = "21.3" description = "Core utilities for Python packages" category = "main" optional = false python-versions = ">=3.6" [package.dependencies] -pyparsing = ">=2.0.2" +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" [[package]] name = "pan-os-python" -version = "1.3.0" +version = "1.6.0" description = "Framework for interacting with Palo Alto Networks devices via API" category = "main" optional = false @@ -1154,7 +1152,7 @@ python-versions = "*" [[package]] name = "paramiko" -version = "2.7.2" +version = "2.8.1" description = "SSH2 protocol library" category = "main" optional = false @@ -1173,15 +1171,15 @@ invoke = ["invoke (>=1.3)"] [[package]] name = "pathspec" -version = "0.8.1" +version = "0.9.0" description = "Utility library for gitignore style pattern matching of file paths." category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "pbr" -version = "5.6.0" +version = "5.8.0" description = "Python Build Reasonableness" category = "dev" optional = false @@ -1189,15 +1187,27 @@ python-versions = ">=2.6" [[package]] name = "pillow" -version = "8.1.2" +version = "8.3.2" description = "Python Imaging Library (Fork)" category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "platformdirs" +version = "2.4.0" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] +test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] + [[package]] name = "prometheus-client" -version = "0.11.0" +version = "0.12.0" description = "Python client for the Prometheus monitoring system." category = "main" optional = false @@ -1222,11 +1232,11 @@ test = ["pytest (>=2.7.3)", "pytest-cov", "coveralls", "futures", "pytest-benchm [[package]] name = "prompt-toolkit" -version = "3.0.3" +version = "3.0.23" description = "Library for building powerful interactive command lines in Python" category = "main" optional = false -python-versions = ">=3.6" +python-versions = ">=3.6.2" [package.dependencies] wcwidth = "*" @@ -1241,15 +1251,15 @@ python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*" [[package]] name = "pycodestyle" -version = "2.7.0" +version = "2.8.0" description = "Python style guide checker" category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" [[package]] name = "pycparser" -version = "2.20" +version = "2.21" description = "C parser in Python" category = "main" optional = false @@ -1257,7 +1267,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pycryptodome" -version = "3.10.1" +version = "3.10.4" description = "Cryptographic library for Python" category = "main" optional = false @@ -1279,7 +1289,7 @@ toml = ["toml"] [[package]] name = "pyflakes" -version = "2.3.1" +version = "2.4.0" description = "passive checker of Python programs" category = "dev" optional = false @@ -1287,32 +1297,34 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [[package]] name = "pyjwt" -version = "2.1.0" +version = "2.3.0" description = "JSON Web Token implementation in Python" category = "main" optional = false python-versions = ">=3.6" [package.extras] -crypto = ["cryptography (>=3.3.1,<4.0.0)"] -dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1,<4.0.0)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"] +crypto = ["cryptography (>=3.3.1)"] +dev = ["sphinx", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.3.1)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "mypy", "pre-commit"] docs = ["sphinx", "sphinx-rtd-theme", "zope.interface"] tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] [[package]] name = "pylint" -version = "2.9.3" +version = "2.12.2" description = "python code static checker" category = "dev" optional = false -python-versions = "~=3.6" +python-versions = ">=3.6.2" [package.dependencies] -astroid = ">=2.6.2,<2.7" +astroid = ">=2.9.0,<2.10" colorama = {version = "*", markers = "sys_platform == \"win32\""} isort = ">=4.2.5,<6" mccabe = ">=0.6,<0.7" -toml = ">=0.7.1" +platformdirs = ">=2.2.0" +toml = ">=0.9.2" +typing-extensions = {version = ">=3.10.0", markers = "python_version < \"3.10\""} [[package]] name = "pylint-django" @@ -1359,11 +1371,14 @@ tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] [[package]] name = "pyparsing" -version = "2.4.7" +version = "3.0.6" description = "Python parsing module" category = "main" optional = false -python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" +python-versions = ">=3.6" + +[package.extras] +diagrams = ["jinja2", "railroad-diagrams"] [[package]] name = "pyrsistent" @@ -1384,6 +1399,17 @@ python-versions = "*" [package.extras] cp2110 = ["hidapi"] +[[package]] +name = "python-dateutil" +version = "2.8.2" +description = "Extensions to the standard Python datetime module" +category = "dev" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" + +[package.dependencies] +six = ">=1.5" + [[package]] name = "python3-openid" version = "3.2.0" @@ -1401,7 +1427,7 @@ postgresql = ["psycopg2"] [[package]] name = "pytz" -version = "2021.1" +version = "2021.3" description = "World timezone definitions, modern and historical" category = "main" optional = false @@ -1409,7 +1435,7 @@ python-versions = "*" [[package]] name = "pyuwsgi" -version = "2.0.19.1.post0" +version = "2.0.20" description = "The uWSGI server" category = "main" optional = false @@ -1424,23 +1450,29 @@ optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" [[package]] -name = "redis" -version = "3.5.3" -description = "Python client for Redis key-value store" -category = "main" +name = "pyyaml-env-tag" +version = "0.1" +description = "A custom YAML tag for referencing environment variables in YAML files. " +category = "dev" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" +python-versions = ">=3.6" -[package.extras] -hiredis = ["hiredis (>=0.1.3)"] +[package.dependencies] +pyyaml = "*" [[package]] -name = "regex" -version = "2021.7.6" -description = "Alternative regular expression module, to replace re." -category = "dev" +name = "redis" +version = "4.0.2" +description = "Python client for Redis database and key-value store" +category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" + +[package.dependencies] +deprecated = "*" + +[package.extras] +hiredis = ["hiredis (>=1.0.0)"] [[package]] name = "requests" @@ -1488,7 +1520,7 @@ requests = ">=2.0.1,<3.0.0" [[package]] name = "rq" -version = "1.9.0" +version = "1.10.0" description = "RQ is a simple, lightweight, library for creating background jobs, and processing them." category = "main" optional = false @@ -1500,7 +1532,7 @@ redis = ">=3.5.0" [[package]] name = "ruamel.yaml" -version = "0.17.10" +version = "0.17.17" description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" category = "main" optional = false @@ -1531,7 +1563,7 @@ python-versions = "*" [[package]] name = "scp" -version = "0.13.6" +version = "0.14.1" description = "scp module for paramiko" category = "main" optional = false @@ -1542,7 +1574,7 @@ paramiko = "*" [[package]] name = "singledispatch" -version = "3.6.2" +version = "3.7.0" description = "Backport functools.singledispatch from Python 3.4 to Python 2.6-3.3." category = "main" optional = false @@ -1565,27 +1597,27 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] name = "slack-sdk" -version = "3.7.0" +version = "3.12.0" description = "The Slack API Platform SDK for Python" category = "main" optional = false python-versions = ">=3.6.0" [package.extras] -optional = ["aiodns (>1.0)", "aiohttp (>=3.7.3,<4)", "boto3 (<=2)", "SQLAlchemy (>=1,<2)", "websockets (>=9.1,<10)", "websocket-client (>=1,<2)"] -testing = ["pytest (>=5.4,<6)", "pytest-asyncio (<1)", "Flask-Sockets (>=0.2,<1)", "pytest-cov (>=2,<3)", "codecov (>=2,<3)", "flake8 (>=3,<4)", "black (==21.6b0)", "psutil (>=5,<6)", "databases (>=0.3)"] +optional = ["aiodns (>1.0)", "aiohttp (>=3.7.3,<4)", "boto3 (<=2)", "SQLAlchemy (>=1,<2)", "websockets (>=10,<11)", "websocket-client (>=1,<2)"] +testing = ["pytest (>=6.2.5,<7)", "pytest-asyncio (<1)", "Flask-Sockets (>=0.2,<1)", "Flask (>=1,<2)", "Werkzeug (<2)", "pytest-cov (>=2,<3)", "codecov (>=2,<3)", "flake8 (>=3,<4)", "black (==21.9b0)", "psutil (>=5,<6)", "databases (>=0.3)", "boto3 (<=2)", "moto (<2)"] [[package]] name = "smmap" -version = "4.0.0" +version = "5.0.0" description = "A pure Python implementation of a sliding window memory map manager" category = "main" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" [[package]] name = "snowballstemmer" -version = "2.1.0" +version = "2.2.0" description = "This package provides 29 stemmers for 28 languages generated from Snowball algorithms." category = "dev" optional = false @@ -1629,7 +1661,7 @@ saml = ["python3-saml (>=1.2.1)"] [[package]] name = "sqlparse" -version = "0.4.1" +version = "0.4.2" description = "A non-validating SQL parser." category = "main" optional = false @@ -1637,7 +1669,7 @@ python-versions = ">=3.5" [[package]] name = "stevedore" -version = "3.3.0" +version = "3.5.0" description = "Manage dynamic plugins for Python applications" category = "dev" optional = false @@ -1657,7 +1689,7 @@ python-versions = ">=3.6" [[package]] name = "swagger-spec-validator" -version = "2.7.3" +version = "2.7.4" description = "Validation of Swagger specifications" category = "main" optional = false @@ -1716,56 +1748,40 @@ optional = false python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" [[package]] -name = "tornado" -version = "6.1" -description = "Tornado is a Python web framework and asynchronous networking library, originally developed at FriendFeed." -category = "dev" -optional = false -python-versions = ">= 3.5" - -[[package]] -name = "tqdm" -version = "4.61.2" -description = "Fast, Extensible Progress Meter" +name = "tomli" +version = "1.2.2" +description = "A lil' TOML parser" category = "dev" optional = false -python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7" - -[package.dependencies] -colorama = {version = "*", markers = "platform_system == \"Windows\""} - -[package.extras] -dev = ["py-make (>=0.1.0)", "twine", "wheel"] -notebook = ["ipywidgets (>=6)"] -telegram = ["requests"] +python-versions = ">=3.6" [[package]] name = "typed-ast" -version = "1.4.3" +version = "1.5.1" description = "a fork of Python 2 and 3 ast modules with type comment support" category = "dev" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "typing-extensions" -version = "3.10.0.0" -description = "Backported and Experimental Type Hints for Python 3.5+" +version = "4.0.1" +description = "Backported and Experimental Type Hints for Python 3.6+" category = "main" optional = false -python-versions = "*" +python-versions = ">=3.6" [[package]] name = "uritemplate" -version = "3.0.1" -description = "URI templates" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" category = "main" optional = false -python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +python-versions = ">=3.6" [[package]] name = "urllib3" -version = "1.26.6" +version = "1.26.7" description = "HTTP library with thread-safe connection pooling, file post, and more." category = "main" optional = false @@ -1784,6 +1800,17 @@ category = "main" optional = false python-versions = ">=3.6" +[[package]] +name = "watchdog" +version = "2.1.6" +description = "Filesystem events monitoring" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +watchmedo = ["PyYAML (>=3.10)"] + [[package]] name = "wcwidth" version = "0.2.5" @@ -1808,19 +1835,19 @@ requests-toolbelt = "*" [[package]] name = "wrapt" -version = "1.12.1" +version = "1.13.3" description = "Module for decorators, wrappers and monkey patching." -category = "dev" +category = "main" optional = false -python-versions = "*" +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" [[package]] name = "yamllint" -version = "1.26.1" +version = "1.26.3" description = "A linter for YAML files." category = "dev" optional = false -python-versions = ">=3.5.*" +python-versions = ">=3.5" [package.dependencies] pathspec = ">=0.5.3" @@ -1828,7 +1855,7 @@ pyyaml = "*" [[package]] name = "zipp" -version = "3.5.0" +version = "3.6.0" description = "Backport of pathlib-compatible object wrapper for zip files" category = "main" optional = false @@ -1843,8 +1870,8 @@ nautobot = ["nautobot"] [metadata] lock-version = "1.1" -python-versions = "^3.6" -content-hash = "1e8dbcb3ce58a2540832bcfaf4d96503068073b2544ec9589ea2e614753dcf51" +python-versions = "^3.6.2" +content-hash = "d0a307c18da15f5cd1b31f963e8ebdd5da76a8c747318293f49186cc2a223022" [metadata.files] amqp = [ @@ -1855,25 +1882,21 @@ aniso8601 = [ {file = "aniso8601-7.0.0-py2.py3-none-any.whl", hash = "sha256:d10a4bf949f619f719b227ef5386e31f49a2b6d453004b21f02661ccc8670c7b"}, {file = "aniso8601-7.0.0.tar.gz", hash = "sha256:513d2b6637b7853806ae79ffaca6f3e8754bdd547048f5ccc1420aec4b714f1e"}, ] -appdirs = [ - {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"}, - {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"}, -] asgiref = [ {file = "asgiref-3.4.1-py3-none-any.whl", hash = "sha256:ffc141aa908e6f175673e7b1b3b7af4fdb0ecb738fc5c8b88f69f055c2415214"}, {file = "asgiref-3.4.1.tar.gz", hash = "sha256:4ef1ab46b484e3c706329cedeff284a5d40824200638503f5768edb6de7d58e9"}, ] astroid = [ - {file = "astroid-2.6.2-py3-none-any.whl", hash = "sha256:606b2911d10c3dcf35e58d2ee5c97360e8477d7b9f3efc3f24811c93e6fc2cd9"}, - {file = "astroid-2.6.2.tar.gz", hash = "sha256:38b95085e9d92e2ca06cf8b35c12a74fa81da395a6f9e65803742e6509c05892"}, + {file = "astroid-2.9.0-py3-none-any.whl", hash = "sha256:776ca0b748b4ad69c00bfe0fff38fa2d21c338e12c84aa9715ee0d473c422778"}, + {file = "astroid-2.9.0.tar.gz", hash = "sha256:5939cf55de24b92bda00345d4d0659d01b3c7dafb5055165c330bc7c568ba273"}, ] attrs = [ {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"}, {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"}, ] bandit = [ - {file = "bandit-1.7.0-py3-none-any.whl", hash = "sha256:216be4d044209fa06cf2a3e51b319769a51be8318140659719aa7a115c35ed07"}, - {file = "bandit-1.7.0.tar.gz", hash = "sha256:8a4c7415254d75df8ff3c3b15cfe9042ecee628a1e40b44c15a98890fbfc2608"}, + {file = "bandit-1.7.1-py3-none-any.whl", hash = "sha256:f5acd838e59c038a159b5c621cf0f8270b279e884eadd7b782d7491c02add0d4"}, + {file = "bandit-1.7.1.tar.gz", hash = "sha256:a81b00b5436e6880fa8ad6799bc830e02032047713cbb143a12939ac67eb756c"}, ] bcrypt = [ {file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"}, @@ -1889,7 +1912,8 @@ billiard = [ {file = "billiard-3.6.4.0.tar.gz", hash = "sha256:299de5a8da28a783d51b197d496bef4f1595dd023a93a4f59dde1886ae905547"}, ] black = [ - {file = "black-20.8b1.tar.gz", hash = "sha256:1c02557aa099101b9d21496f8a914e9ed2222ef70336404eeeac8edba836fbea"}, + {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, + {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, ] cached-property = [ {file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"}, @@ -1900,66 +1924,72 @@ celery = [ {file = "celery-5.1.2.tar.gz", hash = "sha256:8d9a3de9162965e97f8e8cc584c67aad83b3f7a267584fa47701ed11c3e0d4b0"}, ] certifi = [ - {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"}, - {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"}, + {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, + {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, ] cffi = [ - {file = "cffi-1.14.6-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:22b9c3c320171c108e903d61a3723b51e37aaa8c81255b5e7ce102775bd01e2c"}, - {file = "cffi-1.14.6-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:f0c5d1acbfca6ebdd6b1e3eded8d261affb6ddcf2186205518f1428b8569bb99"}, - {file = "cffi-1.14.6-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:99f27fefe34c37ba9875f224a8f36e31d744d8083e00f520f133cab79ad5e819"}, - {file = "cffi-1.14.6-cp27-cp27m-win32.whl", hash = "sha256:55af55e32ae468e9946f741a5d51f9896da6b9bf0bbdd326843fec05c730eb20"}, - {file = "cffi-1.14.6-cp27-cp27m-win_amd64.whl", hash = "sha256:7bcac9a2b4fdbed2c16fa5681356d7121ecabf041f18d97ed5b8e0dd38a80224"}, - {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ed38b924ce794e505647f7c331b22a693bee1538fdf46b0222c4717b42f744e7"}, - {file = "cffi-1.14.6-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:e22dcb48709fc51a7b58a927391b23ab37eb3737a98ac4338e2448bef8559b33"}, - {file = "cffi-1.14.6-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:aedb15f0a5a5949ecb129a82b72b19df97bbbca024081ed2ef88bd5c0a610534"}, - {file = "cffi-1.14.6-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:48916e459c54c4a70e52745639f1db524542140433599e13911b2f329834276a"}, - {file = "cffi-1.14.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f627688813d0a4140153ff532537fbe4afea5a3dffce1f9deb7f91f848a832b5"}, - {file = "cffi-1.14.6-cp35-cp35m-win32.whl", hash = "sha256:f0010c6f9d1a4011e429109fda55a225921e3206e7f62a0c22a35344bfd13cca"}, - {file = "cffi-1.14.6-cp35-cp35m-win_amd64.whl", hash = "sha256:57e555a9feb4a8460415f1aac331a2dc833b1115284f7ded7278b54afc5bd218"}, - {file = "cffi-1.14.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e8c6a99be100371dbb046880e7a282152aa5d6127ae01783e37662ef73850d8f"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:19ca0dbdeda3b2615421d54bef8985f72af6e0c47082a8d26122adac81a95872"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:d950695ae4381ecd856bcaf2b1e866720e4ab9a1498cba61c602e56630ca7195"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9dc245e3ac69c92ee4c167fbdd7428ec1956d4e754223124991ef29eb57a09d"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8661b2ce9694ca01c529bfa204dbb144b275a31685a075ce123f12331be790b"}, - {file = "cffi-1.14.6-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b315d709717a99f4b27b59b021e6207c64620790ca3e0bde636a6c7f14618abb"}, - {file = "cffi-1.14.6-cp36-cp36m-win32.whl", hash = "sha256:80b06212075346b5546b0417b9f2bf467fea3bfe7352f781ffc05a8ab24ba14a"}, - {file = "cffi-1.14.6-cp36-cp36m-win_amd64.whl", hash = "sha256:a9da7010cec5a12193d1af9872a00888f396aba3dc79186604a09ea3ee7c029e"}, - {file = "cffi-1.14.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:4373612d59c404baeb7cbd788a18b2b2a8331abcc84c3ba40051fcd18b17a4d5"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:f10afb1004f102c7868ebfe91c28f4a712227fe4cb24974350ace1f90e1febbf"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:fd4305f86f53dfd8cd3522269ed7fc34856a8ee3709a5e28b2836b2db9d4cd69"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d6169cb3c6c2ad50db5b868db6491a790300ade1ed5d1da29289d73bbe40b56"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5d4b68e216fc65e9fe4f524c177b54964af043dde734807586cf5435af84045c"}, - {file = "cffi-1.14.6-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:33791e8a2dc2953f28b8d8d300dde42dd929ac28f974c4b4c6272cb2955cb762"}, - {file = "cffi-1.14.6-cp37-cp37m-win32.whl", hash = "sha256:0c0591bee64e438883b0c92a7bed78f6290d40bf02e54c5bf0978eaf36061771"}, - {file = "cffi-1.14.6-cp37-cp37m-win_amd64.whl", hash = "sha256:8eb687582ed7cd8c4bdbff3df6c0da443eb89c3c72e6e5dcdd9c81729712791a"}, - {file = "cffi-1.14.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:ba6f2b3f452e150945d58f4badd92310449876c4c954836cfb1803bdd7b422f0"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:64fda793737bc4037521d4899be780534b9aea552eb673b9833b01f945904c2e"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:9f3e33c28cd39d1b655ed1ba7247133b6f7fc16fa16887b120c0c670e35ce346"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bb2549b72708c833f5abe62b756176022a7b9a7f689b571e74c8478ead51dc"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:eb687a11f0a7a1839719edd80f41e459cc5366857ecbed383ff376c4e3cc6afd"}, - {file = "cffi-1.14.6-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d2ad4d668a5c0645d281dcd17aff2be3212bc109b33814bbb15c4939f44181cc"}, - {file = "cffi-1.14.6-cp38-cp38-win32.whl", hash = "sha256:487d63e1454627c8e47dd230025780e91869cfba4c753a74fda196a1f6ad6548"}, - {file = "cffi-1.14.6-cp38-cp38-win_amd64.whl", hash = "sha256:c33d18eb6e6bc36f09d793c0dc58b0211fccc6ae5149b808da4a62660678b156"}, - {file = "cffi-1.14.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:06c54a68935738d206570b20da5ef2b6b6d92b38ef3ec45c5422c0ebaf338d4d"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:f174135f5609428cc6e1b9090f9268f5c8935fddb1b25ccb8255a2d50de6789e"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f3ebe6e73c319340830a9b2825d32eb6d8475c1dac020b4f0aa774ee3b898d1c"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c8d896becff2fa653dc4438b54a5a25a971d1f4110b32bd3068db3722c80202"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4922cd707b25e623b902c86188aca466d3620892db76c0bdd7b99a3d5e61d35f"}, - {file = "cffi-1.14.6-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c9e005e9bd57bc987764c32a1bee4364c44fdc11a3cc20a40b93b444984f2b87"}, - {file = "cffi-1.14.6-cp39-cp39-win32.whl", hash = "sha256:eb9e2a346c5238a30a746893f23a9535e700f8192a68c07c0258e7ece6ff3728"}, - {file = "cffi-1.14.6-cp39-cp39-win_amd64.whl", hash = "sha256:818014c754cd3dba7229c0f5884396264d51ffb87ec86e927ef0be140bfdb0d2"}, - {file = "cffi-1.14.6.tar.gz", hash = "sha256:c9a875ce9d7fe32887784274dd533c57909b7b1dcadcc128a2ac21331a9765dd"}, + {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, + {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, + {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, + {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, + {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, + {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, + {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, + {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, + {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, + {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, + {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, + {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, + {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, + {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, + {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, + {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, + {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, + {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, + {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, + {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, + {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, + {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, + {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, + {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, + {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, + {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, ] charset-normalizer = [ - {file = "charset-normalizer-2.0.2.tar.gz", hash = "sha256:951567c2f7433a70ab63f1be67e5ee05d3925d9423306ecb71a3b272757bcc95"}, - {file = "charset_normalizer-2.0.2-py3-none-any.whl", hash = "sha256:3c502a35807c9df35697b0f44b1d65008f83071ff29c69677c7c22573bc5a45a"}, + {file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"}, + {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"}, ] click = [ {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"}, {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"}, ] click-didyoumean = [ - {file = "click-didyoumean-0.0.3.tar.gz", hash = "sha256:112229485c9704ff51362fe34b2d4f0b12fc71cc20f6d2b3afabed4b8bfa6aeb"}, + {file = "click-didyoumean-0.3.0.tar.gz", hash = "sha256:f184f0d851d96b6d29297354ed981b7dd71df7ff500d82fa6d11f0856bee8035"}, + {file = "click_didyoumean-0.3.0-py3-none-any.whl", hash = "sha256:a0713dc7a1de3f06bc0df5a9567ad19ead2d3d5689b434768a6145bff77c0667"}, ] click-plugins = [ {file = "click-plugins-1.1.1.tar.gz", hash = "sha256:46ab999744a9d831159c3411bb0c79346d94a444df9a3a3742e9ed63645f264b"}, @@ -1982,72 +2012,75 @@ coreschema = [ {file = "coreschema-0.0.4.tar.gz", hash = "sha256:9503506007d482ab0867ba14724b93c18a33b22b6d19fb419ef2d239dd4a1607"}, ] coverage = [ - {file = "coverage-5.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:b6d534e4b2ab35c9f93f46229363e17f63c53ad01330df9f2d6bd1187e5eaacf"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b7895207b4c843c76a25ab8c1e866261bcfe27bfaa20c192de5190121770672b"}, - {file = "coverage-5.5-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:c2723d347ab06e7ddad1a58b2a821218239249a9e4365eaff6649d31180c1669"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:900fbf7759501bc7807fd6638c947d7a831fc9fdf742dc10f02956ff7220fa90"}, - {file = "coverage-5.5-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:004d1880bed2d97151facef49f08e255a20ceb6f9432df75f4eef018fdd5a78c"}, - {file = "coverage-5.5-cp27-cp27m-win32.whl", hash = "sha256:06191eb60f8d8a5bc046f3799f8a07a2d7aefb9504b0209aff0b47298333302a"}, - {file = "coverage-5.5-cp27-cp27m-win_amd64.whl", hash = "sha256:7501140f755b725495941b43347ba8a2777407fc7f250d4f5a7d2a1050ba8e82"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:372da284cfd642d8e08ef606917846fa2ee350f64994bebfbd3afb0040436905"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:8963a499849a1fc54b35b1c9f162f4108017b2e6db2c46c1bed93a72262ed083"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:869a64f53488f40fa5b5b9dcb9e9b2962a66a87dab37790f3fcfb5144b996ef5"}, - {file = "coverage-5.5-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4a7697d8cb0f27399b0e393c0b90f0f1e40c82023ea4d45d22bce7032a5d7b81"}, - {file = "coverage-5.5-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:8d0a0725ad7c1a0bcd8d1b437e191107d457e2ec1084b9f190630a4fb1af78e6"}, - {file = "coverage-5.5-cp310-cp310-manylinux1_x86_64.whl", hash = "sha256:51cb9476a3987c8967ebab3f0fe144819781fca264f57f89760037a2ea191cb0"}, - {file = "coverage-5.5-cp310-cp310-win_amd64.whl", hash = "sha256:c0891a6a97b09c1f3e073a890514d5012eb256845c451bd48f7968ef939bf4ae"}, - {file = "coverage-5.5-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:3487286bc29a5aa4b93a072e9592f22254291ce96a9fbc5251f566b6b7343cdb"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:deee1077aae10d8fa88cb02c845cfba9b62c55e1183f52f6ae6a2df6a2187160"}, - {file = "coverage-5.5-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:f11642dddbb0253cc8853254301b51390ba0081750a8ac03f20ea8103f0c56b6"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:6c90e11318f0d3c436a42409f2749ee1a115cd8b067d7f14c148f1ce5574d701"}, - {file = "coverage-5.5-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:30c77c1dc9f253283e34c27935fded5015f7d1abe83bc7821680ac444eaf7793"}, - {file = "coverage-5.5-cp35-cp35m-win32.whl", hash = "sha256:9a1ef3b66e38ef8618ce5fdc7bea3d9f45f3624e2a66295eea5e57966c85909e"}, - {file = "coverage-5.5-cp35-cp35m-win_amd64.whl", hash = "sha256:972c85d205b51e30e59525694670de6a8a89691186012535f9d7dbaa230e42c3"}, - {file = "coverage-5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:af0e781009aaf59e25c5a678122391cb0f345ac0ec272c7961dc5455e1c40066"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:74d881fc777ebb11c63736622b60cb9e4aee5cace591ce274fb69e582a12a61a"}, - {file = "coverage-5.5-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:92b017ce34b68a7d67bd6d117e6d443a9bf63a2ecf8567bb3d8c6c7bc5014465"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:d636598c8305e1f90b439dbf4f66437de4a5e3c31fdf47ad29542478c8508bbb"}, - {file = "coverage-5.5-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:41179b8a845742d1eb60449bdb2992196e211341818565abded11cfa90efb821"}, - {file = "coverage-5.5-cp36-cp36m-win32.whl", hash = "sha256:040af6c32813fa3eae5305d53f18875bedd079960822ef8ec067a66dd8afcd45"}, - {file = "coverage-5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:5fec2d43a2cc6965edc0bb9e83e1e4b557f76f843a77a2496cbe719583ce8184"}, - {file = "coverage-5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:18ba8bbede96a2c3dde7b868de9dcbd55670690af0988713f0603f037848418a"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:2910f4d36a6a9b4214bb7038d537f015346f413a975d57ca6b43bf23d6563b53"}, - {file = "coverage-5.5-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:f0b278ce10936db1a37e6954e15a3730bea96a0997c26d7fee88e6c396c2086d"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:796c9c3c79747146ebd278dbe1e5c5c05dd6b10cc3bcb8389dfdf844f3ead638"}, - {file = "coverage-5.5-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:53194af30d5bad77fcba80e23a1441c71abfb3e01192034f8246e0d8f99528f3"}, - {file = "coverage-5.5-cp37-cp37m-win32.whl", hash = "sha256:184a47bbe0aa6400ed2d41d8e9ed868b8205046518c52464fde713ea06e3a74a"}, - {file = "coverage-5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:2949cad1c5208b8298d5686d5a85b66aae46d73eec2c3e08c817dd3513e5848a"}, - {file = "coverage-5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:217658ec7187497e3f3ebd901afdca1af062b42cfe3e0dafea4cced3983739f6"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1aa846f56c3d49205c952d8318e76ccc2ae23303351d9270ab220004c580cfe2"}, - {file = "coverage-5.5-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:24d4a7de75446be83244eabbff746d66b9240ae020ced65d060815fac3423759"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:d1f8bf7b90ba55699b3a5e44930e93ff0189aa27186e96071fac7dd0d06a1873"}, - {file = "coverage-5.5-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:970284a88b99673ccb2e4e334cfb38a10aab7cd44f7457564d11898a74b62d0a"}, - {file = "coverage-5.5-cp38-cp38-win32.whl", hash = "sha256:01d84219b5cdbfc8122223b39a954820929497a1cb1422824bb86b07b74594b6"}, - {file = "coverage-5.5-cp38-cp38-win_amd64.whl", hash = "sha256:2e0d881ad471768bf6e6c2bf905d183543f10098e3b3640fc029509530091502"}, - {file = "coverage-5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d1f9ce122f83b2305592c11d64f181b87153fc2c2bbd3bb4a3dde8303cfb1a6b"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_i686.whl", hash = "sha256:13c4ee887eca0f4c5a247b75398d4114c37882658300e153113dafb1d76de529"}, - {file = "coverage-5.5-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:52596d3d0e8bdf3af43db3e9ba8dcdaac724ba7b5ca3f6358529d56f7a166f8b"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2cafbbb3af0733db200c9b5f798d18953b1a304d3f86a938367de1567f4b5bff"}, - {file = "coverage-5.5-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:44d654437b8ddd9eee7d1eaee28b7219bec228520ff809af170488fd2fed3e2b"}, - {file = "coverage-5.5-cp39-cp39-win32.whl", hash = "sha256:d314ed732c25d29775e84a960c3c60808b682c08d86602ec2c3008e1202e3bb6"}, - {file = "coverage-5.5-cp39-cp39-win_amd64.whl", hash = "sha256:13034c4409db851670bc9acd836243aeee299949bd5673e11844befcb0149f03"}, - {file = "coverage-5.5-pp36-none-any.whl", hash = "sha256:f030f8873312a16414c0d8e1a1ddff2d3235655a2174e3648b4fa66b3f2f1079"}, - {file = "coverage-5.5-pp37-none-any.whl", hash = "sha256:2a3859cb82dcbda1cfd3e6f71c27081d18aa251d20a17d87d26d4cd216fb0af4"}, - {file = "coverage-5.5.tar.gz", hash = "sha256:ebe78fe9a0e874362175b02371bdfbee64d8edc42a044253ddf4ee7d3c15212c"}, + {file = "coverage-6.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:6dbc1536e105adda7a6312c778f15aaabe583b0e9a0b0a324990334fd458c94b"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:174cf9b4bef0db2e8244f82059a5a72bd47e1d40e71c68ab055425172b16b7d0"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:92b8c845527eae547a2a6617d336adc56394050c3ed8a6918683646328fbb6da"}, + {file = "coverage-6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c7912d1526299cb04c88288e148c6c87c0df600eca76efd99d84396cfe00ef1d"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d5d2033d5db1d58ae2d62f095e1aefb6988af65b4b12cb8987af409587cc0739"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:3feac4084291642165c3a0d9eaebedf19ffa505016c4d3db15bfe235718d4971"}, + {file = "coverage-6.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:276651978c94a8c5672ea60a2656e95a3cce2a3f31e9fb2d5ebd4c215d095840"}, + {file = "coverage-6.2-cp310-cp310-win32.whl", hash = "sha256:f506af4f27def639ba45789fa6fde45f9a217da0be05f8910458e4557eed020c"}, + {file = "coverage-6.2-cp310-cp310-win_amd64.whl", hash = "sha256:3f7c17209eef285c86f819ff04a6d4cbee9b33ef05cbcaae4c0b4e8e06b3ec8f"}, + {file = "coverage-6.2-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:13362889b2d46e8d9f97c421539c97c963e34031ab0cb89e8ca83a10cc71ac76"}, + {file = "coverage-6.2-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:22e60a3ca5acba37d1d4a2ee66e051f5b0e1b9ac950b5b0cf4aa5366eda41d47"}, + {file = "coverage-6.2-cp311-cp311-win_amd64.whl", hash = "sha256:b637c57fdb8be84e91fac60d9325a66a5981f8086c954ea2772efe28425eaf64"}, + {file = "coverage-6.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f467bbb837691ab5a8ca359199d3429a11a01e6dfb3d9dcc676dc035ca93c0a9"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2641f803ee9f95b1f387f3e8f3bf28d83d9b69a39e9911e5bfee832bea75240d"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:1219d760ccfafc03c0822ae2e06e3b1248a8e6d1a70928966bafc6838d3c9e48"}, + {file = "coverage-6.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9a2b5b52be0a8626fcbffd7e689781bf8c2ac01613e77feda93d96184949a98e"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8e2c35a4c1f269704e90888e56f794e2d9c0262fb0c1b1c8c4ee44d9b9e77b5d"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:5d6b09c972ce9200264c35a1d53d43ca55ef61836d9ec60f0d44273a31aa9f17"}, + {file = "coverage-6.2-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:e3db840a4dee542e37e09f30859f1612da90e1c5239a6a2498c473183a50e781"}, + {file = "coverage-6.2-cp36-cp36m-win32.whl", hash = "sha256:4e547122ca2d244f7c090fe3f4b5a5861255ff66b7ab6d98f44a0222aaf8671a"}, + {file = "coverage-6.2-cp36-cp36m-win_amd64.whl", hash = "sha256:01774a2c2c729619760320270e42cd9e797427ecfddd32c2a7b639cdc481f3c0"}, + {file = "coverage-6.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fb8b8ee99b3fffe4fd86f4c81b35a6bf7e4462cba019997af2fe679365db0c49"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:619346d57c7126ae49ac95b11b0dc8e36c1dd49d148477461bb66c8cf13bb521"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a7726f74ff63f41e95ed3a89fef002916c828bb5fcae83b505b49d81a066884"}, + {file = "coverage-6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cfd9386c1d6f13b37e05a91a8583e802f8059bebfccde61a418c5808dea6bbfa"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:17e6c11038d4ed6e8af1407d9e89a2904d573be29d51515f14262d7f10ef0a64"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c254b03032d5a06de049ce8bca8338a5185f07fb76600afff3c161e053d88617"}, + {file = "coverage-6.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:dca38a21e4423f3edb821292e97cec7ad38086f84313462098568baedf4331f8"}, + {file = "coverage-6.2-cp37-cp37m-win32.whl", hash = "sha256:600617008aa82032ddeace2535626d1bc212dfff32b43989539deda63b3f36e4"}, + {file = "coverage-6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:bf154ba7ee2fd613eb541c2bc03d3d9ac667080a737449d1a3fb342740eb1a74"}, + {file = "coverage-6.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f9afb5b746781fc2abce26193d1c817b7eb0e11459510fba65d2bd77fe161d9e"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:edcada2e24ed68f019175c2b2af2a8b481d3d084798b8c20d15d34f5c733fa58"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a9c8c4283e17690ff1a7427123ffb428ad6a52ed720d550e299e8291e33184dc"}, + {file = "coverage-6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f614fc9956d76d8a88a88bb41ddc12709caa755666f580af3a688899721efecd"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9365ed5cce5d0cf2c10afc6add145c5037d3148585b8ae0e77cc1efdd6aa2953"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8bdfe9ff3a4ea37d17f172ac0dff1e1c383aec17a636b9b35906babc9f0f5475"}, + {file = "coverage-6.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:63c424e6f5b4ab1cf1e23a43b12f542b0ec2e54f99ec9f11b75382152981df57"}, + {file = "coverage-6.2-cp38-cp38-win32.whl", hash = "sha256:49dbff64961bc9bdd2289a2bda6a3a5a331964ba5497f694e2cbd540d656dc1c"}, + {file = "coverage-6.2-cp38-cp38-win_amd64.whl", hash = "sha256:9a29311bd6429be317c1f3fe4bc06c4c5ee45e2fa61b2a19d4d1d6111cb94af2"}, + {file = "coverage-6.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:03b20e52b7d31be571c9c06b74746746d4eb82fc260e594dc662ed48145e9efd"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:215f8afcc02a24c2d9a10d3790b21054b58d71f4b3c6f055d4bb1b15cecce685"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:a4bdeb0a52d1d04123b41d90a4390b096f3ef38eee35e11f0b22c2d031222c6c"}, + {file = "coverage-6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c332d8f8d448ded473b97fefe4a0983265af21917d8b0cdcb8bb06b2afe632c3"}, + {file = "coverage-6.2-cp39-cp39-win32.whl", hash = "sha256:6e1394d24d5938e561fbeaa0cd3d356207579c28bd1792f25a068743f2d5b282"}, + {file = "coverage-6.2-cp39-cp39-win_amd64.whl", hash = "sha256:86f2e78b1eff847609b1ca8050c9e1fa3bd44ce755b2ec30e70f2d3ba3844644"}, + {file = "coverage-6.2-pp36.pp37.pp38-none-any.whl", hash = "sha256:5829192582c0ec8ca4a2532407bc14c2f338d9878a10442f5d03804a95fac9de"}, + {file = "coverage-6.2.tar.gz", hash = "sha256:e2cad8093172b7d1595b4ad66f24270808658e11acf43a8f95b41276162eb5b8"}, ] cryptography = [ - {file = "cryptography-3.4.7-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:3d8427734c781ea5f1b41d6589c293089704d4759e34597dce91014ac125aad1"}, - {file = "cryptography-3.4.7-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:8e56e16617872b0957d1c9742a3f94b43533447fd78321514abbe7db216aa250"}, - {file = "cryptography-3.4.7-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:37340614f8a5d2fb9aeea67fd159bfe4f5f4ed535b1090ce8ec428b2f15a11f2"}, - {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:240f5c21aef0b73f40bb9f78d2caff73186700bf1bc6b94285699aff98cc16c6"}, - {file = "cryptography-3.4.7-cp36-abi3-manylinux2014_x86_64.whl", hash = "sha256:1e056c28420c072c5e3cb36e2b23ee55e260cb04eee08f702e0edfec3fb51959"}, - {file = "cryptography-3.4.7-cp36-abi3-win32.whl", hash = "sha256:0f1212a66329c80d68aeeb39b8a16d54ef57071bf22ff4e521657b27372e327d"}, - {file = "cryptography-3.4.7-cp36-abi3-win_amd64.whl", hash = "sha256:de4e5f7f68220d92b7637fc99847475b59154b7a1b3868fb7385337af54ac9ca"}, - {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:26965837447f9c82f1855e0bc8bc4fb910240b6e0d16a664bb722df3b5b06873"}, - {file = "cryptography-3.4.7-pp36-pypy36_pp73-manylinux2014_x86_64.whl", hash = "sha256:eb8cc2afe8b05acbd84a43905832ec78e7b3873fb124ca190f574dca7389a87d"}, - {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:7ec5d3b029f5fa2b179325908b9cd93db28ab7b85bb6c1db56b10e0b54235177"}, - {file = "cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl", hash = "sha256:ee77aa129f481be46f8d92a1a7db57269a2f23052d5f2433b4621bb457081cc9"}, - {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"}, + {file = "cryptography-36.0.0-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:97199a13b772e74cdcdb03760c32109c808aff7cd49c29e9cf4b7754bb725d1d"}, + {file = "cryptography-36.0.0-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:494106e9cd945c2cadfce5374fa44c94cfadf01d4566a3b13bb487d2e6c7959e"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:6fbbbb8aab4053fa018984bb0e95a16faeb051dd8cca15add2a27e267ba02b58"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:684993ff6f67000a56454b41bdc7e015429732d65a52d06385b6e9de6181c71e"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c702855cd3174666ef0d2d13dcc879090aa9c6c38f5578896407a7028f75b9f"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d91bc9f535599bed58f6d2e21a2724cb0c3895bf41c6403fe881391d29096f1d"}, + {file = "cryptography-36.0.0-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:b17d83b3d1610e571fedac21b2eb36b816654d6f7496004d6a0d32f99d1d8120"}, + {file = "cryptography-36.0.0-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:8982c19bb90a4fa2aad3d635c6d71814e38b643649b4000a8419f8691f20ac44"}, + {file = "cryptography-36.0.0-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:24469d9d33217ffd0ce4582dfcf2a76671af115663a95328f63c99ec7ece61a4"}, + {file = "cryptography-36.0.0-cp36-abi3-win32.whl", hash = "sha256:f6a5a85beb33e57998dc605b9dbe7deaa806385fdf5c4810fb849fcd04640c81"}, + {file = "cryptography-36.0.0-cp36-abi3-win_amd64.whl", hash = "sha256:2deab5ec05d83ddcf9b0916319674d3dae88b0e7ee18f8962642d3cde0496568"}, + {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2049f8b87f449fc6190350de443ee0c1dd631f2ce4fa99efad2984de81031681"}, + {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a776bae1629c8d7198396fd93ec0265f8dd2341c553dc32b976168aaf0e6a636"}, + {file = "cryptography-36.0.0-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:aa94d617a4cd4cdf4af9b5af65100c036bce22280ebb15d8b5262e8273ebc6ba"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5c49c9e8fb26a567a2b3fa0343c89f5d325447956cc2fc7231c943b29a973712"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ef216d13ac8d24d9cd851776662f75f8d29c9f2d05cdcc2d34a18d32463a9b0b"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:231c4a69b11f6af79c1495a0e5a85909686ea8db946935224b7825cfb53827ed"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:f92556f94e476c1b616e6daec5f7ddded2c082efa7cee7f31c7aeda615906ed8"}, + {file = "cryptography-36.0.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:d73e3a96c38173e0aa5646c31bf8473bc3564837977dd480f5cbeacf1d7ef3a3"}, + {file = "cryptography-36.0.0.tar.gz", hash = "sha256:52f769ecb4ef39865719aedc67b4b7eae167bafa48dbc2a26dd36fa56460507f"}, ] dataclasses = [ {file = "dataclasses-0.8-py3-none-any.whl", hash = "sha256:0201d89fa866f68c8ebd9d08ee6ff50c0b255f8ec63a71c16fda7af82bb887bf"}, @@ -2057,13 +2090,17 @@ defusedxml = [ {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"}, {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"}, ] +deprecated = [ + {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, + {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, +] django = [ - {file = "Django-3.1.13-py3-none-any.whl", hash = "sha256:a6e0d1ff11095b7394c079ade7094c73b2dc3df4a7a373c9b58ed73b77a97feb"}, - {file = "Django-3.1.13.tar.gz", hash = "sha256:9f8be75646f62204320b195062b1d696ba28aa3d45ee72fb7c888ffaebc5bdb2"}, + {file = "Django-3.1.14-py3-none-any.whl", hash = "sha256:0fabc786489af16ad87a8c170ba9d42bfd23f7b699bd5ef05675864e8d012859"}, + {file = "Django-3.1.14.tar.gz", hash = "sha256:72a4a5a136a214c39cf016ccdd6b69e2aa08c7479c66d93f3a9b5e4bb9d8a347"}, ] django-appconf = [ - {file = "django-appconf-1.0.4.tar.gz", hash = "sha256:be58deb54a43d77d2e1621fe59f787681376d3cd0b8bd8e4758ef6c3a6453380"}, - {file = "django_appconf-1.0.4-py2.py3-none-any.whl", hash = "sha256:1b1d0e1069c843ebe8ae5aa48ec52403b1440402b320c3e3a206a0907e97bb06"}, + {file = "django-appconf-1.0.5.tar.gz", hash = "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4"}, + {file = "django_appconf-1.0.5-py3-none-any.whl", hash = "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d"}, ] django-cacheops = [ {file = "django-cacheops-5.1.tar.gz", hash = "sha256:d5851cd7bf3087384a1fcecfa8dddb8f55030eedfd6fdf127225b75bca0f99dd"}, @@ -2077,9 +2114,12 @@ django-cryptography = [ {file = "django-cryptography-1.0.tar.gz", hash = "sha256:13de5cf8f1250744c104b9e24774d03aa6d8488959dd40cdc016934043652445"}, {file = "django_cryptography-1.0-py3-none-any.whl", hash = "sha256:0a99980b1cee7cc5e52f9b20b322620fea7cc124d770273e7bd285b20fd9d222"}, ] +django-db-file-storage = [ + {file = "django-db-file-storage-0.5.5.tar.gz", hash = "sha256:5d5da694b78ab202accab4508b958e0e37b3d146310e76f6f6125e1bdeaaad14"}, +] django-debug-toolbar = [ - {file = "django-debug-toolbar-3.2.1.tar.gz", hash = "sha256:a5ff2a54f24bf88286f9872836081078f4baa843dc3735ee88524e89f8821e33"}, - {file = "django_debug_toolbar-3.2.1-py3-none-any.whl", hash = "sha256:e759e63e3fe2d3110e0e519639c166816368701eab4a47fed75d7de7018467b9"}, + {file = "django-debug-toolbar-3.2.2.tar.gz", hash = "sha256:8c5b13795d4040008ee69ba82dcdd259c49db346cf7d0de6e561a49d191f0860"}, + {file = "django_debug_toolbar-3.2.2-py3-none-any.whl", hash = "sha256:d7bab7573fab35b0fd029163371b7182f5826c13da69734beb675c761d06a4d3"}, ] django-filter = [ {file = "django-filter-2.4.0.tar.gz", hash = "sha256:84e9d5bb93f237e451db814ed422a3a625751cbc9968b484ecc74964a8696b06"}, @@ -2090,8 +2130,8 @@ django-health-check = [ {file = "django_health_check-3.16.4-py2.py3-none-any.whl", hash = "sha256:86a8869d67e72394a1dd73e37819a7d2cfd915588b96927fda611d7451fd4735"}, ] django-jinja = [ - {file = "django-jinja-2.9.0.tar.gz", hash = "sha256:69433ea312264a541acf1e3e9748e44783ad33381e48e6a7230762e02f005276"}, - {file = "django_jinja-2.9.0-py3-none-any.whl", hash = "sha256:49611b738bd82c2896e2d3f4662ce6b0bb6a9dac142b0c35cd29603238e05164"}, + {file = "django-jinja-2.7.1.tar.gz", hash = "sha256:0d2c90ccc4763f67b07ace2b8a2f23df16d2995b4dc841597443fb4eea746505"}, + {file = "django_jinja-2.7.1-py3-none-any.whl", hash = "sha256:fa7650ca289544caf441cc922876986cc0df8f111047aaef6836a978c0c66304"}, ] django-js-asset = [ {file = "django-js-asset-1.2.2.tar.gz", hash = "sha256:c163ae80d2e0b22d8fb598047cd0dcef31f81830e127cfecae278ad574167260"}, @@ -2138,8 +2178,8 @@ drf-yasg = [ {file = "drf_yasg-1.20.0-py2.py3-none-any.whl", hash = "sha256:8b72e5b1875931a8d11af407be3a9a5ba8776541492947a0df5bafda6b7f8267"}, ] flake8 = [ - {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"}, - {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"}, + {file = "flake8-4.0.1-py2.py3-none-any.whl", hash = "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d"}, + {file = "flake8-4.0.1.tar.gz", hash = "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d"}, ] funcy = [ {file = "funcy-1.16-py2.py3-none-any.whl", hash = "sha256:1d3fc5d42cf7564a6b2be04042d0df7a50c77903cf760a34786d0c9ebd659b25"}, @@ -2148,17 +2188,21 @@ funcy = [ future = [ {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, ] +ghp-import = [ + {file = "ghp-import-2.0.2.tar.gz", hash = "sha256:947b3771f11be850c852c64b561c600fdddf794bab363060854c1ee7ad05e071"}, + {file = "ghp_import-2.0.2-py3-none-any.whl", hash = "sha256:5f8962b30b20652cdffa9c5a9812f7de6bcb56ec475acac579807719bf242c46"}, +] gitdb = [ - {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"}, - {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"}, + {file = "gitdb-4.0.9-py3-none-any.whl", hash = "sha256:8033ad4e853066ba6ca92050b9df2f89301b8fc8bf7e9324d412a63f8bf1a8fd"}, + {file = "gitdb-4.0.9.tar.gz", hash = "sha256:bac2fd45c0a1c9cf619e63a90d62bdc63892ef92387424b855792a6cabe789aa"}, ] gitpython = [ - {file = "GitPython-3.1.18-py3-none-any.whl", hash = "sha256:fce760879cd2aebd2991b3542876dc5c4a909b30c9d69dfc488e504a8db37ee8"}, - {file = "GitPython-3.1.18.tar.gz", hash = "sha256:b838a895977b45ab6f0cc926a9045c8d1c44e2b653c1fcc39fe91f42c6e8f05b"}, + {file = "GitPython-3.1.20-py3-none-any.whl", hash = "sha256:b1e1c269deab1b08ce65403cf14e10d2ef1f6c89e33ea7c5e5bb0222ea593b8a"}, + {file = "GitPython-3.1.20.tar.gz", hash = "sha256:df0e072a200703a65387b0cfdf0466e3bab729c0458cf6b7349d0e9877636519"}, ] graphene = [ - {file = "graphene-2.1.8-py2.py3-none-any.whl", hash = "sha256:09165f03e1591b76bf57b133482db9be6dac72c74b0a628d3c93182af9c5a896"}, - {file = "graphene-2.1.8.tar.gz", hash = "sha256:2cbe6d4ef15cfc7b7805e0760a0e5b80747161ce1b0f990dfdc0d2cf497c12f9"}, + {file = "graphene-2.1.9-py2.py3-none-any.whl", hash = "sha256:3d446eb1237c551052bc31155cf1a3a607053e4f58c9172b83a1b597beaa0868"}, + {file = "graphene-2.1.9.tar.gz", hash = "sha256:b9f2850e064eebfee9a3ef4a1f8aa0742848d97652173ab44c82cc8a62b9ed93"}, ] graphene-django = [ {file = "graphene-django-2.15.0.tar.gz", hash = "sha256:b78c9b05bc899016b9cc5bf13faa1f37fe1faa8c5407552c6ddd1a28f46fc31a"}, @@ -2173,16 +2217,16 @@ graphql-relay = [ {file = "graphql_relay-2.0.1-py3-none-any.whl", hash = "sha256:ac514cb86db9a43014d7e73511d521137ac12cf0101b2eaa5f0a3da2e10d913d"}, ] idna = [ - {file = "idna-3.2-py3-none-any.whl", hash = "sha256:14475042e284991034cb48e06f6851428fb14c4dc953acd9be9a5e95c7b6dd7a"}, - {file = "idna-3.2.tar.gz", hash = "sha256:467fbad99067910785144ce333826c71fb0e63a425657295239737f7ecd125f3"}, + {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, + {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, ] importlib-metadata = [ - {file = "importlib_metadata-3.4.0-py3-none-any.whl", hash = "sha256:ace61d5fc652dc280e7b6b4ff732a9c2d40db2c0f92bc6cb74e07b73d53a1771"}, - {file = "importlib_metadata-3.4.0.tar.gz", hash = "sha256:fa5daa4477a7414ae34e95942e4dd07f62adf589143c875c133c1e53c4eff38d"}, + {file = "importlib_metadata-3.10.1-py3-none-any.whl", hash = "sha256:2ec0faae539743ae6aaa84b49a169670a465f7f5d64e6add98388cc29fd1f2f6"}, + {file = "importlib_metadata-3.10.1.tar.gz", hash = "sha256:c9356b657de65c53744046fa8f7358afe0714a1af7d570c00c3835c2d724a7c1"}, ] importlib-resources = [ - {file = "importlib_resources-5.2.0-py3-none-any.whl", hash = "sha256:a0143290bef3cbc99de9e40176e4987780939a955b8632f02ce6c935f42e9bfc"}, - {file = "importlib_resources-5.2.0.tar.gz", hash = "sha256:22a2c42d8c6a1d30aa8a0e1f57293725bfd5c013d562585e46aff469e0ff78b3"}, + {file = "importlib_resources-5.4.0-py3-none-any.whl", hash = "sha256:33a95faed5fc19b4bc16b29a6eeae248a3fe69dd55d4d229d2b480e23eeaad45"}, + {file = "importlib_resources-5.4.0.tar.gz", hash = "sha256:d756e2f85dd4de2ba89be0b21dba2a3bbec2e871a42a3a16719258a11f87506b"}, ] inflection = [ {file = "inflection-0.5.1-py2.py3-none-any.whl", hash = "sha256:f38b2b640938a4f35ade69ac3d053042959b62a0f1076a5bbaa1b9526605a8a2"}, @@ -2197,20 +2241,16 @@ ipaddr = [ {file = "ipaddr-2.2.0.tar.gz", hash = "sha256:4092dfe667588d16aa12b59acb7c8a4024e5dcb23a681cd0b0b602373eca88d6"}, ] isort = [ - {file = "isort-5.8.0-py3-none-any.whl", hash = "sha256:2bb1680aad211e3c9944dbce1d4ba09a989f04e238296c87fe2139faa26d655d"}, - {file = "isort-5.8.0.tar.gz", hash = "sha256:0a943902919f65c5684ac4e0154b1ad4fac6dcaa5d9f3426b732f1c8b5419be6"}, + {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, + {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, ] itypes = [ {file = "itypes-1.2.0-py2.py3-none-any.whl", hash = "sha256:03da6872ca89d29aef62773672b2d408f490f80db48b23079a4b194c86dd04c6"}, {file = "itypes-1.2.0.tar.gz", hash = "sha256:af886f129dea4a2a1e3d36595a2d139589e4dd287f5cab0b40e799ee81570ff1"}, ] jinja2 = [ - {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"}, - {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"}, -] -joblib = [ - {file = "joblib-1.0.1-py3-none-any.whl", hash = "sha256:feeb1ec69c4d45129954f1b7034954241eedfd6ba39b5e9e4b6883be3332d5e5"}, - {file = "joblib-1.0.1.tar.gz", hash = "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7"}, + {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"}, + {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"}, ] jsonschema = [ {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"}, @@ -2244,16 +2284,9 @@ lazy-object-proxy = [ {file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"}, {file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"}, ] -livereload = [ - {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"}, -] -lunr = [ - {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"}, - {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"}, -] markdown = [ - {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"}, - {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"}, + {file = "Markdown-3.3.5-py3-none-any.whl", hash = "sha256:0d2d09f75cb8d1ffc6770c65c61770b23a61708101f47bda416a002a0edbc480"}, + {file = "Markdown-3.3.5.tar.gz", hash = "sha256:26e9546bfbcde5fcd072bd8f612c9c1b6e2677cb8aadbdf65206674f46dde069"}, ] markupsafe = [ {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"}, @@ -2295,25 +2328,29 @@ mccabe = [ {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"}, {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"}, ] +mergedeep = [ + {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"}, + {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"}, +] mkdocs = [ - {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"}, - {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"}, + {file = "mkdocs-1.2.3-py3-none-any.whl", hash = "sha256:a1fa8c2d0c1305d7fc2b9d9f607c71778572a8b110fb26642aa00296c9e6d072"}, + {file = "mkdocs-1.2.3.tar.gz", hash = "sha256:89f5a094764381cda656af4298727c9f53dc3e602983087e1fe96ea1df24f4c1"}, ] mypy-extensions = [ {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, ] nautobot = [ - {file = "nautobot-1.1.0b2-py3-none-any.whl", hash = "sha256:d977c3294e4383a12bd694de89f8023c6adf4e5c34fefdd92ebaf87ae28fb96c"}, - {file = "nautobot-1.1.0b2.tar.gz", hash = "sha256:032a8b25768c15ba1ddd11a5c4b1b8c8012d45e441f708a870f7e413116e48a4"}, + {file = "nautobot-1.1.6-py3-none-any.whl", hash = "sha256:7f8b9869d95f3e51ce4e41a7387d86834753f9a5f604d2df9fb4e81ab75e76c9"}, + {file = "nautobot-1.1.6.tar.gz", hash = "sha256:03fcea8a1fe2379dabf54e12ad953433c5f8c0b9ce6933fb361ae1fd9950641a"}, ] nautobot-capacity-metrics = [ - {file = "nautobot-capacity-metrics-1.0.1.tar.gz", hash = "sha256:f6bdde11aedc63c233ad1468b7263a9bc5694938d0f197873fa0abc6a22c2ce5"}, - {file = "nautobot_capacity_metrics-1.0.1-py3-none-any.whl", hash = "sha256:11a5d3686fafa34ddce1d643f54dbd4d205d3c76cb643617b774ff4beb0d3189"}, + {file = "nautobot-capacity-metrics-1.1.0.tar.gz", hash = "sha256:7f39fdc203dec1579902b4972193feace8140addd5360cefadb1dc4895b8a582"}, + {file = "nautobot_capacity_metrics-1.1.0-py3-none-any.whl", hash = "sha256:f5a07e6cfc0dc814109f6806a19079d8ac0ce19cc68d5d19c6433dae11370f5c"}, ] nautobot-chatops = [ - {file = "nautobot-chatops-1.3.1.tar.gz", hash = "sha256:3de491439f8e70d07753bea664ef7dea8781be196950b725b5af4861d63d5fc2"}, - {file = "nautobot_chatops-1.3.1-py3-none-any.whl", hash = "sha256:824aa1190c34f28a7e021d020d58974bc6ec9291f78fa4702f892db67c0e6ce7"}, + {file = "nautobot-chatops-1.5.1.tar.gz", hash = "sha256:cf624560c82bad73a71855f4cf4a45ad94dbd0e260f599fa55fd6474521acde8"}, + {file = "nautobot_chatops-1.5.1-py3-none-any.whl", hash = "sha256:cf50ec4b4686ab7c70f83f3179eb40fccec750b585144a01d5281fcff8394c02"}, ] netaddr = [ {file = "netaddr-0.8.0-py2.py3-none-any.whl", hash = "sha256:9666d0232c32d2656e5e5f8d735f58fd6c7457ce52fc21c98d45f2af78f990ac"}, @@ -2323,87 +2360,111 @@ netmiko = [ {file = "netmiko-3.4.0-py3-none-any.whl", hash = "sha256:b66f25717db3609878f83c85604349dd40a0ab494d8eafd817dcde8388131136"}, {file = "netmiko-3.4.0.tar.gz", hash = "sha256:acadb9dd97864ee848e2032f1f0e301c7b31e7a4153757d98f5c8ba1b9614993"}, ] -nltk = [ - {file = "nltk-3.6.2-py3-none-any.whl", hash = "sha256:240e23ab1ab159ef9940777d30c7c72d7e76d91877099218a7585370c11f6b9e"}, - {file = "nltk-3.6.2.zip", hash = "sha256:57d556abed621ab9be225cc6d2df1edce17572efb67a3d754630c9f8381503eb"}, +netutils = [ + {file = "netutils-0.2.5-py3-none-any.whl", hash = "sha256:6715b0e1175051f5db7685bf057ede68c7dafc56cf705a1ca3896e24980b22ce"}, + {file = "netutils-0.2.5.tar.gz", hash = "sha256:bdfd8e454c4aeacf59c1537bfd434e1d944ce69032fed533f0171c2532c91751"}, ] ntc-templates = [ - {file = "ntc_templates-2.1.0-py3-none-any.whl", hash = "sha256:b42c0d32cf33ccc2ba89b2ec4268ad43d3d872ff569ecefe727b6649adacd175"}, - {file = "ntc_templates-2.1.0.tar.gz", hash = "sha256:6ce17e48d951d531afa83ad3b68fda822a3d8937e8c955387053c501edfec41f"}, + {file = "ntc_templates-3.0.0-py3-none-any.whl", hash = "sha256:a652ff1659803d98fc088fc1b0838a74533316b0ad627c1220c0424dd82d12b3"}, + {file = "ntc_templates-3.0.0.tar.gz", hash = "sha256:866c1b57af48625e1a38cc7226067e400d8a992c4d5594b914cafd7b13fd4a50"}, ] oauthlib = [ {file = "oauthlib-3.1.1-py2.py3-none-any.whl", hash = "sha256:42bf6354c2ed8c6acb54d971fce6f88193d97297e18602a3a886603f9d7730cc"}, {file = "oauthlib-3.1.1.tar.gz", hash = "sha256:8f0215fcc533dd8dd1bee6f4c412d4f0cd7297307d43ac61666389e3bc3198a3"}, ] packaging = [ - {file = "packaging-21.0-py3-none-any.whl", hash = "sha256:c86254f9220d55e31cc94d69bade760f0847da8000def4dfe1c6b872fd14ff14"}, - {file = "packaging-21.0.tar.gz", hash = "sha256:7dc96269f53a4ccec5c0670940a4281106dd0bb343f47b7471f779df49c2fbe7"}, + {file = "packaging-21.3-py3-none-any.whl", hash = "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522"}, + {file = "packaging-21.3.tar.gz", hash = "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb"}, ] pan-os-python = [ - {file = "pan-os-python-1.3.0.tar.gz", hash = "sha256:f599df2a066480ff1ccb535394f249dd5ac325e4b743b14428c65df4c76552d3"}, - {file = "pan_os_python-1.3.0-py2.py3-none-any.whl", hash = "sha256:e90ca5c66e0514b7745269cd6ff76f404dd0751bc6baefa8d950a8938075143b"}, + {file = "pan-os-python-1.6.0.tar.gz", hash = "sha256:c9490c0e78adac9a71adcd4203cf1634186e9cb553c8fe470706b16e00b48928"}, + {file = "pan_os_python-1.6.0-py2.py3-none-any.whl", hash = "sha256:a60af35930c9b325fb48b980921534e2b3ac65d1de452a738a6267fe1f7f572e"}, ] pan-python = [ {file = "pan-python-0.16.0.tar.gz", hash = "sha256:538bd6cbfb64478bce4cea9ba5c4b2d3edfd533c32d0fe740106ec145bc5a35c"}, {file = "pan_python-0.16.0-py2.py3-none-any.whl", hash = "sha256:8955111ca1ad3cc7d82250a83505f6d094dcceb880f996d2005541c9d7be0942"}, ] paramiko = [ - {file = "paramiko-2.7.2-py2.py3-none-any.whl", hash = "sha256:4f3e316fef2ac628b05097a637af35685183111d4bc1b5979bd397c2ab7b5898"}, - {file = "paramiko-2.7.2.tar.gz", hash = "sha256:7f36f4ba2c0d81d219f4595e35f70d56cc94f9ac40a6acdf51d6ca210ce65035"}, + {file = "paramiko-2.8.1-py2.py3-none-any.whl", hash = "sha256:7b5910f5815a00405af55da7abcc8a9e0d9657f57fcdd9a89894fdbba1c6b8a8"}, + {file = "paramiko-2.8.1.tar.gz", hash = "sha256:85b1245054e5d7592b9088cc6d08da22445417912d3a3e48138675c7a8616438"}, ] pathspec = [ - {file = "pathspec-0.8.1-py2.py3-none-any.whl", hash = "sha256:aa0cb481c4041bf52ffa7b0d8fa6cd3e88a2ca4879c533c9153882ee2556790d"}, - {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"}, + {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, + {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, ] pbr = [ - {file = "pbr-5.6.0-py2.py3-none-any.whl", hash = "sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"}, - {file = "pbr-5.6.0.tar.gz", hash = "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"}, + {file = "pbr-5.8.0-py2.py3-none-any.whl", hash = "sha256:176e8560eaf61e127817ef93d8a844803abb27a4d4637f0ff3bb783129be2e0a"}, + {file = "pbr-5.8.0.tar.gz", hash = "sha256:672d8ebee84921862110f23fcec2acea191ef58543d34dfe9ef3d9f13c31cddf"}, ] pillow = [ - {file = "Pillow-8.1.2-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:5cf03b9534aca63b192856aa601c68d0764810857786ea5da652581f3a44c2b0"}, - {file = "Pillow-8.1.2-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:f91b50ad88048d795c0ad004abbe1390aa1882073b1dca10bfd55d0b8cf18ec5"}, - {file = "Pillow-8.1.2-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5762ebb4436f46b566fc6351d67a9b5386b5e5de4e58fdaa18a1c83e0e20f1a8"}, - {file = "Pillow-8.1.2-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e2cd8ac157c1e5ae88b6dd790648ee5d2777e76f1e5c7d184eaddb2938594f34"}, - {file = "Pillow-8.1.2-cp36-cp36m-win32.whl", hash = "sha256:72027ebf682abc9bafd93b43edc44279f641e8996fb2945104471419113cfc71"}, - {file = "Pillow-8.1.2-cp36-cp36m-win_amd64.whl", hash = "sha256:d1d6bca39bb6dd94fba23cdb3eeaea5e30c7717c5343004d900e2a63b132c341"}, - {file = "Pillow-8.1.2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:90882c6f084ef68b71bba190209a734bf90abb82ab5e8f64444c71d5974008c6"}, - {file = "Pillow-8.1.2-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:89e4c757a91b8c55d97c91fa09c69b3677c227b942fa749e9a66eef602f59c28"}, - {file = "Pillow-8.1.2-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:8c4e32218c764bc27fe49b7328195579581aa419920edcc321c4cb877c65258d"}, - {file = "Pillow-8.1.2-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:a01da2c266d9868c4f91a9c6faf47a251f23b9a862dce81d2ff583135206f5be"}, - {file = "Pillow-8.1.2-cp37-cp37m-win32.whl", hash = "sha256:30d33a1a6400132e6f521640dd3f64578ac9bfb79a619416d7e8802b4ce1dd55"}, - {file = "Pillow-8.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:71b01ee69e7df527439d7752a2ce8fb89e19a32df484a308eca3e81f673d3a03"}, - {file = "Pillow-8.1.2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:5a2d957eb4aba9d48170b8fe6538ec1fbc2119ffe6373782c03d8acad3323f2e"}, - {file = "Pillow-8.1.2-cp38-cp38-manylinux1_i686.whl", hash = "sha256:87f42c976f91ca2fc21a3293e25bd3cd895918597db1b95b93cbd949f7d019ce"}, - {file = "Pillow-8.1.2-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:15306d71a1e96d7e271fd2a0737038b5a92ca2978d2e38b6ced7966583e3d5af"}, - {file = "Pillow-8.1.2-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:71f31ee4df3d5e0b366dd362007740106d3210fb6a56ec4b581a5324ba254f06"}, - {file = "Pillow-8.1.2-cp38-cp38-win32.whl", hash = "sha256:98afcac3205d31ab6a10c5006b0cf040d0026a68ec051edd3517b776c1d78b09"}, - {file = "Pillow-8.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:328240f7dddf77783e72d5ed79899a6b48bc6681f8d1f6001f55933cb4905060"}, - {file = "Pillow-8.1.2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:bead24c0ae3f1f6afcb915a057943ccf65fc755d11a1410a909c1fefb6c06ad1"}, - {file = "Pillow-8.1.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:81b3716cc9744ffdf76b39afb6247eae754186838cedad0b0ac63b2571253fe6"}, - {file = "Pillow-8.1.2-cp39-cp39-manylinux1_i686.whl", hash = "sha256:63cd413ac52ee3f67057223d363f4f82ce966e64906aea046daf46695e3c8238"}, - {file = "Pillow-8.1.2-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:8565355a29655b28fdc2c666fd9a3890fe5edc6639d128814fafecfae2d70910"}, - {file = "Pillow-8.1.2-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1940fc4d361f9cc7e558d6f56ff38d7351b53052fd7911f4b60cd7bc091ea3b1"}, - {file = "Pillow-8.1.2-cp39-cp39-win32.whl", hash = "sha256:46c2bcf8e1e75d154e78417b3e3c64e96def738c2a25435e74909e127a8cba5e"}, - {file = "Pillow-8.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:aeab4cd016e11e7aa5cfc49dcff8e51561fa64818a0be86efa82c7038e9369d0"}, - {file = "Pillow-8.1.2-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:74cd9aa648ed6dd25e572453eb09b08817a1e3d9f8d1bd4d8403d99e42ea790b"}, - {file = "Pillow-8.1.2-pp36-pypy36_pp73-manylinux2010_i686.whl", hash = "sha256:e5739ae63636a52b706a0facec77b2b58e485637e1638202556156e424a02dc2"}, - {file = "Pillow-8.1.2-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:903293320efe2466c1ab3509a33d6b866dc850cfd0c5d9cc92632014cec185fb"}, - {file = "Pillow-8.1.2-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:5daba2b40782c1c5157a788ec4454067c6616f5a0c1b70e26ac326a880c2d328"}, - {file = "Pillow-8.1.2-pp37-pypy37_pp73-manylinux2010_i686.whl", hash = "sha256:1f93f2fe211f1ef75e6f589327f4d4f8545d5c8e826231b042b483d8383e8a7c"}, - {file = "Pillow-8.1.2-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:6efac40344d8f668b6c4533ae02a48d52fd852ef0654cc6f19f6ac146399c733"}, - {file = "Pillow-8.1.2-pp37-pypy37_pp73-win32.whl", hash = "sha256:f36c3ff63d6fc509ce599a2f5b0d0732189eed653420e7294c039d342c6e204a"}, - {file = "Pillow-8.1.2.tar.gz", hash = "sha256:b07c660e014852d98a00a91adfbe25033898a9d90a8f39beb2437d22a203fc44"}, + {file = "Pillow-8.3.2-cp310-cp310-macosx_10_10_universal2.whl", hash = "sha256:c691b26283c3a31594683217d746f1dad59a7ae1d4cfc24626d7a064a11197d4"}, + {file = "Pillow-8.3.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:f514c2717012859ccb349c97862568fdc0479aad85b0270d6b5a6509dbc142e2"}, + {file = "Pillow-8.3.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:be25cb93442c6d2f8702c599b51184bd3ccd83adebd08886b682173e09ef0c3f"}, + {file = "Pillow-8.3.2-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d675a876b295afa114ca8bf42d7f86b5fb1298e1b6bb9a24405a3f6c8338811c"}, + {file = "Pillow-8.3.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59697568a0455764a094585b2551fd76bfd6b959c9f92d4bdec9d0e14616303a"}, + {file = "Pillow-8.3.2-cp310-cp310-win32.whl", hash = "sha256:2d5e9dc0bf1b5d9048a94c48d0813b6c96fccfa4ccf276d9c36308840f40c228"}, + {file = "Pillow-8.3.2-cp310-cp310-win_amd64.whl", hash = "sha256:11c27e74bab423eb3c9232d97553111cc0be81b74b47165f07ebfdd29d825875"}, + {file = "Pillow-8.3.2-cp36-cp36m-macosx_10_10_x86_64.whl", hash = "sha256:11eb7f98165d56042545c9e6db3ce394ed8b45089a67124298f0473b29cb60b2"}, + {file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f23b2d3079522fdf3c09de6517f625f7a964f916c956527bed805ac043799b8"}, + {file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:19ec4cfe4b961edc249b0e04b5618666c23a83bc35842dea2bfd5dfa0157f81b"}, + {file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e5a31c07cea5edbaeb4bdba6f2b87db7d3dc0f446f379d907e51cc70ea375629"}, + {file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:15ccb81a6ffc57ea0137f9f3ac2737ffa1d11f786244d719639df17476d399a7"}, + {file = "Pillow-8.3.2-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:8f284dc1695caf71a74f24993b7c7473d77bc760be45f776a2c2f4e04c170550"}, + {file = "Pillow-8.3.2-cp36-cp36m-win32.whl", hash = "sha256:4abc247b31a98f29e5224f2d31ef15f86a71f79c7f4d2ac345a5d551d6393073"}, + {file = "Pillow-8.3.2-cp36-cp36m-win_amd64.whl", hash = "sha256:a048dad5ed6ad1fad338c02c609b862dfaa921fcd065d747194a6805f91f2196"}, + {file = "Pillow-8.3.2-cp37-cp37m-macosx_10_10_x86_64.whl", hash = "sha256:06d1adaa284696785375fa80a6a8eb309be722cf4ef8949518beb34487a3df71"}, + {file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd24054aaf21e70a51e2a2a5ed1183560d3a69e6f9594a4bfe360a46f94eba83"}, + {file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27a330bf7014ee034046db43ccbb05c766aa9e70b8d6c5260bfc38d73103b0ba"}, + {file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13654b521fb98abdecec105ea3fb5ba863d1548c9b58831dd5105bb3873569f1"}, + {file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1bd983c565f92779be456ece2479840ec39d386007cd4ae83382646293d681b"}, + {file = "Pillow-8.3.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:4326ea1e2722f3dc00ed77c36d3b5354b8fb7399fb59230249ea6d59cbed90da"}, + {file = "Pillow-8.3.2-cp37-cp37m-win32.whl", hash = "sha256:085a90a99404b859a4b6c3daa42afde17cb3ad3115e44a75f0d7b4a32f06a6c9"}, + {file = "Pillow-8.3.2-cp37-cp37m-win_amd64.whl", hash = "sha256:18a07a683805d32826c09acfce44a90bf474e6a66ce482b1c7fcd3757d588df3"}, + {file = "Pillow-8.3.2-cp38-cp38-macosx_10_10_x86_64.whl", hash = "sha256:4e59e99fd680e2b8b11bbd463f3c9450ab799305d5f2bafb74fefba6ac058616"}, + {file = "Pillow-8.3.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:4d89a2e9219a526401015153c0e9dd48319ea6ab9fe3b066a20aa9aee23d9fd3"}, + {file = "Pillow-8.3.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:56fd98c8294f57636084f4b076b75f86c57b2a63a8410c0cd172bc93695ee979"}, + {file = "Pillow-8.3.2-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b11c9d310a3522b0fd3c35667914271f570576a0e387701f370eb39d45f08a4"}, + {file = "Pillow-8.3.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0412516dcc9de9b0a1e0ae25a280015809de8270f134cc2c1e32c4eeb397cf30"}, + {file = "Pillow-8.3.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bcb04ff12e79b28be6c9988f275e7ab69f01cc2ba319fb3114f87817bb7c74b6"}, + {file = "Pillow-8.3.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0b9911ec70731711c3b6ebcde26caea620cbdd9dcb73c67b0730c8817f24711b"}, + {file = "Pillow-8.3.2-cp38-cp38-win32.whl", hash = "sha256:ce2e5e04bb86da6187f96d7bab3f93a7877830981b37f0287dd6479e27a10341"}, + {file = "Pillow-8.3.2-cp38-cp38-win_amd64.whl", hash = "sha256:35d27687f027ad25a8d0ef45dd5208ef044c588003cdcedf05afb00dbc5c2deb"}, + {file = "Pillow-8.3.2-cp39-cp39-macosx_10_10_x86_64.whl", hash = "sha256:04835e68ef12904bc3e1fd002b33eea0779320d4346082bd5b24bec12ad9c3e9"}, + {file = "Pillow-8.3.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:10e00f7336780ca7d3653cf3ac26f068fa11b5a96894ea29a64d3dc4b810d630"}, + {file = "Pillow-8.3.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cde7a4d3687f21cffdf5bb171172070bb95e02af448c4c8b2f223d783214056"}, + {file = "Pillow-8.3.2-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1c3ff00110835bdda2b1e2b07f4a2548a39744bb7de5946dc8e95517c4fb2ca6"}, + {file = "Pillow-8.3.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35d409030bf3bd05fa66fb5fdedc39c521b397f61ad04309c90444e893d05f7d"}, + {file = "Pillow-8.3.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6bff50ba9891be0a004ef48828e012babaaf7da204d81ab9be37480b9020a82b"}, + {file = "Pillow-8.3.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7dbfbc0020aa1d9bc1b0b8bcf255a7d73f4ad0336f8fd2533fcc54a4ccfb9441"}, + {file = "Pillow-8.3.2-cp39-cp39-win32.whl", hash = "sha256:963ebdc5365d748185fdb06daf2ac758116deecb2277ec5ae98139f93844bc09"}, + {file = "Pillow-8.3.2-cp39-cp39-win_amd64.whl", hash = "sha256:cc9d0dec711c914ed500f1d0d3822868760954dce98dfb0b7382a854aee55d19"}, + {file = "Pillow-8.3.2-pp36-pypy36_pp73-macosx_10_10_x86_64.whl", hash = "sha256:2c661542c6f71dfd9dc82d9d29a8386287e82813b0375b3a02983feac69ef864"}, + {file = "Pillow-8.3.2-pp36-pypy36_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:548794f99ff52a73a156771a0402f5e1c35285bd981046a502d7e4793e8facaa"}, + {file = "Pillow-8.3.2-pp36-pypy36_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8b68f565a4175e12e68ca900af8910e8fe48aaa48fd3ca853494f384e11c8bcd"}, + {file = "Pillow-8.3.2-pp36-pypy36_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:838eb85de6d9307c19c655c726f8d13b8b646f144ca6b3771fa62b711ebf7624"}, + {file = "Pillow-8.3.2-pp36-pypy36_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:feb5db446e96bfecfec078b943cc07744cc759893cef045aa8b8b6d6aaa8274e"}, + {file = "Pillow-8.3.2-pp37-pypy37_pp73-macosx_10_10_x86_64.whl", hash = "sha256:fc0db32f7223b094964e71729c0361f93db43664dd1ec86d3df217853cedda87"}, + {file = "Pillow-8.3.2-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fd4fd83aa912d7b89b4b4a1580d30e2a4242f3936882a3f433586e5ab97ed0d5"}, + {file = "Pillow-8.3.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d0c8ebbfd439c37624db98f3877d9ed12c137cadd99dde2d2eae0dab0bbfc355"}, + {file = "Pillow-8.3.2-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6cb3dd7f23b044b0737317f892d399f9e2f0b3a02b22b2c692851fb8120d82c6"}, + {file = "Pillow-8.3.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a66566f8a22561fc1a88dc87606c69b84fa9ce724f99522cf922c801ec68f5c1"}, + {file = "Pillow-8.3.2-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:ce651ca46d0202c302a535d3047c55a0131a720cf554a578fc1b8a2aff0e7d96"}, + {file = "Pillow-8.3.2.tar.gz", hash = "sha256:dde3f3ed8d00c72631bc19cbfff8ad3b6215062a5eed402381ad365f82f0c18c"}, +] +platformdirs = [ + {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, + {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, ] prometheus-client = [ - {file = "prometheus_client-0.11.0-py2.py3-none-any.whl", hash = "sha256:b014bc76815eb1399da8ce5fc84b7717a3e63652b0c0f8804092c9363acab1b2"}, - {file = "prometheus_client-0.11.0.tar.gz", hash = "sha256:3a8baade6cb80bcfe43297e33e7623f3118d660d41387593758e2fb1ea173a86"}, + {file = "prometheus_client-0.12.0-py2.py3-none-any.whl", hash = "sha256:317453ebabff0a1b02df7f708efbab21e3489e7072b61cb6957230dd004a0af0"}, + {file = "prometheus_client-0.12.0.tar.gz", hash = "sha256:1b12ba48cee33b9b0b9de64a1047cbd3c5f2d0ab6ebcead7ddda613a750ec3c5"}, ] promise = [ {file = "promise-2.3.tar.gz", hash = "sha256:dfd18337c523ba4b6a58801c164c1904a9d4d1b1747c7d5dbf45b693a49d93d0"}, ] prompt-toolkit = [ - {file = "prompt_toolkit-3.0.3-py3-none-any.whl", hash = "sha256:c93e53af97f630f12f5f62a3274e79527936ed466f038953dfa379d4941f651a"}, - {file = "prompt_toolkit-3.0.3.tar.gz", hash = "sha256:a402e9bf468b63314e37460b68ba68243d55b2f8c4d0192f85a019af3945050e"}, + {file = "prompt_toolkit-3.0.23-py3-none-any.whl", hash = "sha256:5f29d62cb7a0ecacfa3d8ceea05a63cd22500543472d64298fc06ddda906b25d"}, + {file = "prompt_toolkit-3.0.23.tar.gz", hash = "sha256:7053aba00895473cb357819358ef33f11aa97e4ac83d38efb123e5649ceeecaf"}, ] psycopg2-binary = [ {file = "psycopg2-binary-2.8.6.tar.gz", hash = "sha256:11b9c0ebce097180129e422379b824ae21c8f2a6596b159c7659e2e5a00e1aa0"}, @@ -2443,60 +2504,60 @@ psycopg2-binary = [ {file = "psycopg2_binary-2.8.6-cp39-cp39-win_amd64.whl", hash = "sha256:15978a1fbd225583dd8cdaf37e67ccc278b5abecb4caf6b2d6b8e2b948e953f6"}, ] pycodestyle = [ - {file = "pycodestyle-2.7.0-py2.py3-none-any.whl", hash = "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068"}, - {file = "pycodestyle-2.7.0.tar.gz", hash = "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef"}, + {file = "pycodestyle-2.8.0-py2.py3-none-any.whl", hash = "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20"}, + {file = "pycodestyle-2.8.0.tar.gz", hash = "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f"}, ] pycparser = [ - {file = "pycparser-2.20-py2.py3-none-any.whl", hash = "sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705"}, - {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"}, + {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, + {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, ] pycryptodome = [ - {file = "pycryptodome-3.10.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1c5e1ca507de2ad93474be5cfe2bfa76b7cf039a1a32fc196f40935944871a06"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:6260e24d41149268122dd39d4ebd5941e9d107f49463f7e071fd397e29923b0c"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:3f840c49d38986f6e17dbc0673d37947c88bc9d2d9dba1c01b979b36f8447db1"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:2dea65df54349cdfa43d6b2e8edb83f5f8d6861e5cf7b1fbc3e34c5694c85e27"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:e61e363d9a5d7916f3a4ce984a929514c0df3daf3b1b2eb5e6edbb131ee771cf"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:2603c98ae04aac675fefcf71a6c87dc4bb74a75e9071ae3923bbc91a59f08d35"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-win32.whl", hash = "sha256:38661348ecb71476037f1e1f553159b80d256c00f6c0b00502acac891f7116d9"}, - {file = "pycryptodome-3.10.1-cp27-cp27m-win_amd64.whl", hash = "sha256:1723ebee5561628ce96748501cdaa7afaa67329d753933296321f0be55358dce"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:77997519d8eb8a4adcd9a47b9cec18f9b323e296986528186c0e9a7a15d6a07e"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:99b2f3fc51d308286071d0953f92055504a6ffe829a832a9fc7a04318a7683dd"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:e0a4d5933a88a2c98bbe19c0c722f5483dc628d7a38338ac2cb64a7dbd34064b"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:d3d6958d53ad307df5e8469cc44474a75393a434addf20ecd451f38a72fe29b8"}, - {file = "pycryptodome-3.10.1-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:a8eb8b6ea09ec1c2535bf39914377bc8abcab2c7d30fa9225eb4fe412024e427"}, - {file = "pycryptodome-3.10.1-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:31c1df17b3dc5f39600a4057d7db53ac372f492c955b9b75dd439f5d8b460129"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_i686.whl", hash = "sha256:a3105a0eb63eacf98c2ecb0eb4aa03f77f40fbac2bdde22020bb8a536b226bb8"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:a92d5c414e8ee1249e850789052608f582416e82422502dc0ac8c577808a9067"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:60386d1d4cfaad299803b45a5bc2089696eaf6cdd56f9fc17479a6f89595cfc8"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:501ab36aae360e31d0ec370cf5ce8ace6cb4112060d099b993bc02b36ac83fb6"}, - {file = "pycryptodome-3.10.1-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:fc7489a50323a0df02378bc2fff86eb69d94cc5639914346c736be981c6a02e7"}, - {file = "pycryptodome-3.10.1-cp35-abi3-win32.whl", hash = "sha256:9b6f711b25e01931f1c61ce0115245a23cdc8b80bf8539ac0363bdcf27d649b6"}, - {file = "pycryptodome-3.10.1-cp35-abi3-win_amd64.whl", hash = "sha256:7fd519b89585abf57bf47d90166903ec7b43af4fe23c92273ea09e6336af5c07"}, - {file = "pycryptodome-3.10.1-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:09c1555a3fa450e7eaca41ea11cd00afe7c91fef52353488e65663777d8524e0"}, - {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:758949ca62690b1540dfb24ad773c6da9cd0e425189e83e39c038bbd52b8e438"}, - {file = "pycryptodome-3.10.1-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:e3bf558c6aeb49afa9f0c06cee7fb5947ee5a1ff3bd794b653d39926b49077fa"}, - {file = "pycryptodome-3.10.1-pp27-pypy_73-win32.whl", hash = "sha256:f977cdf725b20f6b8229b0c87acb98c7717e742ef9f46b113985303ae12a99da"}, - {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6d2df5223b12437e644ce0a3be7809471ffa71de44ccd28b02180401982594a6"}, - {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:98213ac2b18dc1969a47bc65a79a8fca02a414249d0c8635abb081c7f38c91b6"}, - {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:12222a5edc9ca4a29de15fbd5339099c4c26c56e13c2ceddf0b920794f26165d"}, - {file = "pycryptodome-3.10.1-pp36-pypy36_pp73-win32.whl", hash = "sha256:6bbf7fee7b7948b29d7e71fcacf48bac0c57fb41332007061a933f2d996f9713"}, - {file = "pycryptodome-3.10.1.tar.gz", hash = "sha256:3e2e3a06580c5f190df843cdb90ea28d61099cf4924334d5297a995de68e4673"}, + {file = "pycryptodome-3.10.4-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:91ba4215a1f37d0f371fe43bc88c5ff49c274849f3868321c889313787de7672"}, + {file = "pycryptodome-3.10.4-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:66301e4c42dee43ee2da256625d3fe81ef98cc9924c2bd535008cc3ad8ded77b"}, + {file = "pycryptodome-3.10.4-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:8ec154ec445412df31acf0096e7f715e30e167c8f2318b8f5b1ab7c28f4c82f7"}, + {file = "pycryptodome-3.10.4-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:8e82524e7c354033508891405574d12e612cc4fdd3b55d2c238fc1a3e300b606"}, + {file = "pycryptodome-3.10.4-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:b217b4525e60e1af552d62bec01b4685095436d4de5ecde0f05d75b2f95ba6d4"}, + {file = "pycryptodome-3.10.4-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:3a153658d97258ca20bf18f7fe31c09cc7c558b6f8974a6ec74e19f6c634bd64"}, + {file = "pycryptodome-3.10.4-cp27-cp27m-win32.whl", hash = "sha256:c6469d1453f5864e3321a172b0aa671b938d753cbf2376b99fa2ab8841539bb8"}, + {file = "pycryptodome-3.10.4-cp27-cp27m-win_amd64.whl", hash = "sha256:6b45fcace5a5d9c57ba87cf804b161adc62aa826295ce7f7acbcbdc0df74ed37"}, + {file = "pycryptodome-3.10.4-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:b1daf251395af7336ddde6a0015ba5e632c18fe646ba930ef87402537358e3b4"}, + {file = "pycryptodome-3.10.4-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:9a2312440057bf29b9582f72f14d79692044e63bfbc4b4bbea8559355f44f3dd"}, + {file = "pycryptodome-3.10.4-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:54d4e4d45f349d8c4e2f31c2734637ff62a844af391b833f789da88e43a8f338"}, + {file = "pycryptodome-3.10.4-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:97e7df67a4da2e3f60612bbfd6c3f243a63a15d8f4797dd275e1d7b44a65cb12"}, + {file = "pycryptodome-3.10.4-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:db15fa07d2a4c00beeb5e9acdfdbc1c79f9ccfbdc1a8f36c82c4aa44951b33c9"}, + {file = "pycryptodome-3.10.4-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:217dcc0c92503f7dd4b3d3b7d974331a4419f97f555c99a845c3b366fed7056b"}, + {file = "pycryptodome-3.10.4-cp35-abi3-manylinux1_i686.whl", hash = "sha256:a7471646d8cd1a58bb696d667dcb3853e5c9b341b68dcf3c3cc0893d0f98ca5f"}, + {file = "pycryptodome-3.10.4-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:d713dc0910e5ded07852a05e9b75f1dd9d3a31895eebee0668f612779b2a748c"}, + {file = "pycryptodome-3.10.4-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:ac3012c36633564b2b5539bb7c6d9175f31d2ce74844e9abe654c428f02d0fd8"}, + {file = "pycryptodome-3.10.4-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:3f9fb499e267039262569d08658132c9cd8b136bf1d8c56b72f70ed05551e526"}, + {file = "pycryptodome-3.10.4-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:309529d2526f3fb47102aeef376b3459110a6af7efb162e860b32e3a17a46f06"}, + {file = "pycryptodome-3.10.4-cp35-abi3-win32.whl", hash = "sha256:7efec2418e9746ec48e264eea431f8e422d931f71c57b1c96ee202b117f58fa9"}, + {file = "pycryptodome-3.10.4-cp35-abi3-win_amd64.whl", hash = "sha256:49e54f2245befb0193848c8c8031d8d1358ed4af5a1ae8d0a3ba669a5cdd3a72"}, + {file = "pycryptodome-3.10.4-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:4e8fc4c48365ce8a542fe48bf1360da05bb2851df12f64fc94d751705e7cdbe7"}, + {file = "pycryptodome-3.10.4-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:851e6d4930b160417235955322db44adbdb19589918670d63f4acd5d92959ac0"}, + {file = "pycryptodome-3.10.4-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:04e14c732c3693d2830839feed5129286ce47ffa8bfe90e4ae042c773e51c677"}, + {file = "pycryptodome-3.10.4-pp27-pypy_73-win32.whl", hash = "sha256:cefe6b267b8e5c3c72e11adec35a9c7285b62e8ea141b63e87055e9a9e5f2f8c"}, + {file = "pycryptodome-3.10.4-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:24c1b7705d19d8ae3e7255431efd2e526006855df62620118dd7b5374c6372f6"}, + {file = "pycryptodome-3.10.4-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:c61ea053bd5d4c12a063d7e704fbe1c45abb5d2510dab55bd95d166ba661604f"}, + {file = "pycryptodome-3.10.4-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:11d3164fb49fdee000fde05baecce103c0c698168ef1a18d9c7429dd66f0f5bb"}, + {file = "pycryptodome-3.10.4-pp36-pypy36_pp73-win32.whl", hash = "sha256:3faa6ebd35c61718f3f8862569c1f38450c24f3ededb213e1a64806f02f584bc"}, + {file = "pycryptodome-3.10.4.tar.gz", hash = "sha256:40083b0d7f277452c7f2dd4841801f058cc12a74c219ee4110d65774c6a58bef"}, ] pydocstyle = [ {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"}, {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"}, ] pyflakes = [ - {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"}, - {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"}, + {file = "pyflakes-2.4.0-py2.py3-none-any.whl", hash = "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e"}, + {file = "pyflakes-2.4.0.tar.gz", hash = "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c"}, ] pyjwt = [ - {file = "PyJWT-2.1.0-py3-none-any.whl", hash = "sha256:934d73fbba91b0483d3857d1aff50e96b2a892384ee2c17417ed3203f173fca1"}, - {file = "PyJWT-2.1.0.tar.gz", hash = "sha256:fba44e7898bbca160a2b2b501f492824fc8382485d3a6f11ba5d0c1937ce6130"}, + {file = "PyJWT-2.3.0-py3-none-any.whl", hash = "sha256:e0c4bb8d9f0af0c7f5b1ec4c5036309617d03d56932877f2f7a0beeb5318322f"}, + {file = "PyJWT-2.3.0.tar.gz", hash = "sha256:b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41"}, ] pylint = [ - {file = "pylint-2.9.3-py3-none-any.whl", hash = "sha256:5d46330e6b8886c31b5e3aba5ff48c10f4aa5e76cbf9002c6544306221e63fbc"}, - {file = "pylint-2.9.3.tar.gz", hash = "sha256:23a1dc8b30459d78e9ff25942c61bb936108ccbe29dd9e71c01dc8274961709a"}, + {file = "pylint-2.12.2-py3-none-any.whl", hash = "sha256:daabda3f7ed9d1c60f52d563b1b854632fd90035bcf01443e234d3dc794e3b74"}, + {file = "pylint-2.12.2.tar.gz", hash = "sha256:9d945a73640e1fec07ee34b42f5669b770c759acd536ec7b16d7e4b87a9c9ff9"}, ] pylint-django = [ {file = "pylint-django-2.4.4.tar.gz", hash = "sha256:f63f717169b0c2e4e19c28f1c32c28290647330184fcb7427805ae9b6994f3fc"}, @@ -2527,8 +2588,8 @@ pynacl = [ {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, ] pyparsing = [ - {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"}, - {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"}, + {file = "pyparsing-3.0.6-py3-none-any.whl", hash = "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4"}, + {file = "pyparsing-3.0.6.tar.gz", hash = "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81"}, ] pyrsistent = [ {file = "pyrsistent-0.18.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f4c8cabb46ff8e5d61f56a037974228e978f26bfefce4f61a4b1ac0ba7a2ab72"}, @@ -2557,29 +2618,45 @@ pyserial = [ {file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, {file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, ] +python-dateutil = [ + {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, + {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, +] python3-openid = [ {file = "python3-openid-3.2.0.tar.gz", hash = "sha256:33fbf6928f401e0b790151ed2b5290b02545e8775f982485205a066f874aaeaf"}, {file = "python3_openid-3.2.0-py3-none-any.whl", hash = "sha256:6626f771e0417486701e0b4daff762e7212e820ca5b29fcc0d05f6f8736dfa6b"}, ] pytz = [ - {file = "pytz-2021.1-py2.py3-none-any.whl", hash = "sha256:eb10ce3e7736052ed3623d49975ce333bcd712c7bb19a58b9e2089d4057d0798"}, - {file = "pytz-2021.1.tar.gz", hash = "sha256:83a4a90894bf38e243cf052c8b58f381bfe9a7a483f6a9cab140bc7f702ac4da"}, + {file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"}, + {file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"}, ] pyuwsgi = [ - {file = "pyuwsgi-2.0.19.1.post0-cp27-cp27m-macosx_10_13_x86_64.whl", hash = "sha256:a425f562f382a097ca49df26b70d47d12f0cf0abf233610f3f58b1f7f780355e"}, - {file = "pyuwsgi-2.0.19.1.post0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:56ecda11e873b2eb937b33d2999766322eebfa82ee5b26a2196a335c4e786186"}, - {file = "pyuwsgi-2.0.19.1.post0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:297d1d0b8c472374b12eda7f17a9f5de67cf516612e42b71a7636afb9d1e3974"}, - {file = "pyuwsgi-2.0.19.1.post0-cp35-cp35m-macosx_10_13_intel.whl", hash = "sha256:5439f0f3ef5d6bf1622f341662d04c1d92b88889db40b295419e5fe75a7c7d45"}, - {file = "pyuwsgi-2.0.19.1.post0-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:890e7e863cb61c8369b6bcfa5d6f323753aaeec2cfaba16741f119c79b964aa7"}, - {file = "pyuwsgi-2.0.19.1.post0-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:bddeb8df77010d0f842068765a0b3155fdcfd847f14bc1ba89fc7e37914a13d2"}, - {file = "pyuwsgi-2.0.19.1.post0-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:90e4235020048456ad867aefc383cdf5528b7f6e327555ceec579c428a828759"}, - {file = "pyuwsgi-2.0.19.1.post0-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:94d4287b155aa789ce4b6f671c981f7d6c58fc3113330e2f29ac7926cb854645"}, - {file = "pyuwsgi-2.0.19.1.post0-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:66a9751f28abf348e0ddccadc4ded47623f2d35cf9609c87b57909d55a4cdc15"}, - {file = "pyuwsgi-2.0.19.1.post0-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:285e263a9094389f13cfdefd033a4e99fbed3ad120dba9ac5093846cc03ac5ab"}, - {file = "pyuwsgi-2.0.19.1.post0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:dac4a04dc0f69d641dba984e83214d2c2cc098496c5d5585e7d3f4e7a9190f84"}, - {file = "pyuwsgi-2.0.19.1.post0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ac79dead0685beab5ecfe0926426849a44c5572528f89bb17f6ecf5eb561024e"}, - {file = "pyuwsgi-2.0.19.1.post0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:149675b2e020b0e833e8b871a545751ca346cbfed85c8fd2b320a01d40dc3d8f"}, - {file = "pyuwsgi-2.0.19.1.post0.tar.gz", hash = "sha256:0bd14517398f494d828d77a9bf72b5a6cbef0112e1cc05e9a0080fa8828ccfa0"}, + {file = "pyuwsgi-2.0.20-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd09a7328558728f6e38f77609f44d16f18d8b4dc5f8b8776f657c8892b70337"}, + {file = "pyuwsgi-2.0.20-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:210ad768b8af02bbfd3c0345a51c6d2c56a2333ac268d9a97ecf2d4209f78ef8"}, + {file = "pyuwsgi-2.0.20-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e33111308a79dbd202094963fd3a537dab30854e375d9be018f642fcd359e03d"}, + {file = "pyuwsgi-2.0.20-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:dd048b85abb90c79a34afbf787995ab2933cb2a8b210a6e064e5fca0a9bdf2d8"}, + {file = "pyuwsgi-2.0.20-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:f612a72437c2934e39393fb575fab791e919efc102a0aa1a27b5d9a301d462e9"}, + {file = "pyuwsgi-2.0.20-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:78ac2d8b7d307232f8276cb8a45300211b0fae179c0494f2a3e3178c70693f81"}, + {file = "pyuwsgi-2.0.20-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f84fc44bba77d05fa9d01189028501ff8e6a2d63db184ceea42085ce28196317"}, + {file = "pyuwsgi-2.0.20-cp36-cp36m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:342c312949c2f494f5d9ac272965715483cb22ed0353c18abb1b4590769ef00f"}, + {file = "pyuwsgi-2.0.20-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:dc99724df6d5613300d3357c4017cde840830ca132fe98f0a4feab9a6af48622"}, + {file = "pyuwsgi-2.0.20-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:70096084ccfa18f058dfb3380aa10e80d48958ddfdb888b827f06aacb72f80b8"}, + {file = "pyuwsgi-2.0.20-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3e7a8576452635612c82f660e5be99ba42cdcdff142948f5c263b5278f2c17b0"}, + {file = "pyuwsgi-2.0.20-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2844205e0095a29d3a6e9e23bd37bcf18d65e4e5ab23a14a45f5159c03a3b890"}, + {file = "pyuwsgi-2.0.20-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e2c0d5dafb9faacc8ed5b662f9eacb22e723fbc3cb7152b2ebf09a92b8bd6c0d"}, + {file = "pyuwsgi-2.0.20-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:99b0c5a87aa95dcbaca2ccb599d783f37461600e098881d4ec9796ba68e3ff8f"}, + {file = "pyuwsgi-2.0.20-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:df08a05dda4a92da46bd56af755dac091b145815c3cf6dda1855e01e40d12a99"}, + {file = "pyuwsgi-2.0.20-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:7c8ac4b2f411d15222f53a96627b8fa79056b39606925f429f59a51668bd95ab"}, + {file = "pyuwsgi-2.0.20-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f1494d00df440865b1ee269a350187766c9c364ba5f124eddb064f366c4b6d8a"}, + {file = "pyuwsgi-2.0.20-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c5ee8782932dbacdb4dd98714c3575cbbb88ba87d7bcb3149c828725ba048897"}, + {file = "pyuwsgi-2.0.20-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:015a8d310503eb8c0028ac8f98c7df3b49bc937c7db137aaf149ca7fae64c777"}, + {file = "pyuwsgi-2.0.20-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:9fc3d256270adf24b87f1dff893f3bc59bd5ee20c4ebb5add25e944c36359e1e"}, + {file = "pyuwsgi-2.0.20-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:052fe12d6a9ab9e9fc7814e40d2d28061aaea1a610069008d773e1d8a71feb11"}, + {file = "pyuwsgi-2.0.20-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7d160c7bfa8f2974fc6b807a8fc080be0d130b1a4ad0b463c0320e707e6a6eb6"}, + {file = "pyuwsgi-2.0.20-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:7691ca470e541133c444b23f83c6f07ecca12f4771a49e0e3a04b05f4c80f2d3"}, + {file = "pyuwsgi-2.0.20-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:a8fc4d52232f55df16e9cbf6faf4bf16910d15042229d5f8c717278fd4ea37a5"}, + {file = "pyuwsgi-2.0.20-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1acedb1841aa0e60e1a7245ae483a5fcb30e39cf0cdfe3987e14de4746daf75b"}, + {file = "pyuwsgi-2.0.20.tar.gz", hash = "sha256:8b0936a964511fa0e2ac2847c2435e95130e7c98619f06fe1f67320527576043"}, ] pyyaml = [ {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"}, @@ -2612,52 +2689,13 @@ pyyaml = [ {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"}, {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"}, ] +pyyaml-env-tag = [ + {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"}, + {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"}, +] redis = [ - {file = "redis-3.5.3-py2.py3-none-any.whl", hash = "sha256:432b788c4530cfe16d8d943a09d40ca6c16149727e4afe8c2c9d5580c59d9f24"}, - {file = "redis-3.5.3.tar.gz", hash = "sha256:0e7e0cfca8660dea8b7d5cd8c4f6c5e29e11f31158c0b0ae91a397f00e5a05a2"}, -] -regex = [ - {file = "regex-2021.7.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:e6a1e5ca97d411a461041d057348e578dc344ecd2add3555aedba3b408c9f874"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:6afe6a627888c9a6cfbb603d1d017ce204cebd589d66e0703309b8048c3b0854"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:ccb3d2190476d00414aab36cca453e4596e8f70a206e2aa8db3d495a109153d2"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:ed693137a9187052fc46eedfafdcb74e09917166362af4cc4fddc3b31560e93d"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:99d8ab206a5270c1002bfcf25c51bf329ca951e5a169f3b43214fdda1f0b5f0d"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux2014_i686.whl", hash = "sha256:b85ac458354165405c8a84725de7bbd07b00d9f72c31a60ffbf96bb38d3e25fa"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux2014_x86_64.whl", hash = "sha256:3f5716923d3d0bfb27048242a6e0f14eecdb2e2a7fac47eda1d055288595f222"}, - {file = "regex-2021.7.6-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e5983c19d0beb6af88cb4d47afb92d96751fb3fa1784d8785b1cdf14c6519407"}, - {file = "regex-2021.7.6-cp36-cp36m-win32.whl", hash = "sha256:c92831dac113a6e0ab28bc98f33781383fe294df1a2c3dfd1e850114da35fd5b"}, - {file = "regex-2021.7.6-cp36-cp36m-win_amd64.whl", hash = "sha256:791aa1b300e5b6e5d597c37c346fb4d66422178566bbb426dd87eaae475053fb"}, - {file = "regex-2021.7.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:59506c6e8bd9306cd8a41511e32d16d5d1194110b8cfe5a11d102d8b63cf945d"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:564a4c8a29435d1f2256ba247a0315325ea63335508ad8ed938a4f14c4116a5d"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:59c00bb8dd8775473cbfb967925ad2c3ecc8886b3b2d0c90a8e2707e06c743f0"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:9a854b916806c7e3b40e6616ac9e85d3cdb7649d9e6590653deb5b341a736cec"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:db2b7df831c3187a37f3bb80ec095f249fa276dbe09abd3d35297fc250385694"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux2014_i686.whl", hash = "sha256:173bc44ff95bc1e96398c38f3629d86fa72e539c79900283afa895694229fe6a"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux2014_x86_64.whl", hash = "sha256:15dddb19823f5147e7517bb12635b3c82e6f2a3a6b696cc3e321522e8b9308ad"}, - {file = "regex-2021.7.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ddeabc7652024803666ea09f32dd1ed40a0579b6fbb2a213eba590683025895"}, - {file = "regex-2021.7.6-cp37-cp37m-win32.whl", hash = "sha256:f080248b3e029d052bf74a897b9d74cfb7643537fbde97fe8225a6467fb559b5"}, - {file = "regex-2021.7.6-cp37-cp37m-win_amd64.whl", hash = "sha256:d8bbce0c96462dbceaa7ac4a7dfbbee92745b801b24bce10a98d2f2b1ea9432f"}, - {file = "regex-2021.7.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:edd1a68f79b89b0c57339bce297ad5d5ffcc6ae7e1afdb10f1947706ed066c9c"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux1_i686.whl", hash = "sha256:422dec1e7cbb2efbbe50e3f1de36b82906def93ed48da12d1714cabcd993d7f0"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:cbe23b323988a04c3e5b0c387fe3f8f363bf06c0680daf775875d979e376bd26"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:0eb2c6e0fcec5e0f1d3bcc1133556563222a2ffd2211945d7b1480c1b1a42a6f"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:1c78780bf46d620ff4fff40728f98b8afd8b8e35c3efd638c7df67be2d5cddbf"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux2014_i686.whl", hash = "sha256:bc84fb254a875a9f66616ed4538542fb7965db6356f3df571d783f7c8d256edd"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux2014_x86_64.whl", hash = "sha256:598c0a79b4b851b922f504f9f39a863d83ebdfff787261a5ed061c21e67dd761"}, - {file = "regex-2021.7.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:875c355360d0f8d3d827e462b29ea7682bf52327d500a4f837e934e9e4656068"}, - {file = "regex-2021.7.6-cp38-cp38-win32.whl", hash = "sha256:e586f448df2bbc37dfadccdb7ccd125c62b4348cb90c10840d695592aa1b29e0"}, - {file = "regex-2021.7.6-cp38-cp38-win_amd64.whl", hash = "sha256:2fe5e71e11a54e3355fa272137d521a40aace5d937d08b494bed4529964c19c4"}, - {file = "regex-2021.7.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6110bab7eab6566492618540c70edd4d2a18f40ca1d51d704f1d81c52d245026"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux1_i686.whl", hash = "sha256:4f64fc59fd5b10557f6cd0937e1597af022ad9b27d454e182485f1db3008f417"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:89e5528803566af4df368df2d6f503c84fbfb8249e6631c7b025fe23e6bd0cde"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:2366fe0479ca0e9afa534174faa2beae87847d208d457d200183f28c74eaea59"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:f9392a4555f3e4cb45310a65b403d86b589adc773898c25a39184b1ba4db8985"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux2014_i686.whl", hash = "sha256:2bceeb491b38225b1fee4517107b8491ba54fba77cf22a12e996d96a3c55613d"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux2014_x86_64.whl", hash = "sha256:f98dc35ab9a749276f1a4a38ab3e0e2ba1662ce710f6530f5b0a6656f1c32b58"}, - {file = "regex-2021.7.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:319eb2a8d0888fa6f1d9177705f341bc9455a2c8aca130016e52c7fe8d6c37a3"}, - {file = "regex-2021.7.6-cp39-cp39-win32.whl", hash = "sha256:eaf58b9e30e0e546cdc3ac06cf9165a1ca5b3de8221e9df679416ca667972035"}, - {file = "regex-2021.7.6-cp39-cp39-win_amd64.whl", hash = "sha256:4c9c3155fe74269f61e27617529b7f09552fbb12e44b1189cebbdb24294e6e1c"}, - {file = "regex-2021.7.6.tar.gz", hash = "sha256:8394e266005f2d8c6f0bc6780001f7afa3ef81a7a2111fa35058ded6fce79e4d"}, + {file = "redis-4.0.2-py3-none-any.whl", hash = "sha256:c8481cf414474e3497ec7971a1ba9b998c8efad0f0d289a009a5bbef040894f9"}, + {file = "redis-4.0.2.tar.gz", hash = "sha256:ccf692811f2c1fc7a92b466aa2599e4a6d2d73d5f736a2c70be600657c0da34a"}, ] requests = [ {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, @@ -2673,12 +2711,12 @@ requests-toolbelt = [ {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"}, ] rq = [ - {file = "rq-1.9.0-py2.py3-none-any.whl", hash = "sha256:7af1e9706dbe6f1eac16dffacd8271ec27c1369950941f14dab6bb08a62979d7"}, - {file = "rq-1.9.0.tar.gz", hash = "sha256:bdfef943de838955e474cfd0e25b9b8c53ed4b9c361fe4bb11cf56d17a87acc5"}, + {file = "rq-1.10.0-py2.py3-none-any.whl", hash = "sha256:92950a3e60863de48dd1800882939bbaf089a37497ebf9f2ecf7c9fd0a4c4a95"}, + {file = "rq-1.10.0.tar.gz", hash = "sha256:be09ec43fae9a75a4d26ea3cd520e5fa3ea2ea8cf481be33e6ec9416f0369cac"}, ] "ruamel.yaml" = [ - {file = "ruamel.yaml-0.17.10-py3-none-any.whl", hash = "sha256:ffb9b703853e9e8b7861606dfdab1026cf02505bade0653d1880f4b2db47f815"}, - {file = "ruamel.yaml-0.17.10.tar.gz", hash = "sha256:106bc8d6dc6a0ff7c9196a47570432036f41d556b779c6b4e618085f57e39e67"}, + {file = "ruamel.yaml-0.17.17-py3-none-any.whl", hash = "sha256:9af3ec5d7f8065582f3aa841305465025d0afd26c5fb54e15b964e11838fc74f"}, + {file = "ruamel.yaml-0.17.17.tar.gz", hash = "sha256:9751de4cbb57d4bfbf8fc394e125ed4a2f170fbff3dc3d78abf50be85924f8be"}, ] "ruamel.yaml.clib" = [ {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"}, @@ -2708,28 +2746,28 @@ rx = [ {file = "Rx-1.6.1.tar.gz", hash = "sha256:13a1d8d9e252625c173dc795471e614eadfe1cf40ffc684e08b8fff0d9748c23"}, ] scp = [ - {file = "scp-0.13.6-py2.py3-none-any.whl", hash = "sha256:5e23f22b00bdbeed83a982c6b2dfae98c125b80019c15fbb16dd64dfd864a452"}, - {file = "scp-0.13.6.tar.gz", hash = "sha256:0a72f9d782e968b09b114d5607f96b1f16fe9942857afb355399edd55372fcf1"}, + {file = "scp-0.14.1-py2.py3-none-any.whl", hash = "sha256:e4e0b9b41b73ebcc4e988e8f43039dc3715e88f3ee7b3e2d21521975bcfc82ee"}, + {file = "scp-0.14.1.tar.gz", hash = "sha256:b776bd6ce8c8385aa9a025b64a9815b5d798f12d4ef0d712d569503f62aece8b"}, ] singledispatch = [ - {file = "singledispatch-3.6.2-py2.py3-none-any.whl", hash = "sha256:0d428477703d8386eb6aeed6e522c9f22d49f4363cdf4ed6a2ba3dc276053e20"}, - {file = "singledispatch-3.6.2.tar.gz", hash = "sha256:d5bb9405a4b8de48e36709238e8b91b4f6f300f81a5132ba2531a9a738eca391"}, + {file = "singledispatch-3.7.0-py2.py3-none-any.whl", hash = "sha256:bc77afa97c8a22596d6d4fc20f1b7bdd2b86edc2a65a4262bdd7cc3cc19aa989"}, + {file = "singledispatch-3.7.0.tar.gz", hash = "sha256:c1a4d5c1da310c3fd8fccfb8d4e1cb7df076148fd5d858a819e37fffe44f3092"}, ] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, ] slack-sdk = [ - {file = "slack_sdk-3.7.0-py2.py3-none-any.whl", hash = "sha256:50b9fd6d8f83af7e8ad6d8e76882d04931842241f85ccfd30da09b4a7b9b1516"}, - {file = "slack_sdk-3.7.0.tar.gz", hash = "sha256:f0bf3e38ac393eba7fe1a99191b0e72f710860c6d2edc1271606fcfc08bea2e1"}, + {file = "slack_sdk-3.12.0-py2.py3-none-any.whl", hash = "sha256:f779ff3dc266491b02ad056d28038ec5d708b2a438a3a8f8794fb1121d8274e2"}, + {file = "slack_sdk-3.12.0.tar.gz", hash = "sha256:a384d91c10229f94a9b2cae2ec5af2a683a3d5aee1287c01238630ab42747287"}, ] smmap = [ - {file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"}, - {file = "smmap-4.0.0.tar.gz", hash = "sha256:7e65386bd122d45405ddf795637b7f7d2b532e7e401d46bbe3fb49b9986d5182"}, + {file = "smmap-5.0.0-py3-none-any.whl", hash = "sha256:2aba19d6a040e78d8b09de5c57e96207b09ed71d8e55ce0959eeee6c8e190d94"}, + {file = "smmap-5.0.0.tar.gz", hash = "sha256:c840e62059cd3be204b0c9c9f74be2c09d5648eddd4580d9314c3ecde0b30936"}, ] snowballstemmer = [ - {file = "snowballstemmer-2.1.0-py2.py3-none-any.whl", hash = "sha256:b51b447bea85f9968c13b650126a888aabd4cb4463fca868ec596826325dedc2"}, - {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"}, + {file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"}, + {file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"}, ] social-auth-app-django = [ {file = "social-auth-app-django-4.0.0.tar.gz", hash = "sha256:2c69e57df0b30c9c1823519c5f1992cbe4f3f98fdc7d95c840e091a752708840"}, @@ -2741,20 +2779,20 @@ social-auth-core = [ {file = "social_auth_core-4.1.0-py3-none-any.whl", hash = "sha256:983b53167ac56e7ba4909db555602a6e7a98c97ca47183bb222eb85ba627bf2b"}, ] sqlparse = [ - {file = "sqlparse-0.4.1-py3-none-any.whl", hash = "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0"}, - {file = "sqlparse-0.4.1.tar.gz", hash = "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8"}, + {file = "sqlparse-0.4.2-py3-none-any.whl", hash = "sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d"}, + {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"}, ] stevedore = [ - {file = "stevedore-3.3.0-py3-none-any.whl", hash = "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"}, - {file = "stevedore-3.3.0.tar.gz", hash = "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee"}, + {file = "stevedore-3.5.0-py3-none-any.whl", hash = "sha256:a547de73308fd7e90075bb4d301405bebf705292fa90a90fc3bcf9133f58616c"}, + {file = "stevedore-3.5.0.tar.gz", hash = "sha256:f40253887d8712eaa2bb0ea3830374416736dc8ec0e22f5a65092c1174c44335"}, ] svgwrite = [ {file = "svgwrite-1.4.1-py3-none-any.whl", hash = "sha256:4b21652a1d9c543a6bf4f9f2a54146b214519b7540ca60cb99968ad09ef631d0"}, {file = "svgwrite-1.4.1.zip", hash = "sha256:e220a4bf189e7e214a55e8a11421d152b5b6fb1dd660c86a8b6b61fe8cc2ac48"}, ] swagger-spec-validator = [ - {file = "swagger-spec-validator-2.7.3.tar.gz", hash = "sha256:f4f23ee4dbd52bfcde90b1144dde22304add6260e9f29252e9fd7814c9b8fd16"}, - {file = "swagger_spec_validator-2.7.3-py2.py3-none-any.whl", hash = "sha256:d1514ec7e3c058c701f27cc74f85ceb876d6418c9db57786b9c54085ed5e29eb"}, + {file = "swagger-spec-validator-2.7.4.tar.gz", hash = "sha256:2aee5e1fc0503be9f8299378b10c92169572781573c6de3315e831fd0559ba73"}, + {file = "swagger_spec_validator-2.7.4-py2.py3-none-any.whl", hash = "sha256:4e373a4db5262e7257fde17d84c5c0178327b8057985ab1be63f580bfa009855"}, ] tenacity = [ {file = "tenacity-8.0.1-py3-none-any.whl", hash = "sha256:f78f4ea81b0fabc06728c11dc2a8c01277bfc5181b321a4770471902e3eb844a"}, @@ -2776,102 +2814,72 @@ toml = [ {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, ] -tornado = [ - {file = "tornado-6.1-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:d371e811d6b156d82aa5f9a4e08b58debf97c302a35714f6f45e35139c332e32"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:0d321a39c36e5f2c4ff12b4ed58d41390460f798422c4504e09eb5678e09998c"}, - {file = "tornado-6.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9de9e5188a782be6b1ce866e8a51bc76a0fbaa0e16613823fc38e4fc2556ad05"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:61b32d06ae8a036a6607805e6720ef00a3c98207038444ba7fd3d169cd998910"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:3e63498f680547ed24d2c71e6497f24bca791aca2fe116dbc2bd0ac7f191691b"}, - {file = "tornado-6.1-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:6c77c9937962577a6a76917845d06af6ab9197702a42e1346d8ae2e76b5e3675"}, - {file = "tornado-6.1-cp35-cp35m-win32.whl", hash = "sha256:6286efab1ed6e74b7028327365cf7346b1d777d63ab30e21a0f4d5b275fc17d5"}, - {file = "tornado-6.1-cp35-cp35m-win_amd64.whl", hash = "sha256:fa2ba70284fa42c2a5ecb35e322e68823288a4251f9ba9cc77be04ae15eada68"}, - {file = "tornado-6.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0a00ff4561e2929a2c37ce706cb8233b7907e0cdc22eab98888aca5dd3775feb"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:748290bf9112b581c525e6e6d3820621ff020ed95af6f17fedef416b27ed564c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:e385b637ac3acaae8022e7e47dfa7b83d3620e432e3ecb9a3f7f58f150e50921"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:25ad220258349a12ae87ede08a7b04aca51237721f63b1808d39bdb4b2164558"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:65d98939f1a2e74b58839f8c4dab3b6b3c1ce84972ae712be02845e65391ac7c"}, - {file = "tornado-6.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:e519d64089b0876c7b467274468709dadf11e41d65f63bba207e04217f47c085"}, - {file = "tornado-6.1-cp36-cp36m-win32.whl", hash = "sha256:b87936fd2c317b6ee08a5741ea06b9d11a6074ef4cc42e031bc6403f82a32575"}, - {file = "tornado-6.1-cp36-cp36m-win_amd64.whl", hash = "sha256:cc0ee35043162abbf717b7df924597ade8e5395e7b66d18270116f8745ceb795"}, - {file = "tornado-6.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7250a3fa399f08ec9cb3f7b1b987955d17e044f1ade821b32e5f435130250d7f"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:ed3ad863b1b40cd1d4bd21e7498329ccaece75db5a5bf58cd3c9f130843e7102"}, - {file = "tornado-6.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:dcef026f608f678c118779cd6591c8af6e9b4155c44e0d1bc0c87c036fb8c8c4"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:70dec29e8ac485dbf57481baee40781c63e381bebea080991893cd297742b8fd"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:d3f7594930c423fd9f5d1a76bee85a2c36fd8b4b16921cae7e965f22575e9c01"}, - {file = "tornado-6.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:3447475585bae2e77ecb832fc0300c3695516a47d46cefa0528181a34c5b9d3d"}, - {file = "tornado-6.1-cp37-cp37m-win32.whl", hash = "sha256:e7229e60ac41a1202444497ddde70a48d33909e484f96eb0da9baf8dc68541df"}, - {file = "tornado-6.1-cp37-cp37m-win_amd64.whl", hash = "sha256:cb5ec8eead331e3bb4ce8066cf06d2dfef1bfb1b2a73082dfe8a161301b76e37"}, - {file = "tornado-6.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:20241b3cb4f425e971cb0a8e4ffc9b0a861530ae3c52f2b0434e6c1b57e9fd95"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:c77da1263aa361938476f04c4b6c8916001b90b2c2fdd92d8d535e1af48fba5a"}, - {file = "tornado-6.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:fba85b6cd9c39be262fcd23865652920832b61583de2a2ca907dbd8e8a8c81e5"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:1e8225a1070cd8eec59a996c43229fe8f95689cb16e552d130b9793cb570a288"}, - {file = "tornado-6.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d14d30e7f46a0476efb0deb5b61343b1526f73ebb5ed84f23dc794bdb88f9d9f"}, - {file = "tornado-6.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:8f959b26f2634a091bb42241c3ed8d3cedb506e7c27b8dd5c7b9f745318ddbb6"}, - {file = "tornado-6.1-cp38-cp38-win32.whl", hash = "sha256:34ca2dac9e4d7afb0bed4677512e36a52f09caa6fded70b4e3e1c89dbd92c326"}, - {file = "tornado-6.1-cp38-cp38-win_amd64.whl", hash = "sha256:6196a5c39286cc37c024cd78834fb9345e464525d8991c21e908cc046d1cc02c"}, - {file = "tornado-6.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f0ba29bafd8e7e22920567ce0d232c26d4d47c8b5cf4ed7b562b5db39fa199c5"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:33892118b165401f291070100d6d09359ca74addda679b60390b09f8ef325ffe"}, - {file = "tornado-6.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:7da13da6f985aab7f6f28debab00c67ff9cbacd588e8477034c0652ac141feea"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:e0791ac58d91ac58f694d8d2957884df8e4e2f6687cdf367ef7eb7497f79eaa2"}, - {file = "tornado-6.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:66324e4e1beede9ac79e60f88de548da58b1f8ab4b2f1354d8375774f997e6c0"}, - {file = "tornado-6.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:a48900ecea1cbb71b8c71c620dee15b62f85f7c14189bdeee54966fbd9a0c5bd"}, - {file = "tornado-6.1-cp39-cp39-win32.whl", hash = "sha256:d3d20ea5782ba63ed13bc2b8c291a053c8d807a8fa927d941bd718468f7b950c"}, - {file = "tornado-6.1-cp39-cp39-win_amd64.whl", hash = "sha256:548430be2740e327b3fe0201abe471f314741efcb0067ec4f2d7dcfb4825f3e4"}, - {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"}, -] -tqdm = [ - {file = "tqdm-4.61.2-py2.py3-none-any.whl", hash = "sha256:5aa445ea0ad8b16d82b15ab342de6b195a722d75fc1ef9934a46bba6feafbc64"}, - {file = "tqdm-4.61.2.tar.gz", hash = "sha256:8bb94db0d4468fea27d004a0f1d1c02da3cdedc00fe491c0de986b76a04d6b0a"}, +tomli = [ + {file = "tomli-1.2.2-py3-none-any.whl", hash = "sha256:f04066f68f5554911363063a30b108d2b5a5b1a010aa8b6132af78489fe3aade"}, + {file = "tomli-1.2.2.tar.gz", hash = "sha256:c6ce0015eb38820eaf32b5db832dbc26deb3dd427bd5f6556cf0acac2c214fee"}, ] typed-ast = [ - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:2068531575a125b87a41802130fa7e29f26c09a2833fea68d9a40cf33902eba6"}, - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:c907f561b1e83e93fad565bac5ba9c22d96a54e7ea0267c708bffe863cbe4075"}, - {file = "typed_ast-1.4.3-cp35-cp35m-manylinux2014_aarch64.whl", hash = "sha256:1b3ead4a96c9101bef08f9f7d1217c096f31667617b58de957f690c92378b528"}, - {file = "typed_ast-1.4.3-cp35-cp35m-win32.whl", hash = "sha256:dde816ca9dac1d9c01dd504ea5967821606f02e510438120091b84e852367428"}, - {file = "typed_ast-1.4.3-cp35-cp35m-win_amd64.whl", hash = "sha256:777a26c84bea6cd934422ac2e3b78863a37017618b6e5c08f92ef69853e765d3"}, - {file = "typed_ast-1.4.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f8afcf15cc511ada719a88e013cec87c11aff7b91f019295eb4530f96fe5ef2f"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:52b1eb8c83f178ab787f3a4283f68258525f8d70f778a2f6dd54d3b5e5fb4341"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:01ae5f73431d21eead5015997ab41afa53aa1fbe252f9da060be5dad2c730ace"}, - {file = "typed_ast-1.4.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:c190f0899e9f9f8b6b7863debfb739abcb21a5c054f911ca3596d12b8a4c4c7f"}, - {file = "typed_ast-1.4.3-cp36-cp36m-win32.whl", hash = "sha256:398e44cd480f4d2b7ee8d98385ca104e35c81525dd98c519acff1b79bdaac363"}, - {file = "typed_ast-1.4.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bff6ad71c81b3bba8fa35f0f1921fb24ff4476235a6e94a26ada2e54370e6da7"}, - {file = "typed_ast-1.4.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0fb71b8c643187d7492c1f8352f2c15b4c4af3f6338f21681d3681b3dc31a266"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:760ad187b1041a154f0e4d0f6aae3e40fdb51d6de16e5c99aedadd9246450e9e"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5feca99c17af94057417d744607b82dd0a664fd5e4ca98061480fd8b14b18d04"}, - {file = "typed_ast-1.4.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:95431a26309a21874005845c21118c83991c63ea800dd44843e42a916aec5899"}, - {file = "typed_ast-1.4.3-cp37-cp37m-win32.whl", hash = "sha256:aee0c1256be6c07bd3e1263ff920c325b59849dc95392a05f258bb9b259cf39c"}, - {file = "typed_ast-1.4.3-cp37-cp37m-win_amd64.whl", hash = "sha256:9ad2c92ec681e02baf81fdfa056fe0d818645efa9af1f1cd5fd6f1bd2bdfd805"}, - {file = "typed_ast-1.4.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b36b4f3920103a25e1d5d024d155c504080959582b928e91cb608a65c3a49e1a"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_i686.whl", hash = "sha256:067a74454df670dcaa4e59349a2e5c81e567d8d65458d480a5b3dfecec08c5ff"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7538e495704e2ccda9b234b82423a4038f324f3a10c43bc088a1636180f11a41"}, - {file = "typed_ast-1.4.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:af3d4a73793725138d6b334d9d247ce7e5f084d96284ed23f22ee626a7b88e39"}, - {file = "typed_ast-1.4.3-cp38-cp38-win32.whl", hash = "sha256:f2362f3cb0f3172c42938946dbc5b7843c2a28aec307c49100c8b38764eb6927"}, - {file = "typed_ast-1.4.3-cp38-cp38-win_amd64.whl", hash = "sha256:dd4a21253f42b8d2b48410cb31fe501d32f8b9fbeb1f55063ad102fe9c425e40"}, - {file = "typed_ast-1.4.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:f328adcfebed9f11301eaedfa48e15bdece9b519fb27e6a8c01aa52a17ec31b3"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_i686.whl", hash = "sha256:2c726c276d09fc5c414693a2de063f521052d9ea7c240ce553316f70656c84d4"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:cae53c389825d3b46fb37538441f75d6aecc4174f615d048321b716df2757fb0"}, - {file = "typed_ast-1.4.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:b9574c6f03f685070d859e75c7f9eeca02d6933273b5e69572e5ff9d5e3931c3"}, - {file = "typed_ast-1.4.3-cp39-cp39-win32.whl", hash = "sha256:209596a4ec71d990d71d5e0d312ac935d86930e6eecff6ccc7007fe54d703808"}, - {file = "typed_ast-1.4.3-cp39-cp39-win_amd64.whl", hash = "sha256:9c6d1a54552b5330bc657b7ef0eae25d00ba7ffe85d9ea8ae6540d2197a3788c"}, - {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"}, + {file = "typed_ast-1.5.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5d8314c92414ce7481eee7ad42b353943679cf6f30237b5ecbf7d835519e1212"}, + {file = "typed_ast-1.5.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b53ae5de5500529c76225d18eeb060efbcec90ad5e030713fe8dab0fb4531631"}, + {file = "typed_ast-1.5.1-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:24058827d8f5d633f97223f5148a7d22628099a3d2efe06654ce872f46f07cdb"}, + {file = "typed_ast-1.5.1-cp310-cp310-win_amd64.whl", hash = "sha256:a6d495c1ef572519a7bac9534dbf6d94c40e5b6a608ef41136133377bba4aa08"}, + {file = "typed_ast-1.5.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:de4ecae89c7d8b56169473e08f6bfd2df7f95015591f43126e4ea7865928677e"}, + {file = "typed_ast-1.5.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:256115a5bc7ea9e665c6314ed6671ee2c08ca380f9d5f130bd4d2c1f5848d695"}, + {file = "typed_ast-1.5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:7c42707ab981b6cf4b73490c16e9d17fcd5227039720ca14abe415d39a173a30"}, + {file = "typed_ast-1.5.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:71dcda943a471d826ea930dd449ac7e76db7be778fcd722deb63642bab32ea3f"}, + {file = "typed_ast-1.5.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4f30a2bcd8e68adbb791ce1567fdb897357506f7ea6716f6bbdd3053ac4d9471"}, + {file = "typed_ast-1.5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:ca9e8300d8ba0b66d140820cf463438c8e7b4cdc6fd710c059bfcfb1531d03fb"}, + {file = "typed_ast-1.5.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:9caaf2b440efb39ecbc45e2fabde809cbe56272719131a6318fd9bf08b58e2cb"}, + {file = "typed_ast-1.5.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:c9bcad65d66d594bffab8575f39420fe0ee96f66e23c4d927ebb4e24354ec1af"}, + {file = "typed_ast-1.5.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:591bc04e507595887160ed7aa8d6785867fb86c5793911be79ccede61ae96f4d"}, + {file = "typed_ast-1.5.1-cp38-cp38-win_amd64.whl", hash = "sha256:a80d84f535642420dd17e16ae25bb46c7f4c16ee231105e7f3eb43976a89670a"}, + {file = "typed_ast-1.5.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:38cf5c642fa808300bae1281460d4f9b7617cf864d4e383054a5ef336e344d32"}, + {file = "typed_ast-1.5.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:5b6ab14c56bc9c7e3c30228a0a0b54b915b1579613f6e463ba6f4eb1382e7fd4"}, + {file = "typed_ast-1.5.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a2b8d7007f6280e36fa42652df47087ac7b0a7d7f09f9468f07792ba646aac2d"}, + {file = "typed_ast-1.5.1-cp39-cp39-win_amd64.whl", hash = "sha256:b6d17f37f6edd879141e64a5db17b67488cfeffeedad8c5cec0392305e9bc775"}, + {file = "typed_ast-1.5.1.tar.gz", hash = "sha256:484137cab8ecf47e137260daa20bafbba5f4e3ec7fda1c1e69ab299b75fa81c5"}, ] typing-extensions = [ - {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"}, - {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"}, - {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"}, + {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, + {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, ] uritemplate = [ - {file = "uritemplate-3.0.1-py2.py3-none-any.whl", hash = "sha256:07620c3f3f8eed1f12600845892b0e036a2420acf513c53f7de0abd911a5894f"}, - {file = "uritemplate-3.0.1.tar.gz", hash = "sha256:5af8ad10cec94f215e3f48112de2022e1d5a37ed427fbd88652fa908f2ab7cae"}, + {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, + {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, ] urllib3 = [ - {file = "urllib3-1.26.6-py2.py3-none-any.whl", hash = "sha256:39fb8672126159acb139a7718dd10806104dec1e2f0f6c88aab05d17df10c8d4"}, - {file = "urllib3-1.26.6.tar.gz", hash = "sha256:f57b4c16c62fa2760b7e3d97c35b255512fb6b59a259730f36ba32ce9f8e342f"}, + {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, + {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, ] vine = [ {file = "vine-5.0.0-py2.py3-none-any.whl", hash = "sha256:4c9dceab6f76ed92105027c49c823800dd33cacce13bdedc5b914e3514b7fb30"}, {file = "vine-5.0.0.tar.gz", hash = "sha256:7d3b1624a953da82ef63462013bbd271d3eb75751489f9807598e8f340bd637e"}, ] +watchdog = [ + {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"}, + {file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b"}, + {file = "watchdog-2.1.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542"}, + {file = "watchdog-2.1.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669"}, + {file = "watchdog-2.1.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660"}, + {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3"}, + {file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04"}, + {file = "watchdog-2.1.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b"}, + {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604"}, + {file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6"}, + {file = "watchdog-2.1.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9"}, + {file = "watchdog-2.1.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_armv7l.whl", hash = "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_i686.whl", hash = "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_s390x.whl", hash = "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2"}, + {file = "watchdog-2.1.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15"}, + {file = "watchdog-2.1.6-py3-none-win32.whl", hash = "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d"}, + {file = "watchdog-2.1.6-py3-none-win_amd64.whl", hash = "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5"}, + {file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"}, + {file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"}, +] wcwidth = [ {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"}, {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"}, @@ -2881,12 +2889,62 @@ webexteamssdk = [ {file = "webexteamssdk-1.6.tar.gz", hash = "sha256:980f268d89187d1a157dfbadcb626fce849080a31550e549cfe838f0203b3a3d"}, ] wrapt = [ - {file = "wrapt-1.12.1.tar.gz", hash = "sha256:b62ffa81fb85f4332a4f609cab4ac40709470da05643a082ec1eb88e6d9b97d7"}, + {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"}, + {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"}, + {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"}, + {file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"}, + {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"}, + {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"}, + {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"}, + {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"}, + {file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"}, + {file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"}, + {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"}, + {file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"}, + {file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"}, + {file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"}, + {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"}, + {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"}, + {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"}, + {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"}, + {file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"}, + {file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"}, + {file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"}, + {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"}, + {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"}, + {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"}, + {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"}, + {file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"}, + {file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"}, + {file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"}, + {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"}, + {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"}, + {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"}, + {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"}, + {file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"}, + {file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"}, + {file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"}, + {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"}, + {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"}, + {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"}, + {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"}, + {file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"}, + {file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"}, + {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"}, ] yamllint = [ - {file = "yamllint-1.26.1.tar.gz", hash = "sha256:87d9462b3ed7e9dfa19caa177f7a77cd9888b3dc4044447d6ae0ab233bcd1324"}, + {file = "yamllint-1.26.3.tar.gz", hash = "sha256:3934dcde484374596d6b52d8db412929a169f6d9e52e20f9ade5bf3523d9b96e"}, ] zipp = [ - {file = "zipp-3.5.0-py3-none-any.whl", hash = "sha256:957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"}, - {file = "zipp-3.5.0.tar.gz", hash = "sha256:f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"}, + {file = "zipp-3.6.0-py3-none-any.whl", hash = "sha256:9fe5ea21568a0a70e50f273397638d39b03353731e6cbbb3fd8502a33fec40bc"}, + {file = "zipp-3.6.0.tar.gz", hash = "sha256:71c644c5369f4a6e07636f0aa966270449561fcea2e3d6747b8d23efaa9d7832"}, ] diff --git a/pyproject.toml b/pyproject.toml index fc944f1..5b675a5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,12 +23,13 @@ packages = [ # Used for local development nautobot = { version = "*", optional = true, allow-prereleases = true } nautobot-chatops = "*" -python = "^3.6" +python = "^3.6.2" pan-os-python = "^1.3.0" defusedxml = "^0.7.1" requests = "^2.26.0" netmiko = "^3.4.0" ipaddr = "^2.2.0" +netutils = "^0.2.1" [tool.poetry.dev-dependencies] bandit = "*" diff --git a/tasks.py b/tasks.py index 3d1660f..b02efa6 100644 --- a/tasks.py +++ b/tasks.py @@ -47,7 +47,7 @@ def is_truthy(arg): "docker-compose.requirements.yml", "docker-compose.base.yml", "docker-compose.dev.yml", - "docker-compose.docs.yml", + "docker-compose.celery.yml", "docker-compose.mattermost.yml", ], } @@ -297,6 +297,17 @@ def pylint(context): run_command(context, command) +@task +def yamllint(context): + """Run yamllint to validate formating adheres to NTC defined YAML standards. + + Args: + context (obj): Used to run specific commands + """ + command = "yamllint . --format standard" + run_command(context, command) + + @task def pydocstyle(context): """Run pydocstyle to validate docstring formatting adheres to NTC defined standards.""" @@ -365,6 +376,8 @@ def tests(context, failfast=False): black(context) print("Running flake8...") flake8(context) + print("Running yamllint...") + yamllint(context) print("Running bandit...") bandit(context) print("Running pydocstyle...")