├── .dockerignore ├── .github ├── container-matrix.yml ├── matrix.yml └── workflows │ ├── cicd.yml │ ├── container-build.yml │ └── release-event.json ├── .gitignore ├── .pylintrc ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── README.md ├── galaxy.yml ├── meta └── runtime.yml ├── plugins ├── __init__.py ├── httpapi │ ├── __init__.py │ └── srlinux.py ├── module_utils │ ├── __init__.py │ ├── const.py │ └── srlinux.py └── modules │ ├── __init__.py │ ├── cli.py │ ├── config.py │ ├── get.py │ └── validate.py ├── pyproject.toml ├── run.sh ├── scripts ├── oc.cfg └── topo.clab.yml ├── tests ├── ansible.cfg ├── ci-ansible.cfg ├── hosts ├── playbooks │ ├── auth-fail.yml │ ├── backup-cfg.yml │ ├── cli-put-file.yml │ ├── cli-show-version.yml │ ├── cli-wrong-cmd.yml │ ├── delete-leaves.yml │ ├── get-container.yml │ ├── get-multiple-paths.yml │ ├── get-oc-container.yml │ ├── get-wrong-path.yml │ ├── golden │ │ ├── clab-ansible-srl-golden.cfg.json.j2 │ │ ├── clab-ansible-srl.cfg.json │ │ └── clab-ansible-srl.cfg.yml │ ├── oc-validate.yml │ ├── replace-full-cfg.yml │ ├── replace-json-rpc-cfg.yml │ ├── set-check.yml │ ├── set-confirm-timeout.yml │ ├── set-idempotent.yml │ ├── set-interface.yml │ ├── set-leaves-twice.yml │ ├── set-leaves.yml │ ├── set-multiple-paths.yml │ ├── set-oc-leaf.yml │ ├── set-tools.yml │ ├── set-wrong-value.yml │ ├── templates │ │ └── json-rpc-cfg.yml.j2 │ ├── tls-missed-check-fail.yml │ ├── tls-skipped-check.yml │ ├── tls-with-custom-ca.yml │ └── validate.yml └── sanity │ ├── ignore-2.10.txt │ ├── ignore-2.11.txt │ ├── ignore-2.12.txt │ ├── ignore-2.13.txt │ ├── ignore-2.14.txt │ ├── ignore-2.15.txt │ ├── ignore-2.16.txt │ └── ignore-2.17.txt └── uv.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | **/*.log 2 | -------------------------------------------------------------------------------- /.github/container-matrix.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | # a matrix list of variables used in the container build process 6 | # it is used to define what ansible core images that define both ansible core and the python interpreter 7 | # are going to be used in the container build process for the collection. 8 | # 9 | # python version are taken from ansible's support matrix - https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html#support-life 10 | # ansible-core-image version is taken from https://github.com/orgs/srl-labs/packages?repo_name=ansible-core 11 | include: 12 | # 2.14 13 | - ansible-core-image: "2.14.17:pypy3.10" 14 | runs-on: "ubuntu-22.04" 15 | 16 | - ansible-core-image: "2.14.17:py3.11" 17 | runs-on: "ubuntu-22.04" 18 | addional-tags: "latest" 19 | 20 | # 2.15 21 | - ansible-core-image: "2.15.12:pypy3.10" 22 | runs-on: "ubuntu-22.04" 23 | 24 | - ansible-core-image: "2.15.12:py3.11" 25 | runs-on: "ubuntu-22.04" 26 | 27 | - ansible-core-image: "2.15.12:py3.12" 28 | runs-on: "ubuntu-22.04" 29 | addional-tags: "latest" 30 | 31 | # 2.16 32 | - ansible-core-image: "2.16.8:pypy3.10" 33 | runs-on: "ubuntu-22.04" 34 | 35 | - ansible-core-image: "2.16.8:py3.11" 36 | runs-on: "ubuntu-22.04" 37 | 38 | - ansible-core-image: "2.16.8:py3.12" 39 | runs-on: "ubuntu-22.04" 40 | addional-tags: "latest" 41 | 42 | # 2.17 43 | - ansible-core-image: "2.17.1:pypy3.10" 44 | runs-on: "ubuntu-22.04" 45 | 46 | - ansible-core-image: "2.17.1:py3.11" 47 | runs-on: "ubuntu-22.04" 48 | 49 | - ansible-core-image: "2.17.1:py3.12" 50 | runs-on: "ubuntu-22.04" 51 | addional-tags: "latest" 52 | -------------------------------------------------------------------------------- /.github/matrix.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | # a matrix list of variables used in testing 6 | # kind of a manual way of creating a testing matrix with a flexibility of selecting permutations 7 | # support matrix for ansible control node - https://docs.ansible.com/ansible/latest/installation_guide/intro_installation.html#node-requirement-summary 8 | 9 | include: 10 | #################### 11 | # Python 3.9 12 | #################### 13 | - &latest-2_14 14 | ansible-core-version: "2.14.17" 15 | runs-on: "ubuntu-22.04" 16 | python-version: "3.9" 17 | 18 | - &latest-2_15 19 | ansible-core-version: "2.15.12" 20 | runs-on: "ubuntu-22.04" 21 | python-version: "3.9" 22 | 23 | #################### 24 | # Python 3.10 25 | #################### 26 | 27 | - <<: *latest-2_14 28 | python-version: "3.10" 29 | 30 | - <<: *latest-2_15 31 | python-version: "3.10" 32 | 33 | - &latest-2_16 34 | ansible-core-version: "2.16.8" 35 | runs-on: "ubuntu-22.04" 36 | python-version: "3.10" 37 | 38 | - &latest-2_17 39 | ansible-core-version: "2.17.1" 40 | runs-on: "ubuntu-22.04" 41 | python-version: "3.10" 42 | 43 | #################### 44 | # Python 3.11 45 | #################### 46 | 47 | - <<: *latest-2_14 48 | python-version: "3.11" 49 | 50 | - <<: *latest-2_15 51 | python-version: "3.11" 52 | 53 | - <<: *latest-2_16 54 | python-version: "3.11" 55 | 56 | - <<: *latest-2_17 57 | python-version: "3.11" 58 | 59 | #################### 60 | # Python 3.12 61 | #################### 62 | - <<: *latest-2_15 63 | python-version: "3.12" 64 | 65 | - <<: *latest-2_16 66 | python-version: "3.12" 67 | 68 | - <<: *latest-2_17 69 | python-version: "3.12" 70 | -------------------------------------------------------------------------------- /.github/workflows/cicd.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | --- 6 | name: CICD 7 | "on": 8 | workflow_dispatch: 9 | inputs: 10 | srlinux-version: 11 | description: "SR Linux version" 12 | required: true 13 | default: "24.3.2" 14 | start-tmate-before-test: 15 | description: "start tmate before running tests" 16 | type: boolean 17 | required: false 18 | default: false 19 | start-tmate-after-test: 20 | description: "start tmate after running tests" 21 | type: boolean 22 | required: false 23 | default: false 24 | pull_request: 25 | push: 26 | tags: 27 | - "v*" 28 | 29 | jobs: 30 | prepare-matrix: 31 | runs-on: ubuntu-22.04 32 | outputs: 33 | matrix: ${{ steps.set-matrix.outputs.matrix }} 34 | steps: 35 | - uses: actions/checkout@v4 36 | - uses: fabasoad/data-format-converter-action@v1 37 | id: converter 38 | with: 39 | source-pattern: ".github/matrix.yml" 40 | from: "yaml" 41 | to: "json" 42 | - id: set-matrix 43 | run: echo "matrix=$(jq -c . < ${{ steps.converter.outputs.result-path }}/matrix.json )" >> $GITHUB_OUTPUT 44 | 45 | test: 46 | runs-on: ${{ matrix.runs-on }} 47 | needs: 48 | - prepare-matrix 49 | strategy: 50 | fail-fast: false 51 | matrix: ${{ fromJson(needs.prepare-matrix.outputs.matrix) }} 52 | steps: 53 | # setting env vars for the test based in user input 54 | - name: Set Env vars 55 | run: | 56 | echo "SRLINUX_VERSION=${{ inputs.srlinux-version }}" >> $GITHUB_ENV 57 | 58 | - uses: actions/checkout@v4 59 | 60 | - uses: actions/setup-python@v5 61 | with: 62 | python-version: ${{ matrix.python-version }} 63 | 64 | - name: Install ansible core 65 | run: pip install ansible-core==${{ matrix.ansible-core-version }} 66 | 67 | - name: start tmate session 68 | uses: mxschmitt/action-tmate@v3 69 | if: ${{ inputs.start-tmate-before-test }} 70 | 71 | # Uncomment this section to use private images 72 | # - name: ghcr.io login 73 | # uses: docker/login-action@v2 74 | # with: 75 | # registry: ghcr.io 76 | # username: ${{ github.actor }} 77 | # password: ${{ secrets.GITHUB_TOKEN }} 78 | - name: Test 79 | env: 80 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 81 | run: ./run.sh ci-test 82 | # uncomment this line when you want to continue on error 83 | # and run tmate after it 84 | # continue-on-error: true 85 | 86 | - name: start tmate session 87 | uses: mxschmitt/action-tmate@v3 88 | if: ${{ inputs.start-tmate-after-test }} 89 | 90 | ansible-sanity-test: 91 | runs-on: ubuntu-latest 92 | steps: 93 | - uses: actions/checkout@v4 94 | 95 | - uses: actions/setup-python@v5 96 | with: 97 | python-version: 3.11 98 | 99 | - name: Install ansible core 100 | run: pip install ansible-core==2.15.12 101 | 102 | - name: Ansible sanity test 103 | run: ./run.sh sanity-test 104 | -------------------------------------------------------------------------------- /.github/workflows/container-build.yml: -------------------------------------------------------------------------------- 1 | name: Container build 2 | "on": 3 | release: 4 | types: 5 | - released 6 | workflow_dispatch: 7 | 8 | jobs: 9 | prepare-matrix: 10 | runs-on: ubuntu-22.04 11 | outputs: 12 | matrix: ${{ steps.set-matrix.outputs.matrix }} 13 | steps: 14 | - name: Dump GitHub context 15 | env: 16 | GITHUB_CONTEXT: ${{ toJson(github) }} 17 | run: | 18 | echo "$GITHUB_CONTEXT" 19 | - uses: actions/checkout@v4 20 | - uses: fabasoad/data-format-converter-action@v1 21 | id: converter 22 | with: 23 | source-pattern: ".github/container-matrix.yml" 24 | from: "yaml" 25 | to: "json" 26 | - id: set-matrix 27 | run: echo "matrix=$(jq -c . < ${{ steps.converter.outputs.result-path }}/container-matrix.json )" >> $GITHUB_OUTPUT 28 | 29 | build: 30 | name: Build container 31 | runs-on: ubuntu-22.04 32 | needs: prepare-matrix 33 | strategy: 34 | fail-fast: false 35 | matrix: ${{ fromJson(needs.prepare-matrix.outputs.matrix) }} 36 | 37 | steps: 38 | - name: Dump GitHub context 39 | env: 40 | GITHUB_CONTEXT: ${{ toJson(github) }} 41 | run: | 42 | echo "$GITHUB_CONTEXT" 43 | 44 | - name: Checkout 45 | uses: actions/checkout@v4 46 | 47 | - name: Set up Docker Buildx 48 | uses: docker/setup-buildx-action@v3 49 | 50 | - name: Process ansible core image version 51 | id: core-image 52 | run: | 53 | CORE_VERSION=$(./run.sh _transformAnsibleCoreVersion "${{ matrix.ansible-core-image }}") 54 | echo "core-version=$CORE_VERSION" >> "$GITHUB_OUTPUT" 55 | 56 | - name: Docker meta 57 | id: meta 58 | uses: docker/metadata-action@v5 59 | with: 60 | images: | 61 | ghcr.io/${{ github.repository }}/${{ steps.core-image.outputs.core-version }} 62 | tags: | 63 | type=ref,event=tag 64 | type=ref,event=branch 65 | # git short commit 66 | type=sha 67 | 68 | - name: Build 69 | uses: docker/build-push-action@v6 70 | with: 71 | context: . 72 | load: true 73 | build-args: | 74 | BASE_IMAGE=ghcr.io/srl-labs/ansible-core/${{ matrix.ansible-core-image }} 75 | tags: app:latest 76 | 77 | - name: Test 78 | run: | 79 | sudo docker run --rm app:latest ansible-galaxy collection list | grep -q 'nokia.srlinux' || exit 1 80 | 81 | - name: Login to GitHub Container Registry 82 | if: "!github.event.act" 83 | uses: docker/login-action@v3 84 | with: 85 | registry: ghcr.io 86 | username: ${{ github.repository_owner }} 87 | password: ${{ secrets.GITHUB_TOKEN }} 88 | 89 | - name: Set up QEMU 90 | if: "!github.event.act" 91 | uses: docker/setup-qemu-action@v3 92 | 93 | - name: Build and Push 94 | if: "!github.event.act" 95 | uses: docker/build-push-action@v6 96 | with: 97 | context: . 98 | push: true 99 | platforms: linux/amd64,linux/arm64 100 | build-args: | 101 | BASE_IMAGE=ghcr.io/srl-labs/ansible-core/${{ matrix.ansible-core-image }} 102 | tags: ${{ steps.meta.outputs.tags }} 103 | -------------------------------------------------------------------------------- /.github/workflows/release-event.json: -------------------------------------------------------------------------------- 1 | { 2 | "act": true, 3 | "action": "created", 4 | "ref": "refs/tags/v0.0.1", 5 | "sha": "b7e12928f13caf61af40d3e8788649a1a8f24c22", 6 | "release": { 7 | "tag_name": "v0.0.1" 8 | } 9 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.toptal.com/developers/gitignore/api/ansible,python 2 | # Edit at https://www.toptal.com/developers/gitignore?templates=ansible,python 3 | 4 | ### VS Code ### 5 | .vscode/* 6 | 7 | ### Private folder ### 8 | private/ 9 | 10 | # ignore build artifacts 11 | *.tar.gz 12 | 13 | ### containerlab ### 14 | **/clab-*/ 15 | *.clab.yml.bak 16 | 17 | ### Ansible ### 18 | *.retry 19 | 20 | ### Python ### 21 | # Byte-compiled / optimized / DLL files 22 | __pycache__/ 23 | *.py[cod] 24 | *$py.class 25 | 26 | # C extensions 27 | *.so 28 | 29 | # Distribution / packaging 30 | .Python 31 | build/ 32 | develop-eggs/ 33 | dist/ 34 | downloads/ 35 | eggs/ 36 | .eggs/ 37 | lib/ 38 | lib64/ 39 | parts/ 40 | sdist/ 41 | var/ 42 | wheels/ 43 | share/python-wheels/ 44 | *.egg-info/ 45 | .installed.cfg 46 | *.egg 47 | MANIFEST 48 | 49 | # PyInstaller 50 | # Usually these files are written by a python script from a template 51 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 52 | *.manifest 53 | *.spec 54 | 55 | # Installer logs 56 | pip-log.txt 57 | pip-delete-this-directory.txt 58 | 59 | # Unit test / coverage reports 60 | htmlcov/ 61 | .tox/ 62 | .nox/ 63 | .coverage 64 | .coverage.* 65 | .cache 66 | nosetests.xml 67 | coverage.xml 68 | *.cover 69 | *.py,cover 70 | .hypothesis/ 71 | .pytest_cache/ 72 | cover/ 73 | 74 | # Translations 75 | *.mo 76 | *.pot 77 | 78 | # Django stuff: 79 | *.log 80 | local_settings.py 81 | db.sqlite3 82 | db.sqlite3-journal 83 | 84 | # Flask stuff: 85 | instance/ 86 | .webassets-cache 87 | 88 | # Scrapy stuff: 89 | .scrapy 90 | 91 | # Sphinx documentation 92 | docs/_build/ 93 | 94 | # PyBuilder 95 | .pybuilder/ 96 | target/ 97 | 98 | # Jupyter Notebook 99 | .ipynb_checkpoints 100 | 101 | # IPython 102 | profile_default/ 103 | ipython_config.py 104 | 105 | # pyenv 106 | # For a library or package, you might want to ignore these files since the code is 107 | # intended to run in multiple environments; otherwise, check them in: 108 | .python-version 109 | 110 | # pipenv 111 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 112 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 113 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 114 | # install all needed dependencies. 115 | #Pipfile.lock 116 | 117 | # poetry 118 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 119 | # This is especially recommended for binary packages to ensure reproducibility, and is more 120 | # commonly ignored for libraries. 121 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 122 | #poetry.lock 123 | 124 | # pdm 125 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 126 | #pdm.lock 127 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 128 | # in version control. 129 | # https://pdm.fming.dev/#use-with-ide 130 | .pdm.toml 131 | 132 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 133 | __pypackages__/ 134 | 135 | # Celery stuff 136 | celerybeat-schedule 137 | celerybeat.pid 138 | 139 | # SageMath parsed files 140 | *.sage.py 141 | 142 | # Environments 143 | .env 144 | .venv 145 | env/ 146 | venv/ 147 | ENV/ 148 | env.bak/ 149 | venv.bak/ 150 | 151 | # Spyder project settings 152 | .spyderproject 153 | .spyproject 154 | 155 | # Rope project settings 156 | .ropeproject 157 | 158 | # mkdocs documentation 159 | /site 160 | 161 | # mypy 162 | .mypy_cache/ 163 | .dmypy.json 164 | dmypy.json 165 | 166 | # Pyre type checker 167 | .pyre/ 168 | 169 | # pytype static type analyzer 170 | .pytype/ 171 | 172 | # Cython debug symbols 173 | cython_debug/ 174 | 175 | # PyCharm 176 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 177 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 178 | # and can be added to the global gitignore or merged into this file. For a more nuclear 179 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 180 | #.idea/ 181 | 182 | ### Python Patch ### 183 | # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration 184 | poetry.toml 185 | 186 | # ruff 187 | .ruff_cache/ 188 | 189 | # End of https://www.toptal.com/developers/gitignore/api/ansible,python 190 | 191 | .envrc -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [BASIC] 2 | good-names= 3 | i, 4 | e, 5 | logger -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | * [Changelog](#changelog) 4 | * [v1.0.0](#v100) 5 | * [New modules](#new-modules) 6 | * [v1.0.2](#v102) 7 | * [Bug fixes](#bug-fixes) 8 | * [v1.0.3](#v103) 9 | * [Chores](#chores) 10 | 11 | ## v1.0.0 12 | 13 | ### New modules 14 | 15 | * nokia.srlinux.get - Retrieve configuration or state element from Nokia SR Linux devices. 16 | * nokia.srlinux.config - Update, replace and delete configuration on SR Linux devices. 17 | * nokia.srlinux.validate - Validating configuration on SR Linux devices. 18 | * nokia.srlinux.cli - Execute CLI commands on SR Linux devices. 19 | 20 | ## v1.0.2 21 | 22 | ### Bug fixes 23 | 24 | * Retry on `save_when` when the json-rpc is undergoing a reload. This happens when the replace/update operation changes json-rpc server config. 25 | 26 | ## v1.0.3 27 | 28 | ### Chores 29 | 30 | * Removed unused artifacts from the build. 31 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Refer to the [Ansible community guide](https://docs.ansible.com/ansible/devel/community/index.html). 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BASE_IMAGE=ghcr.io/srl-labs/ansible-core/2.14.10:pypy3.10 2 | 3 | FROM ${BASE_IMAGE} as builder 4 | 5 | COPY . /work 6 | 7 | WORKDIR /work 8 | 9 | RUN ansible-galaxy collection install --no-cache .. 10 | 11 | FROM ${BASE_IMAGE} 12 | 13 | COPY --from=builder /root/.ansible/collections /root/.ansible/collections 14 | 15 | WORKDIR /ansible 16 | 17 | CMD [ "ansible-playbook" ] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2023, Nokia 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | 8 | 1. Redistributions of source code must retain the above copyright notice, this 9 | list of conditions and the following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above copyright notice, 12 | this list of conditions and the following disclaimer in the documentation 13 | and/or other materials provided with the distribution. 14 | 15 | 3. Neither the name of the copyright holder nor the names of its 16 | contributors may be used to endorse or promote products derived from 17 | this software without specific prior written permission. 18 | 19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SR Linux Ansible Collection 2 | 3 | Ansible Collection to manage Nokia SR Linux devices. Documentation is provided at 4 | 5 | ## Quick start for developers 6 | 7 | Start with cloning the repo: 8 | 9 | ```bash 10 | git clone git@github.com:nokia/srlinux-ansible-collection.git 11 | cd srlinux-ansible-collection 12 | ``` 13 | 14 | Deploy the lab to support the tests: 15 | 16 | ```bash 17 | ./run.sh deploy-lab 18 | ``` 19 | 20 | Run the automated suite of tests to make sure nothing is missing. This will also prepare a dev environment (you have to make sure the venv with ansible is activated or ansible-playbook is in your path): 21 | 22 | ```bash 23 | ./run.sh test 24 | ``` 25 | 26 | To validate that the code passes Ansible's sanity check, run: 27 | 28 | ```bash 29 | ./run.sh sanity-test 30 | ``` 31 | 32 | ### Running individual tests 33 | 34 | To run an individual test, first make sure that the local code base is used by ansible. This can be done by running: 35 | 36 | ```bash 37 | ./run.sh prepare-dev-env 38 | ``` 39 | 40 | Then either run one of the provided test playbooks (defined in [run.sh](run.sh)) or run your own: 41 | 42 | ```bash 43 | ./run.sh test-set-leaves 44 | ``` 45 | 46 | ## Contributing to this collection 47 | 48 | The content of this collection is made by people like you, a community of individuals collaborating on making the world better through developing automation software. 49 | 50 | We are actively accepting new contributors and all types of contributions are very welcome. 51 | 52 | Don't know how to start? Refer to the [Ansible community guide](https://docs.ansible.com/ansible/devel/community/index.html)! 53 | 54 | Want to submit code changes? Take a look at the [Quick-start development guide](https://docs.ansible.com/ansible/devel/community/create_pr_quick_start.html). 55 | 56 | We also use the following guidelines: 57 | 58 | * [Collection review checklist](https://docs.ansible.com/ansible/devel/community/collection_contributors/collection_reviewing.html) 59 | * [Ansible development guide](https://docs.ansible.com/ansible/devel/dev_guide/index.html) 60 | * [Ansible collection development guide](https://docs.ansible.com/ansible/devel/dev_guide/developing_collections.html#contributing-to-collections) 61 | 62 | ## Code of Conduct 63 | 64 | We follow the [Ansible Code of Conduct](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html) in all our interactions within this project. 65 | 66 | If you encounter abusive behavior, please refer to the [policy violations](https://docs.ansible.com/ansible/devel/community/code_of_conduct.html#policy-violations) section of the Code for information on how to raise a complaint. 67 | 68 | ## Communication 69 | 70 | Join the Ansible forum: 71 | 72 | * [Get Help](https://forum.ansible.com/c/help/6): get help or help others. Please add appropriate tags if you start new discussions. 73 | * [Posts tagged with 'srlinux'](https://forum.ansible.com/tag/srlinux): subscribe to participate in SR Linux Ansible collection/technology-related conversations. 74 | * [Social Spaces](https://forum.ansible.com/c/chat/4): gather and interact with fellow enthusiasts. 75 | * [News & Announcements](https://forum.ansible.com/c/news/5): track project-wide announcements including social events. The [Bullhorn newsletter](https://docs.ansible.com/ansible/devel/community/communication.html#the-bullhorn), which is used to announce releases and important changes, can also be found here. 76 | 77 | ## Licensing 78 | 79 | BSD 3-Clause License 80 | 81 | See [LICENSE](LICENSE) to see the full text. 82 | -------------------------------------------------------------------------------- /galaxy.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | ### REQUIRED 6 | # The namespace of the collection. This can be a company/brand/organization or product namespace under which all 7 | # content lives. May only contain alphanumeric lowercase characters and underscores. Namespaces cannot start with 8 | # underscores or numbers and cannot contain consecutive underscores 9 | namespace: nokia 10 | 11 | # The name of the collection. Has the same character restrictions as 'namespace' 12 | name: srlinux 13 | 14 | # The version of the collection. Must be compatible with semantic versioning 15 | version: 1.0.3 16 | 17 | # The path to the Markdown (.md) readme file. This path is relative to the root of the collection 18 | readme: README.md 19 | 20 | # A list of the collection's content authors. Can be just the name or in the format 'Full Name (url) 21 | # @nicks:irc/im.site#channel' 22 | authors: 23 | - Roman Dodin 24 | - Patrick Dumais 25 | - Walter De Smedt 26 | 27 | ### OPTIONAL but strongly recommended 28 | # A short summary description of the collection 29 | description: Ansible collection for Nokia SR Linux Network OS. 30 | 31 | # The path to the license file for the collection. This path is relative to the root of the collection. This key is 32 | # mutually exclusive with 'license' 33 | license_file: "LICENSE" 34 | 35 | # A list of tags you want to associate with the collection for indexing/searching. A tag name has the same character 36 | # requirements as 'namespace' and 'name' 37 | tags: 38 | - nokia 39 | - srlinux 40 | - jsonrpc 41 | - httpapi 42 | - networking 43 | 44 | # Collections that this collection requires to be installed for it to be usable. The key of the dict is the 45 | # collection label 'namespace.name'. The value is a version range 46 | # L(specifiers,https://python-semanticversion.readthedocs.io/en/latest/#requirement-specification). Multiple version 47 | # range specifiers can be set and are separated by ',' 48 | # fixed dependencies on ansible.netcommon and ansible.utils since newer versions drop support for ansible core 2.14. We can go to the latest version of these deps later 49 | dependencies: 50 | "ansible.netcommon": ">=5.2.0,<=6.1.3" 51 | "ansible.utils": ">=3.0.0,<5.0.0" 52 | 53 | # The URL of the originating SCM repository 54 | repository: https://github.com/nokia/srlinux-ansible-collection 55 | 56 | # The URL to the collection issue tracker 57 | issues: https://github.com/nokia/srlinux-ansible-collection/issues 58 | 59 | # The URL to any online docs 60 | documentation: https://learn.srlinux.dev/ansible/collection/ 61 | 62 | # A list of file glob-like patterns used to filter any files or directories that should not be included in the build 63 | # artifact. A pattern is matched from the relative path of the file or directory of the collection directory. This 64 | # uses 'fnmatch' to match the files or directories. Some directories and files like 'galaxy.yml', '*.pyc', '*.retry', 65 | # and '.git' are always filtered 66 | build_ignore: 67 | - scripts 68 | - run.sh 69 | - .github 70 | - .vscode 71 | - .venv 72 | - .env 73 | - .envrc 74 | - .gitignore 75 | - private 76 | - .dockerignore 77 | -------------------------------------------------------------------------------- /meta/runtime.yml: -------------------------------------------------------------------------------- 1 | requires_ansible: ">=2.15.0" 2 | plugin_routing: 3 | modules: 4 | srl_config: 5 | redirect: nokia.srlinux.config 6 | srl_get: 7 | redirect: nokia.srlinux.get 8 | -------------------------------------------------------------------------------- /plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nokia/srlinux-ansible-collection/ee912feac4f1d0b35a2f8e3ffb91558f77fc4d2a/plugins/__init__.py -------------------------------------------------------------------------------- /plugins/httpapi/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nokia/srlinux-ansible-collection/ee912feac4f1d0b35a2f8e3ffb91558f77fc4d2a/plugins/httpapi/__init__.py -------------------------------------------------------------------------------- /plugins/httpapi/srlinux.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | """Module for http api base functionality.""" 6 | from __future__ import absolute_import, division, print_function 7 | 8 | import json 9 | 10 | __metaclass__ = type # pylint: disable=invalid-name 11 | 12 | from json.decoder import JSONDecodeError 13 | from urllib.error import HTTPError 14 | 15 | from ansible.errors import AnsibleConnectionFailure 16 | from ansible.module_utils.basic import to_text 17 | from ansible_collections.ansible.netcommon.plugins.plugin_utils.httpapi_base import ( 18 | HttpApiBase, 19 | ) 20 | 21 | DOCUMENTATION = """ 22 | --- 23 | name: srlinux 24 | short_description: HttpApi Plugin for Nokia SR Linux 25 | description: 26 | - This HttpApi plugin provides methods to connect to Nokia SR Linux over a HTTP(S)-based API. 27 | version_added: "0.1.0" 28 | author: 29 | - Patrick Dumais (@Nokia) 30 | - Roman Dodin (@Nokia) 31 | - Walter De Smedt (@Nokia) 32 | """ 33 | 34 | BASE_HEADERS = {"Content-Type": "application/json"} 35 | 36 | 37 | class HttpApi(HttpApiBase): 38 | """HttpApi plugin for Nokia SR Linux""" 39 | 40 | # pylint: disable=arguments-differ 41 | def send_request(self, data, method="POST", path="/jsonrpc"): 42 | try: 43 | self._display_request(data) 44 | response, response_data = self.connection.send( 45 | path, 46 | data, 47 | method=method, 48 | headers=BASE_HEADERS, 49 | force_basic_auth=True, 50 | ) 51 | 52 | return response.getcode(), self._response_to_json( 53 | to_text(response_data.getvalue()) 54 | ) 55 | except AnsibleConnectionFailure as e: 56 | self.connection.queue_message("vvv", f"AnsibleConnectionFailure: {e}") 57 | if to_text("Could not connect to") in to_text(e): 58 | raise 59 | if to_text("401") in to_text(e): 60 | return 401, "Authentication failure" 61 | return 404, "Object not found" 62 | except HTTPError as e: 63 | error = e.read() 64 | return e.code, error 65 | 66 | def _display_request(self, data): 67 | self.connection.queue_message("vvvv", f"HTTP Request data: {data}") 68 | 69 | def _response_to_json(self, response_text): 70 | try: 71 | return json.loads(response_text) if response_text else {} 72 | 73 | except JSONDecodeError as exc: 74 | raise ConnectionError(f"Invalid JSON response: {response_text}") from exc 75 | -------------------------------------------------------------------------------- /plugins/module_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nokia/srlinux-ansible-collection/ee912feac4f1d0b35a2f8e3ffb91558f77fc4d2a/plugins/module_utils/__init__.py -------------------------------------------------------------------------------- /plugins/module_utils/const.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | """Constants for the module.""" 6 | JSON_RPC_VERSION: str = "2.0" 7 | 8 | # text output format value 9 | TEXT_FORMAT: str = "text" 10 | 11 | # datastores 12 | TOOLS_DATASTORE: str = "tools" 13 | 14 | # configuration save path 15 | SAVE_CONFIG_PATH: str = "/system/configuration/save" 16 | -------------------------------------------------------------------------------- /plugins/module_utils/srlinux.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | """srlinux module utils""" 6 | # -*- coding: utf-8 -*- 7 | 8 | from __future__ import absolute_import, division, print_function 9 | 10 | from typing import Any 11 | 12 | # pylint: disable=invalid-name 13 | __metaclass__ = type 14 | import json 15 | from datetime import datetime 16 | from time import sleep 17 | 18 | from ansible.module_utils._text import to_text 19 | from ansible.module_utils.connection import Connection 20 | from ansible_collections.nokia.srlinux.plugins.module_utils.const import ( 21 | JSON_RPC_VERSION, 22 | SAVE_CONFIG_PATH, 23 | TOOLS_DATASTORE, 24 | ) 25 | 26 | 27 | class JSONRPCClient: 28 | """SRLinux JSON-RPC client""" 29 | 30 | def __init__(self, module=None): 31 | self.module = module 32 | if module: 33 | self.connection = Connection(self.module._socket_path) 34 | 35 | def _httpapi_error_handle(self, method="POST", path="/jsonrpc", payload=None): 36 | try: 37 | code, response = self.connection.send_request( 38 | data=payload, method=method, path=path 39 | ) 40 | 41 | if code == 404: 42 | if to_text("Object not found") in to_text(response) or to_text( 43 | "Could not find object" 44 | ) in to_text(response): 45 | return {} 46 | 47 | if not (200 <= code < 300): 48 | self.module.fail_json( 49 | msg=f"srlinux httpapi returned error {code} with message {to_text(response)}" 50 | ) 51 | 52 | return response 53 | 54 | except ConnectionError as e: 55 | self.module.fail_json( 56 | msg=f"connection error occurred: {e}", 57 | ) 58 | 59 | except ValueError as e: 60 | try: 61 | self.module.fail_json(msg=f"certificate not found: {e}") 62 | except AttributeError: 63 | pass 64 | 65 | def post(self, url="/jsonrpc", payload=None, **kwargs): 66 | """JSON-RPC POST request""" 67 | return self._httpapi_error_handle("POST", url, payload=payload, **kwargs) 68 | 69 | 70 | def convertIdentifiers(data): 71 | """Converts keys in the list of dicts to have dashes instead of underscores. 72 | 73 | This is needed, because the JSON-RPC API uses dashes in the keys, but the ansible linter does not allow them. 74 | """ 75 | if isinstance(data, list): 76 | for item in data: 77 | convertIdentifiers(item) 78 | elif isinstance(data, dict): 79 | for key, value in list(data.items()): 80 | if "_" in key: 81 | new_key = key.replace("_", "-") 82 | data[new_key] = data.pop(key) 83 | convertIdentifiers(value) 84 | 85 | 86 | def convertResponseKeys(response): 87 | """Converts keys in the response object in the following manner: 88 | `jsonrpc` -> `jsonrpc_version 89 | `id` -> `jsonrpc_req_id`""" 90 | if "jsonrpc" in response: 91 | response["jsonrpc_version"] = response.pop("jsonrpc") 92 | if "id" in response: 93 | response["jsonrpc_req_id"] = response.pop("id") 94 | 95 | 96 | def rpcID(): 97 | """Generates an id for the JSON-RPC request 98 | which follows the UTC datetime""" 99 | return datetime.now().strftime("%Y-%m-%d %H:%M:%S:%f") 100 | 101 | 102 | def process_save_when(client, json_output) -> Any: 103 | """Handle save_when operation""" 104 | data = { 105 | "jsonrpc": JSON_RPC_VERSION, 106 | "id": rpcID(), 107 | "method": "set", 108 | "params": { 109 | "datastore": TOOLS_DATASTORE, 110 | "commands": [ 111 | { 112 | "action": "update", 113 | "path": SAVE_CONFIG_PATH, 114 | }, 115 | ], 116 | }, 117 | } 118 | 119 | # in case the config changes caused the reload of the json rpc server, 120 | # we need to retry the save operation, as the first ones might fail due to the ongoing server reload 121 | retries = 5 122 | retry_delay = 2 # seconds between retries 123 | 124 | for attempt in range(retries): 125 | try: 126 | set_resp = client.post(payload=json.dumps(data)) 127 | if set_resp and set_resp.get("result"): 128 | json_output["saved"] = True 129 | break 130 | except ( 131 | Exception 132 | ): # You may want to catch specific exceptions based on what client.post raises 133 | if attempt < retries - 1: # Don't sleep on the last attempt 134 | sleep(retry_delay) 135 | continue 136 | 137 | return json_output 138 | -------------------------------------------------------------------------------- /plugins/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nokia/srlinux-ansible-collection/ee912feac4f1d0b35a2f8e3ffb91558f77fc4d2a/plugins/modules/__init__.py -------------------------------------------------------------------------------- /plugins/modules/cli.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright 2023 Nokia 3 | # Licensed under the BSD 3-Clause License. 4 | # SPDX-License-Identifier: BSD-3-Clause 5 | 6 | """Ansible module for executing CLI commands to SR Linux devices""" 7 | 8 | from __future__ import absolute_import, division, print_function 9 | 10 | import json 11 | 12 | from ansible.module_utils.basic import AnsibleModule 13 | from ansible_collections.nokia.srlinux.plugins.module_utils.const import ( 14 | JSON_RPC_VERSION, 15 | ) 16 | from ansible_collections.nokia.srlinux.plugins.module_utils.srlinux import ( 17 | JSONRPCClient, 18 | convertResponseKeys, 19 | rpcID, 20 | ) 21 | 22 | # pylint: disable=invalid-name 23 | __metaclass__ = type 24 | 25 | DOCUMENTATION = """ 26 | --- 27 | module: cli 28 | short_description: "Execute CLI commands on SR Linux devices." 29 | description: 30 | - This module allows CLI commands to be executed in Nokia SR Linux using JSON-RPC interface. 31 | version_added: "0.1.0" 32 | options: 33 | commands: 34 | description: 35 | - List of commands to run. 36 | type: list 37 | elements: str 38 | required: true 39 | output_format: 40 | description: 41 | - Output format. 42 | type: str 43 | choices: ["json", "text", "table"] 44 | default: json 45 | required: false 46 | 47 | author: 48 | - Patrick Dumais (@Nokia) 49 | - Roman Dodin (@Nokia) 50 | - Walter De Smedt (@Nokia) 51 | """ 52 | 53 | EXAMPLES = """ 54 | - name: Run \"show version\" CLI command 55 | nokia.srlinux.cli: 56 | commands: 57 | - show version 58 | register: response 59 | """ 60 | 61 | 62 | def main(): 63 | """Main function""" 64 | argspec = { 65 | "commands": { 66 | "type": "list", 67 | "elements": "str", 68 | "required": True, 69 | }, 70 | "output_format": { 71 | "type": "str", 72 | "choices": ["json", "text", "table"], 73 | "default": "json", 74 | }, 75 | } 76 | 77 | module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) 78 | 79 | client = JSONRPCClient(module) 80 | 81 | commands = module.params.get("commands") 82 | out_format = module.params.get("output_format") 83 | 84 | data = { 85 | "jsonrpc": JSON_RPC_VERSION, 86 | "id": rpcID(), 87 | "method": "cli", 88 | "params": {"commands": commands, "output-format": out_format}, 89 | } 90 | response = client.post(payload=json.dumps(data)) 91 | convertResponseKeys(response) 92 | 93 | if response and response.get("result"): 94 | module.exit_json(**response) 95 | 96 | # handling error case 97 | response["failed"] = True 98 | module.fail_json( 99 | msg=response["error"]["message"], 100 | jsonrpc_req_id=response["jsonrpc_req_id"], 101 | ) 102 | 103 | 104 | if __name__ == "__main__": 105 | main() 106 | -------------------------------------------------------------------------------- /plugins/modules/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright 2023 Nokia 3 | # Licensed under the BSD 3-Clause License. 4 | # SPDX-License-Identifier: BSD-3-Clause 5 | 6 | # -*- coding: utf-8 -*- 7 | """Ansible module for configuring SR Linux devices""" 8 | 9 | from __future__ import absolute_import, division, print_function 10 | 11 | import json 12 | 13 | from ansible.module_utils.basic import AnsibleModule 14 | from ansible_collections.nokia.srlinux.plugins.module_utils.const import ( 15 | JSON_RPC_VERSION, 16 | TEXT_FORMAT, 17 | TOOLS_DATASTORE, 18 | ) 19 | from ansible_collections.nokia.srlinux.plugins.module_utils.srlinux import ( 20 | JSONRPCClient, 21 | convertResponseKeys, 22 | process_save_when, 23 | rpcID, 24 | ) 25 | 26 | # pylint: disable=invalid-name 27 | __metaclass__ = type 28 | 29 | DOCUMENTATION = """ 30 | --- 31 | module: config 32 | short_description: "Update, replace and delete configuration on SR Linux devices." 33 | description: 34 | - This module allows to set a configuration or run operational transaction. The set method can be used with the candidate and tools datastores. 35 | version_added: "0.1.0" 36 | options: 37 | save_when: 38 | type: str 39 | description: 40 | - 'When to save running to startup config.' 41 | choices: ["always", "never", "changed"] 42 | default: never 43 | update: 44 | description: 45 | - Update operation. 46 | required: false 47 | type: list 48 | elements: dict 49 | suboptions: 50 | path: 51 | description: 52 | - targeted path for update operation 53 | type: str 54 | required: true 55 | value: 56 | type: raw 57 | description: 58 | - values to update 59 | delete: 60 | description: 61 | - Delete operation. 62 | required: false 63 | type: list 64 | elements: dict 65 | suboptions: 66 | path: 67 | description: 68 | - targeted path for delete operation 69 | type: str 70 | required: true 71 | replace: 72 | description: 73 | - replace operation. 74 | required: false 75 | type: list 76 | elements: dict 77 | suboptions: 78 | path: 79 | description: 80 | - targeted path for replace operation 81 | type: str 82 | required: true 83 | value: 84 | description: 85 | - values to replace 86 | required: true 87 | type: raw 88 | datastore: 89 | type: str 90 | description: 91 | - The datastore to use 92 | choices: 93 | - candidate 94 | - tools 95 | required: false 96 | default: candidate 97 | yang_models: 98 | type: str 99 | description: 100 | - YANG models to use for the get operation. 101 | choices: 102 | - srl 103 | - oc 104 | default: srl 105 | confirm_timeout: 106 | type: int 107 | description: 108 | - The number of seconds to wait for a confirmation before reverting the commit. 109 | author: 110 | - Patrick Dumais (@Nokia) 111 | - Roman Dodin (@Nokia) 112 | - Walter De Smedt (@Nokia) 113 | """ 114 | 115 | EXAMPLES = """ 116 | - name: Set system information with values 117 | nokia.srlinux.config: 118 | update: 119 | - path: /system/information 120 | value: 121 | location: Some location 122 | contact: Some contact 123 | """ 124 | 125 | 126 | def main(): 127 | """Main entrypoint for module execution""" 128 | argspec = { 129 | "update": { 130 | "type": "list", 131 | "elements": "dict", 132 | "required": False, 133 | "options": { 134 | "path": {"type": "str", "required": True}, 135 | "value": {"type": "raw"}, 136 | }, 137 | }, 138 | "delete": { 139 | "type": "list", 140 | "elements": "dict", 141 | "required": False, 142 | "options": { 143 | "path": {"type": "str", "required": True}, 144 | }, 145 | }, 146 | "replace": { 147 | "type": "list", 148 | "elements": "dict", 149 | "required": False, 150 | "options": { 151 | "path": {"type": "str", "required": True}, 152 | "value": {"type": "raw", "required": True}, 153 | }, 154 | }, 155 | "save_when": {"choices": ["always", "never", "changed"], "default": "never"}, 156 | "datastore": {"choices": ["candidate", "tools"], "default": "candidate"}, 157 | "yang_models": {"choices": ["srl", "oc"], "default": "srl"}, 158 | "confirm_timeout": {"type": "int"}, 159 | } 160 | 161 | module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) 162 | 163 | client = JSONRPCClient(module) 164 | 165 | # used to track if the module changed anything 166 | # default state is False 167 | changed = False 168 | 169 | json_output = {"changed": changed, "saved": False} 170 | 171 | save_when = module.params.get("save_when") 172 | updates = module.params.get("update") or [] 173 | deletes = module.params.get("delete") or [] 174 | replaces = module.params.get("replace") or [] 175 | datastore = module.params.get("datastore") 176 | yang_models = module.params.get("yang_models") 177 | confirm_timeout = module.params.get("confirm_timeout") 178 | 179 | # since operations are modelled as a dict in this collection 180 | # an ordering is implied when multiple operations are provided. 181 | # we add all deletes, followed by replaces, and then followed by updates. 182 | # this ordering should satisfy most use cases where multiple operations 183 | # are bundled in a single request, where deletes are processed first and then 184 | # replaces and updates can be used to enact new config values. 185 | commands = [] 186 | for obj in deletes: 187 | obj["action"] = "delete" 188 | commands += [obj] 189 | for obj in replaces: 190 | obj["action"] = "replace" 191 | commands += [obj] 192 | for obj in updates: 193 | obj["action"] = "update" 194 | commands += [obj] 195 | 196 | diff_resp = {} 197 | # if datastore is tools, collecting diff is a noop, as well as check and diff modes 198 | if datastore != TOOLS_DATASTORE: 199 | # collecting the diff 200 | data = { 201 | "jsonrpc": JSON_RPC_VERSION, 202 | "id": rpcID(), 203 | "method": "diff", 204 | "params": { 205 | "commands": commands, 206 | "output-format": TEXT_FORMAT, 207 | "yang-models": yang_models, 208 | }, 209 | } 210 | 211 | diff_resp = client.post(payload=json.dumps(data)) 212 | convertResponseKeys(diff_resp) 213 | 214 | # we should fail the module if no diff response is returned 215 | # or any errors were reported by it 216 | if not diff_resp or diff_resp.get("error"): 217 | diff_resp["failed"] = True 218 | msg = diff_resp.get("error", {}).get("message", "No diff response") 219 | module.fail_json(msg=msg, method="diff", id=diff_resp["jsonrpc_req_id"]) 220 | 221 | # if diff response is empty, the operation is a noop and we can exit the module 222 | if not [x for x in diff_resp.get("result") if x.strip() != ""]: 223 | json_output["jsonrpc_req_id"] = diff_resp["jsonrpc_req_id"] 224 | module.exit_json(**json_output) 225 | 226 | # if diff response is not empty, we have a diff 227 | # we need to set the changed flag to True 228 | # and save the diff response result in the json_output 229 | if diff_resp.get("result"): 230 | res = diff_resp["result"] 231 | # remove empty lines from the diff output 232 | res = [x for x in res if x != ""] 233 | if len(res): 234 | # save successful diff in the json_output 235 | # so that it is returned to the user 236 | json_output["diff"] = diff_resp 237 | changed = True 238 | 239 | # in check mode we just return the changed status 240 | if module.check_mode: 241 | json_output["changed"] = changed 242 | # prepare on-box diff to be printed for a user 243 | # see https://github.com/ansible/ansible/blob/stable-2.14/lib/ansible/plugins/callback/__init__.py#L344 # pylint: disable=line-too-long 244 | # for details on which fields are used 245 | # diff reports result as a list with one element that contains the full diff for all operations in the diff request 246 | if changed: 247 | json_output.update({"diff": {"prepared": diff_resp.get("result")[0]}}) 248 | module.exit_json(**json_output) 249 | 250 | # when not in check mode, we proceed with modifying the configuration 251 | data = { 252 | "jsonrpc": JSON_RPC_VERSION, 253 | "id": rpcID(), 254 | "method": "set", 255 | "params": { 256 | "commands": commands, 257 | "datastore": datastore, 258 | "yang-models": yang_models, 259 | }, 260 | } 261 | 262 | # add confirm timeout if provided 263 | if confirm_timeout: 264 | data["params"]["confirm-timeout"] = confirm_timeout 265 | 266 | set_resp = client.post(payload=json.dumps(data)) 267 | convertResponseKeys(set_resp) 268 | 269 | # failed to get set response means something went wrong 270 | # we have to fail the module 271 | if not set_resp: 272 | json_output["failed"] = True 273 | module.fail_json(**json_output) 274 | 275 | # we have a successful operation 276 | if set_resp.get("result"): 277 | # if diff mode was set without check mode, 278 | # we add the prepared diff to the output if diff response is not empty 279 | if module._diff and changed: # pylint: disable=protected-access 280 | json_output.update({"diff": {"prepared": diff_resp.get("result")[0]}}) 281 | json_output["changed"] = changed 282 | json_output["jsonrpc_req_id"] = set_resp["jsonrpc_req_id"] 283 | 284 | # saving configuration if needed 285 | if not module.check_mode and ( 286 | save_when == "always" or (save_when == "changed" and changed) 287 | ): 288 | json_output = process_save_when(client, json_output) 289 | 290 | module.exit_json(**json_output) 291 | 292 | # we have an error 293 | if set_resp.get("error"): 294 | json_output["failed"] = True 295 | module.fail_json( 296 | msg=set_resp["error"]["message"], id=set_resp["jsonrpc_req_id"] 297 | ) 298 | 299 | 300 | if __name__ == "__main__": 301 | main() 302 | -------------------------------------------------------------------------------- /plugins/modules/get.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright 2023 Nokia 3 | # Licensed under the BSD 3-Clause License. 4 | # SPDX-License-Identifier: BSD-3-Clause 5 | 6 | """Ansible module for retrieving configuration and state from SR Linux devices""" 7 | 8 | from __future__ import absolute_import, division, print_function 9 | 10 | import json 11 | 12 | from ansible.module_utils.basic import AnsibleModule 13 | from ansible_collections.nokia.srlinux.plugins.module_utils.const import ( 14 | JSON_RPC_VERSION, 15 | ) 16 | from ansible_collections.nokia.srlinux.plugins.module_utils.srlinux import ( 17 | JSONRPCClient, 18 | convertIdentifiers, 19 | convertResponseKeys, 20 | rpcID, 21 | ) 22 | 23 | # pylint: disable=invalid-name 24 | __metaclass__ = type 25 | 26 | DOCUMENTATION = """ 27 | --- 28 | module: get 29 | short_description: "Retrieve configuration or state element from Nokia SR Linux devices." 30 | description: 31 | - >- 32 | This module allows to retrieve configuration and state details from the system. 33 | The get method can be used with candidate, running, and state datastores, but cannot be used with the tools datastore. 34 | version_added: "0.1.0" 35 | options: 36 | paths: 37 | type: list 38 | elements: dict 39 | description: 40 | - List of paths to get data from. 41 | suboptions: 42 | datastore: 43 | type: str 44 | description: 45 | - The datastore to query 46 | choices: 47 | - baseline 48 | - candidate 49 | - running 50 | - state 51 | - tools 52 | default: state 53 | path: 54 | description: 55 | - the YANG path of a datastore node 56 | type: str 57 | required: true 58 | yang_models: 59 | type: str 60 | description: 61 | - YANG models to use for the get operation. 62 | choices: 63 | - srl 64 | - oc 65 | 66 | author: 67 | - Patrick Dumais (@Nokia) 68 | - Roman Dodin (@Nokia) 69 | - Walter De Smedt (@Nokia) 70 | """ 71 | 72 | EXAMPLES = """ 73 | - name: Get /system/information container 74 | nokia.srlinux.get: 75 | paths: 76 | - path: /system/information 77 | datastore: state 78 | """ 79 | 80 | 81 | def main(): 82 | """Main entrypoint for module execution""" 83 | 84 | argspec = { 85 | "paths": { 86 | "type": "list", 87 | "elements": "dict", 88 | "options": { 89 | "path": {"type": "str", "required": True}, 90 | "datastore": { 91 | "type": "str", 92 | "choices": ["baseline", "candidate", "running", "state", "tools"], 93 | "default": "state", 94 | }, 95 | "yang_models": { 96 | "type": "str", 97 | "choices": ["srl", "oc"], 98 | }, 99 | }, 100 | }, 101 | } 102 | 103 | module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) 104 | 105 | client = JSONRPCClient(module) 106 | 107 | paths = module.params.get("paths") 108 | convertIdentifiers(paths) 109 | 110 | data = { 111 | "jsonrpc": JSON_RPC_VERSION, 112 | "id": rpcID(), 113 | "method": "get", 114 | "params": { 115 | "commands": paths, 116 | }, 117 | } 118 | 119 | response = client.post(payload=json.dumps(data)) 120 | convertResponseKeys(response) 121 | 122 | if response and response.get("result"): 123 | module.exit_json(**response) 124 | 125 | # handling error case 126 | response["failed"] = True 127 | module.fail_json( 128 | msg=response["error"]["message"], 129 | jsonrpc_req_id=response["jsonrpc_req_id"], 130 | ) 131 | 132 | 133 | if __name__ == "__main__": 134 | main() 135 | -------------------------------------------------------------------------------- /plugins/modules/validate.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | """Ansible module for validating configuration on SR Linux devices""" 6 | 7 | from __future__ import absolute_import, division, print_function 8 | 9 | import json 10 | 11 | from ansible.module_utils.basic import AnsibleModule 12 | from ansible_collections.nokia.srlinux.plugins.module_utils.const import ( 13 | JSON_RPC_VERSION, 14 | ) 15 | from ansible_collections.nokia.srlinux.plugins.module_utils.srlinux import ( 16 | JSONRPCClient, 17 | convertResponseKeys, 18 | rpcID, 19 | ) 20 | 21 | # pylint: disable=invalid-name 22 | __metaclass__ = type 23 | 24 | DOCUMENTATION = """ 25 | --- 26 | module: validate 27 | short_description: "Validating configuration on SR Linux devices." 28 | description: 29 | - Validating configuration on SR Linux devices using `commit validate` feature of SR Linux. 30 | version_added: "0.1.0" 31 | options: 32 | update: 33 | description: 34 | - Update operations to validate. 35 | required: false 36 | type: list 37 | elements: dict 38 | suboptions: 39 | path: 40 | description: 41 | - targeted path for update operation 42 | type: str 43 | required: true 44 | value: 45 | type: raw 46 | description: 47 | - values to update 48 | required: true 49 | delete: 50 | description: 51 | - Delete operations to validate. 52 | required: false 53 | type: list 54 | elements: dict 55 | suboptions: 56 | path: 57 | description: 58 | - targeted path for delete operation 59 | type: str 60 | required: true 61 | replace: 62 | description: 63 | - Replace operations to validate. 64 | required: false 65 | type: list 66 | elements: dict 67 | suboptions: 68 | path: 69 | description: 70 | - targeted path for replace operation 71 | type: str 72 | required: true 73 | value: 74 | description: 75 | - values to replace 76 | required: true 77 | type: raw 78 | yang_models: 79 | type: str 80 | description: 81 | - YANG models to use for the get operation. 82 | choices: 83 | - srl 84 | - oc 85 | default: srl 86 | 87 | author: 88 | - Patrick Dumais (@Nokia) 89 | - Roman Dodin (@Nokia) 90 | - Walter De Smedt (@Nokia) 91 | """ 92 | 93 | EXAMPLES = """ 94 | - name: Validate a valid change set 95 | nokia.srlinux.validate: 96 | update: 97 | - path: /system/information 98 | value: 99 | location: Some location 100 | contact: Some contact 101 | """ 102 | 103 | 104 | def main(): 105 | """Main function""" 106 | argspec = { 107 | "update": { 108 | "type": "list", 109 | "elements": "dict", 110 | "required": False, 111 | "options": { 112 | "path": {"type": "str", "required": True}, 113 | "value": {"type": "raw", "required": True}, 114 | }, 115 | }, 116 | "delete": { 117 | "type": "list", 118 | "elements": "dict", 119 | "required": False, 120 | "options": { 121 | "path": {"type": "str", "required": True}, 122 | }, 123 | }, 124 | "replace": { 125 | "type": "list", 126 | "elements": "dict", 127 | "required": False, 128 | "options": { 129 | "path": {"type": "str", "required": True}, 130 | "value": {"type": "raw", "required": True}, 131 | }, 132 | }, 133 | "yang_models": {"choices": ["srl", "oc"], "default": "srl"}, 134 | } 135 | 136 | module = AnsibleModule(argument_spec=argspec, supports_check_mode=True) 137 | 138 | client = JSONRPCClient(module) 139 | 140 | updates = module.params.get("update") or [] 141 | deletes = module.params.get("deletes") or [] 142 | replaces = module.params.get("replace") or [] 143 | yang_models = module.params.get("yang_models") 144 | 145 | commands = [] 146 | for obj in updates: 147 | obj["action"] = "update" 148 | commands += [obj] 149 | for obj in replaces: 150 | obj["action"] = "replace" 151 | commands += [obj] 152 | for obj in deletes: 153 | obj["action"] = "delete" 154 | commands += [obj] 155 | 156 | data = { 157 | "jsonrpc": JSON_RPC_VERSION, 158 | "id": rpcID(), 159 | "method": "validate", 160 | "params": { 161 | "commands": commands, 162 | "yang-models": yang_models, 163 | }, 164 | } 165 | response = client.post(payload=json.dumps(data)) 166 | convertResponseKeys(response) 167 | 168 | # If the request was successful, we return the result 169 | if response and not response.get("error"): 170 | response.pop( 171 | "result" 172 | ) # we don't need the result as it is empty in success case 173 | module.exit_json(**response) 174 | 175 | response["failed"] = True 176 | module.fail_json( 177 | msg=response["error"]["message"], 178 | jsonrpc_req_id=response["jsonrpc_req_id"], 179 | ) 180 | 181 | 182 | if __name__ == "__main__": 183 | main() 184 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "srlinux-ansible-collection" 3 | version = "1.0.1" 4 | description = "SR Linux Ansible Collection" 5 | readme = "README.md" 6 | requires-python = ">=3.11" 7 | dependencies = ["ansible-core==2.16.2"] 8 | 9 | [dependency-groups] 10 | dev = [ 11 | "rich>=13.9.4", 12 | "ruff>=0.9.4", 13 | ] 14 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright 2023 Nokia 3 | # Licensed under the BSD 3-Clause License. 4 | # SPDX-License-Identifier: BSD-3-Clause 5 | 6 | 7 | set -o errexit 8 | set -o pipefail 9 | 10 | # lab node name 11 | NODE_NAME="clab-ansible-srl" 12 | 13 | # Directory where the scripts are located. 14 | SCRIPTS_DIR="scripts" 15 | 16 | # Directory where the tests are located. 17 | TESTS_DIR="$(pwd)/tests" 18 | 19 | # Containerlab version to use in CI tests 20 | CLAB_VERSION="0.66.0" 21 | 22 | CHECKPOINT_NAME="clab-initial" 23 | 24 | # ----------------------------------------------------------------------------- 25 | # Helper functions start with _ and aren't listed in this script's help menu. 26 | # ----------------------------------------------------------------------------- 27 | 28 | # Change to the tests directory only if we're not already there. 29 | function _cdTests() { 30 | if [ "$(pwd)" != "${TESTS_DIR}" ]; then 31 | cd "${TESTS_DIR}" 32 | fi 33 | } 34 | 35 | # transforms ansible core version by swapping : with / 36 | function _transformAnsibleCoreVersion() { 37 | echo "${1}" | sed 's/:/\//g' 38 | } 39 | 40 | # ----------------------------------------------------------------------------- 41 | # Install functions. 42 | # ----------------------------------------------------------------------------- 43 | 44 | # Install containerlab. 45 | # To install a specific version, pass a version (without v prefix). 46 | function install-containerlab { 47 | if [ -z "$1" ]; then 48 | echo "Installing latest containerlab version" 49 | bash -c "$(curl -sL https://get.containerlab.dev)" 50 | else 51 | echo "Installing containerlab version $1" 52 | bash -c "$(curl -sL https://get.containerlab.dev)" -- -v "$1" 53 | fi 54 | } 55 | 56 | # Install a local collection. 57 | function install-local-collection { 58 | ansible-galaxy collection install --force .. 59 | } 60 | 61 | function remove-local-collection { 62 | rm -rf ~/.ansible/collections/ansible_collections/nokia 63 | } 64 | 65 | 66 | # Deploy test lab. 67 | function deploy-lab { 68 | cd ${SCRIPTS_DIR} 69 | containerlab deploy -c 70 | } 71 | 72 | # Parse dependencies from galaxy.yml and install each one in the local venv 73 | function install-collection-deps { 74 | python3 -c ' 75 | import yaml 76 | with open("galaxy.yml") as f: 77 | deps = yaml.safe_load(f).get("dependencies", {}) 78 | for name, version in deps.items(): 79 | print(f"'{name}:{version}'") 80 | ' | while read dep; do 81 | ansible-galaxy collection install $dep 82 | done 83 | } 84 | 85 | # Prepare local dev environment by setting the symlink to the collection. 86 | function prepare-dev-env { 87 | # setup the symlink for ansible to resolve collection paths 88 | rm -f /tmp/srl_ansible_dev/ansible_collections/nokia/srlinux 89 | mkdir -p /tmp/srl_ansible_dev/ansible_collections/nokia 90 | ln -s "$(pwd)" /tmp/srl_ansible_dev/ansible_collections/nokia/srlinux 91 | 92 | # setup .env file for python to resolve imports 93 | echo "PYTHONPATH=$(realpath ~)/.ansible/collections:/tmp/srl_ansible_dev" > .env 94 | 95 | # install collection dependencies 96 | install-collection-deps 97 | } 98 | 99 | # revert to initial checkpoint to guarantee the node initial state 100 | function revert-to-checkpoint { 101 | # revert to the checkpoint named "initial" to guarantee a clean state 102 | docker exec ${NODE_NAME} sr_cli /tools system configuration checkpoint ${CHECKPOINT_NAME} revert 103 | } 104 | 105 | 106 | 107 | # ----------------------------------------------------------------------------- 108 | # Test functions. 109 | # ----------------------------------------------------------------------------- 110 | 111 | function test-auth-fail { 112 | _cdTests 113 | ansible-playbook playbooks/auth-fail.yml "$@" 114 | } 115 | 116 | function test-config-backup { 117 | _cdTests 118 | revert-to-checkpoint 119 | ansible-playbook playbooks/backup-cfg.yml "$@" 120 | } 121 | 122 | function test-tls-fail { 123 | _cdTests 124 | revert-to-checkpoint 125 | ansible-playbook playbooks/tls-missed-check-fail.yml "$@" 126 | } 127 | 128 | function test-tls-skip { 129 | _cdTests 130 | revert-to-checkpoint 131 | ansible-playbook playbooks/tls-skipped-check.yml "$@" 132 | } 133 | 134 | function test-tls-custom-ca { 135 | _cdTests 136 | revert-to-checkpoint 137 | ansible-playbook playbooks/tls-with-custom-ca.yml "$@" 138 | } 139 | 140 | function test-get-container { 141 | _cdTests 142 | revert-to-checkpoint 143 | ansible-playbook playbooks/get-container.yml "$@" 144 | } 145 | 146 | function test-get-multiple-paths { 147 | _cdTests 148 | revert-to-checkpoint 149 | ansible-playbook playbooks/get-multiple-paths.yml "$@" 150 | } 151 | 152 | function test-get-oc-container { 153 | _cdTests 154 | ansible-playbook playbooks/get-oc-container.yml "$@" 155 | } 156 | 157 | function test-get-wrong-path { 158 | _cdTests 159 | revert-to-checkpoint 160 | ansible-playbook playbooks/get-wrong-path.yml "$@" 161 | } 162 | 163 | function test-backup-cfg { 164 | _cdTests 165 | revert-to-checkpoint 166 | ansible-playbook playbooks/backup-cfg.yml "$@" 167 | } 168 | 169 | function test-cli-show-version { 170 | _cdTests 171 | revert-to-checkpoint 172 | ansible-playbook playbooks/cli-show-version.yml "$@" 173 | } 174 | 175 | function test-cli-wrong-cmd { 176 | _cdTests 177 | revert-to-checkpoint 178 | ansible-playbook playbooks/cli-wrong-cmd.yml "$@" 179 | } 180 | 181 | function test-cli-put-file { 182 | _cdTests 183 | revert-to-checkpoint 184 | ansible-playbook playbooks/cli-put-file.yml "$@" 185 | } 186 | 187 | function test-set-check-mode { 188 | _cdTests 189 | revert-to-checkpoint 190 | 191 | # file to store the output of the playbook so we can grep it and test diff mode 192 | OUT_LOG=/tmp/srl-ansible-set-check-mode.log 193 | rm -f ${OUT_LOG} 194 | 195 | ansible-playbook playbooks/set-check.yml "$@" | tee ${OUT_LOG} 196 | 197 | # ensure that diff was printed 198 | grep -q "+ contact \"Some contact\"" ${OUT_LOG} 199 | } 200 | 201 | function test-set-leaves { 202 | _cdTests 203 | revert-to-checkpoint 204 | ansible-playbook playbooks/set-leaves.yml "$@" 205 | } 206 | 207 | # test that subsequent config changes are idempotent 208 | # and does not lead to commit being recorded since 209 | # the diff is empty 210 | function test-set-leaves-twice { 211 | _cdTests 212 | revert-to-checkpoint 213 | ansible-playbook playbooks/set-leaves-twice.yml "$@" 214 | } 215 | 216 | function test-set-interface { 217 | _cdTests 218 | revert-to-checkpoint 219 | ansible-playbook playbooks/set-interface.yml "$@" 220 | } 221 | 222 | function test-set-wrong-value { 223 | _cdTests 224 | revert-to-checkpoint 225 | ansible-playbook playbooks/set-wrong-value.yml "$@" 226 | } 227 | 228 | function test-set-multiple-paths { 229 | _cdTests 230 | revert-to-checkpoint 231 | ansible-playbook playbooks/set-multiple-paths.yml "$@" 232 | } 233 | 234 | function test-set-oc-leaf { 235 | _cdTests 236 | revert-to-checkpoint 237 | ansible-playbook playbooks/set-oc-leaf.yml "$@" 238 | } 239 | 240 | function test-set-tools { 241 | _cdTests 242 | revert-to-checkpoint 243 | ansible-playbook playbooks/set-tools.yml "$@" 244 | } 245 | 246 | function test-delete-leaves { 247 | _cdTests 248 | revert-to-checkpoint 249 | ansible-playbook playbooks/delete-leaves.yml "$@" 250 | } 251 | 252 | function test-set-idempotent { 253 | _cdTests 254 | revert-to-checkpoint 255 | ansible-playbook playbooks/set-idempotent.yml "$@" 256 | } 257 | 258 | function test-replace-full-config { 259 | _cdTests 260 | revert-to-checkpoint 261 | ansible-playbook playbooks/replace-full-cfg.yml "$@" 262 | } 263 | 264 | function test-replace-json-rpc-config { 265 | _cdTests 266 | revert-to-checkpoint 267 | ansible-playbook playbooks/replace-json-rpc-cfg.yml "$@" 268 | } 269 | 270 | function test-validate { 271 | _cdTests 272 | revert-to-checkpoint 273 | ansible-playbook playbooks/validate.yml "$@" 274 | } 275 | 276 | function test-oc-validate { 277 | _cdTests 278 | revert-to-checkpoint 279 | ansible-playbook playbooks/oc-validate.yml "$@" 280 | } 281 | 282 | function test-commit-confirm { 283 | _cdTests 284 | revert-to-checkpoint 285 | ansible-playbook playbooks/set-confirm-timeout.yml "$@" 286 | } 287 | 288 | # Shouldn't be called directly, use test or ci-test instead. 289 | # Meant to define the collection of tests to run. 290 | function _run-tests { 291 | test-auth-fail "$@" 292 | test-get-container "$@" 293 | test-config-backup "$@" 294 | test-get-wrong-path "$@" 295 | test-cli-show-version "$@" 296 | test-cli-wrong-cmd "$@" 297 | test-tls-fail "$@" 298 | test-tls-skip "$@" 299 | test-tls-custom-ca "$@" 300 | test-set-check-mode "$@" 301 | test-set-leaves "$@" 302 | test-set-leaves-twice "$@" 303 | test-set-wrong-value "$@" 304 | test-set-multiple-paths "$@" 305 | test-set-interface "$@" 306 | test-set-tools "$@" 307 | test-delete-leaves "$@" 308 | test-set-idempotent "$@" 309 | test-replace-full-config "$@" 310 | test-get-multiple-paths "$@" 311 | test-commit-confirm "$@" 312 | # we keep this test last, because it triggers json-rpc 313 | # server restart, thus instead if introducing a delay for 314 | # the tests after it, we push it to the end 315 | test-replace-json-rpc-config "$@" 316 | 317 | # OC-related tests 318 | if [[ " $* " == *" oc-tests "* ]]; then 319 | # OC-related tests 320 | test-get-oc-container "$@" 321 | test-set-oc-leaf "$@" 322 | test-oc-validate "$@" 323 | fi 324 | } 325 | 326 | # prepare local dev environment and run tests 327 | # to enable debug, pass -vvvv as an argument 328 | function test { 329 | prepare-dev-env 330 | revert-to-checkpoint 331 | 332 | _run-tests "$@" 333 | } 334 | 335 | function dump-logs { 336 | ansible-galaxy collection list 337 | echo 338 | pip list 339 | python --version 340 | } 341 | 342 | # ci-test is a wrapper for testing in CI which first setups the environment. 343 | function ci-test { 344 | install-containerlab ${CLAB_VERSION} 345 | install-local-collection 346 | deploy-lab 347 | 348 | dump-logs 349 | 350 | 351 | # at this point we are already in ./tests dir 352 | # since we changed into it in deploy-lab 353 | # we use ci-ansible.cfg to make sure default collections paths is used 354 | ANSIBLE_CONFIG=ci-ansible.cfg _run-tests "$@" 355 | } 356 | 357 | # copy sanity ignore files from ignore-2.10.txt to all other supported ansible versions 358 | function copy-sanity-ignore { 359 | _cdTests 360 | cd sanity 361 | for version in 2.14 2.15 2.16 2.17; do 362 | cp ignore-2.10.txt ignore-${version}.txt 363 | done 364 | } 365 | 366 | # sanity-test runs ansible-test tool with sanity checks. 367 | function sanity-test { 368 | install-local-collection 369 | 370 | cd ~/.ansible/collections/ansible_collections/nokia/srlinux 371 | ansible-test sanity --docker default -v "$@" 372 | 373 | remove-local-collection 374 | } 375 | 376 | # testing gh workflow 377 | function test-act-release { 378 | gh act release -W '.github/workflows/container-build.yml' -e .github/workflows/release-event.json -s GITHUB_TOKEN="$(gh auth token)" --matrix ansible-core-image:2.14.10:py3.11 379 | } 380 | 381 | # ----------------------------------------------------------------------------- 382 | # Publish functions. 383 | # ----------------------------------------------------------------------------- 384 | 385 | function build-collection { 386 | # cleanup 387 | rm -f nokia-srlinux-*.tar.gz 388 | # build the collection 389 | ansible-galaxy collection build --force 390 | } 391 | 392 | function publish-collection { 393 | # build the collection 394 | build-collection 395 | ansible-galaxy collection publish -v --token $(cat ./private/apikey) $(ls -1 nokia-srlinux-*.tar.gz) 396 | } 397 | 398 | # ----------------------------------------------------------------------------- 399 | # Bash runner functions. 400 | # ----------------------------------------------------------------------------- 401 | function help { 402 | printf "%s [args]\n\nTasks:\n" "${0}" 403 | 404 | compgen -A function | grep -v "^_" | cat -n 405 | 406 | printf "\nExtended help:\n Each task has comments for general usage\n" 407 | } 408 | 409 | # This idea is heavily inspired by: https://github.com/adriancooney/Taskfile 410 | TIMEFORMAT=$'\nTask completed in %3lR' 411 | time "${@:-help}" -------------------------------------------------------------------------------- /scripts/oc.cfg: -------------------------------------------------------------------------------- 1 | system { 2 | management { 3 | openconfig { 4 | admin-state enable 5 | } 6 | } 7 | lldp { 8 | admin-state enable 9 | } 10 | } -------------------------------------------------------------------------------- /scripts/topo.clab.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | name: ansible 6 | 7 | topology: 8 | nodes: 9 | srl: 10 | kind: nokia_srlinux 11 | image: ghcr.io/nokia/srlinux:${SRLINUX_VERSION:=24.10.1} 12 | mgmt-ipv4: 172.20.20.222 13 | -------------------------------------------------------------------------------- /tests/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | collections_paths = ~/.ansible/collections:/tmp/srl_ansible_dev 3 | inventory = ./hosts 4 | log_path=./ansible.log 5 | # debug = True -------------------------------------------------------------------------------- /tests/ci-ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory = ./hosts 3 | # debug = True -------------------------------------------------------------------------------- /tests/hosts: -------------------------------------------------------------------------------- 1 | [clab] 2 | clab-ansible-srl ansible_connection=ansible.netcommon.httpapi ansible_user=admin ansible_password=NokiaSrl1! ansible_network_os=nokia.srlinux.srlinux ansible_httpapi_ciphers=ECDHE-RSA-AES256-SHA 3 | -------------------------------------------------------------------------------- /tests/playbooks/auth-fail.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Authentication failure 6 | hosts: clab 7 | gather_facts: false 8 | vars: 9 | ansible_password: wrong 10 | tasks: 11 | - name: Get system information 12 | nokia.srlinux.get: 13 | paths: 14 | - path: /system/information 15 | datastore: state 16 | register: get_return 17 | failed_when: not get_return.failed or "AuthenticationFailed" not in get_return.msg 18 | 19 | - name: Print debug 20 | ansible.builtin.debug: 21 | var: get_return 22 | -------------------------------------------------------------------------------- /tests/playbooks/backup-cfg.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Backup config 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Delete old config files 10 | ansible.builtin.file: 11 | path: "{{ item }}" 12 | state: absent 13 | with_fileglob: 14 | - "/tmp/{{ inventory_hostname }}.cfg.*" 15 | 16 | - name: Get entire running config 17 | nokia.srlinux.get: 18 | paths: 19 | - path: / 20 | datastore: running 21 | register: response 22 | 23 | - name: Save fetched config in JSON 24 | ansible.builtin.copy: 25 | content: "{{ response.result[0] | to_nice_json }}" 26 | dest: "/tmp/{{ inventory_hostname }}.cfg.json" 27 | 28 | - name: Save fetched config in YAML 29 | ansible.builtin.copy: 30 | content: "{{ response.result[0] | to_nice_yaml }}" 31 | dest: "/tmp/{{ inventory_hostname }}.cfg.yml" 32 | 33 | - name: Check if saved file contains "srl_nokia" 34 | ansible.builtin.shell: 35 | cmd: "grep srl_nokia {{ item }}" 36 | with_fileglob: 37 | - "/tmp/{{ inventory_hostname }}.cfg.*" 38 | -------------------------------------------------------------------------------- /tests/playbooks/cli-put-file.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Template and copy config 6 | hosts: clab 7 | gather_facts: false 8 | vars: 9 | ansible_user: linuxadmin 10 | ansible_connection: ssh 11 | ansible_password: 12 | tasks: 13 | - name: Template config 14 | ansible.builtin.template: 15 | src: "{{ playbook_dir }}/golden/{{ inventory_hostname }}-golden.cfg.json.j2" 16 | dest: /tmp/config.json 17 | # - name: Run "show version" CLI command with text output format 18 | # hosts: clab 19 | # gather_facts: false 20 | # tasks: 21 | # - name: Run "show version" CLI command with text output format 22 | # nokia.srlinux.cli: 23 | # commands: 24 | # - show version 25 | # output_format: text 26 | # register: response 27 | # failed_when: '"Serial Number : Sim Serial No" not in response.result[0]' 28 | 29 | # - ansible.builtin.debug: 30 | # var: response 31 | -------------------------------------------------------------------------------- /tests/playbooks/cli-show-version.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Run "show version" CLI command 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Run "show version" CLI command 10 | nokia.srlinux.cli: 11 | commands: 12 | - show version 13 | register: response 14 | failed_when: response.result[0]["basic system info"].Architecture != "x86_64" 15 | 16 | - name: Print debug 17 | ansible.builtin.debug: 18 | var: response 19 | 20 | - name: Run "show version" CLI command with text output format 21 | hosts: clab 22 | gather_facts: false 23 | tasks: 24 | - name: Run "show version" CLI command with text output format 25 | nokia.srlinux.cli: 26 | commands: 27 | - show version 28 | output_format: text 29 | register: response 30 | failed_when: '"Serial Number : Sim Serial No" not in response.result[0]' 31 | 32 | - name: Print debug 33 | ansible.builtin.debug: 34 | var: response 35 | -------------------------------------------------------------------------------- /tests/playbooks/cli-wrong-cmd.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Run wrong CLI command 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Run wrong CLI command 10 | nokia.srlinux.cli: 11 | commands: 12 | - show wrong 13 | register: response 14 | failed_when: '("Parsing error: Unknown token" not in response.msg) or (response.failed is not true)' 15 | 16 | - name: Print debug 17 | ansible.builtin.debug: 18 | var: response 19 | -------------------------------------------------------------------------------- /tests/playbooks/delete-leaves.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Delete leaf nodes 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | # test preparation step 10 | - name: Set system information with values 11 | nokia.srlinux.config: 12 | update: 13 | - path: /system/information 14 | value: 15 | location: Some location 16 | contact: Some contact 17 | 18 | - name: Test Delete operation on leaf nodes 19 | nokia.srlinux.config: 20 | delete: 21 | - path: /system/information/location 22 | - path: /system/information/contact 23 | register: delete_response 24 | 25 | - name: Print debug 26 | ansible.builtin.debug: 27 | var: delete_response 28 | 29 | - name: Ensure leaves were deleted 30 | nokia.srlinux.get: 31 | paths: 32 | - path: /system/information 33 | datastore: state 34 | register: get_response 35 | failed_when: get_response.result[0].location is defined or get_response.result[0].contact is defined 36 | 37 | - name: Print debug 38 | ansible.builtin.debug: 39 | var: get_response 40 | -------------------------------------------------------------------------------- /tests/playbooks/get-container.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Get container 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | # this task ensures that we can get information for a container 10 | - name: Get /system/information container 11 | nokia.srlinux.get: 12 | paths: 13 | - path: /system/information 14 | datastore: state 15 | register: response 16 | failed_when: '"SRLinux" not in response.result[0].description' 17 | 18 | - name: Print debug 19 | ansible.builtin.debug: 20 | var: response 21 | -------------------------------------------------------------------------------- /tests/playbooks/get-multiple-paths.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Get multiple paths 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | # this task ensures that we can get information for a container 10 | - name: Get multiple paths 11 | nokia.srlinux.get: 12 | paths: 13 | # skipped datastore defaults to state 14 | # - path: /system/state/hostname 15 | # yang_models: oc 16 | - path: /system/information/description 17 | datastore: state 18 | yang_models: srl 19 | - path: /system/json-rpc-server 20 | datastore: running 21 | yang_models: srl 22 | register: response 23 | failed_when: '("SRLinux" not in response.result[0]) or ("mgmt" not in response.result[1]["network-instance"][0].name)' 24 | 25 | - name: Print debug 26 | ansible.builtin.debug: 27 | var: response 28 | -------------------------------------------------------------------------------- /tests/playbooks/get-oc-container.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Get OC container 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | # this task ensures that we can get openconfig container 10 | - name: Get /system/information container 11 | nokia.srlinux.get: 12 | paths: 13 | - path: /system/state/hostname 14 | yang_models: oc 15 | datastore: state 16 | - path: /system/information 17 | yang_models: srl 18 | datastore: state 19 | register: response 20 | failed_when: '("SRLinux" not in response.result[1].description) or ("srl" not in response.result[0])' 21 | 22 | - name: Print debug 23 | ansible.builtin.debug: 24 | var: response 25 | -------------------------------------------------------------------------------- /tests/playbooks/get-wrong-path.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Get wrong path 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | # this task ensures that we fail with expected error when get is targeted to a wrong path 10 | - name: Get wrong path 11 | nokia.srlinux.get: 12 | paths: 13 | - path: /system/informations 14 | datastore: state 15 | register: response 16 | failed_when: (not response.failed) or ("Path not valid" not in response.msg) 17 | 18 | - name: Print debug 19 | ansible.builtin.debug: 20 | var: response 21 | -------------------------------------------------------------------------------- /tests/playbooks/oc-validate.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Validate 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Validate a valid change set 10 | nokia.srlinux.validate: 11 | update: 12 | - path: /system/config 13 | value: 14 | motd-banner: "hey ansible" 15 | yang_models: oc 16 | register: response 17 | 18 | - name: Print debug 19 | ansible.builtin.debug: 20 | var: response 21 | 22 | - name: Validate an invalid change set 23 | nokia.srlinux.validate: 24 | update: 25 | - path: /system/config 26 | value: 27 | wrong: Some location 28 | yang_models: oc 29 | register: response 30 | failed_when: (response.failed == false) or ("has no local leaf with the name 'wrong'" not in response.msg) 31 | 32 | - name: Print debug 33 | ansible.builtin.debug: 34 | var: response 35 | -------------------------------------------------------------------------------- /tests/playbooks/replace-full-cfg.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Replace full config 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Replace entire config from file 10 | nokia.srlinux.config: 11 | replace: 12 | - path: / 13 | value: "{{ lookup('ansible.builtin.template', '{{ playbook_dir }}/golden/{{ inventory_hostname }}-golden.cfg.json.j2') }}" 14 | register: set_response 15 | diff: true 16 | # check_mode: true 17 | 18 | - name: Print debug 19 | ansible.builtin.debug: 20 | var: set_response 21 | 22 | - name: Ensure changes were made to the device 23 | nokia.srlinux.get: 24 | paths: 25 | - path: /interface[name=ethernet-1/1]/description 26 | datastore: state 27 | register: get_response 28 | # check if the expected interface description is present as a result of config replace 29 | failed_when: ("ethernet-1/1 interface on " + vars.inventory_hostname) not in get_response.result[0] 30 | 31 | - name: Print debug 32 | ansible.builtin.debug: 33 | var: get_response 34 | -------------------------------------------------------------------------------- /tests/playbooks/replace-json-rpc-cfg.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Replace JSON-RPC config 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Replace JSON-RPC config 10 | nokia.srlinux.config: 11 | replace: "{{ lookup('ansible.builtin.template', '{{ playbook_dir }}/templates/json-rpc-cfg.yml.j2') | from_yaml }}" 12 | save_when: "always" 13 | register: set_response 14 | # diff: true 15 | # check_mode: true 16 | 17 | - name: Print debug 18 | ansible.builtin.debug: 19 | var: set_response 20 | 21 | # the reload of json-rpc server might take time before we can connect again 22 | # - name: sleep 23 | # ansible.builtin.pause: 24 | # seconds: 5 25 | 26 | - name: Ensure changes were made to the device 27 | nokia.srlinux.get: 28 | paths: 29 | - path: /system/json-rpc-server 30 | datastore: state 31 | register: get_response 32 | # check if the expected interface description is present as a result of config replace 33 | failed_when: (get_response.result[0]["network-instance"][0].http["source-address"][0] != "172.20.20.222") 34 | 35 | - name: Print debug 36 | ansible.builtin.debug: 37 | var: get_response 38 | -------------------------------------------------------------------------------- /tests/playbooks/set-check.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Set leaves with check and diff modes 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Test check mode 10 | # srl_config module name tests the redirection to nokia.srlinux.config 11 | nokia.srlinux.srl_config: 12 | update: 13 | - path: /system/information 14 | value: 15 | location: Some location 16 | contact: Some contact 17 | check_mode: true 18 | register: set_response 19 | failed_when: set_response.changed != true and "Some contact" not in set_response.diff.prepared 20 | 21 | - name: Print debug 22 | ansible.builtin.debug: 23 | var: set_response 24 | 25 | - name: Test check mode with diff 26 | nokia.srlinux.config: 27 | update: 28 | - path: /system/information 29 | value: 30 | location: Some location 31 | contact: Some contact 32 | check_mode: true 33 | diff: true 34 | register: set_response 35 | failed_when: set_response.changed != true and "Some contact" not in set_response.diff.prepared 36 | 37 | - name: Print debug 38 | ansible.builtin.debug: 39 | var: set_response 40 | 41 | - name: Ensure no changes were made to the device 42 | nokia.srlinux.get: 43 | paths: 44 | - path: /system/information 45 | datastore: state 46 | register: get_response 47 | failed_when: response.result[0].location is defined 48 | 49 | - name: Print debug 50 | ansible.builtin.debug: 51 | var: get_response 52 | -------------------------------------------------------------------------------- /tests/playbooks/set-confirm-timeout.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Confirm timeout 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Add interface description with confirm timeout 10 | nokia.srlinux.config: 11 | update: 12 | - path: /interface[name=mgmt0]/description 13 | value: "this description must be gone without commit confirm" 14 | # after 3 seconds commit is reverted if not confirmed 15 | confirm_timeout: 4 16 | 17 | register: set_response 18 | 19 | - name: Print debug 20 | ansible.builtin.debug: 21 | var: set_response 22 | 23 | - name: Ensure leaf has been set before timer expires 24 | nokia.srlinux.get: 25 | paths: 26 | - path: /interface[name=mgmt0]/description 27 | datastore: state 28 | register: get_response 29 | failed_when: get_response.result[0] != "this description must be gone without commit confirm" 30 | 31 | - name: Print debug 32 | ansible.builtin.debug: 33 | var: get_response 34 | 35 | - name: Pause 36 | ansible.builtin.pause: 37 | prompt: "Waiting 4 seconds for commit to revert" 38 | seconds: 4 39 | 40 | - name: Ensure the commit has been reverted 41 | nokia.srlinux.get: 42 | paths: 43 | - path: /interface[name=mgmt0]/description 44 | datastore: state 45 | register: get_response 46 | failed_when: get_response.result[0] 47 | 48 | - name: Print debug 49 | ansible.builtin.debug: 50 | var: get_response 51 | 52 | - name: Add again interface description with confirm timeout 53 | nokia.srlinux.config: 54 | update: 55 | - path: /interface[name=mgmt0]/description 56 | value: "this description must be gone without commit confirm" 57 | # after 3 seconds commit is reverted if not confirmed 58 | confirm_timeout: 2 59 | 60 | - name: Confirm the commit 61 | nokia.srlinux.config: 62 | datastore: tools 63 | update: 64 | - path: /system/configuration/confirmed-accept 65 | 66 | - name: Pause 67 | ansible.builtin.pause: 68 | prompt: "Waiting 2 seconds to ensure commit is not reverted" 69 | seconds: 2 70 | 71 | - name: Ensure leaf has been set before timer expires 72 | nokia.srlinux.get: 73 | paths: 74 | - path: /interface[name=mgmt0]/description 75 | datastore: state 76 | register: get_response 77 | failed_when: get_response.result[0] != "this description must be gone without commit confirm" 78 | 79 | - name: Print debug 80 | ansible.builtin.debug: 81 | var: get_response 82 | -------------------------------------------------------------------------------- /tests/playbooks/set-idempotent.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Set idempoent 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Set system information with values 10 | nokia.srlinux.config: 11 | update: 12 | - path: /system/information 13 | value: 14 | location: Some location 15 | contact: Some contact 16 | diff: true 17 | register: set_response 18 | 19 | - name: Print debug 20 | ansible.builtin.debug: 21 | var: set_response 22 | 23 | - name: Ensure changes were made to the device 24 | nokia.srlinux.get: 25 | paths: 26 | - path: /system/information 27 | datastore: state 28 | register: get_response 29 | failed_when: get_response.result[0].location != "Some location" or get_response.result[0].contact != "Some contact" 30 | 31 | - name: Print debug 32 | ansible.builtin.debug: 33 | var: get_response 34 | 35 | - name: Repeated set should not run 36 | nokia.srlinux.config: 37 | update: 38 | - path: /system/information 39 | value: 40 | location: Some location 41 | contact: Some contact 42 | register: set_response 43 | failed_when: set_response.failed or set_response.changed == true 44 | 45 | - name: Print debug 46 | ansible.builtin.debug: 47 | var: set_response 48 | 49 | - name: Repeated set should not run with diff 50 | nokia.srlinux.config: 51 | update: 52 | - path: /system/information 53 | value: 54 | location: Some location 55 | contact: Some contact 56 | register: set_response 57 | diff: true 58 | failed_when: set_response.failed or set_response.changed == true 59 | 60 | - name: Print debug 61 | ansible.builtin.debug: 62 | var: set_response 63 | -------------------------------------------------------------------------------- /tests/playbooks/set-interface.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Add interface 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Add interface 10 | nokia.srlinux.config: 11 | update: 12 | - path: /interface[name=ethernet-1/1] 13 | value: 14 | admin-state: enable 15 | description: "interface description set with Ansible" 16 | subinterface: 17 | - index: 0 18 | admin-state: enable 19 | description: "subinterface description set with Ansible" 20 | ipv4: 21 | admin-state: enable 22 | address: 23 | - ip-prefix: 192.168.0.100/24 24 | 25 | register: set_response 26 | 27 | - name: Print debug 28 | ansible.builtin.debug: 29 | var: set_response 30 | 31 | - name: Ensure changes were made to the device 32 | nokia.srlinux.get: 33 | paths: 34 | - path: /interface[name=ethernet-1/1] 35 | datastore: state 36 | register: get_response 37 | failed_when: (get_response.result[0].description != "interface description set with Ansible") 38 | 39 | - name: Print debug 40 | ansible.builtin.debug: 41 | var: get_response 42 | -------------------------------------------------------------------------------- /tests/playbooks/set-leaves-twice.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Set leaves 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Set system information with values 10 | nokia.srlinux.config: 11 | update: 12 | - path: /system/information 13 | value: 14 | location: some location 15 | register: set_response 16 | 17 | - name: Print debug 18 | ansible.builtin.debug: 19 | var: set_response 20 | 21 | - name: Ensure changes were made to the device 22 | nokia.srlinux.get: 23 | paths: 24 | - path: /system/information 25 | datastore: state 26 | register: get_response 27 | failed_when: (get_response.result[0].location != "some location") 28 | 29 | - name: Print debug 30 | ansible.builtin.debug: 31 | var: get_response 32 | 33 | - name: Get commits 34 | nokia.srlinux.get: 35 | paths: 36 | - path: /system/configuration/commit 37 | register: commits 38 | 39 | - name: Print debug 40 | ansible.builtin.debug: 41 | var: commits 42 | 43 | - name: Count the number of commits registered after the first set 44 | ansible.builtin.set_fact: 45 | first_commit_count: "{{ commits.result[0].commit | length }}" 46 | 47 | - name: Set information with the same value again 48 | nokia.srlinux.config: 49 | update: 50 | - path: /system/information 51 | value: 52 | location: some location 53 | register: set_response 54 | 55 | - name: Print debug 56 | ansible.builtin.debug: 57 | var: set_response 58 | 59 | - name: Check that no change has been recorded by the module 60 | ansible.builtin.assert: 61 | that: 62 | - set_response.changed is false 63 | 64 | - name: Get commits again 65 | nokia.srlinux.get: 66 | paths: 67 | - path: /system/configuration/commit 68 | register: commits 69 | 70 | - name: Print debug 71 | ansible.builtin.debug: 72 | var: commits 73 | 74 | - name: Count the number of commits registered after the second set 75 | ansible.builtin.set_fact: 76 | second_commit_count: "{{ commits.result[0].commit | length }}" 77 | 78 | - name: Print debug 79 | ansible.builtin.debug: 80 | msg: "1st commit count is: {{ first_commit_count }}, 2nd commit count is: {{ second_commit_count }}" 81 | 82 | - name: Check that no new commits have been recorded 83 | ansible.builtin.assert: 84 | that: 85 | - first_commit_count == second_commit_count 86 | -------------------------------------------------------------------------------- /tests/playbooks/set-leaves.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Set leaves 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Set system information with values 10 | nokia.srlinux.config: 11 | update: 12 | - path: /system/information 13 | value: 14 | location: Some location 15 | contact: initial contact 16 | # ensure that update overrides the original value 17 | - path: /system/information 18 | value: 19 | contact: Some contact 20 | - path: /interface[name=mgmt0]/description 21 | value: "test setting description" 22 | register: set_response 23 | 24 | - name: Print debug 25 | ansible.builtin.debug: 26 | var: set_response 27 | 28 | - name: Ensure changes were made to the device 29 | nokia.srlinux.get: 30 | paths: 31 | - path: /system/information 32 | datastore: state 33 | - path: /interface[name=mgmt0]/description 34 | datastore: state 35 | register: get_response 36 | failed_when: (get_response.result[0].location != "Some location") or (get_response.result[0].contact != "Some contact") or (get_response.result[1] != "test setting description") 37 | 38 | - name: Print debug 39 | ansible.builtin.debug: 40 | var: get_response 41 | -------------------------------------------------------------------------------- /tests/playbooks/set-multiple-paths.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Set multiple paths 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Set with multiple operations 10 | nokia.srlinux.config: 11 | update: 12 | - path: /system/information/location 13 | value: Some location 14 | replace: 15 | - path: /system/grpc-server[name=mgmt]/trace-options 16 | value: 17 | - request 18 | - common 19 | delete: 20 | - path: /system/json-rpc-server/network-instance[name=mgmt]/https 21 | register: set_response 22 | 23 | - name: Print debug 24 | ansible.builtin.debug: 25 | var: set_response 26 | 27 | - name: Ensure changes were made to the device 28 | nokia.srlinux.get: 29 | paths: 30 | - path: /system/information/location 31 | datastore: state 32 | - path: /system/grpc-server[name=mgmt]/trace-options 33 | datastore: state 34 | - path: /system/json-rpc-server/network-instance[name=mgmt]/https 35 | datastore: state 36 | register: get_response 37 | failed_when: (get_response.result[0] != "Some location") or (get_response.result[1] != ["request", "common"]) or (get_response.result[2] != {}) 38 | 39 | - name: Print debug 40 | ansible.builtin.debug: 41 | var: get_response 42 | -------------------------------------------------------------------------------- /tests/playbooks/set-oc-leaf.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Set OC leaf 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Set openconfig leaf 10 | nokia.srlinux.config: 11 | update: 12 | - path: /system/config 13 | value: 14 | motd-banner: "hey ansible" 15 | yang_models: oc 16 | register: set_response 17 | 18 | - name: Print debug 19 | ansible.builtin.debug: 20 | var: set_response 21 | 22 | - name: Ensure changes were made to the device 23 | nokia.srlinux.get: 24 | paths: 25 | - path: /system/config/motd-banner 26 | datastore: state 27 | yang_models: oc 28 | register: get_response 29 | failed_when: get_response.result[0] != "hey ansible" 30 | 31 | - name: Print debug 32 | ansible.builtin.debug: 33 | var: get_response 34 | -------------------------------------------------------------------------------- /tests/playbooks/set-tools.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Set with tools datastore 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Clear interface statistics 10 | nokia.srlinux.config: 11 | update: 12 | - path: /platform/fan-tray[id=1]/locator/enable 13 | datastore: tools 14 | 15 | - name: Check locator state 16 | nokia.srlinux.get: 17 | paths: 18 | - path: /platform/fan-tray[id=1]/locator-state 19 | datastore: state 20 | register: get_response 21 | failed_when: get_response.result[0] != "active" 22 | 23 | - name: Print debug 24 | ansible.builtin.debug: 25 | var: get_response 26 | -------------------------------------------------------------------------------- /tests/playbooks/set-wrong-value.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Set wrong value 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Set system information with values 10 | nokia.srlinux.config: 11 | update: 12 | - path: /system/information 13 | value: 14 | wrong: Some location 15 | register: set_response 16 | failed_when: (not set_response.failed) or ("has no local leaf with the name 'wrong'" not in set_response.msg) 17 | 18 | - name: Print debug 19 | ansible.builtin.debug: 20 | var: set_response 21 | -------------------------------------------------------------------------------- /tests/playbooks/templates/json-rpc-cfg.yml.j2: -------------------------------------------------------------------------------- 1 | - path: /system/json-rpc-server/admin-state 2 | value: enable 3 | - path: /system/json-rpc-server/commit-confirmed-timeout 4 | value: "0" 5 | - path: /system/json-rpc-server/network-instance[name=mgmt]/http/admin-state 6 | value: enable 7 | - path: /system/json-rpc-server/network-instance[name=mgmt]/http/use-authentication 8 | value: "true" 9 | - path: /system/json-rpc-server/network-instance[name=mgmt]/http/port 10 | value: "80" 11 | - path: /system/json-rpc-server/network-instance[name=mgmt]/http/session-limit 12 | value: "10" 13 | - path: /system/json-rpc-server/network-instance[name=mgmt]/http/source-address 14 | value: 15 | - 172.20.20.222 -------------------------------------------------------------------------------- /tests/playbooks/tls-missed-check-fail.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: TLS missed check fail 6 | hosts: clab 7 | gather_facts: false 8 | vars: 9 | ansible_httpapi_use_ssl: true 10 | tasks: 11 | - name: Get with TLS required but without setting skip verify or custom ca cert 12 | nokia.srlinux.get: 13 | paths: 14 | - path: /system/information 15 | datastore: state 16 | register: get_return 17 | failed_when: not get_return.failed or "CERTIFICATE_VERIFY_FAILED" not in get_return.module_stderr 18 | 19 | - name: Print debug 20 | ansible.builtin.debug: 21 | var: get_return 22 | -------------------------------------------------------------------------------- /tests/playbooks/tls-skipped-check.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: TLS skipped check 6 | hosts: clab 7 | gather_facts: false 8 | vars: 9 | ansible_httpapi_use_ssl: true 10 | ansible_httpapi_validate_certs: false 11 | tasks: 12 | - name: Get system information 13 | nokia.srlinux.get: 14 | paths: 15 | - path: /system/information 16 | datastore: state 17 | register: get_return 18 | failed_when: get_return.failed 19 | 20 | - name: Print debug 21 | ansible.builtin.debug: 22 | var: get_return 23 | -------------------------------------------------------------------------------- /tests/playbooks/tls-with-custom-ca.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: TLS with custom CA 6 | hosts: clab 7 | gather_facts: false 8 | vars: 9 | ansible_httpapi_use_ssl: true 10 | ansible_httpapi_ca_path: "{{ playbook_dir }}/../../scripts/clab-ansible/.tls/ca/ca.pem" 11 | tasks: 12 | - name: Get with TLS required and custom ca cert 13 | nokia.srlinux.get: 14 | paths: 15 | - path: /system/information 16 | datastore: state 17 | register: get_return 18 | failed_when: get_return.failed 19 | 20 | - name: Print debug 21 | ansible.builtin.debug: 22 | var: get_return 23 | -------------------------------------------------------------------------------- /tests/playbooks/validate.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2023 Nokia 2 | # Licensed under the BSD 3-Clause License. 3 | # SPDX-License-Identifier: BSD-3-Clause 4 | 5 | - name: Validate 6 | hosts: clab 7 | gather_facts: false 8 | tasks: 9 | - name: Validate a valid change set 10 | nokia.srlinux.validate: 11 | update: 12 | - path: /system/information 13 | value: 14 | location: Some location 15 | contact: Some contact 16 | register: response 17 | 18 | - name: Print debug 19 | ansible.builtin.debug: 20 | var: response 21 | 22 | - name: Validate a valid change set using the non dict value 23 | nokia.srlinux.validate: 24 | update: 25 | - path: /system/information/location 26 | value: Some location 27 | register: response 28 | 29 | - name: Print debug 30 | ansible.builtin.debug: 31 | var: response 32 | 33 | - name: Validate an invalid change set 34 | nokia.srlinux.validate: 35 | update: 36 | - path: /system/information 37 | value: 38 | wrong: Some location 39 | contact: Some contact 40 | register: response 41 | failed_when: (response.failed == false) or ("has no local leaf with the name 'wrong'" not in response.msg) 42 | 43 | - name: Print debug 44 | ansible.builtin.debug: 45 | var: response 46 | -------------------------------------------------------------------------------- /tests/sanity/ignore-2.10.txt: -------------------------------------------------------------------------------- 1 | plugins/httpapi/srlinux.py validate-modules:missing-gplv3-license 2 | plugins/httpapi/srlinux.py validate-modules:import-before-documentation 3 | plugins/modules/cli.py import-2.7!skip # srlinux collection requires py3.10+ 4 | plugins/modules/cli.py import-3.5!skip 5 | plugins/modules/cli.py import-3.6!skip 6 | plugins/modules/cli.py import-3.7!skip 7 | plugins/modules/cli.py import-3.8!skip 8 | plugins/modules/cli.py validate-modules:missing-gplv3-license 9 | plugins/modules/cli.py validate-modules:import-before-documentation 10 | plugins/modules/get.py import-2.7!skip 11 | plugins/modules/get.py import-3.5!skip 12 | plugins/modules/get.py import-3.6!skip 13 | plugins/modules/get.py import-3.7!skip 14 | plugins/modules/get.py import-3.8!skip 15 | plugins/modules/get.py validate-modules:missing-gplv3-license 16 | plugins/modules/get.py validate-modules:import-before-documentation 17 | plugins/modules/config.py import-2.7!skip 18 | plugins/modules/config.py import-3.5!skip 19 | plugins/modules/config.py import-3.6!skip 20 | plugins/modules/config.py import-3.7!skip 21 | plugins/modules/config.py import-3.8!skip 22 | plugins/modules/config.py validate-modules:missing-gplv3-license 23 | plugins/modules/config.py validate-modules:import-before-documentation 24 | plugins/modules/validate.py import-2.7!skip 25 | plugins/modules/validate.py import-3.5!skip 26 | plugins/modules/validate.py import-3.6!skip 27 | plugins/modules/validate.py import-3.7!skip 28 | plugins/modules/validate.py import-3.8!skip 29 | plugins/modules/validate.py validate-modules:missing-gplv3-license 30 | plugins/modules/validate.py validate-modules:import-before-documentation 31 | plugins/module_utils/srlinux.py compile-2.7!skip 32 | plugins/module_utils/srlinux.py compile-3.5!skip 33 | plugins/module_utils/srlinux.py import-2.7!skip 34 | plugins/module_utils/srlinux.py import-3.5!skip 35 | plugins/module_utils/srlinux.py import-3.6!skip 36 | plugins/module_utils/srlinux.py import-3.7!skip 37 | plugins/module_utils/srlinux.py import-3.8!skip 38 | plugins/module_utils/const.py import-2.7!skip 39 | plugins/module_utils/const.py import-3.5!skip 40 | plugins/module_utils/const.py compile-2.7!skip 41 | plugins/module_utils/const.py compile-3.5!skip 42 | plugins/module_utils/const.py import-3.6!skip 43 | plugins/module_utils/const.py import-3.7!skip 44 | plugins/module_utils/const.py import-3.8!skip 45 | plugins/module_utils/const.py future-import-boilerplate!skip 46 | plugins/module_utils/const.py metaclass-boilerplate!skip 47 | tests/playbooks/golden/clab-ansible-srl.cfg.yml yamllint!skip -------------------------------------------------------------------------------- /tests/sanity/ignore-2.11.txt: -------------------------------------------------------------------------------- 1 | plugins/httpapi/srlinux.py validate-modules:missing-gplv3-license 2 | plugins/httpapi/srlinux.py validate-modules:import-before-documentation 3 | plugins/modules/cli.py import-2.7!skip # srlinux collection requires py3.6+ 4 | plugins/modules/cli.py import-3.5!skip 5 | plugins/modules/cli.py validate-modules:missing-gplv3-license 6 | plugins/modules/cli.py validate-modules:import-before-documentation 7 | plugins/modules/get.py import-2.7!skip 8 | plugins/modules/get.py import-3.5!skip 9 | plugins/modules/get.py validate-modules:missing-gplv3-license 10 | plugins/modules/get.py validate-modules:import-before-documentation 11 | plugins/modules/config.py import-2.7!skip 12 | plugins/modules/config.py import-3.5!skip 13 | plugins/modules/config.py validate-modules:missing-gplv3-license 14 | plugins/modules/config.py validate-modules:import-before-documentation 15 | plugins/modules/validate.py import-2.7!skip 16 | plugins/modules/validate.py import-3.5!skip 17 | plugins/modules/validate.py validate-modules:missing-gplv3-license 18 | plugins/modules/validate.py validate-modules:import-before-documentation 19 | plugins/module_utils/srlinux.py import-2.7!skip 20 | plugins/module_utils/srlinux.py import-3.5!skip 21 | plugins/module_utils/const.py import-2.7!skip 22 | plugins/module_utils/const.py import-3.5!skip 23 | plugins/module_utils/const.py compile-2.7!skip 24 | plugins/module_utils/const.py compile-3.5!skip 25 | plugins/module_utils/srlinux.py compile-2.7!skip 26 | plugins/module_utils/srlinux.py compile-3.5!skip 27 | plugins/module_utils/const.py future-import-boilerplate!skip 28 | plugins/module_utils/const.py metaclass-boilerplate!skip 29 | tests/playbooks/golden/clab-ansible-srl.cfg.yml yamllint!skip -------------------------------------------------------------------------------- /tests/sanity/ignore-2.12.txt: -------------------------------------------------------------------------------- 1 | plugins/httpapi/srlinux.py validate-modules:missing-gplv3-license 2 | plugins/httpapi/srlinux.py validate-modules:import-before-documentation 3 | plugins/modules/cli.py import-2.7!skip # srlinux collection requires py3.6+ 4 | plugins/modules/cli.py import-3.5!skip 5 | plugins/modules/cli.py validate-modules:missing-gplv3-license 6 | plugins/modules/cli.py validate-modules:import-before-documentation 7 | plugins/modules/get.py import-2.7!skip 8 | plugins/modules/get.py import-3.5!skip 9 | plugins/modules/get.py validate-modules:missing-gplv3-license 10 | plugins/modules/get.py validate-modules:import-before-documentation 11 | plugins/modules/config.py import-2.7!skip 12 | plugins/modules/config.py import-3.5!skip 13 | plugins/modules/config.py validate-modules:missing-gplv3-license 14 | plugins/modules/config.py validate-modules:import-before-documentation 15 | plugins/modules/validate.py import-2.7!skip 16 | plugins/modules/validate.py import-3.5!skip 17 | plugins/modules/validate.py validate-modules:missing-gplv3-license 18 | plugins/modules/validate.py validate-modules:import-before-documentation 19 | plugins/module_utils/srlinux.py import-2.7!skip 20 | plugins/module_utils/srlinux.py import-3.5!skip 21 | plugins/module_utils/const.py import-2.7!skip 22 | plugins/module_utils/const.py import-3.5!skip 23 | plugins/module_utils/const.py compile-2.7!skip 24 | plugins/module_utils/const.py compile-3.5!skip 25 | plugins/module_utils/srlinux.py compile-2.7!skip 26 | plugins/module_utils/srlinux.py compile-3.5!skip 27 | plugins/module_utils/const.py future-import-boilerplate!skip 28 | plugins/module_utils/const.py metaclass-boilerplate!skip 29 | tests/playbooks/golden/clab-ansible-srl.cfg.yml yamllint!skip -------------------------------------------------------------------------------- /tests/sanity/ignore-2.13.txt: -------------------------------------------------------------------------------- 1 | plugins/httpapi/srlinux.py validate-modules:missing-gplv3-license 2 | plugins/httpapi/srlinux.py validate-modules:import-before-documentation 3 | plugins/modules/cli.py import-2.7!skip # srlinux collection requires py3.6+ 4 | plugins/modules/cli.py import-3.5!skip 5 | plugins/modules/cli.py validate-modules:missing-gplv3-license 6 | plugins/modules/cli.py validate-modules:import-before-documentation 7 | plugins/modules/get.py import-2.7!skip 8 | plugins/modules/get.py import-3.5!skip 9 | plugins/modules/get.py validate-modules:missing-gplv3-license 10 | plugins/modules/get.py validate-modules:import-before-documentation 11 | plugins/modules/config.py import-2.7!skip 12 | plugins/modules/config.py import-3.5!skip 13 | plugins/modules/config.py validate-modules:missing-gplv3-license 14 | plugins/modules/config.py validate-modules:import-before-documentation 15 | plugins/modules/validate.py import-2.7!skip 16 | plugins/modules/validate.py import-3.5!skip 17 | plugins/modules/validate.py validate-modules:missing-gplv3-license 18 | plugins/modules/validate.py validate-modules:import-before-documentation 19 | plugins/module_utils/srlinux.py import-2.7!skip 20 | plugins/module_utils/srlinux.py import-3.5!skip 21 | plugins/module_utils/const.py import-2.7!skip 22 | plugins/module_utils/const.py import-3.5!skip 23 | plugins/module_utils/const.py compile-2.7!skip 24 | plugins/module_utils/const.py compile-3.5!skip 25 | plugins/module_utils/srlinux.py compile-2.7!skip 26 | plugins/module_utils/srlinux.py compile-3.5!skip 27 | plugins/module_utils/const.py future-import-boilerplate!skip 28 | plugins/module_utils/const.py metaclass-boilerplate!skip 29 | tests/playbooks/golden/clab-ansible-srl.cfg.yml yamllint!skip -------------------------------------------------------------------------------- /tests/sanity/ignore-2.14.txt: -------------------------------------------------------------------------------- 1 | plugins/httpapi/srlinux.py validate-modules:missing-gplv3-license 2 | plugins/httpapi/srlinux.py validate-modules:import-before-documentation 3 | plugins/modules/cli.py import-2.7!skip # srlinux collection requires py3.10+ 4 | plugins/modules/cli.py import-3.5!skip 5 | plugins/modules/cli.py import-3.6!skip 6 | plugins/modules/cli.py import-3.7!skip 7 | plugins/modules/cli.py import-3.8!skip 8 | plugins/modules/cli.py validate-modules:missing-gplv3-license 9 | plugins/modules/cli.py validate-modules:import-before-documentation 10 | plugins/modules/get.py import-2.7!skip 11 | plugins/modules/get.py import-3.5!skip 12 | plugins/modules/get.py import-3.6!skip 13 | plugins/modules/get.py import-3.7!skip 14 | plugins/modules/get.py import-3.8!skip 15 | plugins/modules/get.py validate-modules:missing-gplv3-license 16 | plugins/modules/get.py validate-modules:import-before-documentation 17 | plugins/modules/config.py import-2.7!skip 18 | plugins/modules/config.py import-3.5!skip 19 | plugins/modules/config.py import-3.6!skip 20 | plugins/modules/config.py import-3.7!skip 21 | plugins/modules/config.py import-3.8!skip 22 | plugins/modules/config.py validate-modules:missing-gplv3-license 23 | plugins/modules/config.py validate-modules:import-before-documentation 24 | plugins/modules/validate.py import-2.7!skip 25 | plugins/modules/validate.py import-3.5!skip 26 | plugins/modules/validate.py import-3.6!skip 27 | plugins/modules/validate.py import-3.7!skip 28 | plugins/modules/validate.py import-3.8!skip 29 | plugins/modules/validate.py validate-modules:missing-gplv3-license 30 | plugins/modules/validate.py validate-modules:import-before-documentation 31 | plugins/module_utils/srlinux.py compile-2.7!skip 32 | plugins/module_utils/srlinux.py compile-3.5!skip 33 | plugins/module_utils/srlinux.py import-2.7!skip 34 | plugins/module_utils/srlinux.py import-3.5!skip 35 | plugins/module_utils/srlinux.py import-3.6!skip 36 | plugins/module_utils/srlinux.py import-3.7!skip 37 | plugins/module_utils/srlinux.py import-3.8!skip 38 | plugins/module_utils/const.py import-2.7!skip 39 | plugins/module_utils/const.py import-3.5!skip 40 | plugins/module_utils/const.py compile-2.7!skip 41 | plugins/module_utils/const.py compile-3.5!skip 42 | plugins/module_utils/const.py import-3.6!skip 43 | plugins/module_utils/const.py import-3.7!skip 44 | plugins/module_utils/const.py import-3.8!skip 45 | plugins/module_utils/const.py future-import-boilerplate!skip 46 | plugins/module_utils/const.py metaclass-boilerplate!skip 47 | tests/playbooks/golden/clab-ansible-srl.cfg.yml yamllint!skip -------------------------------------------------------------------------------- /tests/sanity/ignore-2.15.txt: -------------------------------------------------------------------------------- 1 | plugins/httpapi/srlinux.py validate-modules:missing-gplv3-license 2 | plugins/httpapi/srlinux.py validate-modules:import-before-documentation 3 | plugins/modules/cli.py import-2.7!skip # srlinux collection requires py3.10+ 4 | plugins/modules/cli.py import-3.5!skip 5 | plugins/modules/cli.py import-3.6!skip 6 | plugins/modules/cli.py import-3.7!skip 7 | plugins/modules/cli.py import-3.8!skip 8 | plugins/modules/cli.py validate-modules:missing-gplv3-license 9 | plugins/modules/cli.py validate-modules:import-before-documentation 10 | plugins/modules/get.py import-2.7!skip 11 | plugins/modules/get.py import-3.5!skip 12 | plugins/modules/get.py import-3.6!skip 13 | plugins/modules/get.py import-3.7!skip 14 | plugins/modules/get.py import-3.8!skip 15 | plugins/modules/get.py validate-modules:missing-gplv3-license 16 | plugins/modules/get.py validate-modules:import-before-documentation 17 | plugins/modules/config.py import-2.7!skip 18 | plugins/modules/config.py import-3.5!skip 19 | plugins/modules/config.py import-3.6!skip 20 | plugins/modules/config.py import-3.7!skip 21 | plugins/modules/config.py import-3.8!skip 22 | plugins/modules/config.py validate-modules:missing-gplv3-license 23 | plugins/modules/config.py validate-modules:import-before-documentation 24 | plugins/modules/validate.py import-2.7!skip 25 | plugins/modules/validate.py import-3.5!skip 26 | plugins/modules/validate.py import-3.6!skip 27 | plugins/modules/validate.py import-3.7!skip 28 | plugins/modules/validate.py import-3.8!skip 29 | plugins/modules/validate.py validate-modules:missing-gplv3-license 30 | plugins/modules/validate.py validate-modules:import-before-documentation 31 | plugins/module_utils/srlinux.py compile-2.7!skip 32 | plugins/module_utils/srlinux.py compile-3.5!skip 33 | plugins/module_utils/srlinux.py import-2.7!skip 34 | plugins/module_utils/srlinux.py import-3.5!skip 35 | plugins/module_utils/srlinux.py import-3.6!skip 36 | plugins/module_utils/srlinux.py import-3.7!skip 37 | plugins/module_utils/srlinux.py import-3.8!skip 38 | plugins/module_utils/const.py import-2.7!skip 39 | plugins/module_utils/const.py import-3.5!skip 40 | plugins/module_utils/const.py compile-2.7!skip 41 | plugins/module_utils/const.py compile-3.5!skip 42 | plugins/module_utils/const.py import-3.6!skip 43 | plugins/module_utils/const.py import-3.7!skip 44 | plugins/module_utils/const.py import-3.8!skip 45 | plugins/module_utils/const.py future-import-boilerplate!skip 46 | plugins/module_utils/const.py metaclass-boilerplate!skip 47 | tests/playbooks/golden/clab-ansible-srl.cfg.yml yamllint!skip -------------------------------------------------------------------------------- /tests/sanity/ignore-2.16.txt: -------------------------------------------------------------------------------- 1 | plugins/httpapi/srlinux.py validate-modules:missing-gplv3-license 2 | plugins/httpapi/srlinux.py validate-modules:import-before-documentation 3 | plugins/modules/cli.py import-2.7!skip # srlinux collection requires py3.10+ 4 | plugins/modules/cli.py import-3.5!skip 5 | plugins/modules/cli.py import-3.6!skip 6 | plugins/modules/cli.py import-3.7!skip 7 | plugins/modules/cli.py import-3.8!skip 8 | plugins/modules/cli.py validate-modules:missing-gplv3-license 9 | plugins/modules/cli.py validate-modules:import-before-documentation 10 | plugins/modules/get.py import-2.7!skip 11 | plugins/modules/get.py import-3.5!skip 12 | plugins/modules/get.py import-3.6!skip 13 | plugins/modules/get.py import-3.7!skip 14 | plugins/modules/get.py import-3.8!skip 15 | plugins/modules/get.py validate-modules:missing-gplv3-license 16 | plugins/modules/get.py validate-modules:import-before-documentation 17 | plugins/modules/config.py import-2.7!skip 18 | plugins/modules/config.py import-3.5!skip 19 | plugins/modules/config.py import-3.6!skip 20 | plugins/modules/config.py import-3.7!skip 21 | plugins/modules/config.py import-3.8!skip 22 | plugins/modules/config.py validate-modules:missing-gplv3-license 23 | plugins/modules/config.py validate-modules:import-before-documentation 24 | plugins/modules/validate.py import-2.7!skip 25 | plugins/modules/validate.py import-3.5!skip 26 | plugins/modules/validate.py import-3.6!skip 27 | plugins/modules/validate.py import-3.7!skip 28 | plugins/modules/validate.py import-3.8!skip 29 | plugins/modules/validate.py validate-modules:missing-gplv3-license 30 | plugins/modules/validate.py validate-modules:import-before-documentation 31 | plugins/module_utils/srlinux.py compile-2.7!skip 32 | plugins/module_utils/srlinux.py compile-3.5!skip 33 | plugins/module_utils/srlinux.py import-2.7!skip 34 | plugins/module_utils/srlinux.py import-3.5!skip 35 | plugins/module_utils/srlinux.py import-3.6!skip 36 | plugins/module_utils/srlinux.py import-3.7!skip 37 | plugins/module_utils/srlinux.py import-3.8!skip 38 | plugins/module_utils/const.py import-2.7!skip 39 | plugins/module_utils/const.py import-3.5!skip 40 | plugins/module_utils/const.py compile-2.7!skip 41 | plugins/module_utils/const.py compile-3.5!skip 42 | plugins/module_utils/const.py import-3.6!skip 43 | plugins/module_utils/const.py import-3.7!skip 44 | plugins/module_utils/const.py import-3.8!skip 45 | plugins/module_utils/const.py future-import-boilerplate!skip 46 | plugins/module_utils/const.py metaclass-boilerplate!skip 47 | tests/playbooks/golden/clab-ansible-srl.cfg.yml yamllint!skip -------------------------------------------------------------------------------- /tests/sanity/ignore-2.17.txt: -------------------------------------------------------------------------------- 1 | plugins/httpapi/srlinux.py validate-modules:missing-gplv3-license 2 | plugins/httpapi/srlinux.py validate-modules:import-before-documentation 3 | plugins/modules/cli.py import-2.7!skip # srlinux collection requires py3.10+ 4 | plugins/modules/cli.py import-3.5!skip 5 | plugins/modules/cli.py import-3.6!skip 6 | plugins/modules/cli.py import-3.7!skip 7 | plugins/modules/cli.py import-3.8!skip 8 | plugins/modules/cli.py validate-modules:missing-gplv3-license 9 | plugins/modules/cli.py validate-modules:import-before-documentation 10 | plugins/modules/get.py import-2.7!skip 11 | plugins/modules/get.py import-3.5!skip 12 | plugins/modules/get.py import-3.6!skip 13 | plugins/modules/get.py import-3.7!skip 14 | plugins/modules/get.py import-3.8!skip 15 | plugins/modules/get.py validate-modules:missing-gplv3-license 16 | plugins/modules/get.py validate-modules:import-before-documentation 17 | plugins/modules/config.py import-2.7!skip 18 | plugins/modules/config.py import-3.5!skip 19 | plugins/modules/config.py import-3.6!skip 20 | plugins/modules/config.py import-3.7!skip 21 | plugins/modules/config.py import-3.8!skip 22 | plugins/modules/config.py validate-modules:missing-gplv3-license 23 | plugins/modules/config.py validate-modules:import-before-documentation 24 | plugins/modules/validate.py import-2.7!skip 25 | plugins/modules/validate.py import-3.5!skip 26 | plugins/modules/validate.py import-3.6!skip 27 | plugins/modules/validate.py import-3.7!skip 28 | plugins/modules/validate.py import-3.8!skip 29 | plugins/modules/validate.py validate-modules:missing-gplv3-license 30 | plugins/modules/validate.py validate-modules:import-before-documentation 31 | plugins/module_utils/srlinux.py compile-2.7!skip 32 | plugins/module_utils/srlinux.py compile-3.5!skip 33 | plugins/module_utils/srlinux.py import-2.7!skip 34 | plugins/module_utils/srlinux.py import-3.5!skip 35 | plugins/module_utils/srlinux.py import-3.6!skip 36 | plugins/module_utils/srlinux.py import-3.7!skip 37 | plugins/module_utils/srlinux.py import-3.8!skip 38 | plugins/module_utils/const.py import-2.7!skip 39 | plugins/module_utils/const.py import-3.5!skip 40 | plugins/module_utils/const.py compile-2.7!skip 41 | plugins/module_utils/const.py compile-3.5!skip 42 | plugins/module_utils/const.py import-3.6!skip 43 | plugins/module_utils/const.py import-3.7!skip 44 | plugins/module_utils/const.py import-3.8!skip 45 | plugins/module_utils/const.py future-import-boilerplate!skip 46 | plugins/module_utils/const.py metaclass-boilerplate!skip 47 | tests/playbooks/golden/clab-ansible-srl.cfg.yml yamllint!skip -------------------------------------------------------------------------------- /uv.lock: -------------------------------------------------------------------------------- 1 | version = 1 2 | requires-python = ">=3.11" 3 | 4 | [[package]] 5 | name = "ansible-core" 6 | version = "2.16.2" 7 | source = { registry = "https://pypi.org/simple" } 8 | dependencies = [ 9 | { name = "cryptography" }, 10 | { name = "jinja2" }, 11 | { name = "packaging" }, 12 | { name = "pyyaml" }, 13 | { name = "resolvelib" }, 14 | ] 15 | sdist = { url = "https://files.pythonhosted.org/packages/66/5f/3098fac361ca16fc42ac30a2da09939286afd9af289f156ac9fbe87595e9/ansible-core-2.16.2.tar.gz", hash = "sha256:e4ab559e7e525b1c6f99084fca873bb014775d5ecbe845b7c07b8e9d6c9c048b", size = 3163899 } 16 | wheels = [ 17 | { url = "https://files.pythonhosted.org/packages/fe/0b/c28a50e7fbb7f6c6eb7bef4f023c5b408b0ff70934c2682be85e412b454d/ansible_core-2.16.2-py3-none-any.whl", hash = "sha256:494f002edcb17b02baef661ff27b8c9c750a534bdc0537ab29dc02e680817d92", size = 2249791 }, 18 | ] 19 | 20 | [[package]] 21 | name = "cffi" 22 | version = "1.17.1" 23 | source = { registry = "https://pypi.org/simple" } 24 | dependencies = [ 25 | { name = "pycparser" }, 26 | ] 27 | sdist = { url = "https://files.pythonhosted.org/packages/fc/97/c783634659c2920c3fc70419e3af40972dbaf758daa229a7d6ea6135c90d/cffi-1.17.1.tar.gz", hash = "sha256:1c39c6016c32bc48dd54561950ebd6836e1670f2ae46128f67cf49e789c52824", size = 516621 } 28 | wheels = [ 29 | { url = "https://files.pythonhosted.org/packages/6b/f4/927e3a8899e52a27fa57a48607ff7dc91a9ebe97399b357b85a0c7892e00/cffi-1.17.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:a45e3c6913c5b87b3ff120dcdc03f6131fa0065027d0ed7ee6190736a74cd401", size = 182264 }, 30 | { url = "https://files.pythonhosted.org/packages/6c/f5/6c3a8efe5f503175aaddcbea6ad0d2c96dad6f5abb205750d1b3df44ef29/cffi-1.17.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:30c5e0cb5ae493c04c8b42916e52ca38079f1b235c2f8ae5f4527b963c401caf", size = 178651 }, 31 | { url = "https://files.pythonhosted.org/packages/94/dd/a3f0118e688d1b1a57553da23b16bdade96d2f9bcda4d32e7d2838047ff7/cffi-1.17.1-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f75c7ab1f9e4aca5414ed4d8e5c0e303a34f4421f8a0d47a4d019ceff0ab6af4", size = 445259 }, 32 | { url = "https://files.pythonhosted.org/packages/2e/ea/70ce63780f096e16ce8588efe039d3c4f91deb1dc01e9c73a287939c79a6/cffi-1.17.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a1ed2dd2972641495a3ec98445e09766f077aee98a1c896dcb4ad0d303628e41", size = 469200 }, 33 | { url = "https://files.pythonhosted.org/packages/1c/a0/a4fa9f4f781bda074c3ddd57a572b060fa0df7655d2a4247bbe277200146/cffi-1.17.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:46bf43160c1a35f7ec506d254e5c890f3c03648a4dbac12d624e4490a7046cd1", size = 477235 }, 34 | { url = "https://files.pythonhosted.org/packages/62/12/ce8710b5b8affbcdd5c6e367217c242524ad17a02fe5beec3ee339f69f85/cffi-1.17.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a24ed04c8ffd54b0729c07cee15a81d964e6fee0e3d4d342a27b020d22959dc6", size = 459721 }, 35 | { url = "https://files.pythonhosted.org/packages/ff/6b/d45873c5e0242196f042d555526f92aa9e0c32355a1be1ff8c27f077fd37/cffi-1.17.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:610faea79c43e44c71e1ec53a554553fa22321b65fae24889706c0a84d4ad86d", size = 467242 }, 36 | { url = "https://files.pythonhosted.org/packages/1a/52/d9a0e523a572fbccf2955f5abe883cfa8bcc570d7faeee06336fbd50c9fc/cffi-1.17.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a9b15d491f3ad5d692e11f6b71f7857e7835eb677955c00cc0aefcd0669adaf6", size = 477999 }, 37 | { url = "https://files.pythonhosted.org/packages/44/74/f2a2460684a1a2d00ca799ad880d54652841a780c4c97b87754f660c7603/cffi-1.17.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:de2ea4b5833625383e464549fec1bc395c1bdeeb5f25c4a3a82b5a8c756ec22f", size = 454242 }, 38 | { url = "https://files.pythonhosted.org/packages/f8/4a/34599cac7dfcd888ff54e801afe06a19c17787dfd94495ab0c8d35fe99fb/cffi-1.17.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:fc48c783f9c87e60831201f2cce7f3b2e4846bf4d8728eabe54d60700b318a0b", size = 478604 }, 39 | { url = "https://files.pythonhosted.org/packages/34/33/e1b8a1ba29025adbdcda5fb3a36f94c03d771c1b7b12f726ff7fef2ebe36/cffi-1.17.1-cp311-cp311-win32.whl", hash = "sha256:85a950a4ac9c359340d5963966e3e0a94a676bd6245a4b55bc43949eee26a655", size = 171727 }, 40 | { url = "https://files.pythonhosted.org/packages/3d/97/50228be003bb2802627d28ec0627837ac0bf35c90cf769812056f235b2d1/cffi-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:caaf0640ef5f5517f49bc275eca1406b0ffa6aa184892812030f04c2abf589a0", size = 181400 }, 41 | { url = "https://files.pythonhosted.org/packages/5a/84/e94227139ee5fb4d600a7a4927f322e1d4aea6fdc50bd3fca8493caba23f/cffi-1.17.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:805b4371bf7197c329fcb3ead37e710d1bca9da5d583f5073b799d5c5bd1eee4", size = 183178 }, 42 | { url = "https://files.pythonhosted.org/packages/da/ee/fb72c2b48656111c4ef27f0f91da355e130a923473bf5ee75c5643d00cca/cffi-1.17.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:733e99bc2df47476e3848417c5a4540522f234dfd4ef3ab7fafdf555b082ec0c", size = 178840 }, 43 | { url = "https://files.pythonhosted.org/packages/cc/b6/db007700f67d151abadf508cbfd6a1884f57eab90b1bb985c4c8c02b0f28/cffi-1.17.1-cp312-cp312-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1257bdabf294dceb59f5e70c64a3e2f462c30c7ad68092d01bbbfb1c16b1ba36", size = 454803 }, 44 | { url = "https://files.pythonhosted.org/packages/1a/df/f8d151540d8c200eb1c6fba8cd0dfd40904f1b0682ea705c36e6c2e97ab3/cffi-1.17.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da95af8214998d77a98cc14e3a3bd00aa191526343078b530ceb0bd710fb48a5", size = 478850 }, 45 | { url = "https://files.pythonhosted.org/packages/28/c0/b31116332a547fd2677ae5b78a2ef662dfc8023d67f41b2a83f7c2aa78b1/cffi-1.17.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d63afe322132c194cf832bfec0dc69a99fb9bb6bbd550f161a49e9e855cc78ff", size = 485729 }, 46 | { url = "https://files.pythonhosted.org/packages/91/2b/9a1ddfa5c7f13cab007a2c9cc295b70fbbda7cb10a286aa6810338e60ea1/cffi-1.17.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f79fc4fc25f1c8698ff97788206bb3c2598949bfe0fef03d299eb1b5356ada99", size = 471256 }, 47 | { url = "https://files.pythonhosted.org/packages/b2/d5/da47df7004cb17e4955df6a43d14b3b4ae77737dff8bf7f8f333196717bf/cffi-1.17.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b62ce867176a75d03a665bad002af8e6d54644fad99a3c70905c543130e39d93", size = 479424 }, 48 | { url = "https://files.pythonhosted.org/packages/0b/ac/2a28bcf513e93a219c8a4e8e125534f4f6db03e3179ba1c45e949b76212c/cffi-1.17.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:386c8bf53c502fff58903061338ce4f4950cbdcb23e2902d86c0f722b786bbe3", size = 484568 }, 49 | { url = "https://files.pythonhosted.org/packages/d4/38/ca8a4f639065f14ae0f1d9751e70447a261f1a30fa7547a828ae08142465/cffi-1.17.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:4ceb10419a9adf4460ea14cfd6bc43d08701f0835e979bf821052f1805850fe8", size = 488736 }, 50 | { url = "https://files.pythonhosted.org/packages/86/c5/28b2d6f799ec0bdecf44dced2ec5ed43e0eb63097b0f58c293583b406582/cffi-1.17.1-cp312-cp312-win32.whl", hash = "sha256:a08d7e755f8ed21095a310a693525137cfe756ce62d066e53f502a83dc550f65", size = 172448 }, 51 | { url = "https://files.pythonhosted.org/packages/50/b9/db34c4755a7bd1cb2d1603ac3863f22bcecbd1ba29e5ee841a4bc510b294/cffi-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:51392eae71afec0d0c8fb1a53b204dbb3bcabcb3c9b807eedf3e1e6ccf2de903", size = 181976 }, 52 | { url = "https://files.pythonhosted.org/packages/8d/f8/dd6c246b148639254dad4d6803eb6a54e8c85c6e11ec9df2cffa87571dbe/cffi-1.17.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f3a2b4222ce6b60e2e8b337bb9596923045681d71e5a082783484d845390938e", size = 182989 }, 53 | { url = "https://files.pythonhosted.org/packages/8b/f1/672d303ddf17c24fc83afd712316fda78dc6fce1cd53011b839483e1ecc8/cffi-1.17.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0984a4925a435b1da406122d4d7968dd861c1385afe3b45ba82b750f229811e2", size = 178802 }, 54 | { url = "https://files.pythonhosted.org/packages/0e/2d/eab2e858a91fdff70533cab61dcff4a1f55ec60425832ddfdc9cd36bc8af/cffi-1.17.1-cp313-cp313-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d01b12eeeb4427d3110de311e1774046ad344f5b1a7403101878976ecd7a10f3", size = 454792 }, 55 | { url = "https://files.pythonhosted.org/packages/75/b2/fbaec7c4455c604e29388d55599b99ebcc250a60050610fadde58932b7ee/cffi-1.17.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:706510fe141c86a69c8ddc029c7910003a17353970cff3b904ff0686a5927683", size = 478893 }, 56 | { url = "https://files.pythonhosted.org/packages/4f/b7/6e4a2162178bf1935c336d4da8a9352cccab4d3a5d7914065490f08c0690/cffi-1.17.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de55b766c7aa2e2a3092c51e0483d700341182f08e67c63630d5b6f200bb28e5", size = 485810 }, 57 | { url = "https://files.pythonhosted.org/packages/c7/8a/1d0e4a9c26e54746dc08c2c6c037889124d4f59dffd853a659fa545f1b40/cffi-1.17.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c59d6e989d07460165cc5ad3c61f9fd8f1b4796eacbd81cee78957842b834af4", size = 471200 }, 58 | { url = "https://files.pythonhosted.org/packages/26/9f/1aab65a6c0db35f43c4d1b4f580e8df53914310afc10ae0397d29d697af4/cffi-1.17.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd398dbc6773384a17fe0d3e7eeb8d1a21c2200473ee6806bb5e6a8e62bb73dd", size = 479447 }, 59 | { url = "https://files.pythonhosted.org/packages/5f/e4/fb8b3dd8dc0e98edf1135ff067ae070bb32ef9d509d6cb0f538cd6f7483f/cffi-1.17.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:3edc8d958eb099c634dace3c7e16560ae474aa3803a5df240542b305d14e14ed", size = 484358 }, 60 | { url = "https://files.pythonhosted.org/packages/f1/47/d7145bf2dc04684935d57d67dff9d6d795b2ba2796806bb109864be3a151/cffi-1.17.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:72e72408cad3d5419375fc87d289076ee319835bdfa2caad331e377589aebba9", size = 488469 }, 61 | { url = "https://files.pythonhosted.org/packages/bf/ee/f94057fa6426481d663b88637a9a10e859e492c73d0384514a17d78ee205/cffi-1.17.1-cp313-cp313-win32.whl", hash = "sha256:e03eab0a8677fa80d646b5ddece1cbeaf556c313dcfac435ba11f107ba117b5d", size = 172475 }, 62 | { url = "https://files.pythonhosted.org/packages/7c/fc/6a8cb64e5f0324877d503c854da15d76c1e50eb722e320b15345c4d0c6de/cffi-1.17.1-cp313-cp313-win_amd64.whl", hash = "sha256:f6a16c31041f09ead72d69f583767292f750d24913dadacf5756b966aacb3f1a", size = 182009 }, 63 | ] 64 | 65 | [[package]] 66 | name = "cryptography" 67 | version = "44.0.0" 68 | source = { registry = "https://pypi.org/simple" } 69 | dependencies = [ 70 | { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, 71 | ] 72 | sdist = { url = "https://files.pythonhosted.org/packages/91/4c/45dfa6829acffa344e3967d6006ee4ae8be57af746ae2eba1c431949b32c/cryptography-44.0.0.tar.gz", hash = "sha256:cd4e834f340b4293430701e772ec543b0fbe6c2dea510a5286fe0acabe153a02", size = 710657 } 73 | wheels = [ 74 | { url = "https://files.pythonhosted.org/packages/55/09/8cc67f9b84730ad330b3b72cf867150744bf07ff113cda21a15a1c6d2c7c/cryptography-44.0.0-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:84111ad4ff3f6253820e6d3e58be2cc2a00adb29335d4cacb5ab4d4d34f2a123", size = 6541833 }, 75 | { url = "https://files.pythonhosted.org/packages/7e/5b/3759e30a103144e29632e7cb72aec28cedc79e514b2ea8896bb17163c19b/cryptography-44.0.0-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b15492a11f9e1b62ba9d73c210e2416724633167de94607ec6069ef724fad092", size = 3922710 }, 76 | { url = "https://files.pythonhosted.org/packages/5f/58/3b14bf39f1a0cfd679e753e8647ada56cddbf5acebffe7db90e184c76168/cryptography-44.0.0-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:831c3c4d0774e488fdc83a1923b49b9957d33287de923d58ebd3cec47a0ae43f", size = 4137546 }, 77 | { url = "https://files.pythonhosted.org/packages/98/65/13d9e76ca19b0ba5603d71ac8424b5694415b348e719db277b5edc985ff5/cryptography-44.0.0-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:761817a3377ef15ac23cd7834715081791d4ec77f9297ee694ca1ee9c2c7e5eb", size = 3915420 }, 78 | { url = "https://files.pythonhosted.org/packages/b1/07/40fe09ce96b91fc9276a9ad272832ead0fddedcba87f1190372af8e3039c/cryptography-44.0.0-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3c672a53c0fb4725a29c303be906d3c1fa99c32f58abe008a82705f9ee96f40b", size = 4154498 }, 79 | { url = "https://files.pythonhosted.org/packages/75/ea/af65619c800ec0a7e4034207aec543acdf248d9bffba0533342d1bd435e1/cryptography-44.0.0-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4ac4c9f37eba52cb6fbeaf5b59c152ea976726b865bd4cf87883a7e7006cc543", size = 3932569 }, 80 | { url = "https://files.pythonhosted.org/packages/c7/af/d1deb0c04d59612e3d5e54203159e284d3e7a6921e565bb0eeb6269bdd8a/cryptography-44.0.0-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:ed3534eb1090483c96178fcb0f8893719d96d5274dfde98aa6add34614e97c8e", size = 4016721 }, 81 | { url = "https://files.pythonhosted.org/packages/bd/69/7ca326c55698d0688db867795134bdfac87136b80ef373aaa42b225d6dd5/cryptography-44.0.0-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:f3f6fdfa89ee2d9d496e2c087cebef9d4fcbb0ad63c40e821b39f74bf48d9c5e", size = 4240915 }, 82 | { url = "https://files.pythonhosted.org/packages/ef/d4/cae11bf68c0f981e0413906c6dd03ae7fa864347ed5fac40021df1ef467c/cryptography-44.0.0-cp37-abi3-win32.whl", hash = "sha256:eb33480f1bad5b78233b0ad3e1b0be21e8ef1da745d8d2aecbb20671658b9053", size = 2757925 }, 83 | { url = "https://files.pythonhosted.org/packages/64/b1/50d7739254d2002acae64eed4fc43b24ac0cc44bf0a0d388d1ca06ec5bb1/cryptography-44.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:abc998e0c0eee3c8a1904221d3f67dcfa76422b23620173e28c11d3e626c21bd", size = 3202055 }, 84 | { url = "https://files.pythonhosted.org/packages/11/18/61e52a3d28fc1514a43b0ac291177acd1b4de00e9301aaf7ef867076ff8a/cryptography-44.0.0-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:660cb7312a08bc38be15b696462fa7cc7cd85c3ed9c576e81f4dc4d8b2b31591", size = 6542801 }, 85 | { url = "https://files.pythonhosted.org/packages/1a/07/5f165b6c65696ef75601b781a280fc3b33f1e0cd6aa5a92d9fb96c410e97/cryptography-44.0.0-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1923cb251c04be85eec9fda837661c67c1049063305d6be5721643c22dd4e2b7", size = 3922613 }, 86 | { url = "https://files.pythonhosted.org/packages/28/34/6b3ac1d80fc174812486561cf25194338151780f27e438526f9c64e16869/cryptography-44.0.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:404fdc66ee5f83a1388be54300ae978b2efd538018de18556dde92575e05defc", size = 4137925 }, 87 | { url = "https://files.pythonhosted.org/packages/d0/c7/c656eb08fd22255d21bc3129625ed9cd5ee305f33752ef2278711b3fa98b/cryptography-44.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:c5eb858beed7835e5ad1faba59e865109f3e52b3783b9ac21e7e47dc5554e289", size = 3915417 }, 88 | { url = "https://files.pythonhosted.org/packages/ef/82/72403624f197af0db6bac4e58153bc9ac0e6020e57234115db9596eee85d/cryptography-44.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:f53c2c87e0fb4b0c00fa9571082a057e37690a8f12233306161c8f4b819960b7", size = 4155160 }, 89 | { url = "https://files.pythonhosted.org/packages/a2/cd/2f3c440913d4329ade49b146d74f2e9766422e1732613f57097fea61f344/cryptography-44.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:9e6fc8a08e116fb7c7dd1f040074c9d7b51d74a8ea40d4df2fc7aa08b76b9e6c", size = 3932331 }, 90 | { url = "https://files.pythonhosted.org/packages/7f/df/8be88797f0a1cca6e255189a57bb49237402b1880d6e8721690c5603ac23/cryptography-44.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:d2436114e46b36d00f8b72ff57e598978b37399d2786fd39793c36c6d5cb1c64", size = 4017372 }, 91 | { url = "https://files.pythonhosted.org/packages/af/36/5ccc376f025a834e72b8e52e18746b927f34e4520487098e283a719c205e/cryptography-44.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a01956ddfa0a6790d594f5b34fc1bfa6098aca434696a03cfdbe469b8ed79285", size = 4239657 }, 92 | { url = "https://files.pythonhosted.org/packages/46/b0/f4f7d0d0bcfbc8dd6296c1449be326d04217c57afb8b2594f017eed95533/cryptography-44.0.0-cp39-abi3-win32.whl", hash = "sha256:eca27345e1214d1b9f9490d200f9db5a874479be914199194e746c893788d417", size = 2758672 }, 93 | { url = "https://files.pythonhosted.org/packages/97/9b/443270b9210f13f6ef240eff73fd32e02d381e7103969dc66ce8e89ee901/cryptography-44.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:708ee5f1bafe76d041b53a4f95eb28cdeb8d18da17e597d46d7833ee59b97ede", size = 3202071 }, 94 | ] 95 | 96 | [[package]] 97 | name = "jinja2" 98 | version = "3.1.5" 99 | source = { registry = "https://pypi.org/simple" } 100 | dependencies = [ 101 | { name = "markupsafe" }, 102 | ] 103 | sdist = { url = "https://files.pythonhosted.org/packages/af/92/b3130cbbf5591acf9ade8708c365f3238046ac7cb8ccba6e81abccb0ccff/jinja2-3.1.5.tar.gz", hash = "sha256:8fefff8dc3034e27bb80d67c671eb8a9bc424c0ef4c0826edbff304cceff43bb", size = 244674 } 104 | wheels = [ 105 | { url = "https://files.pythonhosted.org/packages/bd/0f/2ba5fbcd631e3e88689309dbe978c5769e883e4b84ebfe7da30b43275c5a/jinja2-3.1.5-py3-none-any.whl", hash = "sha256:aba0f4dc9ed8013c424088f68a5c226f7d6097ed89b246d7749c2ec4175c6adb", size = 134596 }, 106 | ] 107 | 108 | [[package]] 109 | name = "markdown-it-py" 110 | version = "3.0.0" 111 | source = { registry = "https://pypi.org/simple" } 112 | dependencies = [ 113 | { name = "mdurl" }, 114 | ] 115 | sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596 } 116 | wheels = [ 117 | { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 }, 118 | ] 119 | 120 | [[package]] 121 | name = "markupsafe" 122 | version = "3.0.2" 123 | source = { registry = "https://pypi.org/simple" } 124 | sdist = { url = "https://files.pythonhosted.org/packages/b2/97/5d42485e71dfc078108a86d6de8fa46db44a1a9295e89c5d6d4a06e23a62/markupsafe-3.0.2.tar.gz", hash = "sha256:ee55d3edf80167e48ea11a923c7386f4669df67d7994554387f84e7d8b0a2bf0", size = 20537 } 125 | wheels = [ 126 | { url = "https://files.pythonhosted.org/packages/6b/28/bbf83e3f76936960b850435576dd5e67034e200469571be53f69174a2dfd/MarkupSafe-3.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9025b4018f3a1314059769c7bf15441064b2207cb3f065e6ea1e7359cb46db9d", size = 14353 }, 127 | { url = "https://files.pythonhosted.org/packages/6c/30/316d194b093cde57d448a4c3209f22e3046c5bb2fb0820b118292b334be7/MarkupSafe-3.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93335ca3812df2f366e80509ae119189886b0f3c2b81325d39efdb84a1e2ae93", size = 12392 }, 128 | { url = "https://files.pythonhosted.org/packages/f2/96/9cdafba8445d3a53cae530aaf83c38ec64c4d5427d975c974084af5bc5d2/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2cb8438c3cbb25e220c2ab33bb226559e7afb3baec11c4f218ffa7308603c832", size = 23984 }, 129 | { url = "https://files.pythonhosted.org/packages/f1/a4/aefb044a2cd8d7334c8a47d3fb2c9f328ac48cb349468cc31c20b539305f/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a123e330ef0853c6e822384873bef7507557d8e4a082961e1defa947aa59ba84", size = 23120 }, 130 | { url = "https://files.pythonhosted.org/packages/8d/21/5e4851379f88f3fad1de30361db501300d4f07bcad047d3cb0449fc51f8c/MarkupSafe-3.0.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1e084f686b92e5b83186b07e8a17fc09e38fff551f3602b249881fec658d3eca", size = 23032 }, 131 | { url = "https://files.pythonhosted.org/packages/00/7b/e92c64e079b2d0d7ddf69899c98842f3f9a60a1ae72657c89ce2655c999d/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:d8213e09c917a951de9d09ecee036d5c7d36cb6cb7dbaece4c71a60d79fb9798", size = 24057 }, 132 | { url = "https://files.pythonhosted.org/packages/f9/ac/46f960ca323037caa0a10662ef97d0a4728e890334fc156b9f9e52bcc4ca/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:5b02fb34468b6aaa40dfc198d813a641e3a63b98c2b05a16b9f80b7ec314185e", size = 23359 }, 133 | { url = "https://files.pythonhosted.org/packages/69/84/83439e16197337b8b14b6a5b9c2105fff81d42c2a7c5b58ac7b62ee2c3b1/MarkupSafe-3.0.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:0bff5e0ae4ef2e1ae4fdf2dfd5b76c75e5c2fa4132d05fc1b0dabcd20c7e28c4", size = 23306 }, 134 | { url = "https://files.pythonhosted.org/packages/9a/34/a15aa69f01e2181ed8d2b685c0d2f6655d5cca2c4db0ddea775e631918cd/MarkupSafe-3.0.2-cp311-cp311-win32.whl", hash = "sha256:6c89876f41da747c8d3677a2b540fb32ef5715f97b66eeb0c6b66f5e3ef6f59d", size = 15094 }, 135 | { url = "https://files.pythonhosted.org/packages/da/b8/3a3bd761922d416f3dc5d00bfbed11f66b1ab89a0c2b6e887240a30b0f6b/MarkupSafe-3.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:70a87b411535ccad5ef2f1df5136506a10775d267e197e4cf531ced10537bd6b", size = 15521 }, 136 | { url = "https://files.pythonhosted.org/packages/22/09/d1f21434c97fc42f09d290cbb6350d44eb12f09cc62c9476effdb33a18aa/MarkupSafe-3.0.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:9778bd8ab0a994ebf6f84c2b949e65736d5575320a17ae8984a77fab08db94cf", size = 14274 }, 137 | { url = "https://files.pythonhosted.org/packages/6b/b0/18f76bba336fa5aecf79d45dcd6c806c280ec44538b3c13671d49099fdd0/MarkupSafe-3.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:846ade7b71e3536c4e56b386c2a47adf5741d2d8b94ec9dc3e92e5e1ee1e2225", size = 12348 }, 138 | { url = "https://files.pythonhosted.org/packages/e0/25/dd5c0f6ac1311e9b40f4af06c78efde0f3b5cbf02502f8ef9501294c425b/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1c99d261bd2d5f6b59325c92c73df481e05e57f19837bdca8413b9eac4bd8028", size = 24149 }, 139 | { url = "https://files.pythonhosted.org/packages/f3/f0/89e7aadfb3749d0f52234a0c8c7867877876e0a20b60e2188e9850794c17/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e17c96c14e19278594aa4841ec148115f9c7615a47382ecb6b82bd8fea3ab0c8", size = 23118 }, 140 | { url = "https://files.pythonhosted.org/packages/d5/da/f2eeb64c723f5e3777bc081da884b414671982008c47dcc1873d81f625b6/MarkupSafe-3.0.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88416bd1e65dcea10bc7569faacb2c20ce071dd1f87539ca2ab364bf6231393c", size = 22993 }, 141 | { url = "https://files.pythonhosted.org/packages/da/0e/1f32af846df486dce7c227fe0f2398dc7e2e51d4a370508281f3c1c5cddc/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2181e67807fc2fa785d0592dc2d6206c019b9502410671cc905d132a92866557", size = 24178 }, 142 | { url = "https://files.pythonhosted.org/packages/c4/f6/bb3ca0532de8086cbff5f06d137064c8410d10779c4c127e0e47d17c0b71/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:52305740fe773d09cffb16f8ed0427942901f00adedac82ec8b67752f58a1b22", size = 23319 }, 143 | { url = "https://files.pythonhosted.org/packages/a2/82/8be4c96ffee03c5b4a034e60a31294daf481e12c7c43ab8e34a1453ee48b/MarkupSafe-3.0.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:ad10d3ded218f1039f11a75f8091880239651b52e9bb592ca27de44eed242a48", size = 23352 }, 144 | { url = "https://files.pythonhosted.org/packages/51/ae/97827349d3fcffee7e184bdf7f41cd6b88d9919c80f0263ba7acd1bbcb18/MarkupSafe-3.0.2-cp312-cp312-win32.whl", hash = "sha256:0f4ca02bea9a23221c0182836703cbf8930c5e9454bacce27e767509fa286a30", size = 15097 }, 145 | { url = "https://files.pythonhosted.org/packages/c1/80/a61f99dc3a936413c3ee4e1eecac96c0da5ed07ad56fd975f1a9da5bc630/MarkupSafe-3.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:8e06879fc22a25ca47312fbe7c8264eb0b662f6db27cb2d3bbbc74b1df4b9b87", size = 15601 }, 146 | { url = "https://files.pythonhosted.org/packages/83/0e/67eb10a7ecc77a0c2bbe2b0235765b98d164d81600746914bebada795e97/MarkupSafe-3.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:ba9527cdd4c926ed0760bc301f6728ef34d841f405abf9d4f959c478421e4efd", size = 14274 }, 147 | { url = "https://files.pythonhosted.org/packages/2b/6d/9409f3684d3335375d04e5f05744dfe7e9f120062c9857df4ab490a1031a/MarkupSafe-3.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f8b3d067f2e40fe93e1ccdd6b2e1d16c43140e76f02fb1319a05cf2b79d99430", size = 12352 }, 148 | { url = "https://files.pythonhosted.org/packages/d2/f5/6eadfcd3885ea85fe2a7c128315cc1bb7241e1987443d78c8fe712d03091/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:569511d3b58c8791ab4c2e1285575265991e6d8f8700c7be0e88f86cb0672094", size = 24122 }, 149 | { url = "https://files.pythonhosted.org/packages/0c/91/96cf928db8236f1bfab6ce15ad070dfdd02ed88261c2afafd4b43575e9e9/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:15ab75ef81add55874e7ab7055e9c397312385bd9ced94920f2802310c930396", size = 23085 }, 150 | { url = "https://files.pythonhosted.org/packages/c2/cf/c9d56af24d56ea04daae7ac0940232d31d5a8354f2b457c6d856b2057d69/MarkupSafe-3.0.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f3818cb119498c0678015754eba762e0d61e5b52d34c8b13d770f0719f7b1d79", size = 22978 }, 151 | { url = "https://files.pythonhosted.org/packages/2a/9f/8619835cd6a711d6272d62abb78c033bda638fdc54c4e7f4272cf1c0962b/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:cdb82a876c47801bb54a690c5ae105a46b392ac6099881cdfb9f6e95e4014c6a", size = 24208 }, 152 | { url = "https://files.pythonhosted.org/packages/f9/bf/176950a1792b2cd2102b8ffeb5133e1ed984547b75db47c25a67d3359f77/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:cabc348d87e913db6ab4aa100f01b08f481097838bdddf7c7a84b7575b7309ca", size = 23357 }, 153 | { url = "https://files.pythonhosted.org/packages/ce/4f/9a02c1d335caabe5c4efb90e1b6e8ee944aa245c1aaaab8e8a618987d816/MarkupSafe-3.0.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:444dcda765c8a838eaae23112db52f1efaf750daddb2d9ca300bcae1039adc5c", size = 23344 }, 154 | { url = "https://files.pythonhosted.org/packages/ee/55/c271b57db36f748f0e04a759ace9f8f759ccf22b4960c270c78a394f58be/MarkupSafe-3.0.2-cp313-cp313-win32.whl", hash = "sha256:bcf3e58998965654fdaff38e58584d8937aa3096ab5354d493c77d1fdd66d7a1", size = 15101 }, 155 | { url = "https://files.pythonhosted.org/packages/29/88/07df22d2dd4df40aba9f3e402e6dc1b8ee86297dddbad4872bd5e7b0094f/MarkupSafe-3.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:e6a2a455bd412959b57a172ce6328d2dd1f01cb2135efda2e4576e8a23fa3b0f", size = 15603 }, 156 | { url = "https://files.pythonhosted.org/packages/62/6a/8b89d24db2d32d433dffcd6a8779159da109842434f1dd2f6e71f32f738c/MarkupSafe-3.0.2-cp313-cp313t-macosx_10_13_universal2.whl", hash = "sha256:b5a6b3ada725cea8a5e634536b1b01c30bcdcd7f9c6fff4151548d5bf6b3a36c", size = 14510 }, 157 | { url = "https://files.pythonhosted.org/packages/7a/06/a10f955f70a2e5a9bf78d11a161029d278eeacbd35ef806c3fd17b13060d/MarkupSafe-3.0.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a904af0a6162c73e3edcb969eeeb53a63ceeb5d8cf642fade7d39e7963a22ddb", size = 12486 }, 158 | { url = "https://files.pythonhosted.org/packages/34/cf/65d4a571869a1a9078198ca28f39fba5fbb910f952f9dbc5220afff9f5e6/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4aa4e5faecf353ed117801a068ebab7b7e09ffb6e1d5e412dc852e0da018126c", size = 25480 }, 159 | { url = "https://files.pythonhosted.org/packages/0c/e3/90e9651924c430b885468b56b3d597cabf6d72be4b24a0acd1fa0e12af67/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c0ef13eaeee5b615fb07c9a7dadb38eac06a0608b41570d8ade51c56539e509d", size = 23914 }, 160 | { url = "https://files.pythonhosted.org/packages/66/8c/6c7cf61f95d63bb866db39085150df1f2a5bd3335298f14a66b48e92659c/MarkupSafe-3.0.2-cp313-cp313t-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d16a81a06776313e817c951135cf7340a3e91e8c1ff2fac444cfd75fffa04afe", size = 23796 }, 161 | { url = "https://files.pythonhosted.org/packages/bb/35/cbe9238ec3f47ac9a7c8b3df7a808e7cb50fe149dc7039f5f454b3fba218/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:6381026f158fdb7c72a168278597a5e3a5222e83ea18f543112b2662a9b699c5", size = 25473 }, 162 | { url = "https://files.pythonhosted.org/packages/e6/32/7621a4382488aa283cc05e8984a9c219abad3bca087be9ec77e89939ded9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_i686.whl", hash = "sha256:3d79d162e7be8f996986c064d1c7c817f6df3a77fe3d6859f6f9e7be4b8c213a", size = 24114 }, 163 | { url = "https://files.pythonhosted.org/packages/0d/80/0985960e4b89922cb5a0bac0ed39c5b96cbc1a536a99f30e8c220a996ed9/MarkupSafe-3.0.2-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:131a3c7689c85f5ad20f9f6fb1b866f402c445b220c19fe4308c0b147ccd2ad9", size = 24098 }, 164 | { url = "https://files.pythonhosted.org/packages/82/78/fedb03c7d5380df2427038ec8d973587e90561b2d90cd472ce9254cf348b/MarkupSafe-3.0.2-cp313-cp313t-win32.whl", hash = "sha256:ba8062ed2cf21c07a9e295d5b8a2a5ce678b913b45fdf68c32d95d6c1291e0b6", size = 15208 }, 165 | { url = "https://files.pythonhosted.org/packages/4f/65/6079a46068dfceaeabb5dcad6d674f5f5c61a6fa5673746f42a9f4c233b3/MarkupSafe-3.0.2-cp313-cp313t-win_amd64.whl", hash = "sha256:e444a31f8db13eb18ada366ab3cf45fd4b31e4db1236a4448f68778c1d1a5a2f", size = 15739 }, 166 | ] 167 | 168 | [[package]] 169 | name = "mdurl" 170 | version = "0.1.2" 171 | source = { registry = "https://pypi.org/simple" } 172 | sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729 } 173 | wheels = [ 174 | { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979 }, 175 | ] 176 | 177 | [[package]] 178 | name = "packaging" 179 | version = "24.2" 180 | source = { registry = "https://pypi.org/simple" } 181 | sdist = { url = "https://files.pythonhosted.org/packages/d0/63/68dbb6eb2de9cb10ee4c9c14a0148804425e13c4fb20d61cce69f53106da/packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f", size = 163950 } 182 | wheels = [ 183 | { url = "https://files.pythonhosted.org/packages/88/ef/eb23f262cca3c0c4eb7ab1933c3b1f03d021f2c48f54763065b6f0e321be/packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759", size = 65451 }, 184 | ] 185 | 186 | [[package]] 187 | name = "pycparser" 188 | version = "2.22" 189 | source = { registry = "https://pypi.org/simple" } 190 | sdist = { url = "https://files.pythonhosted.org/packages/1d/b2/31537cf4b1ca988837256c910a668b553fceb8f069bedc4b1c826024b52c/pycparser-2.22.tar.gz", hash = "sha256:491c8be9c040f5390f5bf44a5b07752bd07f56edf992381b05c701439eec10f6", size = 172736 } 191 | wheels = [ 192 | { url = "https://files.pythonhosted.org/packages/13/a3/a812df4e2dd5696d1f351d58b8fe16a405b234ad2886a0dab9183fb78109/pycparser-2.22-py3-none-any.whl", hash = "sha256:c3702b6d3dd8c7abc1afa565d7e63d53a1d0bd86cdc24edd75470f4de499cfcc", size = 117552 }, 193 | ] 194 | 195 | [[package]] 196 | name = "pygments" 197 | version = "2.19.1" 198 | source = { registry = "https://pypi.org/simple" } 199 | sdist = { url = "https://files.pythonhosted.org/packages/7c/2d/c3338d48ea6cc0feb8446d8e6937e1408088a72a39937982cc6111d17f84/pygments-2.19.1.tar.gz", hash = "sha256:61c16d2a8576dc0649d9f39e089b5f02bcd27fba10d8fb4dcc28173f7a45151f", size = 4968581 } 200 | wheels = [ 201 | { url = "https://files.pythonhosted.org/packages/8a/0b/9fcc47d19c48b59121088dd6da2488a49d5f72dacf8262e2790a1d2c7d15/pygments-2.19.1-py3-none-any.whl", hash = "sha256:9ea1544ad55cecf4b8242fab6dd35a93bbce657034b0611ee383099054ab6d8c", size = 1225293 }, 202 | ] 203 | 204 | [[package]] 205 | name = "pyyaml" 206 | version = "6.0.2" 207 | source = { registry = "https://pypi.org/simple" } 208 | sdist = { url = "https://files.pythonhosted.org/packages/54/ed/79a089b6be93607fa5cdaedf301d7dfb23af5f25c398d5ead2525b063e17/pyyaml-6.0.2.tar.gz", hash = "sha256:d584d9ec91ad65861cc08d42e834324ef890a082e591037abe114850ff7bbc3e", size = 130631 } 209 | wheels = [ 210 | { url = "https://files.pythonhosted.org/packages/f8/aa/7af4e81f7acba21a4c6be026da38fd2b872ca46226673c89a758ebdc4fd2/PyYAML-6.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cc1c1159b3d456576af7a3e4d1ba7e6924cb39de8f67111c735f6fc832082774", size = 184612 }, 211 | { url = "https://files.pythonhosted.org/packages/8b/62/b9faa998fd185f65c1371643678e4d58254add437edb764a08c5a98fb986/PyYAML-6.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1e2120ef853f59c7419231f3bf4e7021f1b936f6ebd222406c3b60212205d2ee", size = 172040 }, 212 | { url = "https://files.pythonhosted.org/packages/ad/0c/c804f5f922a9a6563bab712d8dcc70251e8af811fce4524d57c2c0fd49a4/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d225db5a45f21e78dd9358e58a98702a0302f2659a3c6cd320564b75b86f47c", size = 736829 }, 213 | { url = "https://files.pythonhosted.org/packages/51/16/6af8d6a6b210c8e54f1406a6b9481febf9c64a3109c541567e35a49aa2e7/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5ac9328ec4831237bec75defaf839f7d4564be1e6b25ac710bd1a96321cc8317", size = 764167 }, 214 | { url = "https://files.pythonhosted.org/packages/75/e4/2c27590dfc9992f73aabbeb9241ae20220bd9452df27483b6e56d3975cc5/PyYAML-6.0.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ad2a3decf9aaba3d29c8f537ac4b243e36bef957511b4766cb0057d32b0be85", size = 762952 }, 215 | { url = "https://files.pythonhosted.org/packages/9b/97/ecc1abf4a823f5ac61941a9c00fe501b02ac3ab0e373c3857f7d4b83e2b6/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ff3824dc5261f50c9b0dfb3be22b4567a6f938ccce4587b38952d85fd9e9afe4", size = 735301 }, 216 | { url = "https://files.pythonhosted.org/packages/45/73/0f49dacd6e82c9430e46f4a027baa4ca205e8b0a9dce1397f44edc23559d/PyYAML-6.0.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:797b4f722ffa07cc8d62053e4cff1486fa6dc094105d13fea7b1de7d8bf71c9e", size = 756638 }, 217 | { url = "https://files.pythonhosted.org/packages/22/5f/956f0f9fc65223a58fbc14459bf34b4cc48dec52e00535c79b8db361aabd/PyYAML-6.0.2-cp311-cp311-win32.whl", hash = "sha256:11d8f3dd2b9c1207dcaf2ee0bbbfd5991f571186ec9cc78427ba5bd32afae4b5", size = 143850 }, 218 | { url = "https://files.pythonhosted.org/packages/ed/23/8da0bbe2ab9dcdd11f4f4557ccaf95c10b9811b13ecced089d43ce59c3c8/PyYAML-6.0.2-cp311-cp311-win_amd64.whl", hash = "sha256:e10ce637b18caea04431ce14fabcf5c64a1c61ec9c56b071a4b7ca131ca52d44", size = 161980 }, 219 | { url = "https://files.pythonhosted.org/packages/86/0c/c581167fc46d6d6d7ddcfb8c843a4de25bdd27e4466938109ca68492292c/PyYAML-6.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:c70c95198c015b85feafc136515252a261a84561b7b1d51e3384e0655ddf25ab", size = 183873 }, 220 | { url = "https://files.pythonhosted.org/packages/a8/0c/38374f5bb272c051e2a69281d71cba6fdb983413e6758b84482905e29a5d/PyYAML-6.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ce826d6ef20b1bc864f0a68340c8b3287705cae2f8b4b1d932177dcc76721725", size = 173302 }, 221 | { url = "https://files.pythonhosted.org/packages/c3/93/9916574aa8c00aa06bbac729972eb1071d002b8e158bd0e83a3b9a20a1f7/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1f71ea527786de97d1a0cc0eacd1defc0985dcf6b3f17bb77dcfc8c34bec4dc5", size = 739154 }, 222 | { url = "https://files.pythonhosted.org/packages/95/0f/b8938f1cbd09739c6da569d172531567dbcc9789e0029aa070856f123984/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9b22676e8097e9e22e36d6b7bda33190d0d400f345f23d4065d48f4ca7ae0425", size = 766223 }, 223 | { url = "https://files.pythonhosted.org/packages/b9/2b/614b4752f2e127db5cc206abc23a8c19678e92b23c3db30fc86ab731d3bd/PyYAML-6.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:80bab7bfc629882493af4aa31a4cfa43a4c57c83813253626916b8c7ada83476", size = 767542 }, 224 | { url = "https://files.pythonhosted.org/packages/d4/00/dd137d5bcc7efea1836d6264f049359861cf548469d18da90cd8216cf05f/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:0833f8694549e586547b576dcfaba4a6b55b9e96098b36cdc7ebefe667dfed48", size = 731164 }, 225 | { url = "https://files.pythonhosted.org/packages/c9/1f/4f998c900485e5c0ef43838363ba4a9723ac0ad73a9dc42068b12aaba4e4/PyYAML-6.0.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8b9c7197f7cb2738065c481a0461e50ad02f18c78cd75775628afb4d7137fb3b", size = 756611 }, 226 | { url = "https://files.pythonhosted.org/packages/df/d1/f5a275fdb252768b7a11ec63585bc38d0e87c9e05668a139fea92b80634c/PyYAML-6.0.2-cp312-cp312-win32.whl", hash = "sha256:ef6107725bd54b262d6dedcc2af448a266975032bc85ef0172c5f059da6325b4", size = 140591 }, 227 | { url = "https://files.pythonhosted.org/packages/0c/e8/4f648c598b17c3d06e8753d7d13d57542b30d56e6c2dedf9c331ae56312e/PyYAML-6.0.2-cp312-cp312-win_amd64.whl", hash = "sha256:7e7401d0de89a9a855c839bc697c079a4af81cf878373abd7dc625847d25cbd8", size = 156338 }, 228 | { url = "https://files.pythonhosted.org/packages/ef/e3/3af305b830494fa85d95f6d95ef7fa73f2ee1cc8ef5b495c7c3269fb835f/PyYAML-6.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:efdca5630322a10774e8e98e1af481aad470dd62c3170801852d752aa7a783ba", size = 181309 }, 229 | { url = "https://files.pythonhosted.org/packages/45/9f/3b1c20a0b7a3200524eb0076cc027a970d320bd3a6592873c85c92a08731/PyYAML-6.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:50187695423ffe49e2deacb8cd10510bc361faac997de9efef88badc3bb9e2d1", size = 171679 }, 230 | { url = "https://files.pythonhosted.org/packages/7c/9a/337322f27005c33bcb656c655fa78325b730324c78620e8328ae28b64d0c/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0ffe8360bab4910ef1b9e87fb812d8bc0a308b0d0eef8c8f44e0254ab3b07133", size = 733428 }, 231 | { url = "https://files.pythonhosted.org/packages/a3/69/864fbe19e6c18ea3cc196cbe5d392175b4cf3d5d0ac1403ec3f2d237ebb5/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:17e311b6c678207928d649faa7cb0d7b4c26a0ba73d41e99c4fff6b6c3276484", size = 763361 }, 232 | { url = "https://files.pythonhosted.org/packages/04/24/b7721e4845c2f162d26f50521b825fb061bc0a5afcf9a386840f23ea19fa/PyYAML-6.0.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70b189594dbe54f75ab3a1acec5f1e3faa7e8cf2f1e08d9b561cb41b845f69d5", size = 759523 }, 233 | { url = "https://files.pythonhosted.org/packages/2b/b2/e3234f59ba06559c6ff63c4e10baea10e5e7df868092bf9ab40e5b9c56b6/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:41e4e3953a79407c794916fa277a82531dd93aad34e29c2a514c2c0c5fe971cc", size = 726660 }, 234 | { url = "https://files.pythonhosted.org/packages/fe/0f/25911a9f080464c59fab9027482f822b86bf0608957a5fcc6eaac85aa515/PyYAML-6.0.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:68ccc6023a3400877818152ad9a1033e3db8625d899c72eacb5a668902e4d652", size = 751597 }, 235 | { url = "https://files.pythonhosted.org/packages/14/0d/e2c3b43bbce3cf6bd97c840b46088a3031085179e596d4929729d8d68270/PyYAML-6.0.2-cp313-cp313-win32.whl", hash = "sha256:bc2fa7c6b47d6bc618dd7fb02ef6fdedb1090ec036abab80d4681424b84c1183", size = 140527 }, 236 | { url = "https://files.pythonhosted.org/packages/fa/de/02b54f42487e3d3c6efb3f89428677074ca7bf43aae402517bc7cca949f3/PyYAML-6.0.2-cp313-cp313-win_amd64.whl", hash = "sha256:8388ee1976c416731879ac16da0aff3f63b286ffdd57cdeb95f3f2e085687563", size = 156446 }, 237 | ] 238 | 239 | [[package]] 240 | name = "resolvelib" 241 | version = "1.0.1" 242 | source = { registry = "https://pypi.org/simple" } 243 | sdist = { url = "https://files.pythonhosted.org/packages/ce/10/f699366ce577423cbc3df3280063099054c23df70856465080798c6ebad6/resolvelib-1.0.1.tar.gz", hash = "sha256:04ce76cbd63fded2078ce224785da6ecd42b9564b1390793f64ddecbe997b309", size = 21065 } 244 | wheels = [ 245 | { url = "https://files.pythonhosted.org/packages/d2/fc/e9ccf0521607bcd244aa0b3fbd574f71b65e9ce6a112c83af988bbbe2e23/resolvelib-1.0.1-py2.py3-none-any.whl", hash = "sha256:d2da45d1a8dfee81bdd591647783e340ef3bcb104b54c383f70d422ef5cc7dbf", size = 17194 }, 246 | ] 247 | 248 | [[package]] 249 | name = "rich" 250 | version = "13.9.4" 251 | source = { registry = "https://pypi.org/simple" } 252 | dependencies = [ 253 | { name = "markdown-it-py" }, 254 | { name = "pygments" }, 255 | ] 256 | sdist = { url = "https://files.pythonhosted.org/packages/ab/3a/0316b28d0761c6734d6bc14e770d85506c986c85ffb239e688eeaab2c2bc/rich-13.9.4.tar.gz", hash = "sha256:439594978a49a09530cff7ebc4b5c7103ef57baf48d5ea3184f21d9a2befa098", size = 223149 } 257 | wheels = [ 258 | { url = "https://files.pythonhosted.org/packages/19/71/39c7c0d87f8d4e6c020a393182060eaefeeae6c01dab6a84ec346f2567df/rich-13.9.4-py3-none-any.whl", hash = "sha256:6049d5e6ec054bf2779ab3358186963bac2ea89175919d699e378b99738c2a90", size = 242424 }, 259 | ] 260 | 261 | [[package]] 262 | name = "ruff" 263 | version = "0.9.4" 264 | source = { registry = "https://pypi.org/simple" } 265 | sdist = { url = "https://files.pythonhosted.org/packages/c0/17/529e78f49fc6f8076f50d985edd9a2cf011d1dbadb1cdeacc1d12afc1d26/ruff-0.9.4.tar.gz", hash = "sha256:6907ee3529244bb0ed066683e075f09285b38dd5b4039370df6ff06041ca19e7", size = 3599458 } 266 | wheels = [ 267 | { url = "https://files.pythonhosted.org/packages/b6/f8/3fafb7804d82e0699a122101b5bee5f0d6e17c3a806dcbc527bb7d3f5b7a/ruff-0.9.4-py3-none-linux_armv6l.whl", hash = "sha256:64e73d25b954f71ff100bb70f39f1ee09e880728efb4250c632ceed4e4cdf706", size = 11668400 }, 268 | { url = "https://files.pythonhosted.org/packages/2e/a6/2efa772d335da48a70ab2c6bb41a096c8517ca43c086ea672d51079e3d1f/ruff-0.9.4-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6ce6743ed64d9afab4fafeaea70d3631b4d4b28b592db21a5c2d1f0ef52934bf", size = 11628395 }, 269 | { url = "https://files.pythonhosted.org/packages/dc/d7/cd822437561082f1c9d7225cc0d0fbb4bad117ad7ac3c41cd5d7f0fa948c/ruff-0.9.4-py3-none-macosx_11_0_arm64.whl", hash = "sha256:54499fb08408e32b57360f6f9de7157a5fec24ad79cb3f42ef2c3f3f728dfe2b", size = 11090052 }, 270 | { url = "https://files.pythonhosted.org/packages/9e/67/3660d58e893d470abb9a13f679223368ff1684a4ef40f254a0157f51b448/ruff-0.9.4-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:37c892540108314a6f01f105040b5106aeb829fa5fb0561d2dcaf71485021137", size = 11882221 }, 271 | { url = "https://files.pythonhosted.org/packages/79/d1/757559995c8ba5f14dfec4459ef2dd3fcea82ac43bc4e7c7bf47484180c0/ruff-0.9.4-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:de9edf2ce4b9ddf43fd93e20ef635a900e25f622f87ed6e3047a664d0e8f810e", size = 11424862 }, 272 | { url = "https://files.pythonhosted.org/packages/c0/96/7915a7c6877bb734caa6a2af424045baf6419f685632469643dbd8eb2958/ruff-0.9.4-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:87c90c32357c74f11deb7fbb065126d91771b207bf9bfaaee01277ca59b574ec", size = 12626735 }, 273 | { url = "https://files.pythonhosted.org/packages/0e/cc/dadb9b35473d7cb17c7ffe4737b4377aeec519a446ee8514123ff4a26091/ruff-0.9.4-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:56acd6c694da3695a7461cc55775f3a409c3815ac467279dfa126061d84b314b", size = 13255976 }, 274 | { url = "https://files.pythonhosted.org/packages/5f/c3/ad2dd59d3cabbc12df308cced780f9c14367f0321e7800ca0fe52849da4c/ruff-0.9.4-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e0c93e7d47ed951b9394cf352d6695b31498e68fd5782d6cbc282425655f687a", size = 12752262 }, 275 | { url = "https://files.pythonhosted.org/packages/c7/17/5f1971e54bd71604da6788efd84d66d789362b1105e17e5ccc53bba0289b/ruff-0.9.4-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1d4c8772670aecf037d1bf7a07c39106574d143b26cfe5ed1787d2f31e800214", size = 14401648 }, 276 | { url = "https://files.pythonhosted.org/packages/30/24/6200b13ea611b83260501b6955b764bb320e23b2b75884c60ee7d3f0b68e/ruff-0.9.4-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfc5f1d7afeda8d5d37660eeca6d389b142d7f2b5a1ab659d9214ebd0e025231", size = 12414702 }, 277 | { url = "https://files.pythonhosted.org/packages/34/cb/f5d50d0c4ecdcc7670e348bd0b11878154bc4617f3fdd1e8ad5297c0d0ba/ruff-0.9.4-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faa935fc00ae854d8b638c16a5f1ce881bc3f67446957dd6f2af440a5fc8526b", size = 11859608 }, 278 | { url = "https://files.pythonhosted.org/packages/d6/f4/9c8499ae8426da48363bbb78d081b817b0f64a9305f9b7f87eab2a8fb2c1/ruff-0.9.4-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:a6c634fc6f5a0ceae1ab3e13c58183978185d131a29c425e4eaa9f40afe1e6d6", size = 11485702 }, 279 | { url = "https://files.pythonhosted.org/packages/18/59/30490e483e804ccaa8147dd78c52e44ff96e1c30b5a95d69a63163cdb15b/ruff-0.9.4-py3-none-musllinux_1_2_i686.whl", hash = "sha256:433dedf6ddfdec7f1ac7575ec1eb9844fa60c4c8c2f8887a070672b8d353d34c", size = 12067782 }, 280 | { url = "https://files.pythonhosted.org/packages/3d/8c/893fa9551760b2f8eb2a351b603e96f15af167ceaf27e27ad873570bc04c/ruff-0.9.4-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:d612dbd0f3a919a8cc1d12037168bfa536862066808960e0cc901404b77968f0", size = 12483087 }, 281 | { url = "https://files.pythonhosted.org/packages/23/15/f6751c07c21ca10e3f4a51ea495ca975ad936d780c347d9808bcedbd7182/ruff-0.9.4-py3-none-win32.whl", hash = "sha256:db1192ddda2200671f9ef61d9597fcef89d934f5d1705e571a93a67fb13a4402", size = 9852302 }, 282 | { url = "https://files.pythonhosted.org/packages/12/41/2d2d2c6a72e62566f730e49254f602dfed23019c33b5b21ea8f8917315a1/ruff-0.9.4-py3-none-win_amd64.whl", hash = "sha256:05bebf4cdbe3ef75430d26c375773978950bbf4ee3c95ccb5448940dc092408e", size = 10850051 }, 283 | { url = "https://files.pythonhosted.org/packages/c6/e6/3d6ec3bc3d254e7f005c543a661a41c3e788976d0e52a1ada195bd664344/ruff-0.9.4-py3-none-win_arm64.whl", hash = "sha256:585792f1e81509e38ac5123492f8875fbc36f3ede8185af0a26df348e5154f41", size = 10078251 }, 284 | ] 285 | 286 | [[package]] 287 | name = "srlinux-ansible-collection" 288 | version = "1.0.1" 289 | source = { virtual = "." } 290 | dependencies = [ 291 | { name = "ansible-core" }, 292 | ] 293 | 294 | [package.dev-dependencies] 295 | dev = [ 296 | { name = "rich" }, 297 | { name = "ruff" }, 298 | ] 299 | 300 | [package.metadata] 301 | requires-dist = [{ name = "ansible-core", specifier = "==2.16.2" }] 302 | 303 | [package.metadata.requires-dev] 304 | dev = [ 305 | { name = "rich", specifier = ">=13.9.4" }, 306 | { name = "ruff", specifier = ">=0.9.4" }, 307 | ] 308 | --------------------------------------------------------------------------------