├── Chapter05 ├── Templates │ ├── file1 │ ├── chapter5_7.retry │ ├── chapter5_8.retry │ ├── chapter5_7.yml │ ├── nx-osv-2.conf │ ├── nx-osv-1.conf │ ├── nxos.j2 │ └── chapter5_8.yml ├── Roles │ ├── chapter5_12.retry │ ├── roles │ │ ├── spines │ │ │ ├── vars │ │ │ │ └── main.yml │ │ │ └── tasks │ │ │ │ └── main.yml │ │ └── cisco_nexus │ │ │ ├── vars │ │ │ └── main.yml │ │ │ └── tasks │ │ │ └── main.yml │ ├── hosts │ ├── chapter5_12.yml │ └── chapter5_13.yml ├── Custom_Modules │ ├── chapter5_14.retry │ ├── chapter5_14.yml │ ├── chapter5_15.yml │ └── library │ │ ├── custom_module_1.py │ │ └── custom_module_2.py ├── Includes │ ├── group_vars │ │ └── all │ ├── show_output.yml │ ├── chapter5_11_2.yml │ ├── nx-osv-2.conf │ ├── chapter5_11_1.yml │ ├── nx-osv-1.conf │ ├── nxos.j2 │ └── host_vars │ │ └── localhost ├── Group_Host_Vars │ ├── group_vars │ │ └── all │ ├── nx-osv-2.conf │ ├── chapter5_9.yml │ ├── nx-osv-1.conf │ ├── nxos.j2 │ └── host_vars │ │ └── localhost ├── Vaults │ ├── nx-osv-2.conf │ ├── chapter5_10.yml │ ├── secret.yml │ ├── nx-osv-1.conf │ ├── group_vars │ │ └── all │ ├── nxos.j2 │ └── host_vars │ │ └── localhost ├── chapter5_4.yml ├── hosts ├── chapter5_5.yml ├── chapter5_1.yml ├── chapter5_2.yml ├── chapter5_3.yml └── chapter5_6.yml ├── Chapter01 ├── math_stuff │ ├── __init__.py │ └── subtract.py └── helloworld.py ├── Chapter08 ├── cisco_config_netflow.retry ├── cisco_discover_lldp.retry ├── chapter8_gaphviz_1.gv ├── output │ ├── chapter8_gv_1.png │ ├── chapter8_gv_2.pdf │ ├── chapter8_gv_3.gv.pdf │ ├── chapter8_lldp_graph.gv.pdf │ ├── chapter8_lldp_graph.gv_v1.pdf │ ├── chapter8_lldp_graph.gv_neato.pdf │ ├── chapter8_gv_3.gv │ └── chapter8_lldp_graph.gv ├── chapter8_gv_1.gv ├── chapter8_sflowtool_1.py ├── chapter8_gv_2.gv ├── hosts ├── chapter8_ntop_1.py ├── cisco_config_lldp.yml ├── chapter8_ntop_2.py ├── tmp │ ├── r1_lldp_output.txt │ ├── r2_lldp_output.txt │ ├── r6-edge_lldp_output.txt │ ├── r3_lldp_output.txt │ └── r5-tor_lldp_output.txt ├── cisco_discover_lldp.yml ├── chapter8_logstash_1.py ├── cisco_graph_lldp.py ├── cisco_config_netflow.yml └── netFlow_v5_parser.py ├── Chapter02 ├── commands.txt ├── hosts.json ├── devices.json ├── iosv-1_output.txt ├── iosv-2_output.txt ├── chapter2_1.py ├── chapter2_2.py ├── chapter2_3.py └── chapter2_4.py ├── Chapter07 ├── matplotlib_1_result.png ├── matplotlib_2_result.png ├── matplotlib_3_result.png ├── pygal_2.py ├── matplotlib_3.py ├── cacti_2.py ├── cacti_1.py ├── matplotlib_1.py ├── pysnmp_2.py ├── pysnmp_1.py ├── pygal_1.py ├── matplotlib_2.py ├── pysnmp_3.py ├── results.txt └── pygal_example_3.svg ├── Chapter04 ├── df_playbook.yml ├── df_playbook.json ├── cisco_1.yml ├── juniper_1.yml ├── arista_1.yml ├── cisco_2.yml ├── cisco_5.yml ├── cisco_3.yml └── cisco_4.yml ├── Chapter03 ├── Cisco │ ├── cisco_yang_1_interfaces.xml │ ├── cisco_nxapi_1.py │ ├── cisco_nxapi_3.py │ ├── cisco_nxapi_2.py │ ├── cisco_yang_1.py │ ├── cisco_nxapi_4.py │ └── cisco_apic_em_1.py ├── Arista │ ├── eapi_1.py │ ├── pyeapi_1.py │ ├── eapi_2.py │ └── eapi_2_acl.py └── Juniper │ ├── junos_pyez_1.py │ ├── junos_netconf_1.py │ ├── junos_pyez_2.py │ ├── junos_netconf_2.py │ └── junos_netconf_3.py ├── Chapter06 ├── hosts ├── python_re_search_1.py ├── python_re_search_2.py ├── access_list_mac_iosv.yml └── access_list_nxosv.yml ├── Chapter09 ├── chapter9_1.py ├── requirements.txt ├── chapter9_2.py ├── chapter9_5.py ├── chapter9_3.py ├── chapter9_4.py ├── chapter9_request_1.py ├── chapter9_pexpect_1.py ├── chapter9_db_1.py ├── chapter9_6.py ├── chapter9_7.py ├── chapter9_8.py └── chapter9_9.py ├── .gitattributes ├── Chapter11 ├── Chapter11_6.py ├── Chapter11_7.py ├── Chapter11_1.py ├── Chapter11_5.py ├── Chapter11_2.py ├── Chapter11_3.py └── Chapter11_4.py ├── Chapter13 ├── chapter13_mininet_4.py ├── chapter13_mininet_2.py ├── chapter13_mininet_3.py ├── chapter13_bgp_1.py ├── chapter13_mininet_1.py ├── chapter13_jsonrpc_1.py ├── chapter13_switch_1.py └── chapter13_switch_2.py ├── Chapter10 ├── chapter10_1.py └── chapter10_2.py ├── LICENSE ├── .gitignore └── README.md /Chapter05/Templates/file1: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter01/math_stuff/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Chapter05/Roles/chapter5_12.retry: -------------------------------------------------------------------------------- 1 | nxos-r2 2 | -------------------------------------------------------------------------------- /Chapter08/cisco_config_netflow.retry: -------------------------------------------------------------------------------- 1 | r5-tor 2 | -------------------------------------------------------------------------------- /Chapter05/Templates/chapter5_7.retry: -------------------------------------------------------------------------------- 1 | localhost 2 | -------------------------------------------------------------------------------- /Chapter05/Templates/chapter5_8.retry: -------------------------------------------------------------------------------- 1 | localhost 2 | -------------------------------------------------------------------------------- /Chapter05/Custom_Modules/chapter5_14.retry: -------------------------------------------------------------------------------- 1 | localhost 2 | -------------------------------------------------------------------------------- /Chapter01/helloworld.py: -------------------------------------------------------------------------------- 1 | # This is a comment 2 | print("hello world") 3 | 4 | -------------------------------------------------------------------------------- /Chapter05/Includes/group_vars/all: -------------------------------------------------------------------------------- 1 | --- 2 | username: cisco 3 | password: cisco 4 | -------------------------------------------------------------------------------- /Chapter08/cisco_discover_lldp.retry: -------------------------------------------------------------------------------- 1 | r1 2 | r2 3 | r3 4 | r5-tor 5 | r6-edge 6 | -------------------------------------------------------------------------------- /Chapter05/Group_Host_Vars/group_vars/all: -------------------------------------------------------------------------------- 1 | --- 2 | username: cisco 3 | password: cisco 4 | -------------------------------------------------------------------------------- /Chapter02/commands.txt: -------------------------------------------------------------------------------- 1 | config t 2 | logging buffered 30000 3 | end 4 | copy run start 5 | -------------------------------------------------------------------------------- /Chapter01/math_stuff/subtract.py: -------------------------------------------------------------------------------- 1 | def subtract(a, b): 2 | c = a - b 3 | return c 4 | 5 | -------------------------------------------------------------------------------- /Chapter05/Includes/show_output.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: show output 3 | debug: 4 | var: output 5 | -------------------------------------------------------------------------------- /Chapter02/hosts.json: -------------------------------------------------------------------------------- 1 | { 2 | "iosv-1": {"ip": "172.16.1.20"}, 3 | "iosv-2": {"ip": "172.16.1.21"} 4 | } 5 | 6 | -------------------------------------------------------------------------------- /Chapter02/devices.json: -------------------------------------------------------------------------------- 1 | { 2 | "iosv-1": {"ip": "172.16.1.20"}, 3 | "iosv-2": {"ip": "172.16.1.21"} 4 | } 5 | 6 | -------------------------------------------------------------------------------- /Chapter08/chapter8_gaphviz_1.gv: -------------------------------------------------------------------------------- 1 | graph my_network { 2 | core -- distribution; 3 | distribution -- access; 4 | } 5 | -------------------------------------------------------------------------------- /Chapter07/matplotlib_1_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking/HEAD/Chapter07/matplotlib_1_result.png -------------------------------------------------------------------------------- /Chapter07/matplotlib_2_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking/HEAD/Chapter07/matplotlib_2_result.png -------------------------------------------------------------------------------- /Chapter07/matplotlib_3_result.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking/HEAD/Chapter07/matplotlib_3_result.png -------------------------------------------------------------------------------- /Chapter08/output/chapter8_gv_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking/HEAD/Chapter08/output/chapter8_gv_1.png -------------------------------------------------------------------------------- /Chapter08/output/chapter8_gv_2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking/HEAD/Chapter08/output/chapter8_gv_2.pdf -------------------------------------------------------------------------------- /Chapter08/chapter8_gv_1.gv: -------------------------------------------------------------------------------- 1 | graph my_network { 2 | core -- distribution; 3 | distribution -- access1; 4 | distribution -- access2; 5 | } 6 | -------------------------------------------------------------------------------- /Chapter08/output/chapter8_gv_3.gv.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking/HEAD/Chapter08/output/chapter8_gv_3.gv.pdf -------------------------------------------------------------------------------- /Chapter04/df_playbook.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - hosts: 192.168.199.170 3 | 4 | tasks: 5 | - name: check disk usage 6 | shell: df > df_temp.txt 7 | 8 | -------------------------------------------------------------------------------- /Chapter05/Roles/roles/spines/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | cli: 3 | host: "{{ ansible_host }}" 4 | username: cisco 5 | password: cisco 6 | transport: cli 7 | -------------------------------------------------------------------------------- /Chapter05/Roles/roles/cisco_nexus/vars/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | cli: 3 | host: "{{ ansible_host }}" 4 | username: cisco 5 | password: cisco 6 | transport: cli 7 | -------------------------------------------------------------------------------- /Chapter08/output/chapter8_lldp_graph.gv.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking/HEAD/Chapter08/output/chapter8_lldp_graph.gv.pdf -------------------------------------------------------------------------------- /Chapter05/Roles/hosts: -------------------------------------------------------------------------------- 1 | [cisco_nexus] 2 | nxos-r1 ansible_host=172.16.1.142 3 | nxos-r2 ansible_host=172.16.1.143 4 | 5 | [spines] 6 | nxos-r1 ansible_host=172.16.1.142 7 | -------------------------------------------------------------------------------- /Chapter08/output/chapter8_lldp_graph.gv_v1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking/HEAD/Chapter08/output/chapter8_lldp_graph.gv_v1.pdf -------------------------------------------------------------------------------- /Chapter08/output/chapter8_lldp_graph.gv_neato.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking/HEAD/Chapter08/output/chapter8_lldp_graph.gv_neato.pdf -------------------------------------------------------------------------------- /Chapter05/Roles/roles/spines/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: change logging level 3 | nxos_config: 4 | lines: 5 | - logging level local7 7 6 | provider: "{{ cli }}" 7 | -------------------------------------------------------------------------------- /Chapter03/Cisco/cisco_yang_1_interfaces.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Chapter05/Roles/chapter5_12.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: playbook for cisco_nexus role 3 | hosts: "cisco_nexus" 4 | gather_facts: false 5 | connection: local 6 | 7 | roles: 8 | - cisco_nexus 9 | -------------------------------------------------------------------------------- /Chapter04/df_playbook.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "hosts": "192.168.199.170", 4 | "tasks": [ 5 | "name": "check disk usage", 6 | "shell": "df > df_temp.txt" 7 | ] 8 | } 9 | ] 10 | 11 | -------------------------------------------------------------------------------- /Chapter05/Roles/chapter5_13.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: playbook for spine role 3 | hosts: "spines" 4 | gather_facts: false 5 | connection: local 6 | 7 | roles: 8 | - cisco_nexus 9 | - spines 10 | -------------------------------------------------------------------------------- /Chapter08/output/chapter8_gv_3.gv: -------------------------------------------------------------------------------- 1 | // My Network 2 | digraph { 3 | core 4 | distribution 5 | access1 6 | access2 7 | core -> distribution 8 | distribution -> access1 9 | distribution -> access2 10 | } -------------------------------------------------------------------------------- /Chapter08/chapter8_sflowtool_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import sys, re 4 | 5 | for line in iter(sys.stdin.readline, ''): 6 | if re.search('agent ', line): 7 | print(line.strip()) 8 | 9 | 10 | -------------------------------------------------------------------------------- /Chapter05/Templates/chapter5_7.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Template Basic 3 | hosts: localhost 4 | 5 | tasks: 6 | - name: copy one file to another 7 | template: 8 | src=./file1 9 | dest=./file2 10 | -------------------------------------------------------------------------------- /Chapter06/hosts: -------------------------------------------------------------------------------- 1 | [nxosv-devices] 2 | nx-osv-1 ansible_host=172.16.1.155 ansible_username=cisco ansible_password=cisco 3 | 4 | [iosv-devices] 5 | iosv-1 ansible_host=172.16.1.160 ansible_username=cisco ansible_password=cisco 6 | 7 | -------------------------------------------------------------------------------- /Chapter05/Includes/chapter5_11_2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: show users 3 | hosts: localhost 4 | 5 | tasks: 6 | - name: show local users 7 | command: who 8 | register: output 9 | 10 | - include: show_output.yml 11 | -------------------------------------------------------------------------------- /Chapter08/chapter8_gv_2.gv: -------------------------------------------------------------------------------- 1 | digraph my_network { 2 | node [shape=box]; 3 | size = "50 30"; 4 | core -> distribution [label="2x10G"]; 5 | distribution -> access1 [label="1G"]; 6 | distribution -> access2 [label="1G"]; 7 | } 8 | -------------------------------------------------------------------------------- /Chapter05/Roles/roles/cisco_nexus/tasks/main.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: configure logging parameters 3 | nxos_config: 4 | lines: 5 | - logging server 191.168.1.100 6 | - logging event link-status default 7 | provider: "{{ cli }}" 8 | -------------------------------------------------------------------------------- /Chapter02/iosv-1_output.txt: -------------------------------------------------------------------------------- 1 | terminal length 0 2 | iosv-1#config t 3 | Enter configuration commands, one per line. End with CNTL/Z. 4 | iosv-1(config)#logging buffered 30000 5 | iosv-1(config)#end 6 | iosv-1#copy run start 7 | Destination filename [startup-config]? -------------------------------------------------------------------------------- /Chapter02/iosv-2_output.txt: -------------------------------------------------------------------------------- 1 | terminal length 0 2 | iosv-2#config t 3 | Enter configuration commands, one per line. End with CNTL/Z. 4 | iosv-2(config)#logging buffered 30000 5 | iosv-2(config)#end 6 | iosv-2#copy run start 7 | Destination filename [startup-config]? -------------------------------------------------------------------------------- /Chapter09/chapter9_1.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/') 5 | def hello_networkers(): 6 | return 'Hello Networkers!' 7 | 8 | if __name__ == '__main__': 9 | app.run(host='0.0.0.0', debug=True) 10 | 11 | 12 | -------------------------------------------------------------------------------- /Chapter05/Vaults/nx-osv-2.conf: -------------------------------------------------------------------------------- 1 | hostname nx-osv-2 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | 9 | username cisco password cisco role network-operator 10 | 11 | vlan 100 12 | vlan 200 13 | vlan 300 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter05/chapter5_4.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Echo Loop Items 3 | hosts: "192.168.199.185" 4 | gather_facts: false 5 | 6 | tasks: 7 | - name: echo loop items 8 | command: echo {{ item }} 9 | with_items: ['r1', 'r2', 'r3', 'r4', 'r5'] 10 | 11 | -------------------------------------------------------------------------------- /Chapter05/Includes/nx-osv-2.conf: -------------------------------------------------------------------------------- 1 | hostname nx-osv-2 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | 9 | username cisco password cisco role network-operator 10 | 11 | vlan 100 12 | vlan 200 13 | vlan 300 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter05/Templates/nx-osv-2.conf: -------------------------------------------------------------------------------- 1 | hostname nx-osv-2 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | 9 | username cisco password cisco role network-operator 10 | 11 | vlan 100 12 | vlan 200 13 | vlan 300 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter05/Group_Host_Vars/nx-osv-2.conf: -------------------------------------------------------------------------------- 1 | hostname nx-osv-2 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | 9 | username cisco password cisco role network-operator 10 | 11 | vlan 100 12 | vlan 200 13 | vlan 300 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter09/requirements.txt: -------------------------------------------------------------------------------- 1 | Flask==0.10.1 2 | Flask-HTTPAuth==2.2.1 3 | Flask-SQLAlchemy==1.0 4 | Jinja2==2.7.3 5 | MarkupSafe==0.23 6 | Pygments==1.6 7 | SQLAlchemy==0.9.6 8 | Werkzeug==0.9.6 9 | httpie==0.8.0 10 | itsdangerous==0.24 11 | python-dateutil==2.2 12 | requests==2.3.0 13 | -------------------------------------------------------------------------------- /Chapter05/Vaults/chapter5_10.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ansible Group and Host Varibles 3 | hosts: localhost 4 | 5 | tasks: 6 | - name: create router configuration files 7 | template: 8 | src=./nxos.j2 9 | dest=./{{ item.key }}.conf 10 | with_dict: "{{ nexus_devices }}" 11 | -------------------------------------------------------------------------------- /Chapter05/Group_Host_Vars/chapter5_9.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ansible Group and Host Varibles 3 | hosts: localhost 4 | 5 | tasks: 6 | - name: create router configuration files 7 | template: 8 | src=./nxos.j2 9 | dest=./{{ item.key }}.conf 10 | with_dict: "{{ nexus_devices }}" 11 | -------------------------------------------------------------------------------- /Chapter05/Custom_Modules/chapter5_14.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Your First Custom Module 3 | hosts: localhost 4 | gather_facts: false 5 | connection: local 6 | 7 | tasks: 8 | - name: Show Version 9 | action: custom_module_1 10 | register: output 11 | 12 | - debug: 13 | var: output 14 | -------------------------------------------------------------------------------- /Chapter08/hosts: -------------------------------------------------------------------------------- 1 | [devices] 2 | r1 ansible_hostname=172.16.1.218 3 | r2 ansible_hostname=172.16.1.219 4 | r3 ansible_hostname=172.16.1.220 5 | r5-tor ansible_hostname=172.16.1.221 6 | r6-edge ansible_hostname=172.16.1.222 7 | 8 | [edge-devices] 9 | r5-tor ansible_hostname=172.16.1.221 10 | r6-edge ansible_hostname=172.16.1.222 11 | -------------------------------------------------------------------------------- /Chapter09/chapter9_2.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/') 5 | def index(): 6 | return 'You are at index()' 7 | 8 | @app.route('/routers/') 9 | def routers(): 10 | return 'You are at routers()' 11 | 12 | if __name__ == '__main__': 13 | app.run(host='0.0.0.0', debug=True) 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter07/pygal_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pygal 4 | 5 | line_chart = pygal.Pie() 6 | line_chart.title = "Protocol Breakdown" 7 | line_chart.add('TCP', 15) 8 | line_chart.add('UDP', 30) 9 | line_chart.add('ICMP', 45) 10 | line_chart.add('Others', 10) 11 | line_chart.render_to_file('pygal_example_3.svg') 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Chapter08/output/chapter8_lldp_graph.gv: -------------------------------------------------------------------------------- 1 | digraph My_Network { 2 | {rank=same Client "r6-edge"} 3 | {rank=same r1 r2 r3} 4 | 5 | Client -> "r6-edge" 6 | "r5-tor" -> Server 7 | r1 -> "r5-tor" 8 | "r5-tor" -> r2 9 | "r5-tor" -> r3 10 | "r5-tor" -> r1 11 | r2 -> "r5-tor" 12 | r3 -> "r5-tor" 13 | r3 -> "r6-edge" 14 | "r6-edge" -> r3 15 | } -------------------------------------------------------------------------------- /Chapter09/chapter9_5.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, jsonify 2 | app = Flask(__name__) 3 | 4 | @app.route('/routers//interface/') 5 | def interface(hostname, interface_number): 6 | return jsonify(name=hostname, interface=interface_number) 7 | 8 | if __name__ == '__main__': 9 | app.run(host='0.0.0.0', debug=True) 10 | 11 | 12 | -------------------------------------------------------------------------------- /Chapter05/Custom_Modules/chapter5_15.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Your First Custom Module 3 | hosts: localhost 4 | gather_facts: false 5 | connection: local 6 | 7 | tasks: 8 | - name: Show Version 9 | action: custom_module_1 host="172.16.1.142" username="cisco" password="cisco" 10 | register: output 11 | 12 | - debug: 13 | var: output 14 | -------------------------------------------------------------------------------- /Chapter05/Includes/chapter5_11_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Ansible Group and Host Varibles 3 | hosts: localhost 4 | 5 | tasks: 6 | - name: create router configuration files 7 | template: 8 | src=./nxos.j2 9 | dest=./{{ item.key }}.conf 10 | with_dict: "{{ nexus_devices }}" 11 | register: output 12 | 13 | - include: show_output.yml 14 | -------------------------------------------------------------------------------- /Chapter05/Vaults/secret.yml: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 33656462646237396232663532636132363932363535363064666565643035326138373762353132 3 | 6630336634396166613532666436643533653435626237660a346566666663363833333833386230 4 | 31623466363864653837336637616439373438373439313865386430626361393836653435313034 5 | 3734313936623533350a653537333837383863636530356464623032333432386139303335663262 6 | 3962 7 | -------------------------------------------------------------------------------- /Chapter03/Arista/eapi_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | from __future__ import print_function 4 | from jsonrpclib import Server 5 | import ssl 6 | 7 | ssl._create_default_https_context = ssl._create_unverified_context 8 | 9 | switch = Server("https://admin:arista@192.168.199.158/command-api") 10 | 11 | response = switch.runCmds( 1, [ "show version" ] ) 12 | print('Serial Number: ' + response[0]['serialNumber']) 13 | -------------------------------------------------------------------------------- /Chapter05/Includes/nx-osv-1.conf: -------------------------------------------------------------------------------- 1 | hostname nx-osv-1 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | feature netflow 9 | 10 | username cisco password cisco role network-operator 11 | 12 | vlan 100 13 | vlan 200 14 | vlan 300 15 | 16 | interface 100 17 | ip address 192.168.10.1/24 18 | interface 200 19 | ip address 192.168.20.1/24 20 | interface 300 21 | ip address 192.168.30.1/24 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter05/Templates/nx-osv-1.conf: -------------------------------------------------------------------------------- 1 | hostname nx-osv-1 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | feature netflow 9 | 10 | username cisco password cisco role network-operator 11 | 12 | vlan 100 13 | vlan 200 14 | vlan 300 15 | 16 | interface 100 17 | ip address 192.168.10.1/24 18 | interface 200 19 | ip address 192.168.20.1/24 20 | interface 300 21 | ip address 192.168.30.1/24 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter05/Vaults/nx-osv-1.conf: -------------------------------------------------------------------------------- 1 | hostname nx-osv-1 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | feature netflow 9 | 10 | username cisco password cisco role network-operator 11 | 12 | vlan 100 13 | vlan 200 14 | vlan 300 15 | 16 | interface 100 17 | ip address 192.168.10.1/24 18 | interface 200 19 | ip address 192.168.20.1/24 20 | interface 300 21 | ip address 192.168.30.1/24 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter05/Group_Host_Vars/nx-osv-1.conf: -------------------------------------------------------------------------------- 1 | hostname nx-osv-1 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | feature netflow 9 | 10 | username cisco password cisco role network-operator 11 | 12 | vlan 100 13 | vlan 200 14 | vlan 300 15 | 16 | interface 100 17 | ip address 192.168.10.1/24 18 | interface 200 19 | ip address 192.168.20.1/24 20 | interface 300 21 | ip address 192.168.30.1/24 22 | 23 | 24 | -------------------------------------------------------------------------------- /Chapter05/hosts: -------------------------------------------------------------------------------- 1 | [all:vars] 2 | username=cisco 3 | password=cisco 4 | 5 | [servers] 6 | 192.168.199.185 7 | 8 | [iosv-devices] 9 | ios-r1 ansible_host=172.16.1.134 10 | ios-r2 ansible_host=172.16.1.135 11 | 12 | [nxosv-devices] 13 | nxos-r1 ansible_host=172.16.1.142 14 | nxos-r2 ansible_host=172.16.1.143 15 | 16 | [eos-devices] 17 | arista1 ansible_host=192.168.199.158 18 | 19 | [eos-devices:vars] 20 | username=arista 21 | password=arista 22 | 23 | -------------------------------------------------------------------------------- /Chapter06/python_re_search_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re, datetime 4 | 5 | startTime = datetime.datetime.now() 6 | 7 | with open('sample_log_anonymized.log', 'r') as f: 8 | for line in f.readlines(): 9 | if re.search('ACLLOG-5-ACLLOG_FLOW_INTERVAL', line): 10 | print(line) 11 | 12 | endTime = datetime.datetime.now() 13 | elapsedTime = endTime - startTime 14 | print("Time Elapsed: " + str(elapsedTime)) 15 | 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /Chapter09/chapter9_3.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | app = Flask(__name__) 3 | 4 | @app.route('/routers/') 5 | def router(hostname): 6 | return 'You are at %s' % hostname 7 | 8 | @app.route('/routers//interface/') 9 | def interface(hostname, interface_number): 10 | return 'You are at %s interface %d' % (hostname, interface_number) 11 | 12 | if __name__ == '__main__': 13 | app.run(host='0.0.0.0', debug=True) 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter03/Cisco/cisco_nxapi_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from ncclient import manager 4 | 5 | conn = manager.connect( 6 | host='172.16.1.90', 7 | port=22, 8 | username='cisco', 9 | password='cisco', 10 | hostkey_verify=False, 11 | device_params={'name': 'nexus'}, 12 | look_for_keys=False) 13 | 14 | for value in conn.server_capabilities: 15 | print(value) 16 | 17 | conn.close_session() 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Chapter03/Juniper/junos_pyez_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from jnpr.junos import Device 3 | import xml.etree.ElementTree as ET 4 | import pprint 5 | 6 | dev = Device(host='192.168.24.252', user='juniper', passwd='juniper!') 7 | 8 | try: 9 | dev.open() 10 | except Exception as err: 11 | print(err) 12 | sys.exit(1) 13 | 14 | result = dev.rpc.get_interface_information(interface_name='em1', terse=True) 15 | pprint.pprint(ET.tostring(result)) 16 | 17 | dev.close() 18 | 19 | -------------------------------------------------------------------------------- /Chapter03/Juniper/junos_netconf_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from ncclient import manager 4 | 5 | conn = manager.connect( 6 | host='192.168.24.252', 7 | port='830', 8 | username='netconf', 9 | password='juniper!', 10 | timeout=10, 11 | device_params={'name':'junos'}, 12 | hostkey_verify=False) 13 | 14 | result = conn.command('show version', format='text') 15 | print(result.xpath('output')[0].text) 16 | conn.close_session() 17 | 18 | 19 | -------------------------------------------------------------------------------- /Chapter05/Vaults/group_vars/all: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 63316164353534613166346234343235306234363438323432383036666561643664363165333730 3 | 3633353137613566366630313535353038386163396563630a663865393033626237303963323031 4 | 62363566333666326364663661303433646236326131383635396361303738393435636131653839 5 | 3461366334353637620a303634626235343031643339373034666436343566376233643461393564 6 | 37633030306336623035626266643934373334306230383564663030656164343563373330373161 7 | 3430616163333234346235383336393735316461623563363866 8 | -------------------------------------------------------------------------------- /Chapter08/chapter8_ntop_1.py: -------------------------------------------------------------------------------- 1 | # Import modules for CGI handling 2 | import cgi, cgitb 3 | import ntop 4 | 5 | # Parse URL 6 | cgitb.enable(); 7 | 8 | form = cgi.FieldStorage(); 9 | name = form.getvalue('Name', default="Eric") 10 | 11 | version = ntop.version() 12 | os = ntop.os() 13 | uptime = ntop.uptime() 14 | 15 | ntop.printHTMLHeader('Mastering Pyton Networking', 1, 0) 16 | ntop.sendString("Hello, "+ name +"
") 17 | ntop.sendString("Ntop Information: %s %s %s" % (version, os, uptime)) 18 | ntop.printHTMLFooter() 19 | 20 | -------------------------------------------------------------------------------- /Chapter08/cisco_config_lldp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable LLDP 3 | hosts: "devices" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_hostname }}" 10 | username: cisco 11 | password: cisco 12 | transport: cli 13 | 14 | tasks: 15 | - name: enable LLDP run 16 | ios_config: 17 | lines: lldp run 18 | provider: "{{ cli }}" 19 | 20 | register: output 21 | 22 | - name: show output 23 | debug: 24 | var: output 25 | 26 | -------------------------------------------------------------------------------- /Chapter09/chapter9_4.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, url_for 2 | 3 | app = Flask(__name__) 4 | 5 | @app.route('//list_interfaces') 6 | def device(hostname): 7 | if hostname in routers: 8 | return 'Listing interfaces for %s' % hostname 9 | else: 10 | return 'Invalid hostname' 11 | 12 | routers = ['r1', 'r2', 'r3'] 13 | for router in routers: 14 | with app.test_request_context(): 15 | print(url_for('device', hostname=router)) 16 | 17 | if __name__ == '__main__': 18 | app.run(host='0.0.0.0', debug=True) 19 | 20 | 21 | -------------------------------------------------------------------------------- /Chapter05/chapter5_5.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add Multiple Vlans 3 | hosts: "nxos-r1" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | transport: cli 13 | vlan_numbers: [100, 200, 300] 14 | 15 | tasks: 16 | - name: add vlans 17 | eos_config: 18 | lines: 19 | - vlan {{ item }} 20 | provider: "{{ cli }}" 21 | with_items: "{{ vlan_numbers }}" 22 | register: output 23 | 24 | -------------------------------------------------------------------------------- /Chapter03/Cisco/cisco_nxapi_3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import requests 4 | import json 5 | 6 | url='http://172.16.1.90/ins' 7 | switchuser='cisco' 8 | switchpassword='cisco' 9 | 10 | myheaders={'content-type':'application/json-rpc'} 11 | payload=[ 12 | { 13 | "jsonrpc": "2.0", 14 | "method": "cli", 15 | "params": { 16 | "cmd": "hostname nx-osv-1", 17 | "version": 1.2 18 | }, 19 | "id": 1 20 | } 21 | ] 22 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json() 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter04/cisco_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure SNMP Contact 3 | hosts: "nexus" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ inventory_hostname }}" 10 | username: cisco 11 | password: cisco 12 | transport: cli 13 | 14 | tasks: 15 | - name: configure snmp contact 16 | nxos_snmp_contact: 17 | contact: TEST_1 18 | state: present 19 | provider: "{{ cli }}" 20 | 21 | register: output 22 | 23 | - name: show output 24 | debug: 25 | var: output 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter04/juniper_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Get Juniper Device Facts 3 | hosts: "junos_devices" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | netconf: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | port: 830 13 | transport: netconf 14 | 15 | tasks: 16 | - name: collect default set of facts 17 | junos_facts: 18 | provider: "{{ netconf }}" 19 | 20 | register: output 21 | 22 | - name: show output 23 | debug: 24 | var: output 25 | 26 | -------------------------------------------------------------------------------- /Chapter03/Juniper/junos_pyez_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from jnpr.junos import Device 3 | from jnpr.junos.utils.config import Config 4 | 5 | dev = Device(host='192.168.24.252', user='juniper', passwd='juniper!') 6 | 7 | try: 8 | dev.open() 9 | except Exception as err: 10 | print(err) 11 | sys.exit(1) 12 | 13 | config_change = """ 14 | 15 | master 16 | python 17 | 18 | """ 19 | 20 | cu = Config(dev) 21 | cu.lock() 22 | cu.load(config_change) 23 | cu.commit() 24 | cu.unlock() 25 | 26 | dev.close() 27 | 28 | -------------------------------------------------------------------------------- /Chapter08/chapter8_ntop_2.py: -------------------------------------------------------------------------------- 1 | import ntop, interface, json 2 | 3 | ifnames = [] 4 | try: 5 | for i in range(interface.numInterfaces()): 6 | ifnames.append(interface.name(i)) 7 | 8 | except Exception as inst: 9 | print type(inst) # the exception instance 10 | print inst.args # arguments stored in .args 11 | print inst # __str__ allows args to printed directly 12 | 13 | ntop.printHTMLHeader('Mastering Python Networking', 1, 0) 14 | ntop.sendString("Here are my interfaces:
") 15 | ntop.sendString(json.dumps(ifnames, sort_keys=True, indent=4)) 16 | ntop.printHTMLFooter() 17 | 18 | -------------------------------------------------------------------------------- /Chapter11/Chapter11_6.py: -------------------------------------------------------------------------------- 1 | BGP = { 2 | 'local_as': 65000, 3 | 'router_id': '172.16.1.174', 4 | 'neighbors': [ 5 | { 6 | 'address': '172.16.1.175', 7 | 'remote_as': 1, 8 | 'enable_ipv4': True, 9 | 'enable_ipv6': True, 10 | 'enable_vpnv4': True, 11 | 'enable_vpnv6': True 12 | }, 13 | ] 14 | } 15 | 16 | SSH = { 17 | 'ssh_port': 4990, 18 | 'ssh_host': 'localhost', 19 | # 'ssh_host_key': '/etc/ssh_host_rsa_key', 20 | # 'ssh_username': 'ryu', 21 | # 'ssh_password': 'ryu', 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Chapter04/arista_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: EOS Show Commands 3 | hosts: "eos_devices" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "arista" 11 | password: "arista" 12 | authorize: true 13 | transport: cli 14 | 15 | tasks: 16 | - name: eos show commands 17 | eos_command: 18 | commands: 19 | - show version | i Arista 20 | provider: "{{ cli }}" 21 | 22 | register: output 23 | 24 | - name: show output 25 | debug: 26 | var: output 27 | 28 | -------------------------------------------------------------------------------- /Chapter04/cisco_2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure SNMP Contact 3 | hosts: "nexus_by_name" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | transport: cli 13 | 14 | tasks: 15 | - name: configure snmp contact 16 | nxos_snmp_contact: 17 | contact: TEST_1 18 | state: present 19 | provider: "{{ cli }}" 20 | 21 | register: output 22 | 23 | - name: show output 24 | debug: 25 | var: output 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter03/Cisco/cisco_nxapi_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import requests 4 | import json 5 | 6 | url='http://172.16.1.90/ins' 7 | switchuser='cisco' 8 | switchpassword='cisco' 9 | 10 | myheaders={'content-type':'application/json-rpc'} 11 | payload=[ 12 | { 13 | "jsonrpc": "2.0", 14 | "method": "cli", 15 | "params": { 16 | "cmd": "show version", 17 | "version": 1.2 18 | }, 19 | "id": 1 20 | } 21 | ] 22 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json() 23 | 24 | print(response['result']['body']['sys_ver_str']) 25 | 26 | 27 | -------------------------------------------------------------------------------- /Chapter03/Cisco/cisco_yang_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from ncclient import manager 4 | import xml.dom.minidom 5 | 6 | host = "ios-xe-mgmt.cisco.com" 7 | username = "root" 8 | password = "C!sc0123" 9 | port = 10000 10 | 11 | yang_file = "cisco_yang_1_interfaces.xml" 12 | 13 | conn = manager.connect(host=host, port=port, username=username, password=password, hostkey_verify=False, device_params={'name': 'default'}, allow_agent=False, look_for_keys=False) 14 | 15 | with open(yang_file) as f: 16 | output = (conn.get_config('running', f.read())) 17 | 18 | print(xml.dom.minidom.parseString(output.xml).toprettyxml()) 19 | 20 | 21 | -------------------------------------------------------------------------------- /Chapter08/tmp/r1_lldp_output.txt: -------------------------------------------------------------------------------- 1 | [["Capability codes:", " (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device", " (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other", "", "Device ID Local Intf Hold-time Capability Port ID", "r2.virl.info Gi0/0 120 R Gi0/0", "r3.virl.info Gi0/0 120 R Gi0/0", "r5-tor.virl.info Gi0/0 120 R Gi0/0", "r5-tor.virl.info Gi0/1 120 R Gi0/1", "r6-edge.virl.info Gi0/0 120 R Gi0/0", "", "Total entries displayed: 5", ""]] -------------------------------------------------------------------------------- /Chapter08/tmp/r2_lldp_output.txt: -------------------------------------------------------------------------------- 1 | [["Capability codes:", " (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device", " (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other", "", "Device ID Local Intf Hold-time Capability Port ID", "r3.virl.info Gi0/0 120 R Gi0/0", "r5-tor.virl.info Gi0/1 120 R Gi0/2", "r5-tor.virl.info Gi0/0 120 R Gi0/0", "r6-edge.virl.info Gi0/0 120 R Gi0/0", "r1.virl.info Gi0/0 120 R Gi0/0", "", "Total entries displayed: 5", ""]] -------------------------------------------------------------------------------- /Chapter08/tmp/r6-edge_lldp_output.txt: -------------------------------------------------------------------------------- 1 | [["Capability codes:", " (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device", " (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other", "", "Device ID Local Intf Hold-time Capability Port ID", "r2.virl.info Gi0/0 120 R Gi0/0", "r3.virl.info Gi0/3 120 R Gi0/2", "r3.virl.info Gi0/0 120 R Gi0/0", "r5-tor.virl.info Gi0/0 120 R Gi0/0", "r1.virl.info Gi0/0 120 R Gi0/0", "", "Total entries displayed: 5", ""]] -------------------------------------------------------------------------------- /Chapter13/chapter13_mininet_4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from mininet.net import Mininet 4 | from mininet.node import Controller, OVSKernelSwitch, RemoteController 5 | from mininet.log import setLogLevel 6 | from mininet.cli import CLI 7 | 8 | setLogLevel( 'info' ) 9 | 10 | net = Mininet( controller=RemoteController ) 11 | c1 = net.addController('c0') 12 | 13 | h1 = net.addHost('h1', ip='10.20.1.10/24') 14 | h2 = net.addHost('h2', ip='10.20.1.11/24') 15 | s1 = net.addSwitch('s1') 16 | 17 | s1.linkTo(h1) 18 | s1.linkTo(h2) 19 | 20 | net.start() 21 | s1.cmd('ovs-vsctl set-controller s1 ssl:127.0.0.1:6633') 22 | 23 | CLI( net ) 24 | net.stop() 25 | 26 | -------------------------------------------------------------------------------- /Chapter05/Includes/nxos.j2: -------------------------------------------------------------------------------- 1 | hostname {{ item.value.hostname }} 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | {% if item.value.netflow_enable %} 9 | feature netflow 10 | {% endif %} 11 | 12 | username {{ item.value.username }} password {{ item.value.password }} role network-operator 13 | 14 | {% for vlan_num in item.value.vlans %} 15 | vlan {{ vlan_num }} 16 | {% endfor %} 17 | 18 | {% if item.value.l3_vlan_interfaces %} 19 | {% for vlan_interface in item.value.vlan_interfaces %} 20 | interface {{ vlan_interface.int_num }} 21 | ip address {{ vlan_interface.ip }}/24 22 | {% endfor %} 23 | {% endif %} 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter05/Vaults/nxos.j2: -------------------------------------------------------------------------------- 1 | hostname {{ item.value.hostname }} 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | {% if item.value.netflow_enable %} 9 | feature netflow 10 | {% endif %} 11 | 12 | username {{ item.value.username }} password {{ item.value.password }} role network-operator 13 | 14 | {% for vlan_num in item.value.vlans %} 15 | vlan {{ vlan_num }} 16 | {% endfor %} 17 | 18 | {% if item.value.l3_vlan_interfaces %} 19 | {% for vlan_interface in item.value.vlan_interfaces %} 20 | interface {{ vlan_interface.int_num }} 21 | ip address {{ vlan_interface.ip }}/24 22 | {% endfor %} 23 | {% endif %} 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter05/chapter5_1.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: IOS Command Output 3 | hosts: "iosv-devices" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | transport: cli 13 | 14 | tasks: 15 | - name: show hostname 16 | ios_command: 17 | commands: 18 | - show run | i hostname 19 | provider: "{{ cli }}" 20 | 21 | register: output 22 | 23 | - name: show output 24 | when: '"iosv-2" in "{{ output.stdout }}"' 25 | debug: 26 | msg: '{{ output }}' 27 | 28 | -------------------------------------------------------------------------------- /Chapter04/cisco_5.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: IOS Show Commands 3 | hosts: "ios_devices" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | transport: cli 13 | 14 | tasks: 15 | - name: ios show commands 16 | ios_command: 17 | commands: 18 | - show version | i IOS 19 | - show run | i hostname 20 | provider: "{{ cli }}" 21 | 22 | register: output 23 | 24 | - name: show output in output["end_state"]["contact"] 25 | debug: 26 | var: output 27 | 28 | -------------------------------------------------------------------------------- /Chapter05/Templates/nxos.j2: -------------------------------------------------------------------------------- 1 | hostname {{ item.value.hostname }} 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | {% if item.value.netflow_enable %} 9 | feature netflow 10 | {% endif %} 11 | 12 | username {{ item.value.username }} password {{ item.value.password }} role network-operator 13 | 14 | {% for vlan_num in item.value.vlans %} 15 | vlan {{ vlan_num }} 16 | {% endfor %} 17 | 18 | {% if item.value.l3_vlan_interfaces %} 19 | {% for vlan_interface in item.value.vlan_interfaces %} 20 | interface {{ vlan_interface.int_num }} 21 | ip address {{ vlan_interface.ip }}/24 22 | {% endfor %} 23 | {% endif %} 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter06/python_re_search_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re, datetime 4 | 5 | startTime = datetime.datetime.now() 6 | 7 | term1 = re.compile('ACLLOG-5-ACLLOG_FLOW_INTERVAL') 8 | term2 = re.compile('PAM: Authentication failure') 9 | 10 | fileList = ['sample_log_anonymized.log', 'sample_log_anonymized_1.log'] 11 | 12 | for log in fileList: 13 | with open(log, 'r') as f: 14 | for line in f.readlines(): 15 | if re.search(term1, line) or re.search(term2, line): 16 | print(line) 17 | 18 | endTime = datetime.datetime.now() 19 | elapsedTime = endTime - startTime 20 | print("Time Elapsed: " + str(elapsedTime)) 21 | 22 | -------------------------------------------------------------------------------- /Chapter05/Group_Host_Vars/nxos.j2: -------------------------------------------------------------------------------- 1 | hostname {{ item.value.hostname }} 2 | 3 | feature telnet 4 | feature ospf 5 | feature bgp 6 | feature interface-vlan 7 | 8 | {% if item.value.netflow_enable %} 9 | feature netflow 10 | {% endif %} 11 | 12 | username {{ item.value.username }} password {{ item.value.password }} role network-operator 13 | 14 | {% for vlan_num in item.value.vlans %} 15 | vlan {{ vlan_num }} 16 | {% endfor %} 17 | 18 | {% if item.value.l3_vlan_interfaces %} 19 | {% for vlan_interface in item.value.vlan_interfaces %} 20 | interface {{ vlan_interface.int_num }} 21 | ip address {{ vlan_interface.ip }}/24 22 | {% endfor %} 23 | {% endif %} 24 | 25 | 26 | -------------------------------------------------------------------------------- /Chapter03/Arista/pyeapi_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pyeapi 4 | 5 | class my_switch(): 6 | 7 | def __init__(self, config_file_location, device): 8 | # loads the config file 9 | pyeapi.client.load_config(config_file_location) 10 | self.node = pyeapi.connect_to(device) 11 | self.hostname = self.node.enable('show hostname')[0]['result']['hostname'] 12 | self.running_config = self.node.enable('show running-config') 13 | 14 | def create_vlan(self, vlan_number, vlan_name): 15 | vlans = self.node.api('vlans') 16 | vlans.create(vlan_number) 17 | vlans.set_name(vlan_number, vlan_name) 18 | 19 | -------------------------------------------------------------------------------- /Chapter05/Custom_Modules/library/custom_module_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import requests 4 | import json 5 | 6 | url='http://172.16.1.142/ins' 7 | switchuser='cisco' 8 | switchpassword='cisco' 9 | 10 | myheaders={'content-type':'application/json-rpc'} 11 | payload=[ 12 | { 13 | "jsonrpc": "2.0", 14 | "method": "cli", 15 | "params": { 16 | "cmd": "show version", 17 | "version": 1.2 18 | }, 19 | "id": 1 20 | } 21 | ] 22 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json() 23 | 24 | version = response['result']['body']['sys_ver_str'] 25 | 26 | print json.dumps({"version": version}) 27 | -------------------------------------------------------------------------------- /Chapter09/chapter9_request_1.py: -------------------------------------------------------------------------------- 1 | import requests, time 2 | 3 | server = 'http://172.16.1.173:5000' 4 | endpoint = '/devices/1/version' 5 | 6 | # First request to get the new resource 7 | r = requests.get(server+endpoint) 8 | resource = r.headers['location'] 9 | print("Status: {} Resource: {}".format(r.status_code, resource)) 10 | 11 | # Second request to get the resource status 12 | r = requests.get(resource) 13 | print("Immediate Status Query to Resource: " + str(r.status_code)) 14 | 15 | print("Sleep for 2 seconds") 16 | time.sleep(2) 17 | # Third request to get the resource status 18 | r = requests.get(resource) 19 | print("Status after 2 seconds: " + str(r.status_code)) 20 | 21 | 22 | -------------------------------------------------------------------------------- /Chapter07/matplotlib_3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # Example from http://matplotlib.org/2.0.0/examples/pie_and_polar_charts/pie_demo_features.html 4 | 5 | import matplotlib.pyplot as plt 6 | 7 | # Pie chart, where the slices will be ordered and plotted counter-clockwise: 8 | labels = 'TCP', 'UDP', 'ICMP', 'Others' 9 | sizes = [15, 30, 45, 10] 10 | explode = (0, 0.1, 0, 0) # Make UDP stand out 11 | 12 | fig1, ax1 = plt.subplots() 13 | ax1.pie(sizes, explode=explode, labels=labels, autopct='%1.1f%%', 14 | shadow=True, startangle=90) 15 | ax1.axis('equal') # Equal aspect ratio ensures that pie is drawn as a circle. 16 | 17 | plt.savefig('matplotlib_3_result.png') 18 | plt.show() 19 | 20 | -------------------------------------------------------------------------------- /Chapter08/tmp/r3_lldp_output.txt: -------------------------------------------------------------------------------- 1 | [["Capability codes:", " (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device", " (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other", "", "Device ID Local Intf Hold-time Capability Port ID", "r2.virl.info Gi0/0 120 R Gi0/0", "r5-tor.virl.info Gi0/1 120 R Gi0/3", "r5-tor.virl.info Gi0/0 120 R Gi0/0", "r6-edge.virl.info Gi0/0 120 R Gi0/0", "r6-edge.virl.info Gi0/2 120 R Gi0/3", "r1.virl.info Gi0/0 120 R Gi0/0", "", "Total entries displayed: 6", ""]] -------------------------------------------------------------------------------- /Chapter03/Arista/eapi_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python2 2 | 3 | from __future__ import print_function 4 | from jsonrpclib import Server 5 | import ssl, pprint 6 | 7 | ssl._create_default_https_context = ssl._create_unverified_context 8 | 9 | # Run Arista commands thru eAPI 10 | def runAristaCommands(switch_object, list_of_commands): 11 | response = switch_object.runCmds(1, list_of_commands) 12 | return response 13 | 14 | 15 | switch = Server("https://admin:arista@192.168.199.158/command-api") 16 | 17 | commands = ["enable", "configure", "interface ethernet 1/3", "switchport access vlan 100", "end", "write memory"] 18 | 19 | response = runAristaCommands(switch, commands) 20 | pprint.pprint(response) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Chapter05/chapter5_2.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: IOS Command Output 3 | hosts: "iosv-devices" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | transport: cli 13 | 14 | tasks: 15 | - name: show hostname 16 | ios_command: 17 | commands: 18 | - show run | i hostname 19 | provider: "{{ cli }}" 20 | 21 | register: output 22 | 23 | - name: config example 24 | when: '"iosv-2" in "{{ output.stdout }}"' 25 | ios_config: 26 | lines: 27 | - logging buffered 30000 28 | provider: "{{ cli }}" 29 | -------------------------------------------------------------------------------- /Chapter05/Includes/host_vars/localhost: -------------------------------------------------------------------------------- 1 | --- 2 | "nexus_devices": 3 | "nx-osv-1": 4 | "hostname": "nx-osv-1" 5 | "username": "{{ username }}" 6 | "password": "{{ password }}" 7 | "vlans": [100, 200, 300] 8 | "l3_vlan_interfaces": True 9 | "vlan_interfaces": [ 10 | {"int_num": "100", "ip": "192.168.10.1"}, 11 | {"int_num": "200", "ip": "192.168.20.1"}, 12 | {"int_num": "300", "ip": "192.168.30.1"} 13 | ] 14 | "netflow_enable": True 15 | 16 | "nx-osv-2": 17 | "hostname": "nx-osv-2" 18 | "username": "{{ username }}" 19 | "password": "{{ password }}" 20 | "vlans": [100, 200, 300] 21 | "l3_vlan_interfaces": False 22 | "netflow_enable": False 23 | 24 | -------------------------------------------------------------------------------- /Chapter05/Group_Host_Vars/host_vars/localhost: -------------------------------------------------------------------------------- 1 | --- 2 | "nexus_devices": 3 | "nx-osv-1": 4 | "hostname": "nx-osv-1" 5 | "username": "{{ username }}" 6 | "password": "{{ password }}" 7 | "vlans": [100, 200, 300] 8 | "l3_vlan_interfaces": True 9 | "vlan_interfaces": [ 10 | {"int_num": "100", "ip": "192.168.10.1"}, 11 | {"int_num": "200", "ip": "192.168.20.1"}, 12 | {"int_num": "300", "ip": "192.168.30.1"} 13 | ] 14 | "netflow_enable": True 15 | 16 | "nx-osv-2": 17 | "hostname": "nx-osv-2" 18 | "username": "{{ username }}" 19 | "password": "{{ password }}" 20 | "vlans": [100, 200, 300] 21 | "l3_vlan_interfaces": False 22 | "netflow_enable": False 23 | 24 | -------------------------------------------------------------------------------- /Chapter05/chapter5_3.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: EOS Command Output 3 | hosts: "arista1" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | transport: cli 13 | 14 | tasks: 15 | - name: "sh int ethernet 1/3 | json" 16 | eos_command: 17 | commands: 18 | - "show interface ethernet 1/3 | json" 19 | provider: "{{ cli }}" 20 | waitfor: 21 | - "result[0].interfaces.Ethernet1/3.interfaceStatus eq disabled" 22 | 23 | register: output 24 | - name: show output 25 | debug: 26 | msg: "Interface Disabled, Safe to Proceed" 27 | 28 | -------------------------------------------------------------------------------- /Chapter07/cacti_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import pexpect 4 | 5 | devices = {'iosv-1': {'prompt': 'iosv-1#', 'ip': '172.16.1.189'}} 6 | username = 'cisco' 7 | password = 'cisco' 8 | 9 | for device in devices.keys(): 10 | device_prompt = devices[device]['prompt'] 11 | child = pexpect.spawn('telnet ' + devices[device]['ip']) 12 | child.expect('Username:') 13 | child.sendline(username) 14 | child.expect('Password:') 15 | child.sendline(password) 16 | child.expect(device_prompt) 17 | child.sendline('sh ip access-lists permit_snmp | i 172.16.1.173') 18 | child.expect(device_prompt) 19 | output = child.before 20 | child.sendline('exit') 21 | 22 | print(str(output).split('(')[1].split()[0].strip()) 23 | 24 | -------------------------------------------------------------------------------- /Chapter13/chapter13_mininet_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from mininet.net import Mininet 4 | from mininet.node import Controller, OVSKernelSwitch, RemoteController 5 | from mininet.log import setLogLevel 6 | from mininet.cli import CLI 7 | 8 | setLogLevel( 'info' ) 9 | 10 | net = Mininet( controller=RemoteController, switch=OVSKernelSwitch ) 11 | c1 = net.addController('c2', controller=RemoteController, ip='127.0.0.1', port=5555) 12 | 13 | h1 = net.addHost('h1', ip='10.20.1.10/24') 14 | h2 = net.addHost('h2', ip='10.20.1.11/24') 15 | s1 = net.addSwitch('s1') 16 | 17 | s1.linkTo(h1) 18 | s1.linkTo(h2) 19 | 20 | net.build() 21 | 22 | c1.start() 23 | s1.start([c1]) 24 | 25 | net.start() 26 | net.staticArp() 27 | CLI( net ) 28 | net.stop() 29 | 30 | -------------------------------------------------------------------------------- /Chapter13/chapter13_mininet_3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from mininet.net import Mininet 4 | from mininet.node import Controller, OVSKernelSwitch, RemoteController 5 | from mininet.log import setLogLevel 6 | from mininet.cli import CLI 7 | 8 | setLogLevel( 'info' ) 9 | 10 | net = Mininet( controller=RemoteController, switch=OVSKernelSwitch ) 11 | c1 = net.addController('c2', controller=RemoteController, 12 | ip='127.0.0.1', port=6633) 13 | 14 | h1 = net.addHost('h1', ip='10.20.1.10/24') 15 | h2 = net.addHost('h2', ip='10.20.1.11/24') 16 | s1 = net.addSwitch('s1') 17 | 18 | s1.linkTo(h1) 19 | s1.linkTo(h2) 20 | 21 | net.build() 22 | 23 | c1.start() 24 | s1.start([c1]) 25 | 26 | net.start() 27 | net.staticArp() 28 | CLI( net ) 29 | net.stop() 30 | 31 | -------------------------------------------------------------------------------- /Chapter07/cacti_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import pexpect, sys 4 | 5 | devices = {'iosv-1': {'prompt': 'iosv-1#', 'ip': '172.16.1.189'}} 6 | username = 'cisco' 7 | password = 'cisco' 8 | 9 | for device in devices.keys(): 10 | device_prompt = devices[device]['prompt'] 11 | child = pexpect.spawn('telnet ' + devices[device]['ip']) 12 | child.expect('Username:') 13 | child.sendline(username) 14 | child.expect('Password:') 15 | child.sendline(password) 16 | child.expect(device_prompt) 17 | child.sendline('sh ip access-lists permit_snmp | i 172.16.1.173') 18 | child.expect(device_prompt) 19 | output = child.before 20 | child.sendline('exit') 21 | 22 | sys.stdout.write(str(output).split('(')[1].split()[0].strip()) 23 | 24 | -------------------------------------------------------------------------------- /Chapter02/chapter2_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import pexpect 4 | 5 | devices = {'iosv-1': {'prompt': 'iosv-1#', 'ip': '172.16.1.20'}, 6 | 'iosv-2': {'prompt': 'iosv-2#', 'ip': '172.16.1.21'}} 7 | username = 'cisco' 8 | password = 'cisco' 9 | 10 | for device in devices.keys(): 11 | device_prompt = devices[device]['prompt'] 12 | child = pexpect.spawn('telnet ' + devices[device]['ip']) 13 | child.expect('Username:') 14 | child.sendline(username) 15 | child.expect('Password:') 16 | child.sendline(password) 17 | child.expect(device_prompt) 18 | child.sendline('show version | i V') 19 | child.expect(device_prompt) 20 | print(child.before) 21 | child.sendline('exit') 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Chapter08/tmp/r5-tor_lldp_output.txt: -------------------------------------------------------------------------------- 1 | [["Capability codes:", " (R) Router, (B) Bridge, (T) Telephone, (C) DOCSIS Cable Device", " (W) WLAN Access Point, (P) Repeater, (S) Station, (O) Other", "", "Device ID Local Intf Hold-time Capability Port ID", "r2.virl.info Gi0/2 120 R Gi0/1", "r2.virl.info Gi0/0 120 R Gi0/0", "r3.virl.info Gi0/0 120 R Gi0/0", "r3.virl.info Gi0/3 120 R Gi0/1", "r6-edge.virl.info Gi0/0 120 R Gi0/0", "r1.virl.info Gi0/0 120 R Gi0/0", "r1.virl.info Gi0/1 120 R Gi0/1", "", "Total entries displayed: 7", ""]] -------------------------------------------------------------------------------- /Chapter04/cisco_3.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure SNMP Contact 3 | hosts: "nexus_by_name" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | transport: cli 13 | 14 | tasks: 15 | - name: configure snmp contact 16 | nxos_snmp_contact: 17 | contact: TEST_1 18 | state: present 19 | provider: "{{ cli }}" 20 | 21 | register: output 22 | 23 | - name: show output in output["end_state"]["contact"] 24 | debug: 25 | msg: '{{ output["end_state"]["contact"] }}' 26 | 27 | - name: show output in output.end_state.contact 28 | debug: 29 | msg: '{{ output.end_state.contact }}' 30 | 31 | -------------------------------------------------------------------------------- /Chapter09/chapter9_pexpect_1.py: -------------------------------------------------------------------------------- 1 | import pexpect 2 | 3 | 4 | def show_version(device, prompt, ip, username, password): 5 | device_prompt = prompt 6 | child = pexpect.spawn('telnet ' + ip) 7 | child.expect('Username:') 8 | child.sendline(username) 9 | child.expect('Password:') 10 | child.sendline(password) 11 | child.expect(device_prompt) 12 | child.sendline('show version | i V') 13 | child.expect(device_prompt) 14 | result = child.before 15 | child.sendline('exit') 16 | return device, result 17 | 18 | if __name__ == '__main__': 19 | username = 'cisco' 20 | password = 'cisco' 21 | print(show_version('iosv-1', 'iosv-1#', '172.16.1.225', username, password)) 22 | print(show_version('iosv-2', 'iosv-2#', '172.16.1.226', username, password)) 23 | 24 | -------------------------------------------------------------------------------- /Chapter08/cisco_discover_lldp.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Query LLDP Neighbors 3 | hosts: "devices" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_hostname }}" 10 | username: cisco 11 | password: cisco 12 | transport: cli 13 | 14 | tasks: 15 | - name: Query for LLDP Neighbors 16 | ios_command: 17 | commands: show lldp neighbors 18 | provider: "{{ cli }}" 19 | 20 | register: output 21 | 22 | - name: show output 23 | debug: 24 | var: output 25 | 26 | - name: copy output to file 27 | copy: content="{{ output.stdout_lines }}" dest="./tmp/{{ inventory_hostname }}_lldp_output.txt" 28 | 29 | - name: Execute Python script to render output 30 | command: ./cisco_graph_lldp.py 31 | 32 | -------------------------------------------------------------------------------- /Chapter03/Juniper/junos_netconf_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from ncclient import manager 4 | from ncclient.xml_ import new_ele, sub_ele 5 | 6 | conn = manager.connect(host='192.168.24.252', port='830', username='netconf', password='juniper!', timeout=10, device_params={'name':'junos'}, hostkey_verify=False) 7 | 8 | # lock configuration and make configuration changes 9 | conn.lock() 10 | 11 | # build configuration 12 | config = new_ele('system') 13 | sub_ele(config, 'host-name').text = 'master' 14 | sub_ele(config, 'domain-name').text = 'python' 15 | 16 | # send, validate, and commit config 17 | conn.load_configuration(config=config) 18 | conn.validate() 19 | commit_config = conn.commit() 20 | print(commit_config.tostring) 21 | 22 | # unlock config 23 | conn.unlock() 24 | 25 | # close session 26 | conn.close_session() 27 | 28 | 29 | -------------------------------------------------------------------------------- /Chapter07/matplotlib_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import matplotlib.pyplot as plt 4 | import matplotlib.dates as dates 5 | 6 | x_time = [] 7 | y_value = [] 8 | 9 | with open('results.txt', 'r') as f: 10 | for line in f.readlines(): 11 | # eval(line) reads in each line as dictionary instead of string 12 | line = eval(line) 13 | # convert to internal float 14 | x_time.append(dates.datestr2num(line['Time'])) 15 | y_value.append(line['Gig0-0_Out_uPackets']) 16 | 17 | plt.subplots_adjust(bottom=0.3) 18 | plt.xticks(rotation=80) 19 | 20 | # Use plot_date to display x-axis back in date format 21 | plt.plot_date(x_time, y_value) 22 | plt.title('Router1 G0/0') 23 | plt.xlabel('Time in UTC') 24 | plt.ylabel('Output Unicast Packets') 25 | plt.savefig('matplotlib_1_result.png') 26 | plt.show() 27 | 28 | 29 | -------------------------------------------------------------------------------- /Chapter04/cisco_4.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure SNMP Contact 3 | hosts: "nexus_by_name" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | transport: cli 13 | 14 | tasks: 15 | - name: configure snmp contact 16 | nxos_snmp_contact: 17 | contact: TEST_1 18 | state: present 19 | username: cisco123 20 | password: cisco123 21 | provider: "{{ cli }}" 22 | 23 | register: output 24 | 25 | - name: show output in output["end_state"]["contact"] 26 | debug: 27 | msg: '{{ output["end_state"]["contact"] }}' 28 | 29 | - name: show output in output.end_state.contact 30 | debug: 31 | msg: '{{ output.end_state.contact }}' 32 | 33 | -------------------------------------------------------------------------------- /Chapter07/pysnmp_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env/python3 2 | 3 | from pysnmp.entity.rfc3413.oneliner import cmdgen 4 | 5 | cmdGen = cmdgen.CommandGenerator() 6 | 7 | system_name = '1.3.6.1.2.1.1.5.0' 8 | system_uptime = '1.3.6.1.2.1.1.3.0' 9 | 10 | errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd( 11 | cmdgen.CommunityData('secret'), 12 | cmdgen.UdpTransportTarget(('172.16.1.189', 161)), 13 | system_name, 14 | system_uptime 15 | ) 16 | 17 | # Check for errors and print out results 18 | if errorIndication: 19 | print(errorIndication) 20 | else: 21 | if errorStatus: 22 | print('%s at %s' % ( 23 | errorStatus.prettyPrint(), 24 | errorIndex and varBinds[int(errorIndex)-1] or '?' 25 | ) 26 | ) 27 | else: 28 | for name, val in varBinds: 29 | print('%s = %s' % (name.prettyPrint(), str(val))) 30 | 31 | -------------------------------------------------------------------------------- /Chapter11/Chapter11_7.py: -------------------------------------------------------------------------------- 1 | BGP = { 2 | 'local_as': 65000, 3 | 'router_id': '172.16.1.174', 4 | 'neighbors': [ 5 | { 6 | 'address': '172.16.1.175', 7 | 'remote_as': 1, 8 | 'enable_ipv4': True, 9 | 'enable_ipv6': True, 10 | 'enable_vpnv4': True, 11 | 'enable_vpnv6': True 12 | }, 13 | ], 14 | 'routes': [ 15 | # Example of IPv4 prefix 16 | { 17 | 'prefix': '10.20.0.0/24', 18 | 'next_hop': '172.16.1.1' 19 | }, 20 | { 21 | 'prefix': '10.20.1.0/24', 22 | 'next_hop': '172.16.1.174' 23 | }, 24 | ] 25 | } 26 | 27 | SSH = { 28 | 'ssh_port': 4990, 29 | 'ssh_host': 'localhost', 30 | # 'ssh_host_key': '/etc/ssh_host_rsa_key', 31 | # 'ssh_username': 'ryu', 32 | # 'ssh_password': 'ryu', 33 | } 34 | 35 | -------------------------------------------------------------------------------- /Chapter06/access_list_mac_iosv.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure MAC Access List 3 | hosts: "iosv-1" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ ansible_username }}" 11 | password: "{{ ansible_password }}" 12 | transport: cli 13 | 14 | tasks: 15 | - name: Deny Hosts with vendor id fa16.3e00.0000 16 | ios_config: 17 | lines: 18 | - access-list 700 deny fa16.3e00.0000 0000.00FF.FFFF 19 | - access-list 700 permit 0000.0000.0000 FFFF.FFFF.FFFF 20 | provider: "{{ cli }}" 21 | - name: Apply filter on bridge group 1 22 | ios_config: 23 | lines: 24 | - bridge-group 1 25 | - bridge-group 1 input-address-list 700 26 | parents: 27 | - interface GigabitEthernet0/1 28 | provider: "{{ cli }}" 29 | 30 | -------------------------------------------------------------------------------- /Chapter07/pysnmp_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env/python3 2 | 3 | from pysnmp.entity.rfc3413.oneliner import cmdgen 4 | 5 | cmdGen = cmdgen.CommandGenerator() 6 | 7 | system_up_time_oid = "1.3.6.1.2.1.1.3.0" 8 | cisco_contact_info_oid = "1.3.6.1.4.1.9.2.1.61.0" 9 | 10 | errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd( 11 | cmdgen.CommunityData('secret'), 12 | cmdgen.UdpTransportTarget(('172.16.1.189', 161)), 13 | system_up_time_oid, 14 | cisco_contact_info_oid 15 | ) 16 | 17 | # Check for errors and print out results 18 | if errorIndication: 19 | print(errorIndication) 20 | else: 21 | if errorStatus: 22 | print('%s at %s' % ( 23 | errorStatus.prettyPrint(), 24 | errorIndex and varBinds[int(errorIndex)-1] or '?' 25 | ) 26 | ) 27 | else: 28 | for name, val in varBinds: 29 | print('%s = %s' % (name.prettyPrint(), str(val))) 30 | 31 | -------------------------------------------------------------------------------- /Chapter13/chapter13_bgp_1.py: -------------------------------------------------------------------------------- 1 | BGP = { 2 | 'local_as': 1, 3 | 'router_id': '172.16.2.52', 4 | 'neighbors': [ 5 | { 6 | 'address': '172.16.2.51', 7 | 'remote_as': 1, 8 | 'enable_ipv4': True, 9 | 'enable_ipv6': True, 10 | 'enable_vpnv4': True, 11 | 'enable_vpnv6': True 12 | }, 13 | ], 14 | 'routes': [ 15 | # Example of IPv4 prefix 16 | { 17 | 'prefix': '10.20.1.0/24', 18 | 'next_hop': '172.16.1.174' 19 | }, 20 | { 21 | 'prefix': '172.16.1.0/24' 22 | }, 23 | { 24 | 'prefix': '172.16.2.0/24' 25 | } 26 | ] 27 | } 28 | 29 | SSH = { 30 | 'ssh_port': 4990, 31 | 'ssh_host': 'localhost', 32 | # 'ssh_host_key': '/etc/ssh_host_rsa_key', 33 | # 'ssh_username': 'ryu', 34 | # 'ssh_password': 'ryu', 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Chapter10/chapter10_1.py: -------------------------------------------------------------------------------- 1 | # Referenced from ryu/ryu/app/simple_switch_13.py 2 | 3 | from ryu.base import app_manager 4 | from ryu.controller import ofp_event 5 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 6 | from ryu.controller.handler import set_ev_cls 7 | from ryu.ofproto import ofproto_v1_3, ofproto_v1_0 8 | 9 | class SimpleSwitch(app_manager.RyuApp): 10 | OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] 11 | 12 | def __init__(self, *args, **kwargs): 13 | super(SimpleSwitch, self).__init__(*args, **kwargs) 14 | self.mac_to_port = {} 15 | 16 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 17 | def switch_features_handler(self, ev): 18 | print("ev message: ", ev.msg) 19 | datapath = ev.msg.datapath 20 | print("datapath: ", datapath) 21 | ofproto = datapath.ofproto 22 | print("ofprotocol: ", ofproto) 23 | parser = datapath.ofproto_parser 24 | print("parser: ", parser) 25 | 26 | -------------------------------------------------------------------------------- /Chapter07/pygal_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import pygal 4 | 5 | x_time = [] 6 | out_octets = [] 7 | out_packets = [] 8 | in_octets = [] 9 | in_packets = [] 10 | 11 | with open('results.txt', 'r') as f: 12 | for line in f.readlines(): 13 | # eval(line) reads in each line as dictionary instead of string 14 | line = eval(line) 15 | x_time.append(line['Time']) 16 | out_packets.append(float(line['Gig0-0_Out_uPackets'])) 17 | out_octets.append(float(line['Gig0-0_Out_Octet'])) 18 | in_packets.append(float(line['Gig0-0_In_uPackets'])) 19 | in_octets.append(float(line['Gig0-0_In_Octet'])) 20 | 21 | line_chart = pygal.Line() 22 | line_chart.title = "Router 1 Gig0/0" 23 | line_chart.x_labels = x_time 24 | line_chart.add('out_octets', out_octets) 25 | line_chart.add('out_packets', out_packets) 26 | line_chart.add('in_octets', in_octets) 27 | line_chart.add('in_packets', in_packets) 28 | line_chart.render_to_file('pygal_example_2.svg') 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Chapter09/chapter9_db_1.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_sqlalchemy import SQLAlchemy 3 | 4 | # Create Flask application, load configuration, and create 5 | # the SQLAlchemy object 6 | app = Flask(__name__) 7 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///network.db' 8 | db = SQLAlchemy(app) 9 | 10 | # This is the database model object 11 | class Device(db.Model): 12 | __tablename__ = 'devices' 13 | id = db.Column(db.Integer, primary_key=True) 14 | hostname = db.Column(db.String(120), index=True) 15 | vendor = db.Column(db.String(40)) 16 | 17 | def __init__(self, hostname, vendor): 18 | self.hostname = hostname 19 | self.vendor = vendor 20 | 21 | def __repr__(self): 22 | return '' % self.hostname 23 | 24 | 25 | if __name__ == '__main__': 26 | db.create_all() 27 | r1 = Device('lax-dc1-core1', 'Juniper') 28 | r2 = Device('sfo-dc1-core1', 'Cisco') 29 | db.session.add(r1) 30 | db.session.add(r2) 31 | db.session.commit() 32 | 33 | -------------------------------------------------------------------------------- /Chapter08/chapter8_logstash_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/env/bin python 2 | 3 | #https://www.spamhaus.org/drop/drop.txt 4 | 5 | import logging, pprint, re 6 | import requests, json, datetime 7 | from collections import OrderedDict 8 | 9 | #logging configuration 10 | logging.basicConfig(filename='/home/echou/Master_Python_Networking/Chapter8/tmp/spamhaus_drop_list.log', level=logging.INFO, format='%(asctime)s %(message)s', datefmt='%b %d %I:%M:%S') 11 | host = 'python_networkign' 12 | process = 'spamhause_drop_list' 13 | 14 | r = requests.get('https://www.spamhaus.org/drop/drop.txt') 15 | result = r.text.strip() 16 | 17 | timeInUTC = datetime.datetime.utcnow().isoformat() 18 | Item = OrderedDict() 19 | Item["Time"] = timeInUTC 20 | 21 | for line in result.split('\n'): 22 | if re.match('^;', line) or line == '\r': # comments 23 | next 24 | else: 25 | ip, record_number = line.split(";") 26 | logging.warning(host + ' ' + process + ': ' + 'src_ip=' + ip.split("/")[0] + ' record_number=' + record_number.strip()) 27 | 28 | 29 | -------------------------------------------------------------------------------- /Chapter05/chapter5_6.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Add Multiple Vlans 3 | hosts: "nxos-r1" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ username }}" 11 | password: "{{ password }}" 12 | transport: cli 13 | vlans: { 14 | "100": {"description": "floor_1", "ip": "192.168.10.1"}, 15 | "200": {"description": "floor_2", "ip": "192.168.20.1"}, 16 | "300": {"description": "floor_3", "ip": "192.168.30.1"} 17 | } 18 | 19 | tasks: 20 | - name: add vlans 21 | nxos_config: 22 | lines: 23 | - vlan {{ item.key }} 24 | provider: "{{ cli }}" 25 | with_dict: "{{ vlans }}" 26 | 27 | - name: configure vlans 28 | nxos_config: 29 | lines: 30 | - description {{ item.value.name }} 31 | - ip address {{ item.value.ip }}/24 32 | provider: "{{ cli }}" 33 | parents: interface vlan {{ item.key }} 34 | with_dict: "{{ vlans }}" 35 | 36 | -------------------------------------------------------------------------------- /Chapter13/chapter13_mininet_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from mininet.net import Mininet 4 | from mininet.node import Controller, OVSKernelSwitch, RemoteController 5 | from mininet.log import setLogLevel 6 | from mininet.cli import CLI 7 | 8 | setLogLevel( 'info' ) 9 | 10 | net = Mininet( controller=RemoteController, switch=OVSKernelSwitch ) 11 | c1 = net.addController('c1', controller=RemoteController, ip='127.0.0.1', port=6633) 12 | c2 = net.addController('c2', controller=RemoteController, ip='127.0.0.1', port=5555) 13 | 14 | h1 = net.addHost('h1', ip='10.0.0.1') 15 | h2 = net.addHost('h2', ip='10.0.0.2') 16 | h3 = net.addHost('h3', ip='10.0.0.3') 17 | h4 = net.addHost('h4', ip='10.0.0.4') 18 | s1 = net.addSwitch('s1') 19 | s2 = net.addSwitch('s2') 20 | 21 | s1.linkTo(h1) 22 | s1.linkTo(h2) 23 | s1.linkTo(s2) 24 | s2.linkTo(h3) 25 | s2.linkTo(h4) 26 | 27 | net.build() 28 | 29 | c1.start() 30 | c2.start() 31 | s1.start([c1,c2]) 32 | s2.start([c1,c2]) 33 | 34 | net.start() 35 | net.staticArp() 36 | CLI( net ) 37 | net.stop() 38 | 39 | -------------------------------------------------------------------------------- /Chapter02/chapter2_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import getpass 4 | from pexpect import pxssh 5 | 6 | devices = {'iosv-1': {'prompt': 'iosv-1#', 'ip': '172.16.1.20'}, 7 | 'iosv-2': {'prompt': 'iosv-2#', 'ip': '172.16.1.21'}} 8 | commands = ['term length 0', 'show version', 'show run'] 9 | 10 | username = input('Username: ') 11 | password = getpass.getpass('Password: ') 12 | 13 | # Starts the loop for devices 14 | for device in devices.keys(): 15 | outputFileName = device + '_output.txt' 16 | device_prompt = devices[device]['prompt'] 17 | child = pxssh.pxssh() 18 | child.login(devices[device]['ip'], username.strip(), password.strip(), auto_prompt_reset=False) 19 | # Starts the loop for commands and write to output 20 | with open(outputFileName, 'wb') as f: 21 | for command in commands: 22 | child.sendline(command) 23 | child.expect(device_prompt) 24 | f.write(child.before) 25 | 26 | child.logout() 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /Chapter11/Chapter11_1.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_3 6 | 7 | class SimpleSwitch13(app_manager.RyuApp): 8 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 9 | 10 | def __init__(self, *args, **kwargs): 11 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 12 | self.mac_to_port = {} 13 | 14 | # Catching Switch Features Events 15 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 16 | def switch_features_handler(self, ev): 17 | 18 | print("EventOFPSwitchFeatures: datapath_id {}, n_buffers {} capabilities {}".format(ev.msg.datapath_id, 19 | ev.msg.n_buffers, 20 | ev.msg.capabilities)) 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /Chapter03/Cisco/cisco_nxapi_4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import requests 4 | import json 5 | 6 | url='http://172.16.1.90/ins' 7 | switchuser='cisco' 8 | switchpassword='cisco' 9 | 10 | myheaders={'content-type':'application/json-rpc'} 11 | payload=[ 12 | { 13 | "jsonrpc": "2.0", 14 | "method": "cli", 15 | "params": { 16 | "cmd": "interface ethernet 2/12", 17 | "version": 1.2 18 | }, 19 | "id": 1 20 | }, 21 | { 22 | "jsonrpc": "2.0", 23 | "method": "cli", 24 | "params": { 25 | "cmd": "description foo-bar", 26 | "version": 1.2 27 | }, 28 | "id": 2 29 | }, 30 | { 31 | "jsonrpc": "2.0", 32 | "method": "cli", 33 | "params": { 34 | "cmd": "end", 35 | "version": 1.2 36 | }, 37 | "id": 3 38 | }, 39 | { 40 | "jsonrpc": "2.0", 41 | "method": "cli", 42 | "params": { 43 | "cmd": "copy run start", 44 | "version": 1.2 45 | }, 46 | "id": 4 47 | } 48 | ] 49 | 50 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json() 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Packt 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 | -------------------------------------------------------------------------------- /Chapter05/Templates/chapter5_8.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Template Looping 3 | hosts: localhost 4 | 5 | vars: 6 | nexus_devices: { 7 | "nx-osv-1": { 8 | "hostname": "nx-osv-1", 9 | "username": "cisco", 10 | "password": "cisco", 11 | "vlans": [100, 200, 300], 12 | "l3_vlan_interfaces": True, 13 | "vlan_interfaces": [ 14 | {"int_num": "100", "ip": "192.168.10.1"}, 15 | {"int_num": "200", "ip": "192.168.20.1"}, 16 | {"int_num": "300", "ip": "192.168.30.1"} 17 | ], 18 | "netflow_enable": True 19 | }, 20 | "nx-osv-2": { 21 | "hostname": "nx-osv-2", 22 | "username": "cisco", 23 | "password": "cisco", 24 | "vlans": [100, 200, 300], 25 | "l3_vlan_interfaces": False, 26 | "netflow_enable": False 27 | } 28 | } 29 | tasks: 30 | - name: create router configuration files 31 | template: 32 | src=./nxos.j2 33 | dest=./{{ item.key }}.conf 34 | with_dict: "{{ nexus_devices }}" 35 | -------------------------------------------------------------------------------- /Chapter11/Chapter11_5.py: -------------------------------------------------------------------------------- 1 | import eventlet 2 | 3 | eventlet.monkey_patch() 4 | 5 | import logging 6 | import sys 7 | 8 | log = logging.getLogger() 9 | log.addHandler(logging.StreamHandler(sys.stderr)) 10 | log.setLevel(logging.DEBUG) 11 | 12 | from ryu.services.protocols.bgp.bgpspeaker import BGPSpeaker 13 | 14 | def dump_remote_best_path_change(event): 15 | print 'the best path changed:', event.remote_as, event.prefix,\ 16 | event.nexthop, event.is_withdraw 17 | 18 | def detect_peer_down(remote_ip, remote_as): 19 | print 'Peer down:', remote_ip, remote_as 20 | 21 | if __name__ == "__main__": 22 | speaker = BGPSpeaker(as_number=65000, router_id='172.16.1.174', 23 | best_path_change_handler=dump_remote_best_path_change, 24 | peer_down_handler=detect_peer_down) 25 | 26 | speaker.neighbor_add('172.16.1.175', 1) 27 | count = 0 28 | while True: 29 | eventlet.sleep(30) 30 | prefix = '10.20.' + str(count) + '.0/24' 31 | print "add a new prefix", prefix 32 | speaker.prefix_add(prefix) 33 | count += 1 34 | print("Neighbors: ", speaker.neighbors_get()) 35 | print("Routes: ", speaker.rib_get()) 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Chapter03/Juniper/junos_netconf_3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from ncclient import manager 4 | from ncclient.xml_ import new_ele, sub_ele 5 | 6 | # make a connection object 7 | def connect(host, port, user, password): 8 | connection = manager.connect(host=host, port=port, username=user, 9 | password=password, timeout=10, device_params={'name':'junos'}, 10 | hostkey_verify=False) 11 | return connection 12 | 13 | # execute show commands 14 | def show_cmds(conn, cmd): 15 | result = conn.command(cmd, format='text') 16 | return result 17 | 18 | # push out configuration 19 | def config_cmds(conn, config): 20 | conn.lock() 21 | conn.load_configuration(config=config) 22 | commit_config = conn.commit() 23 | return commit_config.tostring 24 | 25 | 26 | if __name__ == '__main__': 27 | conn = connect('192.168.24.252', '830', 'netconf', 'juniper!') 28 | result = show_cmds(conn, 'show version') 29 | print('show version: ' + str(result)) 30 | new_config = new_ele('system') 31 | sub_ele(new_config, 'host-name').text = 'foo' 32 | sub_ele(new_config, 'domain-name').text = 'bar' 33 | result = config_cmds(conn, new_config) 34 | print('change id: ' + str(result)) 35 | conn.close_session() 36 | 37 | 38 | -------------------------------------------------------------------------------- /Chapter05/Custom_Modules/library/custom_module_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | import requests 4 | import json 5 | 6 | def main(): 7 | module = AnsibleModule( 8 | argument_spec = dict( 9 | host = dict(required=True), 10 | username = dict(required=True), 11 | password = dict(required=True) 12 | ) 13 | ) 14 | 15 | device = module.params.get('host') 16 | username = module.params.get('username') 17 | password = module.params.get('password') 18 | 19 | url='http://' + host + '/ins' 20 | switchuser=username 21 | switchpassword=password 22 | 23 | myheaders={'content-type':'application/json-rpc'} 24 | 25 | payload=[ 26 | { 27 | "jsonrpc": "2.0", 28 | "method": "cli", 29 | "params": { 30 | "cmd": "show version", 31 | "version": 1.2 32 | }, 33 | "id": 1 34 | } 35 | ] 36 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json() 37 | 38 | version = response['result']['body']['sys_ver_str'] 39 | data = json.dumps({"version": version}) 40 | module.exit_json(changed=False, msg=str(data)) 41 | 42 | 43 | from ansible.module_utils.basic import AnsibleModule 44 | if __name__ == '__main__': 45 | main() 46 | 47 | -------------------------------------------------------------------------------- /Chapter07/matplotlib_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import matplotlib.pyplot as plt 4 | import matplotlib.dates as dates 5 | 6 | x_time = [] 7 | out_octets = [] 8 | out_packets = [] 9 | in_octets = [] 10 | in_packets = [] 11 | 12 | with open('results.txt', 'r') as f: 13 | for line in f.readlines(): 14 | # eval(line) reads in each line as dictionary instead of string 15 | line = eval(line) 16 | # convert to internal float 17 | x_time.append(dates.datestr2num(line['Time'])) 18 | out_packets.append(line['Gig0-0_Out_uPackets']) 19 | out_octets.append(line['Gig0-0_Out_Octet']) 20 | in_packets.append(line['Gig0-0_In_uPackets']) 21 | in_octets.append(line['Gig0-0_In_Octet']) 22 | 23 | plt.subplots_adjust(bottom=0.3) 24 | plt.xticks(rotation=80) 25 | 26 | # Use plot_date to display x-axis back in date format 27 | plt.plot_date(x_time, out_packets, '-', label='Out Packets') 28 | plt.plot_date(x_time, out_octets, '-', label='Out Octets') 29 | plt.plot_date(x_time, in_packets, '-', label='In Packets') 30 | plt.plot_date(x_time, in_octets, '-', label='In Octets') 31 | 32 | plt.title('Router1 G0/0') 33 | plt.legend(loc='upper left') 34 | plt.grid(True) 35 | plt.xlabel('Time in UTC') 36 | plt.ylabel('Values') 37 | plt.savefig('matplotlib_2_result.png') 38 | plt.show() 39 | 40 | 41 | -------------------------------------------------------------------------------- /Chapter02/chapter2_3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import paramiko, getpass, time 4 | 5 | devices = {'iosv-1': {'ip': '172.16.1.20'}, 6 | 'iosv-2': {'ip': '172.16.1.21'}} 7 | commands = ['show version\n', 'show run\n'] 8 | 9 | username = input('Username: ') 10 | password = getpass.getpass('Password: ') 11 | 12 | max_buffer = 65535 13 | 14 | def clear_buffer(connection): 15 | if connection.recv_ready(): 16 | return connection.recv(max_buffer) 17 | 18 | # Starts the loop for devices 19 | for device in devices.keys(): 20 | outputFileName = device + '_output.txt' 21 | connection = paramiko.SSHClient() 22 | connection.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 23 | connection.connect(devices[device]['ip'], username=username, password=password, look_for_keys=False, allow_agent=False) 24 | new_connection = connection.invoke_shell() 25 | output = clear_buffer(new_connection) 26 | time.sleep(2) 27 | new_connection.send("terminal length 0\n") 28 | output = clear_buffer(new_connection) 29 | with open(outputFileName, 'wb') as f: 30 | for command in commands: 31 | new_connection.send(command) 32 | time.sleep(2) 33 | output = new_connection.recv(max_buffer) 34 | print(output) 35 | f.write(output) 36 | 37 | new_connection.close() 38 | 39 | 40 | -------------------------------------------------------------------------------- /Chapter10/chapter10_2.py: -------------------------------------------------------------------------------- 1 | # Referenced from ryu/ryu/app/simple_switch_13.py 2 | 3 | from ryu.base import app_manager 4 | from ryu.controller import ofp_event 5 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 6 | from ryu.controller.handler import set_ev_cls 7 | from ryu.ofproto import ofproto_v1_3, ofproto_v1_0 8 | 9 | class SimpleHub(app_manager.RyuApp): 10 | OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] 11 | #OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 12 | 13 | def __init__(self, *args, **kwargs): 14 | super(SimpleHub, self).__init__(*args, **kwargs) 15 | 16 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 17 | def switch_features_handler(self, ev): 18 | message = ev.msg 19 | print("message: ", message) 20 | 21 | 22 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 23 | def packet_in_handler(self, ev): 24 | msg = ev.msg 25 | print("ev message: ", ev.msg) 26 | datapath = msg.datapath 27 | ofproto = datapath.ofproto 28 | ofp_parser = datapath.ofproto_parser 29 | 30 | actions = [ofp_parser.OFPActionOutput(ofproto.OFPP_FLOOD)] 31 | out = ofp_parser.OFPPacketOut( 32 | datapath=datapath, buffer_id=msg.buffer_id, in_port=msg.in_port, 33 | actions=actions) 34 | datapath.send_msg(out) 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /Chapter02/chapter2_4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import paramiko, getpass, time, json 4 | 5 | with open('devices.json', 'r') as f: 6 | devices = json.load(f) 7 | 8 | with open('commands.txt', 'r') as f: 9 | commands = [line for line in f.readlines()] 10 | 11 | username = input('Username: ') 12 | password = getpass.getpass('Password: ') 13 | 14 | max_buffer = 65535 15 | 16 | def clear_buffer(connection): 17 | if connection.recv_ready(): 18 | return connection.recv(max_buffer) 19 | 20 | # Starts the loop for devices 21 | for device in devices.keys(): 22 | outputFileName = device + '_output.txt' 23 | connection = paramiko.SSHClient() 24 | connection.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 25 | connection.connect(devices[device]['ip'], username=username, password=password, look_for_keys=False, allow_agent=False) 26 | new_connection = connection.invoke_shell() 27 | output = clear_buffer(new_connection) 28 | time.sleep(2) 29 | new_connection.send("terminal length 0\n") 30 | output = clear_buffer(new_connection) 31 | with open(outputFileName, 'wb') as f: 32 | for command in commands: 33 | new_connection.send(command) 34 | time.sleep(2) 35 | output = new_connection.recv(max_buffer) 36 | print(output) 37 | f.write(output) 38 | 39 | new_connection.close() 40 | 41 | 42 | -------------------------------------------------------------------------------- /Chapter08/cisco_graph_lldp.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import glob, re 4 | from graphviz import Digraph, Source 5 | 6 | pattern = re.compile('Gi0/[1234]') 7 | 8 | device_lldp_neighbors = [] 9 | 10 | # walk thru files in ./tmp directory 11 | for file_name in glob.glob('tmp/*'): 12 | # device name 13 | device = file_name.split('/')[1].split('_')[0] 14 | print("device: " + device) 15 | with open(file_name, 'r') as f: 16 | for line in f.readlines(): 17 | line = eval(line) #eval the line as list 18 | for item in line[0]: 19 | # only look for GigEth other than Gi0/0 20 | if re.search(pattern, item): 21 | print(" neighbors: " + item.split()[0].split('.')[0]) 22 | device_lldp_neighbors.append((device, item.split()[0].split('.')[0])) 23 | 24 | print("*" * 10) 25 | print("Edges: " + str(device_lldp_neighbors)) 26 | 27 | my_graph = Digraph("My_Network") 28 | my_graph.edge("Client", "r6-edge") 29 | my_graph.edge("r5-tor", "Server") 30 | 31 | # construct the edge relationships 32 | for neighbors in device_lldp_neighbors: 33 | node1, node2 = neighbors 34 | my_graph.edge(node1, node2) 35 | 36 | # Insert arbitrary DOT language commands 37 | # such as the rank=same command 38 | source = my_graph.source 39 | original_text = "digraph My_Network {" 40 | new_text = 'digraph My_Network {\n{rank=same Client "r6-edge"}\n{rank=same r1 r2 r3}\n' 41 | new_source = source.replace(original_text, new_text) 42 | print(new_source) 43 | new_graph = Source(new_source) 44 | new_graph.render("output/chapter8_lldp_graph.gv") 45 | 46 | -------------------------------------------------------------------------------- /Chapter07/pysnmp_3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | from pysnmp.entity.rfc3413.oneliner import cmdgen 3 | import datetime 4 | 5 | cmdGen = cmdgen.CommandGenerator() 6 | 7 | host = '172.16.1.189' 8 | community = 'secret' 9 | 10 | # Hostname OID 11 | system_name = '1.3.6.1.2.1.1.5.0' 12 | 13 | # Interface OID 14 | gig0_0_in_oct = '1.3.6.1.2.1.2.2.1.10.1' 15 | gig0_0_in_uPackets = '1.3.6.1.2.1.2.2.1.11.1' 16 | gig0_0_out_oct = '1.3.6.1.2.1.2.2.1.16.1' 17 | gig0_0_out_uPackets = '1.3.6.1.2.1.2.2.1.17.1' 18 | 19 | 20 | def snmp_query(host, community, oid): 21 | errorIndication, errorStatus, errorIndex, varBinds = cmdGen.getCmd( 22 | cmdgen.CommunityData(community), 23 | cmdgen.UdpTransportTarget((host, 161)), 24 | oid 25 | ) 26 | 27 | # Check for errors and print out results 28 | if errorIndication: 29 | print(errorIndication) 30 | else: 31 | if errorStatus: 32 | print('%s at %s' % ( 33 | errorStatus.prettyPrint(), 34 | errorIndex and varBinds[int(errorIndex)-1] or '?' 35 | ) 36 | ) 37 | else: 38 | for name, val in varBinds: 39 | return(str(val)) 40 | 41 | result = {} 42 | result['Time'] = datetime.datetime.utcnow().isoformat() 43 | result['hostname'] = snmp_query(host, community, system_name) 44 | result['Gig0-0_In_Octet'] = snmp_query(host, community, gig0_0_in_oct) 45 | result['Gig0-0_In_uPackets'] = snmp_query(host, community, gig0_0_in_uPackets) 46 | result['Gig0-0_Out_Octet'] = snmp_query(host, community, gig0_0_out_oct) 47 | result['Gig0-0_Out_uPackets'] = snmp_query(host, community, gig0_0_out_uPackets) 48 | 49 | with open('/home/echou/Master_Python_Networking/Chapter7/results.txt', 'a') as f: 50 | f.write(str(result)) 51 | f.write('\n') 52 | 53 | -------------------------------------------------------------------------------- /Chapter03/Cisco/cisco_apic_em_1.py: -------------------------------------------------------------------------------- 1 | # import requests library 2 | import requests 3 | 4 | #import json library 5 | import json 6 | 7 | controller='URL' 8 | 9 | def getTicket(): 10 | # put the ip address or dns of your apic-em controller in this url 11 | url = "https://" + controller + "/api/v1/ticket" 12 | 13 | #the username and password to access the APIC-EM Controller 14 | payload = {"username":"usernae","password":"password"} 15 | 16 | #Content type must be included in the header 17 | header = {"content-type": "application/json"} 18 | 19 | #Performs a POST on the specified url to get the service ticket 20 | response= requests.post(url,data=json.dumps(payload), headers=header, verify=False) 21 | 22 | #convert response to json format 23 | r_json=response.json() 24 | 25 | #parse the json to get the service ticket 26 | ticket = r_json["response"]["serviceTicket"] 27 | 28 | return ticket 29 | 30 | 31 | def getNetworkDevices(ticket): 32 | # URL for network device REST API call to get list of existing devices on the network. 33 | url = "https://" + controller + "/api/v1/network-device" 34 | 35 | #Content type must be included in the header as well as the ticket 36 | header = {"content-type": "application/json", "X-Auth-Token":ticket} 37 | 38 | # this statement performs a GET on the specified network-device url 39 | response = requests.get(url, headers=header, verify=False) 40 | 41 | # json.dumps serializes the json into a string and allows us to 42 | # print the response in a 'pretty' format with indentation etc. 43 | print ("Network Devices = ") 44 | print (json.dumps(response.json(), indent=4, separators=(',', ': '))) 45 | 46 | #convert data to json format. 47 | r_json=response.json() 48 | 49 | #Iterate through network device data and print the id and series name of each device 50 | for i in r_json["response"]: 51 | print(i["id"] + " " + i["series"]) 52 | 53 | #call the functions 54 | theTicket=getTicket() 55 | getNetworkDevices(theTicket) 56 | 57 | -------------------------------------------------------------------------------- /Chapter08/cisco_config_netflow.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Enable LLDP 3 | hosts: "devices" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_hostname }}" 10 | username: cisco 11 | password: cisco 12 | transport: cli 13 | 14 | tasks: 15 | - name: configure netflow export station 16 | ios_config: 17 | lines: 18 | - ip flow-export destination 172.16.1.173 5556 vrf Mgmt-intf 19 | - ip flow-export version 5 20 | provider: "{{ cli }}" 21 | 22 | - name: configure flow export on Gi0/0 23 | ios_config: 24 | lines: 25 | - ip flow ingress 26 | - ip flow egress 27 | parents: interface GigabitEthernet0/0 28 | provider: "{{ cli }}" 29 | 30 | - name: configure flow export on Gi0/1 31 | ios_config: 32 | lines: 33 | - ip flow ingress 34 | - ip flow egress 35 | parents: interface GigabitEthernet0/1 36 | provider: "{{ cli }}" 37 | 38 | - name: configure flow export on Gi0/2 39 | ios_config: 40 | lines: 41 | - ip flow ingress 42 | - ip flow egress 43 | parents: interface GigabitEthernet0/2 44 | provider: "{{ cli }}" 45 | 46 | - name: Configure LLDP on Gi0/3 Gi0/4 47 | hosts: "edge-devices" 48 | gather_facts: false 49 | connection: local 50 | 51 | vars: 52 | cli: 53 | host: "{{ ansible_hostname }}" 54 | username: cisco 55 | password: cisco 56 | transport: cli 57 | 58 | tasks: 59 | - name: configure flow export on Gi0/3 60 | ios_config: 61 | lines: 62 | - ip flow ingress 63 | - ip flow egress 64 | parents: interface GigabitEthernet0/3 65 | provider: "{{ cli }}" 66 | 67 | - name: configure flow export on Gi0/4 68 | ios_config: 69 | lines: 70 | - ip flow ingress 71 | - ip flow egress 72 | parents: interface GigabitEthernet0/4 73 | provider: "{{ cli }}" 74 | 75 | 76 | -------------------------------------------------------------------------------- /Chapter06/access_list_nxosv.yml: -------------------------------------------------------------------------------- 1 | --- 2 | - name: Configure Access List 3 | hosts: "nxosv-devices" 4 | gather_facts: false 5 | connection: local 6 | 7 | vars: 8 | cli: 9 | host: "{{ ansible_host }}" 10 | username: "{{ ansible_username }}" 11 | password: "{{ ansible_password }}" 12 | transport: cli 13 | 14 | tasks: 15 | - nxos_acl: 16 | name: border_inbound 17 | seq: 20 18 | action: deny 19 | proto: tcp 20 | src: 172.16.0.0/12 21 | dest: any 22 | log: enable 23 | state: present 24 | provider: "{{ cli }}" 25 | - nxos_acl: 26 | name: border_inbound 27 | seq: 30 28 | action: deny 29 | proto: tcp 30 | src: 192.168.0.0/16 31 | dest: any 32 | state: present 33 | log: enable 34 | provider: "{{ cli }}" 35 | - nxos_acl: 36 | name: border_inbound 37 | seq: 40 38 | action: permit 39 | proto: tcp 40 | src: any 41 | dest: 10.0.0.14/32 42 | dest_port_op: eq 43 | dest_port1: 22 44 | state: present 45 | log: enable 46 | provider: "{{ cli }}" 47 | - nxos_acl: 48 | name: border_inbound 49 | seq: 50 50 | action: permit 51 | proto: tcp 52 | src: any 53 | dest: 10.0.0.14/32 54 | dest_port_op: eq 55 | dest_port1: 80 56 | state: present 57 | log: enable 58 | provider: "{{ cli }}" 59 | - nxos_acl: 60 | name: border_inbound 61 | seq: 60 62 | action: permit 63 | proto: tcp 64 | src: any 65 | dest: any 66 | state: present 67 | log: enable 68 | established: enable 69 | provider: "{{ cli }}" 70 | - nxos_acl: 71 | name: border_inbound 72 | seq: 1000 73 | action: deny 74 | proto: ip 75 | src: any 76 | dest: any 77 | state: present 78 | log: enable 79 | provider: "{{ cli }}" 80 | - name: apply ingress acl to Ethernet 2/2 81 | nxos_acl_interface: 82 | name: border_inbound 83 | interface: Ethernet2/2 84 | direction: ingress 85 | state: present 86 | provider: "{{ cli }}" 87 | 88 | 89 | 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | 49 | # Python gitignore 50 | 51 | # Byte-compiled / optimized / DLL files 52 | __pycache__/ 53 | *.py[cod] 54 | *$py.class 55 | 56 | # C extensions 57 | *.so 58 | 59 | # Distribution / packaging 60 | .Python 61 | env/ 62 | build/ 63 | develop-eggs/ 64 | dist/ 65 | downloads/ 66 | eggs/ 67 | .eggs/ 68 | lib/ 69 | lib64/ 70 | parts/ 71 | sdist/ 72 | var/ 73 | wheels/ 74 | *.egg-info/ 75 | .installed.cfg 76 | *.egg 77 | 78 | # PyInstaller 79 | # Usually these files are written by a python script from a template 80 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 81 | *.manifest 82 | *.spec 83 | 84 | # Installer logs 85 | pip-log.txt 86 | pip-delete-this-directory.txt 87 | 88 | # Unit test / coverage reports 89 | htmlcov/ 90 | .tox/ 91 | .coverage 92 | .coverage.* 93 | .cache 94 | nosetests.xml 95 | coverage.xml 96 | *,cover 97 | .hypothesis/ 98 | 99 | # Translations 100 | *.mo 101 | *.pot 102 | 103 | # Django stuff: 104 | *.log 105 | local_settings.py 106 | 107 | # Flask stuff: 108 | instance/ 109 | .webassets-cache 110 | 111 | # Scrapy stuff: 112 | .scrapy 113 | 114 | # Sphinx documentation 115 | docs/_build/ 116 | 117 | # PyBuilder 118 | target/ 119 | 120 | # Jupyter Notebook 121 | .ipynb_checkpoints 122 | 123 | # pyenv 124 | .python-version 125 | 126 | # celery beat schedule file 127 | celerybeat-schedule 128 | 129 | # SageMath parsed files 130 | *.sage.py 131 | 132 | # dotenv 133 | .env 134 | 135 | # virtualenv 136 | .venv 137 | venv/ 138 | ENV/ 139 | 140 | # Spyder project settings 141 | .spyderproject 142 | 143 | # Rope project settings 144 | .ropeproject 145 | 146 | -------------------------------------------------------------------------------- /Chapter08/netFlow_v5_parser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Modified from Brian "devicenull" Rak's example on: 4 | # http://blog.devicenull.org/2013/09/04/python-netflow-v5-parser.html 5 | # 6 | 7 | from __future__ import print_function 8 | import socket, struct 9 | from socket import inet_ntoa 10 | 11 | SIZE_OF_HEADER = 24 12 | SIZE_OF_RECORD = 48 13 | 14 | sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 15 | sock.bind(('0.0.0.0', 9995)) 16 | 17 | while True: 18 | buf, addr = sock.recvfrom(1500) 19 | 20 | (version, count) = struct.unpack('!HH',buf[0:4]) 21 | (sys_uptime, unix_secs, unix_nsecs, flow_sequence) = struct.unpack('!IIII', buf[4:20]) 22 | (engine_type, engine_id, sampling_interval) = struct.unpack('!BBH', buf[20:24]) 23 | print( "Headers: ", 24 | "\nNetFlow Version: " + str(version), 25 | "\nFlow Count: " + str(count), 26 | "\nSystem Uptime: " + str(sys_uptime), 27 | "\nEpoch Time in seconds: " + str(unix_secs), 28 | "\nEpoch Time in nanoseconds: " + str(unix_nsecs), 29 | "\nSequence counter of total flow: " + str(flow_sequence), 30 | ) 31 | 32 | #Can also use socket.htohl() to convert network to host byte order 33 | #uptime = socket.ntohl(struct.unpack('I',buf[4:8])[0]) 34 | #epochseconds = socket.ntohl(struct.unpack('I',buf[8:12])[0]) 35 | 36 | #Flowdata 37 | nfdata = {} 38 | for i in range(0, count): 39 | try: 40 | base = SIZE_OF_HEADER+(i*SIZE_OF_RECORD) 41 | 42 | data = struct.unpack('!IIIIHH',buf[base+16:base+36]) 43 | input_int, output_int = struct.unpack('!HH', buf[base+12:base+16]) 44 | nfdata[i] = {} 45 | nfdata[i]['saddr'] = inet_ntoa(buf[base+0:base+4]) 46 | nfdata[i]['daddr'] = inet_ntoa(buf[base+4:base+8]) 47 | nfdata[i]['pcount'] = data[0] 48 | nfdata[i]['bcount'] = data[1] 49 | nfdata[i]['stime'] = data[2] 50 | nfdata[i]['etime'] = data[3] 51 | nfdata[i]['sport'] = data[4] 52 | nfdata[i]['dport'] = data[5] 53 | print(i, " {0}:{1} -> {2}:{3} {4} packts {5} bytes".format( 54 | nfdata[i]['saddr'], 55 | nfdata[i]['sport'], 56 | nfdata[i]['daddr'], 57 | nfdata[i]['dport'], 58 | nfdata[i]['pcount'], 59 | nfdata[i]['bcount']), 60 | ) 61 | 62 | except: 63 | print("Failed to parse flow record: " + str(i)) 64 | continue 65 | 66 | print("*" * 10) 67 | -------------------------------------------------------------------------------- /Chapter09/chapter9_6.py: -------------------------------------------------------------------------------- 1 | # This example referenced Miguel Grinberg's code on Github: 2 | # https://github.com/miguelgrinberg/oreilly-flask-apis-video/commit/98855d48f52f4dc0f9728c841bdd0645810d708e 3 | # 4 | 5 | from flask import Flask, url_for, jsonify, request 6 | from flask.ext.sqlalchemy import SQLAlchemy 7 | 8 | app = Flask(__name__) 9 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///network.db' 10 | db = SQLAlchemy(app) 11 | 12 | class ValidationError(ValueError): 13 | pass 14 | 15 | 16 | class Device(db.Model): 17 | __tablename__ = 'devices' 18 | id = db.Column(db.Integer, primary_key=True) 19 | hostname = db.Column(db.String(64), unique=True) 20 | loopback = db.Column(db.String(120), unique=True) 21 | mgmt_ip = db.Column(db.String(120), unique=True) 22 | role = db.Column(db.String(64)) 23 | vendor = db.Column(db.String(64)) 24 | os = db.Column(db.String(64)) 25 | 26 | def get_url(self): 27 | return url_for('get_device', id=self.id, _external=True) 28 | 29 | def export_data(self): 30 | return { 31 | 'self_url': self.get_url(), 32 | 'hostname': self.hostname, 33 | 'loopback': self.loopback, 34 | 'mgmt_ip': self.mgmt_ip, 35 | 'role': self.role, 36 | 'vendor': self.vendor, 37 | 'os': self.os 38 | } 39 | 40 | def import_data(self, data): 41 | try: 42 | self.hostname = data['hostname'] 43 | self.loopback = data['loopback'] 44 | self.mgmt_ip = data['mgmt_ip'] 45 | self.role = data['role'] 46 | self.vendor = data['vendor'] 47 | self.os = data['os'] 48 | except KeyError as e: 49 | raise ValidationError('Invalid device: missing ' + e.args[0]) 50 | return self 51 | 52 | 53 | @app.route('/devices/', methods=['GET']) 54 | def get_devices(): 55 | return jsonify({'device': [device.get_url() 56 | for device in Device.query.all()]}) 57 | 58 | @app.route('/devices/', methods=['GET']) 59 | def get_device(id): 60 | return jsonify(Device.query.get_or_404(id).export_data()) 61 | 62 | @app.route('/devices/', methods=['POST']) 63 | def new_device(): 64 | device = Device() 65 | device.import_data(request.json) 66 | db.session.add(device) 67 | db.session.commit() 68 | return jsonify({}), 201, {'Location': device.get_url()} 69 | 70 | @app.route('/devices/', methods=['PUT']) 71 | def edit_device(id): 72 | device = Device.query.get_or_404(id) 73 | device.import_data(request.json) 74 | db.session.add(device) 75 | db.session.commit() 76 | return jsonify({}) 77 | 78 | 79 | if __name__ == '__main__': 80 | db.create_all() 81 | app.run(host='0.0.0.0', debug=True) 82 | 83 | 84 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /Chapter05/Vaults/host_vars/localhost: -------------------------------------------------------------------------------- 1 | $ANSIBLE_VAULT;1.1;AES256 2 | 66366365366233396161616631653930616330303834343665613237653733353939326238313035 3 | 3065643838646562386239666336323233313262663538350a656135303031313239353731626234 4 | 30656432326130613566353364393936636135633335303332363031653964356563393235373032 5 | 6130363661393330650a303163666637333766363463393766613862626665376338353661663464 6 | 35373765313730366330383036366233316335363165333163623330643262306135323834623736 7 | 61613337323836623364653631316533383236643865643435633365633731633836353130643633 8 | 39633034356638396530643232323235613931663136343630636261306335653638366133636534 9 | 31663038303230623336333537326338326330313866656434663736336330623361653239623264 10 | 34336465636366363533373533306564343437633733613439613537633730303435393436303931 11 | 66393164306362646562326564393036653764653962363531333331616563663264383965306234 12 | 39636361353536373637613233353864633938346361386430396639313431323534666362373633 13 | 31633664306538353665323933656237663734633461656363393638343430646132646638356334 14 | 65313535356432386263616334393531386464346564663230363531373431346565343265333736 15 | 64343964346332346133633236613065336565363134633139303137333535353562383139303437 16 | 38626262336338313437303563323634366430626438303733386133373134336632643661303636 17 | 66326639383539363239663136613166366232383131363032663263663639336237663735623530 18 | 36333161356233363261326266363631303237633037623161323738383763323631333030366135 19 | 34313032656662636536636134363436663438376164623031323330626638656234303665626537 20 | 33396330613333363763306131393365386131663638373165663031356133366530326464373062 21 | 62386130636262666164323331666535386266663863633239343330393738393235383830346130 22 | 35613738363464343866323638303432306638313031663663623266396533613634306561363330 23 | 62393534633839656262653635363332653033623730313936366236663161376139616237636464 24 | 63353136613332313465666635643962336464376565383065363061363961353735636666316134 25 | 66316435663434663764333433316331336335613732633465383537393436356335623064636335 26 | 35353162643964373562356262366536336139343537336536323435623036363366306538386236 27 | 66346631666364353661616261346133623061386134376330353433363961396636656433623563 28 | 39343066323539313035396537616634643430636163646562306332656266613466336638303264 29 | 36333732613531646135666637323966616435303462303031323562393364393832373166653038 30 | 38646532623734353732366164333263636133646431393931356430313837393966396433323230 31 | 35323833333231373365396164623233313632663166366231373037363637623336666632363266 32 | 63306534643138343961353435393234346235666465616438623533313862313536653962333064 33 | 35663833646263343766313131306230663666393732323134613730663436393066356534616461 34 | 37326236656339373735323738633239356431636538663236616130653964626437373061303735 35 | 3561356666653666316639633831333564393438316237356330 36 | -------------------------------------------------------------------------------- /Chapter13/chapter13_jsonrpc_1.py: -------------------------------------------------------------------------------- 1 | # Based on ryu/services/protocols/bgp/api/jsonrpc.py 2 | 3 | from ryu.base import app_manager 4 | from ryu.lib import hub 5 | from ryu.app.wsgi import websocket, ControllerBase, WSGIApplication 6 | from ryu.app.wsgi import rpc_public, WebSocketRPCServer 7 | from ryu.services.protocols.bgp.api.base import call 8 | from ryu.services.protocols.bgp.api.base import PREFIX 9 | from ryu.services.protocols.bgp.rtconf.common import LOCAL_AS 10 | from ryu.services.protocols.bgp.rtconf.common import ROUTER_ID 11 | from ryu.services.protocols.bgp.rtconf import neighbors 12 | 13 | 14 | bgp_instance_name = 'bgp_api_app' 15 | url = '/bgp/ws' 16 | 17 | 18 | class BgpWSJsonRpc(app_manager.RyuApp): 19 | _CONTEXTS = { 20 | 'wsgi': WSGIApplication, 21 | } 22 | 23 | def __init__(self, *args, **kwargs): 24 | super(BgpWSJsonRpc, self).__init__(*args, **kwargs) 25 | 26 | wsgi = kwargs['wsgi'] 27 | wsgi.register( 28 | BgpWSJsonRpcController, 29 | data={bgp_instance_name: self}, 30 | ) 31 | self._ws_manager = wsgi.websocketmanager 32 | 33 | @rpc_public('core.start') 34 | def _core_start(self, as_number=64512, router_id='10.0.0.1'): 35 | common_settings = {} 36 | common_settings[LOCAL_AS] = as_number 37 | common_settings[ROUTER_ID] = str(router_id) 38 | waiter = hub.Event() 39 | call('core.start', waiter=waiter, **common_settings) 40 | waiter.wait() 41 | return {} 42 | 43 | @rpc_public('neighbor.create') 44 | def _neighbor_create(self, ip_address='192.168.177.32', 45 | remote_as=64513, is_route_reflector_client=False): 46 | bgp_neighbor = {} 47 | bgp_neighbor[neighbors.IP_ADDRESS] = str(ip_address) 48 | bgp_neighbor[neighbors.REMOTE_AS] = remote_as 49 | bgp_neighbor[neighbors.IS_ROUTE_REFLECTOR_CLIENT] = bool(is_route_reflector_client) 50 | call('neighbor.create', **bgp_neighbor) 51 | return {} 52 | 53 | @rpc_public('network.add') 54 | def _prefix_add(self, prefix='10.20.0.0/24'): 55 | networks = {} 56 | networks[PREFIX] = str(prefix) 57 | call('network.add', **networks) 58 | return {} 59 | 60 | @rpc_public('neighbors.get') 61 | def _neighbors_get(self): 62 | return call('neighbors.get') 63 | 64 | @rpc_public('show.rib') 65 | def _show_rib(self, family='ipv4'): 66 | show = {} 67 | show['params'] = ['rib', family] 68 | return call('operator.show', **show) 69 | 70 | 71 | class BgpWSJsonRpcController(ControllerBase): 72 | def __init__(self, req, link, data, **config): 73 | super(BgpWSJsonRpcController, self).__init__( 74 | req, link, data, **config) 75 | self.bgp_api_app = data[bgp_instance_name] 76 | 77 | @websocket('bgp', url) 78 | def _websocket_handler(self, ws): 79 | rpc_server = WebSocketRPCServer(ws, self.bgp_api_app) 80 | rpc_server.serve_forever() 81 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Mastering Python Networking 5 | This is the code repository for [Mastering Python Networking](https://www.packtpub.com/networking-and-servers/mastering-python-networking?utm_source=github&utm_medium=repository&utm_campaign=9781784397005), published by [Packt](https://www.packtpub.com/?utm_source=github). It contains all the supporting project files necessary to work through the book from start to finish. 6 | ## About the Book 7 | This book begins with a review of the TCP/ IP protocol suite and a refresher of the core elements of the Python language. Next, you will start using Python and supported libraries to automate network tasks from the current major network vendors. We will look at automating traditional network devices based on the command-line interface, as well as newer devices with API support, with hands-on labs. We will then learn the concepts and practical use cases of the Ansible framework in order to achieve your network goals. 8 | 9 | ## Errata 10 | * Page 42 (2nd bullet point): **It is also the popular kid on the bock** _should be_ **It is also the popular kid on the block** 11 | 12 | ## Instructions and Navigation 13 | All of the code is organized into folders. Each folder starts with a number followed by the application name. For example, Chapter02. 14 | 15 | Chapter 12 does not have any code files. 16 | 17 | The code will look like the following: 18 | ``` 19 | # This is a comment 20 | print("hello world") 21 | ``` 22 | 23 | It is strongly recommended that you follow and practice the examples given in this book. To complete the examples, you will need a host machine that runs Python 2.7 and 3.4, with enough administrative permissions to install the tools introduced in the book. The host machine can be a virtual machine and should preferably run a flavor of Linux. In the book, we'll use Ubuntu 16.04, but other Linux distributions should work as well. You might need to tweak your settings accordingly. In addition, either physical or virtual network equipment is needed to test your code. 24 | 25 | ## Related Products 26 | * [Python: Master the Art of Design Patterns](https://www.packtpub.com/application-development/python-master-art-design-patterns?utm_source=github&utm_medium=repository&utm_campaign=9781787125186) 27 | 28 | * [Neural Network Programming with Python](https://www.packtpub.com/big-data-and-business-intelligence/neural-network-programming-python?utm_source=github&utm_medium=repository&utm_campaign=9781784398217) 29 | 30 | * [Mastering Python - Second Edition [Video]](https://www.packtpub.com/application-development/mastering-python-second-edition-video?utm_source=github&utm_medium=repository&utm_campaign=9781786463746) 31 | 32 | ### Download a free PDF 33 | 34 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
35 |

https://packt.link/free-ebook/9781784397005

-------------------------------------------------------------------------------- /Chapter09/chapter9_7.py: -------------------------------------------------------------------------------- 1 | # This example referenced Miguel Grinberg's code on Github: 2 | # https://github.com/miguelgrinberg/oreilly-flask-apis-video/commit/98855d48f52f4dc0f9728c841bdd0645810d708e 3 | # 4 | 5 | from flask import Flask, url_for, jsonify, request 6 | from flask.ext.sqlalchemy import SQLAlchemy 7 | from chapter9_pexpect_1 import show_version 8 | 9 | app = Flask(__name__) 10 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///network.db' 11 | db = SQLAlchemy(app) 12 | 13 | class ValidationError(ValueError): 14 | pass 15 | 16 | 17 | class Device(db.Model): 18 | __tablename__ = 'devices' 19 | id = db.Column(db.Integer, primary_key=True) 20 | hostname = db.Column(db.String(64), unique=True) 21 | loopback = db.Column(db.String(120), unique=True) 22 | mgmt_ip = db.Column(db.String(120), unique=True) 23 | role = db.Column(db.String(64)) 24 | vendor = db.Column(db.String(64)) 25 | os = db.Column(db.String(64)) 26 | 27 | def get_url(self): 28 | return url_for('get_device', id=self.id, _external=True) 29 | 30 | def export_data(self): 31 | return { 32 | 'self_url': self.get_url(), 33 | 'hostname': self.hostname, 34 | 'loopback': self.loopback, 35 | 'mgmt_ip': self.mgmt_ip, 36 | 'role': self.role, 37 | 'vendor': self.vendor, 38 | 'os': self.os 39 | } 40 | 41 | def import_data(self, data): 42 | try: 43 | self.hostname = data['hostname'] 44 | self.loopback = data['loopback'] 45 | self.mgmt_ip = data['mgmt_ip'] 46 | self.role = data['role'] 47 | self.vendor = data['vendor'] 48 | self.os = data['os'] 49 | except KeyError as e: 50 | raise ValidationError('Invalid device: missing ' + e.args[0]) 51 | return self 52 | 53 | 54 | @app.route('/devices/', methods=['GET']) 55 | def get_devices(): 56 | return jsonify({'device': [device.get_url() 57 | for device in Device.query.all()]}) 58 | 59 | @app.route('/devices/', methods=['GET']) 60 | def get_device(id): 61 | return jsonify(Device.query.get_or_404(id).export_data()) 62 | 63 | 64 | @app.route('/devices//version', methods=['GET']) 65 | def get_device_version(id): 66 | device = Device.query.get_or_404(id) 67 | hostname = device.hostname 68 | ip = device.mgmt_ip 69 | prompt = hostname+"#" 70 | result = show_version(hostname, prompt, ip, 'cisco', 'cisco') 71 | return jsonify({"version": str(result)}) 72 | 73 | @app.route('/devices//version', methods=['GET']) 74 | def get_role_version(device_role): 75 | device_id_list = [device.id for device in Device.query.all() if device.role == device_role] 76 | result = {} 77 | for id in device_id_list: 78 | device = Device.query.get_or_404(id) 79 | hostname = device.hostname 80 | ip = device.mgmt_ip 81 | prompt = hostname + "#" 82 | device_result = show_version(hostname, prompt, ip, 'cisco', 'cisco') 83 | result[hostname] = str(device_result) 84 | return jsonify(result) 85 | 86 | @app.route('/devices/', methods=['POST']) 87 | def new_device(): 88 | device = Device() 89 | device.import_data(request.json) 90 | db.session.add(device) 91 | db.session.commit() 92 | return jsonify({}), 201, {'Location': device.get_url()} 93 | 94 | @app.route('/devices/', methods=['PUT']) 95 | def edit_device(id): 96 | device = Device.query.get_or_404(id) 97 | device.import_data(request.json) 98 | db.session.add(device) 99 | db.session.commit() 100 | return jsonify({}) 101 | 102 | 103 | if __name__ == '__main__': 104 | db.create_all() 105 | app.run(host='0.0.0.0', debug=True) 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /Chapter11/Chapter11_2.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_3 6 | from ryu.lib.packet import packet 7 | import array 8 | from ryu.lib.packet import ethernet 9 | from ryu.lib.packet import ether_types 10 | from ryu.lib.packet import ipv4, tcp 11 | 12 | 13 | class SimpleSwitch13(app_manager.RyuApp): 14 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 15 | 16 | def __init__(self, *args, **kwargs): 17 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 18 | self.mac_to_port = {} 19 | 20 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 21 | def switch_features_handler(self, ev): 22 | datapath = ev.msg.datapath 23 | ofproto = datapath.ofproto 24 | parser = datapath.ofproto_parser 25 | 26 | match = parser.OFPMatch() 27 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 28 | ofproto.OFPCML_NO_BUFFER)] 29 | self.add_flow(datapath, 0, match, actions) 30 | 31 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 32 | ofproto = datapath.ofproto 33 | parser = datapath.ofproto_parser 34 | 35 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 36 | actions)] 37 | if buffer_id: 38 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 39 | priority=priority, match=match, 40 | instructions=inst) 41 | else: 42 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 43 | match=match, instructions=inst) 44 | datapath.send_msg(mod) 45 | 46 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 47 | def _packet_in_handler(self, ev): 48 | 49 | # This is where we decode and print out the packet 50 | print("msg.data: {}".format(array.array('B', ev.msg.data))) 51 | pkt = packet.Packet(ev.msg.data) 52 | for p in pkt.protocols: 53 | print(p.protocol_name, p) 54 | if p.protocol_name == 'ipv4': 55 | print('IP: src {} dst {}'.format(p.src, p.dst)) 56 | 57 | # Below is the original code from simple_switch_13.py 58 | msg = ev.msg 59 | datapath = msg.datapath 60 | ofproto = datapath.ofproto 61 | parser = datapath.ofproto_parser 62 | in_port = msg.match['in_port'] 63 | 64 | pkt = packet.Packet(msg.data) 65 | eth = pkt.get_protocols(ethernet.ethernet)[0] 66 | 67 | if eth.ethertype == ether_types.ETH_TYPE_LLDP: 68 | # ignore lldp packet 69 | return 70 | dst = eth.dst 71 | src = eth.src 72 | 73 | dpid = datapath.id 74 | self.mac_to_port.setdefault(dpid, {}) 75 | 76 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 77 | 78 | # learn a mac address to avoid FLOOD next time. 79 | self.mac_to_port[dpid][src] = in_port 80 | 81 | if dst in self.mac_to_port[dpid]: 82 | out_port = self.mac_to_port[dpid][dst] 83 | else: 84 | out_port = ofproto.OFPP_FLOOD 85 | 86 | actions = [parser.OFPActionOutput(out_port)] 87 | 88 | # install a flow to avoid packet_in next time 89 | if out_port != ofproto.OFPP_FLOOD: 90 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst) 91 | # verify if we have a valid buffer_id, if yes avoid to send both 92 | # flow_mod & packet_out 93 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 94 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 95 | return 96 | else: 97 | self.add_flow(datapath, 1, match, actions) 98 | data = None 99 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 100 | data = msg.data 101 | 102 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 103 | in_port=in_port, actions=actions, data=data) 104 | datapath.send_msg(out) 105 | 106 | -------------------------------------------------------------------------------- /Chapter03/Arista/eapi_2_acl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright (c) 2014 Arista Networks 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | 23 | import argparse 24 | import jsonrpclib 25 | import os 26 | import sys 27 | import subprocess 28 | 29 | import ssl 30 | ssl._create_default_https_context = ssl._create_unverified_context 31 | 32 | # EAPI script to remotely edit an access list across multiple 33 | # Arista switches using your editor of choice. 34 | 35 | # From a central server with IP connectivity to your switch, run this 36 | # script and specify an ACL name and a series of switches you are 37 | # interested in editing. This then opens your $EDITOR (e.g. vi or emacs) 38 | # with the contents of the named ACL. When you're finished, close the file 39 | # and this script will update that ACL across all of the switches you specified. 40 | # No more dealing with annoying line numbers in the CLI! 41 | 42 | def main(): 43 | parser = argparse.ArgumentParser(description="Edit Arista ACLs using your local editor") 44 | parser.add_argument("acl", metavar="ACL", 45 | help="Name of the access list to modify") 46 | parser.add_argument("switches", metavar="SWITCH", nargs="+", 47 | help="Hostname or IP of the switch to query") 48 | parser.add_argument("--username", help="Name of the user to connect as", 49 | default="admin") 50 | parser.add_argument("--password", help="The user's password") 51 | parser.add_argument("--https", help="Use HTTPS instead of HTTP", 52 | action="store_const", const="https", default="http") 53 | args = parser.parse_args() 54 | 55 | aclName = args.acl 56 | tmpfile = "/tmp/AclEditor-%s" % aclName 57 | apiEndpoints = getEndpoints(args.switches, args.https, 58 | args.username, args.password) 59 | prepopulateAclFile(tmpfile, aclName, apiEndpoints) 60 | edits = getEdits(tmpfile) 61 | applyChanges(aclName, apiEndpoints, edits) 62 | print 63 | print "Done!" 64 | 65 | def getEndpoints(switchHostnames, protocol, username, password): 66 | """ Check that each server is up, and return a mapping from 67 | hostname to jsonrpclib.Server """ 68 | apiEndpoints = {} # mapping from hostname to the API endpoint 69 | for switch in switchHostnames: 70 | url = "{protocol}://{user}:{pw}@{hostname}/command-api".format( 71 | protocol=protocol, user=username, pw=password, hostname=switch) 72 | server = jsonrpclib.Server(url) 73 | try: 74 | # We should at least be able to 'enable' 75 | server.runCmds(1, ["enable"]) 76 | except Exception as e: 77 | print "Unable to run 'enable' on switch", e 78 | sys.exit(1) 79 | apiEndpoints[switch] = server 80 | return apiEndpoints 81 | 82 | def prepopulateAclFile(filename, aclName, apiEndpoints): 83 | """ Given a jsonrpclib.Server called 'switch', prepopulate 84 | 'filename' with the ACL contents. If the ACL does not yet exist, 85 | just print a message """ 86 | 87 | # Currently assume all switches have the same config, so just use a 88 | # random one as the sample. 89 | apiEndpoint = apiEndpoints.itervalues().next() 90 | responseList = apiEndpoint.runCmds(1, ["enable", 91 | "show ip access-lists %s" % aclName]) 92 | response = responseList[1] # Only care about the ACL output. 93 | if not response["aclList"]: 94 | print "No existing access list named", aclName, "- creating new ACL" 95 | else: 96 | # Prepopulate the file with the existing config 97 | print "Editing existing access list:" 98 | with open(filename, "w") as f: 99 | for rule in response["aclList"][0]["sequence"]: 100 | line = str(rule["sequenceNumber"]) + " " + rule["text"] + "\n" 101 | print " ", line, 102 | f.write(line) 103 | print 104 | 105 | def getEdits(filename): 106 | """ Opens an editor for the user to edit the ACL, and returns a 107 | list of the new ACL contents """ 108 | editor = os.environ.get("EDITOR", "vi") # default editor is "vi" 109 | ret = subprocess.Popen([editor, filename]).wait() 110 | if ret != 0: 111 | print "Bad editor exit. Aborting." 112 | sys.exit(1) 113 | # Read in the file as a list of lines 114 | aclContents = open(filename, "r").readlines() 115 | print "New access list:" 116 | print " ", " ".join(aclContents) 117 | print 118 | return aclContents 119 | 120 | def applyChanges(aclName, apiEndpoints, aclRules): 121 | """ Given the switch mapping and a list of the new ACL rules, apply 122 | the ACL to each switch """ 123 | cmdList = ["enable", 124 | "configure", 125 | # Not the most efficient way to clear an ACL: 126 | "no ip access-list %s" % aclName, 127 | # Now enter configuration mode for the ACL: 128 | "ip access-list %s" % aclName] 129 | cmdList = cmdList + aclRules + ["exit"] 130 | 131 | for hostname, apiEndpoint in apiEndpoints.iteritems(): 132 | print "Updating access list on switch", hostname, "....", 133 | try: 134 | apiEndpoint.runCmds(1, cmdList) 135 | except jsonrpclib.ProtocolError as e: 136 | print "[ERROR]" 137 | print " ", e 138 | # jsonrpclib isn't very friendly at getting the error data as 139 | # specified by the spec. This is a shortcut for getting the 140 | # last error: 141 | errorResponse = jsonrpclib.loads(jsonrpclib.history.response) 142 | print " Details:", errorResponse["error"]["data"][-1]["errors"] 143 | else: 144 | print "[SUCCESS]" 145 | 146 | if __name__ == "__main__": 147 | main() 148 | 149 | -------------------------------------------------------------------------------- /Chapter13/chapter13_switch_1.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | from ryu.base import app_manager 17 | from ryu.controller import ofp_event 18 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 19 | from ryu.controller.handler import set_ev_cls 20 | from ryu.ofproto import ofproto_v1_3 21 | from ryu.lib.packet import packet 22 | from ryu.lib.packet import ethernet 23 | 24 | # new import 25 | from ryu.controller import dpset 26 | from ryu.controller.handler import HANDSHAKE_DISPATCHER 27 | import random 28 | 29 | 30 | class SimpleSwitch13(app_manager.RyuApp): 31 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 32 | 33 | def __init__(self, *args, **kwargs): 34 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 35 | self.mac_to_port = {} 36 | self.gen_id = 0 37 | self.role_string_list = ['nochange', 'equal', 'master', 'slave', 'unknown'] 38 | 39 | 40 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 41 | def switch_features_handler(self, ev): 42 | datapath = ev.msg.datapath 43 | ofproto = datapath.ofproto 44 | parser = datapath.ofproto_parser 45 | 46 | # install table-miss flow entry 47 | # 48 | # We specify NO BUFFER to max_len of the output action due to 49 | # OVS bug. At this moment, if we specify a lesser number, e.g., 50 | # 128, OVS will send Packet-In with invalid buffer_id and 51 | # truncated packet data. In that case, we cannot output packets 52 | # correctly. The bug has been fixed in OVS v2.1.0. 53 | match = parser.OFPMatch() 54 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 55 | ofproto.OFPCML_NO_BUFFER)] 56 | self.add_flow(datapath, 0, match, actions) 57 | 58 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 59 | ofproto = datapath.ofproto 60 | parser = datapath.ofproto_parser 61 | 62 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 63 | actions)] 64 | if buffer_id: 65 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 66 | priority=priority, match=match, 67 | instructions=inst) 68 | else: 69 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 70 | match=match, instructions=inst) 71 | datapath.send_msg(mod) 72 | 73 | 74 | @set_ev_cls(ofp_event.EventOFPErrorMsg, 75 | [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER]) 76 | def on_error_msg(self, ev): 77 | msg = ev.msg 78 | print 'receive a error message: %s' % (msg) 79 | 80 | 81 | @set_ev_cls(dpset.EventDP, MAIN_DISPATCHER) 82 | def on_dp_change(self, ev): 83 | if ev.enter: 84 | dp = ev.dp 85 | dpid = dp.id 86 | ofp = dp.ofproto 87 | ofp_parser = dp.ofproto_parser 88 | 89 | print 'dp entered, id is %s' % (dpid) 90 | self.send_role_request(dp, ofp.OFPCR_ROLE_MASTER, self.gen_id) 91 | 92 | 93 | @set_ev_cls(ofp_event.EventOFPRoleReply, MAIN_DISPATCHER) 94 | def on_role_reply(self, ev): 95 | msg = ev.msg 96 | dp = msg.datapath 97 | ofp = dp.ofproto 98 | role = msg.role 99 | 100 | # unknown role 101 | if role < 0 or role > 3: 102 | role = 4 103 | print '' 104 | print 'get a role reply: %s, generation: %d' % (self.role_string_list[role], msg.generation_id) 105 | 106 | 107 | def send_role_request(self, datapath, role, gen_id): 108 | ofp_parser = datapath.ofproto_parser 109 | print 'send a role change request' 110 | print 'role: %s, gen_id: %d' % (self.role_string_list[role], gen_id) 111 | msg = ofp_parser.OFPRoleRequest(datapath, role, gen_id) 112 | datapath.send_msg(msg) 113 | 114 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 115 | def _packet_in_handler(self, ev): 116 | # If you hit this you might want to increase 117 | # the "miss_send_length" of your switch 118 | if ev.msg.msg_len < ev.msg.total_len: 119 | self.logger.debug("packet truncated: only %s of %s bytes", 120 | ev.msg.msg_len, ev.msg.total_len) 121 | msg = ev.msg 122 | datapath = msg.datapath 123 | ofproto = datapath.ofproto 124 | parser = datapath.ofproto_parser 125 | in_port = msg.match['in_port'] 126 | 127 | pkt = packet.Packet(msg.data) 128 | eth = pkt.get_protocols(ethernet.ethernet)[0] 129 | 130 | dst = eth.dst 131 | src = eth.src 132 | 133 | dpid = datapath.id 134 | self.mac_to_port.setdefault(dpid, {}) 135 | 136 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 137 | 138 | # learn a mac address to avoid FLOOD next time. 139 | self.mac_to_port[dpid][src] = in_port 140 | 141 | if dst in self.mac_to_port[dpid]: 142 | out_port = self.mac_to_port[dpid][dst] 143 | else: 144 | out_port = ofproto.OFPP_FLOOD 145 | 146 | actions = [parser.OFPActionOutput(out_port)] 147 | 148 | # install a flow to avoid packet_in next time 149 | if out_port != ofproto.OFPP_FLOOD: 150 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst) 151 | # verify if we have a valid buffer_id, if yes avoid to send both 152 | # flow_mod & packet_out 153 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 154 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 155 | return 156 | else: 157 | self.add_flow(datapath, 1, match, actions) 158 | data = None 159 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 160 | data = msg.data 161 | 162 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 163 | in_port=in_port, actions=actions, data=data) 164 | datapath.send_msg(out) 165 | -------------------------------------------------------------------------------- /Chapter13/chapter13_switch_2.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2011 Nippon Telegraph and Telephone Corporation. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # OFPRoleRequest code referenced from 17 | # https://github.com/TakeshiTseng/SDN-Work/blob/master/MultiControl/ModStatusApp.py 18 | 19 | from ryu.base import app_manager 20 | from ryu.controller import ofp_event 21 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 22 | from ryu.controller.handler import set_ev_cls 23 | from ryu.ofproto import ofproto_v1_3 24 | from ryu.lib.packet import packet 25 | from ryu.lib.packet import ethernet 26 | 27 | # new import 28 | from ryu.controller import dpset 29 | from ryu.controller.handler import HANDSHAKE_DISPATCHER 30 | import random 31 | 32 | 33 | class SimpleSwitch13(app_manager.RyuApp): 34 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 35 | 36 | def __init__(self, *args, **kwargs): 37 | super(SimpleSwitch13, self).__init__(*args, **kwargs) 38 | self.mac_to_port = {} 39 | self.gen_id = 0 40 | self.role_string_list = ['nochange', 'equal', 'master', 'slave', 'unknown'] 41 | 42 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 43 | def switch_features_handler(self, ev): 44 | datapath = ev.msg.datapath 45 | ofproto = datapath.ofproto 46 | parser = datapath.ofproto_parser 47 | 48 | 49 | # install table-miss flow entry 50 | # 51 | # We specify NO BUFFER to max_len of the output action due to 52 | # OVS bug. At this moment, if we specify a lesser number, e.g., 53 | # 128, OVS will send Packet-In with invalid buffer_id and 54 | # truncated packet data. In that case, we cannot output packets 55 | # correctly. The bug has been fixed in OVS v2.1.0. 56 | match = parser.OFPMatch() 57 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 58 | ofproto.OFPCML_NO_BUFFER)] 59 | self.add_flow(datapath, 0, match, actions) 60 | 61 | 62 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 63 | ofproto = datapath.ofproto 64 | parser = datapath.ofproto_parser 65 | 66 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 67 | actions)] 68 | if buffer_id: 69 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 70 | priority=priority, match=match, 71 | instructions=inst) 72 | else: 73 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 74 | match=match, instructions=inst) 75 | datapath.send_msg(mod) 76 | 77 | 78 | @set_ev_cls(ofp_event.EventOFPErrorMsg, 79 | [HANDSHAKE_DISPATCHER, CONFIG_DISPATCHER, MAIN_DISPATCHER]) 80 | def on_error_msg(self, ev): 81 | msg = ev.msg 82 | print 'receive a error message: %s' % (msg) 83 | 84 | 85 | @set_ev_cls(dpset.EventDP, MAIN_DISPATCHER) 86 | def on_dp_change(self, ev): 87 | 88 | if ev.enter: 89 | dp = ev.dp 90 | dpid = dp.id 91 | ofp = dp.ofproto 92 | ofp_parser = dp.ofproto_parser 93 | 94 | print 'dp entered, id is %s' % (dpid) 95 | self.send_role_request(dp, ofp.OFPCR_ROLE_SLAVE, self.gen_id) 96 | 97 | @set_ev_cls(ofp_event.EventOFPRoleReply, MAIN_DISPATCHER) 98 | def on_role_reply(self, ev): 99 | msg = ev.msg 100 | dp = msg.datapath 101 | ofp = dp.ofproto 102 | role = msg.role 103 | 104 | # unknown role 105 | if role < 0 or role > 3: 106 | role = 4 107 | print '' 108 | print 'get a role reply: %s, generation: %d' % (self.role_string_list[role], msg.generation_id) 109 | 110 | 111 | def send_role_request(self, datapath, role, gen_id): 112 | ofp_parser = datapath.ofproto_parser 113 | print 'send a role change request' 114 | print 'role: %s, gen_id: %d' % (self.role_string_list[role], gen_id) 115 | msg = ofp_parser.OFPRoleRequest(datapath, role, gen_id) 116 | datapath.send_msg(msg) 117 | 118 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 119 | def _packet_in_handler(self, ev): 120 | # If you hit this you might want to increase 121 | # the "miss_send_length" of your switch 122 | if ev.msg.msg_len < ev.msg.total_len: 123 | self.logger.debug("packet truncated: only %s of %s bytes", 124 | ev.msg.msg_len, ev.msg.total_len) 125 | msg = ev.msg 126 | datapath = msg.datapath 127 | ofproto = datapath.ofproto 128 | parser = datapath.ofproto_parser 129 | in_port = msg.match['in_port'] 130 | 131 | pkt = packet.Packet(msg.data) 132 | eth = pkt.get_protocols(ethernet.ethernet)[0] 133 | 134 | dst = eth.dst 135 | src = eth.src 136 | 137 | dpid = datapath.id 138 | self.mac_to_port.setdefault(dpid, {}) 139 | 140 | self.logger.info("packet in %s %s %s %s", dpid, src, dst, in_port) 141 | 142 | # learn a mac address to avoid FLOOD next time. 143 | self.mac_to_port[dpid][src] = in_port 144 | 145 | if dst in self.mac_to_port[dpid]: 146 | out_port = self.mac_to_port[dpid][dst] 147 | else: 148 | out_port = ofproto.OFPP_FLOOD 149 | 150 | actions = [parser.OFPActionOutput(out_port)] 151 | 152 | # install a flow to avoid packet_in next time 153 | if out_port != ofproto.OFPP_FLOOD: 154 | match = parser.OFPMatch(in_port=in_port, eth_dst=dst) 155 | # verify if we have a valid buffer_id, if yes avoid to send both 156 | # flow_mod & packet_out 157 | if msg.buffer_id != ofproto.OFP_NO_BUFFER: 158 | self.add_flow(datapath, 1, match, actions, msg.buffer_id) 159 | return 160 | else: 161 | self.add_flow(datapath, 1, match, actions) 162 | data = None 163 | if msg.buffer_id == ofproto.OFP_NO_BUFFER: 164 | data = msg.data 165 | 166 | out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, 167 | in_port=in_port, actions=actions, data=data) 168 | datapath.send_msg(out) 169 | -------------------------------------------------------------------------------- /Chapter09/chapter9_8.py: -------------------------------------------------------------------------------- 1 | # This example referenced Miguel Grinberg's code on Github: 2 | # https://github.com/miguelgrinberg/oreilly-flask-apis-video/blob/master/camera/camera.py 3 | # 4 | 5 | from flask import Flask, url_for, jsonify, request,\ 6 | make_response, copy_current_request_context 7 | from flask.ext.sqlalchemy import SQLAlchemy 8 | from chapter9_pexpect_1 import show_version 9 | import uuid 10 | import functools 11 | from threading import Thread 12 | 13 | app = Flask(__name__) 14 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///network.db' 15 | db = SQLAlchemy(app) 16 | 17 | background_tasks = {} 18 | app.config['AUTO_DELETE_BG_TASKS'] = True 19 | 20 | 21 | class ValidationError(ValueError): 22 | pass 23 | 24 | 25 | class Device(db.Model): 26 | __tablename__ = 'devices' 27 | id = db.Column(db.Integer, primary_key=True) 28 | hostname = db.Column(db.String(64), unique=True) 29 | loopback = db.Column(db.String(120), unique=True) 30 | mgmt_ip = db.Column(db.String(120), unique=True) 31 | role = db.Column(db.String(64)) 32 | vendor = db.Column(db.String(64)) 33 | os = db.Column(db.String(64)) 34 | 35 | def get_url(self): 36 | return url_for('get_device', id=self.id, _external=True) 37 | 38 | def export_data(self): 39 | return { 40 | 'self_url': self.get_url(), 41 | 'hostname': self.hostname, 42 | 'loopback': self.loopback, 43 | 'mgmt_ip': self.mgmt_ip, 44 | 'role': self.role, 45 | 'vendor': self.vendor, 46 | 'os': self.os 47 | } 48 | 49 | def import_data(self, data): 50 | try: 51 | self.hostname = data['hostname'] 52 | self.loopback = data['loopback'] 53 | self.mgmt_ip = data['mgmt_ip'] 54 | self.role = data['role'] 55 | self.vendor = data['vendor'] 56 | self.os = data['os'] 57 | except KeyError as e: 58 | raise ValidationError('Invalid device: missing ' + e.args[0]) 59 | return self 60 | 61 | 62 | def background(f): 63 | """Decorator that runs the wrapped function as a background task. It is 64 | assumed that this function creates a new resource, and takes a long time 65 | to do so. The response has status code 202 Accepted and includes a Location 66 | header with the URL of a task resource. Sending a GET request to the task 67 | will continue to return 202 for as long as the task is running. When the task 68 | has finished, a status code 303 See Other will be returned, along with a 69 | Location header that points to the newly created resource. The client then 70 | needs to send a DELETE request to the task resource to remove it from the 71 | system.""" 72 | @functools.wraps(f) 73 | def wrapped(*args, **kwargs): 74 | # The background task needs to be decorated with Flask's 75 | # copy_current_request_context to have access to context globals. 76 | @copy_current_request_context 77 | def task(): 78 | global background_tasks 79 | try: 80 | # invoke the wrapped function and record the returned 81 | # response in the background_tasks dictionary 82 | background_tasks[id] = make_response(f(*args, **kwargs)) 83 | except: 84 | # the wrapped function raised an exception, return a 500 85 | # response 86 | background_tasks[id] = make_response(internal_server_error()) 87 | 88 | # store the background task under a randomly generated identifier 89 | # and start it 90 | global background_tasks 91 | id = uuid.uuid4().hex 92 | background_tasks[id] = Thread(target=task) 93 | background_tasks[id].start() 94 | 95 | # return a 202 Accepted response with the location of the task status 96 | # resource 97 | return jsonify({}), 202, {'Location': url_for('get_task_status', id=id)} 98 | return wrapped 99 | 100 | 101 | @app.route('/devices/', methods=['GET']) 102 | def get_devices(): 103 | return jsonify({'device': [device.get_url() 104 | for device in Device.query.all()]}) 105 | 106 | @app.route('/devices/', methods=['GET']) 107 | def get_device(id): 108 | return jsonify(Device.query.get_or_404(id).export_data()) 109 | 110 | 111 | @app.route('/devices//version', methods=['GET']) 112 | @background 113 | def get_device_version(id): 114 | device = Device.query.get_or_404(id) 115 | hostname = device.hostname 116 | ip = device.mgmt_ip 117 | prompt = hostname+"#" 118 | result = show_version(hostname, prompt, ip, 'cisco', 'cisco') 119 | return jsonify({"version": str(result)}) 120 | 121 | @app.route('/devices//version', methods=['GET']) 122 | @background 123 | def get_role_version(device_role): 124 | device_id_list = [device.id for device in Device.query.all() if device.role == device_role] 125 | result = {} 126 | for id in device_id_list: 127 | device = Device.query.get_or_404(id) 128 | hostname = device.hostname 129 | ip = device.mgmt_ip 130 | prompt = hostname + "#" 131 | device_result = show_version(hostname, prompt, ip, 'cisco', 'cisco') 132 | result[hostname] = str(device_result) 133 | return jsonify(result) 134 | 135 | @app.route('/devices/', methods=['POST']) 136 | def new_device(): 137 | device = Device() 138 | device.import_data(request.json) 139 | db.session.add(device) 140 | db.session.commit() 141 | return jsonify({}), 201, {'Location': device.get_url()} 142 | 143 | @app.route('/devices/', methods=['PUT']) 144 | def edit_device(id): 145 | device = Device.query.get_or_404(id) 146 | device.import_data(request.json) 147 | db.session.add(device) 148 | db.session.commit() 149 | return jsonify({}) 150 | 151 | 152 | @app.route('/status/', methods=['GET']) 153 | def get_task_status(id): 154 | """Query the status of an asynchronous task.""" 155 | # obtain the task and validate it 156 | global background_tasks 157 | rv = background_tasks.get(id) 158 | if rv is None: 159 | return not_found(None) 160 | 161 | # if the task object is a Thread object that means that the task is still 162 | # running. In this case return the 202 status message again. 163 | if isinstance(rv, Thread): 164 | return jsonify({}), 202, {'Location': url_for('get_task_status', id=id)} 165 | 166 | # If the task object is not a Thread then it is assumed to be the response 167 | # of the finished task, so that is the response that is returned. 168 | # If the application is configured to auto-delete task status resources once 169 | # the task is done then the deletion happens now, if not the client is 170 | # expected to send a delete request. 171 | if app.config['AUTO_DELETE_BG_TASKS']: 172 | del background_tasks[id] 173 | return rv 174 | 175 | 176 | 177 | if __name__ == '__main__': 178 | db.create_all() 179 | app.run(host='0.0.0.0', debug=True) 180 | 181 | 182 | 183 | 184 | 185 | -------------------------------------------------------------------------------- /Chapter11/Chapter11_3.py: -------------------------------------------------------------------------------- 1 | from ryu.base import app_manager 2 | from ryu.controller import ofp_event 3 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 4 | from ryu.controller.handler import set_ev_cls 5 | from ryu.ofproto import ofproto_v1_3 6 | from ryu.lib.packet import packet 7 | from ryu.lib.packet import ethernet 8 | from ryu.lib.packet import ether_types 9 | 10 | #new import 11 | from ryu.ofproto import ether 12 | from ryu.lib.packet import ipv4, arp 13 | 14 | 15 | class MySimpleStaticRouter(app_manager.RyuApp): 16 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 17 | 18 | def __init__(self, *args, **kwargs): 19 | super(MySimpleStaticRouter, self).__init__(*args, **kwargs) 20 | self.s1_gateway_mac = '00:00:00:00:00:02' # s1 gateway is spoofing h2 21 | self.s2_gateway_mac = '00:00:00:00:00:01' # s2 gateway is spoofing h1 22 | 23 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 24 | def switch_features_handler(self, ev): 25 | datapath = ev.msg.datapath 26 | ofproto = datapath.ofproto 27 | parser = datapath.ofproto_parser 28 | 29 | # install table-miss flow entry 30 | match = parser.OFPMatch() 31 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 32 | ofproto.OFPCML_NO_BUFFER)] 33 | self.add_flow(datapath, 0, match, actions) 34 | 35 | # push out static flows, note this is priority 1 36 | if datapath.id == 1: 37 | # flow from h1 to h2 38 | match = parser.OFPMatch(in_port=1, 39 | eth_type=ether.ETH_TYPE_IP, 40 | ipv4_src=('192.168.1.0', '255.255.255.0'), 41 | ipv4_dst=('192.168.2.0', '255.255.255.0')) 42 | out_port = 2 43 | actions = [parser.OFPActionOutput(out_port)] 44 | self.add_flow(datapath, 1, match, actions) 45 | 46 | 47 | # flow from h2 to h1 48 | match = parser.OFPMatch(in_port=2, 49 | eth_type=ether.ETH_TYPE_IP, 50 | ipv4_src=('192.168.2.0', '255.255.255.0'), 51 | ipv4_dst=('192.168.1.0', '255.255.255.0')) 52 | out_port = 1 53 | actions = [parser.OFPActionOutput(out_port)] 54 | self.add_flow(datapath, 1, match, actions) 55 | 56 | if datapath.id == 2: 57 | # flow from h1 to h2 58 | match = parser.OFPMatch(in_port=2, 59 | eth_type=ether.ETH_TYPE_IP, 60 | ipv4_src=('192.168.1.0', '255.255.255.0'), 61 | ipv4_dst=('192.168.2.0', '255.255.255.0')) 62 | out_port = 1 63 | # Can rewrite dst mac to h2 or spoof like we have done 64 | # parser.OFPActionSetField(eth_dst="00:00:00:00:00:02")] 65 | actions = [parser.OFPActionOutput(out_port)] 66 | self.add_flow(datapath, 1, match, actions) 67 | 68 | # from from h2 to h1 69 | match = parser.OFPMatch(in_port=1, 70 | eth_type=ether.ETH_TYPE_IP, 71 | ipv4_src=('192.168.2.0', '255.255.255.0'), 72 | ipv4_dst=('192.168.1.0', '255.255.255.0')) 73 | out_port = 2 74 | actions = [parser.OFPActionOutput(out_port)] 75 | self.add_flow(datapath, 1, match, actions) 76 | 77 | 78 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 79 | ofproto = datapath.ofproto 80 | parser = datapath.ofproto_parser 81 | 82 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 83 | actions)] 84 | 85 | # Note the addition of idle_timeout and hard_timeout 86 | if buffer_id: 87 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 88 | priority=priority, match=match, 89 | instructions=inst, idle_timeout=6000, 90 | hard_timeout=6000) 91 | else: 92 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 93 | match=match, instructions=inst, 94 | idle_timeout=6000, hard_timeout=6000) 95 | datapath.send_msg(mod) 96 | 97 | 98 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 99 | def _packet_in_handler(self, ev): 100 | msg = ev.msg 101 | datapath = msg.datapath 102 | ofproto = datapath.ofproto 103 | parser = datapath.ofproto_parser 104 | in_port = msg.match['in_port'] 105 | 106 | 107 | pkt = packet.Packet(msg.data) 108 | eth = pkt.get_protocols(ethernet.ethernet)[0] 109 | 110 | # Answering ARP packets for packet destined for gateways 111 | if eth.ethertype == ether_types.ETH_TYPE_ARP: 112 | arp_packet = pkt.get_protocols(arp.arp)[0] 113 | ethernet_src = eth.src 114 | 115 | # answering arp for s2 gateway 192.168.2.1 116 | if arp_packet.dst_ip == '192.168.2.1' and datapath.id == 2: 117 | print('Received ARP for 192.168.2.1') 118 | 119 | # building packet 120 | e = ethernet.ethernet(dst=eth.src, src=self.s2_gateway_mac, ethertype=ether.ETH_TYPE_ARP) 121 | a = arp.arp(hwtype=1, proto=0x0800, hlen=6, plen=4, opcode=2, 122 | src_mac=self.s2_gateway_mac, src_ip='192.168.2.1', 123 | dst_mac=ethernet_src, dst_ip=arp_packet.src_ip) 124 | 125 | p = packet.Packet() 126 | p.add_protocol(e) 127 | p.add_protocol(a) 128 | p.serialize() 129 | 130 | # sending arp response for s2 gateway 131 | outPort = in_port 132 | actions = [datapath.ofproto_parser.OFPActionOutput(outPort, 0)] 133 | out = datapath.ofproto_parser.OFPPacketOut( 134 | datapath=datapath, 135 | buffer_id=0xffffffff, 136 | in_port=datapath.ofproto.OFPP_CONTROLLER, 137 | actions=actions, 138 | data=p.data) 139 | datapath.send_msg(out) 140 | 141 | # answring arp for s1 gateway 192.168.1.1 142 | elif arp_packet.dst_ip == '192.168.1.1' and datapath.id == 1: 143 | print('Received ARP for 192.168.1.1') 144 | 145 | # building packet 146 | e = ethernet.ethernet(dst=eth.src, src=self.s1_gateway_mac, ethertype=ether.ETH_TYPE_ARP) 147 | a = arp.arp(hwtype=1, proto=0x0800, hlen=6, plen=4, opcode=2, 148 | src_mac=self.s1_gateway_mac, src_ip='192.168.1.1', 149 | dst_mac=ethernet_src, dst_ip=arp_packet.src_ip) 150 | 151 | p = packet.Packet() 152 | p.add_protocol(e) 153 | p.add_protocol(a) 154 | p.serialize() 155 | 156 | # sending arp response for s1 gateway 157 | outPort = in_port 158 | actions = [datapath.ofproto_parser.OFPActionOutput(outPort, 0)] 159 | out = datapath.ofproto_parser.OFPPacketOut( 160 | datapath=datapath, 161 | buffer_id=0xffffffff, 162 | in_port=datapath.ofproto.OFPP_CONTROLLER, 163 | actions=actions, 164 | data=p.data) 165 | datapath.send_msg(out) 166 | 167 | # verbose iteration of packets 168 | try: 169 | for p in pkt.protocols: 170 | print(p.protocol_name, p) 171 | print("datapath: {} in_port: {}".format(datapath.id, in_port)) 172 | except: 173 | pass 174 | 175 | 176 | -------------------------------------------------------------------------------- /Chapter09/chapter9_9.py: -------------------------------------------------------------------------------- 1 | # This example referenced Miguel Grinberg's code on Github: 2 | # https://github.com/miguelgrinberg/oreilly-flask-apis-video 3 | # 4 | 5 | from flask import Flask, url_for, jsonify, request,\ 6 | make_response, copy_current_request_context, g 7 | from flask.ext.sqlalchemy import SQLAlchemy 8 | from chapter9_pexpect_1 import show_version 9 | import uuid 10 | import functools 11 | from threading import Thread 12 | from werkzeug.security import generate_password_hash, check_password_hash 13 | from flask.ext.httpauth import HTTPBasicAuth 14 | 15 | app = Flask(__name__) 16 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///network.db' 17 | db = SQLAlchemy(app) 18 | auth = HTTPBasicAuth() 19 | 20 | background_tasks = {} 21 | app.config['AUTO_DELETE_BG_TASKS'] = True 22 | 23 | 24 | class ValidationError(ValueError): 25 | pass 26 | 27 | # The two password function came with Flask Werkzeug 28 | class User(db.Model): 29 | __tablename__ = 'users' 30 | id = db.Column(db.Integer, primary_key=True) 31 | username = db.Column(db.String(64), index=True) 32 | password_hash = db.Column(db.String(128)) 33 | 34 | def set_password(self, password): 35 | self.password_hash = generate_password_hash(password) 36 | 37 | def verify_password(self, password): 38 | return check_password_hash(self.password_hash, password) 39 | 40 | 41 | class Device(db.Model): 42 | __tablename__ = 'devices' 43 | id = db.Column(db.Integer, primary_key=True) 44 | hostname = db.Column(db.String(64), unique=True) 45 | loopback = db.Column(db.String(120), unique=True) 46 | mgmt_ip = db.Column(db.String(120), unique=True) 47 | role = db.Column(db.String(64)) 48 | vendor = db.Column(db.String(64)) 49 | os = db.Column(db.String(64)) 50 | 51 | def get_url(self): 52 | return url_for('get_device', id=self.id, _external=True) 53 | 54 | def export_data(self): 55 | return { 56 | 'self_url': self.get_url(), 57 | 'hostname': self.hostname, 58 | 'loopback': self.loopback, 59 | 'mgmt_ip': self.mgmt_ip, 60 | 'role': self.role, 61 | 'vendor': self.vendor, 62 | 'os': self.os 63 | } 64 | 65 | def import_data(self, data): 66 | try: 67 | self.hostname = data['hostname'] 68 | self.loopback = data['loopback'] 69 | self.mgmt_ip = data['mgmt_ip'] 70 | self.role = data['role'] 71 | self.vendor = data['vendor'] 72 | self.os = data['os'] 73 | except KeyError as e: 74 | raise ValidationError('Invalid device: missing ' + e.args[0]) 75 | return self 76 | 77 | 78 | def background(f): 79 | """Decorator that runs the wrapped function as a background task. It is 80 | assumed that this function creates a new resource, and takes a long time 81 | to do so. The response has status code 202 Accepted and includes a Location 82 | header with the URL of a task resource. Sending a GET request to the task 83 | will continue to return 202 for as long as the task is running. When the task 84 | has finished, a status code 303 See Other will be returned, along with a 85 | Location header that points to the newly created resource. The client then 86 | needs to send a DELETE request to the task resource to remove it from the 87 | system.""" 88 | @functools.wraps(f) 89 | def wrapped(*args, **kwargs): 90 | # The background task needs to be decorated with Flask's 91 | # copy_current_request_context to have access to context globals. 92 | @copy_current_request_context 93 | def task(): 94 | global background_tasks 95 | try: 96 | # invoke the wrapped function and record the returned 97 | # response in the background_tasks dictionary 98 | background_tasks[id] = make_response(f(*args, **kwargs)) 99 | except: 100 | # the wrapped function raised an exception, return a 500 101 | # response 102 | background_tasks[id] = make_response(internal_server_error()) 103 | 104 | # store the background task under a randomly generated identifier 105 | # and start it 106 | global background_tasks 107 | id = uuid.uuid4().hex 108 | background_tasks[id] = Thread(target=task) 109 | background_tasks[id].start() 110 | 111 | # return a 202 Accepted response with the location of the task status 112 | # resource 113 | return jsonify({}), 202, {'Location': url_for('get_task_status', id=id)} 114 | return wrapped 115 | 116 | # g is the context request object from Flask 117 | @auth.verify_password 118 | def verify_password(username, password): 119 | g.user = User.query.filter_by(username=username).first() 120 | if g.user is None: 121 | return False 122 | return g.user.verify_password(password) 123 | 124 | @app.before_request 125 | @auth.login_required 126 | def before_request(): 127 | pass 128 | 129 | # from HTTPAuath extension 130 | @auth.error_handler 131 | def unathorized(): 132 | response = jsonify({'status': 401, 'error': 'unahtorized', 133 | 'message': 'please authenticate'}) 134 | response.status_code = 401 135 | return response 136 | 137 | 138 | @app.route('/devices/', methods=['GET']) 139 | def get_devices(): 140 | return jsonify({'device': [device.get_url() 141 | for device in Device.query.all()]}) 142 | 143 | @app.route('/devices/', methods=['GET']) 144 | def get_device(id): 145 | return jsonify(Device.query.get_or_404(id).export_data()) 146 | 147 | 148 | @app.route('/devices//version', methods=['GET']) 149 | @background 150 | def get_device_version(id): 151 | device = Device.query.get_or_404(id) 152 | hostname = device.hostname 153 | ip = device.mgmt_ip 154 | prompt = hostname+"#" 155 | result = show_version(hostname, prompt, ip, 'cisco', 'cisco') 156 | return jsonify({"version": str(result)}) 157 | 158 | @app.route('/devices//version', methods=['GET']) 159 | @background 160 | def get_role_version(device_role): 161 | device_id_list = [device.id for device in Device.query.all() if device.role == device_role] 162 | result = {} 163 | for id in device_id_list: 164 | device = Device.query.get_or_404(id) 165 | hostname = device.hostname 166 | ip = device.mgmt_ip 167 | prompt = hostname + "#" 168 | device_result = show_version(hostname, prompt, ip, 'cisco', 'cisco') 169 | result[hostname] = str(device_result) 170 | return jsonify(result) 171 | 172 | @app.route('/devices/', methods=['POST']) 173 | def new_device(): 174 | device = Device() 175 | device.import_data(request.json) 176 | db.session.add(device) 177 | db.session.commit() 178 | return jsonify({}), 201, {'Location': device.get_url()} 179 | 180 | @app.route('/devices/', methods=['PUT']) 181 | def edit_device(id): 182 | device = Device.query.get_or_404(id) 183 | device.import_data(request.json) 184 | db.session.add(device) 185 | db.session.commit() 186 | return jsonify({}) 187 | 188 | 189 | @app.route('/status/', methods=['GET']) 190 | def get_task_status(id): 191 | """Query the status of an asynchronous task.""" 192 | # obtain the task and validate it 193 | global background_tasks 194 | rv = background_tasks.get(id) 195 | if rv is None: 196 | return not_found(None) 197 | 198 | # if the task object is a Thread object that means that the task is still 199 | # running. In this case return the 202 status message again. 200 | if isinstance(rv, Thread): 201 | return jsonify({}), 202, {'Location': url_for('get_task_status', id=id)} 202 | 203 | # If the task object is not a Thread then it is assumed to be the response 204 | # of the finished task, so that is the response that is returned. 205 | # If the application is configured to auto-delete task status resources once 206 | # the task is done then the deletion happens now, if not the client is 207 | # expected to send a delete request. 208 | if app.config['AUTO_DELETE_BG_TASKS']: 209 | del background_tasks[id] 210 | return rv 211 | 212 | 213 | 214 | if __name__ == '__main__': 215 | db.create_all() 216 | app.run(host='0.0.0.0', debug=True) 217 | 218 | 219 | 220 | 221 | 222 | -------------------------------------------------------------------------------- /Chapter07/results.txt: -------------------------------------------------------------------------------- 1 | {'Gig0-0_In_Octet': '3990616', 'Gig0-0_Out_uPackets': '60077', 'Gig0-0_In_uPackets': '42229', 'Gig0-0_Out_Octet': '5228254', 'Time': '2017-03-06T02:34:02.146245', 'hostname': 'iosv-1.virl.info'} 2 | {'Gig0-0_Out_uPackets': '60095', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_Octet': '5229721', 'Time': '2017-03-06T02:35:02.072340', 'Gig0-0_In_Octet': '3991754', 'Gig0-0_In_uPackets': '42242'} 3 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_Octet': '5231484', 'Gig0-0_In_Octet': '3993129', 'Time': '2017-03-06T02:36:02.753134', 'Gig0-0_In_uPackets': '42257', 'Gig0-0_Out_uPackets': '60116'} 4 | {'Gig0-0_In_Octet': '3994504', 'Time': '2017-03-06T02:37:02.146894', 'Gig0-0_In_uPackets': '42272', 'Gig0-0_Out_uPackets': '60136', 'Gig0-0_Out_Octet': '5233187', 'hostname': 'iosv-1.virl.info'} 5 | {'Gig0-0_In_uPackets': '42284', 'Time': '2017-03-06T02:38:01.915432', 'Gig0-0_In_Octet': '3995585', 'Gig0-0_Out_Octet': '5234656', 'Gig0-0_Out_uPackets': '60154', 'hostname': 'iosv-1.virl.info'} 6 | {'Gig0-0_Out_Octet': '5236419', 'Time': '2017-03-06T02:39:01.646927', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_uPackets': '60175', 'Gig0-0_In_Octet': '3996960', 'Gig0-0_In_uPackets': '42299'} 7 | {'Gig0-0_In_uPackets': '42311', 'hostname': 'iosv-1.virl.info', 'Time': '2017-03-06T02:40:02.456579', 'Gig0-0_Out_uPackets': '60193', 'Gig0-0_In_Octet': '3998041', 'Gig0-0_Out_Octet': '5237888'} 8 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '3999414', 'Gig0-0_In_uPackets': '42326', 'Gig0-0_Out_uPackets': '60215', 'Time': '2017-03-06T02:41:02.294267', 'Gig0-0_Out_Octet': '5239725'} 9 | {'Gig0-0_Out_Octet': '5241486', 'Gig0-0_Out_uPackets': '60236', 'Gig0-0_In_uPackets': '42341', 'Time': '2017-03-06T02:42:01.966146', 'Gig0-0_In_Octet': '4000786', 'hostname': 'iosv-1.virl.info'} 10 | {'Time': '2017-03-06T02:43:01.731416', 'Gig0-0_Out_uPackets': '60254', 'Gig0-0_Out_Octet': '5242952', 'Gig0-0_In_Octet': '4001865', 'Gig0-0_In_uPackets': '42353', 'hostname': 'iosv-1.virl.info'} 11 | {'Gig0-0_Out_Octet': '5244653', 'Gig0-0_Out_uPackets': '60274', 'Gig0-0_In_uPackets': '42368', 'hostname': 'iosv-1.virl.info', 'Time': '2017-03-06T02:44:02.521641', 'Gig0-0_In_Octet': '4003237'} 12 | {'Gig0-0_Out_Octet': '5246413', 'Gig0-0_Out_uPackets': '60295', 'Time': '2017-03-06T02:45:02.228500', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4004610', 'Gig0-0_In_uPackets': '42383'} 13 | {'Gig0-0_Out_uPackets': '60313', 'Gig0-0_Out_Octet': '5247880', 'Gig0-0_In_uPackets': '42395', 'hostname': 'iosv-1.virl.info', 'Time': '2017-03-06T02:46:01.960311', 'Gig0-0_In_Octet': '4005688'} 14 | {'Gig0-0_Out_uPackets': '60334', 'Gig0-0_Out_Octet': '5249643', 'Gig0-0_In_uPackets': '42410', 'Gig0-0_In_Octet': '4007063', 'hostname': 'iosv-1.virl.info', 'Time': '2017-03-06T02:47:01.861698'} 15 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_In_uPackets': '42422', 'Gig0-0_Out_Octet': '5251109', 'Gig0-0_In_Octet': '4008142', 'Time': '2017-03-06T02:48:01.756321', 'Gig0-0_Out_uPackets': '60352'} 16 | {'Gig0-0_Out_Octet': '5252807', 'Time': '2017-03-06T02:49:01.902937', 'Gig0-0_In_Octet': '4009512', 'Gig0-0_Out_uPackets': '60372', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_uPackets': '42437'} 17 | {'Gig0-0_In_Octet': '4010588', 'Time': '2017-03-06T02:50:01.770566', 'Gig0-0_In_uPackets': '42449', 'Gig0-0_Out_uPackets': '60390', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_Octet': '5254271'} 18 | {'Gig0-0_In_uPackets': '42464', 'Gig0-0_Out_Octet': '5256106', 'Time': '2017-03-06T02:51:02.005671', 'Gig0-0_In_Octet': '4011958', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_uPackets': '60412'} 19 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4013328', 'Time': '2017-03-06T02:52:01.809311', 'Gig0-0_Out_uPackets': '60432', 'Gig0-0_In_uPackets': '42479', 'Gig0-0_Out_Octet': '5257804'} 20 | {'Gig0-0_Out_Octet': '5259271', 'Gig0-0_In_uPackets': '42491', 'Time': '2017-03-06T02:53:01.530597', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4014406', 'Gig0-0_Out_uPackets': '60450'} 21 | {'Gig0-0_Out_uPackets': '60471', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_uPackets': '42506', 'Time': '2017-03-06T02:54:02.209868', 'Gig0-0_In_Octet': '4015781', 'Gig0-0_Out_Octet': '5261034'} 22 | {'Gig0-0_Out_uPackets': '60489', 'Gig0-0_In_uPackets': '42518', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_Octet': '5262503', 'Time': '2017-03-06T02:55:02.205098', 'Gig0-0_In_Octet': '4016862'} 23 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_In_uPackets': '42533', 'Gig0-0_Out_Octet': '5264263', 'Gig0-0_Out_uPackets': '60510', 'Time': '2017-03-06T02:56:02.246366', 'Gig0-0_In_Octet': '4018235'} 24 | {'Gig0-0_Out_Octet': '5266024', 'Time': '2017-03-06T02:57:02.057769', 'Gig0-0_Out_uPackets': '60531', 'Gig0-0_In_Octet': '4019607', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_uPackets': '42548'} 25 | {'Gig0-0_In_uPackets': '42560', 'Gig0-0_Out_uPackets': '60548', 'Time': '2017-03-06T02:58:01.759841', 'Gig0-0_Out_Octet': '5267430', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4020686'} 26 | {'Gig0-0_Out_uPackets': '60569', 'Time': '2017-03-06T02:59:01.500577', 'Gig0-0_Out_Octet': '5269191', 'Gig0-0_In_uPackets': '42575', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4022058'} 27 | {'Gig0-0_Out_uPackets': '60588', 'Gig0-0_In_uPackets': '42587', 'Gig0-0_Out_Octet': '5270737', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4023139', 'Time': '2017-03-06T03:00:02.368872'} 28 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4024512', 'Gig0-0_In_uPackets': '42602', 'Time': '2017-03-06T03:01:02.150037', 'Gig0-0_Out_Octet': '5272497', 'Gig0-0_Out_uPackets': '60609'} 29 | {'Gig0-0_Out_Octet': '5274258', 'Gig0-0_Out_uPackets': '60630', 'Time': '2017-03-06T03:02:01.869521', 'Gig0-0_In_uPackets': '42617', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4025884'} 30 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4026965', 'Gig0-0_Out_Octet': '5275727', 'Gig0-0_In_uPackets': '42629', 'Time': '2017-03-06T03:03:01.570950', 'Gig0-0_Out_uPackets': '60648'} 31 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_In_uPackets': '42644', 'Time': '2017-03-06T03:04:02.255872', 'Gig0-0_Out_uPackets': '60669', 'Gig0-0_Out_Octet': '5277487', 'Gig0-0_In_Octet': '4028338'} 32 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_uPackets': '60690', 'Gig0-0_Out_Octet': '5279248', 'Time': '2017-03-06T03:05:01.962176', 'Gig0-0_In_uPackets': '42659', 'Gig0-0_In_Octet': '4029710'} 33 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4030791', 'Gig0-0_Out_Octet': '5280717', 'Time': '2017-03-06T03:06:01.661128', 'Gig0-0_In_uPackets': '42671', 'Gig0-0_Out_uPackets': '60708'} 34 | {'Gig0-0_In_Octet': '4032164', 'Time': '2017-03-06T03:07:02.336401', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_uPackets': '60728', 'Gig0-0_Out_Octet': '5282417', 'Gig0-0_In_uPackets': '42686'} 35 | {'Gig0-0_Out_Octet': '5283884', 'Gig0-0_In_Octet': '4033242', 'Gig0-0_In_uPackets': '42698', 'Time': '2017-03-06T03:08:02.144784', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_uPackets': '60746'} 36 | {'Gig0-0_Out_Octet': '5285721', 'Time': '2017-03-06T03:09:02.042190', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4034615', 'Gig0-0_In_uPackets': '42713', 'Gig0-0_Out_uPackets': '60768'} 37 | {'Time': '2017-03-06T03:10:01.723442', 'Gig0-0_Out_uPackets': '60789', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_Octet': '5287479', 'Gig0-0_In_Octet': '4035985', 'Gig0-0_In_uPackets': '42728'} 38 | {'Gig0-0_In_uPackets': '42740', 'Time': '2017-03-06T03:11:02.393732', 'Gig0-0_Out_uPackets': '60807', 'Gig0-0_In_Octet': '4037063', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_Octet': '5288946'} 39 | {'Gig0-0_Out_uPackets': '60828', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_uPackets': '42755', 'Gig0-0_In_Octet': '4038438', 'Time': '2017-03-06T03:12:02.123784', 'Gig0-0_Out_Octet': '5290709'} 40 | {'Gig0-0_In_Octet': '4039813', 'Gig0-0_Out_uPackets': '60849', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_uPackets': '42770', 'Time': '2017-03-06T03:13:01.795363', 'Gig0-0_Out_Octet': '5292472'} 41 | {'Gig0-0_Out_Octet': '5293938', 'Gig0-0_Out_uPackets': '60867', 'Gig0-0_In_uPackets': '42782', 'Gig0-0_In_Octet': '4040892', 'Time': '2017-03-06T03:14:01.476548', 'hostname': 'iosv-1.virl.info'} 42 | {'Time': '2017-03-06T03:15:02.254936', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_uPackets': '60888', 'Gig0-0_Out_Octet': '5295699', 'Gig0-0_In_uPackets': '42797', 'Gig0-0_In_Octet': '4042264'} 43 | {'Gig0-0_In_Octet': '4043343', 'Gig0-0_In_uPackets': '42809', 'Gig0-0_Out_uPackets': '60905', 'Time': '2017-03-06T03:16:02.177917', 'Gig0-0_Out_Octet': '5297105', 'hostname': 'iosv-1.virl.info'} 44 | {'Gig0-0_Out_uPackets': '60926', 'hostname': 'iosv-1.virl.info', 'Time': '2017-03-06T03:17:01.881498', 'Gig0-0_In_uPackets': '42824', 'Gig0-0_Out_Octet': '5298863', 'Gig0-0_In_Octet': '4044713'} 45 | {'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_Octet': '5300621', 'Gig0-0_In_Octet': '4046083', 'Time': '2017-03-06T03:18:01.558740', 'Gig0-0_Out_uPackets': '60947', 'Gig0-0_In_uPackets': '42839'} 46 | {'Gig0-0_Out_uPackets': '60965', 'Gig0-0_Out_Octet': '5302088', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4047161', 'Time': '2017-03-06T03:19:02.253796', 'Gig0-0_In_uPackets': '42851'} 47 | {'Gig0-0_Out_uPackets': '60985', 'Time': '2017-03-06T03:20:01.966435', 'Gig0-0_Out_Octet': '5303791', 'Gig0-0_In_uPackets': '42866', 'Gig0-0_In_Octet': '4048536', 'hostname': 'iosv-1.virl.info'} 48 | {'Gig0-0_In_uPackets': '42878', 'Time': '2017-03-06T03:21:01.639857', 'Gig0-0_Out_uPackets': '61004', 'Gig0-0_Out_Octet': '5305337', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4049617'} 49 | {'Gig0-0_Out_Octet': '5307097', 'Gig0-0_Out_uPackets': '61025', 'Time': '2017-03-06T03:22:02.552536', 'hostname': 'iosv-1.virl.info', 'Gig0-0_In_Octet': '4050990', 'Gig0-0_In_uPackets': '42893'} 50 | {'Gig0-0_Out_Octet': '5308855', 'hostname': 'iosv-1.virl.info', 'Gig0-0_Out_uPackets': '61046', 'Gig0-0_In_uPackets': '42909', 'Time': '2017-03-06T03:23:02.386113', 'Gig0-0_In_Octet': '4052420'} 51 | -------------------------------------------------------------------------------- /Chapter11/Chapter11_4.py: -------------------------------------------------------------------------------- 1 | # REST API 2 | # 3 | # Retrieve the switch stats 4 | # 5 | # get the list of all switches 6 | # GET /network/switches 7 | # 8 | # get the description of the switch 9 | # GET /network/desc/ 10 | # 11 | # get flows stats of the switch 12 | # GET /network/flow/ 13 | # 14 | # add a flow entry 15 | # POST /network/flowentry/add 16 | # 17 | # delete all matching flow entries 18 | # POST /network/flowentry/delete 19 | # 20 | 21 | from ryu.base import app_manager 22 | from ryu.controller import ofp_event 23 | from ryu.controller.handler import CONFIG_DISPATCHER, MAIN_DISPATCHER 24 | from ryu.controller.handler import set_ev_cls 25 | from ryu.ofproto import ofproto_v1_3 26 | from ryu.lib.packet import packet 27 | from ryu.lib.packet import ethernet 28 | from ryu.lib.packet import ether_types 29 | from ryu.ofproto import ether 30 | from ryu.lib.packet import ipv4, arp 31 | 32 | # new imports 33 | from ryu.app.ofctl_rest import * 34 | 35 | 36 | class MySimpleRestRouter(app_manager.RyuApp): 37 | OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] 38 | 39 | # new 40 | _CONTEXTS = { 41 | 'dpset': dpset.DPSet, 42 | 'wsgi': WSGIApplication 43 | } 44 | 45 | def __init__(self, *args, **kwargs): 46 | super(MySimpleRestRouter, self).__init__(*args, **kwargs) 47 | self.s1_gateway_mac = '00:00:00:00:00:02' # s1 gateway is spoofing h2 48 | self.s2_gateway_mac = '00:00:00:00:00:01' # s2 gateway is spoofing h1 49 | 50 | # new 51 | self.dpset = kwargs['dpset'] 52 | wsgi = kwargs['wsgi'] 53 | self.waiters = {} 54 | self.data = {} 55 | self.data['dpset'] = self.dpset 56 | self.data['waiters'] = self.waiters 57 | mapper = wsgi.mapper 58 | 59 | wsgi.registory['StatsController'] = self.data 60 | path = '/network' 61 | 62 | uri = path + '/switches' 63 | mapper.connect('stats', uri, 64 | controller=StatsController, action='get_dpids', 65 | conditions=dict(method=['GET'])) 66 | 67 | uri = path + '/desc/{dpid}' 68 | mapper.connect('stats', uri, 69 | controller=StatsController, action='get_desc_stats', 70 | conditions=dict(method=['GET'])) 71 | 72 | uri = path + '/flow/{dpid}' 73 | mapper.connect('stats', uri, 74 | controller=StatsController, action='get_flow_stats', 75 | conditions=dict(method=['GET'])) 76 | 77 | uri = path + '/flowentry/{cmd}' 78 | mapper.connect('stats', uri, 79 | controller=StatsController, action='mod_flow_entry', 80 | conditions=dict(method=['POST'])) 81 | 82 | 83 | @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) 84 | def switch_features_handler(self, ev): 85 | datapath = ev.msg.datapath 86 | ofproto = datapath.ofproto 87 | parser = datapath.ofproto_parser 88 | 89 | # install table-miss flow entry 90 | match = parser.OFPMatch() 91 | actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, 92 | ofproto.OFPCML_NO_BUFFER)] 93 | self.add_flow(datapath, 0, match, actions) 94 | 95 | 96 | def add_flow(self, datapath, priority, match, actions, buffer_id=None): 97 | ofproto = datapath.ofproto 98 | parser = datapath.ofproto_parser 99 | 100 | inst = [parser.OFPInstructionActions(ofproto.OFPIT_APPLY_ACTIONS, 101 | actions)] 102 | 103 | # Note the addition of idle_timeout and hard_timeout 104 | if buffer_id: 105 | mod = parser.OFPFlowMod(datapath=datapath, buffer_id=buffer_id, 106 | priority=priority, match=match, 107 | instructions=inst) 108 | else: 109 | mod = parser.OFPFlowMod(datapath=datapath, priority=priority, 110 | match=match, instructions=inst) 111 | datapath.send_msg(mod) 112 | 113 | 114 | @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) 115 | def _packet_in_handler(self, ev): 116 | msg = ev.msg 117 | datapath = msg.datapath 118 | ofproto = datapath.ofproto 119 | parser = datapath.ofproto_parser 120 | in_port = msg.match['in_port'] 121 | 122 | 123 | pkt = packet.Packet(msg.data) 124 | eth = pkt.get_protocols(ethernet.ethernet)[0] 125 | 126 | # Answering ARP packets for packet destined for gateways 127 | if eth.ethertype == ether_types.ETH_TYPE_ARP: 128 | arp_packet = pkt.get_protocols(arp.arp)[0] 129 | ethernet_src = eth.src 130 | 131 | # answering arp for s2 gateway 192.168.2.1 132 | if arp_packet.dst_ip == '192.168.2.1' and datapath.id == 2: 133 | print('Received ARP for 192.168.2.1') 134 | 135 | # building packet 136 | e = ethernet.ethernet(dst=eth.src, src=self.s2_gateway_mac, ethertype=ether.ETH_TYPE_ARP) 137 | a = arp.arp(hwtype=1, proto=0x0800, hlen=6, plen=4, opcode=2, 138 | src_mac=self.s2_gateway_mac, src_ip='192.168.2.1', 139 | dst_mac=ethernet_src, dst_ip=arp_packet.src_ip) 140 | 141 | p = packet.Packet() 142 | p.add_protocol(e) 143 | p.add_protocol(a) 144 | p.serialize() 145 | 146 | # sending arp response for s2 gateway 147 | outPort = in_port 148 | actions = [datapath.ofproto_parser.OFPActionOutput(outPort, 0)] 149 | out = datapath.ofproto_parser.OFPPacketOut( 150 | datapath=datapath, 151 | buffer_id=0xffffffff, 152 | in_port=datapath.ofproto.OFPP_CONTROLLER, 153 | actions=actions, 154 | data=p.data) 155 | datapath.send_msg(out) 156 | 157 | # answring arp for s1 gateway 192.168.1.1 158 | elif arp_packet.dst_ip == '192.168.1.1' and datapath.id == 1: 159 | print('Received ARP for 192.168.1.1') 160 | 161 | # building packet 162 | e = ethernet.ethernet(dst=eth.src, src=self.s1_gateway_mac, ethertype=ether.ETH_TYPE_ARP) 163 | a = arp.arp(hwtype=1, proto=0x0800, hlen=6, plen=4, opcode=2, 164 | src_mac=self.s1_gateway_mac, src_ip='192.168.1.1', 165 | dst_mac=ethernet_src, dst_ip=arp_packet.src_ip) 166 | 167 | p = packet.Packet() 168 | p.add_protocol(e) 169 | p.add_protocol(a) 170 | p.serialize() 171 | 172 | # sending arp response for s1 gateway 173 | outPort = in_port 174 | actions = [datapath.ofproto_parser.OFPActionOutput(outPort, 0)] 175 | out = datapath.ofproto_parser.OFPPacketOut( 176 | datapath=datapath, 177 | buffer_id=0xffffffff, 178 | in_port=datapath.ofproto.OFPP_CONTROLLER, 179 | actions=actions, 180 | data=p.data) 181 | datapath.send_msg(out) 182 | 183 | # verbose iteration of packets 184 | try: 185 | for p in pkt.protocols: 186 | print(p.protocol_name, p) 187 | print("datapath: {} in_port: {}".format(datapath.id, in_port)) 188 | except: 189 | pass 190 | 191 | # new 192 | @set_ev_cls([ofp_event.EventOFPStatsReply, 193 | ofp_event.EventOFPDescStatsReply, 194 | ofp_event.EventOFPFlowStatsReply, 195 | ofp_event.EventOFPAggregateStatsReply, 196 | ofp_event.EventOFPTableStatsReply, 197 | ofp_event.EventOFPTableFeaturesStatsReply, 198 | ofp_event.EventOFPPortStatsReply, 199 | ofp_event.EventOFPQueueStatsReply, 200 | ofp_event.EventOFPQueueDescStatsReply, 201 | ofp_event.EventOFPMeterStatsReply, 202 | ofp_event.EventOFPMeterFeaturesStatsReply, 203 | ofp_event.EventOFPMeterConfigStatsReply, 204 | ofp_event.EventOFPGroupStatsReply, 205 | ofp_event.EventOFPGroupFeaturesStatsReply, 206 | ofp_event.EventOFPGroupDescStatsReply, 207 | ofp_event.EventOFPPortDescStatsReply 208 | ], MAIN_DISPATCHER) 209 | def stats_reply_handler(self, ev): 210 | msg = ev.msg 211 | dp = msg.datapath 212 | 213 | if dp.id not in self.waiters: 214 | return 215 | if msg.xid not in self.waiters[dp.id]: 216 | return 217 | lock, msgs = self.waiters[dp.id][msg.xid] 218 | msgs.append(msg) 219 | 220 | flags = 0 221 | if dp.ofproto.OFP_VERSION == ofproto_v1_0.OFP_VERSION: 222 | flags = dp.ofproto.OFPSF_REPLY_MORE 223 | elif dp.ofproto.OFP_VERSION == ofproto_v1_2.OFP_VERSION: 224 | flags = dp.ofproto.OFPSF_REPLY_MORE 225 | elif dp.ofproto.OFP_VERSION >= ofproto_v1_3.OFP_VERSION: 226 | flags = dp.ofproto.OFPMPF_REPLY_MORE 227 | 228 | if msg.flags & flags: 229 | return 230 | del self.waiters[dp.id][msg.xid] 231 | lock.set() 232 | 233 | @set_ev_cls([ofp_event.EventOFPQueueGetConfigReply, 234 | ofp_event.EventOFPRoleReply, 235 | ], MAIN_DISPATCHER) 236 | def features_reply_handler(self, ev): 237 | msg = ev.msg 238 | dp = msg.datapath 239 | 240 | if dp.id not in self.waiters: 241 | return 242 | if msg.xid not in self.waiters[dp.id]: 243 | return 244 | lock, msgs = self.waiters[dp.id][msg.xid] 245 | msgs.append(msg) 246 | 247 | del self.waiters[dp.id][msg.xid] 248 | lock.set() 249 | 250 | 251 | 252 | -------------------------------------------------------------------------------- /Chapter07/pygal_example_3.svg: -------------------------------------------------------------------------------- 1 | 2 | Protocol Breakdown15398.346958544159.94556611930458.069440433304.12839187445236.745566119321.54695854410306.671608126152.730559567Protocol BreakdownTCPUDPICMPOthers --------------------------------------------------------------------------------