├── nso ├── __init__.py └── test_api.py ├── scrapli-apps ├── .gitignore ├── constants.py ├── pyproject.toml ├── test-scrapli-replay.py ├── input │ └── nc-config.yaml ├── scrapli_replay_sessions │ └── test_get_serial_num.yaml ├── scrapli-cfg.py ├── ssh-netmiko.py ├── nc-ncclient.py └── nc-scrapli.py ├── netprog-stream-django ├── .dockerignore ├── netprog_stream │ ├── __init__.py │ ├── network │ │ ├── __init__.py │ │ ├── migrations │ │ │ ├── __init__.py │ │ │ └── 0001_initial.py │ │ ├── tests.py │ │ ├── apps.py │ │ ├── admin.py │ │ ├── urls.py │ │ ├── templates │ │ │ ├── base.html │ │ │ └── device.html │ │ ├── tasks.py │ │ ├── models.py │ │ └── views.py │ ├── netprog_stream │ │ ├── __init__.py │ │ ├── wsgi.py │ │ ├── celery.py │ │ └── urls.py │ └── manage.py ├── data │ └── dump.rdb ├── pyproject.toml ├── requirements.txt ├── Dockerfile-dev ├── docker-compose.dev.yml └── Dockerfile ├── nornir ├── network_diagram │ ├── network_diagram.py │ ├── inventory │ │ └── 10-csrs_gns3 │ │ │ ├── groups.yaml │ │ │ ├── defaults.yaml │ │ │ └── hosts.yaml │ ├── topology.png │ ├── config.yaml │ ├── pyproject.toml │ ├── link.py │ └── constants.py ├── lab-system │ ├── user-data │ │ ├── last_run_deployment.yml │ │ └── deployments │ │ │ ├── 1_simple.yml │ │ │ ├── 2_advanced.yml │ │ │ ├── 1_mixed_1_dev.yml │ │ │ └── 2_mixed.yml │ ├── inventory │ │ ├── host_vars │ │ │ ├── R1.yml │ │ │ ├── R2.yml │ │ │ ├── R3.yml │ │ │ ├── R4.yml │ │ │ ├── SW2.yml │ │ │ ├── SW1.yml │ │ │ ├── SW3.yml │ │ │ ├── SW4.yml │ │ │ ├── pod-mgmt-1.yml │ │ │ ├── pair-1.yml │ │ │ ├── matrix-3.yml │ │ │ ├── matrix-4.yml │ │ │ ├── matrix-1.yml │ │ │ └── matrix-2.yml │ │ ├── groups.yaml │ │ └── hosts.yaml │ ├── inventory-to-investigate │ │ ├── group_vars │ │ │ ├── matrix-switches.yml │ │ │ └── all.yml │ │ ├── main.py │ │ ├── config.yaml │ │ ├── hosts.yml │ │ └── host_vars │ │ │ ├── matrix-1.yml │ │ │ ├── matrix-2.yml │ │ │ ├── matrix-3.yml │ │ │ └── matrix-4.yml │ ├── config-ansible.yaml │ ├── topologies │ │ ├── simple │ │ │ ├── configs │ │ │ │ ├── London.txt │ │ │ │ └── Madrid.txt │ │ │ └── topology.yml │ │ ├── mixed │ │ │ ├── configs │ │ │ │ ├── SJ-BR1.txt │ │ │ │ ├── SJ-BR2.txt │ │ │ │ ├── SJ-DSW1.txt │ │ │ │ └── SJ-DSW2.txt │ │ │ └── topology.yml │ │ └── advanced │ │ │ ├── configs │ │ │ ├── rtp-border.txt │ │ │ ├── sjc-border.txt │ │ │ ├── rtp-access.txt │ │ │ └── sjc-access.txt │ │ │ └── topology.yml │ ├── constants.py │ ├── config.yaml │ ├── setup.cfg │ ├── setup.py │ ├── utils.py │ └── main.py └── exploring │ ├── inventory │ ├── gns3 │ │ ├── host_vars │ │ │ ├── SJ-R1.yml │ │ │ ├── SJ-BR2.yml │ │ │ └── SJ-ISW1.yml │ │ ├── group_vars │ │ │ └── sjc.yml │ │ └── hosts.yml │ └── simple │ │ ├── groups.yaml │ │ └── hosts.yaml │ ├── ansible-config.yaml │ ├── config.yaml │ ├── templates │ └── interfaces.j2 │ ├── new.py │ └── main.py ├── ansible ├── lab-system │ ├── user-data │ │ ├── last_run_deployment.yml │ │ ├── lab-templates │ │ │ ├── dev │ │ │ │ ├── topology.yml │ │ │ │ └── configs │ │ │ │ │ ├── BRU-R1.txt │ │ │ │ │ └── BRU-R2.txt │ │ │ └── mixed │ │ │ │ ├── configs │ │ │ │ ├── SJ-BR1.txt │ │ │ │ ├── SJ-BR2.txt │ │ │ │ ├── SJ-DSW1.txt │ │ │ │ └── SJ-DSW2.txt │ │ │ │ └── topology.yml │ │ └── deployments │ │ │ ├── 1_mixed_1_dev.yml │ │ │ └── 2_mixed.yml │ ├── roles │ │ ├── test │ │ │ ├── vars │ │ │ │ ├── main.yml │ │ │ │ ├── matrix-1.yml │ │ │ │ └── host_vars │ │ │ │ │ └── matrix-1.yml │ │ │ └── tasks │ │ │ │ └── main.yml │ │ └── pod-configuration-setup │ │ │ └── tasks │ │ │ └── main.yml │ ├── group_vars │ │ ├── matrix-switches.yml │ │ └── all.yml │ ├── templates │ │ ├── matrix.j2 │ │ └── l2_interfaces.j2 │ ├── collect_outputs.yml │ ├── ansible.cfg │ ├── hosts.yml │ ├── host_vars │ │ ├── matrix-1.yml │ │ ├── matrix-2.yml │ │ ├── matrix-3.yml │ │ └── matrix-4.yml │ ├── vars_plugins │ │ └── load_lab_templates.py │ ├── debug.yml │ └── lab-system.yml └── network-programmability-lab │ ├── debug2.retry │ ├── inventories │ ├── gns3 │ │ ├── host_vars │ │ │ ├── SJ-R1.yml │ │ │ ├── SJ-BR2.yml │ │ │ └── SJ-ISW1.yml │ │ ├── group_vars │ │ │ └── sjc.yml │ │ └── hosts.yml │ └── dev │ │ ├── hosts.yml │ │ ├── host_vars │ │ ├── R2.yml │ │ └── R1.yml │ │ └── group_vars │ │ └── all.yml │ ├── templates │ ├── ios_l3_subif.j2 │ ├── ios_eigrp.j2 │ ├── PE.j2 │ ├── dump_all.j2 │ └── netconf │ │ ├── vrf.j2 │ │ └── l3_interface.j2 │ ├── netconf-examples.yml │ ├── hosts │ ├── debug2.yml │ ├── ansible.cfg │ ├── result.cfg │ ├── roles │ └── borders │ │ └── tasks │ │ └── provision.yml │ ├── provision.yml │ ├── test.yml │ ├── provision_vlans.yml │ ├── debug.yml │ ├── collect_facts.yml │ └── collect_outputs.yml ├── chatops-webex-teams ├── network_overwatch │ ├── __init__.py │ ├── constants.py │ ├── command_handler.py │ ├── restconf.py │ └── app.py ├── chatops-vrf.png ├── payload-examples │ ├── webex-message-direct.json │ ├── webex-message-group.json │ └── webex-webhook.json └── pyproject.toml ├── network-diagram-visualization-js ├── src │ ├── assets │ │ └── .gitkeep │ ├── views │ │ ├── About.vue │ │ ├── NetworkDiagramView.vue │ │ └── Home.vue │ ├── main.js │ ├── components │ │ ├── HelloWorld.vue │ │ └── NetworkDiagramD3Force.vue │ ├── App.vue │ └── router │ │ └── index.js ├── .browserslistrc ├── .prettierrc.toml ├── babel.config.js ├── network-diagram-vis-network.png ├── .gitignore ├── .eslintrc.js ├── public │ └── index.html └── package.json ├── salt ├── pillar │ ├── dummy_pillar.sls │ ├── top.sls │ ├── junos_pillar.sls │ └── R1_pillar.sls ├── proxy ├── states │ └── test.sls ├── master ├── docker-compose-dev.yml └── docker-compose.yml ├── network-testing ├── inventories │ └── 10-csr-local │ │ ├── groups.yaml │ │ ├── defaults.yaml │ │ └── hosts.yaml ├── old-2018-stream │ ├── inventory_2018 │ │ ├── groups.yaml │ │ ├── defaults.yaml │ │ └── hosts.yaml │ ├── tests_2018 │ │ ├── test_krk_stp.yaml │ │ ├── conftest.py │ │ ├── krk_vlans.yaml │ │ ├── test_krk_vlans.py │ │ └── test_krk_stp.py │ └── config_2018.yaml ├── show_vrf_genie.json ├── config.yaml ├── tests │ ├── conftest.py │ ├── test_vrfs.py │ └── test_sw_version.py ├── sandbox.py ├── show_version_genie.json └── pyproject.toml ├── juniper ├── inventory.yml ├── vars.yml └── templates │ └── candidate.conf ├── go └── src │ ├── gather-commands-ssh │ └── hosts.txt │ └── hello │ └── hello.go ├── model-driven-telemetry ├── netconf │ ├── code │ │ ├── constants.py │ │ ├── utils.py │ │ └── test_xpath.py │ ├── rpc-examples │ │ ├── hello.xml │ │ ├── on-change-cdp-oper.xml │ │ └── periodic-subscription-kernel-cpu.xml │ ├── pyproject.toml │ └── docker-compose.yml └── grpc │ ├── telegraf │ └── telegraf.conf │ ├── router.cfg │ └── docker-compose.yml ├── async ├── collect_mac_address_tables │ └── mac_address_table_inventory.yml ├── gather_commands_sync.py ├── http-requests-sync.py ├── gather_commands_async.py ├── http-requests-threads.py ├── http-requests-async.py ├── netdev-async.py └── http-requests-async2.py ├── netconf ├── rpc-examples │ ├── get-config.xml │ ├── hello.xml │ ├── get-vrf-config-xpath.xml │ └── edit-config-hostname-vrfs-loopback.xml ├── pyproject.toml ├── templates │ └── loopbacks.j2 └── example.py ├── netbox ├── templates │ ├── config │ │ └── cisco │ │ │ └── ios │ │ │ ├── access_port.template │ │ │ └── l3_interface.template │ └── management-interface.j2 ├── inventory.yml ├── aiohttp_netbox.py └── add_devices_to_netbox.py ├── napalm └── sandbox.py ├── LICENSE ├── pyats └── intro │ ├── main.py │ └── testbed.yaml ├── README.md ├── .gitignore └── hashicorp-vault └── netmiko-vault.py /nso/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scrapli-apps/.gitignore: -------------------------------------------------------------------------------- 1 | output/ -------------------------------------------------------------------------------- /netprog-stream-django/.dockerignore: -------------------------------------------------------------------------------- 1 | .venv/ -------------------------------------------------------------------------------- /nornir/network_diagram/network_diagram.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible/lab-system/user-data/last_run_deployment.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /chatops-webex-teams/network_overwatch/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /network-diagram-visualization-js/src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /nornir/lab-system/user-data/last_run_deployment.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /salt/pillar/dummy_pillar.sls: -------------------------------------------------------------------------------- 1 | proxy: 2 | proxytype: dummy -------------------------------------------------------------------------------- /ansible/network-programmability-lab/debug2.retry: -------------------------------------------------------------------------------- 1 | localhost 2 | -------------------------------------------------------------------------------- /ansible/lab-system/roles/test/vars/main.yml: -------------------------------------------------------------------------------- 1 | new_var: 12312312312312 -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ansible/lab-system/roles/test/vars/matrix-1.yml: -------------------------------------------------------------------------------- 1 | new_var: 12312312312312 -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/R1.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 17 -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/R2.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 18 -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/R3.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 19 -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/R4.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 20 -------------------------------------------------------------------------------- /network-diagram-visualization-js/.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | -------------------------------------------------------------------------------- /network-diagram-visualization-js/.prettierrc.toml: -------------------------------------------------------------------------------- 1 | trailingComma = "all" 2 | -------------------------------------------------------------------------------- /nornir/exploring/inventory/gns3/host_vars/SJ-R1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | test_variable: 123 -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/SW2.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 15 3 | -------------------------------------------------------------------------------- /ansible/lab-system/group_vars/matrix-switches.yml: -------------------------------------------------------------------------------- 1 | dot1q_tunnel_vlan_start: 2000 2 | -------------------------------------------------------------------------------- /ansible/lab-system/roles/test/vars/host_vars/matrix-1.yml: -------------------------------------------------------------------------------- 1 | new_var: 12312312312312 -------------------------------------------------------------------------------- /nornir/lab-system/user-data/deployments/1_simple.yml: -------------------------------------------------------------------------------- 1 | pods: 2 | - simple: 1 3 | -------------------------------------------------------------------------------- /nornir/lab-system/user-data/deployments/2_advanced.yml: -------------------------------------------------------------------------------- 1 | pods: 2 | - advanced: 2 3 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/inventories/gns3/host_vars/SJ-R1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | test_variable: 123 -------------------------------------------------------------------------------- /network-testing/inventories/10-csr-local/groups.yaml: -------------------------------------------------------------------------------- 1 | Miami: 2 | data: {} 3 | London: 4 | data: {} -------------------------------------------------------------------------------- /nornir/lab-system/inventory-to-investigate/group_vars/matrix-switches.yml: -------------------------------------------------------------------------------- 1 | dot1q_tunnel_vlan_start: 2000 -------------------------------------------------------------------------------- /juniper/inventory.yml: -------------------------------------------------------------------------------- 1 | juniper-mx: 2 | - 192.168.122.101 3 | - 192.168.122.102 4 | - 192.168.122.103 -------------------------------------------------------------------------------- /network-testing/old-2018-stream/inventory_2018/groups.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | krk: {} 3 | krk-l2: {} 4 | 5 | isp1: {} -------------------------------------------------------------------------------- /nornir/network_diagram/inventory/10-csrs_gns3/groups.yaml: -------------------------------------------------------------------------------- 1 | Miami: 2 | data: {} 3 | London: 4 | data: {} -------------------------------------------------------------------------------- /network-testing/old-2018-stream/inventory_2018/defaults.yaml: -------------------------------------------------------------------------------- 1 | username: admin 2 | password: admin 3 | platform: ios 4 | -------------------------------------------------------------------------------- /nornir/network_diagram/inventory/10-csrs_gns3/defaults.yaml: -------------------------------------------------------------------------------- 1 | username: cisco 2 | password: cisco 3 | platform: cisco_ios -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /network-diagram-visualization-js/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"] 3 | }; 4 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory-to-investigate/main.py: -------------------------------------------------------------------------------- 1 | from nornir.core import InitNornir 2 | nr = InitNornir("config.yaml") 3 | -------------------------------------------------------------------------------- /salt/pillar/top.sls: -------------------------------------------------------------------------------- 1 | base: 2 | dummy: 3 | - dummy_pillar 4 | junos-box: 5 | - junos_pillar 6 | R1: 7 | - R1_pillar -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/netprog_stream/__init__.py: -------------------------------------------------------------------------------- 1 | from .celery import app as celery_app 2 | 3 | __all__ = ('celery_app',) 4 | -------------------------------------------------------------------------------- /chatops-webex-teams/chatops-vrf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmfigol/network-programmability-stream/HEAD/chatops-webex-teams/chatops-vrf.png -------------------------------------------------------------------------------- /go/src/gather-commands-ssh/hosts.txt: -------------------------------------------------------------------------------- 1 | 192.168.153.101 R1 2 | 192.168.153.102 R2 3 | 192.168.153.103 R3 4 | 192.168.153.104 R4 5 | 192.168.153.105 R5 -------------------------------------------------------------------------------- /netprog-stream-django/data/dump.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmfigol/network-programmability-stream/HEAD/netprog-stream-django/data/dump.rdb -------------------------------------------------------------------------------- /nornir/network_diagram/topology.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmfigol/network-programmability-stream/HEAD/nornir/network_diagram/topology.png -------------------------------------------------------------------------------- /salt/pillar/junos_pillar.sls: -------------------------------------------------------------------------------- 1 | proxy: 2 | proxytype: junos 3 | host: non-existent-junos.example.com 4 | username: admin 5 | password: admin -------------------------------------------------------------------------------- /salt/proxy: -------------------------------------------------------------------------------- 1 | master: salt-master 2 | open_mode: true 3 | pki_dir: /etc/salt/pki/proxy 4 | cachedir: /var/cache/salt/proxy 5 | multiprocessing: false -------------------------------------------------------------------------------- /salt/states/test.sls: -------------------------------------------------------------------------------- 1 | "Validating NTP servers": 2 | netntp.managed: 3 | - servers: 4 | - 24.124.0.251 5 | - 138.236.128.36 -------------------------------------------------------------------------------- /ansible/network-programmability-lab/templates/ios_l3_subif.j2: -------------------------------------------------------------------------------- 1 | interface {{ name }} 2 | encapsulation dot1q {{ vlan_id }} 3 | ip address {{ ipv4 }} {{ ipv4_mask }} -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class NetworkConfig(AppConfig): 5 | name = 'network' 6 | -------------------------------------------------------------------------------- /model-driven-telemetry/netconf/code/constants.py: -------------------------------------------------------------------------------- 1 | NC_CONN_PARAMS = { 2 | "host": "192.168.153.101", 3 | "username": "cisco", 4 | "password": "cisco", 5 | } -------------------------------------------------------------------------------- /ansible/network-programmability-lab/templates/ios_eigrp.j2: -------------------------------------------------------------------------------- 1 | router eigrp {{ eigrp_as_number }} 2 | network {{ eigrp_network_prefix }} {{ eigrp_network_wildcard_mask }} 3 | -------------------------------------------------------------------------------- /async/collect_mac_address_tables/mac_address_table_inventory.yml: -------------------------------------------------------------------------------- 1 | switches: 2 | - 192.168.122.150 3 | - 192.168.122.151 4 | - 192.168.122.152 5 | - 192.168.122.153 -------------------------------------------------------------------------------- /nornir/exploring/ansible-config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | num_workers: 20 3 | inventory: nornir.plugins.inventory.ansible.AnsibleInventory 4 | AnsibleInventory: 5 | hostsfile: "hosts" -------------------------------------------------------------------------------- /salt/master: -------------------------------------------------------------------------------- 1 | open_mode: true 2 | 3 | pillar_roots: 4 | base: 5 | - /etc/salt/pillar 6 | 7 | file_roots: 8 | base: 9 | - /etc/salt 10 | - /etc/salt/states -------------------------------------------------------------------------------- /ansible/lab-system/roles/test/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Dump all vars 2 | # # action: template src=templates/dumpall.j2 dest=/tmp/ansible.all 3 | debug: var=hostvars[inventory_hostname] -------------------------------------------------------------------------------- /nornir/lab-system/config-ansible.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | num_workers: 20 3 | inventory: nornir.plugins.inventory.ansible.AnsibleInventory 4 | AnsibleInventory: 5 | hostsfile: "inventory/hosts.yaml" -------------------------------------------------------------------------------- /ansible/network-programmability-lab/inventories/dev/hosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | hosts: 4 | R1: 5 | ansible_host: 10.48.18.24 6 | R2: 7 | ansible_host: 10.48.18.30 8 | 9 | -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Device 3 | 4 | # Register your models here. 5 | admin.site.register(Device) 6 | -------------------------------------------------------------------------------- /network-testing/old-2018-stream/tests_2018/test_krk_stp.yaml: -------------------------------------------------------------------------------- 1 | group: krk-l2 2 | data: 3 | stp_root: 4 | vlans: 5 | 10: 6 | - krk-dsw1 7 | 20: 8 | - krk-dsw1 9 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory-to-investigate/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | num_workers: 20 3 | inventory: nornir.plugins.inventory.ansible.AnsibleInventory 4 | AnsibleInventory: 5 | hostsfile: "hosts.yml" -------------------------------------------------------------------------------- /salt/docker-compose-dev.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | nginx: 5 | image: nginx 6 | volumes: 7 | - ${PWD}/master:/master 8 | redis: 9 | image: redis 10 | -------------------------------------------------------------------------------- /ansible/lab-system/user-data/lab-templates/dev/topology.yml: -------------------------------------------------------------------------------- 1 | connections: 2 | - - lab_hostname: BRU-R1 3 | port: Ethernet1/1 4 | - lab_hostname: BRU-R2 5 | port: Ethernet1/1 6 | -------------------------------------------------------------------------------- /salt/pillar/R1_pillar.sls: -------------------------------------------------------------------------------- 1 | proxy: 2 | proxytype: napalm 3 | driver: ios 4 | host: 10.48.18.30 5 | username: cisco 6 | passwd: cisco 7 | ntp.servers: 8 | - 192.168.0.1 9 | - 172.17.17.1 -------------------------------------------------------------------------------- /network-diagram-visualization-js/network-diagram-vis-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dmfigol/network-programmability-stream/HEAD/network-diagram-visualization-js/network-diagram-vis-network.png -------------------------------------------------------------------------------- /nornir/lab-system/topologies/simple/configs/London.txt: -------------------------------------------------------------------------------- 1 | hostname London 2 | interface Ethernet0/0 3 | ! 4 | interface {{ interface_1 }} 5 | ip address 100.0.10.2 255.255.255.0 6 | no shutdown 7 | ! -------------------------------------------------------------------------------- /nornir/lab-system/topologies/simple/configs/Madrid.txt: -------------------------------------------------------------------------------- 1 | hostname Madrid 2 | interface Ethernet0/0 3 | ! 4 | interface {{ interface_1 }} 5 | ip address 100.0.10.1 255.255.255.0 6 | no shutdown 7 | ! -------------------------------------------------------------------------------- /ansible/lab-system/user-data/lab-templates/dev/configs/BRU-R1.txt: -------------------------------------------------------------------------------- 1 | hostname BRU-R1 2 | interface Ethernet0/0 3 | ! 4 | interface Ethernet1/1 5 | ip address 100.0.10.1 255.255.255.0 6 | no shutdown 7 | ! -------------------------------------------------------------------------------- /ansible/lab-system/user-data/lab-templates/dev/configs/BRU-R2.txt: -------------------------------------------------------------------------------- 1 | hostname BRU-R2 2 | interface Ethernet0/0 3 | ! 4 | interface Ethernet1/1 5 | ip address 100.0.10.2 255.255.255.0 6 | no shutdown 7 | ! -------------------------------------------------------------------------------- /nornir/exploring/inventory/gns3/group_vars/sjc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | username: admin 3 | password: admin 4 | eigrp_as_number: 100 5 | eigrp_network_prefix: 172.16.0.0 6 | eigrp_network_wildcard_mask: 0.0.255.255 7 | -------------------------------------------------------------------------------- /nornir/lab-system/constants.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | POD_ID_STEP = 100 4 | DOT1Q_TUNNEL_VLAN_START = 2100 5 | INTERFACE_NAME_RE = re.compile( 6 | r"(?P[a-zA-Z\-_ ]*)(?P[\d.\/]*)" 7 | ) -------------------------------------------------------------------------------- /nornir/lab-system/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | num_workers: 20 3 | inventory: nornir.plugins.inventory.simple.SimpleInventory 4 | SimpleInventory: 5 | host_file: inventory/hosts.yaml 6 | group_file: inventory/groups.yaml 7 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/inventories/gns3/group_vars/sjc.yml: -------------------------------------------------------------------------------- 1 | --- 2 | username: admin 3 | password: admin 4 | eigrp_as_number: 100 5 | eigrp_network_prefix: 172.16.0.0 6 | eigrp_network_wildcard_mask: 0.0.255.255 7 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/SW1.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 14 3 | interfaces: 4 | - name: Ethernet0/0 5 | vrf: Mgmt 6 | connected_device: 7 | name: pod-mgmt-1 8 | port: GigabitEthernet0/2 9 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/SW3.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 16 3 | interfaces: 4 | - name: Ethernet0/0 5 | vrf: Mgmt 6 | connected_device: 7 | name: pod-mgmt-1 8 | port: GigabitEthernet0/4 9 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/SW4.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 17 3 | interfaces: 4 | - name: Ethernet0/0 5 | vrf: Mgmt 6 | connected_device: 7 | name: pod-mgmt-1 8 | port: GigabitEthernet0/4 9 | -------------------------------------------------------------------------------- /ansible/lab-system/group_vars/all.yml: -------------------------------------------------------------------------------- 1 | ansible_user: admin 2 | ansible_ssh_pass: admin 3 | ansible_connection: network_cli 4 | ansible_network_os: ios 5 | ansible_python_interpreter: "/usr/bin/env python" 6 | controlled_pod_number: all -------------------------------------------------------------------------------- /netconf/rpc-examples/get-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | ]]>]]> -------------------------------------------------------------------------------- /nornir/exploring/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | num_workers: 20 3 | inventory: nornir.plugins.inventory.simple.SimpleInventory 4 | SimpleInventory: 5 | host_file: "inventory/simple/hosts.yaml" 6 | group_file: "inventory/simple/groups.yaml" -------------------------------------------------------------------------------- /ansible/network-programmability-lab/inventories/dev/host_vars/R2.yml: -------------------------------------------------------------------------------- 1 | #vrfs: 2 | # - name: NETCONF 3 | # ipv4: true 4 | # - name: YANG 5 | # ipv4: true 6 | # ipv6: true 7 | # - name: STREAM 8 | # ipv4: true 9 | # ipv6: true -------------------------------------------------------------------------------- /network-testing/old-2018-stream/tests_2018/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from nornir import InitNornir 4 | 5 | 6 | @pytest.fixture(scope="session", autouse=True) 7 | def nr(): 8 | return InitNornir(config_file="config.yaml") 9 | -------------------------------------------------------------------------------- /ansible/lab-system/templates/matrix.j2: -------------------------------------------------------------------------------- 1 | vtp mode transparent 2 | vlan 2000-4000 3 | spanning-tree mst configuration 4 | name NG-Lab 5 | revision 1 6 | instance 1 vlan 2000-4000 7 | ! 8 | spanning-tree mode mst 9 | {% include 'l2_interfaces.j2' %} -------------------------------------------------------------------------------- /ansible/network-programmability-lab/inventories/dev/group_vars/all.yml: -------------------------------------------------------------------------------- 1 | ansible_user: cisco 2 | ansible_ssh_pass: cisco 3 | ansible_connection: netconf 4 | ansible_network_os: ios 5 | ansible_python_interpreter: "/usr/bin/env python" 6 | timeout: 120 -------------------------------------------------------------------------------- /netbox/templates/config/cisco/ios/access_port.template: -------------------------------------------------------------------------------- 1 | interface {{ interface_name }} 2 | description {{ interface_description }} 3 | switchport mode access 4 | switchport access vlan {{ vlan_number }} 5 | {{ 'no ' if enabled }}shutdown 6 | ! -------------------------------------------------------------------------------- /netconf/rpc-examples/hello.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | urn:ietf:params:netconf:base:1.0 5 | 6 | ]]>]]> -------------------------------------------------------------------------------- /network-diagram-visualization-js/src/views/About.vue: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory-to-investigate/group_vars/all.yml: -------------------------------------------------------------------------------- 1 | ansible_user: admin 2 | ansible_ssh_pass: admin 3 | ansible_connection: network_cli 4 | ansible_network_os: ios 5 | ansible_python_interpreter: "/usr/bin/env python" 6 | controlled_pod_number: all -------------------------------------------------------------------------------- /network-testing/old-2018-stream/tests_2018/krk_vlans.yaml: -------------------------------------------------------------------------------- 1 | group: krk-l2 2 | excluded_vlans: [1, 1002, 1003, 1004, 1005] 3 | data: 4 | vlans: 5 | - id: 10 6 | name: IT 7 | - id: 20 8 | name: FINANCE 9 | - id: 30 10 | name: HR 11 | -------------------------------------------------------------------------------- /model-driven-telemetry/netconf/rpc-examples/hello.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | urn:ietf:params:netconf:base:1.0 5 | 6 | ]]>]]> -------------------------------------------------------------------------------- /netbox/templates/config/cisco/ios/l3_interface.template: -------------------------------------------------------------------------------- 1 | interface {{ interface_name }} 2 | description {{ description }} 3 | {{ 'no switchport\n ' if switch_l3_interface -}} 4 | ip address {{ ip_address.ip }} {{ ip_address.netmask }} 5 | {{ 'no ' if enabled }}shutdown 6 | ! -------------------------------------------------------------------------------- /network-diagram-visualization-js/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | 5 | Vue.config.productionTip = false; 6 | 7 | new Vue({ 8 | router, 9 | render: h => h(App), 10 | }).$mount("#app"); 11 | -------------------------------------------------------------------------------- /nornir/lab-system/topologies/mixed/configs/SJ-BR1.txt: -------------------------------------------------------------------------------- 1 | hostname SJ-BR1 2 | interface Ethernet0/0 3 | ! 4 | interface Ethernet1/1 5 | ip address 10.0.10.1 255.255.255.0 6 | no shutdown 7 | ! 8 | interface Ethernet1/2 9 | ip address 10.0.20.1 255.255.255.0 10 | no shutdown 11 | ! -------------------------------------------------------------------------------- /nornir/lab-system/topologies/simple/topology.yml: -------------------------------------------------------------------------------- 1 | devices: 2 | Madrid: 3 | tags: 4 | - router 5 | London: 6 | tags: 7 | - router 8 | 9 | connections: 10 | - - hostname: Madrid 11 | - hostname: London 12 | - - hostname: Madrid 13 | - service: internet 14 | -------------------------------------------------------------------------------- /nornir/lab-system/topologies/mixed/configs/SJ-BR2.txt: -------------------------------------------------------------------------------- 1 | hostname SJ-BR2 2 | interface Ethernet0/0 3 | ! 4 | interface Ethernet1/1 5 | ip address 10.0.20.2 255.255.255.0 6 | no shutdown 7 | ! 8 | interface Ethernet1/2 9 | ip address 10.0.10.2 255.255.255.0 10 | no shutdown 11 | ! 12 | -------------------------------------------------------------------------------- /ansible/lab-system/user-data/lab-templates/mixed/configs/SJ-BR1.txt: -------------------------------------------------------------------------------- 1 | hostname SJ-BR1 2 | interface Ethernet0/0 3 | ! 4 | interface Ethernet1/1 5 | ip address 10.0.10.1 255.255.255.0 6 | no shutdown 7 | ! 8 | interface Ethernet1/2 9 | ip address 10.0.20.1 255.255.255.0 10 | no shutdown 11 | ! -------------------------------------------------------------------------------- /chatops-webex-teams/network_overwatch/constants.py: -------------------------------------------------------------------------------- 1 | DEVICE_USERNAME = "cisco" 2 | DEVICE_PASSWORD = "cisco" 3 | 4 | DEVICES = { 5 | "R1": "192.168.153.101", 6 | "R2": "192.168.153.102", 7 | } 8 | 9 | 10 | WEBEX_WEBHOOK_ENDPOINT = "/webex-webhooks" 11 | NGROK_TUNNEL = "http://localhost:3000" 12 | -------------------------------------------------------------------------------- /network-testing/show_vrf_genie.json: -------------------------------------------------------------------------------- 1 | { 2 | "vrf": { 3 | "Mgmt-vrf": { 4 | "protocols": [ 5 | "ipv4", 6 | "ipv6" 7 | ], 8 | "interfaces": [ 9 | "GigabitEthernet1" 10 | ] 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /nornir/lab-system/topologies/advanced/configs/rtp-border.txt: -------------------------------------------------------------------------------- 1 | hostname rtp-border 2 | interface Ethernet0/0 3 | ! 4 | interface Ethernet1/1 5 | ip address 10.0.20.2 255.255.255.0 6 | no shutdown 7 | ! 8 | interface Ethernet1/2 9 | ip address 10.0.10.2 255.255.255.0 10 | no shutdown 11 | ! 12 | -------------------------------------------------------------------------------- /ansible/lab-system/user-data/lab-templates/mixed/configs/SJ-BR2.txt: -------------------------------------------------------------------------------- 1 | hostname SJ-BR2 2 | interface Ethernet0/0 3 | ! 4 | interface Ethernet1/1 5 | ip address 10.0.20.2 255.255.255.0 6 | no shutdown 7 | ! 8 | interface Ethernet1/2 9 | ip address 10.0.10.2 255.255.255.0 10 | no shutdown 11 | ! 12 | -------------------------------------------------------------------------------- /network-testing/old-2018-stream/config_2018.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | core: 3 | num_workers: 10 4 | inventory: 5 | plugin: nornir.plugins.inventory.simple.SimpleInventory 6 | options: 7 | host_file: "inventory/hosts.yaml" 8 | group_file: "inventory/groups.yaml" 9 | defaults_file: "inventory/defaults.yaml" -------------------------------------------------------------------------------- /nornir/exploring/inventory/gns3/host_vars/SJ-BR2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | interfaces: 3 | - name: GigabitEthernet4.2012 4 | vlan_id: 2012 5 | ipv4: 172.16.254.18 6 | ipv4_mask: 255.255.255.252 7 | - name: GigabitEthernet4.2029 8 | vlan_id: 2029 9 | ipv4: 100.64.29.2 10 | ipv4_mask: 255.255.255.0 11 | -------------------------------------------------------------------------------- /ansible/lab-system/collect_outputs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Collect outputs 3 | hosts: all 4 | tasks: 5 | - name: run show commands on remote devices 6 | ios_command: 7 | commands: 8 | - show ip interface brief 9 | register: output 10 | 11 | - debug: 12 | msg: "{{ output.stdout_lines }}" 13 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/inventories/gns3/host_vars/SJ-BR2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | interfaces: 3 | - name: GigabitEthernet4.2012 4 | vlan_id: 2012 5 | ipv4: 172.16.254.18 6 | ipv4_mask: 255.255.255.252 7 | - name: GigabitEthernet4.2029 8 | vlan_id: 2029 9 | ipv4: 100.64.29.2 10 | ipv4_mask: 255.255.255.0 11 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/netconf-examples.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Provision Network 3 | hosts: R1 4 | 5 | tasks: 6 | - name: Configures VRFs 7 | netconf_config: 8 | src: templates/netconf/vrf.j2 9 | 10 | - name: Configure L3 interfaces 11 | netconf_config: 12 | src: templates/netconf/l3_interface.j2 13 | -------------------------------------------------------------------------------- /network-diagram-visualization-js/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | 14 | # Editor directories and files 15 | .idea 16 | .vscode 17 | *.suo 18 | *.ntvs* 19 | *.njsproj 20 | *.sln 21 | *.sw? 22 | -------------------------------------------------------------------------------- /ansible/lab-system/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory = hosts.yml 3 | host_key_checking = False 4 | log_path = ansible.log 5 | gathering = explicit 6 | retry_files_enabled = False 7 | 8 | [paramiko_connection] 9 | host_key_auto_add = True 10 | 11 | [persistent_connection] 12 | 13 | connect_timeout = 30 14 | #connect_retry_timeout = 15 15 | command_timeout = 30 -------------------------------------------------------------------------------- /nornir/exploring/templates/interfaces.j2: -------------------------------------------------------------------------------- 1 | ip domain-name {{ domain_name }} 2 | {% if interfaces is defined %} 3 | {% for interface in interfaces %} 4 | interface {{ interface.name }} 5 | {% if interface.ipv4_address is defined %} 6 | ip address {{ interface.ipv4_address }} {{ interface.ipv4_mask }} 7 | {% endif %} 8 | ! 9 | {% endfor %} 10 | {% endif %} 11 | end -------------------------------------------------------------------------------- /ansible/network-programmability-lab/hosts: -------------------------------------------------------------------------------- 1 | [local] 2 | localhost ansible_connection=local ansible_python_interpreter="/usr/bin/env python" 3 | 4 | [sjc] 5 | sjc-r1 ansible_host=192.168.122.11 6 | sjc-r2 ansible_host=192.168.122.12 7 | 8 | [sjc:vars] 9 | ansible_connection=local 10 | ansible_python_interpreter="/usr/bin/env python" 11 | username=admin 12 | password=admin -------------------------------------------------------------------------------- /ansible/network-programmability-lab/inventories/dev/host_vars/R1.yml: -------------------------------------------------------------------------------- 1 | #vrfs: 2 | # - name: NETCONF 3 | # ipv4: true 4 | # - name: YANG 5 | # ipv4: true 6 | # ipv6: true 7 | # - name: STREAM 8 | # ipv4: true 9 | # ipv6: true 10 | interfaces: 11 | - type: GigabitEthernet 12 | number: "3.100" 13 | dot1q_vlan: 100 14 | ipv4_addr: 100.64.0.1/24 -------------------------------------------------------------------------------- /nornir/lab-system/topologies/advanced/configs/sjc-border.txt: -------------------------------------------------------------------------------- 1 | hostname sjc-border 2 | interface Ethernet0/0 3 | ! 4 | interface Ethernet1/1 5 | ip address 10.0.10.1 255.255.255.0 6 | no shutdown 7 | ! 8 | interface Ethernet1/2 9 | ip address 10.0.20.1 255.255.255.0 10 | no shutdown 11 | ! 12 | interface Ethernet1/3 13 | ip address dhcp 14 | no shutdown 15 | ! -------------------------------------------------------------------------------- /ansible/network-programmability-lab/debug2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Debugging jinja 4 | gather_facts: no 5 | hosts: localhost 6 | vars: 7 | vrfs: 8 | - name: CLIENT 9 | rd: "1:1" 10 | rt: "1:1" 11 | - name: MGMT 12 | 13 | tasks: 14 | - name: Generate configs 15 | template: 16 | src: PE.j2 17 | dest: result.cfg 18 | 19 | -------------------------------------------------------------------------------- /nornir/network_diagram/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | core: 3 | num_workers: 10 4 | raise_on_error: True 5 | 6 | inventory: 7 | plugin: nornir.plugins.inventory.simple.SimpleInventory 8 | options: 9 | host_file: "inventory/10-csrs_gns3/hosts.yaml" 10 | group_file: "inventory/10-csrs_gns3/groups.yaml" 11 | defaults_file: "inventory/10-csrs_gns3/defaults.yaml" -------------------------------------------------------------------------------- /ansible/network-programmability-lab/ansible.cfg: -------------------------------------------------------------------------------- 1 | [defaults] 2 | inventory = hosts.yml 3 | host_key_checking = False 4 | ANSIBLE_SSH_ARGS = -oKexAlgorithms=+diffie-hellman-group1-sha1 -oHostKeyAl 5 | gathering = explicit 6 | 7 | [paramiko_connection] 8 | host_key_auto_add = True 9 | 10 | [persistent_connection] 11 | 12 | connect_timeout = 30 13 | #connect_retry_timeout = 15 14 | command_timeout = 30 -------------------------------------------------------------------------------- /network-diagram-visualization-js/src/views/NetworkDiagramView.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | -------------------------------------------------------------------------------- /nornir/lab-system/setup.cfg: -------------------------------------------------------------------------------- 1 | [mypy] 2 | # The mypy configurations: http://bit.ly/2zEl9WI 3 | python_version = 3.7 4 | 5 | check_untyped_defs = True 6 | disallow_any_generics = True 7 | disallow_untyped_calls = True 8 | ignore_errors = False 9 | ignore_missing_imports = True 10 | strict_optional = True 11 | warn_unused_ignores = True 12 | warn_redundant_casts = True 13 | warn_unused_configs = True 14 | -------------------------------------------------------------------------------- /nornir/exploring/inventory/simple/groups.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | defaults: 3 | domain_name: dmfigol.me 4 | 5 | sj: 6 | some_var: true 7 | 8 | global: 9 | some_var: false 10 | 11 | isp: {} 12 | 13 | 14 | sj-edge: 15 | groups: 16 | - sj 17 | - global 18 | bgp: true 19 | 20 | 21 | isp1: 22 | asn: 1000 23 | groups: 24 | - isp 25 | 26 | isp2: 27 | asn: 2000 28 | groups: 29 | - isp -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/urls.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django.urls import path 3 | 4 | from . import views 5 | 6 | urlpatterns = [ 7 | path('', views.index), 8 | path('device/', views.get_device_stats, name="device"), 9 | path('api/task/', views.get_task_status, name="task_status"), 10 | # path('devices', views.get_devices), 11 | ] 12 | -------------------------------------------------------------------------------- /network-diagram-visualization-js/src/views/Home.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/pod-mgmt-1.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 20 3 | interfaces: 4 | - name: GigabitEthernet0/2 5 | connected_device: 6 | name: SW1 7 | port: Ethernet0/0 8 | - name: GigabitEthernet0/2 9 | connected_device: 10 | name: SW2 11 | port: Ethernet0/0 12 | - name: GigabitEthernet0/4 13 | connected_device: 14 | name: SW3 15 | port: Ethernet0/0 16 | -------------------------------------------------------------------------------- /model-driven-telemetry/netconf/code/utils.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | from xml.dom.minidom import parseString 3 | 4 | from lxml import etree 5 | 6 | 7 | def prettify_xml(xml: Union[str, etree._Element]) -> str: 8 | if isinstance(xml, etree._Element): 9 | result = etree.tostring(xml, pretty_print=True).decode("utf-8") 10 | else: 11 | result = parseString(xml).toprettyxml(" ") 12 | return result -------------------------------------------------------------------------------- /netconf/rpc-examples/get-vrf-config-xpath.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 11 | 12 | ]]>]]> -------------------------------------------------------------------------------- /ansible/network-programmability-lab/result.cfg: -------------------------------------------------------------------------------- 1 | 2 | 3 | CLIENT 4 | 1:1 5 | 6 | export 7 | 1:1 8 | 9 | 10 | import 11 | 1:1 12 | 13 | 14 | 15 | MGMT 16 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/roles/borders/tasks/provision.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Provision Borders 3 | gather_facts: no 4 | hosts: SJ-BR1:SJ-BR2 5 | vars: 6 | ansible_connection: network_cli 7 | ansible_network_os: ios 8 | ansible_user: "{{ username }}" 9 | ansible_ssh_pass: "{{ password }}" 10 | 11 | tasks: 12 | - name: Configure subinterfaces 13 | ios_config: 14 | src: templates/ios_l3_subif.j2 -------------------------------------------------------------------------------- /netconf/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "netconf" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Your Name "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.7" 9 | jinja2 = "^2.11.1" 10 | lxml = "^4.5.0" 11 | ncclient = "^0.6.7" 12 | xmltodict = "^0.12.0" 13 | 14 | [tool.poetry.dev-dependencies] 15 | 16 | [build-system] 17 | requires = ["poetry>=0.12"] 18 | build-backend = "poetry.masonry.api" 19 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/provision.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Provision Network 3 | gather_facts: no 4 | hosts: sjc 5 | connection: local 6 | vars: 7 | credentials: 8 | host: "{{ ansible_host }}" 9 | username: "{{ username }}" 10 | password: "{{ password }}" 11 | 12 | tasks: 13 | - name: Configure EIGRP 14 | ios_config: 15 | provider: "{{ credentials }}" 16 | src: templates/ios_eigrp.j2 17 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/test.yml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | - name: Gather facts (Cisco) 4 | gather_facts: no 5 | hosts: sjc 6 | vars: 7 | creds: 8 | host: "{{ ansible_host }}" 9 | username: "{{ username }}" 10 | password: "{{ password }}" 11 | 12 | tasks: 13 | - ios_facts: 14 | provider: "{{ creds }}" 15 | tags: facts_only 16 | 17 | - debug: 18 | msg: "SN: {{ ansible_net_serialnum }}" -------------------------------------------------------------------------------- /network-diagram-visualization-js/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true 5 | }, 6 | extends: ["plugin:vue/essential", "eslint:recommended", "@vue/prettier"], 7 | parserOptions: { 8 | parser: "babel-eslint" 9 | }, 10 | rules: { 11 | "no-console": process.env.NODE_ENV === "production" ? "error" : "off", 12 | "no-debugger": process.env.NODE_ENV === "production" ? "error" : "off" 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /network-testing/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | runners: 3 | # plugin: serial 4 | plugin: threaded 5 | options: 6 | num_workers: 10 7 | 8 | core: 9 | raise_on_error: True 10 | 11 | logging: 12 | enabled: False 13 | 14 | inventory: 15 | plugin: SimpleInventory 16 | options: 17 | host_file: "inventories/10-csr-local/hosts.yaml" 18 | group_file: "inventories/10-csr-local/groups.yaml" 19 | defaults_file: "inventories/10-csr-local/defaults.yaml" -------------------------------------------------------------------------------- /network-testing/inventories/10-csr-local/defaults.yaml: -------------------------------------------------------------------------------- 1 | username: cisco 2 | password: cisco 3 | platform: cisco_iosxe 4 | connection_options: 5 | scrapli: 6 | # platform: cisco_iosxe 7 | extras: 8 | transport: ssh2 9 | # ssh_config_file: True 10 | auth_strict_key: False 11 | scrapli_netconf: 12 | # platform: cisco_iosxe 13 | extras: 14 | transport: ssh2 15 | # ssh_config_file: True 16 | auth_strict_key: False 17 | # data: 18 | # tests: {} 19 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/provision_vlans.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Provision Vlans 3 | gather_facts: no 4 | hosts: SJ-ISW1 5 | vars: 6 | ansible_connection: network_cli 7 | ansible_network_os: ios 8 | ansible_user: "{{ username }}" 9 | ansible_ssh_pass: "{{ password }}" 10 | 11 | tasks: 12 | - name: Create vlan 13 | ios_vlan: 14 | aggregate: "{{ vlans }}" 15 | 16 | - name: Configure switchports 17 | ios_l2_interface: 18 | aggregate: "{{ interfaces }}" -------------------------------------------------------------------------------- /model-driven-telemetry/grpc/telegraf/telegraf.conf: -------------------------------------------------------------------------------- 1 | # Global Agent Configuration 2 | [agent] 3 | hostname = "ubuntu-server" 4 | flush_interval = "15s" 5 | interval = "15s" 6 | 7 | # gRPC Dial-Out Telemetry Listener 8 | [[inputs.cisco_telemetry_mdt]] 9 | transport = "grpc" 10 | service_address = ":57555" 11 | 12 | # Output Plugin InfluxDB 13 | [[outputs.influxdb]] 14 | database = "cisco_mdt" 15 | urls = [ "http://influxdb:8086" ] 16 | 17 | # [[outputs.file]] 18 | # files = ["/root/telegraf/telegraf.log"] -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/netprog_stream/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for netprog_stream project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'netprog_stream.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/debug.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Debugging 3 | hosts: sjc 4 | tasks: 5 | - name: Dump all vars 6 | # action: template src=templates/dumpall.j2 dest=/tmp/ansible.all 7 | debug: var=hostvars[inventory_hostname] 8 | msg: "{{ username }}" 9 | 10 | - copy: content="{{ ios_facts | to_nice_json }}" dest="out/{{inventory_hostname}}_ios_facts.json" 11 | name: Save facts to JSON file 12 | 13 | - debug: 14 | msg: "{{ test_variable }}" 15 | tags: 16 | - debug -------------------------------------------------------------------------------- /chatops-webex-teams/payload-examples/webex-message-direct.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "Y2lzY29zcGFyazovL3VzL01FU1NBR0UvYjVmOGQ3NjAtNWZjZC0xMWVhLTllZTAtZWZjYzY5Mzc0ZDdh", 3 | "roomId": "Y2lzY29zcGFyazovL3VzL1JPT00vOTMwZjQ0YWYtYTAwZS0zMjMyLWE0MmMtNzc3Y2VjNWE2YTkx", 4 | "roomType": "direct", 5 | "text": "r1", 6 | "personId": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS9mNzI4OTVjYy00MzI3LTQwNGItYTlmOS1lODNiZDBhNzA2YTk", 7 | "personEmail": "test@example.com", 8 | "html": "
r1
", 9 | "created": "2020-03-06T17:12:51.414Z" 10 | } -------------------------------------------------------------------------------- /chatops-webex-teams/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "network-overwatch" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Dmitry Figol "] 6 | license = "MIT" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.7" 10 | starlette = "*" 11 | uvicorn = "*" 12 | httpx = "*" 13 | 14 | [tool.poetry.dev-dependencies] 15 | bpython = "*" 16 | pdbpp = "*" 17 | flake8 = "*" 18 | black = "19.10b0" 19 | mypy = "*" 20 | 21 | [build-system] 22 | requires = ["poetry>=0.12"] 23 | build-backend = "poetry.masonry.api" 24 | -------------------------------------------------------------------------------- /netbox/templates/management-interface.j2: -------------------------------------------------------------------------------- 1 | conf t 2 | vrf definition Mgmt 3 | add ipv4 4 | ! 5 | username admin privi 15 password admin 6 | ip domain name dmfigol.me 7 | int e0/0 8 | no sw 9 | vrf forwarding Mgmt 10 | ip add 192.168.122.152 255.255.255.0 11 | no sh 12 | duplex full 13 | ! 14 | line con 0 15 | tran pref none 16 | loggin synchr 17 | line vty 0 4 18 | login local 19 | privi level 15 20 | transport input all 21 | ! 22 | ip ssh version 2 23 | ip route vrf Mgmt 0.0.0.0 0.0.0.0 192.168.122.1 24 | crypto key gen rsa mod 2048 25 | end 26 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/templates/PE.j2: -------------------------------------------------------------------------------- 1 | #jinja2: trim_blocks:False 2 | {%- for vrf in vrfs %} 3 | 4 | {{ vrf.name }} 5 | {%- if vrf.rd is defined %} 6 | {{ vrf.rd }} 7 | 8 | export 9 | {{ vrf.rt}} 10 | 11 | 12 | import 13 | {{ vrf.rt }} 14 | 15 | {%- endif %} 16 | 17 | {%- endfor %} -------------------------------------------------------------------------------- /model-driven-telemetry/netconf/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "netconf_dial_in" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Dmitry Figol "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.7" 9 | ncclient = { git = "https://github.com/CiscoDevNet/ncclient.git", branch = "master" } 10 | xmltodict = "^0.12.0" 11 | lxml = "^4.5.0" 12 | jxmlease = "^1.0.1" 13 | kafka-python = "^2.0.0" 14 | 15 | [tool.poetry.dev-dependencies] 16 | 17 | [build-system] 18 | requires = ["poetry>=0.12"] 19 | build-backend = "poetry.masonry.api" -------------------------------------------------------------------------------- /ansible/network-programmability-lab/collect_facts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Collect facts 3 | gather_facts: no 4 | hosts: sjc 5 | connection: local 6 | vars: 7 | credentials: 8 | host: "{{ ansible_host }}" 9 | username: "{{ username }}" 10 | password: "{{ password }}" 11 | timeout: 30 12 | 13 | tasks: 14 | - ios_facts: 15 | provider: "{{ credentials }}" 16 | name: Collect IOS facts 17 | register: ios_facts 18 | 19 | - debug: 20 | msg: "{{ ansible_net_hostname }}, version: {{ ansible_net_version }}" 21 | 22 | -------------------------------------------------------------------------------- /network-testing/old-2018-stream/inventory_2018/hosts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | krk-asw1: 3 | hostname: 192.168.122.21 4 | groups: 5 | - krk 6 | - krk-l2 7 | krk-asw2: 8 | hostname: 192.168.122.22 9 | groups: 10 | - krk 11 | - krk-l2 12 | krk-dsw1: 13 | hostname: 192.168.122.23 14 | groups: 15 | - krk 16 | - krk-l2 17 | krk-dsw2: 18 | hostname: 192.168.122.24 19 | groups: 20 | - krk 21 | - krk-l2 22 | krk-edge1: 23 | hostname: 192.168.122.25 24 | groups: 25 | - krk 26 | isp1-pe1: 27 | hostname: 192.168.122.41 28 | groups: 29 | - isp1 -------------------------------------------------------------------------------- /nornir/network_diagram/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "network-diagram" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Dmitry Figol "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.6" 9 | nornir = "^2.0" 10 | requests = "^2.21" 11 | networkx = "^2.2" 12 | matplotlib = "^3.0" 13 | colorama = "^0.4.1" 14 | 15 | [tool.poetry.dev-dependencies] 16 | ipython = "^7.3" 17 | ipdb = "^0.11.0" 18 | black = { version = "*", allows-prereleases = true } 19 | 20 | [build-system] 21 | requires = ["poetry>=0.12"] 22 | build-backend = "poetry.masonry.api" 23 | -------------------------------------------------------------------------------- /model-driven-telemetry/netconf/rpc-examples/on-change-cdp-oper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | yp:yang-push 6 | /cdp-ios-xe-oper:cdp-neighbor-details/cdp-neighbor-detail 7 | 0 8 | 9 | ]]>]]> 10 | 11 | -------------------------------------------------------------------------------- /netprog-stream-django/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "netprog_stream" 3 | version = "0.1.0" 4 | description = "Awesome network controller" 5 | authors = ["Dmitry Figol "] 6 | license = "MIT" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.6" 10 | napalm = "^2.3" 11 | netmiko = "^2.2" 12 | django = "^2.1" 13 | celery = "^4.2" 14 | gevent = "^1.4" 15 | redis = "^3.0" 16 | 17 | [tool.poetry.dev-dependencies] 18 | flake8 = "*" 19 | mypy = "*" 20 | black = { version = "*", allows-prereleases = true } 21 | isort = "*" 22 | ipython = "*" 23 | ipdb = "*" 24 | pytest = "*" 25 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/templates/dump_all.j2: -------------------------------------------------------------------------------- 1 | Module Variables ("vars"): 2 | -------------------------------- 3 | {{ vars | to_nice_json }} 4 | 5 | Environment Variables ("environment"): 6 | -------------------------------- 7 | {{ environment | to_nice_json }} 8 | 9 | GROUP NAMES Variables ("group_names"): 10 | -------------------------------- 11 | {{ group_names | to_nice_json }} 12 | 13 | GROUPS Variables ("groups"): 14 | -------------------------------- 15 | {{ groups | to_nice_json }} 16 | 17 | HOST Variables ("hostvars"): 18 | -------------------------------- 19 | {{ hostvars | to_nice_json }} -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/pair-1.yml: -------------------------------------------------------------------------------- 1 | rack: A2 2 | rack_unit: 20 3 | interfaces: 4 | - name: Ethernet0/0 5 | vrf: Mgmt 6 | ipv4_address: 192.168.122.161/24 7 | - name: Ethernet1/0 8 | ipv4_address: 192.168.122.170/24 9 | - name: Ethernet1/1 10 | - name: Ethernet1/1.1901 11 | vlan: 1901 12 | ipv4_address: 172.16.1.1/24 13 | service: internet 14 | - name: Ethernet1/1.1902 15 | vlan: 1902 16 | ipv4_address: 172.16.2.1/24 17 | service: internet 18 | - name: Ethernet1/1.1903 19 | vlan: 1903 20 | ipv4_address: 172.16.3.1/24 21 | service: internet 22 | -------------------------------------------------------------------------------- /ansible/lab-system/roles/pod-configuration-setup/tasks/main.yml: -------------------------------------------------------------------------------- 1 | - name: Reset configuration of pod gear to bootstrap 2 | ios_command: 3 | commands: 4 | - configure replace nvram:startup-config force 5 | 6 | 7 | - name: Wait for 5 seconds 8 | wait_for: 9 | timeout: 5 10 | 11 | 12 | - name: Configure pod gear according to the lab template config 13 | ios_config: 14 | src: "user-data/topologies/{{ lab_template }}/configs/{{ lab_hostname }}.txt" 15 | 16 | 17 | - name: Save current lab config to the flash 18 | ios_command: 19 | commands: 20 | - "copy running-config unix:lab-config\n\n\n" 21 | -------------------------------------------------------------------------------- /nornir/exploring/inventory/gns3/hosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | vars: 4 | username: admin 5 | password: admin 6 | ansible_connection: local 7 | ansible_python_interpreter: "/usr/bin/env python" 8 | children: 9 | sjc: 10 | hosts: 11 | SJ-R1: 12 | ansible_host: 192.168.122.11 13 | SJ-R2: 14 | ansible_host: 192.168.122.12 15 | SJ-SW1: 16 | ansible_host: 192.168.122.13 17 | SJ-SW2: 18 | ansible_host: 192.168.122.14 19 | SJ-HUB1: 20 | ansible_host: 192.168.122.15 21 | bru: 22 | hosts: 23 | 192.168.122.20: 24 | -------------------------------------------------------------------------------- /nornir/lab-system/user-data/deployments/1_mixed_1_dev.yml: -------------------------------------------------------------------------------- 1 | name: 1 mixed (2 routers and 2 switches) and 1 dev (2 routers) 2 | devices: 3 | SW1: 4 | pod_number: 100 5 | lab_hostname: SJ-DSW1 6 | SW2: 7 | pod_number: 100 8 | lab_hostname: SJ-DSW2 9 | R1: 10 | pod_number: 100 11 | lab_hostname: SJ-BR1 12 | R2: 13 | pod_number: 100 14 | lab_hostname: SJ-BR2 15 | R3: 16 | pod_number: 200 17 | lab_hostname: BRU-R1 18 | R4: 19 | pod_number: 200 20 | lab_hostname: BRU-R2 21 | pods: 22 | - pod_number: 100 23 | topology: mixed 24 | - pod_number: 200 25 | topology: dev 26 | -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'netprog_stream.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /ansible/lab-system/user-data/deployments/1_mixed_1_dev.yml: -------------------------------------------------------------------------------- 1 | deployment_name: 1 mixed (2 routers and 2 switches) and 1 dev (2 routers) 2 | devices: 3 | SW1: 4 | pod_number: 100 5 | lab_hostname: SJ-DSW1 6 | SW2: 7 | pod_number: 100 8 | lab_hostname: SJ-DSW2 9 | R1: 10 | pod_number: 100 11 | lab_hostname: SJ-BR1 12 | R2: 13 | pod_number: 100 14 | lab_hostname: SJ-BR2 15 | R3: 16 | pod_number: 200 17 | lab_hostname: BRU-R1 18 | R4: 19 | pod_number: 200 20 | lab_hostname: BRU-R2 21 | pods: 22 | - pod_number: 100 23 | lab_template: mixed 24 | - pod_number: 200 25 | lab_template: dev 26 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/inventories/gns3/hosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | vars: 4 | username: admin 5 | password: admin 6 | ansible_connection: local 7 | ansible_python_interpreter: "/usr/bin/env python" 8 | children: 9 | sjc: 10 | hosts: 11 | SJ-R1: 12 | ansible_host: 192.168.122.11 13 | SJ-R2: 14 | ansible_host: 192.168.122.12 15 | SJ-SW1: 16 | ansible_host: 192.168.122.13 17 | SJ-SW2: 18 | ansible_host: 192.168.122.14 19 | SJ-HUB1: 20 | ansible_host: 192.168.122.15 21 | bru: 22 | hosts: 23 | 192.168.122.20: 24 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/groups.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | defaults: 3 | username: admin 4 | password: admin 5 | platform: cisco_ios 6 | 7 | dynamic: 8 | advanced-dot1x__01: 9 | advanced-dot1x__02: 10 | 11 | 12 | pod-gear: 13 | pod: unallocated 14 | 15 | infra: 16 | 17 | 18 | pod-routers: 19 | groups: 20 | - pod-gear 21 | tags: 22 | - router 23 | 24 | 25 | pod-switches: 26 | groups: 27 | - pod-gear 28 | tags: 29 | - switch 30 | 31 | 32 | matrix-switches: 33 | dot1q_tunnel_vlan_start: 2000 34 | groups: 35 | - infra 36 | 37 | pair-routers: 38 | groups: 39 | - infra 40 | 41 | pod-mgmt: 42 | groups: 43 | - infra -------------------------------------------------------------------------------- /scrapli-apps/constants.py: -------------------------------------------------------------------------------- 1 | USERNAME = "cisco" 2 | PASSWORD = "cisco" 3 | DEVICES = [ 4 | {"device_name": "R1", "host": "192.168.152.101"}, 5 | {"device_name": "R2", "host": "192.168.152.102"}, 6 | {"device_name": "R3", "host": "192.168.152.103"}, 7 | {"device_name": "R4", "host": "192.168.152.104"}, 8 | {"device_name": "R5", "host": "192.168.152.105"}, 9 | {"device_name": "R6", "host": "192.168.152.106"}, 10 | {"device_name": "R7", "host": "192.168.152.107"}, 11 | {"device_name": "R8", "host": "192.168.152.108"}, 12 | {"device_name": "R9", "host": "192.168.152.109"}, 13 | {"device_name": "R10", "host": "192.168.152.110"}, 14 | ] 15 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/collect_outputs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Collect facts 3 | gather_facts: no 4 | hosts: sjc 5 | connection: local 6 | vars: 7 | credentials: 8 | host: "{{ ansible_host }}" 9 | username: "{{ username }}" 10 | password: "{{ password }}" 11 | 12 | tasks: 13 | - name: run show version on remote devices 14 | ios_command: 15 | commands: 16 | - show version 17 | - show memory statistics 18 | - show arp 19 | - show mac address-table 20 | provider: "{{ credentials }}" 21 | register: output 22 | 23 | - debug: 24 | msg: "{{ output.stdout_lines }}" 25 | -------------------------------------------------------------------------------- /model-driven-telemetry/netconf/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | zookeeper: 4 | image: wurstmeister/zookeeper 5 | ports: 6 | - "2181:2181" 7 | kafka: 8 | image: wurstmeister/kafka:2.12-2.4.0 9 | ports: 10 | - "9092:9092" 11 | environment: 12 | KAFKA_ADVERTISED_HOST_NAME: localhost 13 | KAFKA_CREATE_TOPICS: "test:1:1" 14 | KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 15 | volumes: 16 | - /var/run/docker.sock:/var/run/docker.sock 17 | opentsdb: 18 | image: petergrace/opentsdb-docker:latest 19 | ports: 20 | - 4242:4242 21 | grafana: 22 | image: grafana/grafana:6.5.0 23 | ports: 24 | - 3000:3000 -------------------------------------------------------------------------------- /salt/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2.1' 2 | 3 | services: 4 | salt-master: 5 | image: mirceaulinic/salt-master:2018.3.0 6 | hostname: salt-master 7 | container_name: salt-master 8 | environment: 9 | - LOG_LEVEL 10 | volumes: 11 | - ${PWD}/master:/etc/salt/master 12 | - ${PWD}/pillar/:/etc/salt/pillar/ 13 | - ${PWD}/states/:/etc/salt/states/ 14 | salt-proxy: 15 | image: mirceaulinic/salt-proxy:2018.3.0 16 | hostname: ${PROXYID} 17 | container_name: salt-proxy-${PROXYID} 18 | volumes: 19 | - ${PWD}/proxy:/etc/salt/proxy 20 | environment: 21 | - LOG_LEVEL 22 | - PROXYID 23 | depends_on: 24 | - salt-master 25 | -------------------------------------------------------------------------------- /model-driven-telemetry/netconf/rpc-examples/periodic-subscription-kernel-cpu.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | yp:yang-push 7 | /platform-sw-ios-xe-oper:cisco-platform-software/control-processes/control-process[fru='fru-rp'][slot='0'][bay='0'][chassis='-1']/per-core-stats/per-core-stat[name='1'] 8 | 1000 9 | 10 | ]]>]]> 11 | -------------------------------------------------------------------------------- /network-diagram-visualization-js/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/templates/netconf/vrf.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | {% if vrfs is defined %} 4 | 5 | {% for vrf in vrfs %} 6 | 7 | {{ vrf.name }} 8 | 9 | {% if vrf.ipv4 is defined and vrf.ipv4 %} 10 | 11 | {% endif %} 12 | {% if vrf.ipv6 is defined and vrf.ipv6 %} 13 | 14 | {% endif %} 15 | 16 | 17 | {% endfor %} 18 | 19 | {% else %} 20 | 21 | {% endif %} 22 | 23 | -------------------------------------------------------------------------------- /netprog-stream-django/requirements.txt: -------------------------------------------------------------------------------- 1 | asn1crypto==0.24.0 2 | bcrypt==3.1.4 3 | certifi==2018.8.24 4 | cffi==1.11.5 5 | chardet==3.0.4 6 | cryptography==2.3.1 7 | Django==2.1.2 8 | enum34==1.1.6 9 | future==0.16.0 10 | idna==2.7 11 | ipaddress==1.0.22 12 | Jinja2==2.10 13 | junos-eznc==2.2.0 14 | lxml==4.2.5 15 | MarkupSafe==1.0 16 | napalm==2.3.2 17 | ncclient==0.6.3 18 | netaddr==0.7.19 19 | netmiko==2.2.2 20 | paramiko==2.4.2 21 | pyasn1==0.4.4 22 | pycparser==2.19 23 | pyeapi==0.8.2 24 | pyIOSXR==0.53 25 | PyNaCl==1.3.0 26 | pynxos==0.0.3 27 | pyserial==3.4 28 | pytz==2018.5 29 | PyYAML==3.13 30 | requests==2.19.1 31 | scp==0.11.0 32 | selectors2==2.0.1 33 | six==1.11.0 34 | textfsm==0.4.1 35 | urllib3==1.23 36 | -------------------------------------------------------------------------------- /nornir/lab-system/topologies/mixed/configs/SJ-DSW1.txt: -------------------------------------------------------------------------------- 1 | hostname SJ-DSW1 2 | vtp mode transparent 3 | vlan 10,20 4 | ! 5 | interface Ethernet1/1 6 | switchport mode access 7 | switchport access vlan 10 8 | spanning-tree portfast 9 | no shutdown 10 | ! 11 | interface Ethernet1/2 12 | switchport mode access 13 | switchport access vlan 20 14 | spanning-tree portfast 15 | no shutdown 16 | ! 17 | interface Ethernet1/0 18 | switchport trunk encapsulation dot1q 19 | switchport mode trunk 20 | switchport trunk allowed vlan all 21 | no shutdown 22 | ! 23 | interface vlan10 24 | ip addr 10.0.10.101 255.255.255.0 25 | no shutdown 26 | ! 27 | interface vlan20 28 | ip addr 10.0.20.101 255.255.255.0 29 | no shutdown 30 | ! -------------------------------------------------------------------------------- /nornir/lab-system/topologies/mixed/configs/SJ-DSW2.txt: -------------------------------------------------------------------------------- 1 | hostname SJ-DSW2 2 | vtp mode transparent 3 | vlan 10,20 4 | ! 5 | interface Ethernet1/1 6 | switchport mode access 7 | switchport access vlan 20 8 | spanning-tree portfast 9 | no shutdown 10 | ! 11 | interface Ethernet1/2 12 | switchport mode access 13 | switchport access vlan 10 14 | spanning-tree portfast 15 | no shutdown 16 | ! 17 | interface Ethernet1/0 18 | switchport trunk encapsulation dot1q 19 | switchport mode trunk 20 | switchport trunk allowed vlan all 21 | no shutdown 22 | ! 23 | interface vlan10 24 | ip addr 10.0.10.102 255.255.255.0 25 | no shutdown 26 | ! 27 | interface vlan20 28 | ip addr 10.0.20.102 255.255.255.0 29 | no shutdown 30 | ! -------------------------------------------------------------------------------- /nornir/lab-system/topologies/mixed/topology.yml: -------------------------------------------------------------------------------- 1 | connections: 2 | - - lab_hostname: SJ-BR1 3 | port: Ethernet1/1 4 | - lab_hostname: SJ-DSW1 5 | port: Ethernet1/1 6 | - - lab_hostname: SJ-BR1 7 | port: Ethernet1/2 8 | - lab_hostname: SJ-DSW2 9 | port: Ethernet1/1 10 | - - lab_hostname: SJ-BR2 11 | port: Ethernet1/1 12 | - lab_hostname: SJ-DSW1 13 | port: Ethernet1/2 14 | - - lab_hostname: SJ-BR2 15 | port: Ethernet1/2 16 | - lab_hostname: SJ-DSW2 17 | port: Ethernet1/2 18 | - - lab_hostname: SJ-DSW1 19 | port: Ethernet1/0 20 | - lab_hostname: SJ-DSW2 21 | port: Ethernet1/0 22 | # - - lab_hostname: SJ-DSW1 23 | # port: Ethernet1/0 24 | # - service: Client1 25 | -------------------------------------------------------------------------------- /nornir/lab-system/topologies/advanced/configs/rtp-access.txt: -------------------------------------------------------------------------------- 1 | hostname rtp-access 2 | vtp mode transparent 3 | vlan 10,20 4 | ! 5 | interface Ethernet1/1 6 | switchport mode access 7 | switchport access vlan 20 8 | spanning-tree portfast 9 | no shutdown 10 | ! 11 | interface Ethernet1/2 12 | switchport mode access 13 | switchport access vlan 10 14 | spanning-tree portfast 15 | no shutdown 16 | ! 17 | interface Ethernet1/0 18 | switchport trunk encapsulation dot1q 19 | switchport mode trunk 20 | switchport trunk allowed vlan all 21 | no shutdown 22 | ! 23 | interface vlan10 24 | ip addr 10.0.10.102 255.255.255.0 25 | no shutdown 26 | ! 27 | interface vlan20 28 | ip addr 10.0.20.102 255.255.255.0 29 | no shutdown 30 | ! -------------------------------------------------------------------------------- /nornir/lab-system/topologies/advanced/configs/sjc-access.txt: -------------------------------------------------------------------------------- 1 | hostname sjc-access 2 | vtp mode transparent 3 | vlan 10,20 4 | ! 5 | interface Ethernet1/1 6 | switchport mode access 7 | switchport access vlan 10 8 | spanning-tree portfast 9 | no shutdown 10 | ! 11 | interface Ethernet1/2 12 | switchport mode access 13 | switchport access vlan 20 14 | spanning-tree portfast 15 | no shutdown 16 | ! 17 | interface Ethernet1/0 18 | switchport trunk encapsulation dot1q 19 | switchport mode trunk 20 | switchport trunk allowed vlan all 21 | no shutdown 22 | ! 23 | interface vlan10 24 | ip addr 10.0.10.101 255.255.255.0 25 | no shutdown 26 | ! 27 | interface vlan20 28 | ip addr 10.0.20.101 255.255.255.0 29 | no shutdown 30 | ! -------------------------------------------------------------------------------- /ansible/lab-system/user-data/lab-templates/mixed/configs/SJ-DSW1.txt: -------------------------------------------------------------------------------- 1 | hostname SJ-DSW1 2 | vtp mode transparent 3 | vlan 10,20 4 | ! 5 | interface Ethernet1/1 6 | switchport mode access 7 | switchport access vlan 10 8 | spanning-tree portfast 9 | no shutdown 10 | ! 11 | interface Ethernet1/2 12 | switchport mode access 13 | switchport access vlan 20 14 | spanning-tree portfast 15 | no shutdown 16 | ! 17 | interface Ethernet1/0 18 | switchport trunk encapsulation dot1q 19 | switchport mode trunk 20 | switchport trunk allowed vlan all 21 | no shutdown 22 | ! 23 | interface vlan10 24 | ip addr 10.0.10.101 255.255.255.0 25 | no shutdown 26 | ! 27 | interface vlan20 28 | ip addr 10.0.20.101 255.255.255.0 29 | no shutdown 30 | ! -------------------------------------------------------------------------------- /ansible/lab-system/user-data/lab-templates/mixed/configs/SJ-DSW2.txt: -------------------------------------------------------------------------------- 1 | hostname SJ-DSW2 2 | vtp mode transparent 3 | vlan 10,20 4 | ! 5 | interface Ethernet1/1 6 | switchport mode access 7 | switchport access vlan 20 8 | spanning-tree portfast 9 | no shutdown 10 | ! 11 | interface Ethernet1/2 12 | switchport mode access 13 | switchport access vlan 10 14 | spanning-tree portfast 15 | no shutdown 16 | ! 17 | interface Ethernet1/0 18 | switchport trunk encapsulation dot1q 19 | switchport mode trunk 20 | switchport trunk allowed vlan all 21 | no shutdown 22 | ! 23 | interface vlan10 24 | ip addr 10.0.10.102 255.255.255.0 25 | no shutdown 26 | ! 27 | interface vlan20 28 | ip addr 10.0.20.102 255.255.255.0 29 | no shutdown 30 | ! -------------------------------------------------------------------------------- /netprog-stream-django/Dockerfile-dev: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | LABEL maintainer="Dmitry Figol " 3 | 4 | WORKDIR /app 5 | 6 | RUN apk add --no-cache \ 7 | build-base \ 8 | libffi-dev \ 9 | openssl-dev \ 10 | gcc \ 11 | libxslt-dev \ 12 | libxslt \ 13 | libxml2 \ 14 | libxml2-dev \ 15 | && pip install --no-cache-dir poetry \ 16 | && poetry config settings.virtualenvs.in-project true 17 | 18 | COPY pyproject.toml . 19 | COPY poetry.lock . 20 | 21 | RUN poetry run pip install -U pip \ 22 | && poetry install --no-dev --no-interaction 23 | 24 | EXPOSE 8000 25 | 26 | WORKDIR /app/netprog_stream 27 | 28 | CMD ["poetry", "run", "python", "manage.py", "runserver", "0.0.0.0:8000"] -------------------------------------------------------------------------------- /network-diagram-visualization-js/src/components/HelloWorld.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 19 | 20 | 21 | 37 | -------------------------------------------------------------------------------- /ansible/lab-system/user-data/lab-templates/mixed/topology.yml: -------------------------------------------------------------------------------- 1 | connections: 2 | - - lab_hostname: SJ-BR1 3 | port: Ethernet1/1 4 | - lab_hostname: SJ-DSW1 5 | port: Ethernet1/1 6 | - - lab_hostname: SJ-BR1 7 | port: Ethernet1/2 8 | - lab_hostname: SJ-DSW2 9 | port: Ethernet1/1 10 | - - lab_hostname: SJ-BR2 11 | port: Ethernet1/1 12 | - lab_hostname: SJ-DSW1 13 | port: Ethernet1/2 14 | - - lab_hostname: SJ-BR2 15 | port: Ethernet1/2 16 | - lab_hostname: SJ-DSW2 17 | port: Ethernet1/2 18 | - - lab_hostname: SJ-DSW1 19 | port: Ethernet1/0 20 | - lab_hostname: SJ-DSW2 21 | port: Ethernet1/0 22 | # - - lab_hostname: SJ-DSW1 23 | # port: Ethernet1/0 24 | # - service: Client1 25 | -------------------------------------------------------------------------------- /network-diagram-visualization-js/src/App.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 34 | -------------------------------------------------------------------------------- /nornir/lab-system/user-data/deployments/2_mixed.yml: -------------------------------------------------------------------------------- 1 | name: 2 mixed topologies (2 routers and 2 switches each) 2 | devices: 3 | SW1: 4 | pod_number: 100 5 | lab_hostname: SJ-DSW1 6 | SW2: 7 | pod_number: 100 8 | lab_hostname: SJ-DSW2 9 | R1: 10 | pod_number: 100 11 | lab_hostname: SJ-BR1 12 | R2: 13 | pod_number: 100 14 | lab_hostname: SJ-BR2 15 | SW3: 16 | pod_number: 101 17 | lab_hostname: SJ-DSW1 18 | SW4: 19 | pod_number: 101 20 | lab_hostname: SJ-DSW2 21 | R3: 22 | pod_number: 101 23 | lab_hostname: SJ-BR1 24 | R4: 25 | pod_number: 101 26 | lab_hostname: SJ-BR2 27 | pods: 28 | - pod_number: 100 29 | topology: mixed 30 | - pod_number: 101 31 | topology: mixed 32 | # - pod_number: 200 33 | # lab_template: dev 34 | -------------------------------------------------------------------------------- /ansible/lab-system/user-data/deployments/2_mixed.yml: -------------------------------------------------------------------------------- 1 | deployment_name: 2 mixed topologies (2 routers and 2 switches each) 2 | devices: 3 | SW1: 4 | pod_number: 100 5 | lab_hostname: SJ-DSW1 6 | SW2: 7 | pod_number: 100 8 | lab_hostname: SJ-DSW2 9 | R1: 10 | pod_number: 100 11 | lab_hostname: SJ-BR1 12 | R2: 13 | pod_number: 100 14 | lab_hostname: SJ-BR2 15 | SW3: 16 | pod_number: 101 17 | lab_hostname: SJ-DSW1 18 | SW4: 19 | pod_number: 101 20 | lab_hostname: SJ-DSW2 21 | R3: 22 | pod_number: 101 23 | lab_hostname: SJ-BR1 24 | R4: 25 | pod_number: 101 26 | lab_hostname: SJ-BR2 27 | pods: 28 | - pod_number: 100 29 | topology: mixed 30 | - pod_number: 101 31 | topology: mixed 32 | # - pod_number: 200 33 | # lab_template: dev 34 | -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/netprog_stream/celery.py: -------------------------------------------------------------------------------- 1 | import os 2 | from celery import Celery 3 | 4 | # set the default Django settings module for the 'celery' program. 5 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'netprog_stream.settings') 6 | 7 | app = Celery('netprog_stream') 8 | 9 | # Using a string here means the worker doesn't have to serialize 10 | # the configuration object to child processes. 11 | # - namespace='CELERY' means all celery-related configuration keys 12 | # should have a `CELERY_` prefix. 13 | app.config_from_object('django.conf:settings', namespace='CELERY') 14 | 15 | # Load task modules from all registered Django app configs. 16 | app.autodiscover_tasks() 17 | 18 | 19 | 20 | @app.task(bind=True) 21 | def debug_task(self): 22 | print('Request: {0!r}'.format(self.request)) 23 | -------------------------------------------------------------------------------- /chatops-webex-teams/payload-examples/webex-message-group.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "Y2lzY29zcGFyazovL3VzL01FU1NBR0UvZTdlNjAxZDAtNWZjOC0xMWVhLWJlYTgtOTE3NDQwYTgwZDhh", 3 | "roomId": "Y2lzY29zcGFyazovL3VzL1JPT00vNDJkYWNiODAtMjEyMi0xMWU4LThiMjMtNGZmOThhYjM4OWY2", 4 | "roomType": "group", 5 | "text": "@Overwatch hi", 6 | "personId": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS9mNzI4OTVjYy00MzI3LTQwNGItYTlmOS1lODNiZDBhNzA2YTk", 7 | "personEmail": "test@example.com", 8 | "html": "
Overwatch hi
", 9 | "mentionedPeople": [ 10 | "Y2lzY29zcGFyazovL3VzL1BFT1BMRS83Y2E1MWJmYy1lNjhlLTRiNmQtYWEzZi1kNmZkNzkyNjZkODY" 11 | ], 12 | "created": "2020-03-06T16:38:27.693Z" 13 | } -------------------------------------------------------------------------------- /netconf/templates/loopbacks.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% for loopback in loopbacks %} 5 | 6 | {{ loopback.number }} 7 | {% if loopback.description is defined %} 8 | {{ loopback.description }} 9 | {% endif %} 10 | {% if loopback.ipv4_address is defined %} 11 | 12 |
13 | 14 |
{{ loopback.ipv4_address }}
15 | {{ loopback.ipv4_mask }} 16 |
17 |
18 |
19 | {% endif %} 20 |
21 | {% endfor %} 22 |
23 |
24 |
-------------------------------------------------------------------------------- /ansible/lab-system/templates/l2_interfaces.j2: -------------------------------------------------------------------------------- 1 | {% for interface in interfaces %} 2 | interface {{ interface.name }} 3 | {% if interface.description is defined %} 4 | description {{ interface.description }} 5 | {% endif %} 6 | {% if interface.mode == 'trunk' %} 7 | switchport trunk encapsulation dot1q 8 | {% elif interface.mode == 'dot1q-tunnel' %} 9 | {% if interface.access_vlan is defined %} 10 | switchport access vlan {{ interface.access_vlan }} 11 | {% endif %} 12 | l2protocol-tunnel cdp 13 | l2protocol-tunnel lldp 14 | l2protocol-tunnel stp 15 | l2protocol-tunnel vtp 16 | {% endif %} 17 | switchport mode {{ interface.mode }} 18 | {% if interface.state is defined %} 19 | {% if interface.state == 'present' %} 20 | no shutdown 21 | {% elif interface.state == 'absent' %} 22 | shutdown 23 | {% endif %} 24 | {% endif %} 25 | ! 26 | {% endfor %} -------------------------------------------------------------------------------- /model-driven-telemetry/grpc/router.cfg: -------------------------------------------------------------------------------- 1 | netconf-yang 2 | telemetry ietf subscription 101 3 | encoding encode-kvgpb 4 | filter xpath /memory-ios-xe-oper:memory-statistics/memory-statistic 5 | stream yang-push 6 | update-policy periodic 6000 7 | source-vrf mgmt 8 | receiver ip address 192.168.153.100 57555 protocol grpc-tcp 9 | telemetry ietf subscription 102 10 | encoding encode-kvgpb 11 | filter xpath /cdp-ios-xe-oper:cdp-neighbor-details/cdp-neighbor-detail 12 | stream yang-push 13 | update-policy on-change 14 | source-vrf mgmt 15 | receiver ip address 192.168.153.100 57555 protocol grpc-tcp 16 | telemetry ietf subscription 103 17 | encoding encode-kvgpb 18 | filter xpath /platform-sw-ios-xe-oper:cisco-platform-software/control-processes 19 | stream yang-push 20 | update-policy periodic 1000 21 | source-vrf mgmt 22 | receiver ip address 192.168.153.100 57555 protocol grpc-tcp 23 | -------------------------------------------------------------------------------- /juniper/vars.yml: -------------------------------------------------------------------------------- 1 | devices: 2 | vMX1: 3 | local_as_number: 12 4 | interfaces: 5 | - name: ge-0/0/1 6 | description: Connected to vMX2 7 | ip_address: 10.12.12.1/24 8 | vMX2: 9 | local_as_number: 12 10 | interfaces: 11 | - name: ge-0/0/1 12 | description: Connected to vMX1 13 | ip_address: 10.12.12.2/24 14 | - name: ge-0/0/2 15 | description: Connected to vMX3 16 | ip_address: 209.165.200.2/24 17 | routing: 18 | bgp: 19 | external_neighbors: 20 | - ip_address: 209.165.200.3 21 | as_number: 3 22 | vMX3: 23 | local_as_number: 3 24 | interfaces: 25 | - name: ge-0/0/1 26 | description: Connected to vMX2 27 | ip_address: 209.165.200.3/24 28 | routing: 29 | bgp: 30 | external_neighbors: 31 | - ip_address: 209.165.200.2 32 | as_number: 12 -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/netprog_stream/urls.py: -------------------------------------------------------------------------------- 1 | """netprog_stream URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | 19 | urlpatterns = [ 20 | path('admin/', admin.site.urls), 21 | path('', include('network.urls')), 22 | ] 23 | -------------------------------------------------------------------------------- /napalm/sandbox.py: -------------------------------------------------------------------------------- 1 | from napalm import get_network_driver 2 | 3 | ARISTA_SW_PARAMS = { 4 | "hostname": "192.168.122.23", "username": "admin", "password": "admin" 5 | } 6 | 7 | CSW1_PARAMS = { 8 | "hostname": "192.168.122.31", "username": "admin", "password": "admin" 9 | } 10 | 11 | CONFIG = """interface Ethernet12 12 | description Connected to PC3 13 | """ 14 | 15 | 16 | def main(): 17 | arista_driver = get_network_driver("eos") 18 | ios_driver = get_network_driver("ios") 19 | arista_sw = arista_driver(**ARISTA_SW_PARAMS) 20 | 21 | with arista_driver(**ARISTA_SW_PARAMS) as arista_sw, : 22 | arista_sw.load_merge_candidate(config=CONFIG) 23 | print(arista_sw.compare_config()) 24 | arista_sw.commit_config() 25 | print(arista_sw.get_facts()) 26 | print(arista_sw.get_interface_counters()) 27 | print(arista_sw.get_arp_table()) 28 | 29 | 30 | 31 | if __name__ == "__main__": 32 | main() 33 | -------------------------------------------------------------------------------- /chatops-webex-teams/network_overwatch/command_handler.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Any, Callable, Awaitable 2 | 3 | from network_overwatch.webex_teams import WebexTeams 4 | from network_overwatch.restconf import RESTCONF 5 | 6 | 7 | async def vrf_handler( 8 | command_args: str, message_data: Dict[str, Any], webex_teams: WebexTeams 9 | ) -> None: 10 | device_name = command_args.strip().upper() 11 | restconf = RESTCONF(device_name=device_name) 12 | vrfs = await restconf.get_vrf_list() 13 | vrfs_md = " \n".join([f"* **{vrf}**" for vrf in vrfs]) 14 | md = f"**{device_name}** has VRFs:\n {vrfs_md}\n\n" 15 | message_data = {"markdown": md, "roomId": message_data["roomId"]} 16 | await webex_teams.messages.create(message_data) 17 | 18 | 19 | DISPATCH: Dict[str, Callable[[str, Dict[str, Any], WebexTeams], Awaitable[None]]] = { 20 | "vrf": vrf_handler, 21 | } 22 | 23 | 24 | def dispatch_command(command): 25 | return DISPATCH[command] 26 | -------------------------------------------------------------------------------- /model-driven-telemetry/netconf/code/test_xpath.py: -------------------------------------------------------------------------------- 1 | from ncclient import manager 2 | 3 | import constants 4 | import utils 5 | 6 | # filter xpath /platform-sw-ios-xe-oper:cisco-platform-software/control-processes/control-process[fru="fru-rp" and slot=0 and bay=0 and chassis=-1]/per-core-stats 7 | # XPATH_FILTER = "access-lists/access-list/access-list-entries/access-list-entry[rule-name='20']/access-list-entries-oper-data/match-counter" 8 | # XPATH_FILTER = "cisco-platform-software/control-processes/control-process[fru='fru-rp' and slot=0 and bay=0 and chassis=-1]/per-core-stats" 9 | XPATH_FILTER = "cisco-platform-software/control-processes/control-process[fru='fru-rp'][slot='0'][bay='0'][chassis='-1']/per-core-stats/per-core-stat[name='1']" 10 | 11 | def main(): 12 | with manager.connect(**constants.NC_CONN_PARAMS) as m: 13 | nc_reply = m.get(filter=('xpath', XPATH_FILTER)) 14 | print(utils.prettify_xml(nc_reply.xml)) 15 | 16 | if __name__ == "__main__": 17 | main() -------------------------------------------------------------------------------- /network-diagram-visualization-js/src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | import Home from "../views/Home.vue"; 4 | import NetworkDiagramView from "../views/NetworkDiagramView.vue"; 5 | // import { Network } from "vue-visjs"; 6 | 7 | Vue.use(VueRouter); 8 | // Vue.component("network", Network); 9 | 10 | const routes = [ 11 | { 12 | path: "/", 13 | name: "Home", 14 | component: Home, 15 | }, 16 | { 17 | path: "/diagram", 18 | name: "NetworkDiagram", 19 | component: NetworkDiagramView, 20 | }, 21 | { 22 | path: "/about", 23 | name: "About", 24 | // route level code-splitting 25 | // this generates a separate chunk (about.[hash].js) for this route 26 | // which is lazy-loaded when the route is visited. 27 | component: () => 28 | import(/* webpackChunkName: "about" */ "../views/About.vue"), 29 | }, 30 | ]; 31 | 32 | const router = new VueRouter({ 33 | routes, 34 | }); 35 | 36 | export default router; 37 | -------------------------------------------------------------------------------- /netprog-stream-django/docker-compose.dev.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | django: 5 | build: 6 | context: . 7 | dockerfile: Dockerfile-dev 8 | image: netprog-stream-django:dev 9 | container_name: django 10 | environment: 11 | - PYTHONUNBUFFERED=1 12 | volumes: 13 | - ./log:/log 14 | - ./netprog_stream:/app/netprog_stream 15 | 16 | redis: 17 | image: redis:4-alpine 18 | restart: on-failure 19 | container_name: redis 20 | sysctls: 21 | net.core.somaxconn: '511' 22 | volumes: 23 | - ./data:/data 24 | 25 | celery: 26 | build: 27 | context: . 28 | dockerfile: Dockerfile-dev 29 | container_name: celery 30 | image: netprog-stream-django:dev 31 | command: >- 32 | poetry run celery worker 33 | -A netprog_stream 34 | --loglevel=info --pool=gevent --concurrency=15 35 | volumes: 36 | - ./log:/log 37 | - ./netprog_stream:/app/netprog_stream 38 | depends_on: 39 | - redis -------------------------------------------------------------------------------- /nornir/exploring/inventory/gns3/host_vars/SJ-ISW1.yml: -------------------------------------------------------------------------------- 1 | vlans: 2 | - vlan_id: 999 3 | name: Native 4 | state: present 5 | - vlan_id: 2012 6 | name: BR1 <-> BR2 7 | state: present 8 | - vlan_id: 2018 9 | name: BR1 <-> ISP1 10 | state: present 11 | - vlan_id: 2019 12 | name: BR2 <-> ISP1 13 | state: present 14 | - vlan_id: 2028 15 | name: BR1 <-> ISP2 16 | state: present 17 | - vlan_id: 2029 18 | name: BR2 <-> ISP2 19 | state: present 20 | 21 | interfaces: 22 | - name: Ethernet1/0 23 | mode: trunk 24 | trunk_allowed_vlans: 2012,2018,2019,2028,2029 25 | native_vlan: 999 26 | - name: Ethernet1/1 27 | mode: trunk 28 | trunk_allowed_vlans: 2012,2018,2019,2028,2029 29 | native_vlan: 999 30 | - name: Ethernet1/2 31 | mode: trunk 32 | trunk_allowed_vlans: 2012,2018,2019,2028,2029 33 | native_vlan: 999 34 | - name: Ethernet1/3 35 | mode: trunk 36 | trunk_allowed_vlans: 2012,2018,2019,2028,2029 37 | native_vlan: 999 38 | -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/templates/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Awesome network controller 6 | 7 | 8 | 9 |
10 |

{{ title }}

11 |

This is {{ name }}.

12 |

Devices

13 | 14 | 15 | 16 | 17 | 18 | 19 | {% for device in devices %} 20 | 21 | 22 | 23 | 24 | 25 | {% endfor %} 26 |
IdNameHost
{{ device.id }}{{ device.name }}{{ device.host }}
27 |
28 | 29 | -------------------------------------------------------------------------------- /nornir/network_diagram/link.py: -------------------------------------------------------------------------------- 1 | from typing import List, TYPE_CHECKING 2 | 3 | if TYPE_CHECKING: 4 | from interface import Interface 5 | 6 | 7 | class Link: 8 | def __init__(self, interfaces: List["Interface"]) -> None: 9 | self.interfaces = sorted(interfaces) 10 | 11 | def __eq__(self, other) -> bool: 12 | return all( 13 | int1 == int2 14 | for int1, int2 in zip(self.interfaces, other.interfaces) 15 | ) 16 | 17 | def __hash__(self) -> int: 18 | return hash(tuple(self.interfaces)) 19 | 20 | def __str__(self) -> str: 21 | return " <-> ".join( 22 | str(interface) 23 | for interface in self.interfaces 24 | ) 25 | 26 | def __repr__(self) -> str: 27 | return ( 28 | f"{self.__class__.__qualname__}(" 29 | f"interfaces={self.interfaces})" 30 | ) 31 | 32 | @property 33 | def is_point_to_point(self) -> bool: 34 | return len(self.interfaces) == 2 35 | 36 | -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/tasks.py: -------------------------------------------------------------------------------- 1 | from typing import Any 2 | 3 | from celery import shared_task 4 | from netmiko import ConnectHandler 5 | 6 | from network.models import Device 7 | 8 | @shared_task 9 | def switch_interface(device_id: str, interface_name: str, enable_interface: str) -> Any: 10 | device = Device.objects.get(pk=device_id) 11 | config_commands = [f'interface {interface_name}'] 12 | result = {"interface_name": interface_name} 13 | if enable_interface == 'False': 14 | config_commands.append(' shutdown') 15 | result["up"] = False 16 | else: 17 | config_commands.append(' no shutdown') 18 | result["up"] = True 19 | conn_params = { 20 | 'ip': device.host, 21 | 'username': device.username, 22 | 'password': device.password, 23 | 'device_type': device.netmiko_device_type, 24 | } 25 | with ConnectHandler(**conn_params) as device_conn: 26 | device_conn.send_config_set(config_commands) 27 | return result 28 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/inventories/gns3/host_vars/SJ-ISW1.yml: -------------------------------------------------------------------------------- 1 | vlans: 2 | - vlan_id: 999 3 | name: Native 4 | state: present 5 | - vlan_id: 2012 6 | name: BR1 <-> BR2 7 | state: present 8 | - vlan_id: 2018 9 | name: BR1 <-> ISP1 10 | state: present 11 | - vlan_id: 2019 12 | name: BR2 <-> ISP1 13 | state: present 14 | - vlan_id: 2028 15 | name: BR1 <-> ISP2 16 | state: present 17 | - vlan_id: 2029 18 | name: BR2 <-> ISP2 19 | state: present 20 | 21 | interfaces: 22 | - name: Ethernet1/0 23 | mode: trunk 24 | trunk_allowed_vlans: 2012,2018,2019,2028,2029 25 | native_vlan: 999 26 | - name: Ethernet1/1 27 | mode: trunk 28 | trunk_allowed_vlans: 2012,2018,2019,2028,2029 29 | native_vlan: 999 30 | - name: Ethernet1/2 31 | mode: trunk 32 | trunk_allowed_vlans: 2012,2018,2019,2028,2029 33 | native_vlan: 999 34 | - name: Ethernet1/3 35 | mode: trunk 36 | trunk_allowed_vlans: 2012,2018,2019,2028,2029 37 | native_vlan: 999 38 | -------------------------------------------------------------------------------- /scrapli-apps/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "scrapli-apps" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["Dmitry Figol "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.9" 9 | scrapli = {extras = ["ssh2", "textfsm", "paramiko", "genie", "asyncssh"], version = "*", allow-prereleases = true } 10 | netmiko = "^3" 11 | scrapli-netconf = { version = "*", allow-prereleases = true } 12 | scrapli-cfg = { git = "https://github.com/scrapli/scrapli_cfg.git", branch = "main", allow-prereleases = true } 13 | uvloop = "*" 14 | aiofiles = "*" 15 | ncclient = "*" 16 | "ruamel.yaml" = "*" 17 | genie = "^21.3" 18 | pyats = "^21.3" 19 | 20 | [tool.poetry.dev-dependencies] 21 | bpython = "*" 22 | pdbpp = "*" 23 | flake8 = "*" 24 | black = { version = "*", allow-prereleases = true } 25 | pytest-asyncio = "*" 26 | pytest = "*" 27 | scrapli-replay = { git = "https://github.com/scrapli/scrapli_replay.git", branch = "main", allow-prereleases = true } 28 | 29 | [build-system] 30 | requires = ["poetry_core>=1.0.0"] 31 | build-backend = "poetry.core.masonry.api" 32 | -------------------------------------------------------------------------------- /scrapli-apps/test-scrapli-replay.py: -------------------------------------------------------------------------------- 1 | from scrapli import AsyncScrapli 2 | import re 3 | from typing import cast, TYPE_CHECKING 4 | 5 | import pytest 6 | 7 | if TYPE_CHECKING: 8 | from scrapli.driver import AsyncNetworkDriver 9 | 10 | DEVICE = { 11 | "host": "192.168.152.101", 12 | "auth_username": "cisco", 13 | "auth_password": "cisco", 14 | "auth_strict_key": False, 15 | "platform": "cisco_iosxe", 16 | "transport": "asyncssh", 17 | } 18 | 19 | 20 | async def get_serial_num(): 21 | async with AsyncScrapli(**DEVICE) as device_conn: 22 | device_conn = cast("AsyncNetworkDriver", device_conn) 23 | response = await device_conn.send_command("show license udi") 24 | if re_match := re.search(r"SN:(?P\w+)", response.result): 25 | return re_match.group("serial_num") 26 | raise ValueError("Serial number not found") 27 | 28 | 29 | @pytest.mark.asyncio 30 | @pytest.mark.scrapli_replay 31 | async def test_get_serial_num(): 32 | sn = await get_serial_num() 33 | print(sn) 34 | assert len(sn) == 11 35 | -------------------------------------------------------------------------------- /network-diagram-visualization-js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "network-diagram-js", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.4", 12 | "css-loader": "^3.4.2", 13 | "style-loader": "^1.1.3", 14 | "vis-network": "^7.3.4", 15 | "vue": "^2.6.11", 16 | "vue-d3-network": "^0.1.28", 17 | "vue-router": "^3.1.5", 18 | "vue-visjs": "^0.3.0" 19 | }, 20 | "devDependencies": { 21 | "@vue/cli-plugin-babel": "~4.2.0", 22 | "@vue/cli-plugin-eslint": "~4.2.0", 23 | "@vue/cli-plugin-router": "~4.2.0", 24 | "@vue/cli-service": "~4.2.0", 25 | "@vue/eslint-config-prettier": "^6.0.0", 26 | "babel-eslint": "^10.0.3", 27 | "eslint": "^6.7.2", 28 | "eslint-plugin-prettier": "^3.1.1", 29 | "eslint-plugin-vue": "^6.1.2", 30 | "prettier": "^1.19.1", 31 | "sass": "^1.25.0", 32 | "sass-loader": "^8.0.2", 33 | "vue-template-compiler": "^2.6.11" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /ansible/lab-system/hosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | children: 4 | pod-gear: 5 | children: 6 | pod-routers: 7 | hosts: 8 | R1: 9 | ansible_host: 192.168.122.101 10 | R2: 11 | ansible_host: 192.168.122.102 12 | R3: 13 | ansible_host: 192.168.122.103 14 | R4: 15 | ansible_host: 192.168.122.104 16 | pod-switches: 17 | hosts: 18 | SW1: 19 | ansible_host: 192.168.122.111 20 | SW2: 21 | ansible_host: 192.168.122.112 22 | SW3: 23 | ansible_host: 192.168.122.113 24 | SW4: 25 | ansible_host: 192.168.122.114 26 | infra: 27 | children: 28 | matrix-switches: 29 | hosts: 30 | matrix-1: 31 | ansible_host: 192.168.122.151 32 | matrix-2: 33 | ansible_host: 192.168.122.152 34 | matrix-3: 35 | ansible_host: 192.168.122.153 36 | matrix-4: 37 | ansible_host: 192.168.122.154 38 | -------------------------------------------------------------------------------- /chatops-webex-teams/network_overwatch/restconf.py: -------------------------------------------------------------------------------- 1 | from httpx.client import AsyncClient 2 | 3 | from network_overwatch import constants 4 | 5 | HEADERS = { 6 | "Accept": "application/yang-data+json", 7 | "Content-Type": "application/yang-data+json", 8 | } 9 | RESTCONF_BASE_URL = "https://{host}" 10 | 11 | 12 | class RESTCONF: 13 | def __init__(self, device_name: str) -> None: 14 | device_host = constants.DEVICES[device_name.upper()] 15 | base_url = RESTCONF_BASE_URL.format(host=device_host) 16 | self.http_client = AsyncClient( 17 | headers=HEADERS, 18 | auth=(constants.DEVICE_USERNAME, constants.DEVICE_PASSWORD), 19 | base_url=base_url, 20 | verify=False, 21 | ) 22 | 23 | async def get_vrf_list(self): 24 | response = await self.http_client.get("/restconf/data/native/vrf") 25 | response.raise_for_status() 26 | vrfs = [] 27 | for vrf_data in response.json()["Cisco-IOS-XE-native:vrf"]["definition"]: 28 | vrf_name = vrf_data["name"] 29 | vrfs.append(vrf_name) 30 | return vrfs 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dmitry Figol 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 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory-to-investigate/hosts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | all: 3 | children: 4 | pod-gear: 5 | children: 6 | pod-routers: 7 | hosts: 8 | R1: 9 | ansible_host: 192.168.122.101 10 | R2: 11 | ansible_host: 192.168.122.102 12 | R3: 13 | ansible_host: 192.168.122.103 14 | R4: 15 | ansible_host: 192.168.122.104 16 | pod-switches: 17 | hosts: 18 | SW1: 19 | ansible_host: 192.168.122.111 20 | SW2: 21 | ansible_host: 192.168.122.112 22 | SW3: 23 | ansible_host: 192.168.122.113 24 | SW4: 25 | ansible_host: 192.168.122.114 26 | infra: 27 | children: 28 | matrix-switches: 29 | hosts: 30 | matrix-1: 31 | ansible_host: 192.168.122.151 32 | matrix-2: 33 | ansible_host: 192.168.122.152 34 | matrix-3: 35 | ansible_host: 192.168.122.153 36 | matrix-4: 37 | ansible_host: 192.168.122.154 38 | -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.1 on 2018-08-12 18:22 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Device', 16 | fields=[ 17 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 18 | ('name', models.CharField(max_length=100)), 19 | ('host', models.CharField(max_length=70)), 20 | ('username', models.CharField(max_length=100)), 21 | ('password', models.CharField(blank=True, max_length=100)), 22 | ('device_type', models.CharField(blank=True, choices=[('router', 'Router'), ('switch', 'Switch'), ('firewall', 'Firewall')], max_length=30)), 23 | ('platform', models.CharField(blank=True, choices=[('cisco_ios', 'Cisco IOS'), ('cisco_iosxe', 'Cisco IOS XE')], max_length=30)), 24 | ], 25 | ), 26 | ] 27 | -------------------------------------------------------------------------------- /ansible/network-programmability-lab/templates/netconf/l3_interface.j2: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% for interface in interfaces %} 5 | <{{ interface.type }}> 6 | {{ interface.number }} 7 | {% if interface.dot1q_vlan is defined %} 8 | 9 | 10 | {{ interface.dot1q_vlan }} 11 | 12 | 13 | {% else %} 14 | 15 | {% endif %} 16 | 17 | {% if interface.ipv4_addr is defined %} 18 | 19 |
20 | 21 |
{{ interface.ipv4_addr | ipaddr('address') }}
22 | {{ interface.ipv4_addr | ipaddr('netmask') }} 23 |
24 |
25 |
26 | {% else %} 27 | 28 | {% endif %} 29 | 30 | {% endfor %} 31 |
32 |
33 |
-------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | NAPALM_MAPPING = { 5 | 'cisco_ios': 'ios', 6 | 'cisco_iosxe': 'ios', 7 | } 8 | 9 | NETMIKO_MAPPING = { 10 | 'cisco_ios': 'cisco_ios', 11 | 'cisco_iosxe': 'cisco_ios', 12 | } 13 | 14 | 15 | class Device(models.Model): 16 | name = models.CharField(max_length=100) 17 | host = models.CharField(max_length=70) 18 | username = models.CharField(max_length=100) 19 | password = models.CharField(max_length=100, blank=True) 20 | device_type = models.CharField( 21 | max_length=30, choices=(("router", "Router"), ("switch", "Switch"), ("firewall", "Firewall")), blank=True 22 | ) 23 | platform = models.CharField( 24 | max_length=30, choices=(("cisco_ios", "Cisco IOS"), ("cisco_iosxe", "Cisco IOS XE")), blank=True 25 | ) 26 | 27 | def __str__(self) -> str: 28 | return self.name 29 | 30 | @property 31 | def napalm_driver(self) -> str: 32 | return NAPALM_MAPPING[self.platform] 33 | 34 | @property 35 | def netmiko_device_type(self) -> str: 36 | return NETMIKO_MAPPING[self.platform] 37 | -------------------------------------------------------------------------------- /scrapli-apps/input/nc-config.yaml: -------------------------------------------------------------------------------- 1 | native: 2 | _xmlns: http://cisco.com/ns/yang/Cisco-IOS-XE-native 3 | vrf: 4 | _operation: replace 5 | definition: 6 | - name: Mgmt-vrf 7 | address-family: 8 | ipv4: null 9 | ipv6: null 10 | - name: SCRAPLI 11 | address-family: 12 | ipv4: null 13 | ipv6: null 14 | ip: 15 | access-list: 16 | _operation: replace 17 | extended: 18 | - name: TEST 19 | _xmlns: http://cisco.com/ns/yang/Cisco-IOS-XE-acl 20 | access-list-seq-rule: 21 | - sequence: 10 22 | ace-rule: 23 | action: permit 24 | protocol: ip 25 | any: null 26 | ipv4-address: 192.168.1.0 27 | mask: 0.0.0.255 28 | dst-any: null 29 | - name: RANDOM 30 | _xmlns: http://cisco.com/ns/yang/Cisco-IOS-XE-acl 31 | access-list-seq-rule: 32 | - sequence: 10 33 | ace-rule: 34 | action: deny 35 | protocol: ip 36 | any: null 37 | dst-host: 1.2.3.4 38 | -------------------------------------------------------------------------------- /go/src/hello/hello.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | "math/rand" 7 | "math" 8 | ) 9 | 10 | var c, python, java bool = false, true, false 11 | const C = 5 12 | 13 | func add(x int, y int) int { 14 | return x + y 15 | } 16 | 17 | func swap(x string, y string) (string, string) { 18 | return y, x 19 | } 20 | 21 | func main() { 22 | i := -1 23 | var j int8 = 13 24 | rand.Seed(time.Now().UnixNano()) 25 | 26 | curTime := time.Now() 27 | fmt.Printf("hello, stream!\n") 28 | fmt.Println("Cisco Live Barcelona is soon and I have to finish my presentation deck.") 29 | 30 | fmt.Println("The time is", curTime, "in unix nanoseconds:", curTime.UnixNano()) 31 | 32 | fmt.Println("Selected number:", rand.Intn(10)) 33 | 34 | fmt.Printf("Now you have %f problems.\n", math.Sqrt(7)) 35 | 36 | fmt.Println(math.Pi) 37 | a, b := swap("sad", "happy") 38 | fmt.Println(a, b) 39 | fmt.Println(i, c, python, java) 40 | fmt.Println(j) 41 | 42 | sum := 0 43 | for i := 0; i < 10; i++ { 44 | if i % 2 == 0 { 45 | sum += i 46 | } 47 | } 48 | fmt.Println(sum) 49 | } 50 | -------------------------------------------------------------------------------- /netprog-stream-django/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.6-alpine 2 | LABEL maintainer="Dmitry Figol " 3 | 4 | COPY requirements.txt . 5 | 6 | RUN apk add --no-cache --virtual .build-deps \ 7 | build-base \ 8 | gcc \ 9 | libffi-dev \ 10 | openssl-dev \ 11 | libxslt-dev \ 12 | libxslt \ 13 | libxml2 \ 14 | libxml2-dev \ 15 | && pip install --no-cache -r requirements.txt \ 16 | && find /usr/local \ 17 | \( -type d -a -name test -o -name tests \) \ 18 | -o \( -type f -a -name '*.pyc' -o -name '*.pyo' \) \ 19 | -exec rm -rf '{}' + \ 20 | && runDeps="$( \ 21 | scanelf --needed --nobanner --recursive /usr/local /yang-explorer \ 22 | | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \ 23 | | sort -u \ 24 | | xargs -r apk info --installed \ 25 | | sort -u \ 26 | )" \ 27 | && apk add --no-cache --virtual .rundeps $runDeps \ 28 | && apk del .build-deps \ 29 | && rm -rf /root/.cache 30 | 31 | COPY netprog-stream-django /netprog-stream-django 32 | WORKDIR netprog-stream-django 33 | 34 | CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"] -------------------------------------------------------------------------------- /nso/test_api.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import ncs 3 | from ncs.maagic import Root 4 | from typing import Iterator, Tuple 5 | 6 | 7 | NSO_USERNAME = 'admin' 8 | NSO_CONTEXT = 'python' 9 | # NSO_GROUPS = ['ncsadmin'] 10 | 11 | 12 | def get_device_name(nso: Root) -> Iterator[Tuple[str, str]]: 13 | for device in nso.devices.device: 14 | # print device.config.ios__cached_show.version.model 15 | breakpoint() 16 | yield (device.name, device.ios__cached_show.version.model) 17 | 18 | 19 | def main() -> None: 20 | with ncs.maapi.single_read_trans(NSO_USERNAME, NSO_CONTEXT) as transaction: 21 | nso = ncs.maagic.get_root(transaction) 22 | devices = nso.devices.device 23 | # print(devices["isp1-pe1"].config.ios__ntp.server.peer_list) 24 | # breakpoint() 25 | for device in devices: 26 | device.config.ios__ntp.server.peer_list.append({"name": "1.2.3.4"}) 27 | # device.config.ios__ntp.server.ip = "1.2.3.4" 28 | # print(device.name) 29 | # print(device.config.ios__ntp) 30 | # print(device.config.ios__cached_show.version) 31 | transaction.apply() 32 | 33 | 34 | if __name__ == '__main__': 35 | main() 36 | -------------------------------------------------------------------------------- /network-testing/tests/conftest.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from typing import Dict, List, Tuple 3 | 4 | from nornir import InitNornir 5 | from nornir.core.filter import F 6 | from nornir import InitNornir 7 | import pytest 8 | 9 | 10 | @pytest.fixture(scope="session") 11 | def nr_config_file(): 12 | return "config.yaml" 13 | 14 | 15 | @pytest.fixture(scope="session") 16 | def nr(nr_config_file): 17 | with InitNornir(nr_config_file) as nr: 18 | for host in nr.inventory.hosts.values(): 19 | host.data["test"] = defaultdict(dict) 20 | 21 | yield nr 22 | 23 | for host in nr.inventory.hosts.values(): 24 | host.data.pop("test", None) 25 | 26 | 27 | @pytest.helpers.register 28 | def get_expected_data_from_tags_values( 29 | data: Dict[Tuple[str, ...], List[str]] 30 | ) -> Dict[str, List[str]]: 31 | nr_obj = InitNornir("config.yaml") 32 | result = {} 33 | for tags, expected_value in data.items(): 34 | filtered_nr = nr_obj.filter(F(tags__all=tags)) 35 | for host in filtered_nr.inventory.hosts.values(): 36 | if host.name in result: 37 | continue 38 | result[host.name] = expected_value 39 | return result 40 | -------------------------------------------------------------------------------- /netbox/inventory.yml: -------------------------------------------------------------------------------- 1 | all: 2 | vars: 3 | username: admin 4 | password: admin 5 | sites: 6 | - name: sjc 7 | hosts: 8 | - hostname: SJ-R1 9 | host: 192.168.122.11 10 | device_type_netmiko: cisco_ios 11 | device_type: csr1000v 12 | device_role: core 13 | - hostname: SJ-R2 14 | host: 192.168.122.12 15 | device_type_netmiko: cisco_ios 16 | device_type: csr1000v 17 | device_role: core 18 | - hostname: SJ-SW1 19 | host: 192.168.122.13 20 | device_type_netmiko: cisco_ios 21 | device_type: iosv-l2 22 | device_role: access 23 | - hostname: SJ-SW2 24 | host: 192.168.122.14 25 | device_type_netmiko: cisco_ios 26 | device_type: iosv-l2 27 | device_role: access 28 | - hostname: SJ-HUB1 29 | host: 192.168.122.15 30 | device_type_netmiko: cisco_ios 31 | device_type: csr1000v 32 | device_role: edge 33 | - name: bru 34 | hosts: 35 | - hostname: BRU-EDGE 36 | host: 192.168.122.20 37 | device_type_netmiko: cisco_ios 38 | device_type: csr1000v 39 | device_role: edge 40 | -------------------------------------------------------------------------------- /ansible/lab-system/host_vars/matrix-1.yml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | - name: Ethernet1/0 3 | connected_device: 4 | name: R1 5 | port: Ethernet1/0 6 | mode: dot1q-tunnel 7 | - name: Ethernet1/1 8 | connected_device: 9 | name: R1 10 | port: Ethernet1/1 11 | mode: dot1q-tunnel 12 | - name: Ethernet1/2 13 | connected_device: 14 | name: R1 15 | port: Ethernet1/2 16 | mode: dot1q-tunnel 17 | - name: Ethernet1/3 18 | connected_device: 19 | name: R1 20 | port: Ethernet1/3 21 | mode: dot1q-tunnel 22 | - name: Ethernet2/0 23 | connected_device: 24 | name: SW1 25 | port: Ethernet1/0 26 | mode: dot1q-tunnel 27 | - name: Ethernet2/1 28 | connected_device: 29 | name: SW1 30 | port: Ethernet1/1 31 | mode: dot1q-tunnel 32 | - name: Ethernet2/2 33 | connected_device: 34 | name: SW1 35 | port: Ethernet1/2 36 | mode: dot1q-tunnel 37 | - name: Ethernet2/3 38 | connected_device: 39 | name: SW1 40 | port: Ethernet1/3 41 | mode: dot1q-tunnel 42 | - name: Ethernet7/0 43 | mode: trunk 44 | - name: Ethernet7/1 45 | mode: trunk 46 | - name: Ethernet7/2 47 | mode: trunk 48 | - name: Ethernet7/3 49 | mode: trunk 50 | 51 | -------------------------------------------------------------------------------- /ansible/lab-system/host_vars/matrix-2.yml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | - name: Ethernet1/0 3 | connected_device: 4 | name: R2 5 | port: Ethernet1/0 6 | mode: dot1q-tunnel 7 | - name: Ethernet1/1 8 | connected_device: 9 | name: R2 10 | port: Ethernet1/1 11 | mode: dot1q-tunnel 12 | - name: Ethernet1/2 13 | connected_device: 14 | name: R2 15 | port: Ethernet1/2 16 | mode: dot1q-tunnel 17 | - name: Ethernet1/3 18 | connected_device: 19 | name: R2 20 | port: Ethernet1/3 21 | mode: dot1q-tunnel 22 | - name: Ethernet2/0 23 | connected_device: 24 | name: SW2 25 | port: Ethernet1/0 26 | mode: dot1q-tunnel 27 | - name: Ethernet2/1 28 | connected_device: 29 | name: SW2 30 | port: Ethernet1/1 31 | mode: dot1q-tunnel 32 | - name: Ethernet2/2 33 | connected_device: 34 | name: SW2 35 | port: Ethernet1/2 36 | mode: dot1q-tunnel 37 | - name: Ethernet2/3 38 | connected_device: 39 | name: SW2 40 | port: Ethernet1/3 41 | mode: dot1q-tunnel 42 | - name: Ethernet7/0 43 | mode: trunk 44 | - name: Ethernet7/1 45 | mode: trunk 46 | - name: Ethernet7/2 47 | mode: trunk 48 | - name: Ethernet7/3 49 | mode: trunk 50 | 51 | -------------------------------------------------------------------------------- /ansible/lab-system/host_vars/matrix-3.yml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | - name: Ethernet1/0 3 | connected_device: 4 | name: R3 5 | port: Ethernet1/0 6 | mode: dot1q-tunnel 7 | - name: Ethernet1/1 8 | connected_device: 9 | name: R3 10 | port: Ethernet1/1 11 | mode: dot1q-tunnel 12 | - name: Ethernet1/2 13 | connected_device: 14 | name: R3 15 | port: Ethernet1/2 16 | mode: dot1q-tunnel 17 | - name: Ethernet1/3 18 | connected_device: 19 | name: R3 20 | port: Ethernet1/3 21 | mode: dot1q-tunnel 22 | - name: Ethernet2/0 23 | connected_device: 24 | name: SW3 25 | port: Ethernet1/0 26 | mode: dot1q-tunnel 27 | - name: Ethernet2/1 28 | connected_device: 29 | name: SW3 30 | port: Ethernet1/1 31 | mode: dot1q-tunnel 32 | - name: Ethernet2/2 33 | connected_device: 34 | name: SW3 35 | port: Ethernet1/2 36 | mode: dot1q-tunnel 37 | - name: Ethernet2/3 38 | connected_device: 39 | name: SW3 40 | port: Ethernet1/3 41 | mode: dot1q-tunnel 42 | - name: Ethernet7/0 43 | mode: trunk 44 | - name: Ethernet7/1 45 | mode: trunk 46 | - name: Ethernet7/2 47 | mode: trunk 48 | - name: Ethernet7/3 49 | mode: trunk 50 | 51 | -------------------------------------------------------------------------------- /ansible/lab-system/host_vars/matrix-4.yml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | - name: Ethernet1/0 3 | connected_device: 4 | name: R4 5 | port: Ethernet1/0 6 | mode: dot1q-tunnel 7 | - name: Ethernet1/1 8 | connected_device: 9 | name: R4 10 | port: Ethernet1/1 11 | mode: dot1q-tunnel 12 | - name: Ethernet1/2 13 | connected_device: 14 | name: R4 15 | port: Ethernet1/2 16 | mode: dot1q-tunnel 17 | - name: Ethernet1/3 18 | connected_device: 19 | name: R4 20 | port: Ethernet1/3 21 | mode: dot1q-tunnel 22 | - name: Ethernet2/0 23 | connected_device: 24 | name: SW4 25 | port: Ethernet1/0 26 | mode: dot1q-tunnel 27 | - name: Ethernet2/1 28 | connected_device: 29 | name: SW4 30 | port: Ethernet1/1 31 | mode: dot1q-tunnel 32 | - name: Ethernet2/2 33 | connected_device: 34 | name: SW4 35 | port: Ethernet1/2 36 | mode: dot1q-tunnel 37 | - name: Ethernet2/3 38 | connected_device: 39 | name: SW4 40 | port: Ethernet1/3 41 | mode: dot1q-tunnel 42 | - name: Ethernet7/0 43 | mode: trunk 44 | - name: Ethernet7/1 45 | mode: trunk 46 | - name: Ethernet7/2 47 | mode: trunk 48 | - name: Ethernet7/3 49 | mode: trunk 50 | 51 | -------------------------------------------------------------------------------- /pyats/intro/main.py: -------------------------------------------------------------------------------- 1 | from ats import topology 2 | from genie.conf import Genie 3 | from genie.abstract import Lookup 4 | from genie.libs import ops 5 | 6 | pyats_testbed = topology.loader.load('testbed.yaml') 7 | genie_testbed = Genie.init(pyats_testbed) 8 | 9 | csr_dev = genie_testbed.devices['CSR-DEV'] 10 | csr_dev.connect(via='netconf') 11 | csr3 = genie_testbed.devices['CSR3'] 12 | csr3.connect(via='console') 13 | abstract = Lookup.from_device(csr_dev) 14 | # lookup = Lookup(device.os, device.context) 15 | interfaces = abstract.ops.interface.interface.Interface(csr_dev) 16 | interfaces.learn() 17 | interfaces.info 18 | static_routes = abstract.ops.static_routing.static_routing.StaticRoute(csr_dev) 19 | static_routes.learn() 20 | static_routes.info 21 | 22 | show_version = abstract.ops.platform.platform.show_platform.ShowVersion(csr_dev) 23 | show_version.parse() 24 | 25 | show_boot = abstract.ops.platform.platform.show_platform.ShowBoot(csr_dev) 26 | show_boot.parse() 27 | 28 | show_cpu_ram = abstract.ops.platform.platform.show_platform.ShowPlatformSoftwareStatusControl(csr_dev) 29 | show_cpu_ram.parse() 30 | 31 | 32 | acl = abstract.ops.acl.acl.Acl(csr_dev) 33 | acl.learn() 34 | acl.info 35 | acl_stats = abstract.ops.acl.acl.ShowAccessLists(csr_dev) 36 | acl_stats.parse() 37 | -------------------------------------------------------------------------------- /network-testing/sandbox.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from pathlib import Path 3 | import shutil 4 | 5 | from nornir import InitNornir 6 | 7 | # from nornir_netmiko.tasks import netmiko_send_command 8 | # from nornir.core.filter import F 9 | from nornir_scrapli.tasks import send_command as scrapli_send_command 10 | 11 | NR_CONFIG_FILE = "config.yaml" 12 | 13 | COMMANDS = [ 14 | "show version", 15 | "show ip int br", 16 | "show ip arp", 17 | "show platform resources", 18 | ] 19 | 20 | OUTPUT_DIR = Path("output/cli") 21 | 22 | 23 | def gather_commands(task, commands): 24 | dt = datetime.now() 25 | dt_str = dt.strftime("%Y-%m-%dT%H:%M:%S") 26 | 27 | file_path = OUTPUT_DIR / f"{task.host.name}_{dt_str}.txt" 28 | with open(file_path, "w") as f: 29 | for command in commands: 30 | output = task.run(scrapli_send_command, command=command) 31 | f.write(f"===== {command} ======\n{output.result}\n\n") 32 | 33 | 34 | def main(): 35 | with InitNornir(config_file=NR_CONFIG_FILE) as nr: 36 | nr.run(gather_commands, commands=COMMANDS) 37 | 38 | 39 | if __name__ == "__main__": 40 | if OUTPUT_DIR.is_dir(): 41 | shutil.rmtree(OUTPUT_DIR) 42 | OUTPUT_DIR.mkdir(parents=True, exist_ok=True) 43 | main() 44 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory-to-investigate/host_vars/matrix-1.yml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | - name: Ethernet1/0 3 | connected_device: 4 | name: R1 5 | port: Ethernet1/0 6 | mode: dot1q-tunnel 7 | - name: Ethernet1/1 8 | connected_device: 9 | name: R1 10 | port: Ethernet1/1 11 | mode: dot1q-tunnel 12 | - name: Ethernet1/2 13 | connected_device: 14 | name: R1 15 | port: Ethernet1/2 16 | mode: dot1q-tunnel 17 | - name: Ethernet1/3 18 | connected_device: 19 | name: R1 20 | port: Ethernet1/3 21 | mode: dot1q-tunnel 22 | - name: Ethernet2/0 23 | connected_device: 24 | name: SW1 25 | port: Ethernet1/0 26 | mode: dot1q-tunnel 27 | - name: Ethernet2/1 28 | connected_device: 29 | name: SW1 30 | port: Ethernet1/1 31 | mode: dot1q-tunnel 32 | - name: Ethernet2/2 33 | connected_device: 34 | name: SW1 35 | port: Ethernet1/2 36 | mode: dot1q-tunnel 37 | - name: Ethernet2/3 38 | connected_device: 39 | name: SW1 40 | port: Ethernet1/3 41 | mode: dot1q-tunnel 42 | - name: Ethernet7/0 43 | mode: trunk 44 | - name: Ethernet7/1 45 | mode: trunk 46 | - name: Ethernet7/2 47 | mode: trunk 48 | - name: Ethernet7/3 49 | mode: trunk 50 | 51 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory-to-investigate/host_vars/matrix-2.yml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | - name: Ethernet1/0 3 | connected_device: 4 | name: R2 5 | port: Ethernet1/0 6 | mode: dot1q-tunnel 7 | - name: Ethernet1/1 8 | connected_device: 9 | name: R2 10 | port: Ethernet1/1 11 | mode: dot1q-tunnel 12 | - name: Ethernet1/2 13 | connected_device: 14 | name: R2 15 | port: Ethernet1/2 16 | mode: dot1q-tunnel 17 | - name: Ethernet1/3 18 | connected_device: 19 | name: R2 20 | port: Ethernet1/3 21 | mode: dot1q-tunnel 22 | - name: Ethernet2/0 23 | connected_device: 24 | name: SW2 25 | port: Ethernet1/0 26 | mode: dot1q-tunnel 27 | - name: Ethernet2/1 28 | connected_device: 29 | name: SW2 30 | port: Ethernet1/1 31 | mode: dot1q-tunnel 32 | - name: Ethernet2/2 33 | connected_device: 34 | name: SW2 35 | port: Ethernet1/2 36 | mode: dot1q-tunnel 37 | - name: Ethernet2/3 38 | connected_device: 39 | name: SW2 40 | port: Ethernet1/3 41 | mode: dot1q-tunnel 42 | - name: Ethernet7/0 43 | mode: trunk 44 | - name: Ethernet7/1 45 | mode: trunk 46 | - name: Ethernet7/2 47 | mode: trunk 48 | - name: Ethernet7/3 49 | mode: trunk 50 | 51 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory-to-investigate/host_vars/matrix-3.yml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | - name: Ethernet1/0 3 | connected_device: 4 | name: R3 5 | port: Ethernet1/0 6 | mode: dot1q-tunnel 7 | - name: Ethernet1/1 8 | connected_device: 9 | name: R3 10 | port: Ethernet1/1 11 | mode: dot1q-tunnel 12 | - name: Ethernet1/2 13 | connected_device: 14 | name: R3 15 | port: Ethernet1/2 16 | mode: dot1q-tunnel 17 | - name: Ethernet1/3 18 | connected_device: 19 | name: R3 20 | port: Ethernet1/3 21 | mode: dot1q-tunnel 22 | - name: Ethernet2/0 23 | connected_device: 24 | name: SW3 25 | port: Ethernet1/0 26 | mode: dot1q-tunnel 27 | - name: Ethernet2/1 28 | connected_device: 29 | name: SW3 30 | port: Ethernet1/1 31 | mode: dot1q-tunnel 32 | - name: Ethernet2/2 33 | connected_device: 34 | name: SW3 35 | port: Ethernet1/2 36 | mode: dot1q-tunnel 37 | - name: Ethernet2/3 38 | connected_device: 39 | name: SW3 40 | port: Ethernet1/3 41 | mode: dot1q-tunnel 42 | - name: Ethernet7/0 43 | mode: trunk 44 | - name: Ethernet7/1 45 | mode: trunk 46 | - name: Ethernet7/2 47 | mode: trunk 48 | - name: Ethernet7/3 49 | mode: trunk 50 | 51 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory-to-investigate/host_vars/matrix-4.yml: -------------------------------------------------------------------------------- 1 | interfaces: 2 | - name: Ethernet1/0 3 | connected_device: 4 | name: R4 5 | port: Ethernet1/0 6 | mode: dot1q-tunnel 7 | - name: Ethernet1/1 8 | connected_device: 9 | name: R4 10 | port: Ethernet1/1 11 | mode: dot1q-tunnel 12 | - name: Ethernet1/2 13 | connected_device: 14 | name: R4 15 | port: Ethernet1/2 16 | mode: dot1q-tunnel 17 | - name: Ethernet1/3 18 | connected_device: 19 | name: R4 20 | port: Ethernet1/3 21 | mode: dot1q-tunnel 22 | - name: Ethernet2/0 23 | connected_device: 24 | name: SW4 25 | port: Ethernet1/0 26 | mode: dot1q-tunnel 27 | - name: Ethernet2/1 28 | connected_device: 29 | name: SW4 30 | port: Ethernet1/1 31 | mode: dot1q-tunnel 32 | - name: Ethernet2/2 33 | connected_device: 34 | name: SW4 35 | port: Ethernet1/2 36 | mode: dot1q-tunnel 37 | - name: Ethernet2/3 38 | connected_device: 39 | name: SW4 40 | port: Ethernet1/3 41 | mode: dot1q-tunnel 42 | - name: Ethernet7/0 43 | mode: trunk 44 | - name: Ethernet7/1 45 | mode: trunk 46 | - name: Ethernet7/2 47 | mode: trunk 48 | - name: Ethernet7/3 49 | mode: trunk 50 | 51 | -------------------------------------------------------------------------------- /juniper/templates/candidate.conf: -------------------------------------------------------------------------------- 1 | interfaces { 2 | {% for interface in interfaces %} 3 | {{ interface.name }} { 4 | description "{{ interface.description }}"; 5 | unit 0 { 6 | family inet { 7 | address {{ interface.ip_address }}; 8 | } 9 | 10 | } 11 | } 12 | {% endfor %} 13 | } 14 | {% if routing %} 15 | protocols { 16 | {% if routing.bgp %} 17 | {% set bgp = routing.bgp %} 18 | bgp { 19 | {% if bgp.external_neighbors %} 20 | group external-peers { 21 | type external; 22 | {% for neighbor in bgp.external_neighbors %} 23 | neighbor {{ neighbor.ip_address }} { 24 | peer-as {{ neighbor.as_number }}; 25 | } 26 | {% endfor %} 27 | } 28 | {% endif %} 29 | {% if bgp.internal_neighbors %} 30 | group internal-peers { 31 | type internal; 32 | {% for neighbor in bgp.internal_neighbors %} 33 | neighbor {{ neighbor.ip_address }}; 34 | {% endfor %} 35 | } 36 | {% endif %} 37 | } 38 | {% endif %} 39 | } 40 | {% endif %} 41 | {% if local_as_number %} 42 | routing-options { 43 | autonomous-system {{ local_as_number }}; 44 | } 45 | {% endif %} -------------------------------------------------------------------------------- /network-testing/inventories/10-csr-local/hosts.yaml: -------------------------------------------------------------------------------- 1 | R1: 2 | hostname: 192.168.152.101 3 | groups: 4 | - Miami 5 | data: 6 | tags: 7 | - csr1000v 8 | R2: 9 | hostname: 192.168.152.102 10 | groups: 11 | - Miami 12 | data: 13 | tags: 14 | - csr1000v 15 | R3: 16 | hostname: 192.168.152.103 17 | groups: 18 | - Miami 19 | data: 20 | tags: 21 | - csr1000v 22 | R4: 23 | hostname: 192.168.152.104 24 | groups: 25 | - Miami 26 | data: 27 | tags: 28 | - csr1000v 29 | R5: 30 | hostname: 192.168.152.105 31 | groups: 32 | - Miami 33 | data: 34 | tags: 35 | - csr1000v 36 | - edge 37 | R6: 38 | hostname: 192.168.152.106 39 | groups: 40 | - London 41 | data: 42 | tags: 43 | - csr1000v 44 | - edge 45 | R7: 46 | hostname: 192.168.152.107 47 | groups: 48 | - London 49 | data: 50 | tags: 51 | - csr1000v 52 | - edge 53 | R8: 54 | hostname: 192.168.152.108 55 | groups: 56 | - London 57 | data: 58 | tags: 59 | - csr1000v 60 | R9: 61 | hostname: 192.168.152.109 62 | groups: 63 | - London 64 | data: 65 | tags: 66 | - csr1000v 67 | R10: 68 | hostname: 192.168.152.110 69 | groups: 70 | - London 71 | data: 72 | tags: 73 | - csr1000v -------------------------------------------------------------------------------- /nornir/network_diagram/inventory/10-csrs_gns3/hosts.yaml: -------------------------------------------------------------------------------- 1 | R1: 2 | hostname: 192.168.122.101 3 | groups: 4 | - Miami 5 | data: 6 | tags: 7 | - isr4400 8 | R2: 9 | hostname: 192.168.122.102 10 | groups: 11 | - Miami 12 | data: 13 | tags: 14 | - isr4300 15 | R3: 16 | hostname: 192.168.122.103 17 | groups: 18 | - Miami 19 | data: 20 | tags: 21 | - isr4400 22 | - edge 23 | R4: 24 | hostname: 192.168.122.104 25 | groups: 26 | - Miami 27 | data: 28 | tags: 29 | - isr4300 30 | R5: 31 | hostname: 192.168.122.105 32 | groups: 33 | - Miami 34 | data: 35 | tags: 36 | - isr4400 37 | - edge 38 | R6: 39 | hostname: 192.168.122.106 40 | groups: 41 | - London 42 | data: 43 | tags: 44 | - isr4300 45 | - edge 46 | R7: 47 | hostname: 192.168.122.107 48 | groups: 49 | - London 50 | data: 51 | tags: 52 | - isr4400 53 | - edge 54 | R8: 55 | hostname: 192.168.122.108 56 | groups: 57 | - London 58 | data: 59 | tags: 60 | - isr4300 61 | R9: 62 | hostname: 192.168.122.109 63 | groups: 64 | - London 65 | data: 66 | tags: 67 | - isr4400 68 | R10: 69 | hostname: 192.168.122.110 70 | groups: 71 | - London 72 | data: 73 | tags: 74 | - isr4300 -------------------------------------------------------------------------------- /ansible/lab-system/vars_plugins/load_lab_templates.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | from ansible import constants 4 | from ansible.module_utils._text import to_text 5 | from ansible.plugins.vars import BaseVarsPlugin 6 | 7 | 8 | class VarsModule(BaseVarsPlugin): 9 | 10 | def get_vars(self, loader, path, entities): 11 | super().get_vars(loader, path, entities) 12 | 13 | result = {} 14 | lab_templates_dir = os.path.join(path, 'user-data', 'topologies') 15 | lab_templates = {} 16 | if os.path.isdir(lab_templates_dir): 17 | for dir_path, subdirs, files in os.walk(lab_templates_dir): 18 | for filename in files: 19 | base_filename, extension = os.path.splitext(filename) 20 | if base_filename == 'topology' and to_text(extension) in constants.YAML_FILENAME_EXTENSIONS: 21 | full_path = os.path.join(dir_path, filename) 22 | dir_name = os.path.basename(os.path.dirname(full_path)) 23 | lab_templates[dir_name] = loader.load_from_file( 24 | full_path, 25 | cache=True, 26 | unsafe=False 27 | ) 28 | result['topologies'] = lab_templates 29 | return result 30 | -------------------------------------------------------------------------------- /nornir/exploring/inventory/simple/hosts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | localhost: 3 | nornir_host: localhost 4 | nornir_username: gns3 5 | nornir_password: cisco123 6 | role: server 7 | type: linux 8 | sj-br1: 9 | nornir_host: 192.168.122.11 10 | nornir_username: admin 11 | nornir_password: admin 12 | site: sj 13 | groups: 14 | - sj-edge 15 | nornir_nos: ios 16 | type: network_device 17 | interfaces: 18 | - name: Loopback0 19 | ipv4_address: 10.3.3.3 20 | ipv4_mask: 255.255.255.255 21 | - name: Loopback1 22 | ipv4_address: 100.1.1.1 23 | ipv4_mask: 255.255.255.255 24 | sj-br2: 25 | nornir_host: 192.168.122.12 26 | nornir_username: admin 27 | nornir_password: admin 28 | site: sj 29 | groups: 30 | - sj-edge 31 | nornir_nos: ios 32 | type: network_device 33 | interfaces: 34 | - name: Loopback0 35 | ipv4_address: 10.2.2.2 36 | ipv4_mask: 255.255.255.255 37 | - name: Loopback1 38 | ipv4_address: 100.2.2.2 39 | ipv4_mask: 255.255.255.255 40 | isp1-pe1: 41 | nornir_host: 192.168.122.41 42 | nornir_username: admin 43 | nornir_password: admin 44 | nornir_nos: ios 45 | groups: 46 | - isp1 47 | type: network_device 48 | isp2-pe1: 49 | nornir_host: 192.168.122.51 50 | nornir_username: admin 51 | nornir_password: admin 52 | groups: 53 | - isp2 54 | nornir_nos: ios 55 | type: network_device -------------------------------------------------------------------------------- /ansible/lab-system/debug.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configuring matrix switches 3 | hosts: matrix:pod-gear 4 | vars_files: 5 | - labs/{{ lab_name }}.yml 6 | roles: 7 | - lab-topology 8 | # 9 | # pre_tasks: 10 | # - name: Load topologies 11 | # include_vars: 12 | # file: "topologies/{{ item.path }}/topology.yml" 13 | # name: "{{ item.path }}" 14 | # with_filetree: topologies/ 15 | # when: item.state == 'directory' 16 | # run_once: true 17 | # delegate_to: localhost 18 | # delegate_facts: true 19 | 20 | 21 | # roles: 22 | # - matrix-dot1q-tunnels 23 | 24 | # - name: Load all topologies 25 | # include_vars: 26 | # dir: topologies 27 | # - name: Load the lab 28 | # include_vars: 29 | # file: labs/1_programmability_lab.yml 30 | 31 | # - name: Dump all vars 32 | # # action: template src=templates/dumpall.j2 dest=/tmp/ansible.all 33 | # debug: 34 | # var: pods[inventory_hostname]['pod_number'] 35 | ## var: pod_number 36 | # - debug: 37 | # var: hostvars 38 | 39 | # - name: testing action plugin 40 | ## action: ios_dot1q_tunnel foo=bar 41 | # ios_dot1q_tunnel: 42 | # run_once: true 43 | # - set_fact: 44 | # "{{ interfaces.useless }}": hi 45 | # run_once: true 46 | tasks: 47 | - debug: 48 | var: hostvars 49 | run_once: true 50 | 51 | -------------------------------------------------------------------------------- /nornir/lab-system/topologies/advanced/topology.yml: -------------------------------------------------------------------------------- 1 | devices: 2 | sjc-border: 3 | tags: 4 | - router 5 | group: advanced-dot1x 6 | sjc-access: 7 | tags: 8 | - switch 9 | group: advanced-dot1x 10 | special_reset: true 11 | rtp-border: 12 | tags: 13 | - router 14 | rtp-access: 15 | tags: 16 | - switch 17 | 18 | vms: 19 | - name: ise 20 | - name: dnac 21 | - name: client1 22 | - name: client2 23 | 24 | connections: 25 | - - hostname: sjc-border 26 | port: Ethernet1/1 27 | - hostname: sjc-access 28 | port: Ethernet1/1 29 | - - hostname: sjc-border 30 | port: Ethernet1/2 31 | - hostname: rtp-access 32 | port: Ethernet1/1 33 | - - hostname: rtp-border 34 | port: Ethernet1/1 35 | - hostname: sjc-access 36 | port: Ethernet1/2 37 | - - hostname: rtp-border 38 | port: Ethernet1/2 39 | - hostname: rtp-access 40 | port: Ethernet1/2 41 | - - hostname: sjc-access 42 | port: Ethernet1/0 43 | - hostname: rtp-access 44 | port: Ethernet1/0 45 | - - hostname: sjc-border 46 | port: Ethernet1/3 47 | - service: internet 48 | - - hostname: rtp-border 49 | port: Ethernet1/3 50 | - vm: ise 51 | update_vlan: True 52 | - vm: dnac 53 | update_vlan: True 54 | - - hostname: rtp-border 55 | port: Ethernet1/3 56 | - vm: client1 57 | update_vlan: False 58 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/hosts.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | localhost: 3 | hostname: 127.0.0.1 4 | platform: linux 5 | R1: 6 | hostname: 192.168.122.101 7 | groups: 8 | - pod-routers 9 | - advanced-dot1x__01 10 | R2: 11 | hostname: 192.168.122.102 12 | groups: 13 | - pod-routers 14 | - dynamic 15 | R3: 16 | hostname: 192.168.122.103 17 | groups: 18 | - pod-routers 19 | - advanced-dot1x__02 20 | R4: 21 | hostname: 192.168.122.104 22 | groups: 23 | - pod-routers 24 | - dynamic 25 | SW1: 26 | hostname: 192.168.122.111 27 | groups: 28 | - pod-switches 29 | - advanced-dot1x__01 30 | SW2: 31 | hostname: 192.168.122.112 32 | groups: 33 | - pod-switches 34 | - dynamic 35 | SW3: 36 | hostname: 192.168.122.113 37 | groups: 38 | - pod-switches 39 | - advanced-dot1x__02 40 | SW4: 41 | hostname: 192.168.122.114 42 | groups: 43 | - pod-switches 44 | - dynamic 45 | matrix-1: 46 | hostname: 192.168.122.151 47 | groups: 48 | - matrix-switches 49 | matrix-2: 50 | hostname: 192.168.122.152 51 | groups: 52 | - matrix-switches 53 | matrix-3: 54 | hostname: 192.168.122.153 55 | groups: 56 | - matrix-switches 57 | matrix-4: 58 | hostname: 192.168.122.154 59 | groups: 60 | - matrix-switches 61 | pair-1: 62 | hostname: 192.168.122.161 63 | groups: 64 | - pair-routers 65 | pod-mgmt-1: 66 | hostname: 192.168.122.171 67 | groups: 68 | - pod-mgmt 69 | -------------------------------------------------------------------------------- /nornir/lab-system/setup.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import logging.config 3 | 4 | logging_dict = { 5 | "version": 1, 6 | "disable_existing_loggers": True, 7 | "formatters": { 8 | "standard": { 9 | "format": "[%(asctime)s] %(levelname)-8s {%(name)s:%(lineno)d} %(message)s" 10 | } 11 | }, 12 | "handlers": { 13 | "default": { 14 | "level": "INFO", 15 | "class": "logging.handlers.RotatingFileHandler", 16 | "filename": "lab-system.log", 17 | "maxBytes": 1024 * 1024 * 5, 18 | "backupCount": 5, 19 | "formatter": "standard", 20 | }, 21 | "console": { 22 | "level": "INFO", 23 | "class": "logging.StreamHandler", 24 | "stream": sys.stdout, 25 | "formatter": "standard", 26 | }, 27 | }, 28 | "loggers": { 29 | "lab_system": { 30 | "handlers": ["console", "default"], 31 | "level": "INFO", 32 | "propagate": False 33 | } 34 | }, 35 | "root": {"handlers": ["console", "default"], "level": "INFO"}, 36 | } 37 | 38 | logging.config.dictConfig(logging_dict) 39 | # logging.basicConfig( 40 | # format="[%(asctime)s] %(levelname)-8s {%(filename)s:%(lineno)d} %(message)s", 41 | # level=logging.INFO, 42 | # handlers=[ 43 | # logging.FileHandler("lab-system.log"), 44 | # logging.StreamHandler(), 45 | # ], 46 | # ) 47 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/matrix-3.yml: -------------------------------------------------------------------------------- 1 | rack: A1 2 | rack_unit: 12 3 | interfaces: 4 | - name: Ethernet1/0 5 | connected_device: 6 | name: R3 7 | port: Ethernet1/0 8 | mode: dot1q-tunnel 9 | - name: Ethernet1/1 10 | connected_device: 11 | name: R3 12 | port: Ethernet1/1 13 | mode: dot1q-tunnel 14 | - name: Ethernet1/2 15 | connected_device: 16 | name: R3 17 | port: Ethernet1/2 18 | mode: dot1q-tunnel 19 | - name: Ethernet1/3 20 | connected_device: 21 | name: R3 22 | port: Ethernet1/3 23 | mode: dot1q-tunnel 24 | - name: Ethernet2/0 25 | connected_device: 26 | name: SW3 27 | port: Ethernet1/0 28 | mode: dot1q-tunnel 29 | - name: Ethernet2/1 30 | connected_device: 31 | name: SW3 32 | port: Ethernet1/1 33 | mode: dot1q-tunnel 34 | - name: Ethernet2/2 35 | connected_device: 36 | name: SW3 37 | port: Ethernet1/2 38 | mode: dot1q-tunnel 39 | - name: Ethernet2/3 40 | connected_device: 41 | name: SW3 42 | port: Ethernet1/3 43 | mode: dot1q-tunnel 44 | - name: Ethernet7/0 45 | mode: trunk 46 | connected_device: 47 | name: matrix-4 48 | port: Ethernet7/0 49 | - name: Ethernet7/1 50 | mode: trunk 51 | - name: Ethernet7/2 52 | mode: trunk 53 | connected_device: 54 | name: matrix-1 55 | port: Ethernet7/2 56 | - name: Ethernet7/3 57 | mode: trunk 58 | 59 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/matrix-4.yml: -------------------------------------------------------------------------------- 1 | rack: A1 2 | rack_unit: 13 3 | interfaces: 4 | - name: Ethernet1/0 5 | connected_device: 6 | name: R4 7 | port: Ethernet1/0 8 | mode: dot1q-tunnel 9 | - name: Ethernet1/1 10 | connected_device: 11 | name: R4 12 | port: Ethernet1/1 13 | mode: dot1q-tunnel 14 | - name: Ethernet1/2 15 | connected_device: 16 | name: R4 17 | port: Ethernet1/2 18 | mode: dot1q-tunnel 19 | - name: Ethernet1/3 20 | connected_device: 21 | name: R4 22 | port: Ethernet1/3 23 | mode: dot1q-tunnel 24 | - name: Ethernet2/0 25 | connected_device: 26 | name: SW4 27 | port: Ethernet1/0 28 | mode: dot1q-tunnel 29 | - name: Ethernet2/1 30 | connected_device: 31 | name: SW4 32 | port: Ethernet1/1 33 | mode: dot1q-tunnel 34 | - name: Ethernet2/2 35 | connected_device: 36 | name: SW4 37 | port: Ethernet1/2 38 | mode: dot1q-tunnel 39 | - name: Ethernet2/3 40 | connected_device: 41 | name: SW4 42 | port: Ethernet1/3 43 | mode: dot1q-tunnel 44 | - name: Ethernet7/0 45 | mode: trunk 46 | connected_device: 47 | name: matrix-3 48 | port: Ethernet7/0 49 | - name: Ethernet7/1 50 | mode: trunk 51 | - name: Ethernet7/2 52 | mode: trunk 53 | connected_device: 54 | name: matrix-2 55 | port: Ethernet7/2 56 | - name: Ethernet7/3 57 | mode: trunk 58 | 59 | -------------------------------------------------------------------------------- /netconf/rpc-examples/edit-config-hostname-vrfs-loopback.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | SJ-R1-STREAM 11 | 12 | 13 | MGMT 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 2018 23 | 24 |
25 | 26 |
100.64.0.1
27 | 255.255.255.0 28 |
29 |
30 |
31 |
32 | 33 | 2019 34 | Hello from the future! 35 | 36 |
37 | 38 |
100.65.0.1
39 | 255.255.255.0 40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
]]>]]> -------------------------------------------------------------------------------- /nornir/exploring/new.py: -------------------------------------------------------------------------------- 1 | from nornir.core import InitNornir 2 | from nornir.plugins.tasks import commands, networking, text 3 | from nornir.plugins.functions.text import print_result 4 | 5 | 6 | def basic_configuration(task): 7 | # Transform inventory data to configuration via a template file 8 | result = task.run( 9 | task=text.template_file, 10 | name="Interface Configuration", 11 | template="interfaces.j2", 12 | path="templates" 13 | ) 14 | 15 | # Save the compiled configuration into a host variable 16 | task.host["config"] = result.result 17 | 18 | # Deploy that configuration to the device using NAPALM 19 | # task.run( 20 | # task=networking.napalm_configure, 21 | # name="Loading Configuration on the device", 22 | # replace=False, 23 | # configuration=task.host["config"], 24 | # ) 25 | # Deploy that configuration to the device using Netmiko 26 | task.run( 27 | task=networking.netmiko_send_config, 28 | name="Loading Configuration on the device [Netmiko]", 29 | config_commands=task.host["config"].splitlines(), 30 | ) 31 | 32 | 33 | def main(): 34 | nornir_runner = InitNornir(config_file="config-ansible.yaml") 35 | 36 | inventory = nornir_runner.inventory 37 | for host in inventory.hosts.values(): 38 | print(host.items()) 39 | # print(nornir_runner.inventory.hosts.items()) 40 | 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /pyats/intro/testbed.yaml: -------------------------------------------------------------------------------- 1 | testbed: 2 | 3 | name: static-csrs 4 | 5 | 6 | devices: 7 | 8 | CSR-DEV: 9 | alias: csr_dev 10 | os: iosxe 11 | type: CSR1000v 12 | tacacs: 13 | username: cisco 14 | passwords: 15 | tacacs: cisco 16 | 17 | connections: 18 | defaults: 19 | class: unicon.Unicon 20 | ssh: 21 | ip: 10.48.18.30 22 | protocol: ssh 23 | netconf: 24 | ip: 10.48.18.30 25 | protocol: netconf 26 | port: 830 27 | 28 | # console: 29 | # protocol: telnet 30 | # ip: esxi 31 | # port: 3013 32 | custom: 33 | abstraction: 34 | order: [os, type] 35 | 36 | CSR3: 37 | alias: csr3 38 | os: iosxe 39 | type: CSR1000v 40 | tacacs: 41 | username: cisco 42 | passwords: 43 | tacacs: cisco 44 | 45 | connections: 46 | defaults: 47 | class: unicon.Unicon 48 | ssh: 49 | ip: 10.48.18.26 50 | protocol: ssh 51 | console: 52 | protocol: telnet 53 | ip: esxi 54 | port: 3003 55 | custom: 56 | abstraction: 57 | order: [os, type] 58 | 59 | 60 | #topology: 61 | # csr1000v: 62 | # interfaces: 63 | # GigabitEthernet1: 64 | # ipv4: 10.10.20.48 65 | # link: flat 66 | # type: ethernet 67 | # 68 | # sbx-n9kv-ao: 69 | # interfaces: 70 | # Ethernet1/1: 71 | # ipv4: 10.10.20.95 72 | # link: flat 73 | #type: ethernet -------------------------------------------------------------------------------- /chatops-webex-teams/payload-examples/webex-webhook.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "Y2lzY29zcGFyazovL3VzL1dFQkhPT0svOTk4YTYyMjMtZGRiNS00M2YzLWExY2EtZTNhZGI5YTRkOGEw", 3 | "name": "New messages", 4 | "targetUrl": "https://181c2909.ngrok.io/webex-webhooks", 5 | "resource": "messages", 6 | "event": "created", 7 | "filter": "mentionedPeople=Y2lzY29zcGFyazovL3VzL1BFT1BMRS83Y2E1MWJmYy1lNjhlLTRiNmQtYWEzZi1kNmZkNzkyNjZkODY", 8 | "orgId": "Y2lzY29zcGFyazovL3VzL09SR0FOSVpBVElPTi9jb25zdW1lcg", 9 | "createdBy": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS83Y2E1MWJmYy1lNjhlLTRiNmQtYWEzZi1kNmZkNzkyNjZkODY", 10 | "appId": "Y2lzY29zcGFyazovL3VzL0FQUExJQ0FUSU9OL0MzMmM4MDc3NDBjNmU3ZGYxMWRhZjE2ZjIyOGRmNjI4YmJjYTQ5YmE1MmZlY2JiMmM3ZDUxNWNiNGEwY2M5MWFh", 11 | "ownedBy": "creator", 12 | "status": "active", 13 | "created": "2020-03-06T16:35:49.346Z", 14 | "actorId": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS9mNzI4OTVjYy00MzI3LTQwNGItYTlmOS1lODNiZDBhNzA2YTk", 15 | "data": { 16 | "id": "Y2lzY29zcGFyazovL3VzL01FU1NBR0UvZTdlNjAxZDAtNWZjOC0xMWVhLWJlYTgtOTE3NDQwYTgwZDhh", 17 | "roomId": "Y2lzY29zcGFyazovL3VzL1JPT00vNDJkYWNiODAtMjEyMi0xMWU4LThiMjMtNGZmOThhYjM4OWY2", 18 | "roomType": "group", 19 | "personId": "Y2lzY29zcGFyazovL3VzL1BFT1BMRS9mNzI4OTVjYy00MzI3LTQwNGItYTlmOS1lODNiZDBhNzA2YTk", 20 | "personEmail": "test@example.org", 21 | "mentionedPeople": [ 22 | "Y2lzY29zcGFyazovL3VzL1BFT1BMRS83Y2E1MWJmYy1lNjhlLTRiNmQtYWEzZi1kNmZkNzkyNjZkODY" 23 | ], 24 | "created": "2020-03-06T16:38:27.693Z" 25 | } 26 | } -------------------------------------------------------------------------------- /nornir/lab-system/utils.py: -------------------------------------------------------------------------------- 1 | import math 2 | from pathlib import Path 3 | 4 | import ruamel.yaml 5 | from nornir.core.inventory import Inventory 6 | from nornir.core.filter import F 7 | 8 | YAML = ruamel.yaml.YAML(typ="safe") 9 | 10 | 11 | def update_description(inventory: Inventory) -> None: 12 | infra_devices = inventory.filter(F(has_parent_group='infra')).hosts.values() 13 | for device in infra_devices: 14 | for interface in device.get('interfaces', []): 15 | if 'connected_device' in interface: 16 | connected_device_info = interface["connected_device"] 17 | connected_device_name = connected_device_info["name"] 18 | port = connected_device_info["port"] 19 | connected_device = inventory.hosts[connected_device_name] 20 | rack = connected_device['rack'] 21 | rack_unit = connected_device['rack_unit'] 22 | description = ( 23 | f"To Rack {rack} RU {rack_unit} -> {connected_device_name} {port}" 24 | ) 25 | interface["description"] = description 26 | 27 | 28 | def update_host_vars(inventory: Inventory) -> None: 29 | for host in inventory.hosts.values(): 30 | path = Path(f"inventory/host_vars/{host.name}.yml") 31 | if path.is_file(): 32 | with open(path) as f: 33 | host_info = YAML.load(f) 34 | host.data.update(host_info) 35 | 36 | 37 | def roundup(value: float) -> int: 38 | return int(math.ceil(value / 10)) * 10 39 | -------------------------------------------------------------------------------- /network-testing/show_version_genie.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": { 3 | "xe_version": "17.03.02", 4 | "version_short": "17.3", 5 | "platform": "Virtual XE", 6 | "version": "17.3.2", 7 | "image_id": "X86_64_LINUX_IOSD-UNIVERSALK9-M", 8 | "label": "RELEASE SOFTWARE (fc3)", 9 | "os": "IOS-XE", 10 | "image_type": "production image", 11 | "compiled_date": "Sat 31-Oct-20 13:16", 12 | "compiled_by": "mcpre", 13 | "rom": "IOS-XE ROMMON", 14 | "hostname": "R1", 15 | "uptime": "5 hours, 7 minutes", 16 | "uptime_this_cp": "5 hours, 9 minutes", 17 | "returned_to_rom_by": "reload", 18 | "system_image": "bootflash:packages.conf", 19 | "last_reload_reason": "reload", 20 | "license_level": "ax", 21 | "license_type": "N/A(Smart License Enabled)", 22 | "next_reload_license_level": "ax", 23 | "chassis": "CSR1000V", 24 | "main_mem": "1105351", 25 | "processor_type": "VXE", 26 | "rtr_type": "CSR1000V", 27 | "chassis_sn": "92H4H7348KZ", 28 | "number_of_intfs": { 29 | "Gigabit Ethernet": "4" 30 | }, 31 | "mem_size": { 32 | "non-volatile configuration": "32768", 33 | "physical": "3012036" 34 | }, 35 | "disks": { 36 | "bootflash:.": { 37 | "disk_size": "6188032", 38 | "type_of_disk": "virtual hard disk" 39 | } 40 | }, 41 | "curr_config_register": "0x2102" 42 | } 43 | } -------------------------------------------------------------------------------- /nornir/lab-system/main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import logging 3 | from typing import Tuple, Dict 4 | 5 | from nornir.core import InitNornir 6 | 7 | import setup # noqa 8 | from deployment import Deployment 9 | from utils import update_description, update_host_vars 10 | 11 | logger = logging.getLogger('lab_system.main') 12 | 13 | 14 | def convert_topology_arg(topology: str) -> Tuple[str, Dict[str, int]]: 15 | topology, quantity = topology.split(':') 16 | return topology, {'quantity': int(quantity)} 17 | 18 | 19 | def parse_arguments() -> argparse.Namespace: 20 | """Parses arguments""" 21 | parser = argparse.ArgumentParser() 22 | parser.add_argument( 23 | "-t", 24 | "--topologies", 25 | nargs='+', 26 | help=( 27 | "Specify topology name and quantity separated by :, " 28 | "for example --topology advanced:2 simple:2" 29 | ), 30 | type=convert_topology_arg, 31 | dest="topologies" 32 | ) 33 | parser.add_argument( 34 | "-p", "--pod", type=str, default="all", help="Specify pod number, default: all" 35 | ) 36 | args = parser.parse_args() 37 | args.topologies = dict(args.topologies) 38 | return args 39 | 40 | 41 | def main() -> None: 42 | args = parse_arguments() 43 | nr = InitNornir("config.yaml") 44 | update_host_vars(nr.inventory) 45 | update_description(nr.inventory) 46 | deployment = Deployment(args.topologies, nr.inventory) 47 | import ipdb; 48 | ipdb.set_trace() 49 | 50 | 51 | if __name__ == "__main__": 52 | main() 53 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/matrix-1.yml: -------------------------------------------------------------------------------- 1 | rack: A1 2 | rack_unit: 10 3 | interfaces: 4 | - name: Ethernet1/0 5 | connected_device: 6 | name: R1 7 | port: Ethernet1/0 8 | mode: dot1q-tunnel 9 | - name: Ethernet1/1 10 | connected_device: 11 | name: R1 12 | port: Ethernet1/1 13 | mode: dot1q-tunnel 14 | - name: Ethernet1/2 15 | connected_device: 16 | name: R1 17 | port: Ethernet1/2 18 | mode: dot1q-tunnel 19 | - name: Ethernet1/3 20 | connected_device: 21 | name: R1 22 | port: Ethernet1/3 23 | mode: dot1q-tunnel 24 | - name: Ethernet2/0 25 | connected_device: 26 | name: SW1 27 | port: Ethernet1/0 28 | mode: dot1q-tunnel 29 | - name: Ethernet2/1 30 | connected_device: 31 | name: SW1 32 | port: Ethernet1/1 33 | mode: dot1q-tunnel 34 | - name: Ethernet2/2 35 | connected_device: 36 | name: SW1 37 | port: Ethernet1/2 38 | mode: dot1q-tunnel 39 | - name: Ethernet2/3 40 | connected_device: 41 | name: SW1 42 | port: Ethernet1/3 43 | mode: dot1q-tunnel 44 | - name: Ethernet3/0 45 | connected_device: 46 | name: pair-1 47 | port: Ethernet1/3 48 | mode: trunk 49 | - name: Ethernet7/0 50 | mode: trunk 51 | connected_device: 52 | name: matrix-2 53 | port: Ethernet7/0 54 | - name: Ethernet7/1 55 | mode: trunk 56 | - name: Ethernet7/2 57 | mode: trunk 58 | connected_device: 59 | name: matrix-3 60 | port: Ethernet7/2 61 | - name: Ethernet7/3 62 | mode: trunk 63 | 64 | -------------------------------------------------------------------------------- /nornir/lab-system/inventory/host_vars/matrix-2.yml: -------------------------------------------------------------------------------- 1 | rack: A1 2 | rack_unit: 11 3 | interfaces: 4 | - name: Ethernet1/0 5 | connected_device: 6 | name: R2 7 | port: Ethernet1/0 8 | mode: dot1q-tunnel 9 | - name: Ethernet1/1 10 | connected_device: 11 | name: R2 12 | port: Ethernet1/1 13 | mode: dot1q-tunnel 14 | - name: Ethernet1/2 15 | connected_device: 16 | name: R2 17 | port: Ethernet1/2 18 | mode: dot1q-tunnel 19 | - name: Ethernet1/3 20 | connected_device: 21 | name: R2 22 | port: Ethernet1/3 23 | mode: dot1q-tunnel 24 | - name: Ethernet2/0 25 | connected_device: 26 | name: SW2 27 | port: Ethernet1/0 28 | mode: dot1q-tunnel 29 | - name: Ethernet2/1 30 | connected_device: 31 | name: SW2 32 | port: Ethernet1/1 33 | mode: dot1q-tunnel 34 | - name: Ethernet2/2 35 | connected_device: 36 | name: SW2 37 | port: Ethernet1/2 38 | mode: dot1q-tunnel 39 | - name: Ethernet2/3 40 | connected_device: 41 | name: SW2 42 | port: Ethernet1/3 43 | mode: dot1q-tunnel 44 | - name: Ethernet3/0 45 | mode: trunk 46 | connected_device: 47 | name: pair-1 48 | port: Ethernet1/1 49 | - name: Ethernet7/0 50 | mode: trunk 51 | connected_device: 52 | name: matrix-1 53 | port: Ethernet7/0 54 | - name: Ethernet7/1 55 | mode: trunk 56 | - name: Ethernet7/2 57 | mode: trunk 58 | connected_device: 59 | name: matrix-4 60 | port: Ethernet7/2 61 | - name: Ethernet7/3 62 | mode: trunk 63 | 64 | -------------------------------------------------------------------------------- /chatops-webex-teams/network_overwatch/app.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from starlette.applications import Starlette 5 | from starlette.responses import PlainTextResponse, Response 6 | from starlette.routing import Route 7 | 8 | from network_overwatch.constants import WEBEX_WEBHOOK_ENDPOINT 9 | from network_overwatch.webex_teams import WebexTeams 10 | from network_overwatch.webhook_manager import ( 11 | get_bot_id, 12 | create_or_update_webhook, 13 | handle_event, 14 | ) 15 | 16 | 17 | logger = logging.getLogger() 18 | logger.setLevel(logging.DEBUG) 19 | 20 | 21 | async def homepage(request) -> Response: 22 | return PlainTextResponse("Homepage") 23 | 24 | 25 | async def about(request) -> Response: 26 | return PlainTextResponse("About") 27 | 28 | 29 | async def webex_webhooks(request) -> Response: 30 | data = await request.json() 31 | await handle_event( 32 | event=data, 33 | webex_teams=request.app.state.webex_teams, 34 | bot_id=request.app.state.bot_id, 35 | ) 36 | return Response(status_code=204) 37 | 38 | 39 | async def on_startup() -> None: 40 | webex_teams = WebexTeams(os.environ["OVERWATCH_WEBEX_BOT_TOKEN"]) 41 | app.state.webex_teams = webex_teams 42 | app.state.bot_id = await get_bot_id(webex_teams) 43 | app.state.webhook_id = await create_or_update_webhook(webex_teams) 44 | 45 | 46 | routes = [ 47 | Route("/", endpoint=homepage), 48 | Route("/about", endpoint=about), 49 | Route(WEBEX_WEBHOOK_ENDPOINT, endpoint=webex_webhooks, methods=["POST"]), 50 | ] 51 | 52 | app = Starlette(routes=routes, on_startup=[on_startup],) 53 | -------------------------------------------------------------------------------- /network-testing/pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "network-testing" 3 | version = "0.1.0" 4 | description = "Network testing with pytest" 5 | authors = ["Dmitry Figol "] 6 | 7 | readme = "README.md" 8 | 9 | [tool.poetry.dependencies] 10 | python = "^3.7" 11 | nornir = "^3" 12 | scrapli = {extras = ["ssh2", "textfsm", "genie"], version = "*", allow-prereleases = true } 13 | scrapli-netconf = { version = "*", allow-prereleases = true } 14 | scrapli-cfg = { git = "https://github.com/scrapli/scrapli_cfg.git", branch = "main", allow-prereleases = true } 15 | nornir-scrapli = { git = "https://github.com/scrapli/nornir_scrapli", branch = "develop"} 16 | genie = "21.6" 17 | pyats = "21.6" 18 | 19 | [tool.poetry.dev-dependencies] 20 | bpython = "*" 21 | mypy = "*" 22 | flake8 = "*" 23 | flake8-bugbear = "*" 24 | pdbpp = "*" 25 | black = "*" 26 | isort = "*" 27 | types-setuptools = "*" 28 | pytest = "*" 29 | pytest-html = "*" 30 | pytest-html-reporter = "*" 31 | pytest-helpers-namespace = "*" 32 | 33 | [build-system] 34 | requires = ["poetry-core>=1.0.0"] 35 | build-backend = "poetry.core.masonry.api" 36 | 37 | [tool.mypy] 38 | python_version = "3.6" 39 | check_untyped_defs = true 40 | disallow_any_generics = true 41 | disallow_untyped_calls = true 42 | ignore_errors = false 43 | ignore_missing_imports = true 44 | strict_optional = true 45 | warn_unused_ignores = true 46 | warn_redundant_casts = true 47 | warn_unused_configs = true 48 | warn_return_any = true 49 | warn_no_return = true 50 | warn_unreachable = true 51 | 52 | [tool.pytest.ini_options] 53 | addopts = "-p no:warnings" 54 | norecursedirs= "tests/helpers" 55 | -------------------------------------------------------------------------------- /async/gather_commands_sync.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import netmiko 3 | 4 | from helper import read_yaml, form_connection_params_from_yaml 5 | 6 | SITE_NAME = "SJ-HQ" 7 | 8 | COMMANDS_LIST = [ 9 | "show clock", "show version", "show inventory", "show ip interface brief" 10 | ] 11 | 12 | 13 | def collect_outputs(devices, commands): 14 | """ 15 | Collects commands from the dictionary of devices 16 | 17 | Args: 18 | devices (dict): dictionary, where key is the hostname, value is 19 | netmiko connection dictionary 20 | commands (list): list of commands to be executed on every device 21 | 22 | Returns: 23 | dict: key is the hostname, value is string with all outputs 24 | """ 25 | for device in devices: 26 | hostname = device.pop("hostname") 27 | connection = netmiko.ConnectHandler(**device) 28 | device_result = ["{0} {1} {0}".format("=" * 20, hostname)] 29 | 30 | for command in commands: 31 | command_result = connection.send_command(command) 32 | device_result.append("{0} {1} {0}".format("=" * 20, command)) 33 | device_result.append(command_result) 34 | 35 | device_result_string = "\n\n".join(device_result) 36 | connection.disconnect() 37 | yield device_result_string 38 | 39 | 40 | def main(): 41 | parsed_yaml = read_yaml() 42 | connection_params = form_connection_params_from_yaml(parsed_yaml, site=SITE_NAME) 43 | for device_result in collect_outputs(connection_params, COMMANDS_LIST): 44 | print(device_result) 45 | 46 | 47 | if __name__ == "__main__": 48 | main() 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Network programmability stream supporting files 2 | Supporting files for my network programmability stream, mostly code written live on [Twitch stream](https://twitch.tv/dmfigol). 3 | Stream recordings can be found on my [YouTube](https://youtube.com/dmfigol) 4 | 5 | The repository contains a bunch of folders corresponding to a specific tool or technology. In each folder there is a README.md which contains some details about that particular project, how to run it, what different files are for. It could also contain technology notes. 6 | 7 | For majority of projects I am using Python 3.6+ (the easiest way to install any Python version is [pyenv](https://github.com/pyenv/pyenv)). 8 | I am also a heavy user of [poetry](https://python-poetry.org) - tool for managing python dependencies. It uses `pyproject.toml` and `poetry.lock` files which you can find throughout the repo. You can install dependencies with `poetry install`. I will also do my best to provide `requirements.txt` as well so you can do `pip install -r requirements.txt` in case you don't want to deal with poetry, but don't be surprised if you don't find one. 9 | 10 | Note: Currently the repo files undergo a **major** overhaul. 11 | ### Reworked folders 12 | * **model-driven-telemetry** - a project "Model-Driven Telemetry" including gRPC dial-out telemetry and NETCONF dial-in telemetry 13 | * **network-diagram-visualization-js** - a project "Visualization of a network diagram using JavaScript" 14 | * **chatops-webex-teams** - a project "Webex Teams bot for network automation | ChatOps " 15 | * **scrapli-apps** - code related to my experimentation of scrapli, scrapli-cfg, scrapli-netconf, scrapli-replay -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/templates/device.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Awesome network controller 6 | 7 | 8 | 9 |
10 |

{{ device.name }} ({{ device.host }}) - interfaces

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | {% for interface_name, interface in interfaces.items %} 20 | 21 | {% csrf_token %} 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | {% endfor %} 33 |
Interface nameDescriptionEnabled?Up?Switch on/off
{{ interface_name }}{{ interface.description }}  
34 |
35 | 36 | -------------------------------------------------------------------------------- /async/http-requests-sync.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from typing import Iterable, Tuple 3 | 4 | import requests 5 | from bs4 import BeautifulSoup 6 | import colorama 7 | 8 | URLS = [ 9 | "https://pypi.org", 10 | "https://python.org", 11 | "https://google.com", 12 | "https://amazon.com", 13 | "https://reddit.com", 14 | "https://stackoverflow.com", 15 | "https://ubuntu.com", 16 | "https://facebook.com", 17 | "https://www.microsoft.com", 18 | "https://www.ford.com", 19 | ] 20 | 21 | HEADERS = { 22 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36' 23 | } 24 | 25 | 26 | def get_title(html: str) -> str: 27 | soup = BeautifulSoup(html, 'lxml') 28 | return soup.title.string 29 | 30 | 31 | def download_page_title(url: str, session: requests.Session) -> str: 32 | response = session.get(url, headers=HEADERS) 33 | title = get_title(response.text) 34 | return title 35 | 36 | 37 | def download_all_page_titles(urls) -> Iterable[Tuple[str, str]]: 38 | result = [] 39 | session = requests.Session() 40 | for url in urls: 41 | title = download_page_title(url, session) 42 | result.append((url, title)) 43 | return result 44 | 45 | 46 | def main() -> None: 47 | colorama.init() 48 | start_time = datetime.now() 49 | for url, title in download_all_page_titles(URLS): 50 | print(f"Web page {url} has title: {title}") 51 | exec_time = (datetime.now() - start_time).total_seconds() 52 | print(colorama.Fore.GREEN + f"Summary: it took {exec_time:,.2f} seconds to run") 53 | 54 | 55 | if __name__ == '__main__': 56 | main() 57 | -------------------------------------------------------------------------------- /network-diagram-visualization-js/src/components/NetworkDiagramD3Force.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /network-testing/tests/test_vrfs.py: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | Sequence, 3 | TYPE_CHECKING, 4 | cast, 5 | Dict, 6 | Any, 7 | List, 8 | Sequence, 9 | Tuple, 10 | Iterable, 11 | ) 12 | 13 | import pytest 14 | from nornir.core.filter import F 15 | from nornir_scrapli.tasks import send_command 16 | 17 | if TYPE_CHECKING: 18 | from scrapli.response import Response as ScrapliResponse 19 | 20 | 21 | EXPECTED_VRFS = { 22 | ("csr1000v", "edge"): {"Mgmt-vrf", "Edge"}, 23 | ("csr1000v",): {"Mgmt-vrf"}, 24 | } 25 | 26 | 27 | EXPECTED_DATA = pytest.helpers.get_expected_data_from_tags_values(EXPECTED_VRFS) 28 | 29 | UNSET = object() 30 | 31 | 32 | def collect_vrfs(task, keyword: str): 33 | task.host.data["test"][keyword]["actual"] = UNSET 34 | 35 | result = task.run(send_command, command="show vrf") 36 | scrapli_response: ScrapliResponse = result.scrapli_response 37 | data = cast(Dict[str, Any], scrapli_response.genie_parse_output()) 38 | actual_vrfs = set(data["vrf"].keys()) 39 | task.host.data["test"][keyword]["actual"] = actual_vrfs 40 | 41 | 42 | class TestParsedShowVrf: 43 | keyword = "vrfs" 44 | msg = "Host {device_name!r} has vrfs {actual!r}, expected: {expected!r}" 45 | 46 | @pytest.fixture(autouse=True) 47 | def collect(self, nr): 48 | nr.run(task=collect_vrfs, keyword=self.keyword) 49 | 50 | @pytest.mark.parametrize( 51 | "device_name,expected", EXPECTED_DATA.items(), ids=EXPECTED_DATA.keys() 52 | ) 53 | def test_vrfs(self, nr, device_name: str, expected: List[str]): 54 | host = nr.inventory.hosts[device_name] 55 | actual = host.data["test"][self.keyword]["actual"] 56 | assert expected == actual, self.msg.format(**locals()) 57 | -------------------------------------------------------------------------------- /scrapli-apps/scrapli_replay_sessions/test_get_serial_num.yaml: -------------------------------------------------------------------------------- 1 | 192.168.152.101:22:AsyncsshTransport::0: 2 | connection_profile: 3 | host: 192.168.152.101 4 | port: 22 5 | auth_username: cisco 6 | auth_password: true 7 | auth_private_key: '' 8 | auth_private_key_passphrase: false 9 | auth_bypass: false 10 | transport: asyncssh 11 | auth_secondary: false 12 | interactions: 13 | - channel_output: '' 14 | expected_channel_input: "\n" 15 | expected_channel_input_redacted: false 16 | - channel_output: "Configured by scrapli-cfg4 NEW\n\nR1#" 17 | expected_channel_input: terminal length 0 18 | expected_channel_input_redacted: false 19 | - channel_output: "\nR1#terminal length 0" 20 | expected_channel_input: "\n" 21 | expected_channel_input_redacted: false 22 | - channel_output: "\nR1#" 23 | expected_channel_input: terminal width 512 24 | expected_channel_input_redacted: false 25 | - channel_output: terminal width 512 26 | expected_channel_input: "\n" 27 | expected_channel_input_redacted: false 28 | - channel_output: "\nR1#" 29 | expected_channel_input: show license udi 30 | expected_channel_input_redacted: false 31 | - channel_output: show license udi 32 | expected_channel_input: "\n" 33 | expected_channel_input_redacted: false 34 | - channel_output: "\nUDI: PID:CSR1000V,SN:9228HT2B0FV\n\n\nR1#" 35 | expected_channel_input: "\n" 36 | expected_channel_input_redacted: false 37 | - channel_output: "\nR1#" 38 | expected_channel_input: exit 39 | expected_channel_input_redacted: false 40 | - channel_output: '' 41 | expected_channel_input: "\n" 42 | expected_channel_input_redacted: false 43 | -------------------------------------------------------------------------------- /network-testing/tests/test_sw_version.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING, Dict, Any, cast, Iterable 2 | 3 | import pytest 4 | from nornir.core.filter import F 5 | from nornir_scrapli.tasks import send_command 6 | 7 | if TYPE_CHECKING: 8 | from scrapli.response import Response as ScrapliResponse 9 | 10 | 11 | UNSET = object() 12 | 13 | 14 | def collect_sw_version(task, keyword: str): 15 | task.host.data["test"][keyword]["actual"] = UNSET 16 | result = task.run(send_command, command="show version") 17 | scrapli_response: ScrapliResponse = result.scrapli_response 18 | data = cast(Dict[str, Any], scrapli_response.genie_parse_output()) 19 | actual_sw_version = data["version"]["version"] 20 | task.host.data["test"][keyword]["actual"] = actual_sw_version 21 | 22 | 23 | EXPECTED_SW_VERSION = { 24 | "R1": "17.3.2", 25 | "R2": "17.3.2", 26 | "R3": "17.3.2", 27 | "R4": "17.3.3", 28 | "R5": "17.3.2", 29 | "R6": "17.3.2", 30 | "R7": "17.3.2", 31 | "R8": "17.3.2", 32 | "R9": "17.3.2", 33 | "R10": "17.3.3", 34 | } 35 | 36 | 37 | class TestParsedShowVersion: 38 | keyword = "version" 39 | msg = "Host {device_name!r} has SW version {actual!r}, expected: {expected!r}" 40 | 41 | @pytest.fixture(autouse=True) 42 | def collect(self, nr): 43 | nr.run(task=collect_sw_version, keyword=self.keyword) 44 | 45 | @pytest.mark.parametrize( 46 | "device_name,expected", 47 | EXPECTED_SW_VERSION.items(), 48 | ids=EXPECTED_SW_VERSION.keys(), 49 | ) 50 | def test_sw_version(self, nr, device_name: str, expected: str): 51 | host = nr.inventory.hosts[device_name] 52 | actual = host.data["test"][self.keyword]["actual"] 53 | assert expected == actual, self.msg.format(**locals()) 54 | -------------------------------------------------------------------------------- /async/gather_commands_async.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import asyncio 3 | from copy import deepcopy 4 | 5 | import netdev 6 | import yaml 7 | 8 | from helper import read_yaml, form_connection_params_from_yaml 9 | 10 | SITE_NAME = "SJ-HQ" 11 | 12 | COMMANDS_LIST = [ 13 | "show clock", "show version", "show inventory", "show ip interface brief" 14 | ] 15 | 16 | 17 | async def collect_outputs(device_params, commands): 18 | """ 19 | Collects commands from the dictionary of devices 20 | 21 | Args: 22 | device_params (dict): dictionary, where key is the hostname, value is 23 | netmiko connection dictionary 24 | commands (list): list of commands to be executed on every device 25 | 26 | Returns: 27 | dict: key is the hostname, value is string with all outputs 28 | """ 29 | hostname = device_params.pop("hostname") 30 | async with netdev.create(**device_params) as connection: 31 | device_result = ["{0} {1} {0}".format("=" * 20, hostname)] 32 | 33 | for command in commands: 34 | command_result = await connection.send_command(command) 35 | device_result.append("{0} {1} {0}".format("=" * 20, command)) 36 | device_result.append(command_result) 37 | 38 | device_result_string = "\n\n".join(device_result) 39 | return device_result_string 40 | 41 | 42 | def main(): 43 | parsed_yaml = read_yaml() 44 | loop = asyncio.get_event_loop() 45 | tasks = [ 46 | loop.create_task(collect_outputs(device, COMMANDS_LIST)) 47 | for device in form_connection_params_from_yaml(parsed_yaml, site=SITE_NAME) 48 | ] 49 | loop.run_until_complete(asyncio.wait(tasks)) 50 | for task in tasks: 51 | print(task.result()) 52 | 53 | 54 | if __name__ == "__main__": 55 | main() 56 | -------------------------------------------------------------------------------- /model-driven-telemetry/grpc/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | influxdb: 4 | image: influxdb:1.7-alpine 5 | ports: 6 | - "8086:8086" # HTTP API port 7 | - "8083:8083" # Administrator 8 | volumes: 9 | - influx-storage:/var/lib/influxdb 10 | 11 | telegraf: 12 | image: telegraf:1.13-alpine 13 | volumes: 14 | - ./telegraf/telegraf.conf:/etc/telegraf/telegraf.conf:ro 15 | ports: 16 | - "57555:57555" 17 | depends_on: 18 | - influxdb 19 | 20 | # grafana has more data sources 21 | grafana: 22 | image: grafana/grafana:6.5.0 23 | ports: 24 | - "3000:3000" 25 | user: "104" 26 | volumes: 27 | - grafana-storage:/var/lib/grafana 28 | depends_on: 29 | - influxdb 30 | 31 | # chronograf is similar to grafana, but supports only influxdb 32 | # and provides some administration options for the whole stack 33 | # select which one you prefer (or try both) 34 | chronograf: 35 | image: chronograf:1.7-alpine 36 | ports: 37 | - "8888:8888" 38 | volumes: 39 | - chronograf-storage:/var/lib/chronograf 40 | depends_on: 41 | - influxdb 42 | command: ["--influxdb-url=http://influxdb:8086"] 43 | 44 | # usually if you are using chronograf, you would also want to 45 | # use kapacitor - TICK (Telegraf + InfluxDB + Chronograf + Kapacitor) vs 46 | # TIG (Telegraf + InfluxDB + Grafana) 47 | kapacitor: 48 | image: kapacitor:1.5-alpine 49 | ports: 50 | - "9092:9092" 51 | volumes: 52 | - kapacitor-storage:/var/lib/kapacitor 53 | depends_on: 54 | - influxdb 55 | environment: 56 | - KAPACITOR_INFLUXDB_0_URLS_0=http://influxdb:8086 57 | 58 | volumes: 59 | grafana-storage: 60 | influx-storage: 61 | chronograf-storage: 62 | kapacitor-storage: -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | .envrc 85 | 86 | # virtualenv 87 | .venv* 88 | venv 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | .spyproject 94 | 95 | # Rope project settings 96 | .ropeproject 97 | 98 | # mkdocs documentation 99 | /site 100 | 101 | # mypy 102 | .mypy_cache/ 103 | .idea 104 | tmp/ 105 | .DS_Store 106 | *.sqlite3 107 | .vscode 108 | 109 | 110 | # JS 111 | node_modules/ 112 | -------------------------------------------------------------------------------- /async/http-requests-threads.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | from concurrent.futures import ThreadPoolExecutor 3 | from functools import partial 4 | from typing import Iterable, Tuple 5 | 6 | import requests 7 | from bs4 import BeautifulSoup 8 | import colorama 9 | 10 | URLS = [ 11 | "https://pypi.org", 12 | "https://python.org", 13 | "https://google.com", 14 | "https://amazon.com", 15 | "https://reddit.com", 16 | "https://stackoverflow.com", 17 | "https://ubuntu.com", 18 | "https://facebook.com", 19 | "https://www.microsoft.com", 20 | "https://www.ford.com", 21 | ] 22 | 23 | HEADERS = { 24 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36' 25 | } 26 | 27 | 28 | def get_title(html: str) -> str: 29 | soup = BeautifulSoup(html, 'lxml') 30 | return soup.title.string 31 | 32 | 33 | def download_page_title(url: str, session: requests.Session) -> str: 34 | response = session.get(url, headers=HEADERS) 35 | title = get_title(response.text) 36 | return title 37 | 38 | 39 | def download_all_page_titles(urls) -> Iterable[Tuple[str]]: 40 | with ThreadPoolExecutor(10) as pool: 41 | session = requests.Session() 42 | download_page_title_ = partial(download_page_title, session=session) 43 | result = pool.map(download_page_title_, URLS) 44 | return result 45 | 46 | 47 | def main() -> None: 48 | colorama.init() 49 | start_time = datetime.now() 50 | for url, title in zip(URLS, download_all_page_titles(URLS)): 51 | print(f"Web page {url} has title: {title}") 52 | exec_time = (datetime.now() - start_time).total_seconds() 53 | print(colorama.Fore.GREEN + f"Summary: it took {exec_time:,.2f} seconds to run") 54 | 55 | 56 | if __name__ == '__main__': 57 | main() 58 | -------------------------------------------------------------------------------- /scrapli-apps/scrapli-cfg.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | from scrapli import Scrapli 4 | from scrapli_cfg import ScrapliCfg 5 | from scrapli.logging import enable_basic_logging 6 | 7 | # enable_basic_logging(file=True, level="DEBUG") 8 | device = { 9 | "host": "192.168.152.101", 10 | "auth_username": "cisco", 11 | "auth_password": "cisco", 12 | "auth_strict_key": False, 13 | "platform": "cisco_iosxe", 14 | "transport": "ssh2", 15 | } 16 | CFG_FILE = "input/R1.txt" 17 | 18 | # use https://regex101.com/ with running config example to verify your regexp 19 | BGP_PATTERN = re.compile(r"^router bgp \d+$(?:\n^\s+.*$)*\n!\n", flags=re.I | re.M) 20 | 21 | 22 | def main(): 23 | """This example covers a more complex case of replacing config where some 24 | part of running config should be left untouched, e.g. bgp config, which is marked 25 | {{ bgp }} in the config file and needs to be also marked with correct regexp. 26 | Check easier examples in scrapli-cfg docs for full config replacement""" 27 | with open(CFG_FILE, "r") as f: 28 | cfg = f.read() 29 | # cfg = "banner motd ^Configured by scrapli-cfg5^" 30 | 31 | with Scrapli(**device) as conn: 32 | cfg_conn = ScrapliCfg(conn=conn) # type: ignore 33 | cfg_conn.open() 34 | # do regexp on running-config, extract it 35 | # put into desired config string instead of {{ bgp }} 36 | rendered_config = cfg_conn.render_substituted_config( 37 | config_template=cfg, substitutes=[("bgp", BGP_PATTERN)] 38 | ) 39 | cfg_conn.load_config(config=rendered_config, replace=True) 40 | diff = cfg_conn.diff_config() 41 | print(diff.side_by_side_diff) 42 | print(diff.unified_diff) 43 | cfg_response = cfg_conn.commit_config() 44 | cfg_response.raise_for_status() 45 | 46 | 47 | if __name__ == "__main__": 48 | main() 49 | -------------------------------------------------------------------------------- /ansible/lab-system/lab-system.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configuring matrix switches 3 | hosts: matrix-switches:pod-gear 4 | vars_files: 5 | - user-data/last_run_deployment.yml 6 | - user-data/deployments/{{ deployment }}.yml 7 | 8 | tasks: 9 | - name: Calculate new variables Q-in-Q tunnels based on the lab 10 | deployment_setup: 11 | run_once: true 12 | delegate_to: localhost 13 | delegate_facts: true 14 | tags: 15 | - matrix 16 | - pod-gear 17 | 18 | 19 | - name: Show current deployment 20 | run_once: true 21 | debug: 22 | msg: "Using deployment file: {{ deployment }} for deployment name: {{ deployment_name }}" 23 | 24 | 25 | - name: Update matrix and pod-gear variables 26 | set_fact: 27 | "{{ item }}": "{{ hostvars.localhost.devices[inventory_hostname][item] }}" 28 | loop: "{{ hostvars.localhost.devices[inventory_hostname].updated_vars }}" 29 | when: "hostvars.localhost.devices[inventory_hostname].updated_vars is defined" 30 | tags: 31 | - matrix 32 | - pod-gear 33 | 34 | 35 | - name: Configure matrix switches according to the lab 36 | ios_config: 37 | src: templates/matrix.j2 38 | when: "'matrix-switches' in group_names and 39 | controlled_pod_number == 'all'" 40 | tags: 41 | - matrix 42 | 43 | 44 | - import_role: 45 | name: pod-configuration-setup 46 | when: pod_number is defined and 47 | (controlled_pod_number | int == pod_number | int or 48 | controlled_pod_number == 'all') 49 | tags: 50 | - pod-gear 51 | 52 | 53 | - local_action: 54 | module: copy 55 | content: "deployment: {{ deployment }}" 56 | dest: user-data/last_run_deployment.yml 57 | run_once: true 58 | tags: 59 | - pod-gear 60 | - matrix 61 | 62 | -------------------------------------------------------------------------------- /netprog-stream-django/netprog_stream/network/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.http import HttpResponse, HttpRequest, JsonResponse 3 | from napalm import get_network_driver 4 | from netmiko import ConnectHandler 5 | 6 | from celery.result import AsyncResult 7 | 8 | from .models import Device 9 | from network import tasks 10 | 11 | def index(request: HttpRequest) -> HttpResponse: 12 | devices = Device.objects.all() 13 | context = { 14 | 'title': 'Hello stream!', 15 | 'name': 'Dmitry', 16 | 'devices': devices 17 | } 18 | return render(request, 'base.html', context) 19 | 20 | 21 | def get_device_stats(request: HttpRequest, device_id) -> HttpResponse: 22 | if request.method == 'GET': 23 | device = Device.objects.get(pk=device_id) 24 | driver = get_network_driver(device.napalm_driver) 25 | with driver(device.host, device.username, device.password) as device_conn: 26 | interfaces = device_conn.get_interfaces() 27 | context = { 28 | 'device': device, 29 | 'interfaces': interfaces, 30 | } 31 | return render(request, 'device.html', context) 32 | elif request.method == 'POST': 33 | interface_name = request.POST.get("interface_name") 34 | enable_interface = request.POST.get("enable") 35 | task_id = tasks.switch_interface.delay(device_id, interface_name, enable_interface).id 36 | return HttpResponse(f"

Interface {interface_name} is being switched [task id: {task_id}]

Go to device interfaces page

") 37 | 38 | 39 | def get_task_status(request: HttpRequest, task_id: str) -> JsonResponse: 40 | task = AsyncResult(task_id) 41 | data = { 42 | "id": task_id, 43 | "status": task.state, 44 | "result": task.result 45 | } 46 | return JsonResponse(data) 47 | -------------------------------------------------------------------------------- /network-testing/old-2018-stream/tests_2018/test_krk_vlans.py: -------------------------------------------------------------------------------- 1 | from operator import itemgetter 2 | from pathlib import Path 3 | from typing import Dict, Any 4 | 5 | from ruamel.yaml import YAML 6 | from nornir.core.filter import F 7 | from nornir.plugins.tasks import networking 8 | 9 | CONFIG_FILE = "krk_vlans.yaml" 10 | 11 | 12 | def load_config(config_file: str) -> Dict[str, Any]: 13 | yaml = YAML(typ="safe") 14 | dir_path = Path(__file__).parent 15 | with open(dir_path / config_file) as f: 16 | return yaml.load(f) 17 | 18 | 19 | def process_data(data, config): 20 | result = [] 21 | for vlan_data in data: 22 | name = vlan_data["name"] 23 | id_ = int(vlan_data["vlan_id"]) 24 | if id_ not in config["excluded_vlans"]: 25 | vlan_dict = {"id": id_, "name": name} 26 | result.append(vlan_dict) 27 | return sorted(result, key=itemgetter("id")) 28 | 29 | 30 | def get_data(task, config): 31 | # task.host["vlans"] = 32 | data = task.run( 33 | task=networking.netmiko_send_command, 34 | command_string="show vlan", 35 | use_textfsm=True, 36 | ).result 37 | task.host["vlans"] = process_data(data, config) 38 | 39 | 40 | def test_krk_vlans(nr): 41 | config = load_config(CONFIG_FILE) 42 | hosts = nr.filter(F(groups__contains=config["group"])) 43 | hosts.run(task=get_data, config=config) 44 | 45 | desired_vlans = config["data"]["vlans"] 46 | 47 | for host in hosts.inventory.hosts.values(): 48 | configured_vlans = host["vlans"] 49 | assert configured_vlans == desired_vlans, f"Failed for host: {host.name}" 50 | # if not configured_vlans == desired_vlans: 51 | # raise ValueError( 52 | # f"{host.name!r} has vlans: {configured_vlans}, but " 53 | # f"desired vlans: {desired_vlans}" 54 | # ) 55 | 56 | # print("Test passed: Vlans are correct") 57 | -------------------------------------------------------------------------------- /network-testing/old-2018-stream/tests_2018/test_krk_stp.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | from operator import itemgetter 3 | from pathlib import Path 4 | from typing import Dict, Any 5 | 6 | from ruamel.yaml import YAML 7 | from nornir.core.filter import F 8 | from nornir.plugins.tasks import networking 9 | 10 | CONFIG_FILE = "test_krk_stp.yaml" 11 | 12 | 13 | def load_config(config_file: str) -> Dict[str, Any]: 14 | yaml = YAML(typ="safe") 15 | dir_path = Path(__file__).parent 16 | with open(dir_path / config_file) as f: 17 | return yaml.load(f) 18 | 19 | 20 | def process_data(data, config): 21 | result = [] 22 | for vlan_data in data: 23 | name = vlan_data["name"] 24 | id_ = int(vlan_data["vlan_id"]) 25 | if id_ not in config["excluded_vlans"]: 26 | vlan_dict = {"id": id_, "name": name} 27 | result.append(vlan_dict) 28 | return sorted(result, key=itemgetter("id")) 29 | 30 | 31 | def update_stp_root(task, stp_stats, vlan_ids) -> None: 32 | for vlan_id in vlan_ids: 33 | output = task.run( 34 | task=networking.netmiko_send_command, 35 | command_string=f"show spanning-tree vlan {vlan_id}", 36 | ).result 37 | if "This bridge is the root" in output: 38 | stp_stats["stp_root"]["vlans"][vlan_id].append(task.host.name) 39 | 40 | 41 | def test_krk_stp_root(nr): 42 | config = load_config(CONFIG_FILE) 43 | hosts = nr.filter(F(groups__contains=config["group"])) 44 | configured_stp_stats = {"stp_root": {"vlans": defaultdict(list)}} 45 | desired_stp_stats = config["data"] 46 | hosts.run( 47 | task=update_stp_root, 48 | stp_stats=configured_stp_stats, 49 | vlan_ids=desired_stp_stats["stp_root"]["vlans"].keys(), 50 | ) 51 | 52 | desired_stp_stats = config["data"] 53 | 54 | assert dict(configured_stp_stats["stp_root"]["vlans"]) == desired_stp_stats["stp_root"]["vlans"] 55 | -------------------------------------------------------------------------------- /async/http-requests-async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from datetime import datetime 3 | from typing import Iterable 4 | 5 | import aiohttp 6 | import colorama 7 | from bs4 import BeautifulSoup 8 | 9 | URLS = [ 10 | "https://pypi.org", 11 | "https://python.org", 12 | "https://google.com", 13 | "https://amazon.com", 14 | "https://reddit.com", 15 | "https://stackoverflow.com", 16 | "https://ubuntu.com", 17 | "https://facebook.com", 18 | "https://www.microsoft.com", 19 | "https://www.ford.com", 20 | ] 21 | 22 | HEADERS = { 23 | 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36' 24 | } 25 | 26 | 27 | def get_title(html: str) -> str: 28 | soup = BeautifulSoup(html, 'lxml') 29 | return soup.title.string 30 | 31 | 32 | async def download_page_title(url: str, session: aiohttp.ClientSession) -> str: 33 | async with session.get(url, headers=HEADERS) as response: 34 | from random import random 35 | if random() > 0.8: 36 | raise ValueError("bad") 37 | response.raise_for_status() 38 | html = await response.text() 39 | return get_title(html) 40 | 41 | 42 | async def download_all_page_titles(urls: Iterable[str], loop: asyncio.AbstractEventLoop) -> None: 43 | async with aiohttp.ClientSession() as session: 44 | tasks = [download_page_title(url, session) 45 | for url in urls] 46 | titles = await asyncio.gather(*tasks) 47 | for url, title in zip(urls, titles): 48 | print(f"Web page {url} has title: {title}") 49 | 50 | 51 | def main() -> None: 52 | colorama.init() 53 | start_time = datetime.now() 54 | loop = asyncio.get_event_loop() 55 | loop.run_until_complete(download_all_page_titles(URLS, loop)) 56 | exec_time = (datetime.now() - start_time).total_seconds() 57 | print(colorama.Fore.GREEN + f"Summary: it took {exec_time:,.2f} seconds to run") 58 | 59 | 60 | if __name__ == '__main__': 61 | main() 62 | -------------------------------------------------------------------------------- /netconf/example.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import xml.dom.minidom 3 | import json 4 | 5 | import jinja2 6 | import xmltodict 7 | from ncclient import manager 8 | 9 | CONNECTION_PARAMS = { 10 | 'host': '192.168.153.101', 11 | 'username': 'cisco', 12 | 'password': 'cisco', 13 | 'hostkey_verify': False, 14 | } 15 | 16 | CONFIG_DATA = { 17 | 'loopbacks': [ 18 | { 19 | 'number': '1500', 20 | 'description': 'This one has a description' 21 | }, 22 | { 23 | 'number': '1501', 24 | 'ipv4_address': '100.64.151.1', 25 | 'ipv4_mask': '255.255.255.0', 26 | } 27 | ] 28 | } 29 | 30 | TEMPLATES = jinja2.Environment(loader=jinja2.FileSystemLoader('templates')) 31 | 32 | 33 | def prettify_xml(xml_string): 34 | xml_dom = xml.dom.minidom.parseString(str(xml_string)) 35 | return xml_dom.toprettyxml() 36 | 37 | 38 | def get_config(nc_conn): 39 | nc_reply = nc_conn.get_config(source='running') 40 | current_config = xmltodict.parse(nc_reply.data_xml)['data'] 41 | # print(json.dumps(current_config, indent=2)) 42 | sw_version = current_config['native']['version'] 43 | hostname = current_config['native']['hostname'] 44 | print(f'SW version: {sw_version}') 45 | print(f'hostname: {hostname}') 46 | 47 | 48 | def configure_device(nc_conn, config_data, template_name): 49 | template = TEMPLATES.get_template(template_name) 50 | config = template.render(config_data) 51 | nc_reply = nc_conn.edit_config( 52 | target='running', 53 | config=config, 54 | ) 55 | if nc_reply.ok: 56 | print("Successfully performed NETCONF edit config operation") 57 | 58 | 59 | def main(): 60 | with manager.connect(**CONNECTION_PARAMS) as nc_connection: 61 | get_config(nc_connection) 62 | configure_device( 63 | nc_connection, 64 | config_data=CONFIG_DATA, 65 | template_name='loopbacks.j2' 66 | ) 67 | 68 | 69 | if __name__ == '__main__': 70 | main() 71 | -------------------------------------------------------------------------------- /async/netdev-async.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from datetime import datetime 3 | from typing import List, Dict, Any, Iterable, Tuple 4 | 5 | import netdev 6 | import colorama 7 | 8 | HOSTS = { 9 | "R1": "198.18.1.101", 10 | "R2": "198.18.1.102", 11 | "R3": "198.18.1.103", 12 | "R4": "198.18.1.104", 13 | "R5": "198.18.1.105", 14 | "R6": "198.18.1.106", 15 | "R7": "198.18.1.107", 16 | "R8": "198.18.1.108", 17 | "R9": "198.18.1.109", 18 | "R10": "198.18.1.110", 19 | } 20 | 21 | OTHER_PARAMS = { 22 | "username": "cisco", 23 | "password": "cisco", 24 | "device_type": "cisco_ios", 25 | } 26 | 27 | 28 | COMMANDS = [ 29 | "show version", 30 | "show ip int brief", 31 | "show plat soft status control-processor br" 32 | ] 33 | 34 | 35 | async def get_outputs(host_info: Tuple[str, str], commands: Iterable[str]) -> Iterable[str]: 36 | hostname, host = host_info 37 | device_params = { 38 | "host": host 39 | } 40 | device_params.update(OTHER_PARAMS) 41 | 42 | async with netdev.create(**device_params) as device_conn: 43 | outputs = [await device_conn.send_command(command) for command in commands] 44 | return outputs 45 | 46 | 47 | def main() -> None: 48 | colorama.init() 49 | start_time = datetime.now() 50 | loop = asyncio.get_event_loop() 51 | 52 | tasks = [ 53 | loop.create_task(get_outputs(host_info, COMMANDS)) 54 | for host_info in HOSTS.items() 55 | ] 56 | 57 | loop.run_until_complete(asyncio.wait(tasks)) 58 | 59 | for hostname, task in zip(HOSTS, tasks): 60 | outputs = task.result() 61 | print(f"Device {hostname}") 62 | for command, output in zip(COMMANDS, outputs): 63 | print(f"===== Output from command {command} =====") 64 | print(f"{output}\n") 65 | print(f"=" * 80) 66 | 67 | exec_time = (datetime.now() - start_time).total_seconds() 68 | print(colorama.Fore.GREEN + f"Summary: it took {exec_time:,.2f} seconds to run") 69 | 70 | 71 | if __name__ == '__main__': 72 | main() 73 | -------------------------------------------------------------------------------- /async/http-requests-async2.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from datetime import datetime 3 | from typing import Iterable 4 | 5 | import aiohttp 6 | import colorama 7 | from bs4 import BeautifulSoup 8 | 9 | URLS = [ 10 | "https://pypi.org", 11 | "https://python.org", 12 | "https://google.com", 13 | "https://amazon.com", 14 | "https://reddit.com", 15 | "https://stackoverflow.com", 16 | "https://ubuntu.com", 17 | "https://facebook.com", 18 | "https://www.microsoft.com", 19 | "https://www.ford.com", 20 | ] 21 | 22 | # HEADERS = { 23 | # 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36' 24 | # } 25 | 26 | 27 | def get_title(html: str) -> str: 28 | soup = BeautifulSoup(html, 'lxml') 29 | return soup.title.string 30 | 31 | 32 | async def download_page_title(url: str) -> str: 33 | async with aiohttp.ClientSession() as session: 34 | async with session.get(url) as response: 35 | response.raise_for_status() 36 | html = await response.text() 37 | return get_title(html) 38 | 39 | 40 | # async def download_all_page_titles(urls: Iterable[str], loop: asyncio.AbstractEventLoop) -> None: 41 | # async with aiohttp.ClientSession() as session: 42 | # tasks = [download_page_title(url, session) 43 | # for url in urls] 44 | # titles = await asyncio.gather(*tasks) 45 | # for url, title in zip(urls, titles): 46 | # print(f"Web page {url} has title: {title}") 47 | 48 | 49 | def main() -> None: 50 | colorama.init() 51 | start_time = datetime.now() 52 | loop = asyncio.get_event_loop() 53 | tasks = [ 54 | loop.create_task(download_page_title(url)) 55 | for url in URLS 56 | ] 57 | loop.run_until_complete(asyncio.wait(tasks)) 58 | for url, task in zip(URLS, tasks): 59 | title = task.result() 60 | print(f"Web page {url} has title: {title}") 61 | exec_time = (datetime.now() - start_time).total_seconds() 62 | print(colorama.Fore.GREEN + f"Summary: it took {exec_time:,.2f} seconds to run") 63 | 64 | 65 | if __name__ == '__main__': 66 | main() 67 | -------------------------------------------------------------------------------- /scrapli-apps/ssh-netmiko.py: -------------------------------------------------------------------------------- 1 | from concurrent.futures import ThreadPoolExecutor 2 | from datetime import datetime 3 | import logging 4 | from pathlib import Path 5 | import shutil 6 | from typing import Dict, Any 7 | 8 | from netmiko import ConnectHandler 9 | 10 | from constants import USERNAME, PASSWORD, DEVICES 11 | 12 | NUM_WORKERS = 10 13 | 14 | COMMANDS = [ 15 | "show version", 16 | "show running-config", 17 | "show ip interface brief", 18 | "show arp", 19 | "show platform resources", 20 | ] 21 | CFG = "banner motd $CONFIGURED USING NETMIKO ON {device}$\ninterface loopback 200\ndescription NETMIKO" 22 | OUTPUT_PATH = Path("output/cli/netmiko") 23 | 24 | PLATFORM = "cisco_ios" 25 | 26 | 27 | def create_conn_data(device_data: Dict[str, Any]) -> Dict[str, Any]: 28 | """Creates a connection dictionary for netmiko""" 29 | result = { 30 | "host": device_data["host"], 31 | "username": USERNAME, 32 | "password": PASSWORD, 33 | "device_type": PLATFORM, 34 | "fast_cli": True, 35 | } 36 | return result 37 | 38 | 39 | def show_commands_and_config(device_data: Dict[str, Any]): 40 | """Sends show commands and a config and saves output to a file using netmiko""" 41 | dt_str = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") 42 | device_name = device_data["device_name"] 43 | output_file_path = OUTPUT_PATH / f"{device_name}_{dt_str}.txt" 44 | conn_data = create_conn_data(device_data) 45 | cfg = CFG.format(device=device_name).splitlines() 46 | with ConnectHandler(**conn_data) as conn, open(output_file_path, "w") as f: 47 | for command in COMMANDS: 48 | command_output = conn.send_command(command) 49 | f.write(f"===== {command} ===== \n{command_output}\n") 50 | 51 | f.write("\nSending configuration...\n") 52 | output = conn.send_config_set(cfg) 53 | f.write(output) 54 | 55 | 56 | def main(): 57 | if OUTPUT_PATH.is_dir(): 58 | shutil.rmtree(OUTPUT_PATH) 59 | OUTPUT_PATH.mkdir(exist_ok=True) 60 | 61 | futures = [] 62 | with ThreadPoolExecutor(max_workers=NUM_WORKERS) as pool: 63 | for device_data in DEVICES: 64 | future = pool.submit(show_commands_and_config, device_data) 65 | futures.append(future) 66 | 67 | for future in futures: 68 | _ = future.result() 69 | 70 | 71 | if __name__ == "__main__": 72 | main() 73 | -------------------------------------------------------------------------------- /hashicorp-vault/netmiko-vault.py: -------------------------------------------------------------------------------- 1 | import hvac 2 | import decouple 3 | from netmiko import ConnectHandler 4 | from concurrent.futures import ThreadPoolExecutor 5 | from functools import partial 6 | 7 | from typing import Sequence, Dict 8 | 9 | HOSTS = [ 10 | '10.48.18.26', 11 | '10.48.18.30' 12 | ] 13 | 14 | PARAMS = { 15 | 'device_type': 'cisco_ios' 16 | } 17 | 18 | COMMANDS = [ 19 | 'show version', 20 | 'show ip int brief', 21 | 'show plat soft status control-processor brief' 22 | ] 23 | 24 | VAULT_SERVER = 'http://localhost:8200' 25 | 26 | # device_conn = ConnectHandler(**device_params) 27 | # 28 | # parsed_values = dict() 29 | # parsed_values.update(parse_show_version(device_conn.send_command('show version'))) 30 | # parsed_values.update( 31 | # parse_show_mac_address_table(device_conn.send_command('show mac address-table'))) 32 | # 33 | # result = '{hostname} MAC address table:\n{mac_address_table}'.format(**parsed_values) 34 | # device_conn.disconnect() 35 | # return result 36 | 37 | def form_device_params(host: str, params: Dict[str, str]) -> Dict[str, str]: 38 | return {'host': host, **params, **PARAMS} 39 | 40 | 41 | def get_username_password(vault_server, vault_token: str) -> Dict[str, str]: 42 | vault = hvac.Client(url=vault_server, token=vault_token) 43 | result = { 44 | 'username': vault.read('kv/CSR_USERNAME')['data']['value'], 45 | 'password': vault.read('kv/CSR_PASSWORD')['data']['value'], 46 | } 47 | return result 48 | 49 | 50 | def get_outputs(device_info: Dict[str, str], commands: Sequence[str]) -> Dict[str, str]: 51 | result = {} 52 | with ConnectHandler(**device_info) as device_conn: 53 | for command in commands: 54 | result[command] = device_conn.send_command(command) 55 | return result 56 | 57 | 58 | def main(): 59 | params = get_username_password(VAULT_SERVER, decouple.config('VAULT_TOKEN')) 60 | worker = partial(get_outputs, commands=COMMANDS) 61 | devices_params = (form_device_params(host, params) for host in HOSTS) 62 | with ThreadPoolExecutor(2) as pool: 63 | results = pool.map(worker, devices_params) 64 | 65 | for host, result in zip(HOSTS, results): 66 | print(f'===== Device: {host} =====') 67 | for command, output in result.items(): 68 | print(f'=== output from {command!r} ===') 69 | print(output, end='\n=========\n') 70 | 71 | 72 | 73 | if __name__ == '__main__': 74 | main() 75 | -------------------------------------------------------------------------------- /netbox/aiohttp_netbox.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import async_timeout 3 | import time 4 | from pprint import pprint 5 | 6 | import aiohttp 7 | import decouple 8 | 9 | 10 | NETBOX_API_ROOT = "http://netbox:32768/api" 11 | NETBOX_DEVICES_ENDPOINT = "/dcim/devices/" 12 | NETBOX_INTERFACES_ENDPOINT = "/dcim/interfaces/" 13 | NETBOX_SITES_ENDPOINT = "/dcim/sites/" 14 | NETBOX_IP_ADDRESSES_ENDPOINT = "/ipam/ip-addresses/" 15 | NETBOX_VLANS_ENDPOINT = "/ipam/vlans/" 16 | 17 | 18 | def form_headers(): 19 | api_token = decouple.config("NETBOX_API_TOKEN") 20 | 21 | headers = { 22 | "Authorization": "Token {}".format(api_token), 23 | "Content-Type": "application/json", 24 | "Accept": "application/json", 25 | } 26 | return headers 27 | 28 | 29 | async def fetch_json(url, params=None): 30 | async with aiohttp.ClientSession() as session: 31 | async with async_timeout.timeout(10): 32 | async with session.get( 33 | url, headers=form_headers(), params=params 34 | ) as response: 35 | return await response.json() 36 | 37 | 38 | async def get_everything_from_netbox(): 39 | query_params = {"device": "SJ-R1"} 40 | 41 | ip_address_netbox_dict_coroutine = await fetch_json( 42 | NETBOX_API_ROOT + NETBOX_IP_ADDRESSES_ENDPOINT, params=query_params 43 | ) 44 | ip_address_netbox_dict = ip_address_netbox_dict_coroutine["results"] 45 | 46 | pprint(ip_address_netbox_dict) 47 | 48 | device_interfaces_netbox_dict_coroutine = await fetch_json( 49 | NETBOX_API_ROOT + NETBOX_INTERFACES_ENDPOINT, params=query_params 50 | ) 51 | device_interfaces_netbox_dict = device_interfaces_netbox_dict_coroutine["results"] 52 | 53 | pprint(device_interfaces_netbox_dict) 54 | 55 | 56 | def main(): 57 | start_time = time.time() 58 | 59 | urls = [ 60 | NETBOX_API_ROOT + NETBOX_DEVICES_ENDPOINT, 61 | NETBOX_API_ROOT + NETBOX_IP_ADDRESSES_ENDPOINT, 62 | ] 63 | 64 | loop = asyncio.get_event_loop() 65 | 66 | # tasks = [ 67 | # loop.create_task(get_information_from_netbox(url)) 68 | # for url in urls 69 | # ] 70 | 71 | tasks = [loop.create_task(get_everything_from_netbox())] 72 | 73 | loop.run_until_complete(asyncio.wait(tasks)) 74 | 75 | # for task in tasks: 76 | # print(task.result()) 77 | 78 | print("It took {} seconds to run".format(time.time() - start_time)) 79 | 80 | 81 | if __name__ == "__main__": 82 | main() 83 | -------------------------------------------------------------------------------- /nornir/network_diagram/constants.py: -------------------------------------------------------------------------------- 1 | RESTCONF_ROOT = "https://{host}/restconf/data" 2 | OPENCONFIG_LLDP_NEIGHBORS_ENDPOINT = "/lldp/interfaces/interface" 3 | HEADERS = { 4 | "Accept": "application/yang-data+json", 5 | "Content-Type": "application/yang-data+json", 6 | } 7 | 8 | LOGGING_DICT = { 9 | "version": 1, 10 | "disable_existing_loggers": True, 11 | "formatters": { 12 | "std-module": { 13 | "format": "[%(asctime)s] %(levelname)-8s {%(name)s:%(lineno)d} %(message)s" 14 | }, 15 | "std": { 16 | "format": "[%(asctime)s] %(levelname)-8s {%(filename)s:%(lineno)d} %(message)s" 17 | } 18 | }, 19 | "handlers": { 20 | "file-module": { 21 | "level": "DEBUG", 22 | "class": "logging.handlers.RotatingFileHandler", 23 | "filename": "app.log", 24 | "maxBytes": 1024 * 1024 * 5, 25 | "backupCount": 5, 26 | "formatter": "std-module", 27 | }, 28 | "file": { 29 | "level": "DEBUG", 30 | "class": "logging.handlers.RotatingFileHandler", 31 | "filename": "app.log", 32 | "maxBytes": 1024 * 1024 * 5, 33 | "backupCount": 5, 34 | "formatter": "std", 35 | }, 36 | "console-module": { 37 | "level": "DEBUG", 38 | "class": "logging.StreamHandler", 39 | "stream": "ext://sys.stdout", 40 | "formatter": "std-module", 41 | }, 42 | "console": { 43 | "level": "DEBUG", 44 | "class": "logging.StreamHandler", 45 | "stream": "ext://sys.stdout", 46 | "formatter": "std", 47 | }, 48 | }, 49 | "loggers": { 50 | "nornir": { 51 | "handlers": ["console-module", "file-module"], 52 | "level": "WARNING", 53 | "propagate": False, 54 | }, 55 | "netmiko": { 56 | "handlers": ["console-module", "file-module"], 57 | "level": "WARNING", 58 | "propagate": False, 59 | }, 60 | "paramiko": { 61 | "handlers": ["console-module", "file-module"], 62 | "level": "WARNING", 63 | "propagate": False, 64 | }, 65 | # "": { 66 | # "handlers": ["console", "default"], 67 | # "level": "DEBUG", 68 | # "propagate": False 69 | # } 70 | }, 71 | "root": {"handlers": ["console", "file"], "level": "INFO"}, 72 | } 73 | -------------------------------------------------------------------------------- /scrapli-apps/nc-ncclient.py: -------------------------------------------------------------------------------- 1 | from concurrent.futures import ThreadPoolExecutor 2 | from datetime import datetime 3 | import logging 4 | from pathlib import Path 5 | import shutil 6 | from typing import Any, Dict 7 | 8 | from lxml import etree 9 | from ncclient import manager 10 | 11 | from constants import DEVICES, USERNAME, PASSWORD 12 | import utils 13 | 14 | # logging.basicConfig(level="DEBUG", filename="nc.log") 15 | 16 | OUTPUT_DIR = Path("output/netconf/ncclient") 17 | NUM_WORKERS = 10 18 | NC_EDIT_CONFIG_FILE = "input/nc-config.yaml" 19 | 20 | 21 | def create_conn_data(device_data: Dict[str, Any]) -> Dict[str, Any]: 22 | """Creates a connection dictionary for ncclient""" 23 | result = { 24 | "host": device_data["host"], 25 | "username": USERNAME, 26 | "password": PASSWORD, 27 | "hostkey_verify": False, 28 | } 29 | return result 30 | 31 | 32 | def nc_get_edit_cfg(device_data: Dict[str, Any], cfg: str) -> None: 33 | """Retrieves config with get-config and changes it with edit-config with the 34 | input from YAML file using ncclient""" 35 | dt_str = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") 36 | device_name = device_data["device_name"] 37 | output_path = OUTPUT_DIR / f"{device_name}_{dt_str}_config.xml" 38 | 39 | nc_conn_data = create_conn_data(device_data) 40 | with manager.connect(**nc_conn_data) as nc_conn, open(output_path, "wb") as f: 41 | nc_response = nc_conn.get_config(source="running") 42 | xml_bytes = etree.tostring(nc_response.data_ele, pretty_print=True) 43 | f.write(xml_bytes) 44 | 45 | nc_response = nc_conn.edit_config(config=cfg, target="running") 46 | if not nc_response.ok: 47 | raise ValueError(f"{device_name}: {nc_response.xml}") 48 | 49 | 50 | def main(): 51 | if OUTPUT_DIR.is_dir(): 52 | shutil.rmtree(OUTPUT_DIR) 53 | OUTPUT_DIR.mkdir(exist_ok=True) 54 | 55 | with open(NC_EDIT_CONFIG_FILE) as f: 56 | default_ns = "urn:ietf:params:xml:ns:netconf:base:1.0" 57 | root_element = etree.Element("config", nsmap={None: default_ns}) # type: ignore 58 | cfg = utils.yaml_to_xml_str(f.read(), root=root_element) 59 | 60 | futures = [] 61 | with ThreadPoolExecutor(max_workers=NUM_WORKERS) as pool: 62 | for device_data in DEVICES: 63 | future = pool.submit(nc_get_edit_cfg, device_data, cfg) 64 | futures.append(future) 65 | 66 | for future in futures: 67 | _ = future.result() 68 | 69 | 70 | if __name__ == "__main__": 71 | main() 72 | -------------------------------------------------------------------------------- /netbox/add_devices_to_netbox.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import requests 4 | 5 | from helper import read_yaml, form_device_params_from_yaml 6 | 7 | NETBOX_API_ROOT = "http://netbox:32772/api" 8 | NETBOX_DEVICES_ENDPOINT = "/dcim/devices/" 9 | NETBOX_SITES_ENDPOINT = "/dcim/sites/" 10 | 11 | SITES = [{"name": "Krakow", "slug": "krk"}, {"name": "Reykjavík", "slug": "rkvk"}] 12 | 13 | 14 | class NetboxAPITokenNotFound(Exception): 15 | pass 16 | 17 | 18 | def form_headers(): 19 | api_token = os.environ.get("NETBOX_API_TOKEN") 20 | if api_token is None: 21 | raise NetboxAPITokenNotFound( 22 | "NETBOX_API_TOKEN was not found in environmental variables" 23 | ) 24 | 25 | headers = { 26 | "Authorization": "Token {}".format(api_token), 27 | "Content-Type": "application/json", 28 | "Accept": "application/json", 29 | } 30 | return headers 31 | 32 | 33 | def add_site(name, slug): 34 | headers = form_headers() 35 | 36 | data = {"name": name, "slug": slug} 37 | 38 | r = requests.post( 39 | NETBOX_API_ROOT + NETBOX_SITES_ENDPOINT, headers=headers, json=data 40 | ) 41 | 42 | if r.status_code == 201: 43 | print(f"Site {name} was created successfully") 44 | else: 45 | r.raise_for_status() 46 | 47 | 48 | def add_sites(): 49 | """Add sites from SITES dictionary""" 50 | for site in SITES: 51 | add_site(**site) 52 | print("All sites have been added") 53 | 54 | 55 | def add_device(name, device_type_id, site_id, device_role_id): 56 | headers = form_headers() 57 | 58 | data = { 59 | "name": name, 60 | "display_name": name, 61 | "device_type": device_type_id, # 2, 62 | "site": site_id, # 1, 63 | "status": 1, 64 | } 65 | if device_role_id is not None: 66 | data["device_role"] = device_role_id 67 | 68 | r = requests.post( 69 | NETBOX_API_ROOT + NETBOX_DEVICES_ENDPOINT, headers=headers, json=data 70 | ) 71 | 72 | if r.status_code == 201: 73 | print(f"Device {name} was added successfully") 74 | else: 75 | r.raise_for_status() 76 | 77 | 78 | def add_devices(): 79 | parsed_yaml = read_yaml() 80 | devices_params_gen = form_device_params_from_yaml(parsed_yaml) 81 | for device_params in devices_params_gen: 82 | add_device(**device_params) 83 | print("All devices have been imported") 84 | 85 | 86 | def main(): 87 | # headers = form_headers() 88 | # add_sites() 89 | add_devices() 90 | 91 | 92 | if __name__ == "__main__": 93 | main() 94 | -------------------------------------------------------------------------------- /nornir/exploring/main.py: -------------------------------------------------------------------------------- 1 | from nornir.core import InitNornir 2 | from nornir.plugins.tasks import commands, networking, text, apis 3 | from nornir.plugins.functions.text import print_result 4 | 5 | 6 | def basic_configuration(task): 7 | # Transform inventory data to configuration via a template file 8 | result = task.run( 9 | task=text.template_file, 10 | name="Interface Configuration", 11 | template="interfaces.j2", 12 | path="templates" 13 | ) 14 | 15 | # Save the compiled configuration into a host variable 16 | task.host["config"] = result.result 17 | 18 | # Deploy that configuration to the device using NAPALM 19 | # task.run( 20 | # task=networking.napalm_configure, 21 | # name="Loading Configuration on the device", 22 | # replace=False, 23 | # configuration=task.host["config"], 24 | # ) 25 | # Deploy that configuration to the device using Netmiko 26 | task.run( 27 | task=networking.netmiko_send_config, 28 | name="Loading Configuration on the device [Netmiko]", 29 | config_commands=task.host["config"].splitlines(), 30 | ) 31 | 32 | 33 | def main(): 34 | nornir_runner = InitNornir(config_file="config-ansible.yaml") 35 | 36 | inventory = nornir_runner.inventory 37 | # for host in inventory.hosts.values(): 38 | # print(host.items()) 39 | # print(nornir_runner.inventory.hosts.items()) 40 | nornir_runner.filter(filter_func=lambda x: x.get("site") == "sj" or x.get("country") == 'us').inventory.hosts.keys() 41 | 42 | servers = nornir_runner.filter(role="server") 43 | result = servers.run(task=commands.remote_command, command="whoami ; python -V") 44 | print_result(result) 45 | result = servers.run(task=apis.http_method, url="http://localhost:3080/v2/computes") 46 | print_result(result) 47 | 48 | # sj_edge = nornir_runner.filter(site="sj") 49 | # result = sj_edge.run(task=networking.napalm_get, name="Collecting facts using NAPALM", getters=["facts"]) 50 | # print_result(result) 51 | # 52 | # result = sj_edge.run(task=basic_configuration, name="Compiling and applying configuration") 53 | # print_result(result) 54 | 55 | sj_br1 = nornir_runner.filter(name="sj-br1") 56 | result = sj_br1.run(task=networking.netmiko_file_transfer, 57 | name="send file using Netmiko", 58 | source_file="config-ansible.yaml", 59 | dest_file="config-ansible.yaml") 60 | 61 | print_result(result) 62 | 63 | 64 | if __name__ == '__main__': 65 | main() 66 | -------------------------------------------------------------------------------- /scrapli-apps/nc-scrapli.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from concurrent.futures import ThreadPoolExecutor 3 | from datetime import datetime 4 | from pathlib import Path 5 | import shutil 6 | from typing import Any, Dict, cast 7 | 8 | from lxml import etree 9 | from ruamel.yaml import YAML 10 | from scrapli_netconf.driver import NetconfDriver 11 | 12 | from constants import DEVICES, USERNAME, PASSWORD 13 | import utils 14 | 15 | # logging.basicConfig(level="DEBUG") 16 | 17 | OUTPUT_DIR = Path("output/netconf/scrapli-netconf") 18 | NUM_WORKERS = 10 19 | SCRAPLI_TRANSPORT = "ssh2" 20 | NC_EDIT_CONFIG_FILE = "input/nc-config.yaml" 21 | 22 | 23 | def create_conn_data(device_data: Dict[str, Any]) -> Dict[str, Any]: 24 | """Creates a connection dictionary for scrapli-netconf""" 25 | result = { 26 | "host": device_data["host"], 27 | "auth_username": USERNAME, 28 | "auth_password": PASSWORD, 29 | "transport": SCRAPLI_TRANSPORT, 30 | "auth_strict_key": False, 31 | "ssh_config_file": True, 32 | } 33 | return result 34 | 35 | 36 | def nc_get_edit_cfg(device_data: Dict[str, Any], cfg: str): 37 | """Retrieves config with get-config and changes it with edit-config with the 38 | input from YAML file using scrapli-netconf""" 39 | dt_str = datetime.now().strftime("%Y-%m-%dT%H:%M:%S") 40 | device_name = device_data["device_name"] 41 | output_path = OUTPUT_DIR / f"{device_name}_{dt_str}_config.xml" 42 | conn_data = create_conn_data(device_data) 43 | with NetconfDriver(**conn_data) as nc_conn, open(output_path, "wb") as f: 44 | nc_conn = cast(NetconfDriver, nc_conn) 45 | nc_response = nc_conn.get_config(source="running") 46 | xml_bytes = etree.tostring(nc_response.xml_result, pretty_print=True) 47 | f.write(xml_bytes) 48 | 49 | nc_response = nc_conn.edit_config(cfg, target="running") 50 | if nc_response.failed: 51 | raise ValueError(f"{device_name}: {nc_response.result}") 52 | 53 | 54 | def main(): 55 | if OUTPUT_DIR.is_dir(): 56 | shutil.rmtree(OUTPUT_DIR) 57 | OUTPUT_DIR.mkdir(exist_ok=True) 58 | 59 | with open(NC_EDIT_CONFIG_FILE) as f: 60 | cfg = utils.yaml_to_xml_str(f.read(), root="config") 61 | 62 | futures = [] 63 | with ThreadPoolExecutor(max_workers=NUM_WORKERS) as pool: 64 | for device_data in DEVICES: 65 | future = pool.submit(nc_get_edit_cfg, device_data, cfg) 66 | futures.append(future) 67 | 68 | for future in futures: 69 | _ = future.result() 70 | 71 | 72 | if __name__ == "__main__": 73 | main() 74 | --------------------------------------------------------------------------------