├── tests ├── __init__.py ├── data │ └── __init__.py ├── lib │ ├── __init__.py │ ├── config.py │ ├── mock_ansible.py │ └── cvaas_configlet.py ├── unit │ ├── __init__.py │ ├── conftest.py │ └── test_configlet_input.py ├── system │ └── __init__.py ├── ansible.cfg ├── PR_testing │ ├── ansible.cfg │ └── README.md ├── activate-debug.env ├── Makefile └── pytest.ini ├── ansible_collections └── arista │ └── cvp │ ├── CNAME │ ├── plugins │ ├── __init__.py │ ├── module_utils │ │ ├── __init__.py │ │ ├── resources │ │ │ ├── __init__.py │ │ │ ├── api │ │ │ │ └── __init__.py │ │ │ ├── modules │ │ │ │ └── __init__.py │ │ │ ├── schemas │ │ │ │ └── __init__.py │ │ │ └── exceptions.py │ │ ├── tools_schema.py │ │ ├── generic_tools.py │ │ ├── tools_inventory.py │ │ └── logger.py │ └── README.md │ ├── changelogs │ ├── fragments │ │ └── .gitkeep │ ├── fragments_backup │ │ ├── v3.12.0.yml │ │ ├── v3.10.0.yml │ │ ├── v3.5.1.yml │ │ ├── v3.10.1.yml │ │ ├── v3.7.0.yml │ │ ├── v3.6.0.yml │ │ ├── v3.9.0.yml │ │ ├── v3.11.0.yml │ │ ├── v3.6.1.yml │ │ ├── v3.8.0.yml │ │ ├── v3.5.0.yml │ │ └── v3.4.0.yml │ └── config.yaml │ ├── molecule │ ├── cv_device │ │ ├── inventory │ │ │ ├── host_vars │ │ │ │ └── all.yml │ │ │ ├── hosts.yml │ │ │ └── group_vars │ │ │ │ └── all │ │ │ │ └── credentials.yml │ │ ├── verify.yml │ │ ├── molecule.yml │ │ └── converge.yml │ ├── cv_configlet_loose │ │ ├── inventory │ │ │ ├── host_vars │ │ │ │ └── all.yml │ │ │ ├── hosts.yml │ │ │ └── group_vars │ │ │ │ └── all │ │ │ │ └── credentials.yml │ │ ├── verify.yml │ │ ├── molecule.yml │ │ └── converge.yml │ ├── cv_configlet_strict │ │ ├── inventory │ │ │ ├── host_vars │ │ │ │ └── all.yml │ │ │ ├── hosts.yml │ │ │ └── group_vars │ │ │ │ └── all │ │ │ │ └── credentials.yml │ │ ├── verify.yml │ │ ├── molecule.yml │ │ └── converge.yml │ ├── dhcp_management_offline │ │ ├── inventory │ │ │ ├── host_vars │ │ │ │ └── all.yml │ │ │ ├── hosts │ │ │ └── group_vars │ │ │ │ └── TOOLS.yml │ │ ├── converge.yml │ │ ├── destroy.yml │ │ ├── molecule.yml │ │ └── intended │ │ │ └── configs │ │ │ └── dhcpd.conf │ ├── cv_task_v3 │ │ ├── converge.yml │ │ └── molecule.yml │ ├── cv_configlet_v3 │ │ ├── converge.yml │ │ └── molecule.yml │ ├── fixtures │ │ └── cv_device_v3.yaml │ ├── cv_validate_v3 │ │ ├── converge.yml │ │ └── molecule.yml │ ├── MOLECULE_SCENARIOS.txt │ ├── cv_facts_v3 │ │ ├── converge.yml │ │ ├── molecule.yml │ │ └── test_image_facts.yml │ ├── cv_tag_v3 │ │ ├── converge.yml │ │ └── molecule.yml │ ├── cv_container_v3 │ │ ├── converge.yml │ │ └── molecule.yml │ ├── dhcp_system_mac │ │ ├── verify.yml │ │ ├── molecule.yml │ │ └── converge.yml │ ├── dhcp_management_mac │ │ ├── verify.yml │ │ ├── converge.yml │ │ └── molecule.yml │ ├── cv_image_v3 │ │ ├── converge.yml │ │ └── molecule.yml │ ├── cv_change_control_v3 │ │ ├── converge.yml │ │ └── molecule.yml │ ├── cv_device_v3 │ │ ├── converge.yml │ │ ├── molecule.yml │ │ └── reconcile.py │ └── README.md │ ├── bindep.txt │ ├── roles │ ├── configlets_sync │ │ ├── vars │ │ │ └── main.yml │ │ ├── meta │ │ │ └── main.yml │ │ ├── files │ │ │ └── ansible-cvp-sync.png │ │ ├── handlers │ │ │ └── main.yml │ │ ├── tasks │ │ │ ├── main.yml │ │ │ ├── sync.yml │ │ │ ├── init.yml │ │ │ ├── push.yml │ │ │ └── pull.yml │ │ ├── templates │ │ │ ├── template.cvp.shared_configlets.j2 │ │ │ ├── template.shared_configlets.j2 │ │ │ ├── template.device_configlets.j2 │ │ │ └── template.compare_configlets.j2 │ │ ├── defaults │ │ │ └── main.yml │ │ └── .travis.yml │ └── dhcp_configuration │ │ ├── vars │ │ ├── main.yml │ │ ├── centos-7.yml │ │ ├── centos-8.yml │ │ └── debian.yml │ │ ├── meta │ │ └── main.yml │ │ ├── tasks │ │ ├── offline.yml │ │ ├── fix-debian.yml │ │ ├── main.yml │ │ └── online.yml │ │ ├── handlers │ │ └── main.yml │ │ ├── .travis.yml │ │ └── defaults │ │ └── main.yml │ ├── examples │ ├── cv_validate_v3 │ │ ├── configlet1.cfg │ │ ├── configlet2.cfg │ │ ├── configlet4.cfg │ │ ├── configlet5.cfg │ │ ├── configlet3_multiline.cfg │ │ ├── device_validate_config_multiline.yaml │ │ ├── device_validate_config_error_local.yaml │ │ ├── device_validate_config_valid_local.yaml │ │ ├── device_validate_config_multiple.yaml │ │ ├── device_validate_config_error_cvp.yaml │ │ ├── device_validate_config_warning_cvp.yaml │ │ ├── device_validate_config_warning_mix.yaml │ │ └── device_validate_config_multiple_cvp.yaml │ ├── cv_device_v3 │ │ ├── device_decommissioning.yaml │ │ ├── device_factory_reset.yaml │ │ ├── device_provisioning_reset.yaml │ │ ├── device_config_assign_fqdn.yaml │ │ ├── device_config_assign_strict.yaml │ │ ├── device_config_assign_loose_sn.yaml │ │ ├── remove-image-bundle-to-device.yaml │ │ ├── assign-image-bundle-to-device.yaml │ │ └── device_config_assign_atd.yaml │ ├── cv_tag_v3 │ │ ├── create.delete-device.yml │ │ ├── create.delete-interface.yml │ │ ├── assign.unassign-device.yml │ │ ├── assign.unassign-device-autocreate.yml │ │ ├── assign.unassign-interface.yml │ │ └── assign.unassign-interface-autocreate.yml │ ├── inventory.yml │ ├── cv_image_v3 │ │ ├── image_upload.yaml │ │ ├── bundle_delete.yaml │ │ ├── bundle_create.yaml │ │ └── image_delete.yaml │ ├── cv_container_v3 │ │ ├── container_add_image_bundle.yaml │ │ └── container_remove_image_bundle.yaml │ ├── cv_configlet_v3 │ │ ├── push_configet.yaml │ │ └── delete_configlet.yaml │ ├── cv_facts_v3 │ │ ├── get_images_facts.yml │ │ ├── get_configlets_facts.yml │ │ ├── get_containers_facts.yml │ │ ├── get_tasks_facts.yml │ │ └── get_devices_facts.yml │ └── cv_change_control_v3 │ │ ├── change_control_v3_remove_unknown.yml │ │ ├── change_control_v3_create_schedule.yml │ │ ├── change_control_v3_create_approve_execute.yml │ │ ├── change_control_v3_create_remove.yml │ │ ├── change_control_v3_create_schedule_approve.yml │ │ ├── change_control_v3_show_unknown.yml │ │ ├── change_control_v3_create_approve_and_execute.yml │ │ ├── change_control_v3_create_schedule_and_approve.yml │ │ ├── change_control_v3_create_unapprove.yml │ │ └── change_control_v3_create_show.yml │ ├── docs │ ├── _media │ │ ├── favicon.ico │ │ ├── custom_action_id.png │ │ ├── cv_ansible_logo.png │ │ ├── serviceaccount1.png │ │ ├── serviceaccount2.png │ │ ├── serviceaccount3.png │ │ └── built_in_action_id.png │ ├── requirements.txt │ ├── outputs │ │ ├── cv_configlet.txt │ │ ├── cv_task_v3.txt │ │ ├── cv_validate_v3.txt │ │ ├── cv_facts.txt │ │ ├── cv_container.txt │ │ ├── cv_tag_v3.txt │ │ ├── cv_container_v3.txt │ │ ├── cv_configlet_v3.txt │ │ └── cv_device_v3.txt │ ├── stylesheets │ │ └── tables.js │ ├── schema │ │ ├── cv_container_v3.md │ │ ├── cv_validate_v3.md │ │ ├── cv_tag_v3.md │ │ └── cv_device_v3.md │ ├── modules │ │ ├── cv_task_v3.md │ │ └── cv_task.md │ ├── _overrides │ │ └── main.html │ ├── installation │ │ └── requirements.md │ ├── build-md │ │ ├── doc.py │ │ └── templates │ │ │ └── md.j2 │ └── faq │ │ └── errors.md │ ├── medias │ └── ansible-cloudvision.png │ ├── requirements.txt │ ├── collections.yml │ ├── tests │ ├── config.yml │ └── sanity │ │ ├── ignore-2.15.txt │ │ ├── ignore-2.16.txt │ │ └── ignore-2.17.txt │ ├── meta │ ├── execution-environment.yml │ └── runtime.yml │ ├── requirements-dev.txt │ ├── .ansible-lint │ └── .yamllint ├── .sourcery.yaml ├── development ├── requirements.txt ├── requirements-dev.txt ├── docker-stack.env ├── license-short.txt ├── docker-compose.yml ├── README.md ├── entrypoint.sh └── install.sh ├── .github ├── requirements-ci-molecule.txt ├── .markdownlintignore ├── version.sh ├── reviewers.yml ├── yamllintrc ├── workflows │ ├── pull-request-conflict.yml │ ├── stale.yml │ ├── tag-management.yml │ ├── publish-pages.yml │ └── pull-request-releases.yml ├── stale.yml ├── PULL_REQUEST_TEMPLATE.md ├── check-git-status.sh ├── .markdownlint.yaml └── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── .git-commit-template ├── .readthedocs.yaml └── .gitignore /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/data/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/lib/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/system/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/CNAME: -------------------------------------------------------------------------------- 1 | cvp.avd.sh 2 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.sourcery.yaml: -------------------------------------------------------------------------------- 1 | refactor: 2 | python_version: '3.7' 3 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/resources/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_device/inventory/host_vars/all.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/resources/api/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /development/requirements.txt: -------------------------------------------------------------------------------- 1 | ../ansible_collections/arista/cvp/requirements.txt -------------------------------------------------------------------------------- /.github/requirements-ci-molecule.txt: -------------------------------------------------------------------------------- 1 | molecule>6.0 2 | molecule-docker>=0.2.4 3 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/resources/modules/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/resources/schemas/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_loose/inventory/host_vars/all.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_strict/inventory/host_vars/all.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /development/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | ../ansible_collections/arista/cvp/requirements-dev.txt -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/bindep.txt: -------------------------------------------------------------------------------- 1 | make 2 | wget 3 | curl 4 | less 5 | git 6 | vim 7 | sshpass 8 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for configlets-sync 3 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for dhcp_configuration 3 | -------------------------------------------------------------------------------- /.github/.markdownlintignore: -------------------------------------------------------------------------------- 1 | ansible_collections/arista/cvp/docs/modules/ 2 | ansible_collections/arista/cvp/docs/_build/ 3 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/configlet1.cfg: -------------------------------------------------------------------------------- 1 | interface Ethernet100 2 | description test 3 | ! 4 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/configlet2.cfg: -------------------------------------------------------------------------------- 1 | ruter bgp 1111 2 | neighbor 1.1.1.1 remote-bs 10 3 | ! 4 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_offline/inventory/host_vars/all.yml: -------------------------------------------------------------------------------- 1 | --- 2 | root_dir: '{{playbook_dir}}' 3 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_task_v3/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run cv_task_v3 3 | import_playbook: test_cv_task_v3.yml 4 | -------------------------------------------------------------------------------- /development/docker-stack.env: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GIT_USER=$(git config --get user.name) 4 | export GIT_EMAIL=$(git config --get user.email) 5 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_v3/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run cv_configlet_v3 3 | import_playbook: test_cv_configlet_v3.yaml 4 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/fixtures/cv_device_v3.yaml: -------------------------------------------------------------------------------- 1 | - node: 192.168.0.5 2 | username: arista 3 | password: "" 4 | device: s1-leaf1 5 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/_media/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aristanetworks/ansible-cvp/HEAD/ansible_collections/arista/cvp/docs/_media/favicon.ico -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_validate_v3/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run validate config valid_case 3 | import_playbook: device_validate_config.yaml 4 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | description: Role to configure DHCP service to act as ZTP server 3 | standalone: false 4 | -------------------------------------------------------------------------------- /development/license-short.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Arista Networks, Inc. 2 | Use of this source code is governed by the Apache License 2.0 3 | that can be found in the LICENSE file. 4 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/_media/custom_action_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aristanetworks/ansible-cvp/HEAD/ansible_collections/arista/cvp/docs/_media/custom_action_id.png -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/_media/cv_ansible_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aristanetworks/ansible-cvp/HEAD/ansible_collections/arista/cvp/docs/_media/cv_ansible_logo.png -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/_media/serviceaccount1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aristanetworks/ansible-cvp/HEAD/ansible_collections/arista/cvp/docs/_media/serviceaccount1.png -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/_media/serviceaccount2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aristanetworks/ansible-cvp/HEAD/ansible_collections/arista/cvp/docs/_media/serviceaccount2.png -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/_media/serviceaccount3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aristanetworks/ansible-cvp/HEAD/ansible_collections/arista/cvp/docs/_media/serviceaccount3.png -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/medias/ansible-cloudvision.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aristanetworks/ansible-cvp/HEAD/ansible_collections/arista/cvp/medias/ansible-cloudvision.png -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_device/inventory/hosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | children: 4 | CVP: 5 | hosts: 6 | cv_server: 7 | ansible_user: root 8 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/requirements.txt: -------------------------------------------------------------------------------- 1 | netaddr>=0.7.19 2 | Jinja2>=2.10.3 3 | paramiko>=2.7.1 4 | requests>=2.22.0 5 | cvprac>=1.4.0 6 | jsonschema>=3.2.0 7 | treelib>=1.5.5 8 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/_media/built_in_action_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aristanetworks/ansible-cvp/HEAD/ansible_collections/arista/cvp/docs/_media/built_in_action_id.png -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | description: Role to synchronize configlets across multiple CloudVision servers 3 | standalone: false 4 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_loose/inventory/hosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | children: 4 | CVP: 5 | hosts: 6 | cv_server: 7 | ansible_user: root 8 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_strict/inventory/hosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | children: 4 | CVP: 5 | hosts: 6 | cv_server: 7 | ansible_user: root 8 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_offline/inventory/hosts: -------------------------------------------------------------------------------- 1 | all: 2 | children: 3 | AVD_LAB: 4 | children: 5 | TOOLS: 6 | hosts: 7 | dhcp_server01: 8 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.12.0.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.12.0 - See documentation on cvp.avd.sh for details. 3 | 4 | minor_changes: 5 | - Add support for Ansible 2.18.x 6 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/files/ansible-cvp-sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aristanetworks/ansible-cvp/HEAD/ansible_collections/arista/cvp/roles/configlets_sync/files/ansible-cvp-sync.png -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.10.0.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.10.0 - See documentation on cvp.avd.sh for details. 3 | 4 | minor_changes: 5 | - Bump required ansible version (#683) 6 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.5.1.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.5.1 - See documentation on cvp.avd.sh for details. 3 | 4 | bugfixes: 5 | - Fix Bump update min cvprac version(#547) 6 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/collections.yml: -------------------------------------------------------------------------------- 1 | # collection requirements leveraged by molecule 2 | collections: 3 | - arista.eos 4 | - ansible.netcommon 5 | - community.general 6 | - community.docker 7 | - ansible.posix 8 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/MOLECULE_SCENARIOS.txt: -------------------------------------------------------------------------------- 1 | cv_change_control_v3 2 | cv_configlet_v3 3 | cv_device_v3 4 | cv_tag_v3 5 | cv_container_v3 6 | cv_facts_v3 7 | cv_task_v3 8 | cv_image_v3 9 | cv_validate_v3 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_facts_v3/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Test cv_facts_v3 module 3 | import_playbook: test_cv_facts_v3.yml 4 | 5 | - name: Test cv_facts_v3 module 6 | import_playbook: test_image_facts.yml 7 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_tag_v3/converge.yml: -------------------------------------------------------------------------------- 1 | - name: Run cv_tag_v3 device 2 | import_playbook: test_cv_tag_v3_device.yml 3 | 4 | - name: Run cv_tag_v3 interface 5 | import_playbook: test_cv_tag_v3_interface.yml 6 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.10.1.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.10.1 - See documentation on cvp.avd.sh for details. 3 | 4 | bugfixes: 5 | - Update documentation with new collection requirements 6 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/tests/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Sample ansible-test configuration file for collections: https://github.com/ansible/ansible/blob/devel/test/lib/ansible_test/config/config.yml 3 | modules: 4 | python_requires: ">=3.9" 5 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/configlet4.cfg: -------------------------------------------------------------------------------- 1 | interface Ethernet100 2 | description test 3 | ! 4 | interface Ethernet1 5 | spanning-tree portfast 6 | ! 7 | ruter ospf1 8 | network 10.0.0.0/24 area 0 9 | ! 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/configlet5.cfg: -------------------------------------------------------------------------------- 1 | interface Ethernet100 2 | description test 3 | ! 4 | interface Ethernet1 5 | spanning-tree portfast 6 | ! 7 | interface Ethernet2 8 | spanning-tree portfast 9 | ! 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_container_v3/converge.yml: -------------------------------------------------------------------------------- 1 | - name: Run cv_container_v3 2 | import_playbook: test_cv_container_v3_configlet.yaml 3 | 4 | - name: Run cv_container_v3 5 | import_playbook: test_cv_container_v3_image_bundle.yaml 6 | -------------------------------------------------------------------------------- /.github/version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | BRANCH=$(git rev-parse --symbolic-full-name --abbrev-ref HEAD) 4 | 5 | if [[ $BRANCH == *"/"* ]]; then 6 | echo ${BRANCH} | awk -F '/' '{print $2}' 7 | else 8 | echo $(git describe --abbrev=0 --tags) 9 | fi 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/requirements.txt: -------------------------------------------------------------------------------- 1 | mkdocs 2 | mkdocs-bootswatch 3 | mkdocs-material 4 | mkdocs-material-extensions 5 | pymdown-extensions 6 | mdx_truly_sane_lists 7 | mkdocs-git-revision-date-localized-plugin 8 | mkdocs-git-revision-date-plugin 9 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/configlet3_multiline.cfg: -------------------------------------------------------------------------------- 1 | interface Ethernet100 2 | description test 3 | ! 4 | interface Ethernet1 5 | spanning-tree portfast 6 | ! 7 | ruter bgp 1111 8 | neighbor 1.1.1.1 remote-bs 10 9 | ! 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_system_mac/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This is an example playbook to execute Ansible tests. 3 | 4 | - name: Verify 5 | hosts: all 6 | tasks: 7 | - name: Example assertion 8 | assert: 9 | that: true 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_mac/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # This is an example playbook to execute Ansible tests. 3 | 4 | - name: Verify 5 | hosts: all 6 | tasks: 7 | - name: Example assertion 8 | assert: 9 | that: true 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_image_v3/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run cv_image_v3_bundle_upload 3 | import_playbook: test_cv_image_v3_bundle_upload.yml 4 | 5 | - name: Run cv_image_v3_image_upload 6 | import_playbook: test_cv_image_v3_image_upload.yml 7 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/outputs/cv_configlet.txt: -------------------------------------------------------------------------------- 1 | cvp_configlet: 2 | changed: true 3 | data: 4 | deleted: [] 5 | new: 6 | - Test_Configlet: success 7 | tasks: [] 8 | updated: [] 9 | diff: '' 10 | failed: false 11 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/handlers/main.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | # handlers file for configlets-sync 6 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/vars/centos-7.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables for Red Hat and Centos system 3 | 4 | # List of packages to install to enable DHCP package 5 | dhcp_packages: 6 | - dhcp 7 | 8 | # DHCP service name 9 | dhcp_service: dhcpd 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/vars/centos-8.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables for Red Hat and Centos system 3 | 4 | # List of packages to install to enable DHCP package 5 | dhcp_packages: 6 | - dhcp-server 7 | 8 | # DHCP service name 9 | dhcp_service: dhcpd 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/meta/execution-environment.yml: -------------------------------------------------------------------------------- 1 | # execution-environment.yml 2 | # Read by ansible-builder when this collection is a dependency for an execution-environment 3 | --- 4 | version: 1 5 | 6 | dependencies: 7 | galaxy: collections.yml 8 | python: requirements.txt 9 | system: bindep.txt 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.7.0.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.7.0 - See documentation on cvp.avd.sh for details. 3 | 4 | minor_changes: 5 | - Feat(cv_device_v3) Configlet Validation support (#615) 6 | 7 | bugfixes: 8 | - Fix(cv_device_v3) Increment counter when we remove devices (#629) 9 | -------------------------------------------------------------------------------- /tests/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | hash_behaviour=merge 3 | inventory= inventory.ini 4 | deprecation_warnings=True 5 | command_warnings=True 6 | retry_files_enabled = False 7 | host_key_checking = False 8 | collections_paths = $PWD/./:~/.ansible/collections:/usr/share/ansible/collections 9 | callback_whitelist = profile_tasks 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/stylesheets/tables.js: -------------------------------------------------------------------------------- 1 | // Source: https://squidfunk.github.io/mkdocs-material/reference/data-tables/#sortable-tables 2 | document$.subscribe(function() { 3 | var tables = document.querySelectorAll("article table:not([class])") 4 | tables.forEach(function(table) { 5 | new Tablesort(table) 6 | }) 7 | }) 8 | -------------------------------------------------------------------------------- /development/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | webdoc_cvp: 3 | image: python:3.11.0-alpine3.17 4 | container_name: webdoc_cvp 5 | working_dir: /data 6 | volumes: 7 | - ${PWD}/:/data 8 | ports: 9 | - 127.0.0.1:8000:8000 10 | network_mode: host 11 | entrypoint: "sh /data/development/entrypoint.sh" 12 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/outputs/cv_task_v3.txt: -------------------------------------------------------------------------------- 1 | msg: 2 | actions_manager: 3 | actions_manager_count: 2 4 | actions_manager_list: 5 | - task_747 6 | - task_748 7 | changed: true 8 | diff: {} 9 | success: true 10 | taskIds: [] 11 | changed: true 12 | failed: false 13 | success: true 14 | taskIds: [] 15 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | # Manual action selection 6 | - name: 'Load tasks for {{ action }}' 7 | ansible.builtin.include_tasks: "{{ action }}.yml" 8 | -------------------------------------------------------------------------------- /development/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Development Process 8 | 9 | Current page has moved to [documentation website](https://www.avd.sh/en/latest/docs/installation/development/) 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.6.0.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.6.0 - See documentation on cvp.avd.sh for details. 3 | 4 | minor_changes: 5 | - Feat(cv_container_v3) Add support for image bundles on containers(#550) 6 | 7 | bugfixes: 8 | - Fix(cv_facts_v3) Handle stale and empty devices in the inventory having an image bundle of type None(#569) 9 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/vars/debian.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Variables for Red Hat and Centos system 3 | 4 | # List of packages to install to enable DHCP package 5 | dhcp_packages: 6 | - isc-dhcp-server 7 | 8 | # DHCP service name 9 | dhcp_service: isc-dhcp-server 10 | 11 | # AppArmor Policy for isc-dhcp 12 | dhcp_apparmor_policy: /etc/apparmor.d/usr.sbin.dhcpd 13 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_device_v3/device_decommissioning.yaml: -------------------------------------------------------------------------------- 1 | - name: Decommission device 2 | hosts: cv_server 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | CVP_DEVICES: 7 | - fqdn: leaf1 8 | parentContainerName: "" 9 | tasks: 10 | - name: decommission device 11 | arista.cvp.cv_device_v3: 12 | devices: '{{CVP_DEVICES}}' 13 | state: absent 14 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_device_v3/device_factory_reset.yaml: -------------------------------------------------------------------------------- 1 | - name: Factory reset device 2 | hosts: cv_server 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | CVP_DEVICES: 7 | - fqdn: leaf2 8 | parentContainerName: "" 9 | tasks: 10 | - name: remove device 11 | arista.cvp.cv_device_v3: 12 | devices: '{{CVP_DEVICES}}' 13 | state: factory_reset 14 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_device_v3/device_provisioning_reset.yaml: -------------------------------------------------------------------------------- 1 | - name: Remove device 2 | hosts: cv_server 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | CVP_DEVICES: 7 | - fqdn: leaf2 8 | parentContainerName: "" 9 | tasks: 10 | - name: remove device 11 | arista.cvp.cv_device_v3: 12 | devices: '{{CVP_DEVICES}}' 13 | state: provisioning_reset 14 | -------------------------------------------------------------------------------- /.github/reviewers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ansible_collections/arista/cvp/roles/**/*: 3 | - sugetha24 4 | ansible_collections/arista/cvp/plugins/**/*: 5 | - guillaumeVilar 6 | - sugetha24 7 | - noredistribution 8 | ansible_collections/arista/cvp/molecule/**/*: 9 | - guillaumeVilar 10 | - sugetha24 11 | - noredistribution 12 | ansible_collections/arista/cvp/docs/**/*: 13 | - sugetha24 14 | .github/**: 15 | - sugetha24 16 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.9.0.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.9.0 - See documentation on cvp.avd.sh for details. 3 | 4 | minor_changes: 5 | - Feat(cv_facts_v3) raise errors when svcaccount/user is not authorized (#677) 6 | 7 | bugfixes: 8 | - Fix(cv_tag_v3) Long workspace name to avoid conflict (#679) 9 | - Fix(cv_device_v3) Reconciled configlets are not treated specially (#667) 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/outputs/cv_validate_v3.txt: -------------------------------------------------------------------------------- 1 | msg: 2 | changed: false 3 | configlets_validated: 4 | changed: false 5 | configlets_validated_count: 1 6 | configlets_validated_list: 7 | - validate_valid_on_serialNumber_validated 8 | diff: {} 9 | errors: [] 10 | success: true 11 | taskIds: [] 12 | warnings: [] 13 | failed: false 14 | success: true 15 | taskIds: [] 16 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.11.0.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.11.0 - See documentation on cvp.avd.sh for details. 3 | 4 | bugfixes: 5 | - Cv_validate_v3 broken with cvprac 1.4.0 due to argument keyword change 6 | - Update Readme with correct ansible-core version 7 | - Allow cv_device_v3 to handle SN-based decomms 8 | - Device decommissioning fails if a device is not streaming and not provisioned 9 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_offline/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: TOOLS 4 | gather_facts: false 5 | connection: local 6 | tasks: 7 | 8 | - name: generate intented variables 9 | delegate_to: localhost 10 | import_role: 11 | name: arista.cvp.dhcp_configuration 12 | vars: 13 | mode: offline 14 | output_dir: '{{playbook_dir}}/intended/configs/' 15 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/templates/template.cvp.shared_configlets.j2: -------------------------------------------------------------------------------- 1 | {# 2 | Copyright (c) 2023-2025 Arista Networks, Inc. 3 | Use of this source code is governed by the Apache License 2.0 4 | that can be found in the LICENSE file. 5 | #} 6 | --- 7 | CVP_CONFIGLET: 8 | {% for configlet in shared_configlets %} 9 | {{configlet}}: "{{shared_configlets[configlet]['config'] | replace('\n', '\\n')}}" 10 | {% endfor %} 11 | -------------------------------------------------------------------------------- /.git-commit-template: -------------------------------------------------------------------------------- 1 | 2 | #[optional scope]: 3 | 4 | # Optional description 5 | 6 | # BREAKING CHANHE: < description > 7 | # 8 | # Types: Feat / Fix / Cut / Doc / CI / Start / Stop / Bump / Test / Make / Refactor / Reformat / Optimize / License / Revert 9 | # Scopes: cv_device_v3 cv_container_v3 cv_configlet_v3 cv_task_v3 module_utils dhcp_configuration mkdoc contribution how-to actions molecule ansible github requirements pytest 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/tasks/offline.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | - name: 'Generate DHCPd configuration file' 6 | ansible.builtin.template: 7 | src: 'dhcpd.conf.j2' 8 | dest: '{{ output_dir }}/dhcpd.conf' 9 | backup: true 10 | mode: 0644 11 | delegate_to: localhost 12 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/tasks/fix-debian.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | - name: 'Set a default listening interface' 6 | ansible.builtin.lineinfile: 7 | dest: /etc/default/isc-dhcp-server 8 | line: 'INTERFACESv4="{{ ansible_default_ipv4.interface }}"' 9 | regexp: '^INTERFACESv4=' 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.6.1.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.6.1 - See documentation on cvp.avd.sh for details. 3 | 4 | bugfixes: 5 | - Fix(cv_container_v3) check_mode error(#584) 6 | - Fix(cv_device_v3) Implement device check mode(#571) 7 | - Fix(cv_device_v3) Device decommission failure scenario(#577) 8 | - Fix(cv_device_v3) Fix check for missing devices(#593) 9 | - Fix(cv_tag_v3) Allow tag assignment to serial numbers(#581) 10 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_offline/destroy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Remove output folders 3 | hosts: all 4 | gather_facts: false 5 | connection: local 6 | tasks: 7 | - name: delete local folders 8 | delegate_to: 127.0.0.1 9 | run_once: true 10 | file: 11 | path: "{{playbook_dir}}/{{ item }}" 12 | state: absent 13 | with_items: 14 | - documentation 15 | - config_backup 16 | - reports 17 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.8.0.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.8.0 - See documentation on cvp.avd.sh for details. 3 | 4 | minor_changes: 5 | - Feat(cv_facts_v3) Added 'images', 'tasks' options and containers 'regexp_filter' (#632) 6 | - Feat(cv_device_v3) Add "inventory_mode" to allow ignoring missing devices (#594) 7 | 8 | bugfixes: 9 | - Fix(cv_task_v3) Update cv_task_v3 module documentation (#604) 10 | - Fix(cv_tag_v3) Delete assigned tags (#634) 11 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | ansible-core>=2.16.0,<2.19.0 2 | ansible-builder 3 | ansible-lint>=24.7.0 4 | galaxy-importer>=0.3.1 5 | pycodestyle 6 | flake8 7 | pylint>=2.16.1 8 | twine 9 | pre-commit>=2.9.2 10 | pre-commit-hooks>=3.3.0 11 | identify>=1.4.20 12 | docker 13 | molecule>6.0 14 | molecule-docker>=0.2.4 15 | mitogen 16 | pytest 17 | pytest-cov>=2.11.1 18 | pytest-html>=3.1.1 19 | pytest-metadata>=1.11.0 20 | pytest-dependency 21 | jmespath 22 | pytest-mock 23 | -------------------------------------------------------------------------------- /tests/PR_testing/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | host_key_checking = False 3 | inventory = inventory.yml 4 | gathering = explicit 5 | retry_files_enabled = False 6 | collections_paths = /home/coder/project/persist/arista-ansible/ansible-cvp 7 | jinja2_extensions = jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 8 | # enable the YAML callback plugin 9 | stdout_callback = yaml 10 | # enable the stdout_callback when running ad-hoc commands 11 | bin_ansible_callbacks = True 12 | interpreter_python = auto_silent 13 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/tasks/sync.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | # Run everything if user set action=sync 5 | - name: 'Run init phase' 6 | ansible.builtin.include_tasks: "init.yml" 7 | 8 | - name: 'Run pull phase' 9 | ansible.builtin.include_tasks: "pull.yml" 10 | 11 | - name: 'Run push phase' 12 | ansible.builtin.include_tasks: "push.yml" 13 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_change_control_v3/converge.yml: -------------------------------------------------------------------------------- 1 | - name: Test Create, Approve and Execute Change Control 2 | import_playbook: test_create_approve_execute_cc.yml 3 | 4 | - name: Test Show and Remove Change Control 5 | import_playbook: test_show_and_remove_cc.yml 6 | 7 | - name: Test Unapprove and Execute Change Control 8 | import_playbook: test_unapprove_and_execute_cc.yml 9 | 10 | - name: Test Schedule and Approve Change Control 11 | import_playbook: test_schedule_and_approve_cc.yml 12 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for configlets-sync 3 | 4 | # Dir where to store configlets information 5 | cvpsync_data: 'generated_vars/' 6 | 7 | common_configlets_dir: '{{ cvpsync_data }}/common_configlets/' 8 | cvp_servers_dir: '{{ cvpsync_data }}/cvp_servers/' 9 | devices_dir: '{{ cvpsync_data }}/devices/' 10 | 11 | 12 | # Configlet filter to look for 13 | configlet_filter: 'shared' 14 | 15 | # Ansible runner for delegation 16 | ansible_runner: localhost 17 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_tag_v3/create.delete-device.yml: -------------------------------------------------------------------------------- 1 | - name: Test cv_tag_v3 2 | hosts: CloudVision 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | CVP_TAGS: 7 | - device_tags: 8 | - name: leaf1DTag1 9 | value: leaf1DVal1 10 | - name: leaf1DTag2 11 | value: leaf1DVal2 12 | tasks: 13 | - name: create/delete device tags 14 | arista.cvp.cv_tag_v3: 15 | tags: "{{CVP_TAGS}}" 16 | mode: create 17 | # mode: delete 18 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_device/inventory/group_vars/all/credentials.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ansible_httpapi_host: '{{ ansible_host }}' 3 | ansible_connection: httpapi 4 | # ansible_httpapi_use_ssl: true 5 | # ansible_httpapi_validate_certs: false 6 | # ansible_httpapi_session_key: '/Users/tgrimonet/Projects/cvp-resource-api-testing/cvp.crt' 7 | ansible_network_os: eos 8 | # ansible_httpapi_port: 443 9 | # Configuration to get Virtual Env information 10 | ansible_python_interpreter: $(which python) 11 | 12 | root_dir: '{{playbook_dir}}' 13 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/.ansible-lint: -------------------------------------------------------------------------------- 1 | --- 2 | profile: production 3 | 4 | skip_list: 5 | - name[template] # Remove warnings on task names where Jinja template is not at the end 6 | - run-once[task] # Remove warnings on using run_once with other strategies 7 | - meta-no-info # Ansible-lint does not honor meta-schema with standalone: false 8 | - var-naming[no-role-prefix] 9 | - var-naming[pattern] 10 | # kinds: 11 | # - yaml: "**/molecule/**/inventory/host_vars/roles.yml" 12 | exclude_paths: 13 | - molecule/ 14 | - examples/ 15 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_loose/inventory/group_vars/all/credentials.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ansible_httpapi_host: '{{ ansible_host }}' 3 | ansible_connection: httpapi 4 | # ansible_httpapi_use_ssl: true 5 | # ansible_httpapi_validate_certs: false 6 | # ansible_httpapi_session_key: '/Users/tgrimonet/Projects/cvp-resource-api-testing/cvp.crt' 7 | ansible_network_os: eos 8 | # ansible_httpapi_port: 443 9 | # Configuration to get Virtual Env information 10 | ansible_python_interpreter: $(which python) 11 | 12 | root_dir: '{{playbook_dir}}' 13 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_strict/inventory/group_vars/all/credentials.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ansible_httpapi_host: '{{ ansible_host }}' 3 | ansible_connection: httpapi 4 | # ansible_httpapi_use_ssl: true 5 | # ansible_httpapi_validate_certs: false 6 | # ansible_httpapi_session_key: '/Users/tgrimonet/Projects/cvp-resource-api-testing/cvp.crt' 7 | ansible_network_os: eos 8 | # ansible_httpapi_port: 443 9 | # Configuration to get Virtual Env information 10 | ansible_python_interpreter: $(which python) 11 | 12 | root_dir: '{{playbook_dir}}' 13 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/handlers/main.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | # handlers file for ztp_configuration 6 | 7 | - name: Restart DHCP service 8 | ansible.builtin.service: 9 | name: '{{ dhcp_service }}' 10 | state: restarted 11 | listen: "Restart dhcpd" 12 | 13 | - name: Restart apparmor 14 | ansible.builtin.service: 15 | name: apparmor 16 | state: restarted 17 | -------------------------------------------------------------------------------- /tests/activate-debug.env: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if [[ ! -z ANSIBLE_CVP_LOG_FILE ]]; then 4 | export ANSIBLE_CVP_LOG_FILE=arista.cvp.debug.log 5 | fi 6 | if [[ ! -z ANSIBLE_CVP_LOG_LEVEL ]]; then 7 | export ANSIBLE_CVP_LOG_LEVEL=debug 8 | fi 9 | if [[ ! -z ANSIBLE_CVP_LOG_APICALL ]]; then 10 | export ANSIBLE_CVP_LOG_APICALL=debug 11 | fi 12 | 13 | echo "Configure module for logging:" 14 | echo " - Logging Level: ${ANSIBLE_CVP_LOG_LEVEL}" 15 | echo " - Logging File: ${ANSIBLE_CVP_LOG_FILE}" 16 | echo " - URL Lib logging: ${ANSIBLE_CVP_LOG_APICALL}" 17 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation with mkdocs 15 | mkdocs: 16 | configuration: mkdocs.yml 17 | fail_on_warning: false 18 | 19 | python: 20 | install: 21 | - requirements: ansible_collections/arista/cvp/docs/requirements.txt 22 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_device_v3/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Test cv_device_v3 module 3 | import_playbook: test_move_and_deploy_device.yml 4 | 5 | - name: Test cv_device_v3 module 6 | import_playbook: test_apply_detach_configlet.yml 7 | 8 | - name: Test cv_device_v3 module 9 | import_playbook: test_reconciled_configlet.yml 10 | 11 | - name: Test cv_device_v3 module 12 | import_playbook: test_apply_detach_bundle.yml 13 | 14 | - name: Test cv_device_v3 module 15 | import_playbook: test_decommission_factory_reset_provisioning_reset.yml 16 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_device/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: cv_configlet unit testing 3 | hosts: cv_server 4 | # connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | vars: 9 | CVP_CONFIGLETS: 10 | 01TRAINING-alias: "alias a101 show version" 11 | 01TRAINING-02: "alias a102 show version" 12 | tasks: 13 | - name: "Include offline facts" 14 | include_vars: "{{ root_dir }}/cv_configlet_result.json" 15 | - name: "Print logs" 16 | debug: 17 | msg: "{{ CVP_CONFIGLET_RESULT }}" 18 | -------------------------------------------------------------------------------- /development/entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Installing git 4 | echo "Installing dependencies" 5 | apk add --no-cache git git-fast-import 6 | 7 | # Making /data and /site safe for git 8 | git config --global --add safe.directory /data 9 | git config --global --add safe.directory /site 10 | 11 | # install pip requirements 12 | echo "Installing Python requirements" 13 | pip install -r ansible_collections/arista/cvp/docs/requirements.txt --upgrade 14 | 15 | # Start mkdocs 16 | echo "Starting mkdocs" 17 | mkdocs serve --no-livereload --dev-addr=127.0.0.1:8000 -f mkdocs.yml 18 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_loose/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: cv_configlet unit testing 3 | hosts: cv_server 4 | # connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | vars: 9 | CVP_CONFIGLETS: 10 | 01TRAINING-alias: "alias a101 show version" 11 | 01TRAINING-02: "alias a102 show version" 12 | tasks: 13 | - name: "Include offline facts" 14 | include_vars: "{{ root_dir }}/cv_configlet_result.json" 15 | - name: "Print logs" 16 | debug: 17 | msg: "{{ CVP_CONFIGLET_RESULT }}" 18 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_strict/verify.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: cv_configlet unit testing 3 | hosts: cv_server 4 | # connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | vars: 9 | CVP_CONFIGLETS: 10 | 01TRAINING-alias: "alias a101 show version" 11 | 01TRAINING-02: "alias a102 show version" 12 | tasks: 13 | - name: "Include offline facts" 14 | include_vars: "{{ root_dir }}/cv_configlet_result.json" 15 | - name: "Print logs" 16 | debug: 17 | msg: "{{ CVP_CONFIGLET_RESULT }}" 18 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_device_v3/device_config_assign_fqdn.yaml: -------------------------------------------------------------------------------- 1 | - name: Device Management in Cloudvision 2 | hosts: cv_server 3 | connection: local 4 | gather_facts: false 5 | collections: 6 | - arista.cvp 7 | vars: 8 | CVP_DEVICES: 9 | - fqdn: CV-ANSIBLE-EOS01 10 | parentContainerName: ANSIBLE 11 | configlets: 12 | - 'CV-EOS-ANSIBLE01' 13 | tasks: 14 | - name: "Configure devices on {{inventory_hostname}}" 15 | arista.cvp.cv_device_v3: 16 | devices: '{{CVP_DEVICES}}' 17 | state: present 18 | search_key: fqdn 19 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_device_v3/device_config_assign_strict.yaml: -------------------------------------------------------------------------------- 1 | - name: Device Management in Cloudvision 2 | hosts: cv_server 3 | connection: local 4 | gather_facts: false 5 | collections: 6 | - arista.cvp 7 | vars: 8 | CVP_DEVICES: 9 | - fqdn: CV-ANSIBLE-EOS01 10 | parentContainerName: ANSIBLE 11 | configlets: 12 | - 'CV-EOS-ANSIBLE01' 13 | tasks: 14 | - name: "Configure devices on {{inventory_hostname}}" 15 | arista.cvp.cv_device_v3: 16 | devices: '{{CVP_DEVICES}}' 17 | state: present 18 | apply_mode: strict 19 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_device_v3/device_config_assign_loose_sn.yaml: -------------------------------------------------------------------------------- 1 | - name: Device Management in Cloudvision 2 | hosts: cv_server 3 | connection: local 4 | gather_facts: false 5 | collections: 6 | - arista.cvp 7 | vars: 8 | CVP_DEVICES: 9 | - serialNumber: xxxxxxxxxxxx 10 | parentContainerName: ANSIBLE 11 | configlets: 12 | - 'CV-EOS-ANSIBLE01' 13 | tasks: 14 | - name: "Configure devices on {{inventory_hostname}}" 15 | arista.cvp.cv_device_v3: 16 | devices: '{{CVP_DEVICES}}' 17 | state: present 18 | search_key: serialNumber 19 | -------------------------------------------------------------------------------- /.github/yamllintrc: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | extends: default 4 | 5 | rules: 6 | braces: 7 | level: warning 8 | max-spaces-inside: 1 9 | brackets: 10 | level: warning 11 | max-spaces-inside: 1 12 | colons: 13 | level: warning 14 | commas: 15 | level: warning 16 | comments: disable 17 | comments-indentation: disable 18 | document-start: disable 19 | empty-lines: 20 | level: warning 21 | hyphens: 22 | level: warning 23 | indentation: 24 | level: warning 25 | indent-sequences: consistent 26 | line-length: 27 | max: 350 28 | level: warning 29 | allow-non-breakable-inline-mappings: true 30 | truthy: disable 31 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/outputs/cv_facts.txt: -------------------------------------------------------------------------------- 1 | ansible_facts: 2 | cvp_info: 3 | editable: 2023.1.1 4 | configlets: 5 | - name: ANSIBLE_TESTING_CONTAINER 6 | isDefault: 'no' 7 | config: alias a57 show version 8 | reconciled: false 9 | netElementCount: 3 10 | editable: true 11 | dateTimeInLongFormat: 1574944821353 12 | isDraft: false 13 | note: '## Managed by Ansible ##' 14 | visible: true 15 | containerCount: 2 16 | user: cvpadmin 17 | key: configlet_3503_4572477104617871 18 | sslConfig: false 19 | devices: 20 | - veos01 21 | - veos03 22 | ... 23 | -------------------------------------------------------------------------------- /development/install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Arista Validated Design installation script 4 | # 5 | # See https://www.avd.sh for the installation steps. 6 | # 7 | # This script is meant for quick & easy install via: 8 | # $ curl -fsSL https://get.avd.sh -o get-avd.sh 9 | # $ sh get-avd.sh 10 | # 11 | # NOTE: Make sure to verify the contents of the script 12 | # you downloaded matches the contents of install.sh 13 | # located at https://github.com/arista-netdevops-community/avd-install 14 | # before executing. 15 | # 16 | 17 | echo "Script has moved to https://get.avd.sh" 18 | echo "to install AVD, please use: curl -fsSL https://get.avd.sh | sh" 19 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_device_v3/remove-image-bundle-to-device.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Image Management in Cloudvision 3 | hosts: cv_server 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | vars: 9 | CVP_DEVICES: 10 | - serialNumber: JPE504a004ea054 11 | parentContainerName: L2_Leaf 12 | configlets: 13 | - 'AVD_Ipmi3' 14 | tasks: 15 | - name: "Configure devices on {{inventory_hostname}}" 16 | arista.cvp.cv_device_v3: 17 | devices: '{{CVP_DEVICES}}' 18 | state: present 19 | search_key: serialNumber 20 | apply_mode: strict 21 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_tag_v3/create.delete-interface.yml: -------------------------------------------------------------------------------- 1 | - name: Test cv_tag_v3 2 | hosts: CloudVision 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | CVP_TAGS: 7 | - interface_tags: 8 | - tags: 9 | - name: leaf1IntfTag1 10 | value: leaf1IntfVal1 11 | - name: leaf1IntfTag2 12 | value: leaf1IntfVal2 13 | - name: leaf2IntfTag1 14 | value: leaf2IntfVal1 15 | tasks: 16 | - name: create/delete interface tags 17 | arista.cvp.cv_tag_v3: 18 | tags: "{{CVP_TAGS}}" 19 | # mode: create 20 | mode: delete 21 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/inventory.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | children: 4 | CVP: 5 | hosts: 6 | CloudVision: 7 | ansible_httpapi_host: 192.168.0.5 8 | ansible_host: 192.168.0.5 9 | ansible_user: arista 10 | ansible_password: "{{ lookup('env', 'LABPASSPHRASE') }}" # lab credential password 11 | ansible_connection: httpapi 12 | ansible_httpapi_use_ssl: True 13 | ansible_httpapi_validate_certs: False 14 | ansible_network_os: eos 15 | ansible_httpapi_port: 443 16 | # Configuration to get Virtual Env information 17 | ansible_python_interpreter: $(which python3) 18 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_tag_v3/assign.unassign-device.yml: -------------------------------------------------------------------------------- 1 | - name: Test cv_tag_v3 2 | hosts: CloudVision 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | CVP_TAGS: 7 | # - device_id: leaf1 8 | - device: leaf1.atd.lab 9 | device_tags: 10 | - name: leaf1DTag1 11 | value: leaf1DVal1 12 | # - device: leaf2.atd.lab 13 | - device_id: leaf2 14 | device_tags: 15 | - name: leaf2DTag1 16 | value: leaf2DVal1 17 | tasks: 18 | - name: assign/unassign existing device tags 19 | arista.cvp.cv_tag_v3: 20 | tags: "{{CVP_TAGS}}" 21 | mode: assign 22 | # mode: unassign 23 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_device_v3/assign-image-bundle-to-device.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Image Management in Cloudvision 3 | hosts: cv_server 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | vars: 9 | CVP_DEVICES: 10 | - serialNumber: JPE504a004ea054 11 | parentContainerName: L2_Leaf 12 | configlets: 13 | - 'AVD_Ipmi3' 14 | imageBundle: leaf_bundle 15 | tasks: 16 | - name: "Configure devices on {{inventory_hostname}}" 17 | arista.cvp.cv_device_v3: 18 | devices: '{{CVP_DEVICES}}' 19 | state: present 20 | search_key: serialNumber 21 | apply_mode: strict 22 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_tag_v3/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - converge 5 | driver: 6 | name: default 7 | platforms: 8 | - name: dummy 9 | managed: false 10 | provisioner: 11 | name: ansible 12 | config_options: 13 | defaults: 14 | jinja2_extensions: jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 15 | gathering: explicit 16 | command_warnings: false 17 | remote_tmp: /tmp/.ansible-${USER}/tmp 18 | stdout_callback: yaml 19 | playbooks: 20 | converge: converge.yml 21 | ansible_args: 22 | - --inventory 23 | - ${MOLECULE_SCENARIO_DIRECTORY}/../../examples/inventory.yml 24 | verifier: 25 | name: ansible 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_device_v3/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - converge 5 | driver: 6 | name: default 7 | platforms: 8 | - name: dummy 9 | managed: false 10 | provisioner: 11 | name: ansible 12 | config_options: 13 | defaults: 14 | jinja2_extensions: jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 15 | gathering: explicit 16 | command_warnings: false 17 | remote_tmp: /tmp/.ansible-${USER}/tmp 18 | stdout_callback: yaml 19 | playbooks: 20 | converge: converge.yml 21 | ansible_args: 22 | - --inventory 23 | - ${MOLECULE_SCENARIO_DIRECTORY}/../../examples/inventory.yml 24 | verifier: 25 | name: ansible 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_facts_v3/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - converge 5 | driver: 6 | name: default 7 | platforms: 8 | - name: dummy 9 | managed: false 10 | provisioner: 11 | name: ansible 12 | config_options: 13 | defaults: 14 | jinja2_extensions: jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 15 | gathering: explicit 16 | command_warnings: false 17 | remote_tmp: /tmp/.ansible-${USER}/tmp 18 | stdout_callback: yaml 19 | playbooks: 20 | converge: converge.yml 21 | ansible_args: 22 | - --inventory 23 | - ${MOLECULE_SCENARIO_DIRECTORY}/../../examples/inventory.yml 24 | verifier: 25 | name: ansible 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_image_v3/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - converge 5 | driver: 6 | name: default 7 | platforms: 8 | - name: dummy 9 | managed: false 10 | provisioner: 11 | name: ansible 12 | config_options: 13 | defaults: 14 | jinja2_extensions: jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 15 | gathering: explicit 16 | command_warnings: false 17 | remote_tmp: /tmp/.ansible-${USER}/tmp 18 | stdout_callback: yaml 19 | playbooks: 20 | converge: converge.yml 21 | ansible_args: 22 | - --inventory 23 | - ${MOLECULE_SCENARIO_DIRECTORY}/../../examples/inventory.yml 24 | verifier: 25 | name: ansible 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_task_v3/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - converge 5 | driver: 6 | name: default 7 | platforms: 8 | - name: dummy 9 | managed: false 10 | provisioner: 11 | name: ansible 12 | config_options: 13 | defaults: 14 | jinja2_extensions: jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 15 | gathering: explicit 16 | command_warnings: false 17 | remote_tmp: /tmp/.ansible-${USER}/tmp 18 | stdout_callback: yaml 19 | playbooks: 20 | converge: converge.yml 21 | ansible_args: 22 | - --inventory 23 | - ${MOLECULE_SCENARIO_DIRECTORY}/../../examples/inventory.yml 24 | verifier: 25 | name: ansible 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/templates/template.shared_configlets.j2: -------------------------------------------------------------------------------- 1 | {# 2 | Copyright (c) 2023-2025 Arista Networks, Inc. 3 | Use of this source code is governed by the Apache License 2.0 4 | that can be found in the LICENSE file. 5 | #} 6 | --- 7 | {% for configlet in ansible_facts['configlets'] %} 8 | {% if configlet_filter in configlet['name'] %} 9 | '{{configlet["name"]}}': 10 | config: "{{ configlet['config'] | replace('\n', '\\n')}}" 11 | last_changed: {{configlet['dateTimeInLongFormat']}} 12 | devices: {{configlet['devices'] | replace("u'", "'")}} 13 | containers: {{configlet['containers'] | replace("u'", "'")}} 14 | {% endif %} 15 | {% endfor %} 16 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_v3/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - converge 5 | driver: 6 | name: default 7 | platforms: 8 | - name: dummy 9 | managed: false 10 | provisioner: 11 | name: ansible 12 | config_options: 13 | defaults: 14 | jinja2_extensions: jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 15 | gathering: explicit 16 | command_warnings: false 17 | remote_tmp: /tmp/.ansible-${USER}/tmp 18 | stdout_callback: yaml 19 | playbooks: 20 | converge: converge.yml 21 | ansible_args: 22 | - --inventory 23 | - ${MOLECULE_SCENARIO_DIRECTORY}/../../examples/inventory.yml 24 | verifier: 25 | name: ansible 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_container_v3/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - converge 5 | driver: 6 | name: default 7 | platforms: 8 | - name: dummy 9 | managed: false 10 | provisioner: 11 | name: ansible 12 | config_options: 13 | defaults: 14 | jinja2_extensions: jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 15 | gathering: explicit 16 | command_warnings: false 17 | remote_tmp: /tmp/.ansible-${USER}/tmp 18 | stdout_callback: yaml 19 | playbooks: 20 | converge: converge.yml 21 | ansible_args: 22 | - --inventory 23 | - ${MOLECULE_SCENARIO_DIRECTORY}/../../examples/inventory.yml 24 | verifier: 25 | name: ansible 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_change_control_v3/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - converge 5 | driver: 6 | name: default 7 | platforms: 8 | - name: dummy 9 | managed: false 10 | provisioner: 11 | name: ansible 12 | config_options: 13 | defaults: 14 | jinja2_extensions: jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 15 | gathering: explicit 16 | command_warnings: false 17 | remote_tmp: /tmp/.ansible-${USER}/tmp 18 | stdout_callback: yaml 19 | playbooks: 20 | converge: converge.yml 21 | ansible_args: 22 | - --inventory 23 | - ${MOLECULE_SCENARIO_DIRECTORY}/../../examples/inventory.yml 24 | verifier: 25 | name: ansible 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | 5 | # Use the new container infrastructure 6 | sudo: false 7 | 8 | # Install ansible 9 | addons: 10 | apt: 11 | packages: 12 | - python-pip 13 | 14 | install: 15 | # Install ansible 16 | - pip install ansible 17 | 18 | # Check ansible version 19 | - ansible --version 20 | 21 | # Create ansible.cfg with correct roles_path 22 | - printf '[defaults]\nroles_path=../' >ansible.cfg 23 | 24 | script: 25 | # Basic role syntax check 26 | - ansible-playbook tests/test.yml -i tests/inventory --syntax-check 27 | 28 | notifications: 29 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ 30 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | language: python 3 | python: "2.7" 4 | 5 | # Use the new container infrastructure 6 | sudo: false 7 | 8 | # Install ansible 9 | addons: 10 | apt: 11 | packages: 12 | - python-pip 13 | 14 | install: 15 | # Install ansible 16 | - pip install ansible 17 | 18 | # Check ansible version 19 | - ansible --version 20 | 21 | # Create ansible.cfg with correct roles_path 22 | - printf '[defaults]\nroles_path=../' >ansible.cfg 23 | 24 | script: 25 | # Basic role syntax check 26 | - ansible-playbook tests/test.yml -i tests/inventory --syntax-check 27 | 28 | notifications: 29 | webhooks: https://galaxy.ansible.com/api/v1/notifications/ 30 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_tag_v3/assign.unassign-device-autocreate.yml: -------------------------------------------------------------------------------- 1 | - name: Test cv_tag_v3 2 | hosts: CloudVision 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | CVP_TAGS: 7 | # - device_id: leaf1 8 | - device: leaf1.atd.lab 9 | device_tags: 10 | - name: leaf1DTag1_new 11 | value: leaf1DVal1_new 12 | - device_id: leaf2 13 | # - device: leaf2.atd.lab 14 | device_tags: 15 | - name: leaf2DTag1_new 16 | value: leaf2DVal1_new 17 | tasks: 18 | - name: assign/unassign non-existing device tags 19 | arista.cvp.cv_tag_v3: 20 | tags: "{{CVP_TAGS}}" 21 | mode: assign 22 | auto_create: true 23 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_validate_v3/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - converge 5 | # - verify 6 | driver: 7 | name: default 8 | platforms: 9 | - name: dummy 10 | managed: false 11 | provisioner: 12 | name: ansible 13 | config_options: 14 | defaults: 15 | jinja2_extensions: jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n 16 | gathering: explicit 17 | command_warnings: false 18 | remote_tmp: /tmp/.ansible-${USER}/tmp 19 | stdout_callback: yaml 20 | playbooks: 21 | converge: converge.yml 22 | ansible_args: 23 | - --inventory 24 | - ${MOLECULE_SCENARIO_DIRECTORY}/../../examples/inventory.yml 25 | verifier: 26 | name: ansible 27 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_image_v3/image_upload.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Image Tests 3 | hosts: CVP 4 | gather_facts: no 5 | vars: 6 | tasks: 7 | - name: "Add an image {{inventory_hostname}}" 8 | vars: 9 | ansible_command_timeout: 1200 10 | ansible_connect_timeout: 600 11 | arista.cvp.cv_image_v3: 12 | mode: image 13 | action: add 14 | image: TerminAttr64-1.19.0-1.swix 15 | 16 | - name: "Gather CVP image information facts {{inventory_hostname}}" 17 | arista.cvp.cv_image_v3: 18 | mode: image 19 | action: get 20 | register: image_data 21 | 22 | - name: "Print out facts from {{inventory_hostname}}" 23 | debug: 24 | msg: "{{image_data}}" 25 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_image_v3/bundle_delete.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Image Tests 3 | hosts: CVP 4 | gather_facts: no 5 | vars: 6 | tasks: 7 | - name: "Remove an image bundle {{inventory_hostname}}" 8 | vars: 9 | ansible_command_timeout: 1200 10 | ansible_connect_timeout: 600 11 | arista.cvp.cv_image_v3: 12 | mode: bundle 13 | action: remove 14 | bundle_name: Test_bundle 15 | 16 | - name: "Gather CVP image information facts {{inventory_hostname}}" 17 | arista.cvp.cv_image_v3: 18 | mode: bundle 19 | action: get 20 | register: bundle_data 21 | 22 | - name: "Print out facts from {{inventory_hostname}}" 23 | debug: 24 | msg: "{{bundle_data}}" 25 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/device_validate_config_multiline.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Config Validation in Cloudvision 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: false 6 | vars: 7 | CVP_DEVICES: 8 | - device_name: leaf1 9 | search_type: serialNumber #[hostname | serialNumber | fqdn] 10 | local_configlets: 11 | validate_error: "{{lookup('file', 'configlet3_multiline.cfg')}}" 12 | tasks: 13 | - name: Validate configurations 14 | arista.cvp.cv_validate_v3: 15 | devices: "{{CVP_DEVICES}}" 16 | validate_mode: stop_on_error 17 | register: CVP_DEVICES_RESULTS 18 | - name: print result for {{inventory_hostname}} 19 | debug: 20 | msg: "{{CVP_DEVICES_RESULTS}}" 21 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | # Based on ansible-lint config 3 | extends: default 4 | 5 | rules: 6 | braces: 7 | max-spaces-inside: 1 8 | level: error 9 | brackets: 10 | max-spaces-inside: 1 11 | level: error 12 | colons: 13 | max-spaces-after: -1 14 | level: error 15 | commas: 16 | max-spaces-after: -1 17 | level: error 18 | comments: disable 19 | comments-indentation: disable 20 | document-start: disable 21 | empty-lines: 22 | max: 3 23 | level: error 24 | hyphens: 25 | level: error 26 | indentation: disable 27 | key-duplicates: enable 28 | line-length: disable 29 | new-line-at-end-of-file: disable 30 | new-lines: 31 | type: unix 32 | trailing-spaces: disable 33 | truthy: disable 34 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/device_validate_config_error_local.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Config Validation in Cloudvision 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: false 6 | vars: 7 | CVP_DEVICES: 8 | - device_name: leaf1 9 | search_type: serialNumber #[hostname | serialNumber | fqdn] 10 | local_configlets: 11 | validate_error: "ruter bgp 1111\n neighbor 1.1.1.1 remote-bs 111" 12 | tasks: 13 | - name: Validate configurations 14 | arista.cvp.cv_validate_v3: 15 | devices: "{{CVP_DEVICES}}" 16 | validate_mode: stop_on_error 17 | register: CVP_DEVICES_RESULTS 18 | - name: print result for {{inventory_hostname}} 19 | debug: 20 | msg: "{{CVP_DEVICES_RESULTS}}" 21 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/device_validate_config_valid_local.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Config Validation in Cloudvision 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: false 6 | vars: 7 | CVP_DEVICES: 8 | - device_name: leaf1 9 | search_type: serialNumber #[hostname | serialNumber | fqdn] 10 | local_configlets: 11 | validate_valid: "interface Ethernet1\n description test_validate" 12 | tasks: 13 | - name: Validate configurations 14 | arista.cvp.cv_validate_v3: 15 | devices: "{{CVP_DEVICES}}" 16 | validate_mode: stop_on_warning 17 | register: CVP_DEVICES_RESULTS 18 | - name: print result for {{inventory_hostname}} 19 | debug: 20 | msg: "{{CVP_DEVICES_RESULTS}}" 21 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-conflict.yml: -------------------------------------------------------------------------------- 1 | name: "PR Conflicts checker" 2 | on: 3 | pull_request_target: 4 | types: [synchronize] 5 | 6 | jobs: 7 | Conflict_Check: 8 | name: "Check PR status: conflicts and resolution" 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: check if PRs are dirty 12 | uses: eps1lon/actions-label-merge-conflict@releases/2.x 13 | with: 14 | dirtyLabel: "state: conflict" 15 | removeOnDirtyLabel: "state: conflict resolved" 16 | repoToken: "${{ secrets.GITHUB_TOKEN }}" 17 | commentOnDirty: "This pull request has conflicts, please resolve those before we can evaluate the pull request." 18 | commentOnClean: "Conflicts have been resolved. A maintainer will review the pull request shortly." 19 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_container_v3/container_add_image_bundle.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Container Image Management in Cloudvision 3 | hosts: cv_server 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | 9 | vars: 10 | # Container definition 11 | containers_provision: 12 | Undefined: 13 | configlets: [] 14 | imageBundle: EOS-4.25.4M 15 | parentContainerName: Tenant 16 | Test123: 17 | configlets: [] 18 | imageBundle: 'top_level_container' 19 | parentContainerName: Tenant 20 | 21 | tasks: 22 | - name: "Build Container topology on {{inventory_hostname}}" 23 | arista.cvp.cv_container_v3: 24 | topology: '{{containers_provision}}' 25 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/meta/runtime.yml: -------------------------------------------------------------------------------- 1 | --- 2 | requires_ansible: '>=2.16.0,<2.19.0' 3 | plugin_routing: 4 | modules: 5 | cv_facts: 6 | deprecation: 7 | removal_version: '4.0.0' 8 | warning_text: use modules in version 3 instead 9 | cv_device: 10 | deprecation: 11 | removal_version: '4.0.0' 12 | warning_text: use cv_device_v3 instead. 13 | cv_container: 14 | deprecation: 15 | removal_version: '4.0.0' 16 | warning_text: use cv_container_v3 instead. 17 | cv_configlet: 18 | deprecation: 19 | removal_version: '4.0.0' 20 | warning_text: use cv_configlet_v3 instead. 21 | cv_task: 22 | deprecation: 23 | removal_version: '4.0.0' 24 | warning_text: use cv_task_v3 instead. 25 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/tasks/main.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | # tasks file for dhcp_configuration 6 | 7 | - name: Deprecation warning! 8 | delegate_to: localhost 9 | ansible.builtin.debug: 10 | msg: 11 | - "!!! Deprecation Warning - Please note this role has been deprecated." 12 | - "!!! Deprecation Warning - This role will be removed in version 4.0.0." 13 | run_once: true 14 | 15 | # If mode=online launch server configuration 16 | # If mode=offline generate dhcpd.conf to {{output_dir}} 17 | - name: Start creation/update process. 18 | tags: [provision] 19 | ansible.builtin.include_tasks: "./{{ mode }}.yml" 20 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_image_v3/bundle_create.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Image Tests 3 | hosts: CVP 4 | gather_facts: no 5 | vars: 6 | tasks: 7 | - name: "Add image bundle {{inventory_hostname}}" 8 | vars: 9 | ansible_command_timeout: 1200 10 | ansible_connect_timeout: 600 11 | arista.cvp.cv_image_v3: 12 | mode: bundle 13 | action: add 14 | bundle_name: Test_bundle 15 | image_list: 16 | - EOS-4.25.4M.swi 17 | 18 | - name: "Gather CVP image information facts {{inventory_hostname}}" 19 | arista.cvp.cv_image_v3: 20 | mode: bundle 21 | action: get 22 | register: bundle_data 23 | 24 | - name: "Print out facts from {{inventory_hostname}}" 25 | debug: 26 | msg: "{{bundle_data}}" 27 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_configlet_v3/push_configet.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: cv_configlet_v3 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: no 6 | vars: 7 | CVP_CONFIGLETS: 8 | configlet1: 'alias a{{ 999 | random }} show lldp' 9 | configlet2: 'alias c{{ 999 | random }} show version' 10 | tasks: 11 | - name: "Push config" 12 | arista.cvp.cv_configlet_v3: 13 | configlets: "{{CVP_CONFIGLETS}}" 14 | state: present 15 | 16 | - name: 'Collect devices facts from {{inventory_hostname}}' 17 | arista.cvp.cv_facts_v3: 18 | facts: 19 | - configlets 20 | regexp_filter: "configlet" 21 | register: FACTS_CONFIGLET 22 | 23 | - name: 'Display cv_configlet result' 24 | debug: 25 | msg: '{{FACTS_CONFIGLET}}' 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/outputs/cv_container.txt: -------------------------------------------------------------------------------- 1 | containers: 2 | changed: false 3 | cv_container: 4 | attached_configlet: 5 | configlet_attached: 0 6 | list: [] 7 | taskIds: [] 8 | changed: true 9 | creation_result: 10 | containers_created: '2' 11 | moved_result: 12 | devices_moved: 0 13 | list: [] 14 | taskIds: [] 15 | taskIds: [] 16 | tasks: [] 17 | data: 18 | attached_configlet: 19 | configlet_attached: 0 20 | list: [] 21 | taskIds: [] 22 | changed: true 23 | creation_result: 24 | containers_created: '2' 25 | moved_result: 26 | devices_moved: 0 27 | list: [] 28 | taskIds: [] 29 | taskIds: [] 30 | tasks: [] 31 | failed: false 32 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_configlet_v3/delete_configlet.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: cv_configlet_v3 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: no 6 | vars: 7 | CVP_CONFIGLETS: 8 | configlet1: 'alias a{{ 999 | random }} show lldp' 9 | configlet2: 'alias c{{ 999 | random }} show version' 10 | 11 | tasks: 12 | - name: "Delete config" 13 | arista.cvp.cv_configlet_v3: 14 | configlets: "{{CVP_CONFIGLETS}}" 15 | state: absent 16 | 17 | - name: 'Collect devices facts from {{inventory_hostname}}' 18 | arista.cvp.cv_facts_v3: 19 | facts: 20 | - configlets 21 | regexp_filter: "configlet" 22 | register: FACTS_CONFIGLET 23 | 24 | - name: 'Display cv_configlet result' 25 | debug: 26 | msg: '{{FACTS_CONFIGLET}}' 27 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_container_v3/container_remove_image_bundle.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Container Image Management in Cloudvision 3 | hosts: cv_server 4 | connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | 9 | vars: 10 | # Container definition 11 | containers_provision: 12 | Undefined: 13 | configlets: [] 14 | imageBundle: EOS-4.25.4M 15 | parentContainerName: Tenant 16 | Test123: 17 | configlets: [] 18 | imageBundle: '' 19 | parentContainerName: Tenant 20 | 21 | tasks: 22 | - name: "Apply updated Container topology on {{inventory_hostname}} in strict mode" 23 | arista.cvp.cv_container_v3: 24 | topology: '{{containers_provision}}' 25 | apply_mode: strict 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_image_v3/image_delete.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Image Tests 3 | hosts: CVP 4 | gather_facts: no 5 | vars: 6 | tasks: 7 | # API doesn't support image delete! Should throw error message 8 | - name: "Remove an image {{inventory_hostname}}" 9 | vars: 10 | ansible_command_timeout: 1200 11 | ansible_connect_timeout: 600 12 | arista.cvp.cv_image_v3: 13 | mode: image 14 | action: remove 15 | image: TerminAttr64-1.19.0-1.swix 16 | 17 | - name: "Gather CVP image information facts {{inventory_hostname}}" 18 | arista.cvp.cv_image_v3: 19 | mode: image 20 | action: get 21 | register: image_data 22 | 23 | - name: "Print out facts from {{inventory_hostname}}" 24 | debug: 25 | msg: "{{image_data}}" 26 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for dhcp_configuration 3 | # Configure default connection parameter. 4 | ansible_connection: paramiko 5 | 6 | # Method to become root 7 | ansible_become_method: sudo 8 | 9 | # Default execution mode 10 | mode: online 11 | 12 | # Default folder if mode=offline 13 | output_dir: '{{ inventory_dir }}' 14 | 15 | # List of packages to install to enable DHCP package 16 | dhcp_packages: 17 | - dhcp 18 | 19 | # State of the package 20 | dhcp_packages_state: "present" 21 | 22 | # DHCP configuration folder 23 | dhcp_config_dir: /etc/dhcp 24 | 25 | # Configuration File for DHCP service 26 | dhcp_config: '{{ dhcp_config_dir }}/dhcpd.conf' 27 | 28 | # DHCP service name 29 | dhcp_service: dhcpd 30 | 31 | # Apparmor Fix for DHCP 32 | dhcp_apparmor_fix: true 33 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.5.0.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.5.0 - See documentation on cvp.avd.sh for details. 3 | 4 | minor_changes: 5 | - Feat(cv_device_v3) Implement image bundle on device level(#505) 6 | - Feat(cv_device_v3) Support device decommissioning and device removal from provisioning(#507) 7 | - Feat(dhcp_configuration) Request add support for access point provisioning(#527) 8 | - Feat(cv_change_control_v3) Add support for approve/execute/schedule actions(#529) 9 | - Feat(cv_facts_v3) Expose verbose option in cv_facts_v3(#535) 10 | 11 | bugfixes: 12 | - Fix(cv_device_v3) Allow all search_by options when assigning and removing image bundles(#541) 13 | - Fix(cv_change_control_v3) Dict key check on CC indexing is broken(#524) 14 | - Fix(cv_facts_v3) Make image bundle name key usage consistent(#513) 15 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Collections Plugins Directory 8 | 9 | `arista.cvp` collection provides a set of plugins to configure Arista EOS devices with a CloudVision Platform server. 10 | 11 | ## List of available modules 12 | 13 | - **arista.cvp.cv_facts** - Collect CVP facts from server like list of containers, devices, configlet , images and tasks. 14 | - **arista.cvp.cv_configlet**: Manage configlet configured on CVP. 15 | - **arista.cvp.cv_container**: Manage container topology and attach configlet and devices to containers. 16 | - **arista.cvp.cv_device**: Manage devices configured on CVP 17 | - **arista.cvp.cv_task**: Run tasks created on CVP. 18 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_facts_v3/get_images_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run cv_facts_v3 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: no 6 | tasks: 7 | - name: Collect images facts from {{inventory_hostname}} 8 | arista.cvp.cv_facts_v3: 9 | facts: 10 | - images # collects all images facts 11 | register: CV_FACTS_V3_RESULT 12 | 13 | - name: Display image facts 14 | debug: 15 | msg: "{{CV_FACTS_V3_RESULT}}" 16 | 17 | - name: Collect images facts from {{inventory_hostname}} filtered by name 18 | arista.cvp.cv_facts_v3: 19 | facts: 20 | - images 21 | regexp_filter: 'EOS-4.25.4M.swi' # collects specific image facts 22 | register: CV_FACTS_V3_RESULT 23 | 24 | - name: Display image facts 25 | debug: 26 | msg: "{{CV_FACTS_V3_RESULT}}" 27 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_remove_unknown.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test remove unknown 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf 1A Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_LEAF1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: LEAF1A_MLAG_Health 16 | stages: 17 | - name: LEAF1A_MLAG_Health 18 | mode: parallel 19 | 20 | tasks: 21 | - name: "Remove an unknown change control on {{inventory_hostname}} using change_id field - this should fail" 22 | arista.cvp.cv_change_control_v3: 23 | state: remove 24 | change_id: ["Unknown-1234567890}"] 25 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_device/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - syntax 5 | - create 6 | - converge 7 | # - verify 8 | driver: 9 | name: default 10 | platforms: 11 | - name: cv_server 12 | image: avdteam/base:3.6-v1.0 13 | pre_build_image: true 14 | managed: true 15 | groups: 16 | - CVP 17 | provisioner: 18 | name: ansible 19 | config_options: 20 | defaults: 21 | jinja2_extensions: 'jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n' 22 | gathering: explicit 23 | command_warnings: False 24 | remote_tmp: /tmp/.ansible-${USER}/tmp 25 | inventory: 26 | links: 27 | hosts: 'inventory/hosts.yml' 28 | group_vars: 'inventory/group_vars/' 29 | host_vars: 'inventory/host_vars/' 30 | ansible_args: 31 | - --inventory=inventory/hosts.yml 32 | - --check 33 | verifier: 34 | name: ansible 35 | -------------------------------------------------------------------------------- /tests/Makefile: -------------------------------------------------------------------------------- 1 | CURRENT_DIR = $(shell pwd) 2 | TESTS ?= . 3 | TAG ?= generic or api 4 | TEST_OPT = -rA -q --cov-report term:skip-covered 5 | REPORT = -v --cov-report term:skip-covered --html=report.html --self-contained-html --cov-report=html --color yes 6 | COVERAGE = --cov=ansible_collections.arista.cvp.plugins.module_utils 7 | CLI_LOGGING ?= INFO 8 | PYTEST_LOGGING ?= DEBUG 9 | 10 | .PHONY: unit-tests 11 | unit-tests: ## Run python unit tests in verbose mode with CLI report only for all tests 12 | export PYTEST_LOG_LEVEL=$(PYTEST_LOGGING) && pytest ./unit $(TEST_OPT) $(REPORT) $(COVERAGE) --log-cli-level=$(CLI_LOGGING) -m '$(TAG)' $(TESTS) 13 | 14 | .PHONY: system-tests 15 | system-tests: ## Run python system tests in verbose mode with CLI report only for all tests 16 | export PYTEST_LOG_LEVEL=$(PYTEST_LOGGING) && pytest ./system $(TEST_OPT) $(REPORT) $(COVERAGE) --log-cli-level=$(CLI_LOGGING) -m '$(TAG)' $(TESTS) 17 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/device_validate_config_multiple.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Config Validation in Cloudvision 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: false 6 | vars: 7 | CVP_DEVICES: 8 | - device_name: leaf1 9 | search_type: serialNumber #[hostname | serialNumber | fqdn] 10 | local_configlets: 11 | validate_error: "{{lookup('file', 'configlet2.cfg')}}" 12 | validate_valid: "interface Ethernet1\n description test_validate" 13 | validate_warning: "interface Ethernet1\n spanning-tree portfast" 14 | tasks: 15 | - name: Validate configurations 16 | arista.cvp.cv_validate_v3: 17 | devices: "{{CVP_DEVICES}}" 18 | validate_mode: stop_on_warning 19 | register: CVP_DEVICES_RESULTS 20 | - name: print result for {{inventory_hostname}} 21 | debug: 22 | msg: "{{CVP_DEVICES_RESULTS}}" 23 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/outputs/cv_tag_v3.txt: -------------------------------------------------------------------------------- 1 | actions_manager: 2 | actions_manager_count: 0 3 | actions_manager_list: 4 | - tag_AnsibleWorkspaceLZ7 5 | changed: true 6 | diff: {} 7 | success: true 8 | taskIds: [] 9 | invocation: 10 | module_args: 11 | auto_create: true 12 | mode: create 13 | tags: 14 | - device: leaf1 15 | device_tags: 16 | - name: tag1 17 | value: value1 18 | - name: tag2 19 | value: value2 20 | interface_tags: 21 | - interface: Ethernet1/1 22 | tags: 23 | - name: tag1 24 | value: value1 25 | - name: tag2 26 | value: value2 27 | - interface: Ethernet1/2 28 | tags: 29 | - name: tag1 30 | value: value1 31 | - name: tag2 32 | value: value2 33 | success: true 34 | taskIds: [] 35 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_loose/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - syntax 5 | - create 6 | - converge 7 | # - verify 8 | driver: 9 | name: default 10 | platforms: 11 | - name: cv_server 12 | image: avdteam/base:3.6-v1.0 13 | pre_build_image: true 14 | managed: true 15 | groups: 16 | - CVP 17 | provisioner: 18 | name: ansible 19 | config_options: 20 | defaults: 21 | jinja2_extensions: 'jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n' 22 | gathering: explicit 23 | command_warnings: False 24 | remote_tmp: /tmp/.ansible-${USER}/tmp 25 | inventory: 26 | links: 27 | hosts: 'inventory/hosts.yml' 28 | group_vars: 'inventory/group_vars/' 29 | host_vars: 'inventory/host_vars/' 30 | ansible_args: 31 | - --inventory=inventory/hosts.yml 32 | - --check 33 | verifier: 34 | name: ansible 35 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_strict/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | test_sequence: 4 | - syntax 5 | - create 6 | - converge 7 | # - verify 8 | driver: 9 | name: default 10 | platforms: 11 | - name: cv_server 12 | image: avdteam/base:3.6-v1.0 13 | pre_build_image: true 14 | managed: true 15 | groups: 16 | - CVP 17 | provisioner: 18 | name: ansible 19 | config_options: 20 | defaults: 21 | jinja2_extensions: 'jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n' 22 | gathering: explicit 23 | command_warnings: False 24 | remote_tmp: /tmp/.ansible-${USER}/tmp 25 | inventory: 26 | links: 27 | hosts: 'inventory/hosts.yml' 28 | group_vars: 'inventory/group_vars/' 29 | host_vars: 'inventory/host_vars/' 30 | ansible_args: 31 | - --inventory=inventory/hosts.yml 32 | - --check 33 | verifier: 34 | name: ansible 35 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | name: "Issue and PR stale management" 2 | on: 3 | schedule: 4 | - cron: "0 0 * * *" 5 | 6 | jobs: 7 | stale: 8 | runs-on: ubuntu-latest 9 | steps: 10 | # Issue stale management 11 | - uses: actions/stale@v4 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | days-before-stale: 90 15 | days-before-close: 15 16 | stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 15 days' 17 | stale-issue-label: 'state: stale' 18 | exempt-issue-labels: 'state: accepted, state: in-progress' 19 | stale-pr-message: 'This PR is stale because it has been open 30 days with no activity. Remove stale label or comment or this will be closed in 15 days' 20 | stale-pr-label: 'state: stale' 21 | exempt-pr-labels: 'state: accepted, state: in-progress' 22 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/device_validate_config_error_cvp.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Config Validation in Cloudvision 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: false 6 | vars: 7 | CVP_DEVICES: 8 | - device_name: leaf1 9 | search_type: serialNumber #[hostname | serialNumber | fqdn] 10 | cvp_configlets: 11 | - validate_error 12 | CVP_CONFIGLET: 13 | validate_error: "{{lookup('file', 'configlet4.cfg')}}" 14 | tasks: 15 | - name: "Push configlet" 16 | arista.cvp.cv_configlet_v3: 17 | configlets: "{{CVP_CONFIGLET}}" 18 | state: present 19 | - name: Validate configurations 20 | arista.cvp.cv_validate_v3: 21 | devices: "{{CVP_DEVICES}}" 22 | validate_mode: stop_on_error 23 | register: CVP_DEVICES_RESULTS 24 | - name: print result for {{inventory_hostname}} 25 | debug: 26 | msg: "{{CVP_DEVICES_RESULTS}}" 27 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/device_validate_config_warning_cvp.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Config Validation in Cloudvision 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: false 6 | vars: 7 | CVP_DEVICES: 8 | - device_name: leaf1 9 | search_type: serialNumber #[hostname | serialNumber | fqdn] 10 | cvp_configlets: 11 | - validate_warning 12 | CVP_CONFIGLET: 13 | validate_warning: "{{lookup('file', 'configlet5.cfg')}}" 14 | tasks: 15 | - name: "Push configlet" 16 | arista.cvp.cv_configlet_v3: 17 | configlets: "{{CVP_CONFIGLET}}" 18 | state: present 19 | - name: Validate configurations 20 | arista.cvp.cv_validate_v3: 21 | devices: "{{CVP_DEVICES}}" 22 | validate_mode: stop_on_warning 23 | register: CVP_DEVICES_RESULTS 24 | - name: print result for {{inventory_hostname}} 25 | debug: 26 | msg: "{{CVP_DEVICES_RESULTS}}" 27 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/config.yaml: -------------------------------------------------------------------------------- 1 | changelog_filename_template: ../CHANGELOG.rst 2 | changelog_filename_version_depth: 0 3 | changes_file: changelog.yaml 4 | changes_format: combined 5 | ignore_other_fragment_extensions: true 6 | keep_fragments: false 7 | mention_ancestor: true 8 | new_plugins_after_name: removed_features 9 | notesdir: fragments 10 | prelude_section_name: release_summary 11 | prelude_section_title: Release Summary 12 | sanitize_changelog: true 13 | sections: 14 | - - major_changes 15 | - Major Changes 16 | - - minor_changes 17 | - Minor Changes 18 | - - breaking_changes 19 | - Breaking Changes / Porting Guide 20 | - - deprecated_features 21 | - Deprecated Features 22 | - - removed_features 23 | - Removed Features (previously deprecated) 24 | - - security_fixes 25 | - Security Fixes 26 | - - bugfixes 27 | - Bugfixes 28 | - - known_issues 29 | - Known Issues 30 | title: Arista.Cvp 31 | trivial_section_name: trivial 32 | use_fqcn: true 33 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_facts_v3/get_configlets_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run cv_facts_v3 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: no 6 | vars: 7 | ansible_command_timeout: 1200 8 | ansible_connect_timeout: 600 9 | tasks: 10 | - name: Collect configlet facts from {{inventory_hostname}} 11 | arista.cvp.cv_facts_v3: 12 | facts: 13 | - configlets # collects all configlets facts 14 | register: CV_FACTS_V3_RESULT 15 | 16 | - name: Display configlet facts 17 | debug: 18 | msg: "{{CV_FACTS_V3_RESULT}}" 19 | 20 | - name: Collect configlet facts from {{inventory_hostname}} 21 | arista.cvp.cv_facts_v3: 22 | facts: 23 | - configlets 24 | regexp_filter: 'configlet1' # collects specific configlet facts 25 | register: CV_FACTS_V3_RESULT 26 | 27 | - name: Display configlet facts 28 | debug: 29 | msg: "{{CV_FACTS_V3_RESULT}}" 30 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_facts_v3/get_containers_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run cv_facts_v3 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: no 6 | vars: 7 | ansible_command_timeout: 1200 8 | ansible_connect_timeout: 600 9 | tasks: 10 | - name: Collect containers facts from {{inventory_hostname}} 11 | arista.cvp.cv_facts_v3: 12 | facts: 13 | - containers # collects all containers facts 14 | register: CV_FACTS_V3_RESULT 15 | 16 | - name: Display containers facts 17 | debug: 18 | msg: "{{CV_FACTS_V3_RESULT}}" 19 | 20 | - name: Collect containers facts from {{inventory_hostname}} 21 | arista.cvp.cv_facts_v3: 22 | facts: 23 | - containers 24 | regexp_filter: 'ATD_SPINES' # collects specific container facts 25 | register: CV_FACTS_V3_RESULT 26 | 27 | - name: Display container facts 28 | debug: 29 | msg: "{{CV_FACTS_V3_RESULT}}" 30 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_tag_v3/assign.unassign-interface.yml: -------------------------------------------------------------------------------- 1 | - name: Test cv_tag_v3 2 | hosts: CloudVision 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | CVP_TAGS: 7 | - device: leaf1.atd.lab 8 | # - device_id: leaf1 9 | interface_tags: 10 | - tags: 11 | - name: leaf1IntfTag1 12 | value: leaf1IntfVal1 13 | interface: Ethernet1 14 | - tags: 15 | - name: leaf1IntfTag2 16 | value: leaf1IntfVal2 17 | interface: Ethernet2 18 | - device_id: leaf2 19 | # - device: leaf2.atd.lab 20 | interface_tags: 21 | - tags: 22 | - name: leaf2IntfTag1 23 | value: leaf2IntfVal1 24 | interface: Ethernet1 25 | tasks: 26 | - name: assign/unassign existing interface tags 27 | arista.cvp.cv_tag_v3: 28 | tags: "{{CVP_TAGS}}" 29 | mode: assign 30 | # mode: unassign 31 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/outputs/cv_container_v3.txt: -------------------------------------------------------------------------------- 1 | msg: 2 | changed: true 3 | configlets_attached: 4 | changed: true 5 | configlets_attached_count: 0 6 | configlets_attached_list: 7 | - TEAM01_LEAFS:GLOBAL-ALIASES 8 | diff: {} 9 | success: true 10 | taskIds: 11 | - '565' 12 | configlets_detached: 13 | changed: false 14 | configlets_detached_count: 0 15 | configlets_detached_list: [] 16 | diff: {} 17 | success: true 18 | taskIds: [] 19 | container_added: 20 | changed: false 21 | container_added_count: 0 22 | container_added_list: [] 23 | diff: {} 24 | success: false 25 | taskIds: [] 26 | container_deleted: 27 | changed: false 28 | container_deleted_count: 0 29 | container_deleted_list: [] 30 | diff: {} 31 | success: false 32 | taskIds: [] 33 | failed: false 34 | success: true 35 | taskIds: 36 | - '565' 37 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/outputs/cv_configlet_v3.txt: -------------------------------------------------------------------------------- 1 | msg: 2 | changed: true 3 | configlets_created: 4 | changed: false 5 | configlets_created_count: 0 6 | configlets_created_list: [] 7 | diff: {} 8 | success: false 9 | taskIds: [] 10 | configlets_deleted: 11 | changed: false 12 | configlets_deleted_count: 0 13 | configlets_deleted_list: [] 14 | diff: {} 15 | success: false 16 | taskIds: [] 17 | configlets_updated: 18 | changed: true 19 | configlets_updated_count: 2 20 | configlets_updated_list: 21 | - 01TRAINING-alias 22 | - 01TRAINING-01 23 | diff: 24 | 01TRAINING-alias: 25 | - 0.9565217391304348 26 | - - |- 27 | --- CVP 28 | - |- 29 | +++ Ansible 30 | - |- 31 | @@ -1 +1 @@ 32 | - -alias a101 show version 33 | - +alias a103 show version 34 | success: true 35 | taskIds: 36 | - '460' 37 | failed: false 38 | success: true 39 | taskIds: 40 | - '460' 41 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_tag_v3/assign.unassign-interface-autocreate.yml: -------------------------------------------------------------------------------- 1 | - name: Test cv_tag_v3 2 | hosts: CloudVision 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | CVP_TAGS: 7 | - device: leaf1.atd.lab 8 | # - device_id: leaf1 9 | interface_tags: 10 | - tags: 11 | - name: leaf1IntfTag1_new 12 | value: leaf1IntfVal1_new 13 | interface: Ethernet1 14 | - tags: 15 | - name: leaf1IntfTag2_new 16 | value: leaf1IntfVal2_new 17 | interface: Ethernet2 18 | - device_id: leaf2 19 | # - device: leaf2.atd.lab 20 | interface_tags: 21 | - tags: 22 | - name: leaf2IntfTag1_new 23 | value: leaf2IntfVal1_new 24 | interface: Ethernet1 25 | tasks: 26 | - name: assign/unassign non-existing interface tags 27 | arista.cvp.cv_tag_v3: 28 | tags: "{{CVP_TAGS}}" 29 | mode: assign 30 | auto_create: true 31 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/outputs/cv_device_v3.txt: -------------------------------------------------------------------------------- 1 | msg: 2 | changed: true 3 | configlets_attached: 4 | changed: true 5 | configlets_attached_count: 2 6 | configlets_attached_list: 7 | - CV-ANSIBLE-EOS01_configlet_attached - CV-EOS-ANSIBLE01 8 | diff: {} 9 | success: true 10 | taskIds: 11 | - '469' 12 | configlets_detached: 13 | changed: true 14 | configlets_detached_count: 1 15 | configlets_detached_list: 16 | - CV-ANSIBLE-EOS01_configlet_removed - 01DEMO-alias - 01TRAINING-alias 17 | diff: {} 18 | success: true 19 | taskIds: 20 | - '469' 21 | devices_deployed: 22 | changed: false 23 | devices_deployed_count: 0 24 | devices_deployed_list: [] 25 | diff: {} 26 | success: false 27 | taskIds: [] 28 | devices_moved: 29 | changed: false 30 | devices_moved_count: 0 31 | devices_moved_list: [] 32 | diff: {} 33 | success: false 34 | taskIds: [] 35 | failed: false 36 | success: true 37 | taskIds: 38 | - '469' 39 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/device_validate_config_warning_mix.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Config Validation in Cloudvision 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: false 6 | vars: 7 | CVP_DEVICES: 8 | - device_name: leaf1 9 | search_type: serialNumber #[hostname | serialNumber | fqdn] 10 | local_configlets: 11 | configlet1: "{{lookup('file', 'configlet1.cfg')}}" 12 | cvp_configlets: 13 | - configlet5 14 | CVP_CONFIGLET: 15 | configlet5: "{{lookup('file', 'configlet5.cfg')}}" 16 | tasks: 17 | - name: "Push configlet" 18 | arista.cvp.cv_configlet_v3: 19 | configlets: "{{CVP_CONFIGLET}}" 20 | state: present 21 | - name: Validate configurations 22 | arista.cvp.cv_validate_v3: 23 | devices: "{{CVP_DEVICES}}" 24 | validate_mode: stop_on_warning 25 | register: CVP_DEVICES_RESULTS 26 | - name: print result for {{inventory_hostname}} 27 | debug: 28 | msg: "{{CVP_DEVICES_RESULTS}}" 29 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_mac/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | gather_facts: true 5 | vars: 6 | ztp: 7 | default: 8 | registration: 'http://10.255.0.1/ztp/bootstrap' 9 | gateway: 10.255.0.3 10 | nameservers: 11 | - '10.255.0.3' 12 | general: 13 | subnets: 14 | - network: 172.17.0.0 15 | netmask: 255.255.0.0 16 | start: 172.17.255.200 17 | end: 172.17.255.200 18 | - network: 10.255.0.0 19 | netmask: 255.255.255.0 20 | gateway: 10.255.0.3 21 | nameservers: 22 | - '10.255.0.3' 23 | start: 10.255.0.200 24 | end: 10.255.0.250 25 | lease_time: 300 26 | clients: 27 | # AVD/CVP Integration 28 | - name: DC1-SPINE1 29 | mac: 0c:1d:c0:1d:62:01 30 | ip4: 10.255.0.11 31 | tasks: 32 | - name: 'Execute ZTP configuration role' 33 | import_role: 34 | name: arista.cvp.dhcp_configuration 35 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/tests/sanity/ignore-2.15.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/cv_change_control_v3.py validate-modules:missing-gplv3-license 2 | plugins/modules/cv_configlet_v3.py validate-modules:missing-gplv3-license 3 | plugins/modules/cv_configlet.py validate-modules:missing-gplv3-license 4 | plugins/modules/cv_container_v3.py validate-modules:missing-gplv3-license 5 | plugins/modules/cv_container.py validate-modules:missing-gplv3-license 6 | plugins/modules/cv_device_v3.py validate-modules:missing-gplv3-license 7 | plugins/modules/cv_device.py validate-modules:missing-gplv3-license 8 | plugins/modules/cv_facts_v3.py validate-modules:missing-gplv3-license 9 | plugins/modules/cv_facts.py validate-modules:missing-gplv3-license 10 | plugins/modules/cv_image_v3.py validate-modules:missing-gplv3-license 11 | plugins/modules/cv_tag_v3.py validate-modules:missing-gplv3-license 12 | plugins/modules/cv_validate_v3.py validate-modules:missing-gplv3-license 13 | plugins/modules/cv_task_v3.py validate-modules:missing-gplv3-license 14 | plugins/modules/cv_task.py validate-modules:missing-gplv3-license 15 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/tests/sanity/ignore-2.16.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/cv_change_control_v3.py validate-modules:missing-gplv3-license 2 | plugins/modules/cv_configlet_v3.py validate-modules:missing-gplv3-license 3 | plugins/modules/cv_configlet.py validate-modules:missing-gplv3-license 4 | plugins/modules/cv_container_v3.py validate-modules:missing-gplv3-license 5 | plugins/modules/cv_container.py validate-modules:missing-gplv3-license 6 | plugins/modules/cv_device_v3.py validate-modules:missing-gplv3-license 7 | plugins/modules/cv_device.py validate-modules:missing-gplv3-license 8 | plugins/modules/cv_facts_v3.py validate-modules:missing-gplv3-license 9 | plugins/modules/cv_facts.py validate-modules:missing-gplv3-license 10 | plugins/modules/cv_image_v3.py validate-modules:missing-gplv3-license 11 | plugins/modules/cv_tag_v3.py validate-modules:missing-gplv3-license 12 | plugins/modules/cv_validate_v3.py validate-modules:missing-gplv3-license 13 | plugins/modules/cv_task_v3.py validate-modules:missing-gplv3-license 14 | plugins/modules/cv_task.py validate-modules:missing-gplv3-license 15 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/tests/sanity/ignore-2.17.txt: -------------------------------------------------------------------------------- 1 | plugins/modules/cv_change_control_v3.py validate-modules:missing-gplv3-license 2 | plugins/modules/cv_configlet_v3.py validate-modules:missing-gplv3-license 3 | plugins/modules/cv_configlet.py validate-modules:missing-gplv3-license 4 | plugins/modules/cv_container_v3.py validate-modules:missing-gplv3-license 5 | plugins/modules/cv_container.py validate-modules:missing-gplv3-license 6 | plugins/modules/cv_device_v3.py validate-modules:missing-gplv3-license 7 | plugins/modules/cv_device.py validate-modules:missing-gplv3-license 8 | plugins/modules/cv_facts_v3.py validate-modules:missing-gplv3-license 9 | plugins/modules/cv_facts.py validate-modules:missing-gplv3-license 10 | plugins/modules/cv_image_v3.py validate-modules:missing-gplv3-license 11 | plugins/modules/cv_tag_v3.py validate-modules:missing-gplv3-license 12 | plugins/modules/cv_validate_v3.py validate-modules:missing-gplv3-license 13 | plugins/modules/cv_task_v3.py validate-modules:missing-gplv3-license 14 | plugins/modules/cv_task.py validate-modules:missing-gplv3-license 15 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_system_mac/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | create_sequence: 4 | # - dependency 5 | - create 6 | - prepare 7 | check_sequence: 8 | # - dependency 9 | # - cleanup 10 | - destroy 11 | - create 12 | - prepare 13 | - converge 14 | - check 15 | - destroy 16 | converge_sequence: 17 | # - dependency 18 | - create 19 | - prepare 20 | - converge 21 | - idempotence 22 | destroy_sequence: 23 | # - dependency 24 | # - cleanup 25 | - destroy 26 | test_sequence: 27 | # - dependency 28 | # - cleanup 29 | - destroy 30 | - syntax 31 | - create 32 | - prepare 33 | - converge 34 | # - idempotence ## Deactivate since apt update breaks idempotency 35 | # - side_effect 36 | - verify 37 | - cleanup 38 | - destroy 39 | dependency: 40 | name: galaxy 41 | driver: 42 | name: docker 43 | platforms: 44 | - name: ubuntu-22.04 45 | image: ubuntu:22.04 46 | provisioner: 47 | log: true 48 | name: ansible 49 | verifier: 50 | name: ansible 51 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_system_mac/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Converge 3 | hosts: all 4 | gather_facts: true 5 | vars: 6 | ztp: 7 | default: 8 | registration: 'http://10.255.0.1/ztp/bootstrap' 9 | gateway: 10.255.0.3 10 | nameservers: 11 | - '10.255.0.3' 12 | use_system_mac: true 13 | general: 14 | subnets: 15 | - network: 172.17.0.0 16 | netmask: 255.255.0.0 17 | start: 172.17.255.200 18 | end: 172.17.255.200 19 | - network: 10.255.0.0 20 | netmask: 255.255.255.0 21 | gateway: 10.255.0.3 22 | nameservers: 23 | - '10.255.0.3' 24 | start: 10.255.0.200 25 | end: 10.255.0.250 26 | lease_time: 300 27 | clients: 28 | # AVD/CVP Integration 29 | - name: DC1-SPINE1 30 | mac: 0c:1d:c0:1d:62:01 31 | ip4: 10.255.0.11 32 | tasks: 33 | - name: 'Execute ZTP configuration role' 34 | import_role: 35 | name: arista.cvp.dhcp_configuration 36 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_mac/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | create_sequence: 4 | # - dependency 5 | - create 6 | - prepare 7 | check_sequence: 8 | # - dependency 9 | # - cleanup 10 | - destroy 11 | - create 12 | - prepare 13 | - converge 14 | - check 15 | - destroy 16 | converge_sequence: 17 | # - dependency 18 | - create 19 | - prepare 20 | - converge 21 | - idempotence 22 | destroy_sequence: 23 | # - dependency 24 | # - cleanup 25 | - destroy 26 | test_sequence: 27 | # - dependency 28 | # - lint 29 | # - cleanup 30 | - destroy 31 | - syntax 32 | - create 33 | - prepare 34 | - converge 35 | # - idempotence ## Deactivate since apt update breaks idempotency 36 | # - side_effect 37 | - verify 38 | - cleanup 39 | - destroy 40 | dependency: 41 | name: galaxy 42 | driver: 43 | name: docker 44 | platforms: 45 | - name: ubuntu-22.04 46 | image: ubuntu:22.04 47 | provisioner: 48 | log: true 49 | name: ansible 50 | verifier: 51 | name: ansible 52 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_offline/molecule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | scenario: 3 | create_sequence: 4 | - dependency 5 | - prepare 6 | converge_sequence: 7 | - dependency 8 | - prepare 9 | - converge 10 | test_sequence: 11 | - dependency 12 | - syntax 13 | - converge 14 | - idempotence 15 | - verify 16 | cleanup_sequence: 17 | - destroy 18 | dependency: 19 | name: galaxy 20 | driver: 21 | name: default 22 | platforms: 23 | - name: dhcp_server01 24 | image: avdteam/base:3.6 25 | pre_build_image: true 26 | managed: false 27 | groups: 28 | - TOOLS 29 | provisioner: 30 | name: ansible 31 | config_options: 32 | defaults: 33 | jinja2_extensions: 'jinja2.ext.loopcontrols,jinja2.ext.do,jinja2.ext.i18n' 34 | gathering: explicit 35 | command_warnings: False 36 | inventory: 37 | links: 38 | hosts: 'inventory/hosts' 39 | group_vars: 'inventory/group_vars/' 40 | host_vars: 'inventory/host_vars/' 41 | ansible_args: 42 | - --inventory=inventory/hosts 43 | verifier: 44 | name: ansible 45 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/schema/cv_container_v3.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Schema for cv_container_v3 8 | 9 | | Variable | Type | Required | Default | Choices | Description | 10 | | -------- | ---- | -------- | ------- | ------------------ | ----------- | 11 | | apply_mode | str | No | loose | loose
strict | Set how configlets are attached/detached to containers. If set to strict, all configlets not listed in your vars will be detached. | 12 | | state | str | No | present | present
absent | Set if Ansible should build or remove devices on CloudVision | 13 | | topology | dict | Yes | | | YAML dictionary to describe intended containers | 14 | |     parentContainerName | str | Yes | | | Name of the parent container | 15 | |     configlets | List | No | | | List of configlets | 16 | |     imageBundle | str | No | | | The name of the image bundle to be associated with the container | 17 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_offline/intended/configs/dhcpd.conf: -------------------------------------------------------------------------------- 1 | 2 | # Ansible managed: Do NOT edit this file manually! - dhcp_server01 3 | 4 | # dhcpd raw configuration 5 | class "vendor-class" { 6 | match option vendor-class-identifier; 7 | } 8 | option option-242 code 242 = string; 9 | 10 | # Subnet of ZTP interface 11 | subnet 172.17.0.0 netmask 255.255.0.0 { 12 | range 172.17.255.200 172.17.255.200; 13 | authoritative; 14 | option option-242 "MCIPADD=192.168.190.1,HTTPSRVR=192.168.190.4,HTTP DIR=/,L2QVLAN=190,L2Q=1"; 15 | } 16 | 17 | # Remote Subnet 18 | subnet 10.255.0.0 netmask 255.255.255.0 { 19 | range 10.255.0.200 10.255.0.250; 20 | option routers 10.255.0.3; 21 | option domain-name-servers 10.255.0.3; 22 | option domain-name arista.com; 23 | max-lease-time 300; 24 | } 25 | 26 | # Per host definition 27 | host DC1-SPINE1 { 28 | option host-name "DC1-SPINE1"; 29 | hardware ethernet 0c:1d:c0:1d:62:01; 30 | fixed-address 10.255.0.11; 31 | option bootfile-name "http://10.255.0.1/ztp/bootstrap"; 32 | option routers 10.255.0.3; 33 | option domain-name-servers 10.255.0.3; 34 | } 35 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_create_schedule.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test create followed by schedule 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf 1A Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_LEAF1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: LEAF1A_MLAG_Health 16 | stages: 17 | - name: LEAF1A_MLAG_Health 18 | mode: parallel 19 | 20 | tasks: 21 | - name: "Create a change control on {{inventory_hostname}}" 22 | arista.cvp.cv_change_control_v3: 23 | state: set 24 | change: "{{ change }}" 25 | register: cv_change_control_test 26 | 27 | - name: "Schedule a change control on {{inventory_hostname}} using change_id field" 28 | arista.cvp.cv_change_control_v3: 29 | state: schedule 30 | change_id: ["{{ cv_change_control_test.data.id }}"] 31 | schedule_time: "2023-01-01T01:00:00.0Z" 32 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_validate_v3/device_validate_config_multiple_cvp.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Device Config Validation in Cloudvision 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: false 6 | vars: 7 | CVP_DEVICES: 8 | - device_name: leaf1 9 | search_type: serialNumber #[hostname | serialNumber | fqdn] 10 | cvp_configlets: 11 | - validate_error 12 | - validate_valid 13 | - validate_warning 14 | CVP_CONFIGLET: 15 | validate_error: "{{lookup('file', 'configlet2.cfg')}}" 16 | validate_valid: "interface Ethernet1\n description test_validate" 17 | validate_warning: "interface Ethernet1\n spanning-tree portfast" 18 | tasks: 19 | - name: "Push configlet" 20 | arista.cvp.cv_configlet_v3: 21 | configlets: "{{CVP_CONFIGLET}}" 22 | state: present 23 | - name: Validate configurations 24 | arista.cvp.cv_validate_v3: 25 | devices: "{{CVP_DEVICES}}" 26 | validate_mode: stop_on_warning 27 | register: CVP_DEVICES_RESULTS 28 | - name: print result for {{inventory_hostname}} 29 | debug: 30 | msg: "{{CVP_DEVICES_RESULTS}}" 31 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/dhcp_management_offline/inventory/group_vars/TOOLS.yml: -------------------------------------------------------------------------------- 1 | --- 2 | ztp: 3 | default: 4 | registration: 'http://10.255.0.1/ztp/bootstrap' 5 | gateway: 10.255.0.3 6 | nameservers: 7 | - '10.255.0.3' 8 | general: 9 | dhcpd_raw: | 10 | class "vendor-class" { 11 | match option vendor-class-identifier; 12 | } 13 | option option-242 code 242 = string; 14 | subnets: 15 | - network: 172.17.0.0 16 | netmask: 255.255.0.0 17 | start: 172.17.255.200 18 | end: 172.17.255.200 19 | dhcpd_raw: | 20 | authoritative; 21 | option option-242 "MCIPADD=192.168.190.1,HTTPSRVR=192.168.190.4,HTTP DIR=/,L2QVLAN=190,L2Q=1"; 22 | - name: Remote Subnet 23 | network: 10.255.0.0 24 | netmask: 255.255.255.0 25 | gateway: 10.255.0.3 26 | nameservers: 27 | - '10.255.0.3' 28 | domain_name: arista.com 29 | start: 10.255.0.200 30 | end: 10.255.0.250 31 | lease_time: 300 32 | clients: 33 | # AVD/CVP Integration 34 | - name: DC1-SPINE1 35 | mac: 0c:1d:c0:1d:62:01 36 | ip4: 10.255.0.11 37 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/tasks/init.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | # - name: 'creating folder {{ generated_configlets_dir }}' 6 | # file: 7 | # path: '{{ generated_configlets_dir }}' 8 | # state: directory 9 | # mode: 0755 10 | # delegate_to: localhost 11 | # run_once: True 12 | 13 | - name: 'Creating folder {{ common_configlets_dir }}' 14 | ansible.builtin.file: 15 | path: '{{ common_configlets_dir }}' 16 | recurse: true 17 | state: directory 18 | mode: 0755 19 | delegate_to: localhost 20 | run_once: true 21 | 22 | - name: 'Creating folder {{ cvp_servers_dir }}' 23 | ansible.builtin.file: 24 | path: '{{ cvp_servers_dir }}' 25 | recurse: true 26 | state: directory 27 | mode: 0755 28 | delegate_to: localhost 29 | run_once: true 30 | 31 | - name: 'Creating folder {{ devices_dir }}' 32 | ansible.builtin.file: 33 | path: '{{ devices_dir }}' 34 | recurse: true 35 | state: directory 36 | mode: 0755 37 | delegate_to: localhost 38 | run_once: true 39 | -------------------------------------------------------------------------------- /tests/pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | render_collapsed = true 3 | log_cli = true 4 | filterwarnings = 5 | ignore::urllib3.exceptions.InsecureRequestWarning 6 | markers = 7 | # Generic tags 8 | slow: marks tests as slow (deselect with '-m "not slow"') 9 | api: Test using Cloudvision API (deselect with '-m "not api"') 10 | generic: Local execution environment only (deselect with '-m "not generic"') 11 | # Type of actions to run on API call 12 | create: API call to build topology (deselect with '-m "not create"') 13 | delete: API call to delete topology (deselect with '-m "not delete"') 14 | move: API call to move the device (deselect with '-m "not move"') 15 | builder: API call to deploy/undeploy topology (deselect with '-m "not builder"') 16 | # Module tags 17 | configlet: Tests dedicated to configlet module (deselect with '-m "not configlet"') 18 | container: Tests dedicated to container module (deselect with '-m "not container"') 19 | device: Tests dedicated to device module (deselect with '-m "not device"') 20 | image: Tests dedicated to image module (deselect with '-m "not image"') 21 | facts: Tests dedicated to image module (deselect with '-m "not facts"') 22 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_facts_v3/get_tasks_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run cv_facts_v3 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: no 6 | tasks: 7 | - name: Collect task facts from {{inventory_hostname}} 8 | arista.cvp.cv_facts_v3: 9 | facts: 10 | - tasks 11 | register: CV_FACTS_V3_RESULT 12 | 13 | - name: Display task facts 14 | debug: 15 | msg: "{{CV_FACTS_V3_RESULT}}" 16 | 17 | - name: Collect task facts from {{inventory_hostname}} filtered by status 18 | arista.cvp.cv_facts_v3: 19 | facts: 20 | - tasks 21 | regexp_filter: 'Pending' # filtered by status 22 | verbose: long 23 | register: CV_FACTS_V3_RESULT 24 | 25 | - name: Display task facts 26 | debug: 27 | msg: "{{CV_FACTS_V3_RESULT}}" 28 | 29 | - name: Collect task facts from {{inventory_hostname}} filtered by task_id 30 | arista.cvp.cv_facts_v3: 31 | facts: 32 | - tasks 33 | regexp_filter: 93 # filtered by task_id 34 | register: CV_FACTS_V3_RESULT 35 | 36 | - name: Display task facts filtered by task_id 37 | debug: 38 | msg: "{{CV_FACTS_V3_RESULT}}" 39 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_loose/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: cv_configlet unit testing using loose mode 3 | hosts: cv_server 4 | # connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | vars: 9 | CVP_CONFIGLETS: 10 | 01TRAINING-01: "alias a110 show version" 11 | 01TRAINING-02: "alias a102 show version" 12 | 02DEMO-01: "alias a102 show version" 13 | tasks: 14 | - name: "Include offline facts" 15 | include_vars: "{{ root_dir }}/inventory/cv_facts.json" 16 | 17 | - name: "Configure configlet on {{ inventory_hostname }}" 18 | arista.cvp.cv_configlet: 19 | cvp_facts: "{{ansible_facts}}" 20 | configlets: "{{CVP_CONFIGLETS}}" 21 | configlet_filter: ["TRAINING"] 22 | state: present 23 | register: CVP_CONFIGLET_RESULT 24 | check_mode: yes 25 | 26 | - name: "Print logs" 27 | debug: 28 | msg: "{{ CVP_CONFIGLET_RESULT }}" 29 | 30 | - name: "Check deletion process" 31 | assert: 32 | that: 33 | - "CVP_CONFIGLET_RESULT.data.deleted[0]['01TRAINING-alias'] is defined" 34 | fail_msg: "Incorrect deletion process" 35 | success_msg: "Deletion process is running as expected" 36 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Number of days of inactivity before an issue becomes stale 2 | daysUntilStale: 21 3 | # Number of days of inactivity before a stale issue is closed 4 | daysUntilClose: 7 5 | # Issues with these labels will never be considered stale 6 | exemptLabels: 7 | - "status: accepted" 8 | - "status: gathering feedback" 9 | - "status: in-progress" 10 | - "good first issue" 11 | # Label to use when marking an issue as stale 12 | staleLabel: "state: stale" 13 | # Comment to post when marking an issue as stale. Set to `false` to disable 14 | markComment: > 15 | This issue has been automatically marked as stale because it has not had 16 | recent activity. It will be closed if no further activity occurs. ansible-cvp 17 | is governed by a small group of core maintainers which means not all opened 18 | issues may receive direct feedback. Please see our [contributing guide](https://github.com/aristanetworks/ansible-cvp/blob/master/contributing.md). 19 | # Comment to post when closing a stale issue. Set to `false` to disable 20 | closeComment: > 21 | This issue has been automatically closed due to lack of activity. In an 22 | effort to reduce noise, please do not comment any further. Note that the 23 | core maintainers may elect to reopen this issue at a later date if deemed 24 | necessary. 25 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/schema/cv_validate_v3.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Schema for cv_validate_v3 8 | 9 | | Variable | Type | Required | Default | Choices | Description | 10 | | -------- | ---- | -------- | ------- | ------------------ | ----------- | 11 | | validate_mode | str | Yes | | stop_on_error
stop_on_warning
ignore | Error reporting mechanism.
stop_on_error - Stop when configlet validation throws an error or warning
stop_on_warning - Stop when configlet validation throws a warning
ignore - ignore errors and warning | 12 | | devices | list | Yes | | | CVP device and configlet information | 13 | |     device_name | str | Yes | | | Device hostname, FQDN or Serial Number. Use `search_type` to identify which information has been provided | 14 | |     search_type | str | No | hostname | fqdn
hostname
serialNumber | Search type for device_name | 15 | |     cvp_configlets | List | No | | | Name of configlets already present on CloudVision | 16 | |     local_configlets | Dict | No | | | Name and config of configlets | 17 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_create_approve_execute.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test approve, execute 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf 1A Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_LEAF1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: LEAF1A_MLAG_Health 16 | stages: 17 | - name: LEAF1A_MLAG_Health 18 | mode: parallel 19 | 20 | tasks: 21 | - name: "Create a change control on {{ inventory_hostname }}" 22 | arista.cvp.cv_change_control_v3: 23 | state: set 24 | change: "{{ change }}" 25 | register: cv_change_control_test 26 | 27 | - name: "Approve a change control using change_id field on {{ inventory_hostname }}" 28 | arista.cvp.cv_change_control_v3: 29 | state: approve 30 | change_id: ["{{ cv_change_control_test.data.id }}"] 31 | 32 | - name: "Execute a change control using change_id field on {{ inventory_hostname }} " 33 | arista.cvp.cv_change_control_v3: 34 | state: execute 35 | change_id: ["{{ cv_change_control_test.data.id }}"] 36 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_create_remove.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test create followed by remove 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf 1A Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_LEAF1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: LEAF1A_MLAG_Health 16 | stages: 17 | - name: LEAF1A_MLAG_Health 18 | mode: parallel 19 | 20 | tasks: 21 | - name: "Create a change control on {{inventory_hostname}}" 22 | arista.cvp.cv_change_control_v3: 23 | state: set 24 | change: "{{ change }}" 25 | register: cv_change_control_test 26 | 27 | - name: "Show a change control on {{inventory_hostname}} using change_id field" 28 | arista.cvp.cv_change_control_v3: 29 | state: show 30 | change_id: ["{{ cv_change_control_test.data.id }}"] 31 | register: cv_show_cc_by_id 32 | 33 | - name: "Remove a change control on {{inventory_hostname}} using change_id field" 34 | arista.cvp.cv_change_control_v3: 35 | state: remove 36 | change_id: ["{{ cv_change_control_test.data.id }}"] 37 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_facts_v3/get_devices_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Run cv_facts_v3 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: no 6 | vars: 7 | ansible_command_timeout: 1200 8 | ansible_connect_timeout: 600 9 | tasks: 10 | - name: Collect devices facts from {{inventory_hostname}} 11 | arista.cvp.cv_facts_v3: 12 | facts: 13 | - devices # collects all devices facts 14 | register: CV_FACTS_V3_RESULT 15 | 16 | - name: Display devices facts 17 | debug: 18 | msg: "{{CV_FACTS_V3_RESULT}}" 19 | 20 | - name: Collect devices facts from {{inventory_hostname}} 21 | arista.cvp.cv_facts_v3: 22 | facts: 23 | - devices 24 | regexp_filter: 'leaf3' # collects specific device facts 25 | register: CV_FACTS_V3_RESULT 26 | 27 | - name: Display devices facts 28 | debug: 29 | msg: "{{CV_FACTS_V3_RESULT}}" 30 | 31 | - name: Collect devices facts from {{inventory_hostname}} 32 | arista.cvp.cv_facts_v3: 33 | facts: 34 | - devices 35 | regexp_filter: 'leaf3' # collects specific device facts 36 | verbose: long # using verbose long 37 | register: CV_FACTS_V3_RESULT 38 | 39 | - name: Display devices facts 40 | debug: 41 | msg: "{{CV_FACTS_V3_RESULT}}" 42 | -------------------------------------------------------------------------------- /tests/lib/config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | # coding: utf-8 -*- 6 | """ 7 | config.py - Declares the credential of the specified server 8 | """ 9 | import os 10 | 11 | # TODO - use f-strings 12 | # pylint: disable=consider-using-f-string 13 | 14 | 15 | def strtobool(val): 16 | """ 17 | Convert a string representation of truth to true (1) or false (0). 18 | True values are 'y', 'yes', 't', 'true', 'on', and '1'; false values 19 | are 'n', 'no', 'f', 'false', 'off', and '0'. Raises ValueError if 20 | 'val' is anything else. 21 | 22 | Port of distutils.util.strtobool as per PEP 0632 recommendation 23 | https://peps.python.org/pep-0632/#migration-advice 24 | """ 25 | val = val.lower() 26 | if val in ("y", "yes", "t", "true", "on", "1"): 27 | return 1 28 | elif val in ("n", "no", "f", "false", "off", "0"): 29 | return 0 30 | else: 31 | raise ValueError("invalid truth value %r" % (val,)) 32 | 33 | 34 | user_token = os.getenv("ARISTA_AVD_CV_TOKEN", "unset_token") 35 | server = os.getenv("ARISTA_AVD_CV_SERVER", "") 36 | provision_cv = strtobool(os.getenv("ARISTA_AVD_CV_PROVISION", "true")) 37 | cvaas = strtobool(os.getenv("ARISTA_AVD_CVAAS", "true")) 38 | -------------------------------------------------------------------------------- /.github/workflows/tag-management.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Tag & Release management" 3 | 4 | on: 5 | push: 6 | # Sequence of patterns matched against refs/tags 7 | tags: 8 | - 'v*' # Push events to matching v*, i.e. v1.0, v20.15.10 9 | 10 | jobs: 11 | build: 12 | name: Upload Release Asset 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout code 16 | uses: actions/checkout@v4 17 | 18 | - name: Build project # This would actually build your project, using zip for an example artifact 19 | run: | 20 | sed -i 's/version: 0\.0\.0/version: '"$(git describe --abbrev=0 --tags | sed 's/v//')"'/' ansible_collections/arista/cvp/galaxy.yml 21 | make collection-build 22 | 23 | - name: Upload Collection Package to GH Action 24 | uses: actions/upload-artifact@v4 25 | with: 26 | name: ansible-collection-package 27 | path: | 28 | ./*.tar.gz 29 | 30 | # - name: Release on Github 31 | # uses: softprops/action-gh-release@v1 32 | # with: 33 | # files: '*.tar.gz' 34 | 35 | # - name: Publish Collection to galaxy 36 | # uses: artis3n/ansible_galaxy_collection@v2 37 | # with: 38 | # api_key: '${{ secrets.GALAXY_API_KEY }}' 39 | # collection_dir: 'ansible_collections/arista/cvp/' 40 | # build: false 41 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Change Summary 2 | 3 | 4 | 5 | ## Related Issue(s) 6 | 7 | Fixes # 8 | 9 | ## Component(s) name 10 | 11 | `arista.cvp.` 12 | 13 | ## Proposed changes 14 | 15 | 16 | 17 | ## How to test 18 | 19 | 20 | 21 | ## Checklist 22 | 23 | ### User Checklist 24 | 25 | 26 | - N/A 27 | 28 | ### Repository Checklist 29 | 30 | 31 | 32 | - [ ] My code has been rebased from devel before I start 33 | - [ ] I have read the [**CONTRIBUTING**](https://avd.arista.com/devel/docs/contribution/overview.html) document. 34 | - [ ] My change requires a change to the documentation and documentation have been updated accordingly. (check the box if not applicable) 35 | - [ ] I have updated [molecule CI](https://github.com/aristanetworks/ansible-cvp/tree/devel/ansible_collections/arista/cvp/molecule) testing accordingly. (check the box if not applicable) 36 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_create_schedule_approve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test schedule followed by approve 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf 1A Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_LEAF1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: LEAF1A_MLAG_Health 16 | stages: 17 | - name: LEAF1A_MLAG_Health 18 | mode: parallel 19 | 20 | tasks: 21 | - name: "Create a change control on {{inventory_hostname}}" 22 | arista.cvp.cv_change_control_v3: 23 | state: set 24 | change: "{{ change }}" 25 | register: cv_change_control_test 26 | 27 | - name: "Schedule a change control on {{inventory_hostname}} using change_id field" 28 | arista.cvp.cv_change_control_v3: 29 | state: schedule 30 | change_id: ["{{ cv_change_control_test.data.id }}"] 31 | schedule_time: "2023-01-01T01:00:00.0Z" 32 | 33 | - name: "Approve a change control on {{inventory_hostname}} using change_id field" 34 | arista.cvp.cv_change_control_v3: 35 | state: approve 36 | change_id: ["{{ cv_change_control_test.data.id }}"] 37 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_show_unknown.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test show unknown 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf 1A Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_LEAF1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: LEAF1A_MLAG_Health 16 | stages: 17 | - name: LEAF1A_MLAG_Health 18 | mode: parallel 19 | 20 | tasks: 21 | - name: "Show an unknown change control on {{inventory_hostname}} using change_id field - this should fail" 22 | arista.cvp.cv_change_control_v3: 23 | state: show 24 | change_id: ["1234567890"] 25 | register: cv_show_unknown_id 26 | 27 | - name: "Print out the change control}" 28 | debug: 29 | msg: "{{ cv_show_unknown_id }}" 30 | 31 | - name: "Show an unknown change control on {{inventory_hostname}} using change control name - this should fail" 32 | arista.cvp.cv_change_control_v3: 33 | state: show 34 | name: "Unknown-CC-1234567890" 35 | register: cv_show_unknown_name 36 | 37 | - name: "Print out the change control}" 38 | debug: 39 | msg: "{{ cv_show_unknown_name }}" 40 | -------------------------------------------------------------------------------- /.github/workflows/publish-pages.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: mkdocs-to-pages 3 | concurrency: mkdocs-to-pages 4 | on: 5 | push: 6 | branches: [devel] 7 | paths: 8 | - ansible_collections/arista/cvp/** 9 | - mkdocs.yml 10 | - .github/workflows/publish-pages.yml 11 | workflow_dispatch: 12 | branches: [devel] 13 | permissions: 14 | contents: write 15 | pages: write 16 | id-token: write 17 | jobs: 18 | build: 19 | if: github.repository == 'aristanetworks/ansible-cvp' 20 | runs-on: ubuntu-22.04 21 | steps: 22 | 23 | - name: Checkout code ✅ 24 | uses: actions/checkout@v4 25 | 26 | - name: Setup Python3 🐍 27 | uses: actions/setup-python@v5 28 | with: 29 | python-version: "3.11" 30 | 31 | - name: Build MkDocs Site 32 | run: | 33 | pip install mkdocs-material 34 | pip install mkdocs-macros-plugin 35 | pip install mdx_truly_sane_lists 36 | pip install mkdocs-glightbox 37 | pip install mkdocs-git-revision-date-localized-plugin 38 | mkdocs build 39 | 40 | - name: Setup Pages 📖 41 | uses: actions/configure-pages@v5 42 | 43 | - name: Upload artifact 🔼 44 | uses: actions/upload-pages-artifact@v3 45 | with: 46 | path: ./site/ 47 | 48 | - name: Deploy to GitHub Pages 🚀 49 | id: deployment 50 | uses: actions/deploy-pages@v4 51 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/tools_schema.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | # coding: utf-8 -*- 6 | # pylint: disable=logging-format-interpolation 7 | # pylint: disable = duplicate-code 8 | # flake8: noqa: R0801 9 | # 10 | 11 | 12 | from __future__ import (absolute_import, division, print_function) 13 | __metaclass__ = type 14 | 15 | import logging 16 | try: 17 | import jsonschema 18 | HAS_JSONSCHEMA = True 19 | except ImportError: 20 | HAS_JSONSCHEMA = False 21 | 22 | 23 | LOGGER = logging.getLogger(__name__) 24 | 25 | 26 | def validate_json_schema(user_json: dict, schema): 27 | """ 28 | validate_cv_inputs JSON SCHEMA Validation. 29 | 30 | Run a JSON validation against a muser's defined JSONSCHEMA. 31 | 32 | Parameters 33 | ---------- 34 | user_json : dict 35 | JSON to validate 36 | schema : jsonschema 37 | JSON Schema to use to validate JSON 38 | 39 | Returns 40 | ------- 41 | boolean 42 | True if valid, False if not. 43 | """ 44 | try: 45 | jsonschema.validate(instance=user_json, schema=schema) 46 | except jsonschema.ValidationError as error_message: 47 | LOGGER.error("Invalid inputs %s", str(error_message)) 48 | return False 49 | return True 50 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/tasks/push.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | - name: "Collect CVP Facts" 6 | tags: 7 | - sync 8 | arista.cvp.cv_facts: 9 | register: CVP_FACTS 10 | 11 | - name: "Refresh Shared Configlet Data" 12 | tags: 13 | - sync 14 | ansible.builtin.include_vars: 15 | file: '{{ common_configlets_dir }}/master.yml' 16 | name: 'shared_configlets' 17 | delegate_to: '{{ ansible_runner }}' 18 | 19 | - name: 'Refresh Shared configlet files for CVP' 20 | tags: 21 | - sync 22 | ansible.builtin.template: 23 | src: "template.cvp.shared_configlets.j2" 24 | dest: '{{ cvp_servers_dir }}/{{ inventory_hostname }}_shared.yml' 25 | mode: 0644 26 | delegate_to: '{{ ansible_runner }}' 27 | run_once: false 28 | 29 | - name: "Load refreshed CVP shared Configlet information" 30 | tags: 31 | - sync 32 | ansible.builtin.include_vars: '{{ cvp_servers_dir }}/{{ inventory_hostname }}_shared.yml' 33 | delegate_to: '{{ ansible_runner }}' 34 | 35 | - name: 'Update Shared configlets on CVP Servers.' 36 | tags: 37 | - sync 38 | arista.cvp.cv_configlet: 39 | cvp_facts: "{{ CVP_FACTS.ansible_facts }}" 40 | configlets: "{{ CVP_CONFIGLET }}" 41 | configlet_filter: ['{{ configlet_filter }}'] 42 | when: 43 | - CVP_CONFIGLET is defined 44 | -------------------------------------------------------------------------------- /.github/check-git-status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Purpose: Molecule runner for github-action 4 | # Author: @titom73 5 | # Date: 2020-12-16 6 | # Version: 1.1 7 | # License: APACHE 8 | # -------------------------------------- 9 | 10 | echo "Script running from ${PWD}" 11 | 12 | # Set default values 13 | INPUT_CHECK_GIT="${INPUT_CHECK_GIT:-true}" 14 | INPUT_CHECK_GIT_ENFORCED="${INPUT_CHECK_GIT_ENFORCED:-true}" 15 | 16 | if [ ${INPUT_CHECK_GIT} = "true" ]; then 17 | git config core.fileMode false 18 | echo " * Run Git Verifier because CHECK_GIT is set to ${INPUT_CHECK_GIT}" 19 | # if git diff-index --quiet HEAD --; then 20 | GIT_STATUS="$(git status --porcelain)" 21 | if [ "$?" -ne "0" ]; then 22 | echo "'git status --porcelain' failed to run - something is wrong" 23 | exit 1 24 | fi 25 | if [ -n "$GIT_STATUS" ]; then 26 | # Some changes 27 | echo 'Some changes' 28 | echo '------------' 29 | git --no-pager status --short 30 | echo '' 31 | echo 'Diffs are:' 32 | echo '------------' 33 | git --no-pager diff 34 | if [ ${INPUT_CHECK_GIT_ENFORCED} = "true" ]; then 35 | exit 1 36 | else 37 | exit 0 38 | fi 39 | else 40 | # No Changes 41 | echo ' - No change found after running Molecule' 42 | exit 0 43 | fi 44 | exit 0 45 | else 46 | echo " * Git verifier skipped as not set to true" 47 | fi 48 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/changelogs/fragments_backup/v3.4.0.yml: -------------------------------------------------------------------------------- 1 | release_summary: | 2 | Release 3.4.0 - See documentation on cvp.avd.sh for details. 3 | minor_changes: 4 | - Feat (dhcp_configuration) add a name to the dhcp record (https://github.com/aristanetworks/ansible-cvp/issues/481) 5 | - Feat Add support for change controls (https://github.com/aristanetworks/ansible-cvp/issues/464) 6 | - Feat Add svc account token auth method for on-prem and standardize it with cvaas (https://github.com/aristanetworks/ansible-cvp/issues/458) 7 | - Feat New module to support topology tags (https://github.com/aristanetworks/ansible-cvp/issues/459) 8 | - Feat Facts update (https://github.com/aristanetworks/ansible-cvp/issues/469) 9 | - Feat(cv_facts_v3) Show assigned image bundles on devices and containers (https://github.com/aristanetworks/ansible-cvp/issues/488) 10 | - Feat(module_utils) Raise NotImplementedError if encrypted Vault password (https://github.com/aristanetworks/ansible-cvp/issues/479) 11 | 12 | bugfixes: 13 | - Fix(image_tools) Change from error to warning if image already exists (https://github.com/aristanetworks/ansible-cvp/issues/471) 14 | - Fix Changed pytest to check for a warning (https://github.com/aristanetworks/ansible-cvp/issues/485) 15 | - Fix(cv_container_v3) Cannot remove containers anymore (https://github.com/aristanetworks/ansible-cvp/issues/487) 16 | - Fix(cv_device_v3) device lookup to use search_key instead of FQDN always (https://github.com/aristanetworks/ansible-cvp/issues/483) 17 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/dhcp_configuration/tasks/online.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | - name: Gather os specific variables for Centos / Red Hat OS 6 | ansible.builtin.include_vars: "centos-{{ ansible_distribution_major_version }}.yml" 7 | when: ansible_distribution == "CentOS" or ansible_distribution == 'Red Hat Enterprise Linux' 8 | 9 | - name: Gather os specific variables for Debian / Ubuntu OS 10 | ansible.builtin.include_vars: "debian.yml" 11 | when: ansible_distribution == "Debian" or ansible_distribution == 'Ubuntu' 12 | 13 | - name: Update packages list 14 | ansible.builtin.apt: 15 | update_cache: true 16 | when: ansible_distribution == "Debian" or ansible_distribution == 'Ubuntu' 17 | 18 | - name: Install packages 19 | become: true 20 | ansible.builtin.package: 21 | name: "{{ dhcp_packages }}" 22 | state: "{{ dhcp_packages_state }}" 23 | 24 | - name: Run task for debian host 25 | ansible.builtin.include_tasks: fix-debian.yml 26 | when: ansible_distribution == "Debian" or ansible_distribution == 'Ubuntu' 27 | 28 | - name: 'Generate DHCPd configuration file' 29 | become: true 30 | ansible.builtin.template: 31 | src: 'dhcpd.conf.j2' 32 | dest: '{{ dhcp_config }}' 33 | mode: 0644 34 | backup: true 35 | notify: "Restart dhcpd" 36 | 37 | - name: Check & activate DHCP service 38 | become: true 39 | ansible.builtin.service: 40 | name: '{{ dhcp_service }}' 41 | enabled: true 42 | state: started 43 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_device_v3/reconcile.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | from cvprac.cvp_client import CvpClient 6 | import yaml 7 | import requests.packages.urllib3 8 | requests.packages.urllib3.disable_warnings() 9 | 10 | # Setting variables 11 | RECONCILE = 'RECONCILE_' # Prefix for the configlet name 12 | fixture_file = '../fixtures/cv_device_v3.yaml' 13 | 14 | with open(fixture_file, encoding="utf-8") as f: 15 | dut = yaml.safe_load(f) 16 | 17 | # load password from fixtures otherwise assume it's and ATD environemnt and read the password from 18 | # the config file 19 | if len(dut[0]["password"]) > 0: 20 | password = dut[0]["password"] 21 | else: 22 | config_file = "/home/coder/.config/code-server/config.yaml" 23 | with open(config_file, encoding="utf-8") as f: 24 | password = yaml.safe_load(f)["password"] 25 | 26 | # Connect to CloudVision 27 | clnt = CvpClient() 28 | clnt.set_log_level(log_level='WARNING') 29 | clnt.connect([dut[0]["node"]], dut[0]["username"], password) 30 | 31 | # Store device information 32 | device = clnt.api.get_device_by_serial(dut[0]["device"]) 33 | dev_mac = device["systemMacAddress"] 34 | 35 | # Reconcile device configuration 36 | rc = clnt.api.get_device_configuration(dev_mac) 37 | name = RECONCILE + device['serialNumber'] 38 | update = clnt.api.update_reconcile_configlet(dev_mac, rc, "", name, True) 39 | addcfg = clnt.api.apply_configlets_to_device("auto-reconciling", device, [update['data']]) 40 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/modules/cv_task_v3.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # cv_task_v3 8 | 9 | Execute or Cancel CVP Tasks. 10 | 11 | Module added in version 3.0.0 12 | ## Synopsis 13 | 14 | CloudVision Portal Task module to action pending tasks on CloudVision 15 | 16 | ## Module-specific Options 17 | 18 | The following options may be specified for this module: 19 | 20 | | parameter | type | required | default | choices | comments | 21 | | ------------- |-------------| ---------|----------- |--------- |--------- | 22 | | tasks | list | True | | | CVP taskIDs to act on | 23 | | state | str | False | executed |
  • executed
  • cancelled
| Action to carry out on the task. | 24 | 25 | 26 | ## Examples 27 | 28 | ```yaml 29 | 30 | --- 31 | - name: Execute all tasks registered in cvp_configlets variable 32 | arista.cvp.cv_task_v3: 33 | tasks: "{{ cvp_configlets.taskIds }}" 34 | 35 | - name: Cancel a list of pending tasks 36 | arista.cvp.cv_task_v3: 37 | tasks: ['666', '667'] 38 | state: cancelled 39 | 40 | ``` 41 | 42 | For a complete list of examples, check them out on our [GitHub repository](https://github.com/aristanetworks/ansible-cvp/tree/devel/ansible_collections/arista/cvp/examples). 43 | 44 | ## Module output 45 | 46 | ??? output "Example output" 47 | ```yaml 48 | --8<-- 49 | docs/outputs/cv_task_v3.txt 50 | --8<-- 51 | ``` 52 | 53 | ## Author 54 | 55 | Ansible Arista Team (@aristanetworks) 56 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_create_approve_and_execute.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test approve_and_execute 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf1 Pair Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_Leaf1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: Leaf_MLAG_Health 16 | - action: "mlaghealthcheck" 17 | name: Check_Leaf1B_MLAG_Health 18 | arguments: 19 | - name: DeviceID 20 | value: SN-DC1-POD1-LEAF1B 21 | stage: Leaf_MLAG_Health 22 | - task_id: "50" 23 | stage: Leaf1A_upgrade 24 | - task_id: "51" 25 | stage: Leaf1B_upgrade 26 | stages: 27 | - name: Leaf_MLAG_Health 28 | mode: parallel 29 | - name: Leaf Upgrades 30 | modes: series 31 | - name: Leaf1A_upgrade 32 | parent: Leaf Upgrades 33 | - name: Leaf1B_upgrade 34 | parent: Leaf Upgrades 35 | 36 | tasks: 37 | - name: "Create a change control on {{ inventory_hostname }}" 38 | arista.cvp.cv_change_control_v3: 39 | state: set 40 | change: "{{ change }}" 41 | register: cv_change_control_test 42 | 43 | - name: "Approve and Execute a change control using change_id field on {{ inventory_hostname }}" 44 | arista.cvp.cv_change_control_v3: 45 | state: approve_and_execute 46 | change_id: ["{{ cv_change_control_test.data.id }}"] 47 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_create_schedule_and_approve.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test schedule_and_approve 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf1 Pair Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_Leaf1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: Leaf_MLAG_Health 16 | - action: "mlaghealthcheck" 17 | name: Check_Leaf1B_MLAG_Health 18 | arguments: 19 | - name: DeviceID 20 | value: SN-DC1-POD1-LEAF1B 21 | stage: Leaf_MLAG_Health 22 | - task_id: "50" 23 | stage: Leaf1A_upgrade 24 | - task_id: "51" 25 | stage: Leaf1B_upgrade 26 | stages: 27 | - name: Leaf_MLAG_Health 28 | mode: parallel 29 | - name: Leaf Upgrades 30 | modes: series 31 | - name: Leaf1A_upgrade 32 | parent: Leaf Upgrades 33 | - name: Leaf1B_upgrade 34 | parent: Leaf Upgrades 35 | 36 | tasks: 37 | - name: "Create a change control on {{inventory_hostname}}" 38 | arista.cvp.cv_change_control_v3: 39 | state: set 40 | change: "{{ change }}" 41 | register: cv_change_control_test 42 | 43 | - name: "Schedule and Approve a change control on {{inventory_hostname}} using change_id field" 44 | arista.cvp.cv_change_control_v3: 45 | state: schedule_and_approve 46 | change_id: ["{{ cv_change_control_test.data.id }}"] 47 | schedule_time: "2023-01-01T01:00:00.0Z" 48 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/_overrides/main.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | 4 | {% block extrahead %} 5 | 6 | 7 | {% set title = config.site_name %} 8 | {% if page and page.title and not page.is_homepage %} 9 | {% set title = config.site_name ~ " - " ~ page.title | striptags %} 10 | {% endif %} 11 | 12 | {% set image = 'https://raw.githubusercontent.com/aristanetworks/avd/devel/docs/_media/avd-logo.png' %} 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {% endblock %} 33 | 34 | 35 | {% block content %} 36 | {{ super() }} 37 | 38 | 39 |
40 | 41 |
42 | 43 | {% include ".icons/fontawesome/solid/book.svg" %} 44 | 45 |
46 | 47 |
48 | {% endblock %} 49 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/schema/cv_tag_v3.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Schema for cv_tag_v3 8 | 9 | | Variable | Type | Required | Default | Choices | Description | 10 | | -------- | ---- | -------- | ------- | ------------------ | ----------- | 11 | | auto_create | bool | No | True | Yes
No | auto_create tags before assigning | 12 | | mode | str | No | | create
delete
assign
unassign | action to carry out on the tags.
create - create tags
delete - delete tags
assign - assign existing tags on device
unassign - unassign existing tags from device | 13 | | tags | list | Yes | | | CVP tags | 14 | |     device | str | No | | | device to assign tags to | 15 | |     device_id | str | No | | | serial number of the device to assign tags to | 16 | |     device_tags | List | No | | | device tags | 17 | |         - name | str | Yes | | | name of tag | 18 | |           value | str | Yes | | | value of tag | 19 | |     interface_tags | List | No | | | interface tags | 20 | |         interface | str | No | | | Interface to apply tags on | 21 | |         tags | List | No | | | interface tags | 22 | |           - name | str | Yes | | | name of tag | 23 | |             value | str | Yes | | | value of tag | 24 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/templates/template.device_configlets.j2: -------------------------------------------------------------------------------- 1 | {# 2 | Copyright (c) 2023-2025 Arista Networks, Inc. 3 | Use of this source code is governed by the Apache License 2.0 4 | that can be found in the LICENSE file. 5 | #} 6 | --- 7 | {% if "_1" in inventory_hostname %} 8 | CVP_DEVICES: 9 | {% for device in groups['leafs_1'] %} 10 | '{{device}}': 11 | name: {{device}} 12 | configlets: 13 | - '{{device}}_base-sw' 14 | - '{{device}}_nw' 15 | - '{{device}}_ep_INNOVATE' 16 | {% endfor %} 17 | 18 | CVP_CONFIGLET: 19 | {% for host in groups['leafs_1'] %} 20 | {{host}}_ep_INNOVATE: "{{ lookup('file', 'generated_vars/devices/'+host+'.txt') | replace('\n', '\\n')}}" 21 | {% endfor %} 22 | {% else %} 23 | CVP_DEVICES: 24 | {% for device in groups['leafs_2'] %} 25 | '{{device}}': 26 | name: {{device}} 27 | configlets: 28 | - '{{device}}_base-sw' 29 | - '{{device}}_nw' 30 | - '{{device}}_ep_INNOVATE' 31 | {% endfor %} 32 | 33 | CVP_CONFIGLET: 34 | {% for host in groups['leafs_2'] %} 35 | {{host}}_ep_INNOVATE: "{{ lookup('file', 'generated_vars/devices/'+host+'.txt') | replace('\n', '\\n')}}" 36 | {% endfor %} 37 | {% endif %} 38 | 39 | CVP_DEVICES_ROLLBACK: 40 | {% if "_1" in inventory_hostname %} 41 | {% for device in groups['leafs_1'] %} 42 | '{{device}}': 43 | name: {{device}} 44 | configlets: 45 | - '{{device}}_base-sw' 46 | - '{{device}}_nw' 47 | - '{{device}}_ep' 48 | {% endfor %} 49 | {% else %} 50 | {% for device in groups['leafs_2'] %} 51 | '{{device}}': 52 | name: {{device}} 53 | configlets: 54 | - '{{device}}_base-sw' 55 | - '{{device}}_nw' 56 | - '{{device}}_ep' 57 | {% endfor %} 58 | {% endif %} 59 | -------------------------------------------------------------------------------- /tests/lib/mock_ansible.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | # coding: utf-8 -*- 6 | 7 | from unittest.mock import MagicMock, create_autospec 8 | import logging 9 | from ansible.module_utils.basic import AnsibleModule 10 | from ansible.module_utils.connection import Connection 11 | 12 | LOGGER = logging.getLogger(__name__) 13 | 14 | 15 | class AnsibleFailJson(Exception): 16 | """Exception class to be raised by module.fail_json and caught by the test case""" 17 | pass 18 | 19 | 20 | def fail_json(msg: str = None): 21 | raise AnsibleFailJson(msg) 22 | 23 | 24 | def get_ansible_module(check_mode: bool = False): 25 | """ 26 | Return a mock ansible.module_utils.basic.AnsibleModule instance. 27 | The test case could eventually verify that the module exited correcty by calling the `module.exit_json.assert_called()` method. 28 | 29 | Returns 30 | ------- 31 | MagicMock 32 | The mock AnsibleModule instance 33 | """ 34 | mock_module = create_autospec(AnsibleModule) 35 | mock_module.fail_json.side_effect = fail_json 36 | mock_module.check_mode = check_mode 37 | return mock_module 38 | 39 | 40 | def get_ansible_connection(): 41 | """ 42 | Return a mock ansible.module_utils.connection.Connection instance. 43 | 44 | Returns 45 | ------- 46 | MagicMock 47 | The mock Connection instance 48 | """ 49 | # The issue with create_autospec is that Connection relies on 50 | # __getattr__ method to automatically generate a call to __rpc__ 51 | # for any method applied to it. Hence using a generic MagicMock 52 | mock_connection = MagicMock(spec=Connection) 53 | return mock_connection 54 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_device_v3/device_config_assign_atd.yaml: -------------------------------------------------------------------------------- 1 | - name: Playbook to demonstrate cvp modules. 2 | hosts: cv_server 3 | connection: local 4 | gather_facts: no 5 | vars: 6 | # Container definition 7 | containers_provision: 8 | ATD_FABRIC: 9 | parentContainerName: Tenant 10 | ATD_LEAFS: 11 | parentContainerName: ATD_FABRIC 12 | ATD_SPINES: 13 | parentContainerName: ATD_FABRIC 14 | ATD_LEAF1: 15 | parentContainerName: ATD_LEAFS 16 | ATD_LEAF2: 17 | parentContainerName: ATD_LEAFS 18 | 19 | # Device definition 20 | devices_provision: 21 | - fqdn: spine1 22 | parentContainerName: 'ATD_SPINES' 23 | configlets: 24 | - BaseIPv4_Spine1_EVPN_GUIDE 25 | - fqdn: spine2 26 | parentContainerName: 'ATD_SPINES' 27 | configlets: 28 | - BaseIPv4_Spine2_EVPN_GUIDE 29 | - fqdn: leaf1 30 | parentContainerName: 'ATD_LEAF1' 31 | configlets: 32 | - BaseIPv4_Leaf1_EVPN_GUIDE 33 | - fqdn: leaf2 34 | parentContainerName: 'ATD_LEAF1' 35 | configlets: 36 | - BaseIPv4_Leaf2_EVPN_GUIDE 37 | - fqdn: leaf3 38 | parentContainerName: 'ATD_LEAF2' 39 | configlets: 40 | - BaseIPv4_Leaf3_EVPN_GUIDE 41 | - fqdn: leaf4 42 | parentContainerName: 'ATD_LEAF2' 43 | configlets: 44 | - BaseIPv4_Leaf4_EVPN_GUIDE 45 | 46 | 47 | tasks: 48 | - name: "Build Container topology on {{inventory_hostname}}" 49 | arista.cvp.cv_container_v3: 50 | topology: '{{containers_provision}}' 51 | 52 | - name: "Configure devices on {{inventory_hostname}}" 53 | arista.cvp.cv_device_v3: 54 | devices: '{{devices_provision}}' 55 | apply_mode: loose 56 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_facts_v3/test_image_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Test cv_facts_v3 3 | hosts: CloudVision 4 | connection: local 5 | gather_facts: false 6 | vars: 7 | ansible_command_timeout: 1200 8 | ansible_connect_timeout: 600 9 | image_name: vEOS-lab-4.30.1F.swi # image file should be present 10 | bundle_name: Test_bundle 11 | 12 | tasks: 13 | # Upload image 14 | - name: Upload EOS image to {{ inventory_hostname }} 15 | arista.cvp.cv_image_v3: 16 | mode: image 17 | action: add 18 | image: "{{ image_name }}" 19 | 20 | # Create bundle 21 | - name: Create bundle {{ inventory_hostname }} 22 | arista.cvp.cv_image_v3: 23 | mode: bundle 24 | action: add 25 | bundle_name: "{{ bundle_name }}" 26 | image_list: 27 | - "{{ image_name }}" 28 | 29 | - name: Collect images facts from {{ inventory_hostname }} filtered by name 30 | arista.cvp.cv_facts_v3: 31 | facts: 32 | - images 33 | regexp_filter: "{{ image_name }}" # collects specific image facts 34 | register: CV_FACTS_V3_RESULT 35 | 36 | - name: Check cv_facts_v3 result 37 | ansible.builtin.assert: 38 | that: 39 | - CV_FACTS_V3_RESULT.changed == false 40 | - CV_FACTS_V3_RESULT.data.cvp_configlets == [] 41 | - CV_FACTS_V3_RESULT.data.cvp_containers == [] 42 | - CV_FACTS_V3_RESULT.data.cvp_devices == [] 43 | - CV_FACTS_V3_RESULT.data.cvp_tasks == [] 44 | - CV_FACTS_V3_RESULT.data.cvp_images['vEOS-lab-4.30.1F.swi'] is defined 45 | 46 | - name: Delete bundle {{ inventory_hostname }} 47 | arista.cvp.cv_image_v3: 48 | mode: bundle 49 | action: remove 50 | bundle_name: "{{ bundle_name }}" 51 | image_list: 52 | - "{{ image_name }}" 53 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/generic_tools.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | # coding: utf-8 -*- 6 | # 7 | 8 | 9 | from __future__ import (absolute_import, division, print_function) 10 | __metaclass__ = type 11 | 12 | 13 | class CvElement(object): 14 | """ 15 | CvElement Structure for simple element representation 16 | """ 17 | 18 | def __init__(self, cv_data: dict): 19 | self.__cv_data = cv_data 20 | 21 | @property 22 | def name(self): 23 | """ 24 | name Getter to expose NAME field 25 | 26 | Returns 27 | ------- 28 | str 29 | Value of KEY field 30 | """ 31 | if 'name' in self.__cv_data: 32 | return self.__cv_data['name'] 33 | return None 34 | 35 | @property 36 | def key(self): 37 | """ 38 | key Getter to expose KEY field 39 | 40 | Returns 41 | ------- 42 | str 43 | Value of the KEY field 44 | """ 45 | if 'key' in self.__cv_data: 46 | return self.__cv_data['key'] 47 | 48 | @property 49 | def reconciled(self): 50 | """ 51 | key Getter to expose RECONCILED field 52 | 53 | Returns 54 | ------- 55 | str 56 | Value of the KEY field 57 | """ 58 | if 'reconciled' in self.__cv_data: 59 | return self.__cv_data['reconciled'] 60 | 61 | @property 62 | def data(self): 63 | """ 64 | data Getter to expose structure in a dict way 65 | 66 | Returns 67 | ------- 68 | dict 69 | A dict with KEY and NAME data 70 | """ 71 | return self.__cv_data 72 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/installation/requirements.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Requirements 8 | 9 | ## Arista EOS version 10 | 11 | - EOS **4.21.8M** or later 12 | - Roles validated with eAPI transport -> `ansible_connection: httpapi` 13 | 14 | ## Arista CloudVision 15 | 16 | !!! info 17 | Starting with version 2.0.0, the collection uses [cvprac](https://github.com/aristanetworks/cvprac) as CloudVision connection manager. So support for any new CloudVision server is tied to it's support in this Python library. 18 | 19 | | ansible-cvp | 1.0.0 | 1.1.0 | >= 2.0.0 |>= 3.9.0 | 20 | | ----------- | ----- | ----- | -------- | -------- | 21 | | 2018.2 | ✅ | ✅ | ✅ | | 22 | | 2019.x | ✅ | ✅ | ✅ | | 23 | | 2020.1 | | ✅ | ✅ | | 24 | | >= 2020.2 | | | ✅ | | 25 | | >= 2021.3 | | | | ✅ | 26 | 27 | ## Python 28 | 29 | - Python **3.10** or later 30 | 31 | ## Supported Ansible Versions 32 | 33 | - ansible-core from **2.16.0** to **2.18.x** 34 | 35 | ## Additional Python Libraries required 36 | 37 | ```pip 38 | --8<-- 39 | requirements.txt 40 | --8<-- 41 | ``` 42 | 43 | ### Python requirements installation 44 | 45 | In a shell, run the following commands after installing the collection from ansible-galaxy: 46 | 47 | ```shell 48 | export ARISTA_CVP_DIR=$(ansible-galaxy collection list arista.cvp --format yaml | head -1 | cut -d: -f1) 49 | pip3 install -r ${ARISTA_CVP_DIR}/arista/cvp/requirements.txt 50 | ``` 51 | 52 | If the collection is cloned from GitHub, the requirements file can be referenced directly: 53 | 54 | ```shell 55 | pip3 install -r ansible-cvp/ansible_collections/arista/cvp/requirements.txt 56 | ``` 57 | 58 | !!! warning 59 | Depending of your operating system settings, `pip3` might be replaced by `pip`. 60 | -------------------------------------------------------------------------------- /tests/unit/conftest.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | from unittest.mock import create_autospec 5 | import pytest 6 | from tests.lib import mockMagic 7 | from cvprac.cvp_client import CvpClient, CvpApi 8 | 9 | 10 | @pytest.fixture 11 | def apply_mock(mocker): 12 | """ 13 | apply_mock - factory function to return a method to apply mocker.patch() on paths 14 | """ 15 | def _apply_mock_factory(paths: list): 16 | return [mocker.patch(path) for path in paths] 17 | 18 | return _apply_mock_factory 19 | 20 | @pytest.fixture 21 | def mock_cvpClient(): 22 | """ 23 | mock_cvprac - mocks cvprac classes/objects 24 | """ 25 | # mocked cvpClient object 26 | mock_cvpClient = create_autospec(CvpClient) 27 | mock_cvpClient.api = create_autospec(spec=CvpApi) 28 | mock_cvpClient.api.validate_config_for_device.side_effect = mockMagic.validate_config_for_device 29 | mock_cvpClient.api.move_device_to_container.side_effect = mockMagic.move_device_to_container 30 | mock_cvpClient.api.remove_image_from_element.side_effect = mockMagic.remove_image_from_element 31 | mock_cvpClient.api.get_image_bundle_by_name.side_effect = mockMagic.get_image_bundle_by_name 32 | mock_cvpClient.api.apply_image_to_element.side_effect = mockMagic.apply_image_to_element 33 | mock_cvpClient.api.get_image_bundle_by_name.side_effect = mockMagic.get_image_bundle_by_name 34 | mock_cvpClient.api.device_decommissioning.side_effect = mockMagic.device_decommissioning 35 | mock_cvpClient.api.device_decommissioning_status_get_one.side_effect = mockMagic.device_decommissioning_status_get_one 36 | mock_cvpClient.api.reset_device.side_effect = mockMagic.reset_device 37 | mock_cvpClient.api.delete_device.side_effect = mockMagic.delete_device 38 | return mock_cvpClient 39 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/build-md/doc.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | import os 5 | from pathlib import Path 6 | 7 | from ansible.parsing.plugin_docs import read_docstring 8 | from jinja2 import Environment, FileSystemLoader 9 | 10 | MODULE_NAME_STARTS_WITH = "cv_" 11 | MODULEDIR = "../../plugins/modules/" 12 | OUTPUTDIR = "../modules" 13 | 14 | 15 | def jinja2_writer(template_dir, name, data): 16 | env = Environment( 17 | loader=FileSystemLoader(template_dir), 18 | trim_blocks=True, 19 | keep_trailing_newline=True, 20 | ) 21 | 22 | template = env.get_template("md.j2") 23 | name = name.removesuffix(".py") 24 | filename = f"{name}.md" 25 | 26 | try: 27 | Path(f"../schema/{name}.md").read_text(encoding="UTF-8") 28 | schema = f"../schema/{name}.md" 29 | except FileNotFoundError: 30 | schema = None 31 | 32 | try: 33 | Path(f"../outputs/{name}.txt").read_text(encoding="UTF-8") 34 | module_output = f"docs/outputs/{name}.txt" 35 | except FileNotFoundError: 36 | module_output = None 37 | 38 | content = template.render( 39 | module=data, name=name, schema=schema, module_output=module_output 40 | ) 41 | with open(os.path.join(OUTPUTDIR, filename), mode="w", encoding="utf-8") as file: 42 | file.write(content) 43 | print(f"{filename} saved") 44 | 45 | return env, template 46 | 47 | 48 | def main(): 49 | for module in os.listdir(MODULEDIR): 50 | if module.startswith(MODULE_NAME_STARTS_WITH): 51 | file = os.path.join(MODULEDIR, module) 52 | if Path(file).is_file(): 53 | data = read_docstring(file) 54 | jinja2_writer("./templates", module, data) 55 | 56 | 57 | if __name__ == "__main__": 58 | main() 59 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/faq/errors.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Common Error messages 8 | 9 | > All Ansible error messages should be read from bottom to top. 10 | 11 | ## cv_facts error messages 12 | 13 | ### Unsupported CV version 14 | 15 | Prior version **2.0.0** `arista.cvp` collection ran test to determine CloudVision version. If version is not supported, following error should happen: 16 | 17 | ```shell 18 | \"/var/folders/q0/92fg6g7s1bv6kgfdcgfjwt0c0000gp/T/ansible_arista.cvp.cv_facts_payload_2ld7vf9v/ansible_arista.cvp.\ 19 | cv_facts_payload.zip/ansible_collections/arista/cvp/plugins/modules/cv_facts.py\", line 359, in facts_builder\n\ 20 | AttributeError: 'NoneType' object has no attribute 'get_cvp_info'\n", "module_stdout": "", "msg": "MODULE \ 21 | FAILURE\nSee stdout/stderr for the exact error", "rc": 1} 22 | ``` 23 | 24 | ## cv_container 25 | 26 | ### Missing Treelib requirement 27 | 28 | [Treelib](https://treelib.readthedocs.io/en/latest/) is a python library used to build container topology. When missing, Ansible raise following error message 29 | 30 | ```shell 31 | \"/tmp/ansible_arista.cvp.cv_container_payload_YG2p15/ansible_arista.cvp.cv_container_payload.zip/ansible_collections\ 32 | /arista/cvp/plugins/modules/cv_container.py\", line 254, in tree_build_from_dict\n\ 33 | NameError: global name 'Tree' is not defined\n", 34 | ``` 35 | 36 | With newer version of the collection, module should fails with an specific message like below: 37 | 38 | ```shell 39 | TASK [running cv_container in merge on cv_server] ***************************** 40 | Wednesday 07 October 2020 08:21:50 +0200 (0:00:20.050) 0:00:20.114 ***** 41 | Wednesday 07 October 2020 08:21:50 +0200 (0:00:20.050) 0:00:20.114 ***** 42 | fatal: [cv_server]: FAILED! => changed=false 43 | msg: treelib required for this module 44 | ``` 45 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/schema/cv_device_v3.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # Schema for cv_device_v3 8 | 9 | | Variable | Type | Required | Default | Choices | Description | 10 | | -------- | ---- | -------- | ------- | ------------------ | ----------- | 11 | | apply_mode | str | No | loose | loose
strict | Set how configlets are attached/detached on device. If set to strict all configlets not listed in your vars are detached | 12 | | inventory_mode | str | No | strict | loose
strict | Define how missing devices are handled. "loose" will ignore missing devices. "strict" will fail on any missing device | 13 | | search_key | str | No | hostname | fqdn
hostname
serialNumber | Key name to use to look for device in CloudVision | 14 | | state | str| No | present | present
factory_reset
provisioning_reset
absent | Set if Ansible should build, remove devices from provisioning, fully decommission or factory reset devices on CloudVision | 15 | | devices | List | Yes | | | List of devices with their container and configlets information | 16 | |     ipAddress | str | Yes | | | IP address of the device | 17 | |     fqdn | str | Yes | | | Fully Qualified Domain Name of the device.
This field is required along with `parentContainerName` | 18 | |     serialNumber | str | Yes | | | serial number of the device.
This field is required along with `parentContainerName` | 19 | |     systemMacAddress | str | No | | | MAC address of the device | 20 | |     parentContainerName | str | Yes | | | Name of the parent container.
This field is required along with either `serialNumber` or `fqdn` | 21 | |     configlets | List | No | | | List of configlets | 22 | |     imageBundle | str | No | | | Name of the image bundle applied to a container/device | 23 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_device/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: cv_configlet unit testing 3 | hosts: cv_server 4 | # connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | vars: 9 | CVP_DEVICES: 10 | DC1-SPINE1: 11 | name: 'DC1-SPINE1' 12 | parentContainerName: DC1_L3LEAFS 13 | configlets: 14 | - 'AVD_DC1-SPINE1' 15 | - '01TRAINING-01' 16 | imageBundle: [] # Not yet supported 17 | DC1-SPINE2: 18 | name: 'DC1-SPINE2' 19 | parentContainerName: DC1_SPINES 20 | configlets: 21 | - 'AVD_DC1-SPINE2' 22 | - '01TRAINING-01' 23 | - 'AVD_DC1-SPINE1' 24 | imageBundle: [] # Not yet supported 25 | DC1-LEAF2A: 26 | name: 'DC1-LEAF2A' 27 | parentContainerName: DC1_SPINES 28 | configlets: 29 | - 'AVD_DC1-SPINE2' 30 | - '01TRAINING-01' 31 | - 'AVD_DC1-SPINE1' 32 | imageBundle: [] # Not yet supported 33 | tasks: 34 | - name: "Include offline facts" 35 | include_vars: "{{ root_dir }}/inventory/cv_facts.json" 36 | 37 | - name: "Configure devices on {{inventory_hostname}}" 38 | arista.cvp.cv_device: 39 | devices: "{{ CVP_DEVICES }}" 40 | cvp_facts: '{{ ansible_facts }}' 41 | device_filter: ['DC1-SPINE1', 'DC1-SPINE2'] 42 | state: present 43 | register: CVP_DEVICES_RESULTS 44 | 45 | - name: "Print logs" 46 | debug: 47 | msg: "{{ CVP_DEVICES_RESULTS }}" 48 | 49 | - name: "Check move process" 50 | assert: 51 | that: 52 | - "CVP_DEVICES_RESULTS.data.moved[0]['DC1-SPINE1'] is defined" 53 | fail_msg: "Incorrect move process" 54 | success_msg: "Move process is running as expected" 55 | 56 | - name: "Check update process" 57 | assert: 58 | that: 59 | - "CVP_DEVICES_RESULTS.data.updated[0]['DC1-SPINE2'] is defined" 60 | fail_msg: "Incorrect update process" 61 | success_msg: "Update process is running as expected" 62 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/tools_inventory.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | # coding: utf-8 -*- 6 | # 7 | 8 | 9 | from __future__ import (absolute_import, division, print_function) 10 | __metaclass__ = type 11 | 12 | import logging 13 | import ansible_collections.arista.cvp.plugins.module_utils.logger # noqa # pylint: disable=unused-import 14 | 15 | MODULE_LOGGER = logging.getLogger('arista.cvp.tools_inventory') 16 | 17 | 18 | def find_hostname_by_mac(inventory, mac_address): 19 | """ 20 | Function to get device hostname based on System Mac Address. 21 | 22 | Parameters 23 | ---------- 24 | inventory : list 25 | Inventory list extracted from CVP. 26 | mac_address : string 27 | Mac address to search 28 | 29 | Returns 30 | ------- 31 | string 32 | Device hostname. Default None if not found 33 | """ 34 | for device in inventory: 35 | if 'systemMacAddress' in device: 36 | if device['systemMacAddress'] == mac_address: 37 | MODULE_LOGGER.debug('device data: %s', str(device)) 38 | if 'name' in device: 39 | return device['name'] 40 | elif 'hostname' in device: 41 | return device['hostname'] 42 | return None 43 | 44 | 45 | def find_containerName_by_containerId(containers_list, container_id): 46 | """ 47 | Function to get containername based on container ID. 48 | 49 | Parameters 50 | ---------- 51 | containers_list : list 52 | Containers list extracted from CVP. 53 | container_id : string 54 | ID of the container to search 55 | 56 | Returns 57 | ------- 58 | string 59 | Container name. Default None if not found 60 | """ 61 | for container in containers_list: 62 | if 'Key' in container: 63 | if container['Key'] == container_id: 64 | return container['Name'] 65 | return None 66 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/templates/template.compare_configlets.j2: -------------------------------------------------------------------------------- 1 | {# 2 | Copyright (c) 2023-2025 Arista Networks, Inc. 3 | Use of this source code is governed by the Apache License 2.0 4 | that can be found in the LICENSE file. 5 | #} 6 | --- 7 | {% for configlet in shared_server %} 8 | {% if shared_master[configlet] is defined %} 9 | {% if shared_master[configlet]['last_changed'] < shared_server[configlet]['last_changed'] %} 10 | {{configlet}}: 11 | source: "updated_by_{{inventory_hostname}}" 12 | config: "{{ shared_server[configlet]['config'] | replace('\n', '\\n')}}" 13 | last_changed: {{shared_server[configlet]['last_changed']}} 14 | devices: {{shared_server[configlet]['devices'] | replace("u'", "'")}} 15 | containers: {{shared_server[configlet]['containers'] | replace("u'", "'")}} 16 | {% else %} 17 | {{configlet}}: 18 | source: "updated_by_common_master" 19 | config: "{{ shared_master[configlet]['config'] | replace('\n', '\\n')}}" 20 | last_changed: {{shared_master[configlet]['last_changed']}} 21 | devices: {{shared_master[configlet]['devices'] | replace("u'", "'")}} 22 | containers: {{shared_master[configlet]['containers'] | replace("u'", "'")}} 23 | {% endif %} 24 | {% else %} 25 | {{configlet}}: 26 | source: "add_by_{{inventory_hostname}}" 27 | config: "{{ shared_server[configlet]['config'] | replace('\n', '\\n')}}" 28 | last_changed: {{shared_server[configlet]['last_changed']}} 29 | devices: {{shared_server[configlet]['devices'] | replace("u'", "'")}} 30 | containers: {{shared_server[configlet]['containers'] | replace("u'", "'")}} 31 | {% endif %} 32 | {% endfor %} 33 | {% for configlet in shared_master %} 34 | {% if shared_server[configlet] is not defined %} 35 | {{configlet}}: 36 | source: "added_by_common_master" 37 | config: "{{ shared_master[configlet]['config'] | replace('\n', '\\n')}}" 38 | last_changed: {{shared_master[configlet]['last_changed']}} 39 | devices: {{shared_master[configlet]['devices'] | replace("u'", "'")}} 40 | containers: {{shared_master[configlet]['containers'] | replace("u'", "'")}} 41 | {% endif %} 42 | {% endfor %} 43 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_create_unapprove.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test unapprove 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf1 Pair Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_Leaf1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: Leaf_MLAG_Health 16 | - action: "mlaghealthcheck" 17 | name: Check_Leaf1B_MLAG_Health 18 | arguments: 19 | - name: DeviceID 20 | value: SN-DC1-POD1-LEAF1B 21 | stage: Leaf_MLAG_Health 22 | - task_id: "50" 23 | stage: Leaf1A_upgrade 24 | - task_id: "51" 25 | stage: Leaf1B_upgrade 26 | stages: 27 | - name: Leaf_MLAG_Health 28 | mode: parallel 29 | - name: Leaf Upgrades 30 | modes: series 31 | - name: Leaf1A_upgrade 32 | parent: Leaf Upgrades 33 | - name: Leaf1B_upgrade 34 | parent: Leaf Upgrades 35 | 36 | tasks: 37 | - name: "Create a change control on {{inventory_hostname}}" 38 | arista.cvp.cv_change_control_v3: 39 | state: set 40 | change: "{{ change }}" 41 | register: cv_change_control_test 42 | 43 | - name: "Approve a change control on {{inventory_hostname}} using change_id field" 44 | arista.cvp.cv_change_control_v3: 45 | state: approve 46 | change_id: ["{{ cv_change_control_test.data.id }}"] 47 | 48 | - name: "Unapprove a change control on {{inventory_hostname}} using change_id field" 49 | arista.cvp.cv_change_control_v3: 50 | state: unapprove 51 | change_id: ["{{ cv_change_control_test.data.id }}"] 52 | 53 | - name: "Execute a change control on {{inventory_hostname}} using change_id field - this should fail" 54 | arista.cvp.cv_change_control_v3: 55 | state: execute 56 | change_id: ["{{ cv_change_control_test.data.id }}"] 57 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/modules/cv_task.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # cv_task 8 | 9 | Execute or Cancel CVP Tasks. 10 | 11 | Module added in version 1.0.0 12 | ## Synopsis 13 | 14 | CloudVision Portal Task module 15 | 16 | ## Module-specific Options 17 | 18 | The following options may be specified for this module: 19 | 20 | | parameter | type | required | default | choices | comments | 21 | | ------------- |-------------| ---------|----------- |--------- |--------- | 22 | | tasks | list | True | | | CVP taskIDs to act on. | 23 | | wait | int | False | 0 | | Time to wait for tasks to transition to 'Completed.' | 24 | | state | str | False | executed |
  • executed
  • cancelled
| Action to carry out on the task. | 25 | | options | dict | False | | | Implements the ability to create a sub-argument_spec, where the sub options of the top level argument are also validated using the attributes discussed in this section. | 26 | 27 | 28 | ## Examples 29 | 30 | ```yaml 31 | 32 | --- 33 | - name: Execute all tasks registered in cvp_configlets variable 34 | arista.cvp.cv_task: 35 | tasks: "{{ cvp_configlets.data.tasks }}" 36 | 37 | - name: Cancel a list of pending tasks 38 | arista.cvp.cv_task: 39 | tasks: "{{ cvp_configlets.data.tasks }}" 40 | state: cancelled 41 | 42 | # Execute all pending tasks and wait for completion for 60 seconds 43 | # In order to get a list of all pending tasks, execute cv_facts first 44 | - name: Update cvp facts 45 | arista.cvp.cv_facts: 46 | 47 | - name: Execute all pending tasks and wait for completion for 60 seconds 48 | arista.cvp.cv_task: 49 | port: '{{cvp_port}}' 50 | tasks: "{{ tasks }}" 51 | wait: 60 52 | 53 | ``` 54 | 55 | For a complete list of examples, check them out on our [GitHub repository](https://github.com/aristanetworks/ansible-cvp/tree/devel/ansible_collections/arista/cvp/examples). 56 | 57 | 58 | ## Author 59 | 60 | Ansible Arista Team (@aristanetworks) 61 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/examples/cv_change_control_v3/change_control_v3_create_show.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: CVP Change Control Test create followed by show 3 | hosts: cv_server 4 | gather_facts: false 5 | vars: 6 | change: 7 | name: Leaf1 Pair Change Control 8 | notes: Created via playbook 9 | activities: 10 | - action: "mlaghealthcheck" 11 | name: Check_Leaf1A_MLAG_Health 12 | arguments: 13 | - name: DeviceID 14 | value: SN-DC1-POD1-LEAF1A 15 | stage: Leaf_MLAG_Health 16 | - action: "mlaghealthcheck" 17 | name: Check_Leaf1B_MLAG_Health 18 | arguments: 19 | - name: DeviceID 20 | value: SN-DC1-POD1-LEAF1B 21 | stage: Leaf_MLAG_Health 22 | - task_id: "50" 23 | stage: Leaf1A_upgrade 24 | - task_id: "51" 25 | stage: Leaf1B_upgrade 26 | stages: 27 | - name: Leaf_MLAG_Health 28 | mode: parallel 29 | - name: Leaf Upgrades 30 | modes: series 31 | - name: Leaf1A_upgrade 32 | parent: Leaf Upgrades 33 | - name: Leaf1B_upgrade 34 | parent: Leaf Upgrades 35 | 36 | tasks: 37 | - name: "Create a change control on {{inventory_hostname}}" 38 | arista.cvp.cv_change_control_v3: 39 | state: set 40 | change: "{{ change }}" 41 | register: cv_change_control_test 42 | 43 | - name: "Show a change control on {{inventory_hostname}} using change_id field" 44 | arista.cvp.cv_change_control_v3: 45 | state: show 46 | change_id: ["{{ cv_change_control_test.data.id }}"] 47 | register: cv_show_cc_by_id 48 | 49 | - name: "Print out the change control}" 50 | debug: 51 | msg: "{{ cv_show_cc_by_id }}" 52 | 53 | - name: "Show a change control on {{inventory_hostname}} using CC name" 54 | arista.cvp.cv_change_control_v3: 55 | state: show 56 | name: "{{ change.name }}" 57 | register: cv_show_cc_by_name 58 | 59 | - name: "Print out the change control}" 60 | debug: 61 | msg: "{{ cv_show_cc_by_name }}" 62 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/roles/configlets_sync/tasks/pull.yml: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025 Arista Networks, Inc. 2 | # Use of this source code is governed by the Apache License 2.0 3 | # that can be found in the LICENSE file. 4 | --- 5 | # tasks file for configlets-sync 6 | - name: "Collect facts from {{ inventory_hostname }}" 7 | arista.cvp.cv_facts: 8 | register: CVP_FACTS 9 | 10 | - name: "Extract shared Configlets" 11 | ansible.builtin.template: 12 | src: "template.shared_configlets.j2" 13 | dest: "{{ common_configlets_dir }}/{{ inventory_hostname }}.yml" 14 | mode: 0644 15 | delegate_to: '{{ ansible_runner }}' 16 | run_once: false 17 | 18 | - name: "Check for shared_master" 19 | ansible.builtin.stat: 20 | path: '{{ common_configlets_dir }}/master.yml' 21 | register: status_shared_master 22 | delegate_to: '{{ ansible_runner }}' 23 | 24 | - name: "Create {{ common_configlets_dir }}/master.yml" 25 | ansible.builtin.file: 26 | path: '{{ common_configlets_dir }}/master.yml' 27 | state: touch 28 | mode: 0644 29 | when: status_shared_master.stat.exists is false 30 | delegate_to: '{{ ansible_runner }}' 31 | 32 | - name: "Add YAML designators to {{ common_configlets_dir }}/master.yml" 33 | ansible.builtin.copy: 34 | content: "---" 35 | dest: "{{ common_configlets_dir }}/master.yml" 36 | mode: 0644 37 | when: status_shared_master.stat.exists is false 38 | delegate_to: '{{ ansible_runner }}' 39 | 40 | - name: "Load Common CVP Servers Configlet Data" 41 | ansible.builtin.include_vars: 42 | file: '{{ common_configlets_dir }}/master.yml' 43 | name: 'shared_master' 44 | delegate_to: '{{ ansible_runner }}' 45 | 46 | - name: "Load CVP Instance Shared Configlet Data" 47 | ansible.builtin.include_vars: 48 | file: "{{ common_configlets_dir }}/{{ inventory_hostname }}.yml" 49 | name: 'shared_server' 50 | delegate_to: '{{ ansible_runner }}' 51 | 52 | - name: "Compare shared Configlets" 53 | ansible.builtin.template: 54 | src: "template.compare_configlets.j2" 55 | dest: "{{ common_configlets_dir }}/master.yml" 56 | mode: 0644 57 | delegate_to: '{{ ansible_runner }}' 58 | -------------------------------------------------------------------------------- /tests/PR_testing/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | # How to test PR 8 | 9 | ## Setup 10 | 11 | - Deploy an [ATD](http://testdrive.arista.com) instance with `Datacenter` topology and `ceos` image 12 | - Wait for your topology to deploy and drop into `Programmability IDE` 13 | - On the terminal, run the install script to setup the environment for testing: 14 | 15 | - ```shell 16 | cd persist 17 | ``` 18 | 19 | - ```shell 20 | wget https://raw.githubusercontent.com/aristanetworks/ansible-cvp/devel/tests/PR_testing/install.sh 21 | ``` 22 | 23 | - ```shell 24 | sh install.sh 25 | ``` 26 | 27 | - This script would place the ansible-cvp PR code base under `persist/arista-ansible` and example playbooks under `persist/PR_testing/examples` 28 | 29 | - Export the ATD lab password using: 30 | 31 | ```shell 32 | export LABPASSPHRASE=`cat /home/coder/.config/code-server/config.yaml| grep "password:" | awk '{print $2}'` 33 | ``` 34 | 35 | > NOTE: This has to be exported every time a new terminal session is created (including lab reboot). 36 | 37 | ## Run Example Playbooks 38 | 39 | - Change directory to `persist/PR_testing` 40 | 41 | ```shell 42 | cd persist/PR_testing 43 | ``` 44 | 45 | - Run the desired playbook: 46 | 47 | ```shell 48 | ansible-playbook cv_device_v3/device_validate_config_valid.yaml -i inventory.yml 49 | ``` 50 | 51 | > NOTE: If not using ATD update the `ansible_password` in `PR_testing/inventory.yml`. 52 | 53 | ## Run molecule tests 54 | 55 | - Run the molecule test: 56 | - Navigate to `/home/coder/project/persist/arista-ansible/ansible-cvp/ansible_collections/arista/cvp` and run `/home/coder/.local/bin/molecule converge -s ` 57 | - eg: 58 | 59 | ```shell 60 | /home/coder/.local/bin/molecule converge -s cv_device_v3 61 | ``` 62 | 63 | > NOTE: If not using ATD update the `ansible_password` in `ansible_collection/arista/cvp/examples/inventory.yml`. 64 | 65 | HAPPY TESTING! 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib64/ 18 | parts/ 19 | sdist/ 20 | var/ 21 | wheels/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | MANIFEST 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *.cover 46 | .hypothesis/ 47 | .pytest_cache/ 48 | report.html 49 | assets 50 | *log.txt* 51 | 52 | # Ansible builder 53 | *context/ 54 | *_build/ 55 | Containerfile 56 | Dockerfile 57 | 58 | # Translations 59 | *.mo 60 | *.pot 61 | 62 | # Django stuff: 63 | *.log 64 | local_settings.py 65 | db.sqlite3 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # pyenv 84 | .python-version 85 | 86 | # celery beat schedule file 87 | celerybeat-schedule 88 | 89 | # SageMath parsed files 90 | *.sage.py 91 | 92 | # Environments 93 | .env 94 | .venv 95 | env/ 96 | venv/ 97 | ENV/ 98 | env.bak/ 99 | venv.bak/ 100 | 101 | # Spyder project settings 102 | .spyderproject 103 | .spyproject 104 | 105 | # Rope project settings 106 | .ropeproject 107 | 108 | # mkdocs documentation 109 | /site 110 | 111 | # mypy 112 | .mypy_cache/ 113 | 114 | .vscode/* 115 | *.tar.gz 116 | */collections/* 117 | *.old 118 | *.DS_Store 119 | *.log 120 | *.log.* 121 | ansible_collections/arista/cvp/tests/output 122 | 123 | # ignore temporary dhcpd.conf.~ files 124 | **/dhcpd.conf.*~ 125 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/logger.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | # coding: utf-8 -*- 6 | # 7 | 8 | 9 | from __future__ import (absolute_import, division, print_function) 10 | __metaclass__ = type 11 | 12 | import logging 13 | import uuid 14 | import os 15 | from logging.handlers import RotatingFileHandler # noqa # pylint: disable=unused-import 16 | 17 | # Get Logging level from Environment variable / Default INFO 18 | 19 | # Define standard logging verbosity 20 | LEVELS = {'debug': logging.DEBUG, 21 | 'info': logging.INFO, 22 | 'warning': logging.WARNING, 23 | 'error': logging.ERROR, 24 | 'critical': logging.CRITICAL} 25 | 26 | # Set loglevel for arista.cvp modules 27 | LOGGING_LEVEL = os.getenv('ANSIBLE_CVP_LOG_LEVEL', 'error') 28 | LOGLEVEL = LEVELS.get(LOGGING_LEVEL, logging.NOTSET) 29 | 30 | # Set loglevel for urllib3 31 | LOGGING_LEVEL_URLLIB3 = os.getenv('ANSIBLE_CVP_LOG_APICALL', 'error') 32 | LOGLEVEL_URLLIB3 = LEVELS.get(LOGGING_LEVEL_URLLIB3, logging.ERROR) 33 | 34 | # Get filename to write logs / default /temp/arista.cvp.debug-.log 35 | DEFAULT_LOG_FILE = '/tmp/arista.cvp.debug.' + str(uuid.uuid4()) + '.log' 36 | LOGGING_FILENAME = os.getenv( 37 | 'ANSIBLE_CVP_LOG_FILE', DEFAULT_LOG_FILE) 38 | 39 | # set a format which is simpler for console use 40 | formatter = logging.Formatter( 41 | '%(asctime)s - %(name)-12s: %(levelname)-s - func: %(funcName)-12s (L:%(lineno)-3d) - %(message)s') 42 | 43 | # set up ROOT handler to use logging with file rotation. 44 | handler = logging.handlers.RotatingFileHandler( 45 | LOGGING_FILENAME, maxBytes=1000000, backupCount=5) 46 | handler.setFormatter(formatter) 47 | handler.setLevel(LOGLEVEL) 48 | # Unset default logging level for root handler 49 | logging.getLogger('').setLevel(logging.NOTSET) 50 | logging.getLogger('').addHandler(handler) 51 | 52 | # Configure URLLIB3 logging (default Warning to avoid too much verbosity) 53 | logging.getLogger("urllib3").setLevel(LOGLEVEL_URLLIB3) 54 | -------------------------------------------------------------------------------- /.github/.markdownlint.yaml: -------------------------------------------------------------------------------- 1 | # markdownlint configuration 2 | # the definitive list of rules for markdownlint can be found: 3 | # https://github.com/DavidAnson/markdownlint/blob/main/doc/Rules.md 4 | # 5 | # only deviations from the defaults are noted here or where there's an opinion 6 | # being expressed. 7 | 8 | # default state for all rules 9 | default: 10 | true 11 | 12 | # heading style 13 | MD003: 14 | style: "atx" 15 | 16 | # unordered list style 17 | MD004: 18 | style: "dash" 19 | 20 | # unorderd list indentation (2-spaces) 21 | # keep it tight yo! 22 | MD007: 23 | indent: 2 24 | 25 | # line length 26 | MD013: 27 | false 28 | # a lot of debate whether to wrap or not wrap 29 | 30 | # multiple headings with the same content 31 | # siblings_only is set here to allow for common header values in structured 32 | # documents 33 | MD024: 34 | siblings_only: true 35 | 36 | # MD029/ol-prefix - Ordered list item prefix 37 | MD029: 38 | # List style 39 | style: "ordered" 40 | 41 | # fenced code should be surrounded by blank lines default: true 42 | MD031: 43 | true 44 | 45 | # lists should be surrounded by blank lines default: true 46 | MD032: 47 | true 48 | 49 | # MD033/no-inline-html - Inline HTML 50 | MD033: 51 | false 52 | 53 | # bare URL - bare URLs should be wrapped in angle brackets 54 | # 55 | MD034: 56 | false 57 | 58 | # horizontal rule style default: consistent 59 | MD035: 60 | style: "---" 61 | 62 | # first line in a file to be a top-level heading 63 | # since we're using front-matter, this 64 | MD041: 65 | false 66 | 67 | 68 | # proper-names - proper names to have the correct capitalization 69 | # probably not entirely helpful in a technical writing environment. 70 | MD044: 71 | false 72 | 73 | # block style - disabled to allow for admonitions 74 | MD046: 75 | false 76 | 77 | # MD048/code-fence-style - Code fence style 78 | MD048: 79 | # Code fence style 80 | style: "backtick" 81 | 82 | # MD049/Emphasis style should be consistent 83 | MD049: 84 | # Emphasis style should be consistent 85 | style: "asterisk" 86 | 87 | # MD050/Strong style should be consistent 88 | MD050: 89 | # Strong style should be consistent 90 | style: "asterisk" 91 | -------------------------------------------------------------------------------- /tests/unit/test_configlet_input.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | # coding: utf-8 -*- 6 | # pylint: disable=logging-format-interpolation 7 | # pylint: disable=dangerous-default-value 8 | # flake8: noqa: W503 9 | # flake8: noqa: W1202 10 | 11 | from __future__ import (absolute_import, division, print_function) 12 | import sys 13 | sys.path.append("./") 14 | sys.path.append("../") 15 | sys.path.append("../../") 16 | from ansible_collections.arista.cvp.plugins.module_utils.configlet_tools import ConfigletInput 17 | from lib.parametrize import generate_flat_data 18 | from lib.helpers import to_nice_json, setup_custom_logger 19 | import pytest 20 | 21 | 22 | logger = setup_custom_logger(__name__) 23 | 24 | # TODO - use f-strings 25 | # pylint: disable=consider-using-f-string 26 | 27 | @pytest.mark.generic 28 | @pytest.mark.configlet 29 | # Parametrize to build a ConfigletInput from list of configlet in SYSTEM_CONFIGLETS_TESTS. Only those set with is_present_expected to False 30 | class Test_ConfigletInput(): 31 | 32 | @pytest.mark.parametrize("configlet_inventory", generate_flat_data(type='configlet', mode='valid')) 33 | def test_print_inventory_data(self, configlet_inventory): 34 | logger.debug('Inventory has {} configlets'.format(len(configlet_inventory))) 35 | logger.debug('Inventory is: {}'.format(to_nice_json(data=configlet_inventory))) 36 | 37 | @pytest.mark.parametrize("configlet_inventory", generate_flat_data(type='configlet', mode='valid')) 38 | def test_inventory_is_valid(self, configlet_inventory): 39 | configletInput = ConfigletInput(user_topology=configlet_inventory) 40 | logger.info('Test configlet is valid') 41 | assert configletInput.is_valid 42 | 43 | @pytest.mark.parametrize("configlet_inventory", generate_flat_data(type='configlet', mode='invalid')) 44 | def test_inventory_is_invalid(self, configlet_inventory): 45 | configletInput = ConfigletInput(user_topology=configlet_inventory) 46 | logger.info('Test configlet is invalid') 47 | assert not configletInput.is_valid 48 | -------------------------------------------------------------------------------- /tests/lib/cvaas_configlet.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | # coding: utf-8 -*- 6 | # pylint: disable=logging-format-interpolation 7 | # pylint: disable = duplicate-code 8 | # flake8: noqa: R0801 9 | # 10 | 11 | 12 | from .static_content import CONFIGLET_CONTENT 13 | 14 | """ 15 | cvaas_configlet - Test data for system/test_cv_configlet.py 16 | """ 17 | 18 | """ 19 | Configlet Test structure: 20 | - `name`: Name of the configlet to use for CV 21 | - `content`: Content of the configlet captured by ansible and pushed to CV (can deviate from CV) 22 | - `content_expected`: Content that should be on CV. Used for diff tests 23 | - `is_present_expected`: Set if configlet should be present on CV or not. Used for system tests. 24 | 25 | List of specific tests: 26 | - `is_present_expected` is True: 27 | - test_configlet_data_from_cv 28 | - test_update_configlet 29 | - `is_present_expected` is False: 30 | - test_create_configlet 31 | - test_delete_configlet 32 | """ 33 | 34 | SYSTEM_CONFIGLETS_TESTS = [ 35 | { 36 | 'name': 'system-configlet-tests01', 37 | 'config': CONFIGLET_CONTENT, 38 | 'config_expected': CONFIGLET_CONTENT, 39 | 'is_present_expected': True, 40 | 'is_valid_expected': True 41 | }, 42 | { 43 | 'name': 'system-configlet-tests02', 44 | 'config': 'alias sib show ip interfaces', 45 | 'config_expected': 'alias sib show ip interfaces brief', 46 | 'is_present_expected': True, 47 | 'is_valid_expected': True 48 | }, 49 | { 50 | 'name': 'system-configlet-tests03', 51 | 'config': 'alias sib2 show ip interfaces brief', 52 | 'config_expected': 'alias sib2 show ip interfaces brief', 53 | 'is_present_expected': False, 54 | 'is_valid_expected': True 55 | }, 56 | { 57 | 'name': 'system-configlet-tests04', 58 | 'config': CONFIGLET_CONTENT, 59 | 'config_expected': CONFIGLET_CONTENT, 60 | 'is_present_expected': False, 61 | 'is_valid_expected': True 62 | }, 63 | ] 64 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/README.md: -------------------------------------------------------------------------------- 1 | # CVP Unit test 2 | 3 | This section provides a list of Ansible CloudVision scenario executed during Continuous Integration to validate CVP integration. 4 | 5 | ## Ansible molecule 6 | 7 | Molecule provides support for testing with multiple instances, operating systems and distributions, virtualization providers, test frameworks and testing scenarios. Molecule encourages an approach that results in consistently developed roles that are well-written, easily understood and maintained. 8 | 9 | ## Scenario 10 | 11 | Current molecule implementation provides following scenario: 12 | 13 | - dhcp_configuration 14 | 15 | ## Manual execution 16 | 17 | To manually run molecule testing, follow commands: 18 | 19 | ```shell 20 | # Install development requirements 21 | $ pip install -r development/requirements-dev.txt 22 | 23 | # Move to CVP collection 24 | $ ansible-cvp/ansible_collections/arista/cvp 25 | 26 | # Run molecule for a given test 27 | $ molecule test -s 28 | 29 | # Run molecule for all test 30 | $ molecule test --all 31 | ``` 32 | 33 | ## Continuous Integration 34 | 35 | These scenario are all included in github actions and executed on `push` and `pull_request` when a file under `roles` and/or `molecule` is updated. 36 | 37 | ```yaml 38 | name: Ansible Molecule 39 | on: 40 | push: 41 | pull_request: 42 | paths: 43 | - 'ansible_collections/arista/cvp/roles/**' 44 | - 'ansible_collections/arista/cvp/molecules/**' 45 | - 'requirements.txt' 46 | jobs: 47 | molecule: 48 | runs-on: ubuntu-latest 49 | env: 50 | PY_COLORS: 1 # allows molecule colors to be passed to GitHub Actions 51 | ANSIBLE_FORCE_COLOR: 1 # allows ansible colors to be passed to GitHub Actions 52 | strategy: 53 | fail-fast: true 54 | matrix: 55 | avd_scenario: 56 | - dhcp_configuration 57 | steps: 58 | - name: Checkout repository 59 | uses: actions/checkout@v2 60 | 61 | - name: Run molecule action 62 | uses: inetsix/molecule-collection-actions@master 63 | with: 64 | molecule_parentdir: 'ansible_collections/arista/cvp' 65 | molecule_command: 'test' 66 | molecule_args: '-s ${{ matrix.avd_scenario }}' 67 | pip_file: 'requirements.txt' 68 | ``` 69 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: Report a bug impacting CV collection 3 | title: "Bug Report Title" 4 | labels: ['type: bug', triage] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | - type: textarea 11 | id: issue-summary 12 | attributes: 13 | label: Issue Summary 14 | description: Also tell us, what did you expect to happen? 15 | placeholder: Tell us what you see! 16 | value: "please detail your issue" 17 | validations: 18 | required: true 19 | 20 | - type: dropdown 21 | id: component_v1 22 | attributes: 23 | label: Which component(s) of AVD impacted 24 | multiple: false 25 | options: 26 | - cv_device_v3 27 | - cv_container_v3 28 | - cv_configlet_v3 29 | - cv_task_v3 30 | - cv_tag_v3 31 | - cv_facts_v3 32 | - cv_image_v3 33 | - cv_change_control_v3 34 | - other 35 | 36 | - type: dropdown 37 | id: runner 38 | attributes: 39 | label: How do you run AVD ? 40 | multiple: false 41 | options: 42 | - Ansible CLI (with virtual-env or native python) 43 | - Ansible CLI with AVD Runner 44 | - Ansible AWX 45 | 46 | - type: textarea 47 | id: user_var 48 | attributes: 49 | label: Input variables 50 | description: Please copy and paste any relevant YAML data to reproduce your issue. 51 | render: yaml 52 | 53 | - type: textarea 54 | id: step-to-reproduce 55 | attributes: 56 | label: Steps to reproduce 57 | description: Please copy and paste any relevant inputs to reproduce your issue. 58 | render: shell 59 | 60 | - type: textarea 61 | id: logs 62 | attributes: 63 | label: Relevant log output 64 | description: Please copy and paste any relevant log output from the debug logs. This will be automatically formatted into code. 65 | render: shell 66 | 67 | - type: checkboxes 68 | id: terms 69 | attributes: 70 | label: Code of Conduct 71 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://avd.arista.com/devel/docs/contribution/overview.html) 72 | options: 73 | - label: I agree to follow this project's Code of Conduct 74 | required: true 75 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Ask for new feature or feature enhancement 3 | title: "Feature request Title" 4 | labels: ['type: enhancement', triage] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this feature request! 10 | - type: textarea 11 | id: feature-summary 12 | attributes: 13 | label: Enhancement summary 14 | value: "please detail your feature enhancement" 15 | validations: 16 | required: true 17 | 18 | - type: dropdown 19 | id: component 20 | attributes: 21 | label: Which component of AVD is impacted 22 | multiple: false 23 | options: 24 | - cv_device_v3 25 | - cv_container_v3 26 | - cv_configlet_v3 27 | - cv_task_v3 28 | - cv_tag_v3 29 | - cv_facts_v3 30 | - cv_image_v3 31 | - cv_change_control_v3 32 | - other 33 | validations: 34 | required: true 35 | 36 | - type: textarea 37 | id: feature-use-case 38 | attributes: 39 | label: Use case example 40 | value: "please detail your use case" 41 | validations: 42 | required: true 43 | 44 | - type: textarea 45 | id: expected-solution 46 | attributes: 47 | label: Describe the solution you'd like 48 | description: A clear and concise description of what you want to happen. 49 | validations: 50 | required: true 51 | 52 | - type: textarea 53 | id: alternative-solution 54 | attributes: 55 | label: Describe alternatives you've considered 56 | description: A clear and concise description of any alternative solutions or features you've considered. 57 | validations: 58 | required: false 59 | 60 | - type: textarea 61 | id: additional-context 62 | attributes: 63 | label: Additional context 64 | description: Add any other context or screenshots about the feature request here. 65 | validations: 66 | required: false 67 | 68 | - type: checkboxes 69 | id: terms 70 | attributes: 71 | label: Code of Conduct 72 | description: By submitting this issue, you agree to follow our [Code of Conduct](https://avd.arista.com/devel/docs/contribution/overview.html) 73 | options: 74 | - label: I agree to follow this project's Code of Conduct 75 | required: true 76 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/docs/build-md/templates/md.j2: -------------------------------------------------------------------------------- 1 | {# 2 | Copyright (c) 2023-2025 Arista Networks, Inc. 3 | Use of this source code is governed by the Apache License 2.0 4 | that can be found in the LICENSE file. 5 | #} 6 | # {{ name }} 7 | 8 | {{ module.doc.short_description }} 9 | 10 | {% if module.doc.version_added -%} 11 | Module added in version {{ module.doc.version_added }} 12 | {%- endif %} 13 | 14 | ## Synopsis 15 | 16 | {% for desc in module.doc.description %} 17 | {{ desc }} 18 | {% endfor %} 19 | 20 | ## Module-specific Options 21 | 22 | The following options may be specified for this module: 23 | 24 | | parameter | type | required | default | choices | comments | 25 | | ------------- |-------------| ---------|----------- |--------- |--------- | 26 | {% for k,v in module.doc.items() %} 27 | {% if k == 'options' %} 28 | {% for option,values in v.items() %} 29 | | {{ option }} | {% if values.get('type') != None %} {{ values.get('type') }} {% endif %}|{% if values.get('required') != None %} {{ values['required'] | replace('true','yes') | replace('false','no') }} {% endif %} | {% if values.get('default') != None %} {{ values['default'] | replace('None','')}} {% endif %} |{% if values.get('choices') != None %}
    {% for each in values['choices'] %}
  • {{ each }}
  • {% endfor %}
{% endif %} | {% if values.get('description') is iterable and (values.get('description') is not string and values.get('description') is not mapping) %}
    {% for each in values['description'] %}
  • {{ each }}
  • {% endfor %}
{% else %}{{ values['description'] }}{% endif %} | 30 | {% endfor %} 31 | {% endif %} 32 | {% endfor %} 33 | 34 | {% if schema %} 35 | ## Inputs 36 | 37 | For a full view of the module inputs, please see the [schema documentation]({{ schema }}). 38 | {% endif %} 39 | 40 | ## Examples 41 | 42 | {% if module.examples%} 43 | ```yaml 44 | {{ module.examples }} 45 | ``` 46 | {% elif module.plainexamples %} 47 | ```yaml 48 | {{ module.plainexamples }} 49 | ``` 50 | {% endif%} 51 | 52 | For a complete list of examples, check them out on our [GitHub repository](https://github.com/aristanetworks/ansible-cvp/tree/devel/ansible_collections/arista/cvp/examples). 53 | 54 | {% if module_output %} 55 | ## Module output 56 | 57 | ??? output "Example output" 58 | ```yaml 59 | --8<-- 60 | {{ module_output }} 61 | --8<-- 62 | ``` 63 | {% endif %} 64 | 65 | ## Author 66 | 67 | {{ module.doc.author }} 68 | -------------------------------------------------------------------------------- /.github/workflows/pull-request-releases.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: "Pre-release validation" 3 | on: 4 | pull_request: 5 | branches: 6 | - releases/** 7 | 8 | concurrency: 9 | group: ${{ github.workflow }}-${{ github.head_ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | ansible_test: 14 | name: Run ansible-test validation 15 | runs-on: ubuntu-latest 16 | if: needs.cloudvision.status != 'failed' && needs.molecule_eos_designs.status != 'failed' && needs.file-changes.outputs.plugins == 'true' 17 | strategy: 18 | fail-fast: true 19 | matrix: 20 | python_version: [3.8] 21 | steps: 22 | - name: "set environment variables" 23 | run: | 24 | echo "PY_COLORS=1" >> $GITHUB_ENV 25 | echo "ANSIBLE_FORCE_COLOR=1" >> $GITHUB_ENV 26 | - uses: actions/checkout@v2 27 | - name: Set up Python 3 28 | uses: actions/setup-python@v2 29 | with: 30 | python-version: ${{ matrix.python_version }} 31 | - name: "Install Python requirements" 32 | run: make install-requirements 33 | - name: "ansible-test linting" 34 | run: | 35 | cd ansible_collections/arista/avd/ 36 | ansible-test sanity -v --requirements --docker --skip-test yamllint --exclude docs/ 37 | 38 | # galaxy_importer: 39 | # name: Test galaxy-importer 40 | # runs-on: ubuntu-latest 41 | # container: avdteam/base:3.8-v2.0 42 | # needs: [ molecule_eos_designs ] 43 | # if: needs.cloudvision.status != 'failed' && needs.molecule_eos_designs.status != 'failed' && needs.file-changes.outputs.plugins == 'true' 44 | # env: 45 | # PY_COLORS: 1 # allows molecule colors to be passed to GitHub Actions 46 | # ANSIBLE_FORCE_COLOR: 1 # allows ansible colors to be passed to GitHub Actions 47 | # steps: 48 | # - name: 'set environment variables' 49 | # run: | 50 | # echo "PY_COLORS=1" >> $GITHUB_ENV 51 | # echo "ANSIBLE_FORCE_COLOR=1" >> $GITHUB_ENV 52 | # - uses: actions/checkout@v2 53 | # - name: install requirements 54 | # run: make install-requirements 55 | # - name: 'build ansible package' 56 | # run: make collection-build 57 | # - name: 'run ansible-importer checks' 58 | # run: python -m galaxy_importer.main *.tar.gz 59 | # - uses: actions/upload-artifact@v2 60 | # with: 61 | # name: importer-logs 62 | # path: ./importer_result.json 63 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/molecule/cv_configlet_strict/converge.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: cv_configlet unit testing in strict filter mode 3 | hosts: localhost 4 | # connection: local 5 | gather_facts: false 6 | collections: 7 | - arista.cvp 8 | vars: 9 | CVP_CONFIGLETS: 10 | 01TRAINING-01: "alias a110 show version" 11 | 01TRAINING: "alias a120 show version" 12 | 01TRAINING-101: "alias a120 show version" 13 | 01TRAINING-02: "alias a102 show version" 14 | 01TRAINING-03: "alias a102 show version" 15 | 02DEMO-01: "alias a102 show version" 16 | tasks: 17 | - name: "Include offline facts" 18 | include_vars: "{{ root_dir }}/inventory/cv_facts.json" 19 | 20 | - name: "Configure configlet on {{ inventory_hostname }}" 21 | arista.cvp.cv_configlet: 22 | cvp_facts: "{{ansible_facts}}" 23 | configlets: "{{CVP_CONFIGLETS}}" 24 | configlet_filter: ["TRAINING", "01TRAINING-01", "01TRAINING-02"] 25 | state: present 26 | filter_mode: strict 27 | register: CVP_CONFIGLET_RESULT 28 | check_mode: yes 29 | 30 | - name: "Print logs" 31 | debug: 32 | msg: "{{ CVP_CONFIGLET_RESULT }}" 33 | 34 | - name: "Check deletion process" 35 | assert: 36 | that: 37 | - "CVP_CONFIGLET_RESULT.data.deleted[0] is not defined " 38 | fail_msg: "Incorrect deletion process" 39 | success_msg: "Deletion process is running as expected" 40 | 41 | - name: "Check creation process" 42 | assert: 43 | that: 44 | - "CVP_CONFIGLET_RESULT.data.new[0]['01TRAINING-02'] is defined" 45 | - "CVP_CONFIGLET_RESULT.data.new[0]['01TRAINING-101'] is not defined" 46 | fail_msg: "Incorrect creation process" 47 | success_msg: "Creation process is running as expected" 48 | 49 | - name: "Check update process" 50 | assert: 51 | that: 52 | - "CVP_CONFIGLET_RESULT.data.updated[0]['01TRAINING-01'] is defined" 53 | fail_msg: "Incorrect update process" 54 | success_msg: "Update process is running as expected" 55 | 56 | - name: "Check filter processing" 57 | assert: 58 | that: 59 | - "CVP_CONFIGLET_RESULT.data.updated[0]['01TRAINING'] is not defined" 60 | - "CVP_CONFIGLET_RESULT.data.updated[0]['01TRAINING-101'] is not defined" 61 | fail_msg: "Incorrect filter processing" 62 | success_msg: "Filter processing is running as expected" 63 | -------------------------------------------------------------------------------- /ansible_collections/arista/cvp/plugins/module_utils/resources/exceptions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2023-2025 Arista Networks, Inc. 3 | # Use of this source code is governed by the Apache License 2.0 4 | # that can be found in the LICENSE file. 5 | # coding: utf-8 -*- 6 | # 7 | 8 | 9 | from __future__ import (absolute_import, division, print_function) 10 | __metaclass__ = type 11 | 12 | from enum import Enum, auto 13 | from typing import Callable 14 | 15 | 16 | class CVPRessource(Enum): 17 | """Enumeration that defines possible ressource in CVP""" 18 | DEVICE = auto() 19 | CONTAINER = auto() 20 | CONFIGLET = auto() 21 | TASK = auto() 22 | 23 | def __str__(self): 24 | return str(self.name).lower() 25 | 26 | 27 | class AnsibleCVPError(Exception): 28 | """Base class for exceptions in ansible-cvp collection""" 29 | pass 30 | 31 | 32 | class AnsibleCVPApiError(Exception): 33 | """Exception raised when an API-related error occurs""" 34 | def __init__(self, cvprac_method: Callable, message: str) -> None: 35 | """ 36 | Constructor for the AnsibleCVPApiError class 37 | 38 | Parameters 39 | ---------- 40 | cvprac_method : Callable 41 | The cvprac module method called 42 | message : str 43 | Description of the context in which the error occured 44 | """ 45 | self.cvprac_method = cvprac_method 46 | self.message = message 47 | super().__init__(self.message) 48 | 49 | def __str__(self): 50 | return f'{self.cvprac_method.__name__} -> {self.message}' 51 | 52 | 53 | class AnsibleCVPNotFoundError(Exception): 54 | """Raised when the ressource is not found in CloudVision instance.""" 55 | def __init__(self, name: str, type: CVPRessource, message: str = None) -> None: 56 | """ 57 | Constructor for the AnsibleCVPNotFoundError class 58 | 59 | Parameters 60 | ---------- 61 | name : str 62 | The ressource name 63 | type : CVPRessource 64 | The ressource type 65 | message : str, optional 66 | Description of the context in which the error occured 67 | """ 68 | self.name = name 69 | self.type = type 70 | self.message = message 71 | super().__init__(self.message) 72 | 73 | def __str__(self): 74 | if self.message: 75 | return f'{self.type} {self.name} -> {self.message}' 76 | else: 77 | return f'{self.type} {self.name}' 78 | --------------------------------------------------------------------------------