├── .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 |
--------------------------------------------------------------------------------