├── .gitignore ├── .virlrc ├── LICENSE ├── README.md ├── Vagrantfile ├── config_mgmt └── ansible │ └── demo_setup ├── csv_config_gen ├── .gitignore ├── config_render_01.py ├── config_render_02.py ├── switch-ports.csv └── switchport-interface-template.j2 ├── data_manipulation ├── csv │ ├── add_router.py │ ├── csv_example.csv │ └── csv_example.py ├── json │ ├── json_example.json │ └── json_example.py ├── xml │ ├── xml_example.py │ └── xml_example.xml ├── yaml │ ├── yaml_example.py │ └── yaml_example.yaml └── yang │ ├── cisco-xe-ietf-ip-deviation.yang │ ├── ietf-inet-types.yang │ ├── ietf-interfaces.yang │ ├── ietf-ip.yang │ ├── ietf-ipv4-unicast-routing.yang │ ├── ietf-routing.yang │ └── pyang-examples.sh ├── device_apis ├── cli │ ├── netmiko_example1.py │ ├── netmiko_example1a.py │ ├── netmiko_example2.py │ └── netmiko_example3.py ├── device_info.py ├── netconf │ ├── netconf-ssh-example.md │ ├── netconf_example1.py │ ├── netconf_example1a.py │ ├── netconf_example2.py │ └── netconf_example3.py ├── rest │ ├── restconf_example1.py │ ├── restconf_example1a.py │ ├── restconf_example2.py │ └── restconf_example3.py └── snmp │ └── pysnmp_example1.py ├── lab.md ├── network_testing └── pyats │ ├── default_testbed.yaml │ └── pyats-example1.py ├── python_code_tips ├── README.md ├── command_line_tool_example │ └── argparse_example.py ├── exception_handling │ ├── with_try.py │ └── without_try.py ├── functions_example │ ├── functions-after.py │ └── functions-before.py ├── misc │ └── example1.py ├── modules_example │ ├── dnac_functions.py │ ├── dnac_resources.py │ ├── modules_after.py │ └── modules_before.py ├── objects_example │ ├── dnac │ │ ├── DNAC.py │ │ └── __init__.py │ └── host_troubleshooting.py └── packages_example │ ├── dnac │ ├── __init__.py │ ├── dnac_functions.py │ └── dnac_resources.py │ └── host_troubleshooting.py ├── requirements-win.txt ├── requirements.txt ├── setup ├── ansible.cfg ├── configs │ └── README.md ├── group_vars │ ├── access.yaml │ ├── all.yaml │ ├── core.yaml │ └── distribution.yaml ├── host_vars │ ├── access1.yaml │ ├── core1.yaml │ ├── core2.yaml │ ├── dist1.yaml │ └── dist2.yaml ├── network_deploy.yaml └── roles │ ├── network_enable_api │ ├── README.md │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── tasks │ │ ├── ios.yml │ │ ├── main.yml │ │ └── nxos.yml │ ├── tests │ │ ├── inventory │ │ └── test.yml │ └── vars │ │ └── main.yml │ ├── network_inspect │ ├── README.md │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── tasks │ │ ├── ios.yml │ │ ├── main.yml │ │ └── nxos.yml │ ├── tests │ │ ├── inventory │ │ └── test.yml │ └── vars │ │ └── main.yml │ ├── network_interface │ ├── README.md │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── tasks │ │ ├── hsrp_nxapi.yml │ │ ├── l3_netconf.yml │ │ ├── l3_nxapi.yml │ │ ├── main.yml │ │ └── portchannel_nxapi.yml │ ├── templates │ │ └── ietf_interface_template.j2 │ ├── tests │ │ ├── inventory │ │ └── test.yml │ └── vars │ │ └── main.yml │ ├── network_ospf │ ├── README.md │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── tasks │ │ ├── main.yml │ │ ├── netconf.yml │ │ └── nxapi.yml │ ├── templates │ │ ├── Cisco-IOS-XE_ospf.j2 │ │ └── ned_ospf.j2 │ ├── tests │ │ ├── inventory │ │ └── test.yml │ └── vars │ │ └── main.yml │ ├── network_vlan │ ├── README.md │ ├── defaults │ │ └── main.yml │ ├── handlers │ │ └── main.yml │ ├── meta │ │ └── main.yml │ ├── tasks │ │ ├── main.yml │ │ └── nxapi.yml │ ├── tests │ │ ├── inventory │ │ └── test.yml │ └── vars │ │ └── main.yml │ └── network_vpc │ ├── README.md │ ├── defaults │ └── main.yml │ ├── handlers │ └── main.yml │ ├── meta │ └── main.yml │ ├── tasks │ ├── main.yml │ └── nxapi.yml │ ├── tests │ ├── inventory │ └── test.yml │ └── vars │ └── main.yml ├── topology.virl ├── vagrant_device_config.xml └── vagrant_device_setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | .virl/ 2 | scratch* 3 | *.retry 4 | default_inventory.yaml 5 | .vagrant/ 6 | setup/configs/*.xml 7 | 8 | 9 | 10 | # Byte-compiled / optimized / DLL files 11 | __pycache__/ 12 | *.py[cod] 13 | *$py.class 14 | 15 | # C extensions 16 | *.so 17 | 18 | # Distribution / packaging 19 | .Python 20 | build/ 21 | develop-eggs/ 22 | dist/ 23 | downloads/ 24 | eggs/ 25 | .eggs/ 26 | lib/ 27 | lib64/ 28 | parts/ 29 | sdist/ 30 | var/ 31 | wheels/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | MANIFEST 36 | 37 | # PyInstaller 38 | # Usually these files are written by a python script from a template 39 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 40 | *.manifest 41 | *.spec 42 | 43 | # Installer logs 44 | pip-log.txt 45 | pip-delete-this-directory.txt 46 | 47 | # Unit test / coverage reports 48 | htmlcov/ 49 | .tox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *.cover 56 | .hypothesis/ 57 | .pytest_cache/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | 68 | # Flask stuff: 69 | instance/ 70 | .webassets-cache 71 | 72 | # Scrapy stuff: 73 | .scrapy 74 | 75 | # Sphinx documentation 76 | docs/_build/ 77 | 78 | # PyBuilder 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # celery beat schedule file 88 | celerybeat-schedule 89 | 90 | # SageMath parsed files 91 | *.sage.py 92 | 93 | # Environments 94 | .env 95 | .venv 96 | env/ 97 | venv/ 98 | ENV/ 99 | env.bak/ 100 | venv.bak/ 101 | 102 | # Spyder project settings 103 | .spyderproject 104 | .spyproject 105 | 106 | # Rope project settings 107 | .ropeproject 108 | 109 | # mkdocs documentation 110 | /site 111 | 112 | # mypy 113 | .mypy_cache/ 114 | -------------------------------------------------------------------------------- /.virlrc: -------------------------------------------------------------------------------- 1 | VIRL_HOST=10.10.20.160 2 | VIRL_USERNAME=guest 3 | VIRL_PASSWORD=guest 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Hank Preston 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Useful Python Libraries for Network Engineers 2 | Collection of scripts and examples of Python code, libraries, and utilities for working with Network Devices. 3 | 4 | > The code in this repository is used in a demonstrations, talks, and labs as part of [Cisco DevNet](https://developer.cisco.com) and specifically [NetDevOps Live!](https://developer.cisco.com/netdevops/live). 5 | 6 | # Hands On Lab 7 | Included in this repo is a short hands on lab guide that walks through many of the libraries discussed and used through the examples. You can [view the lab guide here](lab.md). 8 | 9 | # Setting Up to Run Examples 10 | ## Clone and Prep the Environment 11 | 1. Clone the code repo 12 | 13 | ```bash 14 | git clone https://github.com/hpreston/python_networking 15 | cd python_networking 16 | ``` 17 | 18 | 1. Setup Python Virtual Environment. 19 | 20 | ```bash 21 | # MacOS or Linux 22 | python3.6 -m venv venv 23 | source venv/bin/activate 24 | pip install -r requirements.txt 25 | ``` 26 | 27 | * *Note: If on Linux, you will need to install the Python3.6 development files. On CentOS this is done with `yum install -y python36u-devel`* 28 | 29 | ```bash 30 | # Windows - recommendation to use git-bash terminal 31 | py -3 -m venv venv 32 | source venv/Scripts/activate 33 | pip install -r requirements-win.txt 34 | ``` 35 | 36 | * *Note: Creation and activation of a venv in Windows is slightly different. Also Ansible and pyATS aren't currently supported on Windows so the `requirements-win.txt` doesn't contain those libraries.* 37 | 38 | 39 | ## Infrastructure Resources 40 | The example scripts for `data_manipulation` require nothing other than the files in this repository and the Python libraries installed with `pip install` above. 41 | 42 | The example scripts for `device_apis` & `network_testing` leverage DevNet Always On Sandboxes that are publicly available, with no VPN connection needed. The details for these infrastructure are included in the scripts. 43 | 44 | > There is also a `Vagrantfile` included in the repo that can be used to spin up a local IOS XE device to use for the API examples. You'll need to have Vagrant and a box already available. You can find details on obtaining and using Vagrant boxes for Cisco devices at [github.com/hpreston/vagrant_net_prog](https://github.com/hpreston/vagrant_net_prog). If you do do this, the following line would need to be changed in the code examples. 45 | > 46 | > `from device_info import ios_xe1 as device` -> `from device_info import vagrant_iosxe as device` 47 | 48 | ## Infrastructure for Configuration Management Demonstrations 49 | The configuration management scripts in this repository are written to target a sample network topology built as Core > Dist > Access with IOS XE devices in the Core, and NX-OS devices for Dist and Access. The demo network can be run with Cisco VIRL or CML, and the [`topology.virl`](topology.virl) file in the repo has the details. If you do not have your own VIRL server, you can reserve a free [DevNet Multi-IOS VIRL Sandbox](https://devnetsandbox.cisco.com/RM/Diagram/Index/6b023525-4e7f-4755-81ae-05ac500d464a?diagramType=Topology) to use. 50 | 51 | 1. After connecting to the Sandbox with VPN, start the development network. This single line command will start the simulation, wait to completely start, and then lay down an initial configuration with Ansible. 52 | 53 | ```bash 54 | virl up --provision \ 55 | && virl generate ansible -o setup/default_inventory.yaml \ 56 | && cd setup \ 57 | && ansible-playbook network_deploy.yaml \ 58 | && cd ../ 59 | ``` 60 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # -*- mode: ruby -*- 2 | # vi: set ft=ruby : 3 | 4 | Vagrant.configure("2") do |config| 5 | 6 | # Create IOS XE Node 7 | config.vm.define "iosxe1" do |node| 8 | node.vm.box = "iosxe/16.09.01" 9 | 10 | # nic_type: "virtio" needed for IOS XE 16.7+ 11 | node.vm.network :private_network, virtualbox__intnet: "link1", auto_config: false, nic_type: "virtio" 12 | node.vm.network :private_network, virtualbox__intnet: "link2", auto_config: false, nic_type: "virtio" 13 | 14 | node.vm.network :forwarded_port, protocol: 'udp', guest: 161, host: 2227, id: 'snmp', auto_correct: true 15 | 16 | # Vagrant 2.1.0 or higher 17 | # node.trigger.after :up do |trigger| 18 | # trigger.info = "Running baseline script" 19 | # trigger.run = {path: "vagrant_device_setup.py"} 20 | # end 21 | 22 | end 23 | 24 | end 25 | -------------------------------------------------------------------------------- /config_mgmt/ansible/demo_setup: -------------------------------------------------------------------------------- 1 | ../../setup/ -------------------------------------------------------------------------------- /csv_config_gen/.gitignore: -------------------------------------------------------------------------------- 1 | interface_configs.txt 2 | -------------------------------------------------------------------------------- /csv_config_gen/config_render_01.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Demo script showing how to create network configurations by combining data from CSV files with Jinja templates. 4 | """ 5 | 6 | import csv 7 | from jinja2 import Template 8 | 9 | source_file = "switch-ports.csv" 10 | interface_template_file = "switchport-interface-template.j2" 11 | 12 | # String that will hold final full configuration of all interfaces 13 | interface_configs = "" 14 | 15 | # Open up the Jinja template file (as text) and then create a Jinja Template Object 16 | with open(interface_template_file) as f: 17 | interface_template = Template(f.read(), keep_trailing_newline=True) 18 | 19 | # Open up the CSV file containing the data 20 | with open(source_file) as f: 21 | # Use DictReader to access data from CSV 22 | reader = csv.DictReader(f) 23 | # For each row in the CSV, generate an interface configuration using the jinja template 24 | for row in reader: 25 | interface_config = interface_template.render( 26 | interface = row["Interface"], 27 | vlan = row["VLAN"], 28 | server = row["Server"], 29 | link = row["Link"], 30 | purpose = row["Purpose"] 31 | ) 32 | 33 | # Append this interface configuration to the full configuration 34 | interface_configs += interface_config 35 | 36 | # Save the final configuraiton to a file 37 | with open("interface_configs.txt", "w") as f: 38 | f.write(interface_configs) 39 | -------------------------------------------------------------------------------- /csv_config_gen/config_render_02.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Demo script showing how to create network configurations by combining data from CSV files with Jinja templates. 4 | """ 5 | 6 | import csv 7 | from jinja2 import Template 8 | from netmiko import ConnectHandler 9 | 10 | source_file = "switch-ports.csv" 11 | interface_template_file = "switchport-interface-template.j2" 12 | 13 | # DevNet Sandbox Nexus 9000 switch to send configuration to 14 | device = { 15 | "address": "sbx-nxos-mgmt.cisco.com", 16 | "device_type": "cisco_nxos", 17 | "ssh_port": 8181, 18 | "username": "admin", 19 | "password": "Admin_1234!" 20 | } 21 | 22 | 23 | # String that will hold final full configuration of all interfaces 24 | interface_configs = "" 25 | 26 | # Open up the Jinja template file (as text) and then create a Jinja Template Object 27 | with open(interface_template_file) as f: 28 | interface_template = Template(f.read(), keep_trailing_newline=True) 29 | 30 | # Open up the CSV file containing the data 31 | with open(source_file) as f: 32 | # Use DictReader to access data from CSV 33 | reader = csv.DictReader(f) 34 | # For each row in the CSV, generate an interface configuration using the jinja template 35 | for row in reader: 36 | interface_config = interface_template.render( 37 | interface = row["Interface"], 38 | vlan = row["VLAN"], 39 | server = row["Server"], 40 | link = row["Link"], 41 | purpose = row["Purpose"] 42 | ) 43 | 44 | # Append this interface configuration to the full configuration 45 | interface_configs += interface_config 46 | 47 | # Save the final configuraiton to a file 48 | with open("interface_configs.txt", "w") as f: 49 | f.write(interface_configs) 50 | 51 | # Use Netmiko to connect to the device and send the configuration 52 | with ConnectHandler(ip = device["address"], 53 | port = device["ssh_port"], 54 | username = device["username"], 55 | password = device["password"], 56 | device_type = device["device_type"]) as ch: 57 | 58 | config_set = interface_configs.split("\n") 59 | output = ch.send_config_set(config_set) 60 | print(output) 61 | -------------------------------------------------------------------------------- /csv_config_gen/switch-ports.csv: -------------------------------------------------------------------------------- 1 | Switch,Interface,Server,Link,Purpose,VLAN 2 | sbx-n9kv-ao,Ethernet1/13,esxi-01,nic 0,Virtualization Host,trunk 3 | sbx-n9kv-ao,Ethernet1/14,esxi-01,nic 1,Virtualization Host,trunk 4 | sbx-n9kv-ao,Ethernet1/15,esxi-01,nic 2,Virtualization Host,trunk 5 | sbx-n9kv-ao,Ethernet1/16,esxi-01,nic 3,Virtualization Host,trunk 6 | sbx-n9kv-ao,Ethernet1/17,esxi-02,nic 0,Virtualization Host,trunk 7 | sbx-n9kv-ao,Ethernet1/18,esxi-02,nic 1,Virtualization Host,trunk 8 | sbx-n9kv-ao,Ethernet1/19,esxi-02,nic 2,Virtualization Host,trunk 9 | sbx-n9kv-ao,Ethernet1/20,esxi-02,nic 3,Virtualization Host,trunk 10 | sbx-n9kv-ao,Ethernet1/21,esxi-03,nic 0,Virtualization Host,trunk 11 | sbx-n9kv-ao,Ethernet1/22,esxi-03,nic 1,Virtualization Host,trunk 12 | sbx-n9kv-ao,Ethernet1/23,esxi-03,nic 2,Virtualization Host,trunk 13 | sbx-n9kv-ao,Ethernet1/24,esxi-03,nic 3,Virtualization Host,trunk 14 | sbx-n9kv-ao,Ethernet1/25,esxi-04,nic 0,Virtualization Host,trunk 15 | sbx-n9kv-ao,Ethernet1/26,esxi-04,nic 1,Virtualization Host,trunk 16 | sbx-n9kv-ao,Ethernet1/27,esxi-04,nic 2,Virtualization Host,trunk 17 | sbx-n9kv-ao,Ethernet1/28,esxi-04,nic 3,Virtualization Host,trunk 18 | sbx-n9kv-ao,Ethernet1/29,esxi-05,nic 0,Virtualization Host,trunk 19 | sbx-n9kv-ao,Ethernet1/30,esxi-05,nic 1,Virtualization Host,trunk 20 | sbx-n9kv-ao,Ethernet1/31,esxi-05,nic 2,Virtualization Host,trunk 21 | sbx-n9kv-ao,Ethernet1/32,esxi-05,nic 3,Virtualization Host,trunk 22 | sbx-n9kv-ao,Ethernet1/33,esxi-06,nic 0,Virtualization Host,trunk 23 | sbx-n9kv-ao,Ethernet1/34,esxi-06,nic 1,Virtualization Host,trunk 24 | sbx-n9kv-ao,Ethernet1/35,esxi-06,nic 2,Virtualization Host,trunk 25 | sbx-n9kv-ao,Ethernet1/36,esxi-06,nic 3,Virtualization Host,trunk 26 | sbx-n9kv-ao,Ethernet1/37,esxi-07,nic 0,Virtualization Host,trunk 27 | sbx-n9kv-ao,Ethernet1/38,esxi-07,nic 1,Virtualization Host,trunk 28 | sbx-n9kv-ao,Ethernet1/39,esxi-07,nic 2,Virtualization Host,trunk 29 | sbx-n9kv-ao,Ethernet1/40,esxi-07,nic 3,Virtualization Host,trunk 30 | sbx-n9kv-ao,Ethernet1/41,esxi-08,nic 0,Virtualization Host,trunk 31 | sbx-n9kv-ao,Ethernet1/42,esxi-08,nic 1,Virtualization Host,trunk 32 | sbx-n9kv-ao,Ethernet1/43,esxi-08,nic 2,Virtualization Host,trunk 33 | sbx-n9kv-ao,Ethernet1/44,esxi-08,nic 3,Virtualization Host,trunk 34 | sbx-n9kv-ao,Ethernet1/45,db-01,nic 0,Database Server,101 35 | sbx-n9kv-ao,Ethernet1/46,db-01,nic 1,Database Server,101 36 | sbx-n9kv-ao,Ethernet1/47,db-02,nic 0,Database Server,101 37 | sbx-n9kv-ao,Ethernet1/48,db-02,nic 1,Database Server,101 38 | sbx-n9kv-ao,Ethernet1/49,db-03,nic 0,Database Server,101 39 | sbx-n9kv-ao,Ethernet1/50,db-03,nic 1,Database Server,101 40 | sbx-n9kv-ao,Ethernet1/51,dev-db-01,nic 0,Database Server,102 41 | sbx-n9kv-ao,Ethernet1/52,dev-db-01,nic 1,Database Server,102 42 | sbx-n9kv-ao,Ethernet1/53,dev-db-02,nic 0,Database Server,102 43 | sbx-n9kv-ao,Ethernet1/54,dev-db-02,nic 1,Database Server,102 44 | sbx-n9kv-ao,Ethernet1/55,dev-db-03,nic 0,Database Server,102 45 | sbx-n9kv-ao,Ethernet1/56,dev-db-03,nic 1,Database Server,102 46 | sbx-n9kv-ao,Ethernet1/57,admin-01,nic 0,Admin Server,100 47 | sbx-n9kv-ao,Ethernet1/58,admin-01,nic 2,Admin Server,100 48 | sbx-n9kv-ao,Ethernet1/59,server-01,nic 0,App Server,103 49 | sbx-n9kv-ao,Ethernet1/60,server-01,nic 1,App Server,103 50 | sbx-n9kv-ao,Ethernet1/61,server-02,nic 0,App Server,104 51 | sbx-n9kv-ao,Ethernet1/62,server-02,nic 1,App Server,104 52 | sbx-n9kv-ao,Ethernet1/63,server-03,nic 0,App Server,105 53 | sbx-n9kv-ao,Ethernet1/64,server-03,nic 1,App Server,105 -------------------------------------------------------------------------------- /csv_config_gen/switchport-interface-template.j2: -------------------------------------------------------------------------------- 1 | ! Generated Configuration 2 | interface {{ interface }} 3 | description Link to {{ server }} port {{ link }} for {{ purpose }} 4 | switchport 5 | {% if vlan == "trunk" -%} 6 | switchport mode trunk 7 | {% else -%} 8 | switchport mode access 9 | switchport access vlan {{ vlan }} 10 | spanning-tree port type edge 11 | {% endif -%} 12 | no shutdown 13 | 14 | 15 | -------------------------------------------------------------------------------- /data_manipulation/csv/add_router.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """This script illustrates how to use the Python csv library to add data to a file. 3 | 4 | Copyright (c) 2018 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | import csv 26 | 27 | # Collect information from the user and save to variables 28 | print("Let's add a new router.") 29 | hostname = input("What is the hostname? ") 30 | ip = input("What is the ip address? ") 31 | location = input("What is the location? ") 32 | 33 | # Create new list representing device 34 | device = [hostname, ip, location] 35 | 36 | # Open the csv file in "append" mode to add new device 37 | with open("csv_example.csv", "a") as f: 38 | # Create a csv.writer object from the file 39 | csv_writer = csv.writer(f) 40 | # Add new row based on new device 41 | csv_writer.writerow(device) 42 | -------------------------------------------------------------------------------- /data_manipulation/csv/csv_example.csv: -------------------------------------------------------------------------------- 1 | "router1","10.1.0.1","New York" 2 | "router2","10.2.0.1","Denver" 3 | "router3","10.3.0.1","Austin" 4 | -------------------------------------------------------------------------------- /data_manipulation/csv/csv_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """This script illustrates how to manipulate csv data easily in Python with 3 | the csv library. 4 | 5 | Copyright (c) 2018 Cisco and/or its affiliates. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | # Import the csv library 27 | import csv 28 | 29 | # Open the sample csv file and print it to screen 30 | with open("csv_example.csv") as f: 31 | print(f.read()) 32 | 33 | # Open the sample csv file, and create a csv.reader object 34 | with open("csv_example.csv") as f: 35 | csv_python = csv.reader(f) 36 | # Loop over each row in csv and leverage the data in code 37 | for row in csv_python: 38 | print("{device} is in {location} " \ 39 | "and has IP {ip}.".format( 40 | device = row[0], 41 | location = row[2], 42 | ip = row[1] 43 | ) 44 | ) 45 | -------------------------------------------------------------------------------- /data_manipulation/json/json_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "interface": { 3 | "name": "GigabitEthernet2", 4 | "description": "Wide Area Network", 5 | "enabled": true, 6 | "ipv4": { 7 | "address": [ 8 | { 9 | "ip": "172.16.0.2", 10 | "netmask": "255.255.255.0" 11 | } 12 | ] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /data_manipulation/json/json_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """This script illustrates how to manipulate JSON data easily in Python with 3 | the json library. 4 | 5 | Copyright (c) 2018 Cisco and/or its affiliates. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | # Import the jsontodict library 27 | import json 28 | 29 | # Open the sample json file and read it into variable 30 | with open("json_example.json") as f: 31 | json_example = f.read() 32 | 33 | # Print the raw json data 34 | print(json_example) 35 | 36 | # Parse the json into a Python dictionary 37 | json_dict = json.loads(json_example) 38 | 39 | # Save the interface name into a variable 40 | int_name = json_dict["interface"]["name"] 41 | 42 | # Print the interface name 43 | print(int_name) 44 | 45 | # Change the IP address of the interface 46 | json_dict["interface"]["ipv4"]["address"][0]["ip"] = "192.168.0.2" 47 | 48 | # Revert to the json string version of the dictionary 49 | print(json.dumps(json_dict)) 50 | -------------------------------------------------------------------------------- /data_manipulation/xml/xml_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """This script illustrates how to manipulate XML data easily in Python with 3 | the xmltodict library. 4 | 5 | Prep with: 6 | pip install xmltodict 7 | 8 | Copyright (c) 2018 Cisco and/or its affiliates. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | """ 28 | 29 | # Import the xmltodict library 30 | import xmltodict 31 | 32 | # Open the sample xml file and read it into variable 33 | with open("xml_example.xml") as f: 34 | xml_example = f.read() 35 | 36 | # Print the raw XML data 37 | print(xml_example) 38 | 39 | # Parse the XML into a Python dictionary 40 | xml_dict = xmltodict.parse(xml_example) 41 | 42 | # Save the interface name into a variable using XML nodes as keys 43 | int_name = xml_dict["interface"]["name"] 44 | 45 | # Print the interface name 46 | print(int_name) 47 | 48 | # Change the IP address of the interface 49 | xml_dict["interface"]["ipv4"]["address"]["ip"] = "192.168.0.2" 50 | 51 | # Revert to the XML string version of the dictionary 52 | print(xmltodict.unparse(xml_dict)) 53 | -------------------------------------------------------------------------------- /data_manipulation/xml/xml_example.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | GigabitEthernet2 4 | Wide Area Network 5 | true 6 | 7 |
8 | 172.16.0.2 9 | 255.255.255.0 10 |
11 |
12 |
13 | -------------------------------------------------------------------------------- /data_manipulation/yaml/yaml_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """This script illustrates how to manipulate yaml data easily in Python with 3 | the PyYAML library. 4 | 5 | Copyright (c) 2018 Cisco and/or its affiliates. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | """ 25 | 26 | # Import the yamltodict library 27 | import yaml 28 | 29 | # Open the sample yaml file and read it into variable 30 | with open("yaml_example.yaml") as f: 31 | yaml_example = f.read() 32 | 33 | # Print the raw yaml data 34 | print(yaml_example) 35 | 36 | # Parse the yaml into a Python dictionary 37 | yaml_dict = yaml.load(yaml_example) 38 | 39 | # Save the interface name into a variable 40 | int_name = yaml_dict["interface"]["name"] 41 | 42 | # Print the interface name 43 | print(int_name) 44 | 45 | # Change the IP address of the interface 46 | yaml_dict["interface"]["ipv4"]["address"][0]["ip"] = "192.168.0.2" 47 | 48 | # Revert to the yaml string version of the dictionary 49 | print(yaml.dump(yaml_dict, default_flow_style=False)) 50 | -------------------------------------------------------------------------------- /data_manipulation/yaml/yaml_example.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | interface: 3 | name: GigabitEthernet2 4 | description: Wide Area Network 5 | enabled: true 6 | ipv4: 7 | address: 8 | - ip: 172.16.0.2 9 | netmask: 255.255.255.0 10 | -------------------------------------------------------------------------------- /data_manipulation/yang/cisco-xe-ietf-ip-deviation.yang: -------------------------------------------------------------------------------- 1 | module cisco-xe-ietf-ip-deviation { 2 | namespace "http://cisco.com/ns/cisco-xe-ietf-ip-deviation"; 3 | 4 | prefix ip-devs; 5 | 6 | import ietf-interfaces { 7 | prefix if; 8 | } 9 | 10 | import ietf-ip { 11 | prefix ip; 12 | } 13 | 14 | organization 15 | "Cisco Systems, Inc."; 16 | 17 | contact 18 | "Cisco Systems, Inc. 19 | Customer Service 20 | 21 | Postal: 170 W Tasman Drive 22 | San Jose, CA 95134 23 | 24 | Tel: +1 1800 553-NETS 25 | 26 | E-mail: cs-yang@cisco.com"; 27 | 28 | description 29 | "This module defines deviation statements for ietf-ip module."; 30 | 31 | revision 2016-08-10 { 32 | description 33 | "Updated deviation statements for 16.3.2 release."; 34 | } 35 | 36 | revision 2015-09-11 { 37 | description 38 | "Initial Revision"; 39 | 40 | reference 41 | "RFC 6020: YANG - A Data Modeling Language for the 42 | Network Configuration Protocol (NETCONF)"; 43 | } 44 | 45 | deviation /if:interfaces/if:interface/ip:ipv4/ip:enabled { 46 | deviate not-supported; 47 | description "Not supported in IOS-XE 3.17 release."; 48 | } 49 | 50 | deviation /if:interfaces/if:interface/ip:ipv4/ip:forwarding { 51 | deviate not-supported; 52 | description "Not supported in IOS-XE 3.17 release."; 53 | } 54 | 55 | deviation /if:interfaces/if:interface/ip:ipv4/ip:mtu { 56 | deviate not-supported; 57 | description "Not supported in IOS-XE 3.17 release."; 58 | } 59 | 60 | /* deviation "/if:interfaces/if:interface/ip:ipv4/ip:address" + 61 | "/ip:subnet/ip:prefix-length" { 62 | deviate not-supported; 63 | description "Not supported in IOS-XE 3.17 release."; 64 | } */ 65 | 66 | deviation /if:interfaces/if:interface/ip:ipv4/ip:neighbor { 67 | deviate not-supported; 68 | description "Not supported in IOS-XE 3.17 release."; 69 | } 70 | 71 | deviation /if:interfaces/if:interface/ip:ipv6/ip:enabled { 72 | deviate not-supported; 73 | description "Not supported in IOS-XE 3.17 release."; 74 | } 75 | 76 | deviation /if:interfaces/if:interface/ip:ipv6/ip:forwarding { 77 | deviate not-supported; 78 | description "Not supported in IOS-XE 3.17 release."; 79 | } 80 | 81 | deviation /if:interfaces/if:interface/ip:ipv6/ip:mtu { 82 | deviate not-supported; 83 | description "Not supported in IOS-XE 3.17 release."; 84 | } 85 | 86 | deviation /if:interfaces/if:interface/ip:ipv6/ip:dup-addr-detect-transmits { 87 | deviate not-supported; 88 | description "Not supported in IOS-XE 3.17 release."; 89 | } 90 | 91 | deviation /if:interfaces/if:interface/ip:ipv6/ip:autoconf { 92 | deviate not-supported; 93 | description "Not supported in IOS-XE 3.17 release."; 94 | } 95 | 96 | deviation /if:interfaces/if:interface/ip:ipv6/ip:neighbor { 97 | deviate not-supported; 98 | description "Not supported in IOS-XE 3.17 release."; 99 | } 100 | 101 | deviation /if:interfaces-state/if:interface/ip:ipv4 { 102 | deviate not-supported; 103 | description "Not supported in IOS-XE 16.3.2 release"; 104 | } 105 | 106 | deviation /if:interfaces-state/if:interface/ip:ipv6 { 107 | deviate not-supported; 108 | description "Not supported in IOS-XE 16.3.2 release"; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /data_manipulation/yang/ietf-ipv4-unicast-routing.yang: -------------------------------------------------------------------------------- 1 | module ietf-ipv4-unicast-routing { 2 | 3 | yang-version "1.1"; 4 | 5 | namespace "urn:ietf:params:xml:ns:yang:ietf-ipv4-unicast-routing"; 6 | 7 | prefix "v4ur"; 8 | 9 | import ietf-routing { 10 | prefix "rt"; 11 | } 12 | 13 | import ietf-inet-types { 14 | prefix "inet"; 15 | } 16 | organization 17 | "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; 18 | 19 | contact 20 | "WG Web: 21 | WG List: 22 | 23 | WG Chair: Lou Berger 24 | 25 | 26 | WG Chair: Kent Watsen 27 | 28 | 29 | Editor: Ladislav Lhotka 30 | 31 | 32 | Editor: Acee Lindem 33 | "; 34 | 35 | description 36 | "This YANG module augments the 'ietf-routing' module with basic 37 | configuration and state data for IPv4 unicast routing. 38 | 39 | Copyright (c) 2016 IETF Trust and the persons identified as 40 | authors of the code. All rights reserved. 41 | 42 | Redistribution and use in source and binary forms, with or 43 | without modification, is permitted pursuant to, and subject to 44 | the license terms contained in, the Simplified BSD License set 45 | forth in Section 4.c of the IETF Trust's Legal Provisions 46 | Relating to IETF Documents 47 | (http://trustee.ietf.org/license-info). 48 | 49 | The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', 'SHALL 50 | NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', 'MAY', and 51 | 'OPTIONAL' in the module text are to be interpreted as described 52 | in RFC 2119. 53 | 54 | This version of this YANG module is part of RFC 8022; 55 | see the RFC itself for full legal notices."; 56 | 57 | revision 2016-11-04 { 58 | description 59 | "Initial revision."; 60 | reference 61 | "RFC 8022: A YANG Data Model for Routing Management"; 62 | } 63 | 64 | /* Identities */ 65 | 66 | identity ipv4-unicast { 67 | base rt:ipv4; 68 | description 69 | "This identity represents the IPv4 unicast address family."; 70 | } 71 | 72 | /* State data */ 73 | 74 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" { 75 | when "derived-from-or-self(../../rt:address-family, " 76 | + "'v4ur:ipv4-unicast')" { 77 | description 78 | "This augment is valid only for IPv4 unicast."; 79 | } 80 | description 81 | "This leaf augments an IPv4 unicast route."; 82 | leaf destination-prefix { 83 | type inet:ipv4-prefix; 84 | description 85 | "IPv4 destination prefix."; 86 | } 87 | } 88 | 89 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" 90 | + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" { 91 | when "derived-from-or-self(../../../rt:address-family, " 92 | + "'v4ur:ipv4-unicast')" { 93 | description 94 | "This augment is valid only for IPv4 unicast."; 95 | } 96 | description 97 | "Augment 'simple-next-hop' case in IPv4 unicast routes."; 98 | leaf next-hop-address { 99 | type inet:ipv4-address; 100 | description 101 | "IPv4 address of the next hop."; 102 | } 103 | } 104 | 105 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" 106 | + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/" 107 | + "rt:next-hop-list/rt:next-hop" { 108 | when "derived-from-or-self(../../../../../rt:address-family, " 109 | + "'v4ur:ipv4-unicast')" { 110 | description 111 | "This augment is valid only for IPv4 unicast."; 112 | } 113 | description 114 | "This leaf augments the 'next-hop-list' case of IPv4 unicast 115 | routes."; 116 | leaf address { 117 | type inet:ipv4-address; 118 | description 119 | "IPv4 address of the next-hop."; 120 | } 121 | } 122 | 123 | augment 124 | "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/rt:input" { 125 | when "derived-from-or-self(../rt:address-family, " 126 | + "'v4ur:ipv4-unicast')" { 127 | description 128 | "This augment is valid only for IPv4 unicast RIBs."; 129 | } 130 | description 131 | "This augment adds the input parameter of the 'active-route' 132 | action."; 133 | leaf destination-address { 134 | type inet:ipv4-address; 135 | description 136 | "IPv4 destination address."; 137 | } 138 | } 139 | 140 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" 141 | + "rt:output/rt:route" { 142 | when "derived-from-or-self(../../rt:address-family, " 143 | + "'v4ur:ipv4-unicast')" { 144 | description 145 | "This augment is valid only for IPv4 unicast."; 146 | } 147 | description 148 | "This augment adds the destination prefix to the reply of the 149 | 'active-route' action."; 150 | leaf destination-prefix { 151 | type inet:ipv4-prefix; 152 | description 153 | "IPv4 destination prefix."; 154 | } 155 | } 156 | 157 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" 158 | + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" 159 | + "rt:simple-next-hop" { 160 | when "derived-from-or-self(../../../rt:address-family, " 161 | + "'v4ur:ipv4-unicast')" { 162 | description 163 | "This augment is valid only for IPv4 unicast."; 164 | } 165 | description 166 | "Augment 'simple-next-hop' case in the reply to the 167 | 'active-route' action."; 168 | leaf next-hop-address { 169 | type inet:ipv4-address; 170 | description 171 | "IPv4 address of the next hop."; 172 | } 173 | } 174 | 175 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" 176 | + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" 177 | + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" { 178 | when "derived-from-or-self(../../../../../rt:address-family, " 179 | + "'v4ur:ipv4-unicast')" { 180 | description 181 | "This augment is valid only for IPv4 unicast."; 182 | } 183 | description 184 | "Augment 'next-hop-list' case in the reply to the 185 | 'active-route' action."; 186 | leaf next-hop-address { 187 | type inet:ipv4-address; 188 | description 189 | "IPv4 address of the next hop."; 190 | } 191 | } 192 | 193 | /* Configuration data */ 194 | 195 | augment "/rt:routing/rt:control-plane-protocols/" 196 | + "rt:control-plane-protocol/rt:static-routes" { 197 | description 198 | "This augment defines the configuration of the 'static' 199 | pseudo-protocol with data specific to IPv4 unicast."; 200 | container ipv4 { 201 | description 202 | "Configuration of a 'static' pseudo-protocol instance 203 | consists of a list of routes."; 204 | list route { 205 | key "destination-prefix"; 206 | description 207 | "A list of static routes."; 208 | leaf destination-prefix { 209 | type inet:ipv4-prefix; 210 | mandatory "true"; 211 | description 212 | "IPv4 destination prefix."; 213 | } 214 | leaf description { 215 | type string; 216 | description 217 | "Textual description of the route."; 218 | } 219 | container next-hop { 220 | description 221 | "Configuration of next-hop."; 222 | uses rt:next-hop-content { 223 | augment "next-hop-options/simple-next-hop" { 224 | description 225 | "Augment 'simple-next-hop' case in IPv4 static 226 | routes."; 227 | leaf next-hop-address { 228 | type inet:ipv4-address; 229 | description 230 | "IPv4 address of the next hop."; 231 | } 232 | } 233 | augment "next-hop-options/next-hop-list/next-hop-list/" 234 | + "next-hop" { 235 | description 236 | "Augment 'next-hop-list' case in IPv4 static 237 | routes."; 238 | leaf next-hop-address { 239 | type inet:ipv4-address; 240 | description 241 | "IPv4 address of the next hop."; 242 | } 243 | } 244 | } 245 | } 246 | } 247 | } 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /data_manipulation/yang/pyang-examples.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # These example bash commands show how to use the pyang utility to work 3 | # with yang module files. 4 | # 5 | # Copyright (c) 2018 Cisco and/or its affiliates. 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in all 15 | # copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | # SOFTWARE. 24 | 25 | echo "Print the YANG module in a simple text tree" 26 | pyang -f tree ietf-interfaces.yang 27 | 28 | echo "Print only part of the tree" 29 | pyang -f tree --tree-path=/interfaces/interface \ 30 | ietf-interfaces.yang 31 | 32 | echo "Print an example XML skeleton (NETCONF)" 33 | pyang -f sample-xml-skeleton ietf-interfaces.yang 34 | 35 | echo "Create an HTTP/JS view of the YANG Model" 36 | pyang -f jstree -o ietf-interfaces.html \ 37 | ietf-interfaces.yang 38 | open ietf-interfaces.html 39 | 40 | echo 'Control the "nested depth" in trees' 41 | pyang -f tree --tree-depth=2 ietf-ip.yang 42 | 43 | echo "Include deviation models in the processing" 44 | pyang -f tree \ 45 | --deviation-module=cisco-xe-ietf-ip-deviation.yang \ 46 | ietf-ip.yang 47 | -------------------------------------------------------------------------------- /device_apis/cli/netmiko_example1.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the netmiko library for CLI interfacing 3 | 4 | This script will retrieve information from a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | from netmiko import ConnectHandler 29 | import re 30 | import sys 31 | 32 | # Add parent directory to path to allow importing common vars 33 | sys.path.append("..") # noqa 34 | from device_info import ios_xe1 as device # noqa 35 | 36 | # Set device_type for netmiko 37 | device["device_type"] = "cisco_ios" 38 | 39 | # Create a CLI command template 40 | show_interface_config_temp = "show running-config interface {}" 41 | 42 | # Open CLI connection to device 43 | with ConnectHandler(ip = device["address"], 44 | port = device["ssh_port"], 45 | username = device["username"], 46 | password = device["password"], 47 | device_type = device["device_type"]) as ch: 48 | 49 | # Create desired CLI command and send to device 50 | command = show_interface_config_temp.format("GigabitEthernet2") 51 | interface = ch.send_command(command) 52 | 53 | # Print the raw command output to the screen 54 | print(interface) 55 | 56 | # Use regular expressions to parse the output for desired data 57 | name = re.search(r'interface (.*)', interface).group(1) 58 | description = re.search(r'description (.*)', interface).group(1) 59 | ip_info = re.search(r'ip address (.*) (.*)', interface) 60 | ip = ip_info.group(1) 61 | netmask = ip_info.group(2) 62 | 63 | # Print the info to the screen 64 | print("The interface {name} has ip address {ip}/{mask}".format( 65 | name = name, 66 | ip = ip, 67 | mask = netmask, 68 | ) 69 | ) 70 | -------------------------------------------------------------------------------- /device_apis/cli/netmiko_example1a.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the netmiko library for CLI interfacing 3 | 4 | This script will retrieve information from a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | from netmiko import ConnectHandler 29 | import re 30 | import sys 31 | 32 | # Add parent directory to path to allow importing common vars 33 | sys.path.append("..") # noqa 34 | from device_info import ios_xe1 as device # noqa 35 | 36 | # Set device_type for netmiko 37 | device["device_type"] = "cisco_ios" 38 | 39 | # Create a CLI command template 40 | show_interface_config_temp = "show running-config interface {}" 41 | 42 | # Open CLI connection to device 43 | with ConnectHandler(ip = device["address"], 44 | port = device["ssh_port"], 45 | username = device["username"], 46 | password = device["password"], 47 | device_type = device["device_type"]) as ch: 48 | 49 | # Create desired CLI command and send to device 50 | command = show_interface_config_temp.format("Loopback103") 51 | interface = ch.send_command(command) 52 | 53 | # Print the raw command output to the screen 54 | print(interface) 55 | 56 | # Use regular expressions to parse the output for desired data 57 | name = re.search(r'interface (.*)', interface).group(1) 58 | description = re.search(r'description (.*)', interface).group(1) 59 | ip_info = re.search(r'ip address (.*) (.*)', interface) 60 | ip = ip_info.group(1) 61 | netmask = ip_info.group(2) 62 | 63 | # Print the info to the screen 64 | print("The interface {name} has ip address {ip}/{mask}".format( 65 | name = name, 66 | ip = ip, 67 | mask = netmask, 68 | ) 69 | ) 70 | -------------------------------------------------------------------------------- /device_apis/cli/netmiko_example2.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the netmiko library for CLI interfacing 3 | 4 | This script will create new configuration on a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | from netmiko import ConnectHandler 29 | import sys 30 | 31 | # Add parent directory to path to allow importing common vars 32 | sys.path.append("..") # noqa 33 | from device_info import ios_xe1 as device # noqa 34 | 35 | # Set device_type for netmiko 36 | device["device_type"] = "cisco_ios" 37 | 38 | # New Loopback Details 39 | loopback = {"int_name": "Loopback103", 40 | "description": "Demo interface by CLI and netmiko", 41 | "ip": "192.168.103.1", 42 | "netmask": "255.255.255.0"} 43 | 44 | # Create a CLI configuration 45 | interface_config = [ 46 | "interface {}".format(loopback["int_name"]), 47 | "description {}".format(loopback["description"]), 48 | "ip address {} {}".format(loopback["ip"], loopback["netmask"]), 49 | "no shut" 50 | ] 51 | 52 | # Open CLI connection to device 53 | with ConnectHandler(ip = device["address"], 54 | port = device["ssh_port"], 55 | username = device["username"], 56 | password = device["password"], 57 | device_type = device["device_type"]) as ch: 58 | 59 | # Send configuration to device 60 | output = ch.send_config_set(interface_config) 61 | 62 | # Print the raw command output to the screen 63 | print("The following configuration was sent: ") 64 | print(output) 65 | -------------------------------------------------------------------------------- /device_apis/cli/netmiko_example3.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the netmiko library for CLI interfacing 3 | 4 | This script will delete configuration on a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | from netmiko import ConnectHandler 29 | import sys 30 | 31 | # Add parent directory to path to allow importing common vars 32 | sys.path.append("..") # noqa 33 | from device_info import ios_xe1 as device # noqa 34 | 35 | # Set device_type for netmiko 36 | device["device_type"] = "cisco_ios" 37 | 38 | # New Loopback Details 39 | loopback = {"int_name": "Loopback103"} 40 | 41 | # Create a CLI configuration 42 | interface_config = [ 43 | "no interface {}".format(loopback["int_name"]) 44 | ] 45 | 46 | # Open CLI connection to device 47 | with ConnectHandler(ip = device["address"], 48 | port = device["ssh_port"], 49 | username = device["username"], 50 | password = device["password"], 51 | device_type = device["device_type"]) as ch: 52 | 53 | # Send configuration to device 54 | output = ch.send_config_set(interface_config) 55 | 56 | # Print the raw command output to the screen 57 | print("The following configuration was sent: ") 58 | print(output) 59 | -------------------------------------------------------------------------------- /device_apis/device_info.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Device Details for DevNet Sandboxes 3 | 4 | This script is imported into other code. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | __author__ = "Hank Preston" 28 | __author_email__ = "hapresto@cisco.com" 29 | __copyright__ = "Copyright (c) 2016 Cisco Systems, Inc." 30 | __license__ = "MIT" 31 | 32 | # DevNet Always-On NETCONF/YANG & RESTCONF Sandbox Device 33 | # https://devnetsandbox.cisco.com/RM/Diagram/Index/27d9747a-db48-4565-8d44-df318fce37ad?diagramType=Topology 34 | ios_xe1 = { 35 | "address": "ios-xe-mgmt.cisco.com", 36 | "netconf_port": 10000, 37 | "restconf_port": 9443, 38 | "ssh_port": 8181, 39 | "username": "root", 40 | "password": "D_Vay!_10&" 41 | } 42 | 43 | # Vagrant option - Uncomment the below if using Vagrant IOS XE Device 44 | vagrant_iosxe = { 45 | "address": "127.0.0.1", 46 | "netconf_port": 2223, 47 | "restconf_port": 2225, 48 | "ssh_port": 2222, 49 | "snmp_port": 2227, 50 | "username": "vagrant", 51 | "password": "vagrant" 52 | } 53 | 54 | 55 | # DevNet Always-On Sandbox APIC-EM 56 | # https://devnetsandbox.cisco.com/RM/Diagram/Index/2e0f9525-5f46-4f46-973e-0f0c1bf934fa?diagramType=Topology 57 | apicem = { 58 | "host": "sandboxapicem.cisco.com", 59 | "username": "devnetuser", 60 | "password": "Cisco123!", 61 | "port": 443 62 | } 63 | 64 | # DevNet Always-On Sandbox ACI APIC 65 | # https://devnetsandbox.cisco.com/RM/Diagram/Index/5a229a7c-95d5-4cfd-a651-5ee9bc1b30e2?diagramType=Topology 66 | apic = { 67 | "host": "https://sandboxapicdc.cisco.com", 68 | "username": "admin", 69 | "password": "ciscopsdt", 70 | "port": 443 71 | } 72 | -------------------------------------------------------------------------------- /device_apis/netconf/netconf-ssh-example.md: -------------------------------------------------------------------------------- 1 | # Example NETCONF Flow over SSH 2 | 3 | 1. Open SSH Connection 4 | 5 | ```bash 6 | ssh -oHostKeyAlgorithms=+ssh-dss root@ios-xe-mgmt.cisco.com -p 10000 -s netconf 7 | ``` 8 | 9 | 1. Send Password 10 | 11 | ```bash 12 | D_Vay!_10& 13 | ``` 14 | 15 | 1. Receive agent ``. 16 | 17 | 1. Send manager `` 18 | 19 | ```xml 20 | 21 | 22 | 23 | urn:ietf:params:netconf:base:1.0 24 | 25 | ]]>]]> 26 | ``` 27 | 28 | * _Note: There will be **NO** response from device._ 29 | 30 | 1. End connection with `` 31 | 32 | ```xml 33 | 34 | 35 | 36 | 37 | ]]>]]> 38 | ``` 39 | 40 | 1. Receive `` from agent. Notice how the `message-id` matches your sent value. 41 | -------------------------------------------------------------------------------- /device_apis/netconf/netconf_example1.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the ncclient library for NETCONF 3 | 4 | This script will retrieve information from a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | from ncclient import manager 29 | from xml.dom import minidom 30 | import xmltodict 31 | import sys 32 | 33 | # Add parent directory to path to allow importing common vars 34 | sys.path.append("..") # noqa 35 | # from device_info import ios_xe1 as device # noqa 36 | from device_info import ios_xe1 as device # noqa 37 | 38 | 39 | # Create filter template for an interface 40 | interface_filter = """ 41 | 42 | 43 | 44 | {int_name} 45 | 46 | 47 | 48 | """ 49 | 50 | # Open NETCONF connection to device 51 | with manager.connect(host = device["address"], 52 | port = device["netconf_port"], 53 | username = device["username"], 54 | password = device["password"], 55 | hostkey_verify = False) as m: 56 | 57 | # Create desired NETCONF filter and 58 | filter = interface_filter.format(int_name = "GigabitEthernet1") 59 | r = m.get_config("running", filter) 60 | 61 | # Pretty print raw xml to screen 62 | xml_doc = minidom.parseString(r.xml) 63 | print(xml_doc.toprettyxml(indent = " ")) 64 | 65 | # Process the XML data into Python Dictionary and use 66 | interface = xmltodict.parse(r.xml) 67 | 68 | # Only if RPC returned data 69 | if not interface["rpc-reply"]["data"] is None: 70 | interface = interface["rpc-reply"]["data"]["interfaces"]["interface"] 71 | 72 | print("The interface {name} has ip address {ip}/{mask}".format( 73 | name = interface["name"]["#text"], 74 | ip = interface["ipv4"]["address"]["ip"], 75 | mask = interface["ipv4"]["address"]["netmask"], 76 | ) 77 | ) 78 | else: 79 | print("No interface {} found".format("GigabitEthernet1")) 80 | -------------------------------------------------------------------------------- /device_apis/netconf/netconf_example1a.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the ncclient library for NETCONF 3 | 4 | This script will retrieve information from a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | from ncclient import manager 29 | from xml.dom import minidom 30 | import yaml, xmltodict 31 | import sys 32 | 33 | # Add parent directory to path to allow importing common vars 34 | sys.path.append("..") # noqa 35 | from device_info import ios_xe1 as device # noqa 36 | 37 | # Create filter template for an interface 38 | interface_filter = """ 39 | 40 | 41 | 42 | {int_name} 43 | 44 | 45 | 46 | """ 47 | 48 | # Open NETCONF connection to device 49 | with manager.connect(host = device["address"], 50 | port = device["netconf_port"], 51 | username = device["username"], 52 | password = device["password"], 53 | hostkey_verify = False) as m: 54 | 55 | # Create desired NETCONF filter and 56 | filter = interface_filter.format(int_name = "Loopback102") 57 | r = m.get_config("running", filter) 58 | 59 | # Pretty print raw xml to screen 60 | xml_doc = minidom.parseString(r.xml) 61 | print(xml_doc.toprettyxml(indent = " ")) 62 | 63 | # Process the XML data into Python Dictionary and use 64 | interface = xmltodict.parse(r.xml) 65 | 66 | # Only if RPC returned data 67 | if not interface["rpc-reply"]["data"] is None: 68 | interface = interface["rpc-reply"]["data"]["interfaces"]["interface"] 69 | 70 | print("The interface {name} has ip address {ip}/{mask}".format( 71 | name = interface["name"]["#text"], 72 | ip = interface["ipv4"]["address"]["ip"], 73 | mask = interface["ipv4"]["address"]["netmask"], 74 | ) 75 | ) 76 | else: 77 | print("No interface {} found".format("Loopback102")) 78 | -------------------------------------------------------------------------------- /device_apis/netconf/netconf_example2.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the ncclient library for NETCONF 3 | 4 | This script will create new configuration on a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | from ncclient import manager 29 | from xml.dom import minidom 30 | import xmltodict 31 | import sys 32 | 33 | # Add parent directory to path to allow importing common vars 34 | sys.path.append("..") # noqa 35 | from device_info import ios_xe1 as device # noqa 36 | 37 | # New Loopback Details 38 | loopback = {"int_name": "Loopback102", 39 | "description": "Demo interface by NETCONF", 40 | "ip": "192.168.102.1", 41 | "netmask": "255.255.255.0"} 42 | 43 | 44 | # Create config template for an interface 45 | config_data = """ 46 | 47 | 48 | 49 | {int_name} 50 | {description} 51 | 52 | ianaift:softwareLoopback 53 | 54 | true 55 | 56 |
57 | {ip} 58 | {netmask} 59 |
60 |
61 |
62 |
63 |
64 | """ 65 | 66 | # Open NETCONF connection to device 67 | with manager.connect(host = device["address"], 68 | port = device["netconf_port"], 69 | username = device["username"], 70 | password = device["password"], 71 | hostkey_verify = False) as m: 72 | 73 | # Create desired NETCONF config payload and 74 | config = config_data.format(**loopback) 75 | r = m.edit_config(target = "running", config = config) 76 | 77 | # Print OK status 78 | print("NETCONF RPC OK: {}".format(r.ok)) 79 | -------------------------------------------------------------------------------- /device_apis/netconf/netconf_example3.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the ncclient library for NETCONF 3 | 4 | This script will delete configuration on a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | from ncclient import manager 29 | from xml.dom import minidom 30 | import xmltodict 31 | import sys 32 | 33 | # Add parent directory to path to allow importing common vars 34 | sys.path.append("..") # noqa 35 | from device_info import ios_xe1 as device # noqa 36 | 37 | # New Loopback Details 38 | loopback = {"int_name": "Loopback102", 39 | "description": "Demo interface by NETCONF", 40 | "ip": "192.168.102.1", 41 | "netmask": "255.255.255.0"} 42 | 43 | 44 | # Create config template for an interface 45 | config_data = """ 46 | 47 | 48 | 49 | {int_name} 50 | 51 | 52 | 53 | """ 54 | 55 | # Open NETCONF connection to device 56 | with manager.connect(host = device["address"], 57 | port = device["netconf_port"], 58 | username = device["username"], 59 | password = device["password"], 60 | hostkey_verify = False) as m: 61 | 62 | # Create desired NETCONF config payload and 63 | config = config_data.format(**loopback) 64 | r = m.edit_config(target = "running", config = config) 65 | 66 | # Print OK status 67 | print("NETCONF RPC OK: {}".format(r.ok)) 68 | -------------------------------------------------------------------------------- /device_apis/rest/restconf_example1.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the requests library for RESTCONF. 3 | 4 | This script will retrieve information from a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | import requests, urllib3 29 | import sys 30 | 31 | # Add parent directory to path to allow importing common vars 32 | sys.path.append("..") # noqa 33 | from device_info import ios_xe1 as device # noqa 34 | 35 | # Disable Self-Signed Cert warning for demo 36 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 37 | 38 | # Setup base variable for request 39 | restconf_headers = {"Accept": "application/yang-data+json"} 40 | restconf_base = "https://{ip}:{port}/restconf/data" 41 | interface_url = restconf_base + "/ietf-interfaces:interfaces/interface={int_name}" 42 | 43 | # Create URL and send RESTCONF request to core1 for GigE2 Config 44 | url = interface_url.format(ip = device["address"], 45 | port = device["restconf_port"], 46 | int_name = "GigabitEthernet1" 47 | ) 48 | print("URL: {}\n".format(url)) 49 | 50 | r = requests.get(url, 51 | headers = restconf_headers, 52 | auth=(device["username"], device["password"]), 53 | verify=False) 54 | 55 | # Print returned data 56 | print("GET DATA:") 57 | print(r.text) 58 | 59 | if r.status_code == 200: 60 | # Process JSON data into Python Dictionary and use 61 | interface = r.json()["ietf-interfaces:interface"] 62 | print("The interface {name} has ip address {ip}/{mask}".format( 63 | name = interface["name"], 64 | ip = interface["ietf-ip:ipv4"]["address"][0]["ip"], 65 | mask = interface["ietf-ip:ipv4"]["address"][0]["netmask"], 66 | ) 67 | ) 68 | else: 69 | print("No interface {} found.".format("GigabitEthernet1")) 70 | -------------------------------------------------------------------------------- /device_apis/rest/restconf_example1a.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the requests library for RESTCONF. 3 | 4 | This script will retrieve information from a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | import requests, urllib3 29 | import sys 30 | import yaml 31 | 32 | # Add parent directory to path to allow importing common vars 33 | sys.path.append("..") # noqa 34 | from device_info import ios_xe1 as device # noqa 35 | 36 | # Disable Self-Signed Cert warning for demo 37 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 38 | 39 | # Setup base variable for request 40 | restconf_headers = {"Accept": "application/yang-data+json"} 41 | restconf_base = "https://{ip}:{port}/restconf/data" 42 | interface_url = restconf_base + "/ietf-interfaces:interfaces/interface={int_name}" 43 | 44 | # Create URL and send RESTCONF request to core1 for GigE2 Config 45 | url = interface_url.format(ip = device["address"], 46 | port = device["restconf_port"], 47 | int_name = "Loopback101" 48 | ) 49 | print("URL: {}\n".format(url)) 50 | 51 | r = requests.get(url, 52 | headers = restconf_headers, 53 | auth=(device["username"], device["password"]), 54 | verify=False) 55 | 56 | # Print returned data 57 | print("GET DATA:") 58 | print(r.text) 59 | 60 | if r.status_code == 200: 61 | # Process JSON data into Python Dictionary and use 62 | interface = r.json()["ietf-interfaces:interface"] 63 | print("The interface {name} has ip address {ip}/{mask}".format( 64 | name = interface["name"], 65 | ip = interface["ietf-ip:ipv4"]["address"][0]["ip"], 66 | mask = interface["ietf-ip:ipv4"]["address"][0]["netmask"], 67 | ) 68 | ) 69 | else: 70 | print("No interface {} found.".format("Loopback101")) 71 | -------------------------------------------------------------------------------- /device_apis/rest/restconf_example2.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the requests library for RESTCONF. 3 | 4 | This script will create new configuration on a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | import requests, urllib3, sys, yaml 29 | 30 | # Add parent directory to path to allow importing common vars 31 | sys.path.append("..") # noqa 32 | from device_info import ios_xe1 as device # noqa 33 | 34 | # Disable Self-Signed Cert warning for demo 35 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 36 | 37 | # Setup base variable for request 38 | restconf_headers = {"Accept": "application/yang-data+json", 39 | "Content-Type": "application/yang-data+json"} 40 | restconf_base = "https://{ip}:{port}/restconf/data" 41 | interface_url = restconf_base + "/ietf-interfaces:interfaces/interface={int_name}" 42 | 43 | # New Loopback Details 44 | loopback = {"name": "Loopback101", 45 | "description": "Demo interface by RESTCONF", 46 | "ip": "192.168.101.1", 47 | "netmask": "255.255.255.0"} 48 | 49 | # Setup data body to create new loopback interface 50 | data = { 51 | "ietf-interfaces:interface": { 52 | "name": loopback["name"], 53 | "description": loopback["description"], 54 | "type": "iana-if-type:softwareLoopback", 55 | "enabled": True, 56 | "ietf-ip:ipv4": { 57 | "address": [ 58 | { 59 | "ip": loopback["ip"], 60 | "netmask": loopback["netmask"] 61 | } 62 | ] 63 | } 64 | } 65 | } 66 | 67 | # Create URL and send RESTCONF request to device 68 | url = interface_url.format(ip = device["address"], 69 | port = device["restconf_port"], 70 | int_name = loopback["name"] 71 | ) 72 | print("URL: {}\n".format(url)) 73 | 74 | r = requests.put(url, 75 | headers = restconf_headers, 76 | auth=(device["username"], device["password"]), 77 | json = data, 78 | verify=False) 79 | 80 | # Print returned data 81 | print("PUT Request Status Code: {}".format(r.status_code)) 82 | -------------------------------------------------------------------------------- /device_apis/rest/restconf_example3.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the requests library for RESTCONF. 3 | 4 | This script will delete information from a device. 5 | 6 | Copyright (c) 2018 Cisco and/or its affiliates. 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | """ 26 | 27 | # Import libraries 28 | import requests, urllib3 29 | import sys 30 | 31 | # Add parent directory to path to allow importing common vars 32 | sys.path.append("..") # noqa 33 | from device_info import ios_xe1 as device # noqa 34 | 35 | # Disable Self-Signed Cert warning for demo 36 | urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) 37 | 38 | # Setup base variable for request 39 | restconf_headers = {"Accept": "application/yang-data+json"} 40 | restconf_base = "https://{ip}:{port}/restconf/data" 41 | interface_url = restconf_base + "/ietf-interfaces:interfaces/interface={int_name}" 42 | 43 | # Create URL and send RESTCONF request to core1 for GigE2 Config 44 | url = interface_url.format(ip = device["address"], 45 | port = device["restconf_port"], 46 | int_name = "Loopback101" 47 | ) 48 | print("URL: {}\n".format(url)) 49 | 50 | r = requests.delete(url, 51 | headers = restconf_headers, 52 | auth=(device["username"], device["password"]), 53 | verify=False) 54 | 55 | # Print returned data 56 | print("DELETE Request Status Code: {}".format(r.status_code)) 57 | 58 | # # Process JSON data into Python Dictionary and use 59 | # interface = r.json()["ietf-interfaces:interface"] 60 | # print("The interface {name} has ip address {ip}/{mask}".format( 61 | # name = interface["name"], 62 | # ip = interface["ietf-ip:ipv4"]["address"][0]["ip"], 63 | # mask = interface["ietf-ip:ipv4"]["address"][0]["netmask"], 64 | # ) 65 | # ) 66 | -------------------------------------------------------------------------------- /device_apis/snmp/pysnmp_example1.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the PySNMP library for SNMP interfacing 3 | 4 | This script will query for information from a device 5 | 6 | Uses code example from: https://github.com/etingof/pysnmp 7 | 8 | Copyright (c) 2018 Cisco and/or its affiliates. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | """ 28 | 29 | # Import libraries 30 | from pysnmp.hlapi import * 31 | import sys 32 | 33 | # Add parent directory to path to allow importing common vars 34 | sys.path.append("..") # noqa 35 | from device_info import ios_xe1 as device # noqa 36 | 37 | ro_community, rw_community = "public", "private" 38 | 39 | # Setup SNMP connection and query a MIB 40 | iterator = getCmd(SnmpEngine(), 41 | CommunityData(ro_community), 42 | UdpTransportTarget((device["address"], device["snmp_port"])), 43 | ContextData(), 44 | ObjectType(ObjectIdentity('SNMPv2-MIB', 'sysDescr', 0))) 45 | 46 | # Process the query 47 | errorIndication, errorStatus, errorIndex, varBinds = next(iterator) 48 | 49 | # Check for errors, and if OK, print returned result 50 | if errorIndication: # SNMP engine errors 51 | print(errorIndication) 52 | else: 53 | if errorStatus: # SNMP agent errors 54 | print('%s at %s' % (errorStatus.prettyPrint(), 55 | varBinds[int(errorIndex)-1] if errorIndex else '?')) 56 | else: 57 | for varBind in varBinds: # SNMP response contents 58 | print(' = '.join([x.prettyPrint() for x in varBind])) 59 | -------------------------------------------------------------------------------- /network_testing/pyats/default_testbed.yaml: -------------------------------------------------------------------------------- 1 | testbed: 2 | 3 | name: test_default_q2KEVs 4 | 5 | 6 | devices: 7 | 8 | csr1000v: 9 | alias: csr1000v 10 | os: iosxe 11 | type: CSR1000v 12 | tacacs: 13 | username: root 14 | passwords: 15 | tacacs: D_Vay!_10& 16 | 17 | connections: 18 | defaults: 19 | class: unicon.Unicon 20 | console: 21 | ip: ios-xe-mgmt.cisco.com 22 | protocol: ssh 23 | port: 8181 24 | custom: 25 | abstraction: 26 | order: [os, type] 27 | 28 | sbx-n9kv-ao: 29 | alias: nxos 30 | os: nxos 31 | type: Nexus9000v 32 | tacacs: 33 | username: admin 34 | passwords: 35 | tacacs: Admin_1234! 36 | 37 | connections: 38 | defaults: 39 | class: unicon.Unicon 40 | console: 41 | ip: sbx-nxos-mgmt.cisco.com 42 | protocol: ssh 43 | port: 8181 44 | custom: 45 | abstraction: 46 | order: [os, type] 47 | 48 | vagrant-iosxe1: 49 | alias: vagrant-iosxe1 50 | os: iosxe 51 | type: CSR1000v 52 | tacacs: 53 | username: vagrant 54 | passwords: 55 | tacacs: vagrant 56 | 57 | connections: 58 | defaults: 59 | class: unicon.Unicon 60 | console: 61 | ip: 127.0.0.1 62 | protocol: ssh 63 | port: 2222 64 | custom: 65 | abstraction: 66 | order: [os, type] 67 | 68 | 69 | topology: 70 | csr1000v: 71 | interfaces: 72 | GigabitEthernet1: 73 | ipv4: 10.10.20.48 74 | link: flat 75 | type: ethernet 76 | 77 | vagrant-iosxe1: 78 | interfaces: 79 | GigabitEthernet1: 80 | ipv4: 10.0.1.23 81 | link: flat 82 | type: ethernet 83 | GigabitEthernet2: 84 | ipv4: 10.2.1.1 85 | link: flat 86 | type: ethernet 87 | 88 | sbx-n9kv-ao: 89 | interfaces: 90 | Ethernet1/1: 91 | ipv4: 10.10.20.95 92 | link: flat 93 | type: ethernet 94 | -------------------------------------------------------------------------------- /network_testing/pyats/pyats-example1.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Sample use of the pyATS library. 3 | 4 | This script will connects to a device and makes several queries. 5 | 6 | The commands are intended to be executed from within an interactive interpreter. 7 | 8 | Copyright (c) 2018 Cisco and/or its affiliates. 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | """ 28 | # Import in pyATS libraries and tools 29 | from genie.conf import Genie 30 | from ats.topology import loader 31 | from genie.abstract import Lookup 32 | from genie.libs import ops # noqa 33 | 34 | # Read and process the testbed (inventory) file 35 | genie_testbed = Genie.init("./default_testbed.yaml") 36 | 37 | # Create a pyATS device object from testbed 38 | vagrant_iosxe1 = genie_testbed.devices["vagrant-iosxe1"] 39 | 40 | # Connect to the device 41 | vagrant_iosxe1.connect() 42 | 43 | # Create an abstract device to standardize Python API and code for platform 44 | vagrant_iosxe1_abstract = Lookup.from_device(vagrant_iosxe1) 45 | 46 | # Using the absract device, learn about the Interfaces on the end device 47 | vagrant_iosxe1_interfaces = vagrant_iosxe1_abstract.ops.interface.interface.Interface(vagrant_iosxe1) 48 | vagrant_iosxe1_interfaces.learn() 49 | 50 | # Print out the interface details that were learned 51 | vagrant_iosxe1_interfaces.info 52 | 53 | # Display a single interface from the device 54 | vagrant_iosxe1_interfaces.info["GigabitEthernet1"] 55 | 56 | # Print the mac address for the interface 57 | vagrant_iosxe1_interfaces.info["GigabitEthernet1"]["mac_address"] 58 | 59 | # Notice that there was no parsing of command line output needed to access this data 60 | 61 | # Execute a command on the device and print the output 62 | print(vagrant_iosxe1.execute("show version")) 63 | 64 | # Or store the output into a variable 65 | version = vagrant_iosxe1.execute("show version") 66 | 67 | # Send a configuration command to the 68 | vagrant_iosxe1.configure("ntp server 10.10.10.10") 69 | 70 | # Create a configuration command list and send to the device 71 | config_loopback = [ 72 | "interface Loopback201", 73 | "description Configured by pyATS", 74 | "ip address 172.16.201.1 255.255.255.0", 75 | "no shut" 76 | ] 77 | vagrant_iosxe1.configure(config_loopback) 78 | 79 | # Re-learn the interfaces 80 | vagrant_iosxe1_interfaces = vagrant_iosxe1_abstract.ops.interface.interface.Interface(vagrant_iosxe1) 81 | vagrant_iosxe1_interfaces.learn() 82 | 83 | # Get details about new interface 84 | vagrant_iosxe1_interfaces.info["Loopback201"] 85 | 86 | # Disconnect from the devices 87 | vagrant_iosxe1.disconnect() 88 | -------------------------------------------------------------------------------- /python_code_tips/README.md: -------------------------------------------------------------------------------- 1 | # Python Code Tips 2 | This directory includes a selection of example scripts highlighting different Python coding tips discussed in workshops and presentations such as [Python Skills and Techniques for Network Engineers](https://developer.cisco.com/netdevops/live/#s02t01) from [NetDevOps Live!](https://developer.cisco.com/netdevops/live/) -------------------------------------------------------------------------------- /python_code_tips/command_line_tool_example/argparse_example.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Simple CLI Tool Example to check routing table using ietf-routing YANG model and NETCONF 3 | """ 4 | 5 | from ncclient import manager 6 | from ncclient.transport.errors import SSHError 7 | import xmltodict 8 | from xml.dom import minidom 9 | 10 | 11 | def get_ipv4_default_rib(host, username, password, port=830): 12 | """Use NETCONF to connect to device, retrieve IPv4 default RIP, and return as dictionary 13 | """ 14 | filter = """ 15 | 16 | 18 | 19 | default 20 | 21 | 22 | ipv4-default 23 | 24 | 25 | 26 | 27 | 28 | """ 29 | 30 | # Open NETCONF connection to device 31 | try: 32 | with manager.connect( 33 | host=host, 34 | port=port, 35 | username=username, 36 | password=password, 37 | hostkey_verify=False, 38 | ) as m: 39 | 40 | r = m.get(filter) 41 | except SSHError: 42 | print( 43 | "Unable to connect to device {} with NETCONF on port {}.".format( 44 | host, port 45 | ) 46 | ) 47 | exit(1) 48 | 49 | # Pretty print raw xml to screen 50 | # xml_doc = minidom.parseString(r.xml) 51 | # print(xml_doc.toprettyxml(indent = " ")) 52 | 53 | # Process the XML data into Python Dictionary and use 54 | response_dict = xmltodict.parse(r.xml) 55 | 56 | routes = response_dict["rpc-reply"]["data"]["routing-state"][ 57 | "routing-instance" 58 | ]["ribs"]["rib"]["routes"]["route"] 59 | 60 | return routes 61 | 62 | 63 | def print_routes(routes): 64 | """Print out the routing table based on ietf-routing ipv4 ribs 65 | """ 66 | if len(routes) == 0: 67 | print("No routes found.") 68 | exit(1) 69 | 70 | try: 71 | print( 72 | "{route:<20} {source:<10} {nexthop:<20}".format( 73 | route="Prefix", source="Source", nexthop="Next Hop" 74 | ) 75 | ) 76 | for route in routes: 77 | print( 78 | "{route:<20} {source:<10} {nexthop:<20}".format( 79 | route=route["destination-prefix"], 80 | source=route["source-protocol"], 81 | nexthop=route["next-hop"]["next-hop-address"], 82 | ) 83 | ) 84 | except Exception: 85 | print("Problem in routing table, unable to print.") 86 | exit(1) 87 | 88 | 89 | # Script Entry Point 90 | if __name__ == "__main__": 91 | # Use Arg Parse to retrieve device details 92 | import argparse 93 | import os 94 | 95 | parser = argparse.ArgumentParser() 96 | parser.add_argument( 97 | "--host", help="Host address for network device", required=True 98 | ) 99 | parser.add_argument( 100 | "--port", help="Override default NETCONF port of 830", default=830 101 | ) 102 | parser.add_argument("--username", help="Device username", required=False) 103 | parser.add_argument("--password", help="Device password", required=False) 104 | 105 | args = parser.parse_args() 106 | username = args.username 107 | password = args.password 108 | 109 | # If Username or Password not provided as arguments, check OS ENV 110 | if username is None: 111 | username = os.getenv("USERNAME") 112 | if password is None: 113 | password = os.getenv("PASSWORD") 114 | 115 | if username is None or password is None: 116 | print( 117 | "You must provide a username and password as a command argument," 118 | ) 119 | print("or as Environment Variables of USERNAME or PASSWORD") 120 | exit(1) 121 | 122 | print("Getting route list from device...") 123 | print("") 124 | 125 | # Get route list 126 | routes = get_ipv4_default_rib( 127 | host=args.host, port=args.port, username=username, password=password 128 | ) 129 | 130 | # Print route list 131 | print_routes(routes) 132 | -------------------------------------------------------------------------------- /python_code_tips/exception_handling/with_try.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | # Diable InsecureRequestWarning 4 | requests.packages.urllib3.disable_warnings( 5 | requests.packages.urllib3.exceptions.InsecureRequestWarning 6 | ) 7 | 8 | headers = {"content-type": "application/json", "x-auth-token": ""} 9 | 10 | 11 | def dnac_login(dnac, username, password): 12 | """ 13 | Use the REST API to Log into an DNA Center and retrieve ticket 14 | """ 15 | url = "https://{}/dna/system/api/v1/auth/token".format(dnac) 16 | 17 | # Make Login request and return the response body 18 | try: 19 | response = requests.request( 20 | "POST", 21 | url, 22 | auth=(username, password), 23 | headers=headers, 24 | verify=False, 25 | ) 26 | except requests.exceptions.ConnectionError: 27 | print("Unable to connect to address https://{}".format(dnac)) 28 | exit(1) 29 | 30 | if response.status_code != 200: 31 | print( 32 | "Login request failed. Status Code {}".format( 33 | response.status_code 34 | ) 35 | ) 36 | print("Response body: ") 37 | print(response.text) 38 | exit(1) 39 | 40 | # Return the Token 41 | try: 42 | return response.json()["Token"] 43 | except KeyError: 44 | print("No token found in authentication response.") 45 | print("Response body: ") 46 | print(response.text) 47 | exit(1) 48 | 49 | 50 | # Entry point for program 51 | if __name__ == "__main__": 52 | # Setup Arg Parse for Command Line parameters 53 | import argparse 54 | 55 | parser = argparse.ArgumentParser() 56 | 57 | # Command Line Parameters for Source and Destination IP 58 | parser.add_argument("dnac", help="Cisco DNA Center Address") 59 | parser.add_argument("username", help="Cisco DNA Center Username") 60 | parser.add_argument("password", help="Cisco DNA Center Password") 61 | args = parser.parse_args() 62 | 63 | # Get Source and Destination IPs from Command Line 64 | dnac = args.dnac 65 | username = args.username 66 | password = args.password 67 | 68 | # Log into the dnac Controller to get Ticket 69 | token = dnac_login(dnac, username, password) 70 | 71 | print("Your token is {}".format(token)) 72 | -------------------------------------------------------------------------------- /python_code_tips/exception_handling/without_try.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | # Diable InsecureRequestWarning 4 | requests.packages.urllib3.disable_warnings( 5 | requests.packages.urllib3.exceptions.InsecureRequestWarning 6 | ) 7 | 8 | headers = {"content-type": "application/json", "x-auth-token": ""} 9 | 10 | 11 | def dnac_login(dnac, username, password): 12 | """ 13 | Use the REST API to Log into an DNA Center and retrieve ticket 14 | """ 15 | url = "https://{}/dna/system/api/v1/auth/token".format(dnac) 16 | 17 | # Make Login request and return the response body 18 | response = requests.request( 19 | "POST", url, auth=(username, password), headers=headers, verify=False 20 | ) 21 | return response.json()["Token"] 22 | 23 | 24 | # Entry point for program 25 | if __name__ == "__main__": 26 | # Setup Arg Parse for Command Line parameters 27 | import argparse 28 | 29 | parser = argparse.ArgumentParser() 30 | 31 | # Command Line Parameters for Source and Destination IP 32 | parser.add_argument("dnac", help="Cisco DNA Center Address") 33 | parser.add_argument("username", help="Cisco DNA Center Username") 34 | parser.add_argument("password", help="Cisco DNA Center Password") 35 | args = parser.parse_args() 36 | 37 | # Get Source and Destination IPs from Command Line 38 | dnac = args.dnac 39 | username = args.username 40 | password = args.password 41 | 42 | # Log into the dnac Controller to get Ticket 43 | token = dnac_login(dnac, username, password) 44 | 45 | print("Your token is {}".format(token)) 46 | -------------------------------------------------------------------------------- /python_code_tips/functions_example/functions-after.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import requests 3 | import json 4 | import argparse 5 | 6 | # Diable InsecureRequestWarning 7 | requests.packages.urllib3.disable_warnings( 8 | requests.packages.urllib3.exceptions.InsecureRequestWarning 9 | ) 10 | 11 | # DevNet Always-On Sandbox DNA Center 12 | # https://devnetsandbox.cisco.com/RM/Diagram/Index/471eb739-323e-4805-b2a6-d0ec813dc8fc?diagramType=Topology 13 | dnac = { 14 | "host": "sandboxdnac2.cisco.com", 15 | "username": "devnetuser", 16 | "password": "Cisco123!", 17 | "port": 443, 18 | } 19 | 20 | # Headers for DNAC 21 | headers = {"content-type": "application/json", "x-auth-token": ""} 22 | 23 | 24 | def dnac_login(dnac, port, username, password): 25 | """ 26 | Use the REST API to Log into an DNA Center and retrieve ticket 27 | """ 28 | url = "https://{}:{}/dna/system/api/v1/auth/token".format(dnac, port) 29 | 30 | # Make Login request and return the response body 31 | response = requests.request( 32 | "POST", url, auth=(username, password), headers=headers, verify=False 33 | ) 34 | return response.json()["Token"] 35 | 36 | 37 | def host_list(dnac, ticket, ip=None): 38 | """ 39 | Use the REST API to retrieve the list of hosts. 40 | Optional parameters to filter by: 41 | IP address 42 | MAC address 43 | Hostname 44 | """ 45 | url = "https://{}/api/v1/host?hostIp={}".format(dnac, ip) 46 | headers["x-auth-token"] = ticket 47 | filters = [] 48 | 49 | # Make API request and return the response body 50 | response = requests.request("GET", url, headers=headers, verify=False) 51 | return response.json()["response"] 52 | 53 | 54 | def print_host_details(host): 55 | """ 56 | Print to screen interesting details about a given host. 57 | Input Paramters are: 58 | host_desc: string to describe this host. Example "Source" 59 | host: dictionary object of a host returned from dnac 60 | Standard Output Details: 61 | Host Name (hostName) - If available 62 | Host IP (hostIp) 63 | Host MAC (hostMac) 64 | Network Type (hostType) - wired/wireless 65 | Host Sub Type (subType) 66 | VLAN (vlanId) 67 | Connected Network Device (connectedNetworkDeviceIpAddress) 68 | 69 | Wired Host Details: 70 | Connected Interface Name (connectedInterfaceName) 71 | 72 | Wireless Host Details: 73 | Connected AP Name (connectedAPName) 74 | """ 75 | # If optional host details missing, add as "Unavailable" 76 | if "hostName" not in host.keys(): 77 | host["hostName"] = "Unavailable" 78 | 79 | # Print Standard Details 80 | print("Host Name: {}".format(host["hostName"])) 81 | print("Network Type: {}".format(host["hostType"])) 82 | print( 83 | "Connected Network Device: {}".format( 84 | host["connectedNetworkDeviceIpAddress"] 85 | ) 86 | ) # noqa: E501 87 | 88 | # Print Wired/Wireless Details 89 | if host["hostType"] == "wired": 90 | print( 91 | "Connected Interface Name: {}".format( 92 | host["connectedInterfaceName"] 93 | ) 94 | ) # noqa: E501 95 | if host["hostType"] == "wireless": 96 | print("Connected AP Name: {}".format(host["connectedAPName"])) 97 | 98 | # Print More Standard Details 99 | print("VLAN: {}".format(host["vlanId"])) 100 | print("Host IP: {}".format(host["hostIp"])) 101 | print("Host MAC: {}".format(host["hostMac"])) 102 | print("Host Sub Type: {}".format(host["subType"])) 103 | 104 | # Blank line at the end 105 | print("") 106 | 107 | 108 | # Entry point for program 109 | if __name__ == "__main__": 110 | # Setup Arg Parse for Command Line parameters 111 | import argparse 112 | 113 | parser = argparse.ArgumentParser() 114 | 115 | # Command Line Parameters for Source and Destination IP 116 | parser.add_argument("source_ip", help="Source IP Address") 117 | parser.add_argument("destination_ip", help="Destination IP Address") 118 | args = parser.parse_args() 119 | 120 | # Get Source and Destination IPs from Command Line 121 | source_ip = args.source_ip 122 | destination_ip = args.destination_ip 123 | 124 | # Print Starting message 125 | print("Running Troubleshooting Script for ") 126 | print(" Source IP: {} ".format(source_ip)) 127 | print(" Destination IP: {}".format(destination_ip)) 128 | print("") 129 | 130 | # Log into the dnac Controller to get Ticket 131 | token = dnac_login( 132 | dnac["host"], dnac["port"], dnac["username"], dnac["password"] 133 | ) 134 | 135 | # Retrieve Host Details from dnac 136 | source_host = host_list(dnac["host"], token, ip=source_ip) 137 | destination_host = host_list(dnac["host"], token, ip=destination_ip) 138 | 139 | # Print Out Host details 140 | print("Source Host Details:") 141 | print("-" * 25) 142 | print_host_details(source_host[0]) 143 | 144 | print("Destination Host Details:") 145 | print("-" * 25) 146 | print_host_details(destination_host[0]) 147 | -------------------------------------------------------------------------------- /python_code_tips/functions_example/functions-before.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | 3 | import requests 4 | import json 5 | import argparse 6 | 7 | # Diable InsecureRequestWarning 8 | requests.packages.urllib3.disable_warnings( 9 | requests.packages.urllib3.exceptions.InsecureRequestWarning 10 | ) 11 | 12 | # DevNet Always-On Sandbox DNA Center 13 | # https://devnetsandbox.cisco.com/RM/Diagram/Index/471eb739-323e-4805-b2a6-d0ec813dc8fc?diagramType=Topology 14 | dnac = { 15 | "host": "sandboxdnac2.cisco.com", 16 | "username": "devnetuser", 17 | "password": "Cisco123!", 18 | "port": 443, 19 | } 20 | 21 | 22 | # Setup Arg Parse for Command Line parameters 23 | parser = argparse.ArgumentParser() 24 | 25 | # Command Line Parameters for Source and Destination IP 26 | parser.add_argument("source_ip", help="Source IP Address") 27 | parser.add_argument("destination_ip", help="Destination IP Address") 28 | args = parser.parse_args() 29 | 30 | # Get Source and Destination IPs from Command Line 31 | source_ip = args.source_ip 32 | destination_ip = args.destination_ip 33 | 34 | # Print Starting message 35 | print("Running Troubleshooting Script for ") 36 | print(" Source IP: {} ".format(source_ip)) 37 | print(" Destination IP: {}".format(destination_ip)) 38 | print("") 39 | 40 | # Headers for DNAC 41 | headers = {"content-type": "application/json", "x-auth-token": ""} 42 | 43 | 44 | # Login to DNAC 45 | url = "https://{}:{}/dna/system/api/v1/auth/token".format( 46 | dnac["host"], dnac["port"] 47 | ) 48 | 49 | # Make Login request and return the response body 50 | response = requests.request( 51 | "POST", 52 | url, 53 | auth=(dnac["username"], dnac["password"]), 54 | headers=headers, 55 | verify=False, 56 | ) 57 | token = response.json()["Token"] 58 | headers["x-auth-token"] = token 59 | 60 | # URL for Host Calls 61 | url = "https://{}/api/v1/host".format(dnac["host"]) 62 | 63 | # Get Host Infomration for Source 64 | source_url = url + "?" + "&hostIp={}".format(source_ip) 65 | 66 | # Make API request and return the response body 67 | response = requests.request("GET", source_url, headers=headers, verify=False) 68 | source_host = response.json()["response"][0] 69 | 70 | # Print out details about source 71 | print("Source Host Details:") 72 | print("-" * 25) 73 | # If optional host details missing, add as "Unavailable" 74 | if "hostName" not in source_host.keys(): 75 | source_host["hostName"] = "Unavailable" 76 | 77 | # Print Standard Details 78 | print("Host Name: {}".format(source_host["hostName"])) 79 | print("Network Type: {}".format(source_host["hostType"])) 80 | print( 81 | "Connected Network Device: {}".format( 82 | source_host["connectedNetworkDeviceIpAddress"] 83 | ) 84 | ) # noqa: E501 85 | 86 | # Print Wired/Wireless Details 87 | if source_host["hostType"] == "wired": 88 | print( 89 | "Connected Interface Name: {}".format( 90 | source_host["connectedInterfaceName"] 91 | ) 92 | ) # noqa: E501 93 | if source_host["hostType"] == "wireless": 94 | print("Connected AP Name: {}".format(source_host["connectedAPName"])) 95 | 96 | # Print More Standard Details 97 | print("VLAN: {}".format(source_host["vlanId"])) 98 | print("Host IP: {}".format(source_host["hostIp"])) 99 | print("Host MAC: {}".format(source_host["hostMac"])) 100 | print("Host Sub Type: {}".format(source_host["subType"])) 101 | 102 | # Blank line at the end 103 | print("") 104 | 105 | # Get Host Infomration for Destination 106 | destination_url = url + "?" + "&hostIp={}".format(destination_ip) 107 | 108 | # Make API request and return the response body 109 | response = requests.request( 110 | "GET", destination_url, headers=headers, verify=False 111 | ) 112 | destination_host = response.json()["response"][0] 113 | 114 | # Print out details about source 115 | print("Detination Host Details:") 116 | print("-" * 25) 117 | # If optional host details missing, add as "Unavailable" 118 | if "hostName" not in destination_host.keys(): 119 | destination_host["hostName"] = "Unavailable" 120 | 121 | # Print Standard Details 122 | print("Host Name: {}".format(destination_host["hostName"])) 123 | print("Network Type: {}".format(destination_host["hostType"])) 124 | print( 125 | "Connected Network Device: {}".format( 126 | destination_host["connectedNetworkDeviceIpAddress"] 127 | ) 128 | ) # noqa: E501 129 | 130 | # Print Wired/Wireless Details 131 | if destination_host["hostType"] == "wired": 132 | print( 133 | "Connected Interface Name: {}".format( 134 | destination_host["connectedInterfaceName"] 135 | ) 136 | ) # noqa: E501 137 | if destination_host["hostType"] == "wireless": 138 | print("Connected AP Name: {}".format(destination_host["connectedAPName"])) 139 | 140 | # Print More Standard Details 141 | print("VLAN: {}".format(destination_host["vlanId"])) 142 | print("Host IP: {}".format(destination_host["hostIp"])) 143 | print("Host MAC: {}".format(destination_host["hostMac"])) 144 | print("Host Sub Type: {}".format(destination_host["subType"])) 145 | 146 | # Blank line at the end 147 | print("") 148 | -------------------------------------------------------------------------------- /python_code_tips/misc/example1.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Example Python script. 3 | 4 | Copyright (c) 2018 Cisco and/or its affiliates.""" 5 | 6 | import os 7 | 8 | def say_hello(name): 9 | """Function that will say hello to someone. 10 | """ 11 | 12 | # Print out a hello message to the name given 13 | print("Hello there {name}. It's great to see you.".format(name = name)) 14 | 15 | 16 | 17 | def script_details(): 18 | """Function that reports by printing to screen some details about the execution of the script.""" 19 | # Get the current directory and print it out. 20 | cur_dir = os.getcwd() 21 | print("Current directory is {}".format(cur_dir)) 22 | 23 | # Get the User ID and Group List for the User 24 | user_id = os.getuid() 25 | group_list = os.getgroups() 26 | 27 | # Print to screen 28 | print("The user id is {}".format(user_id)) 29 | print("The user is a member of the following groups:") 30 | print(",".join(str(g) for g in group_list)) 31 | 32 | 33 | if __name__ == "__main__": 34 | # If executed as a script, run this block. 35 | 36 | # Check Script details 37 | script_details() 38 | 39 | # List of names, and say hello to them 40 | names = ["Hank","Eric","Stuart","Bryan"] 41 | for name in names: 42 | say_hello(name) 43 | -------------------------------------------------------------------------------- /python_code_tips/modules_example/dnac_functions.py: -------------------------------------------------------------------------------- 1 | from time import sleep 2 | import json 3 | import requests 4 | import sys 5 | 6 | # Import DNAC Resources 7 | from dnac_resources import dnac, headers 8 | 9 | # Diable InsecureRequestWarning 10 | requests.packages.urllib3.disable_warnings( 11 | requests.packages.urllib3.exceptions.InsecureRequestWarning 12 | ) 13 | 14 | 15 | def dnac_login(dnac, port, username, password): 16 | """ 17 | Use the REST API to Log into an DNA Center and retrieve ticket 18 | """ 19 | url = "https://{}:{}/dna/system/api/v1/auth/token".format(dnac, port) 20 | 21 | # Make Login request and return the response body 22 | response = requests.request( 23 | "POST", url, auth=(username, password), headers=headers, verify=False 24 | ) 25 | return response.json()["Token"] 26 | 27 | 28 | def host_list(dnac, ticket, ip=None, mac=None, name=None): 29 | """ 30 | Use the REST API to retrieve the list of hosts. 31 | Optional parameters to filter by: 32 | IP address 33 | MAC address 34 | Hostname 35 | """ 36 | url = "https://{}/api/v1/host".format(dnac) 37 | headers["x-auth-token"] = ticket 38 | filters = [] 39 | 40 | # Add filters if provided 41 | if ip: 42 | filters.append("hostIp={}".format(ip)) 43 | if mac: 44 | filters.append("hostMac={}".format(mac)) 45 | if name: 46 | filters.append("hostName={}".format(name)) 47 | if len(filters) > 0: 48 | url += "?" + "&".join(filters) 49 | 50 | # Make API request and return the response body 51 | response = requests.request("GET", url, headers=headers, verify=False) 52 | return response.json()["response"] 53 | 54 | 55 | def verify_single_host(host, ip): 56 | """ 57 | Simple function to verify only a single host returned from query. 58 | If no hosts, or multiple hosts are returned, an error message is printed 59 | and the program exits. 60 | """ 61 | if len(host) == 0: 62 | print("Error: No host with IP address {} was found".format(ip)) 63 | sys.exit(1) 64 | if len(host) > 1: 65 | print("Error: Multiple hosts with IP address {} were found".format(ip)) 66 | print(json.dumps(host, indent=2)) 67 | sys.exit(1) 68 | 69 | 70 | def print_host_details(host): 71 | """ 72 | Print to screen interesting details about a given host. 73 | Input Paramters are: 74 | host_desc: string to describe this host. Example "Source" 75 | host: dictionary object of a host returned from dnac 76 | Standard Output Details: 77 | Host Name (hostName) - If available 78 | Host IP (hostIp) 79 | Host MAC (hostMac) 80 | Network Type (hostType) - wired/wireless 81 | Host Sub Type (subType) 82 | VLAN (vlanId) 83 | Connected Network Device (connectedNetworkDeviceIpAddress) 84 | 85 | Wired Host Details: 86 | Connected Interface Name (connectedInterfaceName) 87 | 88 | Wireless Host Details: 89 | Connected AP Name (connectedAPName) 90 | """ 91 | # If optional host details missing, add as "Unavailable" 92 | if "hostName" not in host.keys(): 93 | host["hostName"] = "Unavailable" 94 | 95 | # Print Standard Details 96 | print("Host Name: {}".format(host["hostName"])) 97 | print("Network Type: {}".format(host["hostType"])) 98 | print( 99 | "Connected Network Device: {}".format( 100 | host["connectedNetworkDeviceIpAddress"] 101 | ) 102 | ) # noqa: E501 103 | 104 | # Print Wired/Wireless Details 105 | if host["hostType"] == "wired": 106 | print( 107 | "Connected Interface Name: {}".format( 108 | host["connectedInterfaceName"] 109 | ) 110 | ) # noqa: E501 111 | if host["hostType"] == "wireless": 112 | print("Connected AP Name: {}".format(host["connectedAPName"])) 113 | 114 | # Print More Standard Details 115 | print("VLAN: {}".format(host["vlanId"])) 116 | print("Host IP: {}".format(host["hostIp"])) 117 | print("Host MAC: {}".format(host["hostMac"])) 118 | print("Host Sub Type: {}".format(host["subType"])) 119 | 120 | # Blank line at the end 121 | print("") 122 | 123 | 124 | def network_device_list(dnac, ticket, id=None): 125 | """ 126 | Use the REST API to retrieve the list of network devices. 127 | If a device id is provided, return only that device 128 | """ 129 | url = "https://{}/dna/intent/api/v1/network-device".format(dnac) 130 | headers["x-auth-token"] = ticket 131 | 132 | # Change URL to single device given an id 133 | if id: 134 | url += "/{}".format(id) 135 | 136 | # Make API request and return the response body 137 | response = requests.request("GET", url, headers=headers, verify=False) 138 | 139 | # Always return a list object, even if single device for consistency 140 | if id: 141 | return [response.json()["response"]] 142 | 143 | return response.json()["response"] 144 | 145 | 146 | def interface_details(dnac, ticket, id): 147 | """ 148 | Use the REST API to retrieve details about an interface based on id. 149 | """ 150 | url = "https://{}/dna/intent/api/v1/interface/{}".format(dnac, id) 151 | headers["x-auth-token"] = ticket 152 | 153 | response = requests.request("GET", url, headers=headers, verify=False) 154 | return response.json()["response"] 155 | 156 | 157 | def print_network_device_details(network_device): 158 | """ 159 | Print to screen interesting details about a network device. 160 | Input Paramters are: 161 | network_device: dict object of a network device returned from dnac 162 | Standard Output Details: 163 | Device Hostname (hostname) 164 | Management IP (managementIpAddress) 165 | Device Location (locationName) 166 | Device Type (type) 167 | Platform Id (platformId) 168 | Device Role (role) 169 | Serial Number (serialNumber) 170 | Software Version (softwareVersion) 171 | Up Time (upTime) 172 | Reachability Status (reachabilityStatus) 173 | Error Code (errorCode) 174 | Error Description (errorDescription) 175 | """ 176 | 177 | # Print Standard Details 178 | print("Device Hostname: {}".format(network_device["hostname"])) 179 | print("Management IP: {}".format(network_device["managementIpAddress"])) 180 | print("Device Location: {}".format(network_device["locationName"])) 181 | print("Device Type: {}".format(network_device["type"])) 182 | print("Platform Id: {}".format(network_device["platformId"])) 183 | print("Device Role: {}".format(network_device["role"])) 184 | print("Serial Number: {}".format(network_device["serialNumber"])) 185 | print("Software Version: {}".format(network_device["softwareVersion"])) 186 | print("Up Time: {}".format(network_device["upTime"])) 187 | print( 188 | "Reachability Status: {}".format(network_device["reachabilityStatus"]) 189 | ) # noqa: E501 190 | print("Error Code: {}".format(network_device["errorCode"])) 191 | print("Error Description: {}".format(network_device["errorDescription"])) 192 | 193 | # Blank line at the end 194 | print("") 195 | 196 | 197 | def print_interface_details(interface): 198 | """ 199 | Print to screen interesting details about an interface. 200 | Input Paramters are: 201 | interface: dictionary object of an interface returned from dnac 202 | Standard Output Details: 203 | Port Name - (portName) 204 | Interface Type (interfaceType) - Physical/Virtual 205 | Admin Status - (adminStatus) 206 | Operational Status (status) 207 | Media Type - (mediaType) 208 | Speed - (speed) 209 | Duplex Setting (duplex) 210 | Port Mode (portMode) - access/trunk/routed 211 | Interface VLAN - (vlanId) 212 | Voice VLAN - (voiceVlan) 213 | """ 214 | 215 | # Print Standard Details 216 | print("Port Name: {}".format(interface["portName"])) 217 | print("Interface Type: {}".format(interface["interfaceType"])) 218 | print("Admin Status: {}".format(interface["adminStatus"])) 219 | print("Operational Status: {}".format(interface["status"])) 220 | print("Media Type: {}".format(interface["mediaType"])) 221 | print("Speed: {}".format(interface["speed"])) 222 | print("Duplex Setting: {}".format(interface["duplex"])) 223 | print("Port Mode: {}".format(interface["portMode"])) 224 | print("Interface VLAN: {}".format(interface["vlanId"])) 225 | print("Voice VLAN: {}".format(interface["voiceVlan"])) 226 | 227 | # Blank line at the end 228 | print("") 229 | 230 | 231 | def run_flow_analysis(dnac, ticket, source_ip, destination_ip): 232 | """ 233 | Use the REST API to initiate a Flow Analysis (Path Trace) from a given 234 | source_ip to destination_ip. Function will wait for analysis to complete, 235 | and return the results. 236 | """ 237 | base_url = "https://{}/dna/intent/api/v1/flow-analysis".format(dnac) 238 | headers["x-auth-token"] = ticket 239 | 240 | # initiate flow analysis 241 | body = {"destIP": destination_ip, "sourceIP": source_ip} 242 | initiate_response = requests.post( 243 | base_url, headers=headers, verify=False, json=body 244 | ) 245 | 246 | # Verify successfully initiated. If not error and exit 247 | if initiate_response.status_code != 202: 248 | print("Error: Flow Analysis Initiation Failed") 249 | print(initiate_response.text) 250 | sys.exit(1) 251 | 252 | # Check status of analysis and wait until completed 253 | flowAnalysisId = initiate_response.json()["response"]["flowAnalysisId"] 254 | detail_url = base_url + "/{}".format(flowAnalysisId) 255 | detail_response = requests.get(detail_url, headers=headers, verify=False) 256 | while ( 257 | not detail_response.json()["response"]["request"]["status"] 258 | == "COMPLETED" 259 | ): # noqa: E501 260 | print("Flow analysis not complete yet, waiting 5 seconds") 261 | sleep(5) 262 | detail_response = requests.get( 263 | detail_url, headers=headers, verify=False 264 | ) 265 | 266 | # Return the flow analysis details 267 | return detail_response.json()["response"] 268 | 269 | 270 | def print_flow_analysis_details(flow_analysis): 271 | """ 272 | Print to screen interesting details about the flow analysis. 273 | Input Parameters are: 274 | flow_analysis: dictionary object of a flow analysis returned from dnac 275 | """ 276 | hops = flow_analysis["networkElementsInfo"] 277 | 278 | print("Number of Hops from Source to Destination: {}".format(len(hops))) 279 | print() 280 | 281 | # Print Details per hop 282 | print("Flow Details: ") 283 | # Hop 1 (index 0) and the last hop (index len - 1) represent the endpoints 284 | for i, hop in enumerate(hops): 285 | if i == 0 or i == len(hops) - 1: 286 | continue 287 | 288 | print("*" * 40) 289 | print("Hop {}: Network Device {}".format(i, hop["name"])) 290 | # If the hop is "UNKNOWN" continue along 291 | if hop["name"] == "UNKNOWN": 292 | print() 293 | continue 294 | 295 | print("Device IP: {}".format(hop["ip"])) 296 | print("Device Role: {}".format(hop["role"])) 297 | 298 | # If type is an Access Point, skip interface details 299 | if hop["type"] == "Unified AP": 300 | continue 301 | print() 302 | 303 | # Step 4: Are there any problems along the path? 304 | # Print details about each interface 305 | print("Ingress Interface") 306 | print("-" * 20) 307 | ingress = interface_details( 308 | dnac["host"], 309 | token, 310 | hop["ingressInterface"]["physicalInterface"]["id"], 311 | ) # noqa: E501 312 | print_interface_details(ingress) 313 | print("Egress Interface") 314 | print("-" * 20) 315 | egress = interface_details( 316 | dnac["host"], 317 | token, 318 | hop["egressInterface"]["physicalInterface"]["id"], 319 | ) # noqa: E501 320 | print_interface_details(egress) 321 | 322 | # Print blank line at end 323 | print("") 324 | -------------------------------------------------------------------------------- /python_code_tips/modules_example/dnac_resources.py: -------------------------------------------------------------------------------- 1 | # DevNet Always-On Sandbox DNA Center 2 | # https://devnetsandbox.cisco.com/RM/Diagram/Index/471eb739-323e-4805-b2a6-d0ec813dc8fc?diagramType=Topology 3 | dnac = { 4 | "host": "sandboxdnac2.cisco.com", 5 | "username": "devnetuser", 6 | "password": "Cisco123!", 7 | "port": 443, 8 | } 9 | 10 | 11 | headers = {"content-type": "application/json", "x-auth-token": ""} 12 | -------------------------------------------------------------------------------- /python_code_tips/modules_example/modules_after.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | """ 4 | 5 | # Import and functions 6 | from dnac_resources import dnac 7 | from dnac_functions import ( 8 | dnac_login, 9 | host_list, 10 | verify_single_host, 11 | print_host_details, 12 | network_device_list, 13 | interface_details, 14 | print_network_device_details, 15 | print_interface_details, 16 | ) 17 | 18 | 19 | # Entry point for program 20 | if __name__ == "__main__": 21 | # Setup Arg Parse for Command Line parameters 22 | import argparse 23 | 24 | parser = argparse.ArgumentParser() 25 | 26 | # Command Line Parameters for Source and Destination IP 27 | parser.add_argument("source_ip", help="Source IP Address") 28 | parser.add_argument("destination_ip", help="Destination IP Address") 29 | args = parser.parse_args() 30 | 31 | # Get Source and Destination IPs from Command Line 32 | source_ip = args.source_ip 33 | destination_ip = args.destination_ip 34 | 35 | # Print Starting message 36 | print("Running Troubleshooting Script for ") 37 | print(" Source IP: {} ".format(source_ip)) 38 | print(" Destination IP: {}".format(destination_ip)) 39 | print("") 40 | 41 | # Log into the dnac Controller to get Ticket 42 | token = dnac_login( 43 | dnac["host"], dnac["port"], dnac["username"], dnac["password"] 44 | ) 45 | 46 | # Step 1: Identify involved hosts 47 | # Retrieve Host Details from dnac 48 | source_host = host_list(dnac["host"], token, ip=source_ip) 49 | destination_host = host_list(dnac["host"], token, ip=destination_ip) 50 | 51 | # Verify single host found for each IP 52 | verify_single_host(source_host, source_ip) 53 | verify_single_host(destination_host, destination_ip) 54 | 55 | # Print Out Host details 56 | print("Source Host Details:") 57 | print("-" * 25) 58 | print_host_details(source_host[0]) 59 | 60 | print("Destination Host Details:") 61 | print("-" * 25) 62 | print_host_details(destination_host[0]) 63 | 64 | # Step 2: Where are they in the network? 65 | # Retrieve and Print Source Device Details from dnac 66 | source_host_net_device = network_device_list( 67 | dnac["host"], token, id=source_host[0]["connectedNetworkDeviceId"] 68 | ) # noqa: E501 69 | print("Source Host Network Connection Details:") 70 | print("-" * 45) 71 | print_network_device_details(source_host_net_device[0]) 72 | # If Host is wired, collect interface details 73 | if source_host[0]["hostType"] == "wired": 74 | source_host_interface = interface_details( 75 | dnac["host"], token, id=source_host[0]["connectedInterfaceId"] 76 | ) # noqa: E501 77 | print("Attached Interface:") 78 | print("-" * 20) 79 | print_interface_details(source_host_interface) 80 | 81 | destination_host_net_device = network_device_list( 82 | dnac["host"], token, id=destination_host[0]["connectedNetworkDeviceId"] 83 | ) # noqa: E501 84 | print("Destination Host Network Connection Details:") 85 | print("-" * 45) 86 | print_network_device_details(destination_host_net_device[0]) 87 | # If Host is wired, collect interface details 88 | if destination_host[0]["hostType"] == "wired": 89 | destination_host_interface = interface_details( 90 | dnac["host"], token, id=destination_host[0]["connectedInterfaceId"] 91 | ) # noqa: E501 92 | print("Attached Interface:") 93 | print("-" * 20) 94 | print_interface_details(destination_host_interface) 95 | -------------------------------------------------------------------------------- /python_code_tips/modules_example/modules_before.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | """ 4 | 5 | 6 | from time import sleep 7 | import json 8 | import requests 9 | import sys 10 | 11 | # Diable InsecureRequestWarning 12 | requests.packages.urllib3.disable_warnings( 13 | requests.packages.urllib3.exceptions.InsecureRequestWarning 14 | ) 15 | 16 | # DevNet Always-On Sandbox DNA Center 17 | # https://devnetsandbox.cisco.com/RM/Diagram/Index/471eb739-323e-4805-b2a6-d0ec813dc8fc?diagramType=Topology 18 | dnac = { 19 | "host": "sandboxdnac2.cisco.com", 20 | "username": "devnetuser", 21 | "password": "Cisco123!", 22 | "port": 443, 23 | } 24 | 25 | 26 | headers = {"content-type": "application/json", "x-auth-token": ""} 27 | 28 | 29 | def dnac_login(dnac, port, username, password): 30 | """ 31 | Use the REST API to Log into an DNA Center and retrieve ticket 32 | """ 33 | url = "https://{}:{}/dna/system/api/v1/auth/token".format(dnac, port) 34 | 35 | # Make Login request and return the response body 36 | response = requests.request( 37 | "POST", url, auth=(username, password), headers=headers, verify=False 38 | ) 39 | return response.json()["Token"] 40 | 41 | 42 | def host_list(dnac, ticket, ip=None, mac=None, name=None): 43 | """ 44 | Use the REST API to retrieve the list of hosts. 45 | Optional parameters to filter by: 46 | IP address 47 | MAC address 48 | Hostname 49 | """ 50 | url = "https://{}/api/v1/host".format(dnac) 51 | headers["x-auth-token"] = ticket 52 | filters = [] 53 | 54 | # Add filters if provided 55 | if ip: 56 | filters.append("hostIp={}".format(ip)) 57 | if mac: 58 | filters.append("hostMac={}".format(mac)) 59 | if name: 60 | filters.append("hostName={}".format(name)) 61 | if len(filters) > 0: 62 | url += "?" + "&".join(filters) 63 | 64 | # Make API request and return the response body 65 | response = requests.request("GET", url, headers=headers, verify=False) 66 | return response.json()["response"] 67 | 68 | 69 | def verify_single_host(host, ip): 70 | """ 71 | Simple function to verify only a single host returned from query. 72 | If no hosts, or multiple hosts are returned, an error message is printed 73 | and the program exits. 74 | """ 75 | if len(host) == 0: 76 | print("Error: No host with IP address {} was found".format(ip)) 77 | sys.exit(1) 78 | if len(host) > 1: 79 | print("Error: Multiple hosts with IP address {} were found".format(ip)) 80 | print(json.dumps(host, indent=2)) 81 | sys.exit(1) 82 | 83 | 84 | def print_host_details(host): 85 | """ 86 | Print to screen interesting details about a given host. 87 | Input Paramters are: 88 | host_desc: string to describe this host. Example "Source" 89 | host: dictionary object of a host returned from dnac 90 | Standard Output Details: 91 | Host Name (hostName) - If available 92 | Host IP (hostIp) 93 | Host MAC (hostMac) 94 | Network Type (hostType) - wired/wireless 95 | Host Sub Type (subType) 96 | VLAN (vlanId) 97 | Connected Network Device (connectedNetworkDeviceIpAddress) 98 | 99 | Wired Host Details: 100 | Connected Interface Name (connectedInterfaceName) 101 | 102 | Wireless Host Details: 103 | Connected AP Name (connectedAPName) 104 | """ 105 | # If optional host details missing, add as "Unavailable" 106 | if "hostName" not in host.keys(): 107 | host["hostName"] = "Unavailable" 108 | 109 | # Print Standard Details 110 | print("Host Name: {}".format(host["hostName"])) 111 | print("Network Type: {}".format(host["hostType"])) 112 | print( 113 | "Connected Network Device: {}".format( 114 | host["connectedNetworkDeviceIpAddress"] 115 | ) 116 | ) # noqa: E501 117 | 118 | # Print Wired/Wireless Details 119 | if host["hostType"] == "wired": 120 | print( 121 | "Connected Interface Name: {}".format( 122 | host["connectedInterfaceName"] 123 | ) 124 | ) # noqa: E501 125 | if host["hostType"] == "wireless": 126 | print("Connected AP Name: {}".format(host["connectedAPName"])) 127 | 128 | # Print More Standard Details 129 | print("VLAN: {}".format(host["vlanId"])) 130 | print("Host IP: {}".format(host["hostIp"])) 131 | print("Host MAC: {}".format(host["hostMac"])) 132 | print("Host Sub Type: {}".format(host["subType"])) 133 | 134 | # Blank line at the end 135 | print("") 136 | 137 | 138 | def network_device_list(dnac, ticket, id=None): 139 | """ 140 | Use the REST API to retrieve the list of network devices. 141 | If a device id is provided, return only that device 142 | """ 143 | url = "https://{}/dna/intent/api/v1/network-device".format(dnac) 144 | headers["x-auth-token"] = ticket 145 | 146 | # Change URL to single device given an id 147 | if id: 148 | url += "/{}".format(id) 149 | 150 | # Make API request and return the response body 151 | response = requests.request("GET", url, headers=headers, verify=False) 152 | 153 | # Always return a list object, even if single device for consistency 154 | if id: 155 | return [response.json()["response"]] 156 | 157 | return response.json()["response"] 158 | 159 | 160 | def interface_details(dnac, ticket, id): 161 | """ 162 | Use the REST API to retrieve details about an interface based on id. 163 | """ 164 | url = "https://{}/dna/intent/api/v1/interface/{}".format(dnac, id) 165 | headers["x-auth-token"] = ticket 166 | 167 | response = requests.request("GET", url, headers=headers, verify=False) 168 | return response.json()["response"] 169 | 170 | 171 | def print_network_device_details(network_device): 172 | """ 173 | Print to screen interesting details about a network device. 174 | Input Paramters are: 175 | network_device: dict object of a network device returned from dnac 176 | Standard Output Details: 177 | Device Hostname (hostname) 178 | Management IP (managementIpAddress) 179 | Device Location (locationName) 180 | Device Type (type) 181 | Platform Id (platformId) 182 | Device Role (role) 183 | Serial Number (serialNumber) 184 | Software Version (softwareVersion) 185 | Up Time (upTime) 186 | Reachability Status (reachabilityStatus) 187 | Error Code (errorCode) 188 | Error Description (errorDescription) 189 | """ 190 | 191 | # Print Standard Details 192 | print("Device Hostname: {}".format(network_device["hostname"])) 193 | print("Management IP: {}".format(network_device["managementIpAddress"])) 194 | print("Device Location: {}".format(network_device["locationName"])) 195 | print("Device Type: {}".format(network_device["type"])) 196 | print("Platform Id: {}".format(network_device["platformId"])) 197 | print("Device Role: {}".format(network_device["role"])) 198 | print("Serial Number: {}".format(network_device["serialNumber"])) 199 | print("Software Version: {}".format(network_device["softwareVersion"])) 200 | print("Up Time: {}".format(network_device["upTime"])) 201 | print( 202 | "Reachability Status: {}".format(network_device["reachabilityStatus"]) 203 | ) # noqa: E501 204 | print("Error Code: {}".format(network_device["errorCode"])) 205 | print("Error Description: {}".format(network_device["errorDescription"])) 206 | 207 | # Blank line at the end 208 | print("") 209 | 210 | 211 | def print_interface_details(interface): 212 | """ 213 | Print to screen interesting details about an interface. 214 | Input Paramters are: 215 | interface: dictionary object of an interface returned from dnac 216 | Standard Output Details: 217 | Port Name - (portName) 218 | Interface Type (interfaceType) - Physical/Virtual 219 | Admin Status - (adminStatus) 220 | Operational Status (status) 221 | Media Type - (mediaType) 222 | Speed - (speed) 223 | Duplex Setting (duplex) 224 | Port Mode (portMode) - access/trunk/routed 225 | Interface VLAN - (vlanId) 226 | Voice VLAN - (voiceVlan) 227 | """ 228 | 229 | # Print Standard Details 230 | print("Port Name: {}".format(interface["portName"])) 231 | print("Interface Type: {}".format(interface["interfaceType"])) 232 | print("Admin Status: {}".format(interface["adminStatus"])) 233 | print("Operational Status: {}".format(interface["status"])) 234 | print("Media Type: {}".format(interface["mediaType"])) 235 | print("Speed: {}".format(interface["speed"])) 236 | print("Duplex Setting: {}".format(interface["duplex"])) 237 | print("Port Mode: {}".format(interface["portMode"])) 238 | print("Interface VLAN: {}".format(interface["vlanId"])) 239 | print("Voice VLAN: {}".format(interface["voiceVlan"])) 240 | 241 | # Blank line at the end 242 | print("") 243 | 244 | 245 | # Entry point for program 246 | if __name__ == "__main__": 247 | # Setup Arg Parse for Command Line parameters 248 | import argparse 249 | 250 | parser = argparse.ArgumentParser() 251 | 252 | # Command Line Parameters for Source and Destination IP 253 | parser.add_argument("source_ip", help="Source IP Address") 254 | parser.add_argument("destination_ip", help="Destination IP Address") 255 | args = parser.parse_args() 256 | 257 | # Get Source and Destination IPs from Command Line 258 | source_ip = args.source_ip 259 | destination_ip = args.destination_ip 260 | 261 | # Print Starting message 262 | print("Running Troubleshooting Script for ") 263 | print(" Source IP: {} ".format(source_ip)) 264 | print(" Destination IP: {}".format(destination_ip)) 265 | print("") 266 | 267 | # Log into the dnac Controller to get Ticket 268 | token = dnac_login( 269 | dnac["host"], dnac["port"], dnac["username"], dnac["password"] 270 | ) 271 | 272 | # Step 1: Identify involved hosts 273 | # Retrieve Host Details from dnac 274 | source_host = host_list(dnac["host"], token, ip=source_ip) 275 | destination_host = host_list(dnac["host"], token, ip=destination_ip) 276 | 277 | # Verify single host found for each IP 278 | verify_single_host(source_host, source_ip) 279 | verify_single_host(destination_host, destination_ip) 280 | 281 | # Print Out Host details 282 | print("Source Host Details:") 283 | print("-" * 25) 284 | print_host_details(source_host[0]) 285 | 286 | print("Destination Host Details:") 287 | print("-" * 25) 288 | print_host_details(destination_host[0]) 289 | 290 | # Step 2: Where are they in the network? 291 | # Retrieve and Print Source Device Details from dnac 292 | source_host_net_device = network_device_list( 293 | dnac["host"], token, id=source_host[0]["connectedNetworkDeviceId"] 294 | ) # noqa: E501 295 | print("Source Host Network Connection Details:") 296 | print("-" * 45) 297 | print_network_device_details(source_host_net_device[0]) 298 | # If Host is wired, collect interface details 299 | if source_host[0]["hostType"] == "wired": 300 | source_host_interface = interface_details( 301 | dnac["host"], token, id=source_host[0]["connectedInterfaceId"] 302 | ) # noqa: E501 303 | print("Attached Interface:") 304 | print("-" * 20) 305 | print_interface_details(source_host_interface) 306 | 307 | destination_host_net_device = network_device_list( 308 | dnac["host"], token, id=destination_host[0]["connectedNetworkDeviceId"] 309 | ) # noqa: E501 310 | print("Destination Host Network Connection Details:") 311 | print("-" * 45) 312 | print_network_device_details(destination_host_net_device[0]) 313 | # If Host is wired, collect interface details 314 | if destination_host[0]["hostType"] == "wired": 315 | destination_host_interface = interface_details( 316 | dnac["host"], token, id=destination_host[0]["connectedInterfaceId"] 317 | ) # noqa: E501 318 | print("Attached Interface:") 319 | print("-" * 20) 320 | print_interface_details(destination_host_interface) 321 | -------------------------------------------------------------------------------- /python_code_tips/objects_example/dnac/__init__.py: -------------------------------------------------------------------------------- 1 | """Basic package for interacting with Cisco DNA Center""" 2 | 3 | from .DNAC import DNAC 4 | -------------------------------------------------------------------------------- /python_code_tips/objects_example/host_troubleshooting.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Discover details about Cisco DNA Center connected hosts. 4 | """ 5 | 6 | # Import and functions 7 | from dnac import DNAC 8 | 9 | 10 | # Entry point for program 11 | if __name__ == "__main__": 12 | # Setup Arg Parse for Command Line parameters 13 | import argparse 14 | 15 | parser = argparse.ArgumentParser() 16 | 17 | # Command Line Parameters for Source and Destination IP 18 | parser.add_argument("source_ip", help="Source IP Address") 19 | parser.add_argument("destination_ip", help="Destination IP Address") 20 | parser.add_argument( 21 | "--dnac", help="Address for Cisco DNA Center", required=True 22 | ) 23 | parser.add_argument( 24 | "--port", 25 | help="Override default port of 443 for Cisco DNA Center", 26 | required=False, 27 | default=443, 28 | ) 29 | parser.add_argument( 30 | "--username", help="Username for Cisco DNA Center", required=True 31 | ) 32 | parser.add_argument( 33 | "--password", help="Password for Cisco DNA Center", required=True 34 | ) 35 | args = parser.parse_args() 36 | 37 | # Get Source and Destination IPs from Command Line 38 | source_ip = args.source_ip 39 | destination_ip = args.destination_ip 40 | 41 | # Print Starting message 42 | print("Running Troubleshooting Script for ") 43 | print(" Source IP: {} ".format(source_ip)) 44 | print(" Destination IP: {}".format(destination_ip)) 45 | print("") 46 | 47 | # Initialize Cisco DNA Center Object 48 | dnac = DNAC( 49 | address=args.dnac, 50 | port=args.port, 51 | username=args.username, 52 | password=args.password, 53 | ) 54 | 55 | # Step 1: Identify involved hosts 56 | # Retrieve Host Details from dnac 57 | source_host = dnac.host_list(ip=source_ip) 58 | destination_host = dnac.host_list(ip=destination_ip) 59 | 60 | # Verify single host found for each IP 61 | dnac.verify_single_host(source_host, source_ip) 62 | dnac.verify_single_host(destination_host, destination_ip) 63 | 64 | # Print Out Host details 65 | print("Source Host Details:") 66 | print("-" * 25) 67 | dnac.print_host_details(source_host[0]) 68 | 69 | print("Destination Host Details:") 70 | print("-" * 25) 71 | dnac.print_host_details(destination_host[0]) 72 | 73 | # Step 2: Where are they in the network? 74 | # Retrieve and Print Source Device Details from dnac 75 | source_host_net_device = dnac.network_device_list( 76 | id=source_host[0]["connectedNetworkDeviceId"] 77 | ) # noqa: E501 78 | print("Source Host Network Connection Details:") 79 | print("-" * 45) 80 | dnac.print_network_device_details(source_host_net_device[0]) 81 | # If Host is wired, collect interface details 82 | if source_host[0]["hostType"] == "wired": 83 | source_host_interface = dnac.interface_details( 84 | id=source_host[0]["connectedInterfaceId"] 85 | ) # noqa: E501 86 | print("Attached Interface:") 87 | print("-" * 20) 88 | dnac.print_interface_details(source_host_interface) 89 | 90 | destination_host_net_device = dnac.network_device_list( 91 | id=destination_host[0]["connectedNetworkDeviceId"] 92 | ) # noqa: E501 93 | print("Destination Host Network Connection Details:") 94 | print("-" * 45) 95 | dnac.print_network_device_details(destination_host_net_device[0]) 96 | # If Host is wired, collect interface details 97 | if destination_host[0]["hostType"] == "wired": 98 | destination_host_interface = dnac.interface_details( 99 | id=destination_host[0]["connectedInterfaceId"] 100 | ) # noqa: E501 101 | print("Attached Interface:") 102 | print("-" * 20) 103 | dnac.print_interface_details(destination_host_interface) 104 | -------------------------------------------------------------------------------- /python_code_tips/packages_example/dnac/__init__.py: -------------------------------------------------------------------------------- 1 | """Basic package for interacting with Cisco DNA Center""" 2 | 3 | from .dnac_resources import dnac 4 | from .dnac_functions import ( 5 | dnac_login, 6 | host_list, 7 | verify_single_host, 8 | print_host_details, 9 | network_device_list, 10 | interface_details, 11 | print_network_device_details, 12 | print_interface_details, 13 | ) 14 | -------------------------------------------------------------------------------- /python_code_tips/packages_example/dnac/dnac_resources.py: -------------------------------------------------------------------------------- 1 | # DevNet Always-On Sandbox DNA Center 2 | # https://devnetsandbox.cisco.com/RM/Diagram/Index/471eb739-323e-4805-b2a6-d0ec813dc8fc?diagramType=Topology 3 | dnac = { 4 | "host": "sandboxdnac2.cisco.com", 5 | "username": "devnetuser", 6 | "password": "Cisco123!", 7 | "port": 443, 8 | } 9 | 10 | 11 | headers = {"content-type": "application/json", "x-auth-token": ""} 12 | -------------------------------------------------------------------------------- /python_code_tips/packages_example/host_troubleshooting.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """ 3 | Discover details about Cisco DNA Center connected hosts. 4 | """ 5 | 6 | # Import and functions 7 | from dnac import ( 8 | dnac, 9 | dnac_login, 10 | host_list, 11 | verify_single_host, 12 | print_host_details, 13 | network_device_list, 14 | interface_details, 15 | print_network_device_details, 16 | print_interface_details, 17 | ) 18 | 19 | 20 | # Entry point for program 21 | if __name__ == "__main__": 22 | # Setup Arg Parse for Command Line parameters 23 | import argparse 24 | 25 | parser = argparse.ArgumentParser() 26 | 27 | # Command Line Parameters for Source and Destination IP 28 | parser.add_argument("source_ip", help="Source IP Address") 29 | parser.add_argument("destination_ip", help="Destination IP Address") 30 | args = parser.parse_args() 31 | 32 | # Get Source and Destination IPs from Command Line 33 | source_ip = args.source_ip 34 | destination_ip = args.destination_ip 35 | 36 | # Print Starting message 37 | print("Running Troubleshooting Script for ") 38 | print(" Source IP: {} ".format(source_ip)) 39 | print(" Destination IP: {}".format(destination_ip)) 40 | print("") 41 | 42 | # Log into the dnac Controller to get Ticket 43 | token = dnac_login( 44 | dnac["host"], dnac["port"], dnac["username"], dnac["password"] 45 | ) 46 | 47 | # Step 1: Identify involved hosts 48 | # Retrieve Host Details from dnac 49 | source_host = host_list(dnac["host"], token, ip=source_ip) 50 | destination_host = host_list(dnac["host"], token, ip=destination_ip) 51 | 52 | # Verify single host found for each IP 53 | verify_single_host(source_host, source_ip) 54 | verify_single_host(destination_host, destination_ip) 55 | 56 | # Print Out Host details 57 | print("Source Host Details:") 58 | print("-" * 25) 59 | print_host_details(source_host[0]) 60 | 61 | print("Destination Host Details:") 62 | print("-" * 25) 63 | print_host_details(destination_host[0]) 64 | 65 | # Step 2: Where are they in the network? 66 | # Retrieve and Print Source Device Details from dnac 67 | source_host_net_device = network_device_list( 68 | dnac["host"], token, id=source_host[0]["connectedNetworkDeviceId"] 69 | ) # noqa: E501 70 | print("Source Host Network Connection Details:") 71 | print("-" * 45) 72 | print_network_device_details(source_host_net_device[0]) 73 | # If Host is wired, collect interface details 74 | if source_host[0]["hostType"] == "wired": 75 | source_host_interface = interface_details( 76 | dnac["host"], token, id=source_host[0]["connectedInterfaceId"] 77 | ) # noqa: E501 78 | print("Attached Interface:") 79 | print("-" * 20) 80 | print_interface_details(source_host_interface) 81 | 82 | destination_host_net_device = network_device_list( 83 | dnac["host"], token, id=destination_host[0]["connectedNetworkDeviceId"] 84 | ) # noqa: E501 85 | print("Destination Host Network Connection Details:") 86 | print("-" * 45) 87 | print_network_device_details(destination_host_net_device[0]) 88 | # If Host is wired, collect interface details 89 | if destination_host[0]["hostType"] == "wired": 90 | destination_host_interface = interface_details( 91 | dnac["host"], token, id=destination_host[0]["connectedInterfaceId"] 92 | ) # noqa: E501 93 | print("Attached Interface:") 94 | print("-" * 20) 95 | print_interface_details(destination_host_interface) 96 | -------------------------------------------------------------------------------- /requirements-win.txt: -------------------------------------------------------------------------------- 1 | ipython==6.5.0 2 | napalm==2.3.1 3 | ncclient==0.6.0 4 | netmiko==2.2.2 5 | pyang==1.7.5 6 | pysnmp==4.4.4 7 | pyyaml>=4.2b1 8 | requests>=2.20.0 9 | urllib3==1.23 10 | virlutils==0.8.4 11 | xmltodict==0.11.0 12 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | ansible==2.6.3 2 | black==19.3b0 3 | flake8==3.7.7 4 | genie==19.0.1 5 | ipython==6.5.0 6 | napalm==2.4.0 7 | ncclient==0.6.3 8 | netmiko==2.3.3 9 | pyang==1.7.5 10 | pyats==19.0 11 | PyYAML==5.1 12 | requests==2.21.0 13 | urllib3==1.24.1 14 | virlutils==0.8.4 15 | xmltodict==0.12.0 16 | -------------------------------------------------------------------------------- /setup/ansible.cfg: -------------------------------------------------------------------------------- 1 | # config file for ansible 2 | # override global certain global settings 3 | 4 | [defaults] 5 | # default to inventory file of ./hosts 6 | inventory = ./default_inventory.yaml 7 | # disable host checking to automatically add hosts to known_hosts 8 | host_key_checking = False 9 | # set the roles path to the local directory 10 | roles_path = ./roles/ 11 | 12 | [persistent_connection] 13 | connect_timeout = 300 14 | command_timeout = 300 15 | connect_retry_timeout = 300 16 | -------------------------------------------------------------------------------- /setup/configs/README.md: -------------------------------------------------------------------------------- 1 | Constructed configuration files stored here. 2 | -------------------------------------------------------------------------------- /setup/group_vars/access.yaml: -------------------------------------------------------------------------------- 1 | ansible_network_os: nxos 2 | portchannels: 3 | - port_channel_id: 11 4 | vpc: false 5 | members: 6 | - Ethernet1/1 7 | - Ethernet1/2 8 | -------------------------------------------------------------------------------- /setup/group_vars/all.yaml: -------------------------------------------------------------------------------- 1 | ansible_python_interpreter: /usr/bin/env python 2 | ansible_user: cisco 3 | ansible_ssh_pass: cisco 4 | ansible_become: no 5 | ansible_become_method: enable 6 | ansible_become_pass: cisco 7 | snmp_ro_community: public 8 | snmp_rw_community: private 9 | vlans: 10 | - id: 100 11 | name: Management 12 | gateway: 172.20.100.1 13 | - id: 101 14 | name: Private 15 | gateway: 172.20.101.1 16 | - id: 102 17 | name: Guest 18 | gateway: 172.20.102.1 19 | - id: 103 20 | name: Security 21 | gateway: 172.20.103.1 22 | -------------------------------------------------------------------------------- /setup/group_vars/core.yaml: -------------------------------------------------------------------------------- 1 | ansible_network_os: ios 2 | ospf: 3 | process_id: 1 4 | -------------------------------------------------------------------------------- /setup/group_vars/distribution.yaml: -------------------------------------------------------------------------------- 1 | ansible_network_os: nxos 2 | hsrp: 3 | interfaces: 4 | - interface: vlan100 5 | group: 10 6 | vip: 172.16.100.1 7 | - interface: vlan101 8 | group: 10 9 | vip: 172.16.101.1 10 | - interface: vlan102 11 | group: 10 12 | vip: 172.16.102.1 13 | - interface: vlan103 14 | group: 10 15 | vip: 172.16.103.1 16 | ospf: 17 | process_id: 1 18 | networks: 19 | - interface: Ethernet1/5 20 | area: 0 21 | - interface: Ethernet1/6 22 | area: 0 23 | - interface: vlan100 24 | area: 0 25 | - interface: vlan101 26 | area: 0 27 | - interface: vlan102 28 | area: 0 29 | - interface: vlan103 30 | area: 0 31 | - interface: loopback0 32 | area: 0 33 | vpc: 34 | domain: 100 35 | pkl_vrf: management 36 | port_channel: 37 | id: 1 38 | members: 39 | - Ethernet1/3 40 | - Ethernet1/4 41 | portchannels: 42 | - port_channel_id: 11 43 | vpc: true 44 | members: 45 | - Ethernet1/1 46 | -------------------------------------------------------------------------------- /setup/host_vars/access1.yaml: -------------------------------------------------------------------------------- 1 | provider: 2 | host: "{{ ansible_host }}" 3 | username: cisco 4 | password: cisco 5 | auth_pass: cisco 6 | authorize: no 7 | port: 22 8 | timeout: 15 9 | l3_interfaces: 10 | - interface_type: Loopback 11 | interface_id: 0 12 | description: Default Loopback 13 | ip_address: 192.168.0.2 14 | subnet_mask: 255.255.255.255 15 | prefix: 32 16 | area: 0 17 | -------------------------------------------------------------------------------- /setup/host_vars/core1.yaml: -------------------------------------------------------------------------------- 1 | provider: 2 | host: "{{ ansible_host }}" 3 | username: cisco 4 | password: cisco 5 | auth_pass: cisco 6 | authorize: no 7 | port: 22 8 | timeout: 20 9 | l3_interfaces: 10 | - interface_type: GigabitEthernet 11 | interface_id: 2 12 | description: L3 Link to dist1 13 | ip_address: 172.16.0.1 14 | subnet_mask: 255.255.255.252 15 | prefix: 30 16 | - interface_type: GigabitEthernet 17 | interface_id: 3 18 | description: L3 Link to dist2 19 | ip_address: 172.16.0.5 20 | subnet_mask: 255.255.255.252 21 | prefix: 30 22 | - interface_type: GigabitEthernet 23 | interface_id: 4 24 | description: L3 Link to core2 25 | ip_address: 172.16.0.17 26 | subnet_mask: 255.255.255.252 27 | prefix: 30 28 | - interface_type: Loopback 29 | interface_id: 0 30 | description: Default Loopback 31 | ip_address: 192.168.1.1 32 | subnet_mask: 255.255.255.255 33 | prefix: 32 34 | ospf_router_id: 192.168.1.1 35 | ospf_networks: 36 | - network: 172.16.0.0 37 | mask: 0.0.0.3 38 | area: 0 39 | - network: 172.16.0.4 40 | mask: 0.0.0.3 41 | area: 0 42 | - network: 172.16.0.16 43 | mask: 0.0.0.3 44 | area: 0 45 | - network: 192.168.1.1 46 | mask: 0.0.0.0 47 | area: 0 48 | -------------------------------------------------------------------------------- /setup/host_vars/core2.yaml: -------------------------------------------------------------------------------- 1 | provider: 2 | host: "{{ ansible_host }}" 3 | username: cisco 4 | password: cisco 5 | auth_pass: cisco 6 | authorize: no 7 | port: 22 8 | timeout: 20 9 | l3_interfaces: 10 | - interface_type: GigabitEthernet 11 | interface_id: 2 12 | description: L3 Link to dist2 13 | ip_address: 172.16.0.13 14 | subnet_mask: 255.255.255.252 15 | prefix: 30 16 | - interface_type: GigabitEthernet 17 | interface_id: 3 18 | description: L3 Link to dist1 19 | ip_address: 172.16.0.9 20 | subnet_mask: 255.255.255.252 21 | prefix: 30 22 | - interface_type: GigabitEthernet 23 | interface_id: 4 24 | description: L3 Link to core1 25 | ip_address: 172.16.0.18 26 | subnet_mask: 255.255.255.252 27 | prefix: 30 28 | - interface_type: Loopback 29 | interface_id: 0 30 | description: Default Loopback 31 | ip_address: 192.168.1.2 32 | subnet_mask: 255.255.255.255 33 | prefix: 32 34 | ospf_router_id: 192.168.1.2 35 | ospf_networks: 36 | - network: 172.16.0.12 37 | mask: 0.0.0.3 38 | area: 0 39 | - network: 172.16.0.8 40 | mask: 0.0.0.3 41 | area: 0 42 | - network: 172.16.0.16 43 | mask: 0.0.0.3 44 | area: 0 45 | - network: 192.168.1.2 46 | mask: 0.0.0.0 47 | area: 0 48 | -------------------------------------------------------------------------------- /setup/host_vars/dist1.yaml: -------------------------------------------------------------------------------- 1 | provider: 2 | host: "{{ ansible_host }}" 3 | username: cisco 4 | password: cisco 5 | auth_pass: cisco 6 | authorize: no 7 | port: 22 8 | timeout: 15 9 | ospf_router_id: 192.168.0.1 10 | vpc_pkl_src: 172.16.30.101 11 | vpc_pkl_dest: 172.16.30.102 12 | l3_interfaces: 13 | - interface_type: Loopback 14 | interface_id: 0 15 | description: Default Loopback 16 | ip_address: 192.168.0.1 17 | subnet_mask: 255.255.255.255 18 | prefix: 32 19 | area: 0 20 | - interface_type: Ethernet 21 | interface_id: 1/5 22 | description: L3 Link to core1 23 | ip_address: 172.16.0.2 24 | subnet_mask: 255.255.255.252 25 | prefix: 30 26 | area: 0 27 | - interface_type: Ethernet 28 | interface_id: 1/6 29 | description: L3 Link to core2 30 | ip_address: 172.16.0.10 31 | subnet_mask: 255.255.255.252 32 | prefix: 30 33 | area: 0 34 | - interface_type: vlan 35 | interface_id: 100 36 | description: VLAN Interface - Management 37 | ip_address: 172.16.100.2 38 | subnet_mask: 255.255.255.0 39 | prefix: 24 40 | area: 0 41 | - interface_type: vlan 42 | interface_id: 101 43 | description: VLAN Interface - Private 44 | ip_address: 172.16.101.2 45 | subnet_mask: 255.255.255.0 46 | prefix: 24 47 | area: 0 48 | - interface_type: vlan 49 | interface_id: 102 50 | description: VLAN Interface - Guest 51 | ip_address: 172.16.102.2 52 | subnet_mask: 255.255.255.0 53 | prefix: 24 54 | area: 0 55 | - interface_type: vlan 56 | interface_id: 103 57 | description: VLAN Interface - Security 58 | ip_address: 172.16.103.2 59 | subnet_mask: 255.255.255.0 60 | prefix: 24 61 | area: 0 62 | -------------------------------------------------------------------------------- /setup/host_vars/dist2.yaml: -------------------------------------------------------------------------------- 1 | provider: 2 | host: "{{ ansible_host }}" 3 | username: cisco 4 | password: cisco 5 | auth_pass: cisco 6 | authorize: no 7 | port: 22 8 | timeout: 15 9 | ospf_router_id: 192.168.0.2 10 | vpc_pkl_src: 172.16.30.102 11 | vpc_pkl_dest: 172.16.30.101 12 | l3_interfaces: 13 | - interface_type: Loopback 14 | interface_id: 0 15 | description: Default Loopback 16 | ip_address: 192.168.0.2 17 | subnet_mask: 255.255.255.255 18 | prefix: 32 19 | area: 0 20 | - interface_type: Ethernet 21 | interface_id: 1/5 22 | description: L3 Link to core2 23 | ip_address: 172.16.0.14 24 | subnet_mask: 255.255.255.252 25 | prefix: 30 26 | area: 0 27 | - interface_type: Ethernet 28 | interface_id: 1/6 29 | description: L3 Link to core1 30 | ip_address: 172.16.0.6 31 | subnet_mask: 255.255.255.252 32 | prefix: 30 33 | area: 0 34 | - interface_type: vlan 35 | interface_id: 100 36 | description: VLAN Interface - Management 37 | ip_address: 172.16.100.3 38 | subnet_mask: 255.255.255.0 39 | prefix: 24 40 | area: 0 41 | - interface_type: vlan 42 | interface_id: 101 43 | description: VLAN Interface - Private 44 | ip_address: 172.16.101.3 45 | subnet_mask: 255.255.255.0 46 | prefix: 24 47 | area: 0 48 | - interface_type: vlan 49 | interface_id: 102 50 | description: VLAN Interface - Guest 51 | ip_address: 172.16.102.3 52 | subnet_mask: 255.255.255.0 53 | prefix: 24 54 | area: 0 55 | - interface_type: vlan 56 | interface_id: 103 57 | description: VLAN Interface - Security 58 | ip_address: 172.16.103.3 59 | subnet_mask: 255.255.255.0 60 | prefix: 24 61 | area: 0 62 | -------------------------------------------------------------------------------- /setup/network_deploy.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Wait for all devices to be available 3 | hosts: all 4 | connection: local 5 | gather_facts: false 6 | 7 | tasks: 8 | - wait_for: 9 | port: 22 10 | sleep: 10 11 | 12 | - name: Gather Facts about Network 13 | hosts: all 14 | connection: network_cli 15 | gather_facts: false 16 | 17 | roles: 18 | - network_inspect 19 | 20 | - name: Enable Network APIs 21 | hosts: all 22 | connection: network_cli 23 | gather_facts: false 24 | 25 | roles: 26 | - network_enable_api 27 | 28 | - name: Configure Network Core 29 | hosts: core 30 | connection: local 31 | gather_facts: false 32 | 33 | roles: 34 | - network_interface 35 | - network_ospf 36 | 37 | - name: Configure Distribution Switches 38 | hosts: distribution 39 | connection: network_cli 40 | gather_facts: false 41 | 42 | roles: 43 | - network_vlan 44 | - network_vpc 45 | - network_interface 46 | - network_ospf 47 | 48 | - name: Configure Access Switches 49 | hosts: access 50 | connection: network_cli 51 | gather_facts: false 52 | 53 | roles: 54 | - network_vlan 55 | - network_interface 56 | -------------------------------------------------------------------------------- /setup/roles/network_enable_api/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 20 | 21 | Example Playbook 22 | ---------------- 23 | 24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 25 | 26 | - hosts: servers 27 | roles: 28 | - { role: username.rolename, x: 42 } 29 | 30 | License 31 | ------- 32 | 33 | BSD 34 | 35 | Author Information 36 | ------------------ 37 | 38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 39 | -------------------------------------------------------------------------------- /setup/roles/network_enable_api/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for network_enable_api -------------------------------------------------------------------------------- /setup/roles/network_enable_api/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for network_enable_api -------------------------------------------------------------------------------- /setup/roles/network_enable_api/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: your name 3 | description: your description 4 | company: your company (optional) 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 1.2 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # Optionally specify the branch Galaxy will use when accessing the GitHub 25 | # repo for this role. During role install, if no tags are available, 26 | # Galaxy will use this branch. During import Galaxy will access files on 27 | # this branch. If Travis integration is configured, only notifications for this 28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 29 | # (usually master) will be used. 30 | #github_branch: 31 | 32 | # 33 | # platforms is a list of platforms, and each platform has a name and a list of versions. 34 | # 35 | # platforms: 36 | # - name: Fedora 37 | # versions: 38 | # - all 39 | # - 25 40 | # - name: SomePlatform 41 | # versions: 42 | # - all 43 | # - 1.0 44 | # - 7 45 | # - 99.99 46 | 47 | galaxy_tags: [] 48 | # List tags for your role here, one per line. A tag is a keyword that describes 49 | # and categorizes the role. Users find roles by searching for tags. Be sure to 50 | # remove the '[]' above, if you add tags to this list. 51 | # 52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 53 | # Maximum 20 tags per role. 54 | 55 | dependencies: [] 56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 57 | # if you add dependencies to this list. -------------------------------------------------------------------------------- /setup/roles/network_enable_api/tasks/ios.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure NETCONF 3 | tags: [api, netconf] 4 | ios_config: 5 | provider: "{{provider}}" 6 | lines: 7 | - netconf-yang 8 | - netconf-yang cisco-odm polling-enable 9 | - restconf 10 | - ip http server 11 | - ip http secure-server 12 | 13 | - name: Configure SNMP 14 | tags: [api] 15 | ios_config: 16 | provider: "{{provider}}" 17 | lines: 18 | - snmp-server community {{ snmp_ro_community | default('public') }} ro 19 | - snmp-server community {{ snmp_rw_community | default('private') }} rw 20 | 21 | - name: Pause for NETCONF to start 22 | pause: 23 | seconds: 45 24 | -------------------------------------------------------------------------------- /setup/roles/network_enable_api/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for network_enable_api 3 | - name: IOS Devices 4 | include_tasks: ios.yml 5 | when: ansible_network_os == "ios" 6 | - name: NX-OS Devices 7 | include_tasks: nxos.yml 8 | when: ansible_network_os == "nxos" 9 | -------------------------------------------------------------------------------- /setup/roles/network_enable_api/tasks/nxos.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable NX-API 3 | tags: [api, nxapi] 4 | nxos_feature: 5 | provider: "{{provider}}" 6 | feature: nxapi 7 | state: enabled 8 | 9 | - name: Enable SNMP 10 | tags: [api] 11 | nxos_config: 12 | provider: "{{provider}}" 13 | lines: 14 | - snmp-server community {{ snmp_ro_community | default('public') }} ro 15 | - snmp-server community {{ snmp_rw_community | default('private') }} rw 16 | 17 | -------------------------------------------------------------------------------- /setup/roles/network_enable_api/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /setup/roles/network_enable_api/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - network_enable_api -------------------------------------------------------------------------------- /setup/roles/network_enable_api/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for network_enable_api -------------------------------------------------------------------------------- /setup/roles/network_inspect/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 20 | 21 | Example Playbook 22 | ---------------- 23 | 24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 25 | 26 | - hosts: servers 27 | roles: 28 | - { role: username.rolename, x: 42 } 29 | 30 | License 31 | ------- 32 | 33 | BSD 34 | 35 | Author Information 36 | ------------------ 37 | 38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 39 | -------------------------------------------------------------------------------- /setup/roles/network_inspect/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for network_inspect -------------------------------------------------------------------------------- /setup/roles/network_inspect/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for network_inspect -------------------------------------------------------------------------------- /setup/roles/network_inspect/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: your name 3 | description: your description 4 | company: your company (optional) 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 1.2 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # Optionally specify the branch Galaxy will use when accessing the GitHub 25 | # repo for this role. During role install, if no tags are available, 26 | # Galaxy will use this branch. During import Galaxy will access files on 27 | # this branch. If Travis integration is configured, only notifications for this 28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 29 | # (usually master) will be used. 30 | #github_branch: 31 | 32 | # 33 | # platforms is a list of platforms, and each platform has a name and a list of versions. 34 | # 35 | # platforms: 36 | # - name: Fedora 37 | # versions: 38 | # - all 39 | # - 25 40 | # - name: SomePlatform 41 | # versions: 42 | # - all 43 | # - 1.0 44 | # - 7 45 | # - 99.99 46 | 47 | galaxy_tags: [] 48 | # List tags for your role here, one per line. A tag is a keyword that describes 49 | # and categorizes the role. Users find roles by searching for tags. Be sure to 50 | # remove the '[]' above, if you add tags to this list. 51 | # 52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 53 | # Maximum 20 tags per role. 54 | 55 | dependencies: [] 56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 57 | # if you add dependencies to this list. -------------------------------------------------------------------------------- /setup/roles/network_inspect/tasks/ios.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Gather IOS Facts 3 | ios_facts: 4 | provider: "{{ provider }}" 5 | gather_subset: all 6 | -------------------------------------------------------------------------------- /setup/roles/network_inspect/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for network_inspect 3 | - name: Inspect IOS 4 | include_tasks: ios.yml 5 | when: ansible_network_os == "ios" 6 | - name: Inspect NX-OS 7 | include_tasks: nxos.yml 8 | when: ansible_network_os == "nxos" 9 | 10 | - name: Device Info 11 | debug: 12 | msg: "Device Type: {{ ansible_network_os }} OS Version: {{ ansible_net_version }}" 13 | -------------------------------------------------------------------------------- /setup/roles/network_inspect/tasks/nxos.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Gather NX-OS Facts 3 | nxos_facts: 4 | provider: "{{ provider }}" 5 | gather_subset: all 6 | -------------------------------------------------------------------------------- /setup/roles/network_inspect/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /setup/roles/network_inspect/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - network_inspect -------------------------------------------------------------------------------- /setup/roles/network_inspect/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for network_inspect -------------------------------------------------------------------------------- /setup/roles/network_interface/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 20 | 21 | Example Playbook 22 | ---------------- 23 | 24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 25 | 26 | - hosts: servers 27 | roles: 28 | - { role: username.rolename, x: 42 } 29 | 30 | License 31 | ------- 32 | 33 | BSD 34 | 35 | Author Information 36 | ------------------ 37 | 38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 39 | -------------------------------------------------------------------------------- /setup/roles/network_interface/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for network_interface -------------------------------------------------------------------------------- /setup/roles/network_interface/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for network_interface -------------------------------------------------------------------------------- /setup/roles/network_interface/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: your name 3 | description: your description 4 | company: your company (optional) 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 1.2 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # Optionally specify the branch Galaxy will use when accessing the GitHub 25 | # repo for this role. During role install, if no tags are available, 26 | # Galaxy will use this branch. During import Galaxy will access files on 27 | # this branch. If Travis integration is configured, only notifications for this 28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 29 | # (usually master) will be used. 30 | #github_branch: 31 | 32 | # 33 | # platforms is a list of platforms, and each platform has a name and a list of versions. 34 | # 35 | # platforms: 36 | # - name: Fedora 37 | # versions: 38 | # - all 39 | # - 25 40 | # - name: SomePlatform 41 | # versions: 42 | # - all 43 | # - 1.0 44 | # - 7 45 | # - 99.99 46 | 47 | galaxy_tags: [] 48 | # List tags for your role here, one per line. A tag is a keyword that describes 49 | # and categorizes the role. Users find roles by searching for tags. Be sure to 50 | # remove the '[]' above, if you add tags to this list. 51 | # 52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 53 | # Maximum 20 tags per role. 54 | 55 | dependencies: [] 56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 57 | # if you add dependencies to this list. -------------------------------------------------------------------------------- /setup/roles/network_interface/tasks/hsrp_nxapi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable Features 3 | tags: [api, nxapi, hsrp] 4 | with_items: 5 | - hsrp 6 | nxos_feature: 7 | provider: "{{provider}}" 8 | feature: "{{ item }}" 9 | state: enabled 10 | 11 | - name: Configure HSRP 12 | tags: [api, nxapi, hsrp] 13 | with_items: "{{ hsrp.interfaces }}" 14 | nxos_hsrp: 15 | provider: "{{provider}}" 16 | group: "{{ item.group }}" 17 | vip: "{{ item.vip }}" 18 | interface: "{{ item.interface }}" 19 | -------------------------------------------------------------------------------- /setup/roles/network_interface/tasks/l3_netconf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Generate NETCONF Interface config" 3 | tags: [api, netconf, layer3] 4 | with_items: "{{l3_interfaces}}" 5 | template: 6 | src: "templates/ietf_interface_template.j2" 7 | dest: "./configs/{{inventory_hostname}}-{{item.interface_type}}{{item.interface_id}}.xml" 8 | 9 | - name: Configure Interfaces with NETCONF 10 | tags: [api, netconf, layer3] 11 | with_items: "{{l3_interfaces}}" 12 | netconf_config: 13 | host: "{{provider.host}}" 14 | hostkey_verify: false 15 | username: "{{provider.username}}" 16 | password: "{{provider.password}}" 17 | port: "{{netconf_port | default(830)}}" 18 | src: "./configs/{{inventory_hostname}}-{{item.interface_type}}{{item.interface_id}}.xml" 19 | -------------------------------------------------------------------------------- /setup/roles/network_interface/tasks/l3_nxapi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable Features 3 | tags: [api, nxapi, layer3] 4 | with_items: 5 | - interface-vlan 6 | nxos_feature: 7 | provider: "{{provider}}" 8 | feature: "{{ item }}" 9 | state: enabled 10 | 11 | - name: Configure Layer 3 Interfaces 12 | tags: [api, nxapi, layer3] 13 | with_items: "{{ l3_interfaces }}" 14 | nxos_interface: 15 | provider: "{{provider}}" 16 | interface: "{{ item.interface_type }}{{ item.interface_id }}" 17 | # mode: layer3 18 | description: "{{ item.description }}" 19 | admin_state: up 20 | 21 | - name: Ethernet and PortChannel Enable Layer 3 22 | tags: [api, nxapi, layer3] 23 | with_items: "{{ l3_interfaces }}" 24 | when: item.interface_type in ["Ethernet", "PortChannel"] 25 | nxos_interface: 26 | provider: "{{provider}}" 27 | interface: "{{ item.interface_type }}{{ item.interface_id }}" 28 | mode: layer3 29 | 30 | 31 | - name: Configure IPv4 Address on Interface 32 | tags: [api, nxapi, layer3] 33 | with_items: "{{ l3_interfaces }}" 34 | nxos_ip_interface: 35 | provider: "{{provider}}" 36 | interface: "{{ item.interface_type }}{{ item.interface_id }}" 37 | version: v4 38 | addr: "{{ item.ip_address }}" 39 | mask: "{{ item.prefix }}" 40 | -------------------------------------------------------------------------------- /setup/roles/network_interface/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for network_interface 3 | - name: IOS Devices L3 Interfaces 4 | include_tasks: l3_netconf.yml 5 | when: ansible_network_os == "ios" and l3_interfaces is defined 6 | - name: NX-OS Devices L3 Interfaces 7 | include_tasks: l3_nxapi.yml 8 | when: ansible_network_os == "nxos" and l3_interfaces is defined 9 | - name: NX-OS Devices HSRP Configuration 10 | include_tasks: hsrp_nxapi.yml 11 | when: ansible_network_os == "nxos" and hsrp is defined 12 | - name: NX-OS Devices PortChannels 13 | include_tasks: portchannel_nxapi.yml 14 | when: ansible_network_os == "nxos" and portchannels is defined 15 | -------------------------------------------------------------------------------- /setup/roles/network_interface/tasks/portchannel_nxapi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable Features 3 | tags: [api, nxapi, vpc, vlan, trunks] 4 | with_items: 5 | # - vpc 6 | - lacp 7 | nxos_feature: 8 | provider: "{{provider}}" 9 | feature: "{{ item }}" 10 | state: enabled 11 | 12 | - name: Create Port Channels 13 | tags: [api, nxapi, vpc, vlan, trunks] 14 | with_items: "{{ portchannels }}" 15 | nxos_portchannel: 16 | provider: "{{provider}}" 17 | group: "{{ item.port_channel_id }}" 18 | members: "{{ item.members }}" 19 | force: true 20 | mode: on 21 | state: present 22 | 23 | - name: Configure Access Switch Port Channels as Trunk 24 | tags: [api, nxapi, vpc, vlan, trunks] 25 | with_items: "{{ portchannels }}" 26 | nxos_switchport: 27 | provider: "{{provider}}" 28 | interface: "po{{ item.port_channel_id }}" 29 | mode: trunk 30 | 31 | - name: Enable Port Channel as VPC 32 | tags: [api, nxapi, vpc, vlan, trunks] 33 | with_items: "{{ portchannels }}" 34 | when: item.vpc == true 35 | nxos_vpc_interface: 36 | provider: "{{provider}}" 37 | portchannel: "{{ item.port_channel_id }}" 38 | vpc: "{{ item.port_channel_id }}" 39 | -------------------------------------------------------------------------------- /setup/roles/network_interface/templates/ietf_interface_template.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{item.interface_type}}{{item.interface_id}} 5 | {{item.description}} 6 | {% if item.interface_type == "Loopback" %} 7 | ianaift:softwareLoopback 8 | {% else %} 9 | ianaift:ethernetCsmacd 10 | {% endif %} 11 | true 12 | 13 |
14 | {{item.ip_address}} 15 | {{item.subnet_mask}} 16 |
17 |
18 |
19 |
20 |
21 | -------------------------------------------------------------------------------- /setup/roles/network_interface/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /setup/roles/network_interface/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - network_interface -------------------------------------------------------------------------------- /setup/roles/network_interface/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for network_interface -------------------------------------------------------------------------------- /setup/roles/network_ospf/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 20 | 21 | Example Playbook 22 | ---------------- 23 | 24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 25 | 26 | - hosts: servers 27 | roles: 28 | - { role: username.rolename, x: 42 } 29 | 30 | License 31 | ------- 32 | 33 | BSD 34 | 35 | Author Information 36 | ------------------ 37 | 38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 39 | -------------------------------------------------------------------------------- /setup/roles/network_ospf/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for network_ospf -------------------------------------------------------------------------------- /setup/roles/network_ospf/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for network_ospf -------------------------------------------------------------------------------- /setup/roles/network_ospf/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: your name 3 | description: your description 4 | company: your company (optional) 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 1.2 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # Optionally specify the branch Galaxy will use when accessing the GitHub 25 | # repo for this role. During role install, if no tags are available, 26 | # Galaxy will use this branch. During import Galaxy will access files on 27 | # this branch. If Travis integration is configured, only notifications for this 28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 29 | # (usually master) will be used. 30 | #github_branch: 31 | 32 | # 33 | # platforms is a list of platforms, and each platform has a name and a list of versions. 34 | # 35 | # platforms: 36 | # - name: Fedora 37 | # versions: 38 | # - all 39 | # - 25 40 | # - name: SomePlatform 41 | # versions: 42 | # - all 43 | # - 1.0 44 | # - 7 45 | # - 99.99 46 | 47 | galaxy_tags: [] 48 | # List tags for your role here, one per line. A tag is a keyword that describes 49 | # and categorizes the role. Users find roles by searching for tags. Be sure to 50 | # remove the '[]' above, if you add tags to this list. 51 | # 52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 53 | # Maximum 20 tags per role. 54 | 55 | dependencies: [] 56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 57 | # if you add dependencies to this list. -------------------------------------------------------------------------------- /setup/roles/network_ospf/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for network_ospf 3 | - name: IOS Devices 4 | include_tasks: netconf.yml 5 | when: ansible_network_os == "ios" and ospf is defined 6 | - name: NX-OS Devices 7 | include_tasks: nxapi.yml 8 | when: ansible_network_os == "nxos" and ospf is defined 9 | -------------------------------------------------------------------------------- /setup/roles/network_ospf/tasks/netconf.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: "Generate NETCONF OSPF config" 3 | tags: [api, netconf, ospf] 4 | when: ansible_network_os == "ios" 5 | template: 6 | src: "templates/Cisco-IOS-XE_ospf.j2" 7 | dest: "./configs/{{inventory_hostname}}-ospf.xml" 8 | 9 | - name: Configure OSPF with NETCONF 10 | tags: [api, netconf, ospf] 11 | netconf_config: 12 | host: "{{provider.host}}" 13 | hostkey_verify: false 14 | username: "{{provider.username}}" 15 | password: "{{provider.password}}" 16 | port: "{{netconf_port | default(830)}}" 17 | src: "./configs/{{inventory_hostname}}-ospf.xml" 18 | -------------------------------------------------------------------------------- /setup/roles/network_ospf/tasks/nxapi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable Features 3 | tags: [api, nxapi, ospf] 4 | with_items: 5 | - ospf 6 | nxos_feature: 7 | provider: "{{provider}}" 8 | feature: "{{ item }}" 9 | state: enabled 10 | 11 | - name: Configure OSPF 12 | tags: [api, nxapi, ospf] 13 | nxos_ospf: 14 | provider: "{{provider}}" 15 | ospf: "{{ ospf.process_id }}" 16 | state: present 17 | 18 | - name: Configure OSPF Interfaces 19 | tags: [api, nxapi, ospf] 20 | with_items: "{{ ospf.networks }}" 21 | nxos_interface_ospf: 22 | provider: "{{provider}}" 23 | interface: "{{ item.interface }}" 24 | ospf: "{{ ospf.process_id }}" 25 | area: "{{ item.area }}" 26 | -------------------------------------------------------------------------------- /setup/roles/network_ospf/templates/Cisco-IOS-XE_ospf.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ ospf.process_id }} 6 | {{ ospf_router_id }} 7 | {% for network in ospf_networks %} 8 | 9 | {{ network.network }} 10 | {{ network.mask }} 11 | {{ network.area }} 12 | 13 | {% endfor %} 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /setup/roles/network_ospf/templates/ned_ospf.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {{ ospf.process_id }} 6 | {{ ospf_router_id }} 7 | {% for network in ospf_networks %} 8 | 9 | {{ network.network }} 10 | {{ network.mask }} 11 | {{ network.area }} 12 | 13 | {% endfor %} 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /setup/roles/network_ospf/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /setup/roles/network_ospf/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - network_ospf -------------------------------------------------------------------------------- /setup/roles/network_ospf/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for network_ospf -------------------------------------------------------------------------------- /setup/roles/network_vlan/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 20 | 21 | Example Playbook 22 | ---------------- 23 | 24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 25 | 26 | - hosts: servers 27 | roles: 28 | - { role: username.rolename, x: 42 } 29 | 30 | License 31 | ------- 32 | 33 | BSD 34 | 35 | Author Information 36 | ------------------ 37 | 38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 39 | -------------------------------------------------------------------------------- /setup/roles/network_vlan/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for network_vlan -------------------------------------------------------------------------------- /setup/roles/network_vlan/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for network_vlan -------------------------------------------------------------------------------- /setup/roles/network_vlan/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: your name 3 | description: your description 4 | company: your company (optional) 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 1.2 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # Optionally specify the branch Galaxy will use when accessing the GitHub 25 | # repo for this role. During role install, if no tags are available, 26 | # Galaxy will use this branch. During import Galaxy will access files on 27 | # this branch. If Travis integration is configured, only notifications for this 28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 29 | # (usually master) will be used. 30 | #github_branch: 31 | 32 | # 33 | # platforms is a list of platforms, and each platform has a name and a list of versions. 34 | # 35 | # platforms: 36 | # - name: Fedora 37 | # versions: 38 | # - all 39 | # - 25 40 | # - name: SomePlatform 41 | # versions: 42 | # - all 43 | # - 1.0 44 | # - 7 45 | # - 99.99 46 | 47 | galaxy_tags: [] 48 | # List tags for your role here, one per line. A tag is a keyword that describes 49 | # and categorizes the role. Users find roles by searching for tags. Be sure to 50 | # remove the '[]' above, if you add tags to this list. 51 | # 52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 53 | # Maximum 20 tags per role. 54 | 55 | dependencies: [] 56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 57 | # if you add dependencies to this list. -------------------------------------------------------------------------------- /setup/roles/network_vlan/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for network_vlan 3 | # - name: IOS Devices 4 | # include_tasks: netconf.yml 5 | # when: device_type == "ios" and ospf is defined 6 | - name: NX-OS Devices 7 | include_tasks: nxapi.yml 8 | when: ansible_network_os == "nxos" and vlans is defined 9 | -------------------------------------------------------------------------------- /setup/roles/network_vlan/tasks/nxapi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure VLANs 3 | tags: [api, nxapi, vlan] 4 | with_items: "{{ vlans }}" 5 | nxos_vlan: 6 | provider: "{{provider}}" 7 | vlan_id: "{{ item.id }}" 8 | name: "{{ item.name }}" 9 | -------------------------------------------------------------------------------- /setup/roles/network_vlan/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /setup/roles/network_vlan/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - network_vlan -------------------------------------------------------------------------------- /setup/roles/network_vlan/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for network_vlan -------------------------------------------------------------------------------- /setup/roles/network_vpc/README.md: -------------------------------------------------------------------------------- 1 | Role Name 2 | ========= 3 | 4 | A brief description of the role goes here. 5 | 6 | Requirements 7 | ------------ 8 | 9 | Any pre-requisites that may not be covered by Ansible itself or the role should be mentioned here. For instance, if the role uses the EC2 module, it may be a good idea to mention in this section that the boto package is required. 10 | 11 | Role Variables 12 | -------------- 13 | 14 | A description of the settable variables for this role should go here, including any variables that are in defaults/main.yml, vars/main.yml, and any variables that can/should be set via parameters to the role. Any variables that are read from other roles and/or the global scope (ie. hostvars, group vars, etc.) should be mentioned here as well. 15 | 16 | Dependencies 17 | ------------ 18 | 19 | A list of other roles hosted on Galaxy should go here, plus any details in regards to parameters that may need to be set for other roles, or variables that are used from other roles. 20 | 21 | Example Playbook 22 | ---------------- 23 | 24 | Including an example of how to use your role (for instance, with variables passed in as parameters) is always nice for users too: 25 | 26 | - hosts: servers 27 | roles: 28 | - { role: username.rolename, x: 42 } 29 | 30 | License 31 | ------- 32 | 33 | BSD 34 | 35 | Author Information 36 | ------------------ 37 | 38 | An optional section for the role authors to include contact information, or a website (HTML is not allowed). 39 | -------------------------------------------------------------------------------- /setup/roles/network_vpc/defaults/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # defaults file for network_vpc -------------------------------------------------------------------------------- /setup/roles/network_vpc/handlers/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # handlers file for network_vpc -------------------------------------------------------------------------------- /setup/roles/network_vpc/meta/main.yml: -------------------------------------------------------------------------------- 1 | galaxy_info: 2 | author: your name 3 | description: your description 4 | company: your company (optional) 5 | 6 | # If the issue tracker for your role is not on github, uncomment the 7 | # next line and provide a value 8 | # issue_tracker_url: http://example.com/issue/tracker 9 | 10 | # Some suggested licenses: 11 | # - BSD (default) 12 | # - MIT 13 | # - GPLv2 14 | # - GPLv3 15 | # - Apache 16 | # - CC-BY 17 | license: license (GPLv2, CC-BY, etc) 18 | 19 | min_ansible_version: 1.2 20 | 21 | # If this a Container Enabled role, provide the minimum Ansible Container version. 22 | # min_ansible_container_version: 23 | 24 | # Optionally specify the branch Galaxy will use when accessing the GitHub 25 | # repo for this role. During role install, if no tags are available, 26 | # Galaxy will use this branch. During import Galaxy will access files on 27 | # this branch. If Travis integration is configured, only notifications for this 28 | # branch will be accepted. Otherwise, in all cases, the repo's default branch 29 | # (usually master) will be used. 30 | #github_branch: 31 | 32 | # 33 | # platforms is a list of platforms, and each platform has a name and a list of versions. 34 | # 35 | # platforms: 36 | # - name: Fedora 37 | # versions: 38 | # - all 39 | # - 25 40 | # - name: SomePlatform 41 | # versions: 42 | # - all 43 | # - 1.0 44 | # - 7 45 | # - 99.99 46 | 47 | galaxy_tags: [] 48 | # List tags for your role here, one per line. A tag is a keyword that describes 49 | # and categorizes the role. Users find roles by searching for tags. Be sure to 50 | # remove the '[]' above, if you add tags to this list. 51 | # 52 | # NOTE: A tag is limited to a single word comprised of alphanumeric characters. 53 | # Maximum 20 tags per role. 54 | 55 | dependencies: [] 56 | # List your role dependencies here, one per line. Be sure to remove the '[]' above, 57 | # if you add dependencies to this list. -------------------------------------------------------------------------------- /setup/roles/network_vpc/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # tasks file for network_vpc 3 | # - name: IOS Devices 4 | # include_tasks: netconf.yml 5 | # when: device_type == "ios" and ospf is defined 6 | - name: NX-OS Devices 7 | include_tasks: nxapi.yml 8 | when: ansible_network_os == "nxos" and vpc is defined 9 | -------------------------------------------------------------------------------- /setup/roles/network_vpc/tasks/nxapi.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable Features 3 | tags: [api, nxapi, vpc] 4 | with_items: 5 | - vpc 6 | - lacp 7 | nxos_feature: 8 | provider: "{{provider}}" 9 | feature: "{{ item }}" 10 | state: enabled 11 | 12 | - name: Configure VPC 13 | tags: [api, nxapi, vpc] 14 | nxos_vpc: 15 | provider: "{{provider}}" 16 | domain: "{{ vpc.domain }}" 17 | pkl_src: "{{ vpc_pkl_src }}" 18 | pkl_dest: "{{ vpc_pkl_dest }}" 19 | pkl_vrf: "{{ vpc.pkl_vrf }}" 20 | peer_gw: true 21 | auto_recovery: true 22 | 23 | - name: Create Port Channel for VPC 24 | tags: [api, nxapi, vpc] 25 | nxos_portchannel: 26 | provider: "{{provider}}" 27 | group: "{{ vpc.port_channel.id }}" 28 | members: "{{ vpc.port_channel.members }}" 29 | force: true 30 | mode: on 31 | state: present 32 | 33 | - name: Configure VPC Port Channel Trunk 34 | tags: [api, nxapi, vpc] 35 | nxos_switchport: 36 | provider: "{{provider}}" 37 | interface: "po{{ vpc.port_channel.id }}" 38 | mode: trunk 39 | 40 | - name: Enable VPC Peer Link on port_channel 41 | tags: [api, nxapi, vpc] 42 | nxos_vpc_interface: 43 | provider: "{{provider}}" 44 | portchannel: "{{ vpc.port_channel.id }}" 45 | peer_link: true 46 | -------------------------------------------------------------------------------- /setup/roles/network_vpc/tests/inventory: -------------------------------------------------------------------------------- 1 | localhost 2 | 3 | -------------------------------------------------------------------------------- /setup/roles/network_vpc/tests/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: localhost 3 | remote_user: root 4 | roles: 5 | - network_vpc -------------------------------------------------------------------------------- /setup/roles/network_vpc/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # vars file for network_vpc -------------------------------------------------------------------------------- /topology.virl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | flat 6 | 7 | 8 | 9 | aa:aa:aa:aa:aa:aa 10 | 11 | false 12 | 13 | distribution 14 | power redundancy-mode combined force 15 | license grace-period 16 | 17 | hostname dist1 18 | 19 | feature telnet 20 | feature nxapi 21 | feature bash-shell 22 | feature scp-server 23 | 24 | no password strength-check 25 | username admin password 5 $1$KuOSBsvW$Cy0TSD..gEBGBPjzpDgf51 role network-admin 26 | username cisco password 5 $1$Nk7ZkwH0$fyiRmMMfIheqE3BqvcL0C1 role network-admin 27 | no ip domain-lookup 28 | 29 | vrf context management 30 | ip route 0.0.0.0/0 {{ gateway }} 31 | hardware forwarding unicast trace 32 | 33 | interface mgmt0 34 | description OOB Management 35 | ! Configured on launch 36 | no ip address 37 | mac-address fa16.3e01.0007 38 | no shutdown 39 | vrf member management 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | aa:aa:aa:aa:aa:ac 53 | 54 | false 55 | 56 | access 57 | power redundancy-mode combined force 58 | 59 | license grace-period 60 | 61 | hostname access1 62 | 63 | feature telnet 64 | feature nxapi 65 | feature bash-shell 66 | feature scp-server 67 | 68 | no password strength-check 69 | username admin password 5 $1$KuOSBsvW$Cy0TSD..gEBGBPjzpDgf51 role network-admin 70 | username cisco password 5 $1$Nk7ZkwH0$fyiRmMMfIheqE3BqvcL0C1 role network-admin 71 | no ip domain-lookup 72 | 73 | vrf context management 74 | ip route 0.0.0.0/0 {{ gateway }} 75 | hardware forwarding unicast trace 76 | 77 | interface mgmt0 78 | description OOB Management 79 | ! Configured on launch 80 | no ip address 81 | mac-address fa16.3e03.0007 82 | no shutdown 83 | vrf member management 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | aa:aa:aa:aa:aa:ab 93 | 94 | distribution 95 | 96 | false 97 | power redundancy-mode combined force 98 | 99 | license grace-period 100 | 101 | hostname dist2 102 | 103 | feature telnet 104 | feature nxapi 105 | feature bash-shell 106 | feature scp-server 107 | 108 | no password strength-check 109 | username admin password 5 $1$KuOSBsvW$Cy0TSD..gEBGBPjzpDgf51 role network-admin 110 | username cisco password 5 $1$Nk7ZkwH0$fyiRmMMfIheqE3BqvcL0C1 role network-admin 111 | no ip domain-lookup 112 | 113 | vrf context management 114 | ip route 0.0.0.0/0 {{ gateway }} 115 | hardware forwarding unicast trace 116 | 117 | interface mgmt0 118 | description OOB Management 119 | ! Configured on launch 120 | no ip address 121 | mac-address fa16.3e02.0007 122 | no shutdown 123 | vrf member management 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | core 137 | 138 | false 139 | ! IOS Config generated on 2017-10-04 14:46 140 | hostname core1 141 | boot-start-marker 142 | boot-end-marker 143 | ! 144 | vrf definition Mgmt-intf 145 | ! 146 | address-family ipv4 147 | exit-address-family 148 | ! 149 | address-family ipv6 150 | exit-address-family 151 | ! 152 | ! 153 | ! 154 | license accept end user agreement 155 | license boot level premium 156 | ! 157 | ! 158 | no aaa new-model 159 | ! 160 | ! 161 | ipv6 unicast-routing 162 | ! 163 | ! 164 | service timestamps debug datetime msec 165 | service timestamps log datetime msec 166 | no service password-encryption 167 | no service config 168 | enable password cisco 169 | enable secret 4 tnhtc92DXBhelxjYk8LWJrPV36S2i4ntXrpb4RFmfqY 170 | ip classless 171 | ip subnet-zero 172 | no ip domain lookup 173 | crypto key generate rsa modulus 1024 174 | ip ssh server algorithm authentication password 175 | username cisco privilege 15 secret cisco 176 | line vty 0 4 177 | transport input ssh telnet 178 | exec-timeout 720 0 179 | password cisco 180 | login local 181 | line con 0 182 | password cisco 183 | ! 184 | no cdp run 185 | ! 186 | ! 187 | interface GigabitEthernet1 188 | description OOB Management 189 | vrf forwarding Mgmt-intf 190 | ! Configured on launch 191 | no ip address 192 | cdp enable 193 | no shutdown 194 | 195 | ip route vrf Mgmt-intf 0.0.0.0 0.0.0.0 {{ gateway }} 196 | ! 197 | interface GigabitEthernet2 198 | description to dist1 199 | no shutdown 200 | ! 201 | interface GigabitEthernet3 202 | description to dist2 203 | no shutdown 204 | ! 205 | interface GigabitEthernet4 206 | description core2 207 | no ip address 208 | no shutdown 209 | ! 210 | interface Loopback 0 211 | ! 212 | ! 213 | ! 214 | end 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | core 224 | 225 | false 226 | ! IOS Config generated on 2017-10-04 14:46 227 | hostname core2 228 | boot-start-marker 229 | boot-end-marker 230 | ! 231 | vrf definition Mgmt-intf 232 | ! 233 | address-family ipv4 234 | exit-address-family 235 | ! 236 | address-family ipv6 237 | exit-address-family 238 | ! 239 | ! 240 | ! 241 | license accept end user agreement 242 | license boot level premium 243 | ! 244 | ! 245 | no aaa new-model 246 | ! 247 | ! 248 | ipv6 unicast-routing 249 | ! 250 | ! 251 | service timestamps debug datetime msec 252 | service timestamps log datetime msec 253 | no service password-encryption 254 | no service config 255 | enable password cisco 256 | enable secret 4 tnhtc92DXBhelxjYk8LWJrPV36S2i4ntXrpb4RFmfqY 257 | ip classless 258 | ip subnet-zero 259 | no ip domain lookup 260 | crypto key generate rsa modulus 1024 261 | ip ssh server algorithm authentication password 262 | username cisco privilege 15 secret cisco 263 | line vty 0 4 264 | transport input ssh telnet 265 | exec-timeout 720 0 266 | password cisco 267 | login local 268 | line con 0 269 | password cisco 270 | ! 271 | no cdp run 272 | ! 273 | ! 274 | interface GigabitEthernet1 275 | description OOB Management 276 | vrf forwarding Mgmt-intf 277 | ! Configured on launch 278 | no ip address 279 | cdp enable 280 | no shutdown 281 | 282 | ip route vrf Mgmt-intf 0.0.0.0 0.0.0.0 {{ gateway }} 283 | ! 284 | interface GigabitEthernet2 285 | description to dist1 286 | no shutdown 287 | ! 288 | interface GigabitEthernet3 289 | description to dist2 290 | no shutdown 291 | ! 292 | interface GigabitEthernet4 293 | description core2 294 | no ip address 295 | no shutdown 296 | ! 297 | interface Loopback 0 298 | ! 299 | ! 300 | ! 301 | end 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | -------------------------------------------------------------------------------- /vagrant_device_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | vagrant-iosxe1 4 | 5 | 6 | 1 7 | MGMT Interface 8 | 9 | 10 | 2 11 | LAN Interface 12 | 13 | 14 |
15 | 16 |
10.2.1.1
17 | 255.255.255.0 18 |
19 |
20 |
21 | 22 | false 23 | false 24 | 25 | 26 | true 27 | 28 |
29 | 30 | 3 31 | WAN Interface 32 | 33 | 34 |
35 | 36 |
10.2.2.1
37 | 255.255.255.0 38 |
39 |
40 |
41 | 42 | false 43 | false 44 | 45 | 46 | true 47 | 48 |
49 |
50 | 51 | 52 | private 53 | 54 | 55 | 56 | public 57 | 58 | 59 | 60 |
61 |
62 | -------------------------------------------------------------------------------- /vagrant_device_setup.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | """Configure the baseline on Vagrant device 3 | 4 | Copyright (c) 2018 Cisco and/or its affiliates. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | """ 24 | 25 | # Import libraries 26 | from ncclient import manager 27 | from xml.dom import minidom 28 | import xmltodict 29 | from device_apis.device_info import vagrant_iosxe as device # noqa 30 | 31 | # Open and read in configuration template 32 | with open("vagrant_device_config.xml") as f: 33 | config_data = f.read() 34 | 35 | # Open NETCONF connection to device 36 | with manager.connect(host = device["address"], 37 | port = device["netconf_port"], 38 | username = device["username"], 39 | password = device["password"], 40 | hostkey_verify = False) as m: 41 | 42 | # Create desired NETCONF config payload and 43 | r = m.edit_config(target = "running", config = config_data) 44 | 45 | # Print OK status 46 | print("NETCONF RPC OK: {}".format(r.ok)) 47 | --------------------------------------------------------------------------------