├── .gitignore
├── Chapter01
├── helloworld.py
└── math_stuff
│ ├── __init__.py
│ └── subtract.py
├── Chapter02
├── 2_DC_Topology.yaml
├── 2_DC_Topology_CML_2.6_Fix.yaml
├── chapter2_1.py
├── chapter2_2.py
├── chapter2_3.py
├── chapter2_4.py
├── chapter2_5.py
├── commands.txt
├── devices.json
├── hosts.yaml
├── lax-edg-r1_output.txt
└── lax-edg-r2_output.txt
├── Chapter03
├── Arista
│ ├── eapi_1.py
│ ├── eapi_2.py
│ ├── eapi_2_acl.py
│ └── pyeapi_1.py
├── Cisco
│ ├── cisco_meraki_1.py
│ ├── cisco_meraki_2.py
│ ├── cisco_nxapi_1.py
│ ├── cisco_nxapi_2.py
│ ├── cisco_nxapi_3.py
│ ├── cisco_nxapi_4.py
│ ├── cisco_yang_1.py
│ ├── cisco_yang_1_interfaces.xml
│ └── nxapi_show_interface_output.txt
├── Juniper
│ ├── junos_netconf_1.py
│ ├── junos_netconf_2.py
│ ├── junos_netconf_3.py
│ ├── junos_pyez_1.py
│ └── junos_pyez_2.py
└── VyOS
│ └── vyos_1.py
├── Chapter04
├── Templates
│ ├── file1
│ ├── file2
│ ├── hosts
│ ├── nx-osv-1.conf
│ ├── nx-osv-2.conf
│ ├── nxos.j2
│ ├── template_1.yml
│ └── template_2.yml
├── ansible.cfg
├── host_vars
│ ├── iosv-1
│ ├── iosv-2
│ ├── lax-cor-r1
│ ├── lax-edg-r1
│ ├── lax-edg-r2
│ ├── nyc-cor-r1
│ ├── nyc-edg-r1
│ └── nyc-edg-r2
├── hosts
├── hosts_full
├── ios_conditional.yml
├── ios_conditional_config.yml
├── ios_config_backup.yml
├── ios_facts_output.txt
├── ios_facts_playbook.yml
├── nxos_config_backup.yml
├── standard_loop.yml
├── standard_loop_vlan_example.yml
└── standard_loop_vlan_example_2.yml
├── Chapter05
├── ansible_container
│ ├── ansible.cfg
│ ├── host_vars
│ │ ├── iosv-1
│ │ └── iosv-2
│ ├── hosts
│ └── ios_config_backup.yml
└── container_lab
│ ├── .srl02.clab.yml.bak
│ ├── clab-srl02
│ ├── ansible-inventory.yml
│ ├── ca
│ │ ├── root
│ │ │ ├── root-ca.csr
│ │ │ └── root-ca.pem
│ │ ├── srl1
│ │ │ ├── srl1.csr
│ │ │ └── srl1.pem
│ │ └── srl2
│ │ │ ├── srl2.csr
│ │ │ └── srl2.pem
│ ├── srl1
│ │ └── topology.yml
│ ├── srl2
│ │ └── topology.yml
│ └── topology-data.json
│ ├── srl02.clab.yml
│ ├── srl1.cfg
│ └── srl2.cfg
├── Chapter06
├── 2_DC_Topology_with_Hosts.yaml
├── access_list_mac_iosv.yml
├── access_list_nxosv.yml
├── chapter6_topology.virl.backup
├── host_vars
│ ├── nyc-cor-r1
│ └── nyc-edg-r1
├── hosts
├── python_re_search_1.py
├── python_re_search_2.py
├── scapy_ping_collection.py
└── scapy_tcp_scan_1.py
├── Chapter07
├── cacti_1.py
├── cacti_2.py
├── chapter7_topology.virl.bak
├── figuer1.png
├── figure1.pdf
├── matplotlib_1.py
├── matplotlib_1_result.png
├── matplotlib_2.py
├── matplotlib_2_result.png
├── matplotlib_3.py
├── matplotlib_3_result.png
├── pygal_1.py
├── pygal_2.py
├── pygal_example_1.svg
├── pygal_example_2.svg
├── pygal_example_3.svg
├── pysnmp_1.py
├── pysnmp_2.py
├── pysnmp_3.py
└── results.txt
├── Chapter08
├── chapter8_graphviz_1.gv
├── chapter8_gv_1.gv
├── chapter8_gv_2.gv
├── chapter8_ntop_1.py
├── chapter8_ntop_2.py
├── chapter8_sflowtool_1.py
├── chapter8_topology.virl
├── chapter8_topology.yaml
├── cisco_config_lldp.yml
├── cisco_config_netflow.yml
├── cisco_discover_lldp.yml
├── cisco_graph_lldp.py
├── host_vars
│ ├── r1
│ ├── r2
│ ├── r3
│ ├── r4
│ ├── r5-tor
│ └── r6-edge
├── hosts
├── netFlow_v5_parser.py
├── output
│ ├── chapter8_gv_1.png
│ ├── chapter8_gv_2.pdf
│ ├── chapter8_gv_3.gv
│ ├── chapter8_gv_3.gv.pdf
│ ├── chapter8_lldp_graph.gv
│ ├── chapter8_lldp_graph.gv.pdf
│ ├── chapter8_lldp_graph.gv_neato.pdf
│ └── chapter8_lldp_graph.gv_v1.pdf
└── tmp
│ ├── r1_lldp_output.txt
│ ├── r2_lldp_output.txt
│ ├── r3_lldp_output.txt
│ ├── r5-tor_lldp_output.txt
│ └── r6-edge_lldp_output.txt
├── Chapter09
├── TestApp
│ ├── app
│ │ ├── __init__.py
│ │ └── network.db
│ ├── main.py
│ ├── requirements.txt
│ ├── start.sh
│ └── uwsgi.ini
├── chapter9_1.py
├── chapter9_2.py
├── chapter9_3.py
├── chapter9_4.py
├── chapter9_5.py
├── chapter9_6.py
├── chapter9_7.py
├── chapter9_8.py
├── chapter9_9.py
├── chapter9_db_1.py
├── chapter9_pexpect_1.py
├── chapter9_request_1.py
├── chapter9_topology.virl
└── requirements.txt
├── Chapter10
├── async_count.py
├── asyncio_1.py
├── multiprocess_1.py
├── scrapli
│ ├── scrapli_example_1.py
│ ├── scrapli_example_2.py
│ ├── scrapli_example_3_async.py
│ └── scrapli_example_3_sync.py
├── sync_count.py
└── threading_1.py
├── Chapter11
├── Chapter11_1_query_vpc.py
├── Chapter11_2_query_route_tables.py
├── Chapter11_3_cloud_formation.yml
├── Chapter11_4_cloud_formation_full.yml
└── Chapter11_5_security_group.py
├── Chapter12
├── Chapter12_1_auth.py
├── Chapter12_2_subnet.py
└── Chapter12_3_vnet.py
├── Chapter13
├── Chapter13_1.py
├── Chapter13_2.py
├── Chapter13_3.py
├── Chapter13_4.py
├── Chapter13_5.py
├── Chapter13_6.py
├── chapter13_topology.virl.backup
├── network_configs
│ └── simple_config.conf
├── query_body_1.json
├── query_body_2.json
├── query_body_3.json
├── query_body_4.json
└── query_body_5.json
├── Chapter14
└── Chapter14_1.py
├── Chapter16
├── chapter15_topology.virl
├── chapter16_10_ping.py
├── chapter16_1_xml.py
├── chapter16_2_validation.py
├── chapter16_3_test_fail.py
├── chapter16_4_unittest.py
├── chapter16_5_more_unittest.py
├── chapter16_5_more_unittest_mocks.py
├── chapter16_6_pytest_1.py
├── chapter16_7_pytest_2.py
├── chapter16_8_pytest_3.py
├── chapter16_9_pytest_4.py
├── chapter16_topology.yaml
├── pyATS
│ ├── chapter16_11_pyats_1.py
│ ├── chapter16_12_pyats_2.py
│ ├── chapter16_pyats_testbed_1.yml
│ └── chapter16_pyats_testbed_2.yml
└── result.xml
├── Errata.md
├── LICENSE
└── README.md
/.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 | # echou common
35 | credentials*
36 | config*
37 | *insecure*
38 | src_env
39 | secrets*
40 | id_rsa
41 | *key*
42 |
43 | # Docker
44 | Dockerfile
45 | docker-compose.yml
46 |
47 | # Files that might appear in the root of a volume
48 | .DocumentRevisions-V100
49 | .fseventsd
50 | .Spotlight-V100
51 | .TemporaryItems
52 | .Trashes
53 | .VolumeIcon.icns
54 |
55 | # Directories potentially created on remote AFP share
56 | .AppleDB
57 | .AppleDesktop
58 | Network Trash Folder
59 | Temporary Items
60 | .apdisk
61 |
62 | # Ansible gitignore
63 | *.retry
64 |
65 | # Python gitignore
66 |
67 | # Byte-compiled / optimized / DLL files
68 | __pycache__/
69 | *.py[cod]
70 | *$py.class
71 |
72 | # C extensions
73 | *.so
74 |
75 | # Distribution / packaging
76 | .Python
77 | env/
78 | build/
79 | develop-eggs/
80 | dist/
81 | downloads/
82 | eggs/
83 | .eggs/
84 | lib/
85 | lib64/
86 | parts/
87 | sdist/
88 | var/
89 | wheels/
90 | *.egg-info/
91 | .installed.cfg
92 | *.egg
93 |
94 | # PyInstaller
95 | # Usually these files are written by a python script from a template
96 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
97 | *.manifest
98 | *.spec
99 |
100 | # Installer logs
101 | pip-log.txt
102 | pip-delete-this-directory.txt
103 |
104 | # Unit test / coverage reports
105 | htmlcov/
106 | .tox/
107 | .coverage
108 | .coverage.*
109 | .cache
110 | nosetests.xml
111 | coverage.xml
112 | *,cover
113 | .hypothesis/
114 |
115 | # Translations
116 | *.mo
117 | *.pot
118 |
119 | # Django stuff:
120 | *.log
121 | local_settings.py
122 |
123 | # Flask stuff:
124 | instance/
125 | .webassets-cache
126 |
127 | # Scrapy stuff:
128 | .scrapy
129 |
130 | # Sphinx documentation
131 | docs/_build/
132 |
133 | # PyBuilder
134 | target/
135 |
136 | # Jupyter Notebook
137 | .ipynb_checkpoints
138 |
139 | # pyenv
140 | .python-version
141 |
142 | # celery beat schedule file
143 | celerybeat-schedule
144 |
145 | # SageMath parsed files
146 | *.sage.py
147 |
148 | # dotenv
149 | .env
150 |
151 | # virtualenv
152 | .venv
153 | venv/
154 | ENV/
155 |
156 | # Spyder project settings
157 | .spyderproject
158 |
159 | # Rope project settings
160 | .ropeproject
161 |
162 | # IDE
163 | .vscode
164 |
165 | # Windows image file caches
166 | Thumbs.db
167 | ehthumbs.db
168 |
169 | # Folder config file
170 | Desktop.ini
171 |
172 | # Recycle Bin used on file shares
173 | $RECYCLE.BIN/
174 |
175 | # Windows Installer files
176 | *.cab
177 | *.msi
178 | *.msm
179 | *.msp
180 |
181 | # Windows shortcuts
182 | *.lnk
183 |
184 | # =========================
185 | # Operating System Files
186 | # =========================
187 |
188 | # OSX
189 | # =========================
190 |
191 | .DS_Store
192 | .AppleDouble
193 | .LSOverride
194 |
195 | # Thumbnails
196 | ._*
197 |
198 | # echou common
199 | credentials*
200 | config*
201 | *insecure*
202 | src_env
203 | secrets*
204 | id_rsa
205 | *key*
206 |
207 | # Docker
208 | Dockerfile
209 | docker-compose.yml
210 |
211 | # Files that might appear in the root of a volume
212 | .DocumentRevisions-V100
213 | .fseventsd
214 | .Spotlight-V100
215 | .TemporaryItems
216 | .Trashes
217 | .VolumeIcon.icns
218 |
219 | # Directories potentially created on remote AFP share
220 | .AppleDB
221 | .AppleDesktop
222 | Network Trash Folder
223 | Temporary Items
224 | .apdisk
225 |
226 | # Ansible gitignore
227 | *.retry
228 |
229 | # Python gitignore
230 |
231 | # Byte-compiled / optimized / DLL files
232 | __pycache__/
233 | *.py[cod]
234 | *$py.class
235 |
236 | # C extensions
237 | *.so
238 |
239 | # Distribution / packaging
240 | .Python
241 | env/
242 | build/
243 | develop-eggs/
244 | dist/
245 | downloads/
246 | eggs/
247 | .eggs/
248 | lib/
249 | lib64/
250 | parts/
251 | sdist/
252 | var/
253 | wheels/
254 | *.egg-info/
255 | .installed.cfg
256 | *.egg
257 |
258 | # PyInstaller
259 | # Usually these files are written by a python script from a template
260 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
261 | *.manifest
262 | *.spec
263 |
264 | # Installer logs
265 | pip-log.txt
266 | pip-delete-this-directory.txt
267 |
268 | # Unit test / coverage reports
269 | htmlcov/
270 | .tox/
271 | .coverage
272 | .coverage.*
273 | .cache
274 | nosetests.xml
275 | coverage.xml
276 | *,cover
277 | .hypothesis/
278 |
279 | # Translations
280 | *.mo
281 | *.pot
282 |
283 | # Django stuff:
284 | *.log
285 | local_settings.py
286 |
287 | # Flask stuff:
288 | instance/
289 | .webassets-cache
290 |
291 | # Scrapy stuff:
292 | .scrapy
293 |
294 | # Sphinx documentation
295 | docs/_build/
296 |
297 | # PyBuilder
298 | target/
299 |
300 | # Jupyter Notebook
301 | .ipynb_checkpoints
302 |
303 | # pyenv
304 | .python-version
305 |
306 | # celery beat schedule file
307 | celerybeat-schedule
308 |
309 | # SageMath parsed files
310 | *.sage.py
311 |
312 | # dotenv
313 | .env
314 |
315 | # virtualenv
316 | .venv
317 | venv/
318 | ENV/
319 |
320 | # Spyder project settings
321 | .spyderproject
322 |
323 | # Rope project settings
324 | .ropeproject
325 |
326 | # IDE
327 | .vscode
328 |
329 |
--------------------------------------------------------------------------------
/Chapter01/helloworld.py:
--------------------------------------------------------------------------------
1 | # This is a comment
2 | print("hello world")
3 |
4 |
--------------------------------------------------------------------------------
/Chapter01/math_stuff/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter01/math_stuff/__init__.py
--------------------------------------------------------------------------------
/Chapter01/math_stuff/subtract.py:
--------------------------------------------------------------------------------
1 | def subtract(a, b):
2 | c = a - b
3 | return c
4 |
5 |
--------------------------------------------------------------------------------
/Chapter02/chapter2_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import pexpect
4 |
5 | devices = {'iosv-1': {'prompt': 'lax-edg-r1#', 'ip': '192.168.2.51'},
6 | 'iosv-2': {'prompt': 'lax-edg-r2#', 'ip': '192.168.2.52'}}
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 |
--------------------------------------------------------------------------------
/Chapter02/chapter2_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import getpass
4 | from pexpect import pxssh
5 |
6 | devices = {'lax-edg-r1': {'prompt': 'lax-edg-r1#', 'ip': '192.168.2.51'},
7 | 'lax-edg-r2': {'prompt': 'lax-edg-r2#', 'ip': '192.168.2.52'}}
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 |
--------------------------------------------------------------------------------
/Chapter02/chapter2_3.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import paramiko, getpass, time
4 |
5 | devices = {'lax-edg-r1': {'ip': '192.168.2.51'},
6 | 'lax-edg-r2': {'ip': '192.168.2.52'}}
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(5)
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(5)
33 | output = new_connection.recv(max_buffer)
34 | print(output)
35 | f.write(output)
36 |
37 | new_connection.close()
38 |
--------------------------------------------------------------------------------
/Chapter02/chapter2_4.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
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 = 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 |
--------------------------------------------------------------------------------
/Chapter02/chapter2_5.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | from nornir import InitNornir
4 | from nornir_utils.plugins.functions import print_result
5 | from nornir_netmiko import netmiko_send_command
6 |
7 |
8 | nr = InitNornir()
9 |
10 | result = nr.run(
11 | task=netmiko_send_command,
12 | command_string="show arp"
13 | )
14 |
15 | print_result(result)
16 |
--------------------------------------------------------------------------------
/Chapter02/commands.txt:
--------------------------------------------------------------------------------
1 | config t
2 | logging buffered 30000
3 | end
4 | copy run start
--------------------------------------------------------------------------------
/Chapter02/devices.json:
--------------------------------------------------------------------------------
1 | {
2 | "lax-edg-r1": {
3 | "ip": "192.168.2.51"
4 | },
5 | "lax-edg-r2": {
6 | "ip": "192.168.2.52"
7 | }
8 | }
--------------------------------------------------------------------------------
/Chapter02/hosts.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | lax-edg-r1:
3 | hostname: '192.168.2.51'
4 | port: 22
5 | username: 'cisco'
6 | password: 'cisco'
7 | platform: 'cisco_ios'
8 |
9 | lax-edg-r2:
10 | hostname: '192.168.2.52'
11 | port: 22
12 | username: 'cisco'
13 | password: 'cisco'
14 | platform: 'cisco_ios'
--------------------------------------------------------------------------------
/Chapter02/lax-edg-r1_output.txt:
--------------------------------------------------------------------------------
1 | terminal length 0
2 | lax-edg-r1#config t
3 | Enter configuration commands, one per line. End with CNTL/Z.
4 | lax-edg-r1(config)#logging buffered 30000
5 | lax-edg-r1(config)#end
6 | lax-edg-r1#copy run start
--------------------------------------------------------------------------------
/Chapter02/lax-edg-r2_output.txt:
--------------------------------------------------------------------------------
1 | terminal length 0
2 | lax-edg-r2#config t
3 | Enter configuration commands, one per line. End with CNTL/Z.
4 | lax-edg-r2(config)#logging buffered 30000
5 | lax-edg-r2(config)#end
6 | lax-edg-r2#copy run start
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter03/Cisco/cisco_meraki_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import requests
3 | import pprint
4 |
5 | myheaders={'X-Cisco-Meraki-API-Key': '6bec40cf957de430a6f1f2baa056b99a4fac9ea0'}
6 | url = 'https://dashboard.meraki.com/api/v0/organizations'
7 | response = requests.get(url, headers=myheaders, verify=False)
8 | pprint.pprint(response.json())
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Chapter03/Cisco/cisco_meraki_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import requests
3 | import pprint
4 |
5 | myheaders={'X-Cisco-Meraki-API-Key': '6bec40cf957de430a6f1f2baa056b99a4fac9ea0'}
6 | orgId = '549236'
7 | url = 'https://dashboard.meraki.com/api/v0/organizations/' + orgId + '/networks'
8 | response = requests.get(url, headers=myheaders, verify=False)
9 | pprint.pprint(response.json())
10 |
--------------------------------------------------------------------------------
/Chapter03/Cisco/cisco_nxapi_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from ncclient import manager
4 |
5 | conn = manager.connect(
6 | host='192.168.2.50',
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 |
15 | for value in conn.server_capabilities:
16 | print(value)
17 |
18 | conn.close_session()
19 |
--------------------------------------------------------------------------------
/Chapter03/Cisco/cisco_nxapi_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import requests
4 | import json
5 | import pprint
6 |
7 | url='http://192.168.2.50/ins'
8 | switchuser='cisco'
9 | switchpassword='cisco'
10 |
11 | myheaders={'content-type':'application/json-rpc'}
12 | payload=[
13 | {
14 | "jsonrpc": "2.0",
15 | "method": "cli",
16 | "params": {
17 | "cmd": "show version",
18 | "version": 1.2
19 | },
20 | "id": 1
21 | }
22 | ]
23 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json()
24 |
25 | print(response['result']['body']['sys_ver_str'])
26 |
27 |
--------------------------------------------------------------------------------
/Chapter03/Cisco/cisco_nxapi_3.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import requests
4 | import json
5 |
6 | url='http://192.168.2.50/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 lax-cor-r1-new",
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 |
--------------------------------------------------------------------------------
/Chapter03/Cisco/cisco_nxapi_4.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import requests
4 | import json
5 |
6 | url='http://192.168.2.50/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 |
--------------------------------------------------------------------------------
/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 = "sandbox-iosxe-recomm-1.cisco.com"
7 | username = "developer"
8 | password = "C1sco12345"
9 | port = 830
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 |
--------------------------------------------------------------------------------
/Chapter03/Cisco/cisco_yang_1_interfaces.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/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.2.70',
7 | port='830',
8 | username='juniper',
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 |
--------------------------------------------------------------------------------
/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.2.70', port='830', username='juniper', 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 |
--------------------------------------------------------------------------------
/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.2.70', '830', 'juniper', '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 |
--------------------------------------------------------------------------------
/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.2.70', 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_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.2.70', 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 |
--------------------------------------------------------------------------------
/Chapter03/VyOS/vyos_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import vymgmt
4 |
5 | vyos = vymgmt.Router('192.168.2.116', 'vyos', password='vyos')
6 | vyos.login()
7 | vyos.configure()
8 | vyos.set("system domain-name networkautomationnerds.net")
9 | vyos.commit()
10 | vyos.save()
11 | vyos.exit()
12 | vyos.logout()
13 | exit()
14 |
15 |
--------------------------------------------------------------------------------
/Chapter04/Templates/file1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter04/Templates/file1
--------------------------------------------------------------------------------
/Chapter04/Templates/file2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter04/Templates/file2
--------------------------------------------------------------------------------
/Chapter04/Templates/hosts:
--------------------------------------------------------------------------------
1 | [lax_cor_devices]
2 | lax-cor-r1
3 |
4 | [lax_edg_devices]
5 | lax-edg-r1
6 | lax-edg-r2
7 |
8 | [nyc_cor_devices]
9 | nyc-cor-r1
10 |
11 | [nyc_edg_devices]
12 | nyc-edg-r1
13 | nyc-edg-r2
14 |
15 | [lax_dc:children]
16 | lax_cor_devices
17 | lax_edg_devices
18 |
19 | [nyc_dc:children]
20 | nyc_cor_devices
21 | nyc_edg_devices
22 |
23 | [ios_devices:children]
24 | lax_edg_devices
25 | nyc_edg_devices
26 |
27 | [nxos_devices:children]
28 | nyc_cor_devices
29 | lax_cor_devices
30 |
--------------------------------------------------------------------------------
/Chapter04/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 |
--------------------------------------------------------------------------------
/Chapter04/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 |
--------------------------------------------------------------------------------
/Chapter04/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 %}
--------------------------------------------------------------------------------
/Chapter04/Templates/template_1.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Template Basic
3 | hosts: localhost
4 |
5 | tasks:
6 | - name: copy one file to another
7 | template:
8 | src=/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter04/Templates/file1
9 | dest=/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter04/Templates/file2
--------------------------------------------------------------------------------
/Chapter04/Templates/template_2.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=/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter04/Templates/nxos.j2
33 | dest=/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter04/Templates/{{ item.key }}.conf
34 | with_dict: "{{ nexus_devices }}"
35 |
--------------------------------------------------------------------------------
/Chapter04/ansible.cfg:
--------------------------------------------------------------------------------
1 | [persistent_connection]
2 | ssh_type = libssh
3 |
4 |
--------------------------------------------------------------------------------
/Chapter04/host_vars/iosv-1:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.51
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter04/host_vars/iosv-2:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.52
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
--------------------------------------------------------------------------------
/Chapter04/host_vars/lax-cor-r1:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.50
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: nxos
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter04/host_vars/lax-edg-r1:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.51
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter04/host_vars/lax-edg-r2:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.52
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
--------------------------------------------------------------------------------
/Chapter04/host_vars/nyc-cor-r1:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.60
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: nxos
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter04/host_vars/nyc-edg-r1:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.61
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter04/host_vars/nyc-edg-r2:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.62
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
--------------------------------------------------------------------------------
/Chapter04/hosts:
--------------------------------------------------------------------------------
1 | [ios_devices]
2 | iosv-1
3 | iosv-2
4 |
--------------------------------------------------------------------------------
/Chapter04/hosts_full:
--------------------------------------------------------------------------------
1 | [lax_cor_devices]
2 | lax-cor-r1
3 |
4 | [lax_edg_devices]
5 | lax-edg-r1
6 | lax-edg-r2
7 |
8 | [nyc_cor_devices]
9 | nyc-cor-r1
10 |
11 | [nyc_edg_devices]
12 | nyc-edg-r1
13 | nyc-edg-r2
14 |
15 | [lax_dc:children]
16 | lax_cor_devices
17 | lax_edg_devices
18 |
19 | [nyc_dc:children]
20 | nyc_cor_devices
21 | nyc_edg_devices
22 |
23 | [ios_devices:children]
24 | lax_edg_devices
25 | nyc_edg_devices
26 |
27 | [nxos_devices:children]
28 | nyc_cor_devices
29 | lax_cor_devices
30 |
--------------------------------------------------------------------------------
/Chapter04/ios_conditional.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: IOS command output for when clause
3 | hosts: ios_devices
4 | gather_facts: false
5 | tasks:
6 | - name: show hostname
7 | ios_command:
8 | commands:
9 | - show run | i hostname
10 | register: output
11 |
12 | - name: show output with when conditions
13 | when: output.stdout == ["hostname nyc-edg-r2"]
14 | debug:
15 | msg: '{{ output }}'
16 |
17 |
--------------------------------------------------------------------------------
/Chapter04/ios_conditional_config.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: IOS command output for when clause
3 | hosts: ios_devices
4 | gather_facts: false
5 | tasks:
6 | - name: show hostname
7 | ios_command:
8 | commands:
9 | - show run | i hostname
10 | register: output
11 |
12 | - name: show output with when conditions
13 | when: output.stdout == ["hostname nyc-edg-r2"]
14 | ios_config:
15 | lines:
16 | - logging buffered 30000
17 |
18 |
--------------------------------------------------------------------------------
/Chapter04/ios_config_backup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Back Up IOS Device Configurations
3 | hosts: all
4 | gather_facts: false
5 | tasks:
6 | - name: backup
7 | ios_config:
8 | backup: yes
9 |
10 |
--------------------------------------------------------------------------------
/Chapter04/ios_facts_playbook.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: IOS network facts
3 | connection: network_cli
4 | gather_facts: false
5 | hosts: ios_devices
6 | tasks:
7 | - name: Gathering facts via ios_facts module
8 | ios_facts:
9 | when: ansible_network_os == 'ios'
10 |
11 | - name: Display certain facts
12 | debug:
13 | msg: "The hostname is {{ ansible_net_hostname }} running {{ ansible_net_version }}"
14 |
15 | - name: Display all facts for hosts
16 | debug:
17 | var: hostvars
18 |
--------------------------------------------------------------------------------
/Chapter04/nxos_config_backup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Back Up NX-OS Device Configurations
3 | hosts: nxos_devices
4 | gather_facts: false
5 | tasks:
6 | - name: backup
7 | nxos_config:
8 | backup: yes
9 |
10 |
--------------------------------------------------------------------------------
/Chapter04/standard_loop.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Echo Loop Items
3 | hosts: "localhost"
4 | gather_facts: false
5 | tasks:
6 | - name: echo loop items
7 | command: echo "{{ item }}"
8 | loop:
9 | - 'r1'
10 | - 'r2'
11 | - 'r3'
12 | - 'r4'
13 | - 'r5'
14 |
--------------------------------------------------------------------------------
/Chapter04/standard_loop_vlan_example.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Add Multiple Vlans
3 | hosts: "nyc-cor-r1"
4 | gather_facts: false
5 | connection: network_cli
6 | vars:
7 | vlan_numbers: [100, 200, 300]
8 | tasks:
9 | - name: add vlans
10 | nxos_config:
11 | lines:
12 | - vlan {{ item }}
13 | loop: "{{ vlan_numbers }}"
14 | register: output
15 |
--------------------------------------------------------------------------------
/Chapter04/standard_loop_vlan_example_2.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Add Multiple Vlans
3 | hosts: "nyc-cor-r1"
4 | gather_facts: false
5 | connection: network_cli
6 | vars:
7 | vlans: {
8 | "100": {"description": "floor_1", "ip": "192.168.10.1"},
9 | "200": {"description": "floor_2", "ip": "192.168.20.1"},
10 | "300": {"description": "floor_3", "ip": "192.168.30.1"}
11 | }
12 |
13 | tasks:
14 | - name: add vlans
15 | nxos_config:
16 | lines:
17 | - vlan {{ item.key }}
18 | with_dict: "{{ vlans }}"
19 |
20 | - name: configure vlans
21 | nxos_config:
22 | lines:
23 | - description {{ item.value.description }}
24 | - ip address {{ item.value.ip }}/24
25 | parents: interface vlan {{ item.key }}
26 | with_dict: "{{ vlans }}"
27 |
28 |
--------------------------------------------------------------------------------
/Chapter05/ansible_container/ansible.cfg:
--------------------------------------------------------------------------------
1 | [defaults]
2 | host_key_checking = False
3 |
4 | [persistent_connection]
5 | ssh_type = libssh
6 |
7 |
--------------------------------------------------------------------------------
/Chapter05/ansible_container/host_vars/iosv-1:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.51
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter05/ansible_container/host_vars/iosv-2:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.52
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
--------------------------------------------------------------------------------
/Chapter05/ansible_container/hosts:
--------------------------------------------------------------------------------
1 | [ios_devices]
2 | iosv-1
3 | iosv-2
4 |
--------------------------------------------------------------------------------
/Chapter05/ansible_container/ios_config_backup.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Back Up IOS Device Configurations
3 | hosts: all
4 | gather_facts: false
5 | tasks:
6 | - name: backup
7 | ios_config:
8 | backup: yes
9 |
10 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/.srl02.clab.yml.bak:
--------------------------------------------------------------------------------
1 | # topology documentation: http://containerlab.dev/lab-examples/two-srls/
2 | # https://github.com/srl-labs/containerlab/blob/main/lab-examples/srl02/srl02.clab.yml
3 | name: srl02
4 |
5 | topology:
6 | nodes:
7 | srl1:
8 | kind: srl
9 | image: ghcr.io/nokia/srlinux
10 | startup-config: srl1.cfg
11 | srl2:
12 | kind: srl
13 | image: ghcr.io/nokia/srlinux
14 | startup-config: srl2.cfg
15 |
16 | links:
17 | - endpoints: ["srl1:e1-1", "srl2:e1-1"]
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/ansible-inventory.yml:
--------------------------------------------------------------------------------
1 | all:
2 | children:
3 | srl:
4 | hosts:
5 | clab-srl02-srl1:
6 | ansible_host: 172.20.20.3
7 | clab-srl02-srl2:
8 | ansible_host: 172.20.20.2
9 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/ca/root/root-ca.csr:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE REQUEST-----
2 | MIICxjCCAa4CAQAwXzELMAkGA1UEBhMCQkUxEDAOBgNVBAcTB0FudHdlcnAxDjAM
3 | BgNVBAoTBU5va2lhMRYwFAYDVQQLEw1Db250YWluZXIgbGFiMRYwFAYDVQQDEw1z
4 | cmwwMiBSb290IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAw23n
5 | yPJ4XUramJJfMLPbVHWb/C0pE6xYMPJ6AP6+LIosjf5ofbTd7HhPVz3koTlF2iCH
6 | sT6kFoz51sLbsiLV5SrTr0kZYRhd3MA1OUCiCERb3YMzFWFXThTiwkaCY+xlIKjS
7 | Nkg/HgwuzJ2xmzpnKP+S1zU88goMPhOyGvlQdkFouttqegrGEdeInq5vt6al36xF
8 | nWRIeT1X6vPMtt3Hp1WGXld5NbxiSqIdw3FiGzaUET4SKoLZFcxILPrCK1E6D6vh
9 | 5ntUp20GmjhdBbD/gvVCna78/KDDydueJiXPSFmE1kZhM09cHIfttatCY/NptT0h
10 | NH7CdWUgX9LpxsxniwIDAQABoCIwIAYJKoZIhvcNAQkOMRMwETAPBgNVHRMBAf8E
11 | BTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCHw8xWGEjfAHSIvEmeIP8/T/R+oj5/
12 | T1fLUAKT4L1JndkRQT1nHUQ+S5BmJUxIBXmfhNzQwWUPHXOmcD/7TO7w61R0inBw
13 | dMmW0cGw9p5UEw7taFQ43SKz+LhEs975Xm5JS66gJR+v9aXykSujOSJVW/QWOCza
14 | 2h7LNvIEP3ABu5XmQFMsdtAR5ZrEcB3yok9IS514got8oh80n69FgiGEtLxWrRcj
15 | 4bcYvK3XBudAJol3AShf0sXGIQU5kguQ54OD/XqQVCa8eKAyHd4e4aigmpT3BMcT
16 | Qo/5P1gWIuIklC/oIPv8o3e2JwRasLDiUQjfNuM5hlw3EUzNJZhGUtDL
17 | -----END CERTIFICATE REQUEST-----
18 |
19 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/ca/root/root-ca.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIIDkDCCAnigAwIBAgIUc/q/ZoRKB0HXufJeLztqGSc+vWUwDQYJKoZIhvcNAQEL
3 | BQAwXzELMAkGA1UEBhMCQkUxEDAOBgNVBAcTB0FudHdlcnAxDjAMBgNVBAoTBU5v
4 | a2lhMRYwFAYDVQQLEw1Db250YWluZXIgbGFiMRYwFAYDVQQDEw1zcmwwMiBSb290
5 | IENBMCAXDTIyMDkxMjAwMzgwMFoYDzIwNTIwOTA0MDAzODAwWjBfMQswCQYDVQQG
6 | EwJCRTEQMA4GA1UEBxMHQW50d2VycDEOMAwGA1UEChMFTm9raWExFjAUBgNVBAsT
7 | DUNvbnRhaW5lciBsYWIxFjAUBgNVBAMTDXNybDAyIFJvb3QgQ0EwggEiMA0GCSqG
8 | SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDDbefI8nhdStqYkl8ws9tUdZv8LSkTrFgw
9 | 8noA/r4siiyN/mh9tN3seE9XPeShOUXaIIexPqQWjPnWwtuyItXlKtOvSRlhGF3c
10 | wDU5QKIIRFvdgzMVYVdOFOLCRoJj7GUgqNI2SD8eDC7MnbGbOmco/5LXNTzyCgw+
11 | E7Ia+VB2QWi622p6CsYR14ierm+3pqXfrEWdZEh5PVfq88y23cenVYZeV3k1vGJK
12 | oh3DcWIbNpQRPhIqgtkVzEgs+sIrUToPq+Hme1SnbQaaOF0FsP+C9UKdrvz8oMPJ
13 | 254mJc9IWYTWRmEzT1wch+21q0Jj82m1PSE0fsJ1ZSBf0unGzGeLAgMBAAGjQjBA
14 | MA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRHY9x0
15 | CHqeZmKNC83sihFAf1PI3TANBgkqhkiG9w0BAQsFAAOCAQEAFdHC0fEnv0ELZgQD
16 | FQfZDSsRzP5WSw4jwHQBnOJAIsGNw+a5av5m2bocbS5YEIlVvP8O2/EkD9DfhAUe
17 | tsjhTcnQnIVMaX3JewS7cDSzdDbR347YYtryyr0Plqq9riSIDTRbnj0sIExqM0J+
18 | ERKRngeparfozVcFTC9Nhfu84p5twM/gbnqvE8CPDkHC1Nrv0DmVyMO8GqZx8keD
19 | sahNOZliFW+yIBWZ4+5bA3CGdPnu1HeKIUz6ZsOx3adsKDVcYXydFf7LB4KC7OoI
20 | xbPeOzcblR+jE1UVCkGTor0zu9SpEOxUe9NHpTsZc+VjLT5a3OCekbEleagYIE4b
21 | K7klxA==
22 | -----END CERTIFICATE-----
23 |
24 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/ca/srl1/srl1.csr:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE REQUEST-----
2 | MIIC5jCCAc4CAQAwXzELMAkGA1UEBhMCQkUxEDAOBgNVBAcTB0FudHdlcnAxDjAM
3 | BgNVBAoTBU5va2lhMRYwFAYDVQQLEw1Db250YWluZXIgbGFiMRYwFAYDVQQDEw1z
4 | cmwxLnNybDAyLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7VQ7
5 | 3kZ5P0zEc00nRCOh2Vt0e4aPUiPtrcvMkvjc67lFAaNbNN6Js7A9WWc8FHRZG4Tm
6 | Rx/vdm2pQQhuTmmZR37NkT/Rl/FHCPguAZ8gFqx2i7LtFYN826h8Vn40rpUUZPv3
7 | umGKzcvb2B0VvLDvLJvVqmY5jdDUmd0tpN54anRFtD82zH7gxUz/K/TpdCQhDnUO
8 | +dC9ZM2kjft9DADit4EW/3pTNfnRHBary2en9Km5wmSq8c2rk0wOpB5fiXh7I3uU
9 | kEWCjI6MuJeYF996NZWBG3NtPKwyNWFQNNZTkHYY9FFYBFnm5KY8L8ObAJuTcdbV
10 | KHKgbVdCFhJ095LPWwIDAQABoEIwQAYJKoZIhvcNAQkOMTMwMTAvBgNVHREEKDAm
11 | ggRzcmwxgg9jbGFiLXNybDAyLXNybDGCDXNybDEuc3JsMDIuaW8wDQYJKoZIhvcN
12 | AQELBQADggEBAJbB0pj6qJlQ4XxhwLRUxp8bXwMOKuHz6PcKE793fhL+YYx3uYNK
13 | GvDhOXkI4dJvNLMrqtIytvAnWVdz9DqUU2AX2dSfvI0Qym+PXC3k7z3vqHlfz5kV
14 | wbfaOKwa3mFXR9/Zo+0VCT8VIaGSUdtAKCIqk7kfjEmQw/eD7Tul1dXqoXQlSwfk
15 | tmgvkGvIctbjIt4FrzMl3aVTiZ7AbG1bMgYn/vSbSYk43K4VxYV1uUidjGWeKEqM
16 | Tl7EBze8kV78YFu0wZ5enQzuLVZ9Tu1WWZZTjNgeMEbnIlfm5kF5alZLBHXlEZ7e
17 | Z87H5mSuoAthkmS9NHR6/IlD7VGD4DLgKbA=
18 | -----END CERTIFICATE REQUEST-----
19 |
20 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/ca/srl1/srl1.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIID/jCCAuagAwIBAgIUAeKdnoRvcX06NRqAOCMFxIP8lNMwDQYJKoZIhvcNAQEL
3 | BQAwXzELMAkGA1UEBhMCQkUxEDAOBgNVBAcTB0FudHdlcnAxDjAMBgNVBAoTBU5v
4 | a2lhMRYwFAYDVQQLEw1Db250YWluZXIgbGFiMRYwFAYDVQQDEw1zcmwwMiBSb290
5 | IENBMB4XDTIyMDkxMjAwMzgwMFoXDTIzMDkxMjAwMzgwMFowXzELMAkGA1UEBhMC
6 | QkUxEDAOBgNVBAcTB0FudHdlcnAxDjAMBgNVBAoTBU5va2lhMRYwFAYDVQQLEw1D
7 | b250YWluZXIgbGFiMRYwFAYDVQQDEw1zcmwxLnNybDAyLmlvMIIBIjANBgkqhkiG
8 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA7VQ73kZ5P0zEc00nRCOh2Vt0e4aPUiPtrcvM
9 | kvjc67lFAaNbNN6Js7A9WWc8FHRZG4TmRx/vdm2pQQhuTmmZR37NkT/Rl/FHCPgu
10 | AZ8gFqx2i7LtFYN826h8Vn40rpUUZPv3umGKzcvb2B0VvLDvLJvVqmY5jdDUmd0t
11 | pN54anRFtD82zH7gxUz/K/TpdCQhDnUO+dC9ZM2kjft9DADit4EW/3pTNfnRHBar
12 | y2en9Km5wmSq8c2rk0wOpB5fiXh7I3uUkEWCjI6MuJeYF996NZWBG3NtPKwyNWFQ
13 | NNZTkHYY9FFYBFnm5KY8L8ObAJuTcdbVKHKgbVdCFhJ095LPWwIDAQABo4GxMIGu
14 | MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
15 | DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUUdfgVSZnOg/bgDKa/qznpqxL9OswHwYD
16 | VR0jBBgwFoAUR2PcdAh6nmZijQvN7IoRQH9TyN0wLwYDVR0RBCgwJoIEc3JsMYIP
17 | Y2xhYi1zcmwwMi1zcmwxgg1zcmwxLnNybDAyLmlvMA0GCSqGSIb3DQEBCwUAA4IB
18 | AQC923UlzccV+nSOyDtmoU0DYliZ9UKY4CYsl2IwAIEjYyVGEGS5iCX2wVTZgkt5
19 | i8/axFvfTpR7oUooxLQvGAVEOQLv2R6Fq0nqvKAxwbCHJQjloxqfRfBUGyTkRy8u
20 | quJbBcxAd70BNHjcxcFA7cfGLCEo7TRuiHyi76HiQLtttYvCXbVBOLMadS7VRC+w
21 | gPd8l7G7A/zXdKwNM2XR0x6ZQNBdVvJ/mHdYtwVbnQnzg2HDhzR3b6IFQSBWQDi6
22 | /dOrRxLpI2mO9GFiejN8KsqdxZVA5kzPIuTTRnQSezatusiJHPfyiGFbcK/uMbRp
23 | +eSKjuAjtiI3krlMX+RTw0dD
24 | -----END CERTIFICATE-----
25 |
26 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/ca/srl2/srl2.csr:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE REQUEST-----
2 | MIIC5jCCAc4CAQAwXzELMAkGA1UEBhMCQkUxEDAOBgNVBAcTB0FudHdlcnAxDjAM
3 | BgNVBAoTBU5va2lhMRYwFAYDVQQLEw1Db250YWluZXIgbGFiMRYwFAYDVQQDEw1z
4 | cmwyLnNybDAyLmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5btb
5 | 5NR3OcyiACTHsTQALfKtxtKZowQ4OAobTJFKgra+FE8M1oRSoxU8//Xj9KRbF4l5
6 | tvhvvjD2W24Z1at8qXgv6V26rUGHw8b28SK9grumE4YWSSy58syVPg4CPk4qJnwa
7 | 14Pl6WEI2YkSN8qIUKZc8YNP7/fFM6HEuPbtUgSp6xxcf1fhX1eZQvPB8FXTSSRS
8 | fx/zXmPeZfshlwYDoBWAfjKX/11PtnrnpB95x/Z8hzA8XOgknMijzthWNB/01qC9
9 | 4V8dcEVEv2qWCMUV66O544i1hFdH8djDFNTBMQt4zApmYANp3PCE6YucuFivz9gf
10 | cUa0s0HYguUb+uHDWwIDAQABoEIwQAYJKoZIhvcNAQkOMTMwMTAvBgNVHREEKDAm
11 | ggRzcmwygg9jbGFiLXNybDAyLXNybDKCDXNybDIuc3JsMDIuaW8wDQYJKoZIhvcN
12 | AQELBQADggEBAGXmaagAZKlFjH0rxIvi/uvgEuXAPK3GYZUbaXYbG/qcVEJByrz9
13 | VgSZ6g77ZlHjSt2pQ3wsXY+i08pPvmHwbnhTp0q+gZE/02WbcKwRIfyycoEqmKiU
14 | 5AnW54hPyL24Z8OUK0/wER/3yAY3SMhXfrGxhRQ9/oWA+0GQnrKEeoR/Iq0QCW88
15 | dRSWqYMIKEi/VY+MzG9XLtJinDmeWLgVc1fDovSYdjEEZ7Q1iawi90LuBKlXKdcX
16 | GebGLZTZsa7Zw3tNRSl5kUfhoN119k2F7bcVRmpBpVjEZaI3uBTvStGCBLExdw30
17 | c+lUirvsQRN1xw4lIRVU0i0hQtONkN/IRxs=
18 | -----END CERTIFICATE REQUEST-----
19 |
20 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/ca/srl2/srl2.pem:
--------------------------------------------------------------------------------
1 | -----BEGIN CERTIFICATE-----
2 | MIID/jCCAuagAwIBAgIUcv7tSlIu1ri7yRersljdrCZoK8owDQYJKoZIhvcNAQEL
3 | BQAwXzELMAkGA1UEBhMCQkUxEDAOBgNVBAcTB0FudHdlcnAxDjAMBgNVBAoTBU5v
4 | a2lhMRYwFAYDVQQLEw1Db250YWluZXIgbGFiMRYwFAYDVQQDEw1zcmwwMiBSb290
5 | IENBMB4XDTIyMDkxMjAwMzgwMFoXDTIzMDkxMjAwMzgwMFowXzELMAkGA1UEBhMC
6 | QkUxEDAOBgNVBAcTB0FudHdlcnAxDjAMBgNVBAoTBU5va2lhMRYwFAYDVQQLEw1D
7 | b250YWluZXIgbGFiMRYwFAYDVQQDEw1zcmwyLnNybDAyLmlvMIIBIjANBgkqhkiG
8 | 9w0BAQEFAAOCAQ8AMIIBCgKCAQEA5btb5NR3OcyiACTHsTQALfKtxtKZowQ4OAob
9 | TJFKgra+FE8M1oRSoxU8//Xj9KRbF4l5tvhvvjD2W24Z1at8qXgv6V26rUGHw8b2
10 | 8SK9grumE4YWSSy58syVPg4CPk4qJnwa14Pl6WEI2YkSN8qIUKZc8YNP7/fFM6HE
11 | uPbtUgSp6xxcf1fhX1eZQvPB8FXTSSRSfx/zXmPeZfshlwYDoBWAfjKX/11Ptnrn
12 | pB95x/Z8hzA8XOgknMijzthWNB/01qC94V8dcEVEv2qWCMUV66O544i1hFdH8djD
13 | FNTBMQt4zApmYANp3PCE6YucuFivz9gfcUa0s0HYguUb+uHDWwIDAQABo4GxMIGu
14 | MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw
15 | DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQUFC2KkXjLwj7m++9DJ/DF+KgHFeUwHwYD
16 | VR0jBBgwFoAUR2PcdAh6nmZijQvN7IoRQH9TyN0wLwYDVR0RBCgwJoIEc3JsMoIP
17 | Y2xhYi1zcmwwMi1zcmwygg1zcmwyLnNybDAyLmlvMA0GCSqGSIb3DQEBCwUAA4IB
18 | AQAJppwYcxh4GvS1cp/T36Ln3xm/qE41l7Ox5aZjca91u7tsY7y6U5BjTlABgeiM
19 | RqOdj2YxrfOyu5nirvD/EIWy1wUioscL+m25frh95Qc9DLc9N3aV6B/jowEdGV1y
20 | lWAfds48VopCDSQ2i3Vd9s2wJOvtH+R9+3AuzNt4FwhmDcY6HjTyMDqlSWkaP+9N
21 | dO2HkQHeSfJjQAlPvouDH1lObSetiCGZPv7ly8ABf7VEc61RhKV/PXMZ1EXvv6Y2
22 | DvKLNXmWG/fmv09SixHZbNQZhMIE8Nu3aSrHKIWVd/xcNg4nsweaIFaQwrK053PX
23 | MZZPxGIM8qWExk2Lv/7VL4Xz
24 | -----END CERTIFICATE-----
25 |
26 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/srl1/topology.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Nokia
2 | # Licensed under the BSD 3-Clause License.
3 | # SPDX-License-Identifier: BSD-3-Clause
4 |
5 | chassis_configuration:
6 | "chassis_type": 65
7 | "base_mac": "1a:85:00:00:00:00"
8 | "cpm_card_type": 176
9 |
10 | slot_configuration:
11 | 1:
12 | "card_type": 176
13 | "mda_type": 195 # imm48-25g-sfp28+8-100g-qsfp28
14 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/srl2/topology.yml:
--------------------------------------------------------------------------------
1 | # Copyright 2020 Nokia
2 | # Licensed under the BSD 3-Clause License.
3 | # SPDX-License-Identifier: BSD-3-Clause
4 |
5 | chassis_configuration:
6 | "chassis_type": 65
7 | "base_mac": "1a:21:01:00:00:00"
8 | "cpm_card_type": 176
9 |
10 | slot_configuration:
11 | 1:
12 | "card_type": 176
13 | "mda_type": 195 # imm48-25g-sfp28+8-100g-qsfp28
14 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/clab-srl02/topology-data.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "srl02",
3 | "type": "clab",
4 | "clab": {
5 | "config": {
6 | "prefix": "clab",
7 | "mgmt": {
8 | "network": "clab",
9 | "bridge": "br-4807fa9091c5",
10 | "ipv4-subnet": "172.20.20.0/24",
11 | "ipv4-gw": "172.20.20.1",
12 | "ipv6-subnet": "2001:172:20:20::/64",
13 | "ipv6-gw": "2001:172:20:20::1",
14 | "mtu": "1500",
15 | "external-access": true
16 | }
17 | }
18 | },
19 | "nodes": {
20 | "srl1": {
21 | "index": "0",
22 | "shortname": "srl1",
23 | "longname": "clab-srl02-srl1",
24 | "fqdn": "srl1.srl02.io",
25 | "group": "",
26 | "labdir": "/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter05/container_lab/clab-srl02/srl1",
27 | "kind": "srl",
28 | "image": "ghcr.io/nokia/srlinux",
29 | "mgmt-net": "",
30 | "mgmt-intf": "",
31 | "mgmt-ipv4-address": "172.20.20.3",
32 | "mgmt-ipv4-prefix-length": 24,
33 | "mgmt-ipv6-address": "2001:172:20:20::3",
34 | "mgmt-ipv6-prefix-length": 64,
35 | "mac-address": "",
36 | "labels": {
37 | "clab-mgmt-net-bridge": "br-4807fa9091c5",
38 | "clab-node-group": "",
39 | "clab-node-kind": "srl",
40 | "clab-node-lab-dir": "/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter05/container_lab/clab-srl02/srl1",
41 | "clab-node-name": "srl1",
42 | "clab-node-type": "ixrd2",
43 | "clab-topo-file": "/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter05/container_lab/srl02.clab.yml",
44 | "containerlab": "srl02"
45 | }
46 | },
47 | "srl2": {
48 | "index": "1",
49 | "shortname": "srl2",
50 | "longname": "clab-srl02-srl2",
51 | "fqdn": "srl2.srl02.io",
52 | "group": "",
53 | "labdir": "/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter05/container_lab/clab-srl02/srl2",
54 | "kind": "srl",
55 | "image": "ghcr.io/nokia/srlinux",
56 | "mgmt-net": "",
57 | "mgmt-intf": "",
58 | "mgmt-ipv4-address": "172.20.20.2",
59 | "mgmt-ipv4-prefix-length": 24,
60 | "mgmt-ipv6-address": "2001:172:20:20::2",
61 | "mgmt-ipv6-prefix-length": 64,
62 | "mac-address": "",
63 | "labels": {
64 | "clab-mgmt-net-bridge": "br-4807fa9091c5",
65 | "clab-node-group": "",
66 | "clab-node-kind": "srl",
67 | "clab-node-lab-dir": "/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter05/container_lab/clab-srl02/srl2",
68 | "clab-node-name": "srl2",
69 | "clab-node-type": "ixrd2",
70 | "clab-topo-file": "/home/echou/Mastering_Python_Networking_Fourth_Edition/Chapter05/container_lab/srl02.clab.yml",
71 | "containerlab": "srl02"
72 | }
73 | }
74 | },
75 | "links": [
76 | {
77 | "a": {
78 | "node": "srl1",
79 | "interface": "e1-1",
80 | "mac": "aa:c1:ab:04:fc:b3",
81 | "peer": "z"
82 | },
83 | "z": {
84 | "node": "srl2",
85 | "interface": "e1-1",
86 | "mac": "aa:c1:ab:08:0b:b7",
87 | "peer": "a"
88 | }
89 | }
90 | ]
91 | }
92 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/srl02.clab.yml:
--------------------------------------------------------------------------------
1 | # topology documentation: http://containerlab.dev/lab-examples/two-srls/
2 | # https://github.com/srl-labs/containerlab/blob/main/lab-examples/srl02/srl02.clab.yml
3 | name: srl02
4 |
5 | topology:
6 | nodes:
7 | srl1:
8 | kind: srl
9 | image: ghcr.io/nokia/srlinux
10 | startup-config: srl1.cfg
11 | srl2:
12 | kind: srl
13 | image: ghcr.io/nokia/srlinux
14 | startup-config: srl2.cfg
15 |
16 | links:
17 | - endpoints: ["srl1:e1-1", "srl2:e1-1"]
18 |
19 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/srl1.cfg:
--------------------------------------------------------------------------------
1 | set / interface ethernet-1/1
2 | set / interface ethernet-1/1 subinterface 0
3 | set / interface ethernet-1/1 subinterface 0 ipv4
4 | set / interface ethernet-1/1 subinterface 0 ipv4 address 192.168.0.0/31
5 | set / interface ethernet-1/1 subinterface 0 ipv6
6 | set / interface ethernet-1/1 subinterface 0 ipv6 address 2002::192.168.0.0/127
7 |
8 | set / network-instance default
9 | set / network-instance default interface ethernet-1/1.0
10 |
11 |
--------------------------------------------------------------------------------
/Chapter05/container_lab/srl2.cfg:
--------------------------------------------------------------------------------
1 | set / interface ethernet-1/1
2 | set / interface ethernet-1/1 subinterface 0
3 | set / interface ethernet-1/1 subinterface 0 ipv4
4 | set / interface ethernet-1/1 subinterface 0 ipv4 address 192.168.0.1/31
5 | set / interface ethernet-1/1 subinterface 0 ipv6
6 | set / interface ethernet-1/1 subinterface 0 ipv6 address 2002::192.168.0.1/127
7 |
8 | set / network-instance default
9 | set / network-instance default interface ethernet-1/1.0
10 |
11 |
--------------------------------------------------------------------------------
/Chapter06/access_list_mac_iosv.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Configure MAC Access List
3 | hosts: "nyc-edg-r1"
4 | gather_facts: false
5 | connection: local
6 |
7 | vars:
8 | cli:
9 | host: "{{ ansible_host }}"
10 | username: "{{ ansible_user }}"
11 | password: "{{ ansible_ssh_pass }}"
12 |
13 | tasks:
14 | - name: Deny Hosts with vendor id fa16.3e00.0000
15 | ios_config:
16 | lines:
17 | - access-list 700 deny fa16.3e00.0000 0000.00FF.FFFF
18 | - access-list 700 permit 0000.0000.0000 FFFF.FFFF.FFFF
19 | - name: Apply filter on bridge group 1
20 | ios_config:
21 | lines:
22 | - bridge-group 1
23 | - bridge-group 1 input-address-list 700
24 | parents:
25 | - interface GigabitEthernet0/1
26 |
27 |
--------------------------------------------------------------------------------
/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_user }}"
11 | password: "{{ ansible_ssh_pass }}"
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 | - nxos_acl:
25 | name: border_inbound
26 | seq: 30
27 | action: deny
28 | proto: tcp
29 | src: 192.168.0.0/16
30 | dest: any
31 | state: present
32 | log: enable
33 | - nxos_acl:
34 | name: border_inbound
35 | seq: 40
36 | action: permit
37 | proto: tcp
38 | src: any
39 | dest: 10.0.0.9/32
40 | dest_port_op: eq
41 | dest_port1: 22
42 | state: present
43 | log: enable
44 | - nxos_acl:
45 | name: border_inbound
46 | seq: 50
47 | action: permit
48 | proto: tcp
49 | src: any
50 | dest: 10.0.0.9/32
51 | dest_port_op: eq
52 | dest_port1: 80
53 | state: present
54 | log: enable
55 | - nxos_acl:
56 | name: border_inbound
57 | seq: 60
58 | action: permit
59 | proto: tcp
60 | src: any
61 | dest: any
62 | state: present
63 | log: enable
64 | established: enable
65 | - nxos_acl:
66 | name: border_inbound
67 | seq: 1000
68 | action: deny
69 | proto: ip
70 | src: any
71 | dest: any
72 | state: present
73 | log: enable
74 | - name: apply ingress acl to Ethernet 2/4
75 | nxos_acl_interface:
76 | name: border_inbound
77 | interface: Ethernet2/4
78 | direction: ingress
79 | state: present
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Chapter06/chapter6_topology.virl.backup:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | flat
5 |
6 |
7 |
8 | ! IOS Config generated on 2017-09-08 18:49
9 | ! by autonetkit_0.23.5
10 | !
11 | hostname iosv-1
12 | boot-start-marker
13 | boot-end-marker
14 | !
15 | vrf definition Mgmt-intf
16 | !
17 | address-family ipv4
18 | exit-address-family
19 | !
20 | address-family ipv6
21 | exit-address-family
22 | !
23 | !
24 | !
25 | no aaa new-model
26 | !
27 | !
28 | ip cef
29 | ipv6 unicast-routing
30 | ipv6 cef
31 | !
32 | !
33 | service timestamps debug datetime msec
34 | service timestamps log datetime msec
35 | no service password-encryption
36 | no service config
37 | enable password cisco
38 | ip classless
39 | ip subnet-zero
40 | no ip domain lookup
41 | ip domain name virl.info
42 | crypto key generate rsa modulus 768
43 | ip ssh server algorithm authentication password
44 | username cisco privilege 15 secret cisco
45 | line vty 0 4
46 | transport input ssh telnet
47 | exec-timeout 720 0
48 | password cisco
49 | login local
50 | line con 0
51 | password cisco
52 | !
53 | no cdp run
54 | !
55 | !
56 | interface Loopback0
57 | description Loopback
58 | ip address 192.168.0.2 255.255.255.255
59 | !
60 | interface GigabitEthernet0/0
61 | description OOB Management
62 | vrf forwarding Mgmt-intf
63 | ! Configured on launch
64 | no ip address
65 | duplex full
66 | speed auto
67 | no shutdown
68 | !
69 | interface GigabitEthernet0/1
70 | description to nx-osv-1
71 | ip address 10.0.0.13 255.255.255.252
72 | ip ospf cost 1
73 | duplex full
74 | speed auto
75 | no shutdown
76 | !
77 | interface GigabitEthernet0/2
78 | description to Server
79 | ip address 10.0.0.10 255.255.255.252
80 | ip ospf cost 1
81 | duplex full
82 | speed auto
83 | no shutdown
84 | !
85 | !
86 | !
87 | router ospf 1
88 | network 192.168.0.2 0.0.0.0 area 0
89 | log-adjacency-changes
90 | passive-interface Loopback0
91 | network 10.0.0.12 0.0.0.3 area 0
92 | network 10.0.0.8 0.0.0.3 area 0
93 | !
94 | !
95 | router bgp 1
96 | bgp router-id 192.168.0.2
97 | no synchronization
98 | ! ibgp
99 | ! ibgp peers
100 | !
101 | neighbor 192.168.0.1 remote-as 1
102 | neighbor 192.168.0.1 description iBGP peer nx-osv-1
103 | neighbor 192.168.0.1 update-source Loopback0
104 | !
105 | !
106 | !
107 | address-family ipv4
108 | network 192.168.0.2 mask 255.255.255.255
109 | neighbor 192.168.0.1 activate
110 | exit-address-family
111 | !
112 | !
113 | !
114 | end
115 |
116 | 172.16.1.154
117 |
118 |
119 |
120 |
121 |
122 |
123 | ! NX-OSv Config generated on 2017-09-08 18:49
124 | ! by autonetkit_0.23.5
125 | !
126 | version 6.2(1)
127 | license grace-period
128 | !
129 | hostname nx-osv-1
130 | vdc nx-osv-1 id 1
131 | allocate interface Ethernet2/1-48
132 | allocate interface Ethernet3/1-48
133 | limit-resource vlan minimum 16 maximum 4094
134 | limit-resource vrf minimum 2 maximum 4096
135 | limit-resource port-channel minimum 0 maximum 768
136 | limit-resource u4route-mem minimum 96 maximum 96
137 | limit-resource u6route-mem minimum 24 maximum 24
138 | limit-resource m4route-mem minimum 58 maximum 58
139 | limit-resource m6route-mem minimum 8 maximum 8
140 |
141 | feature telnet
142 |
143 | feature ospf
144 | feature bgp
145 |
146 | username adminbackup password 5 ! role network-operator
147 | username admin password 5 $1$KuOSBsvW$Cy0TSD..gEBGBPjzpDgf51 role network-admin
148 | username cisco password 5 $1$Nk7ZkwH0$fyiRmMMfIheqE3BqvcL0C1 role network-operator
149 | username cisco role network-admin
150 | username lab password 5 $1$buoy/oqy$.EXQz8rCn72ii8qtdldj00 role network-admin
151 | no password strength-check
152 | ip domain-lookup
153 | copp profile strict
154 | snmp-server user lab network-admin auth md5 0x5ceb414591539ee35159fca86fdfa101 priv 0x5ceb414591539ee35159fca86fdfa101 localizedkey
155 | snmp-server user admin network-admin auth md5 0x328945d53e05e8e7207f8c20b142f0b7 priv 0x328945d53e05e8e7207f8c20b142f0b7 localizedkey
156 | snmp-server user cisco network-operator auth md5 0x55b3c64a53fb95518e75358ee75e82e9 priv 0x55b3c64a53fb95518e75358ee75e82e9 localizedkey
157 | snmp-server user cisco network-admin
158 | rmon event 1 log trap public description FATAL(1) owner PMON@FATAL
159 | rmon event 2 log trap public description CRITICAL(2) owner PMON@CRITICAL
160 | rmon event 3 log trap public description ERROR(3) owner PMON@ERROR
161 | rmon event 4 log trap public description WARNING(4) owner PMON@WARNING
162 | rmon event 5 log trap public description INFORMATION(5) owner PMON@INFO
163 |
164 |
165 | vlan 1
166 |
167 | vrf context management
168 | hardware forwarding unicast trace
169 |
170 | interface Loopback0
171 | description Loopback
172 | ip address 192.168.0.1/32
173 | ip router ospf 1 area 0
174 |
175 | interface Ethernet2/1
176 | description to Client
177 | ip address 10.0.0.6/30
178 | ip router ospf 1 area 0
179 | duplex full
180 | mac-address fa16.3e00.0001
181 | no shutdown
182 |
183 | interface Ethernet2/2
184 | description to iosv-1
185 | ip address 10.0.0.14/30
186 | ip router ospf 1 area 0
187 | duplex full
188 | mac-address fa16.3e00.0002
189 | no shutdown
190 |
191 | interface mgmt0
192 | description OOB Management
193 | ! Configured on launch
194 | no ip address
195 | duplex full
196 | mac-address fa16.3e00.0003
197 | no shutdown
198 | vrf member management
199 |
200 |
201 | line console
202 | line vty
203 | router ospf 1
204 | router-id 192.168.0.1
205 | router bgp 1
206 | router-id 192.168.0.1
207 | address-family ipv4 unicast
208 | network 192.168.0.1/32
209 | !
210 | ! iBGP
211 | !
212 | ! iBGP peers
213 | !
214 | neighbor 192.168.0.2 remote-as 1
215 | description iBGP peer iosv-1
216 | update-source Loopback0
217 | address-family ipv4 unicast
218 | !
219 | !
220 |
221 |
222 | 172.16.1.155
223 |
224 |
225 |
226 |
227 |
228 |
229 | #cloud-config
230 | bootcmd:
231 | - ln -s -t /etc/rc.d /etc/rc.local
232 | hostname: Client
233 | manage_etc_hosts: true
234 | runcmd:
235 | - start ttyS0
236 | - systemctl start getty@ttyS0.service
237 | - systemctl start rc-local
238 | - sed -i '/^\s*PasswordAuthentication\s\+no/d' /etc/ssh/sshd_config
239 | - echo "UseDNS no" >> /etc/ssh/sshd_config
240 | - service ssh restart
241 | - service sshd restart
242 | users:
243 | - default
244 | - gecos: User configured by VIRL Configuration Engine 0.23.10
245 | lock-passwd: false
246 | name: cisco
247 | plain-text-passwd: cisco
248 | shell: /bin/bash
249 | ssh-authorized-keys:
250 | - VIRL-USER-SSH-PUBLIC-KEY
251 | sudo: ALL=(ALL) ALL
252 | write_files:
253 | - path: /etc/init/ttyS0.conf
254 | owner: root:root
255 | content: |
256 | # ttyS0 - getty
257 | # This service maintains a getty on ttyS0 from the point the system is
258 | # started until it is shut down again.
259 | start on stopped rc or RUNLEVEL=[12345]
260 | stop on runlevel [!12345]
261 | respawn
262 | exec /sbin/getty -L 115200 ttyS0 vt102
263 | permissions: '0644'
264 | - path: /etc/systemd/system/dhclient@.service
265 | content: |
266 | [Unit]
267 | Description=Run dhclient on %i interface
268 | After=network.target
269 | [Service]
270 | Type=oneshot
271 | ExecStart=/sbin/dhclient %i -pf /var/run/dhclient.%i.pid -lf /var/lib/dhclient/dhclient.%i.lease
272 | RemainAfterExit=yes
273 | owner: root:root
274 | permissions: '0644'
275 | - path: /etc/rc.local
276 | owner: root:root
277 | permissions: '0755'
278 | content: |-
279 | #!/bin/sh
280 | ifconfig eth1 up 10.0.0.5 netmask 255.255.255.252
281 | route add -net 10.0.0.0/8 gw 10.0.0.6 dev eth1
282 | route add -net 192.168.0.0/29 gw 10.0.0.6 dev eth1
283 | exit 0
284 |
285 | 172.16.1.152
286 |
287 |
288 |
289 |
290 |
291 | #cloud-config
292 | bootcmd:
293 | - ln -s -t /etc/rc.d /etc/rc.local
294 | hostname: Server
295 | manage_etc_hosts: true
296 | runcmd:
297 | - start ttyS0
298 | - systemctl start getty@ttyS0.service
299 | - systemctl start rc-local
300 | - sed -i '/^\s*PasswordAuthentication\s\+no/d' /etc/ssh/sshd_config
301 | - echo "UseDNS no" >> /etc/ssh/sshd_config
302 | - service ssh restart
303 | - service sshd restart
304 | users:
305 | - default
306 | - gecos: User configured by VIRL Configuration Engine 0.23.10
307 | lock-passwd: false
308 | name: cisco
309 | plain-text-passwd: cisco
310 | shell: /bin/bash
311 | ssh-authorized-keys:
312 | - VIRL-USER-SSH-PUBLIC-KEY
313 | sudo: ALL=(ALL) ALL
314 | write_files:
315 | - path: /etc/init/ttyS0.conf
316 | owner: root:root
317 | content: |
318 | # ttyS0 - getty
319 | # This service maintains a getty on ttyS0 from the point the system is
320 | # started until it is shut down again.
321 | start on stopped rc or RUNLEVEL=[12345]
322 | stop on runlevel [!12345]
323 | respawn
324 | exec /sbin/getty -L 115200 ttyS0 vt102
325 | permissions: '0644'
326 | - path: /etc/systemd/system/dhclient@.service
327 | content: |
328 | [Unit]
329 | Description=Run dhclient on %i interface
330 | After=network.target
331 | [Service]
332 | Type=oneshot
333 | ExecStart=/sbin/dhclient %i -pf /var/run/dhclient.%i.pid -lf /var/lib/dhclient/dhclient.%i.lease
334 | RemainAfterExit=yes
335 | owner: root:root
336 | permissions: '0644'
337 | - path: /etc/rc.local
338 | owner: root:root
339 | permissions: '0755'
340 | content: |-
341 | #!/bin/sh
342 | ifconfig eth1 up 10.0.0.9 netmask 255.255.255.252
343 | route add -net 10.0.0.0/8 gw 10.0.0.10 dev eth1
344 | route add -net 192.168.0.0/29 gw 10.0.0.10 dev eth1
345 | exit 0
346 |
347 | 172.16.1.153
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
--------------------------------------------------------------------------------
/Chapter06/host_vars/nyc-cor-r1:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.60
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: nxos
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter06/host_vars/nyc-edg-r1:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.61
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter06/hosts:
--------------------------------------------------------------------------------
1 | [nxosv-devices]
2 | nyc-cor-r1
3 |
4 | [iosv-devices]
5 | nyc-edg-r1
6 |
7 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter06/scapy_ping_collection.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from scapy.all import *
4 |
5 | def icmp_ping(destination):
6 | # regular ICMP ping
7 | ans, unans = sr(IP(dst=destination)/ICMP())
8 | return ans
9 |
10 | def tcp_ping(destination, dport):
11 | ans, unans = sr(IP(dst=destination)/TCP(dport=dport,flags="S"))
12 | return ans
13 |
14 | def udp_ping(destination):
15 | ans, unans = sr(IP(dst=destination)/UDP(dport=0))
16 | return ans
17 |
18 | def answer_summary(ans):
19 | for send, recv in ans:
20 | print(recv.sprintf("%IP.src% is alive"))
21 |
22 |
23 | def main():
24 | print("** ICMP Ping **")
25 | ans = icmp_ping("10.0.0.13-14")
26 | answer_summary(ans)
27 | print("** TCP Ping ***")
28 | ans = tcp_ping("10.0.0.13", 22)
29 | answer_summary(ans)
30 | print("** UDP Ping ***")
31 | ans = udp_ping("10.0.0.13-14")
32 | answer_summary(ans)
33 |
34 |
--------------------------------------------------------------------------------
/Chapter06/scapy_tcp_scan_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | from scapy.all import *
4 | import sys
5 |
6 | def tcp_scan(destination, dport):
7 | ans, unans = sr(IP(dst=destination)/TCP(sport=666,dport=dport,flags="S"))
8 | for sending, returned in ans:
9 | if 'SA' in str(returned[TCP].flags):
10 | return destination + " port " + str(sending[TCP].dport) + " is open."
11 | else:
12 | return destination + " port " + str(sending[TCP].dport) + " is not open."
13 |
14 | def main():
15 | destination = sys.argv[1]
16 | port = int(sys.argv[2])
17 | scan_result = tcp_scan(destination, port)
18 | print(scan_result)
19 |
20 | if __name__ == "__main__":
21 | main()
22 |
23 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter07/figuer1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter07/figuer1.png
--------------------------------------------------------------------------------
/Chapter07/figure1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter07/figure1.pdf
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter07/matplotlib_1_result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter07/matplotlib_1_result.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter07/matplotlib_2_result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter07/matplotlib_2_result.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter07/matplotlib_3_result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter07/matplotlib_3_result.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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(('192.168.2.218', 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 |
--------------------------------------------------------------------------------
/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(('192.168.2.218', 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 |
--------------------------------------------------------------------------------
/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 = '192.168.2.218'
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter08/chapter8_graphviz_1.gv:
--------------------------------------------------------------------------------
1 | graph my_network {
2 | core -- distribution;
3 | distribution -- access;
4 | }
5 |
--------------------------------------------------------------------------------
/Chapter08/chapter8_gv_1.gv:
--------------------------------------------------------------------------------
1 | graph my_network {
2 | core -- distribution;
3 | distribution -- access1;
4 | distribution -- access2;
5 | }
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter08/cisco_config_lldp.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Enable LLDP
3 | hosts: "devices"
4 | gather_facts: false
5 | connection: network_cli
6 |
7 |
8 | tasks:
9 | - name: enable LLDP service
10 | ios_lldp:
11 | state: present
12 |
13 | register: output
14 |
15 | - name: show output
16 | debug:
17 | var: output
18 |
19 |
--------------------------------------------------------------------------------
/Chapter08/cisco_config_netflow.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Configure NetFlow
3 | hosts: "devices"
4 | gather_facts: false
5 | connection: network_cli
6 |
7 | tasks:
8 | - name: configure netflow export
9 | ios_config:
10 | lines:
11 | - ip flow-export destination 172.16.1.123 5556 vrf Mgmt-intf
12 | - ip flow-export version 5
13 |
14 | - name: configure flow export on Gi0/0
15 | ios_config:
16 | lines:
17 | - ip flow ingress
18 | - ip flow egress
19 | parents: interface GigabitEthernet0/0
20 |
21 | - name: configure flow export on Gi0/1
22 | ios_config:
23 | lines:
24 | - ip flow ingress
25 | - ip flow egress
26 | parents: interface GigabitEthernet0/1
27 |
28 | - name: configure flow export on Gi0/2
29 | ios_config:
30 | lines:
31 | - ip flow ingress
32 | - ip flow egress
33 | parents: interface GigabitEthernet0/2
34 |
35 | - name: Configure NetFlow on edge-devices
36 | hosts: "edge-devices"
37 | gather_facts: false
38 | connection: network_cli
39 |
40 |
41 | tasks:
42 | - name: configure flow export on Gi0/3
43 | ios_config:
44 | lines:
45 | - ip flow ingress
46 | - ip flow egress
47 | parents: interface GigabitEthernet0/3
48 |
49 | - name: configure flow export on Gi0/4
50 | ios_config:
51 | lines:
52 | - ip flow ingress
53 | - ip flow egress
54 | parents: interface GigabitEthernet0/4
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Chapter08/cisco_discover_lldp.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - name: Query LLDP Neighbors
3 | hosts: "devices"
4 | gather_facts: false
5 | connection: network_cli
6 |
7 |
8 | tasks:
9 | - name: Query for LLDP Neighbors
10 | ios_command:
11 | commands: show lldp neighbors
12 |
13 | register: output
14 |
15 | - name: show output
16 | debug:
17 | var: output
18 |
19 | - name: copy output to file
20 | copy: content="{{ output.stdout_lines }}" dest="./tmp/{{ inventory_hostname }}_lldp_output.txt"
21 |
22 |
23 |
--------------------------------------------------------------------------------
/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-2/*'):
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 |
--------------------------------------------------------------------------------
/Chapter08/host_vars/r1:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.218
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter08/host_vars/r2:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.219
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter08/host_vars/r3:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.220
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
--------------------------------------------------------------------------------
/Chapter08/host_vars/r4:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.221
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter08/host_vars/r5-tor:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.221
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
10 |
--------------------------------------------------------------------------------
/Chapter08/host_vars/r6-edge:
--------------------------------------------------------------------------------
1 | ---
2 | ansible_host: 192.168.2.222
3 | ansible_user: cisco
4 | ansible_ssh_pass: cisco
5 | ansible_connection: network_cli
6 | ansible_network_os: ios
7 | ansbile_become: yes
8 | ansible_become_method: enable
9 | ansible_become_pass: cisco
--------------------------------------------------------------------------------
/Chapter08/hosts:
--------------------------------------------------------------------------------
1 | [devices]
2 | r1
3 | r2
4 | r3
5 | r5-tor
6 | r6-edge
7 |
8 | [edge-devices]
9 | r5-tor
10 | r6-edge
11 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter08/output/chapter8_gv_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter08/output/chapter8_gv_1.png
--------------------------------------------------------------------------------
/Chapter08/output/chapter8_gv_2.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter08/output/chapter8_gv_2.pdf
--------------------------------------------------------------------------------
/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 | }
11 |
--------------------------------------------------------------------------------
/Chapter08/output/chapter8_gv_3.gv.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter08/output/chapter8_gv_3.gv.pdf
--------------------------------------------------------------------------------
/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 | "r6-edge" -> r2
8 | "r6-edge" -> r1
9 | "r6-edge" -> r3
10 | r2 -> r5
11 | r2 -> r6
12 | r3 -> r5
13 | r3 -> r6
14 | "r5-tor" -> r3
15 | "r5-tor" -> r1
16 | "r5-tor" -> r2
17 | r1 -> r5
18 | r1 -> r6
19 | }
20 |
--------------------------------------------------------------------------------
/Chapter08/output/chapter8_lldp_graph.gv.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter08/output/chapter8_lldp_graph.gv.pdf
--------------------------------------------------------------------------------
/Chapter08/output/chapter8_lldp_graph.gv_neato.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter08/output/chapter8_lldp_graph.gv_neato.pdf
--------------------------------------------------------------------------------
/Chapter08/output/chapter8_lldp_graph.gv_v1.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter08/output/chapter8_lldp_graph.gv_v1.pdf
--------------------------------------------------------------------------------
/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", "veos01 Gi0/0 120 B Ethernet1", "r2.virl.info Gi0/0 120 R Gi0/0", "r3.virl.info Gi0/0 120 R Gi0/0", "r5.virl.info Gi0/2 120 R Gi0/1", "r5.virl.info Gi0/0 120 R Gi0/0", "r6.virl.info Gi0/0 120 R Gi0/0", "r6.virl.info Gi0/1 120 R Gi0/1", "", "Total entries displayed: 7"]]
--------------------------------------------------------------------------------
/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", "veos01 Gi0/0 120 B Ethernet1", "r1.virl.info Gi0/0 120 R Gi0/0", "r5.virl.info Gi0/2 120 R Gi0/2", "r3.virl.info Gi0/0 120 R Gi0/0", "r5.virl.info Gi0/0 120 R Gi0/0", "r6.virl.info Gi0/0 120 R Gi0/0", "r6.virl.info Gi0/1 120 R Gi0/2", "", "Total entries displayed: 7"]]
--------------------------------------------------------------------------------
/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", "veos01 Gi0/0 120 B Ethernet1", "r5.virl.info Gi0/2 120 R Gi0/3", "r1.virl.info Gi0/0 120 R Gi0/0", "r2.virl.info Gi0/0 120 R Gi0/0", "r5.virl.info Gi0/0 120 R Gi0/0", "r6.virl.info Gi0/0 120 R Gi0/0", "r6.virl.info Gi0/1 120 R Gi0/3", "", "Total entries displayed: 7"]]
--------------------------------------------------------------------------------
/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", "veos01 Gi0/0 120 B Ethernet1", "r1.virl.info Gi0/0 120 R Gi0/0", "r3.virl.info Gi0/3 120 R Gi0/2", "r2.virl.info Gi0/0 120 R Gi0/0", "r3.virl.info Gi0/0 120 R Gi0/0", "r1.virl.info Gi0/1 120 R Gi0/2", "r2.virl.info Gi0/2 120 R Gi0/2", "r6.virl.info Gi0/0 120 R Gi0/0", "", "Total entries displayed: 8"]]
--------------------------------------------------------------------------------
/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", "veos01 Gi0/0 120 B Ethernet1", "r1.virl.info Gi0/0 120 R Gi0/0", "r2.virl.info Gi0/2 120 R Gi0/1", "r2.virl.info Gi0/0 120 R Gi0/0", "r1.virl.info Gi0/1 120 R Gi0/1", "r3.virl.info Gi0/0 120 R Gi0/0", "r3.virl.info Gi0/3 120 R Gi0/1", "r5.virl.info Gi0/0 120 R Gi0/0", "", "Total entries displayed: 8"]]
--------------------------------------------------------------------------------
/Chapter09/TestApp/app/__init__.py:
--------------------------------------------------------------------------------
1 | from flask import Flask, url_for, jsonify, request
2 | from flask_sqlalchemy import SQLAlchemy
3 |
4 | app = Flask(__name__)
5 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///network.db'
6 | db = SQLAlchemy(app)
7 |
8 | @app.route('/')
9 | def home():
10 | return "Hello Python Netowrking!"
11 |
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 |
60 | @app.route('/devices/', methods=['GET'])
61 | def get_device(id):
62 | return jsonify(Device.query.get_or_404(id).export_data())
63 |
64 |
65 | @app.route('/devices/', methods=['POST'])
66 | def new_device():
67 | device = Device()
68 | device.import_data(request.json)
69 | db.session.add(device)
70 | db.session.commit()
71 | return jsonify({}), 201, {'Location': device.get_url()}
72 |
73 |
74 | @app.route('/devices/', methods=['PUT'])
75 | def edit_device(id):
76 | device = Device.query.get_or_404(id)
77 | device.import_data(request.json)
78 | db.session.add(device)
79 | db.session.commit()
80 | return jsonify({})
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Chapter09/TestApp/app/network.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/77a03fd4e05818497ac8b0c8e10eabd5a447e2b1/Chapter09/TestApp/app/network.db
--------------------------------------------------------------------------------
/Chapter09/TestApp/main.py:
--------------------------------------------------------------------------------
1 | from app import app
2 |
3 |
--------------------------------------------------------------------------------
/Chapter09/TestApp/requirements.txt:
--------------------------------------------------------------------------------
1 | Flask==1.1.1
2 | Flask-HTTPAuth==3.3.0
3 | Flask-SQLAlchemy==2.4.1
4 | Jinja2==2.10.1
5 | MarkupSafe==1.1.1
6 | Pygments==2.4.2
7 | SQLAlchemy==1.3.9
8 | Werkzeug==0.16.0
9 | httpie==1.0.3
10 | itsdangerous==1.1.0
11 | python-dateutil==2.8.0
12 | requests==2.20.1
--------------------------------------------------------------------------------
/Chapter09/TestApp/start.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | app="docker.test"
3 | docker build -t ${app} .
4 | docker run -d -p 8000:80 \
5 | --name=${app} \
6 | -v $PWD:/app ${app}
7 |
--------------------------------------------------------------------------------
/Chapter09/TestApp/uwsgi.ini:
--------------------------------------------------------------------------------
1 | [uwsgi]
2 | module = main
3 | callable = app
4 | master = true
5 |
6 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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_sqlalchemy import SQLAlchemy
7 |
8 | app = Flask(__name__)
9 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///network.db'
10 | db = SQLAlchemy(app)
11 |
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 |
60 | @app.route('/devices/', methods=['GET'])
61 | def get_device(id):
62 | return jsonify(Device.query.get_or_404(id).export_data())
63 |
64 |
65 | @app.route('/devices/', methods=['POST'])
66 | def new_device():
67 | device = Device()
68 | device.import_data(request.json)
69 | db.session.add(device)
70 | db.session.commit()
71 | return jsonify({}), 201, {'Location': device.get_url()}
72 |
73 |
74 | @app.route('/devices/', methods=['PUT'])
75 | def edit_device(id):
76 | device = Device.query.get_or_404(id)
77 | device.import_data(request.json)
78 | db.session.add(device)
79 | db.session.commit()
80 | return jsonify({})
81 |
82 |
83 | if __name__ == '__main__':
84 | db.create_all()
85 | app.run(host='0.0.0.0', debug=True)
86 |
87 |
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/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_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 |
--------------------------------------------------------------------------------
/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_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 |
--------------------------------------------------------------------------------
/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_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_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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/Chapter09/chapter9_request_1.py:
--------------------------------------------------------------------------------
1 | import requests, time
2 |
3 | server = 'http://192.168.2.126: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(server+"/"+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(server+"/"+resource)
19 | print("Status after 2 seconds: " + str(r.status_code))
20 |
21 |
22 |
--------------------------------------------------------------------------------
/Chapter09/requirements.txt:
--------------------------------------------------------------------------------
1 | certifi==2022.9.14
2 | charset-normalizer==2.1.1
3 | click==8.1.3
4 | commonmark==0.9.1
5 | defusedxml==0.7.1
6 | Flask==2.2.2
7 | Flask-HTTPAuth==4.7.0
8 | Flask-SQLAlchemy==2.5.1
9 | greenlet==1.1.3
10 | httpie==3.2.1
11 | idna==3.4
12 | itsdangerous==2.1.2
13 | Jinja2==3.1.2
14 | MarkupSafe==2.1.1
15 | multidict==6.0.2
16 | pexpect==4.8.0
17 | ptyprocess==0.7.0
18 | Pygments==2.13.0
19 | PySocks==1.7.1
20 | requests==2.28.1
21 | requests-toolbelt==0.9.1
22 | rich==12.5.1
23 | SQLAlchemy==1.4.41
24 | urllib3==1.26.12
25 | Werkzeug==2.2.2
26 |
--------------------------------------------------------------------------------
/Chapter10/async_count.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # example from https://realpython.com/async-io-python/#the-asyncio-package-and-asyncawait countasync.py
3 |
4 | import asyncio
5 |
6 | async def count():
7 | print("One")
8 | await asyncio.sleep(1)
9 | print("Two")
10 |
11 | async def main():
12 | await asyncio.gather(count(), count(), count())
13 |
14 | if __name__ == "__main__":
15 | import time
16 | s = time.perf_counter()
17 | asyncio.run(main())
18 | elapsed = time.perf_counter() - s
19 | print(f"Completed in {elapsed:0.2f} seconds.")
20 |
21 |
--------------------------------------------------------------------------------
/Chapter10/asyncio_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import asyncio
3 |
4 | async def main():
5 | print('Hello ...')
6 | await asyncio.sleep(1)
7 | print('... World!')
8 | await asyncio.sleep(2)
9 | print('... and again.')
10 |
11 |
12 | asyncio.run(main())
13 |
14 |
--------------------------------------------------------------------------------
/Chapter10/multiprocess_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Modified from https://docs.python.org/3/library/multiprocessing.html
3 | from multiprocessing import Process
4 | import os
5 |
6 | # Get process information
7 | def process_info():
8 | print('process id:', os.getpid())
9 |
10 | # Worker function
11 | def worker(number):
12 | print(f'Worker number {number}')
13 | process_info()
14 |
15 |
16 | if __name__ == '__main__':
17 | for i in range(5):
18 | p = Process(target=worker, args=(i,))
19 | p.start()
20 |
21 |
--------------------------------------------------------------------------------
/Chapter10/scrapli/scrapli_example_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Modified from https://github.com/carlmontanari/scrapli
3 | from scrapli import Scrapli
4 |
5 | device = {
6 | "host": "192.168.2.50",
7 | "auth_username": "cisco",
8 | "auth_password": "cisco",
9 | "auth_strict_key": False,
10 | "ssh_config_file": True,
11 | "platform": "cisco_nxos",
12 | }
13 |
14 | conn = Scrapli(**device)
15 | conn.open()
16 | response = conn.send_command("show version")
17 | print(response.result)
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Chapter10/scrapli/scrapli_example_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Modified from
3 | # https://github.com/carlmontanari/scrapli/blob/main/examples/async_usage/async_multiple_connections.py
4 | import asyncio
5 | from scrapli.driver.core import AsyncNXOSDriver
6 |
7 |
8 | async def gather_cor_device_version(ip, username, password):
9 | device = {
10 | "host": ip,
11 | "auth_username": username,
12 | "auth_password": password,
13 | "auth_strict_key": False,
14 | "ssh_config_file": True,
15 | "transport": "asyncssh",
16 | "driver": AsyncNXOSDriver
17 | }
18 |
19 | driver = device.pop("driver")
20 | conn = driver(**device)
21 | await conn.open()
22 | response = await conn.send_command("show version")
23 | await conn.close()
24 | return response
25 |
26 | async def main():
27 | results = await asyncio.gather(
28 | gather_cor_device_version('192.168.2.50', 'cisco', 'cisco'),
29 | gather_cor_device_version('192.168.2.60', 'cisco', 'cisco')
30 | )
31 | for result in results:
32 | print(result.result)
33 |
34 |
35 | if __name__ == "__main__":
36 | import time
37 | s = time.perf_counter()
38 | asyncio.run(main())
39 | elapsed = time.perf_counter() - s
40 | print(f"Completed in {elapsed:0.2f} seconds.")
41 |
--------------------------------------------------------------------------------
/Chapter10/scrapli/scrapli_example_3_async.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Modified from
3 | # https://github.com/carlmontanari/scrapli/blob/main/examples/async_usage/async_multiple_connections.py
4 | import asyncio
5 | from scrapli.driver.core import AsyncNXOSDriver
6 |
7 |
8 | async def gather_cor_device_version(ip, username, password):
9 | device = {
10 | "host": ip,
11 | "auth_username": username,
12 | "auth_password": password,
13 | "auth_strict_key": False,
14 | "ssh_config_file": True,
15 | "transport": "asyncssh",
16 | "driver": AsyncNXOSDriver
17 | }
18 |
19 | driver = device.pop("driver")
20 | conn = driver(**device)
21 | await conn.open()
22 | response = await conn.send_command("show version")
23 | await conn.close()
24 | return response
25 |
26 | async def main():
27 | results = await asyncio.gather(
28 | gather_cor_device_version('192.168.2.50', 'cisco', 'cisco'),
29 | gather_cor_device_version('192.168.2.60', 'cisco', 'cisco'),
30 | gather_cor_device_version('192.168.2.50', 'cisco', 'cisco'),
31 | gather_cor_device_version('192.168.2.60', 'cisco', 'cisco'),
32 | gather_cor_device_version('192.168.2.50', 'cisco', 'cisco'),
33 | gather_cor_device_version('192.168.2.60', 'cisco', 'cisco'),
34 | gather_cor_device_version('192.168.2.50', 'cisco', 'cisco'),
35 | gather_cor_device_version('192.168.2.60', 'cisco', 'cisco'),
36 | )
37 | return results
38 |
39 |
40 | if __name__ == "__main__":
41 | import time
42 | s = time.perf_counter()
43 | asyncio.run(main())
44 | elapsed = time.perf_counter() - s
45 | print(f"Completed in {elapsed:0.2f} seconds.")
46 |
--------------------------------------------------------------------------------
/Chapter10/scrapli/scrapli_example_3_sync.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Modified from
3 | # https://github.com/carlmontanari/scrapli/blob/main/examples/async_usage/async_multiple_connections.py
4 | import asyncio
5 | # from scrapli.driver.core import Paramiko
6 | from scrapli.driver import GenericDriver
7 |
8 |
9 | def gather_cor_device_version(ip, username, password):
10 | device = {
11 | "host": ip,
12 | "auth_username": username,
13 | "auth_password": password,
14 | "auth_strict_key": False,
15 | "ssh_config_file": True,
16 | "driver": GenericDriver
17 | }
18 |
19 | driver = device.pop("driver")
20 | conn = driver(**device)
21 | conn.open()
22 | response = conn.send_command("show version")
23 | conn.close()
24 | return response
25 |
26 | def main():
27 | results = []
28 | for device in [
29 | '192.168.2.50',
30 | '192.168.2.60',
31 | '192.168.2.50',
32 | '192.168.2.60',
33 | '192.168.2.50',
34 | '192.168.2.60',
35 | '192.168.2.50',
36 | '192.168.2.60',
37 | ]:
38 | results.append(gather_cor_device_version(device, 'cisco', 'cisco'))
39 | return results
40 |
41 |
42 | if __name__ == "__main__":
43 | import time
44 | s = time.perf_counter()
45 | main()
46 | elapsed = time.perf_counter() - s
47 | print(f"Completed in {elapsed:0.2f} seconds.")
48 |
--------------------------------------------------------------------------------
/Chapter10/sync_count.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Modified from https://realpython.com/async-io-python/#the-asyncio-package-and-asyncawait countsync.py example
3 | import time
4 |
5 | def count():
6 | print("One")
7 | time.sleep(1)
8 | print("Two")
9 |
10 | def main():
11 | count()
12 | count()
13 | count()
14 |
15 | if __name__ == "__main__":
16 | s = time.perf_counter()
17 | main()
18 | elapsed = time.perf_counter() - s
19 | print(f"Completed in {elapsed:0.2f} seconds.")
20 |
21 |
--------------------------------------------------------------------------------
/Chapter10/threading_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Modified from https://pymotw.com/3/threading/index.html
3 | import threading
4 |
5 | # Get thread ID
6 | def thread_id():
7 | print('thread id:', threading.get_ident())
8 |
9 | # Worker function
10 | def worker(number):
11 | print(f'Worker number {number}')
12 | thread_id()
13 |
14 |
15 | threads = []
16 | for i in range(5):
17 | t = threading.Thread(target=worker, args=(i,))
18 | threads.append(t)
19 | t.start()
20 |
--------------------------------------------------------------------------------
/Chapter11/Chapter11_1_query_vpc.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import json
3 | import boto3
4 |
5 | region = 'us-east-1'
6 | vpc_name = 'mastering_python_networking_demo'
7 |
8 | ec2 = boto3.resource('ec2', region_name=region)
9 | client = boto3.client('ec2')
10 |
11 |
12 | filters = [{'Name': 'tag:Name', 'Values': [vpc_name]}]
13 |
14 | vpcs = list(ec2.vpcs.filter(Filters=filters))
15 |
16 | for vpc in vpcs:
17 | print(vpc.id)
18 | response = client.describe_vpcs(
19 | VpcIds=[vpc.id]
20 | )
21 | print(json.dumps(response, sort_keys=True, indent=4))
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Chapter11/Chapter11_2_query_route_tables.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import json, boto3
4 |
5 | region = 'us-east-1'
6 | vpc_name = 'mastering_python_networking_demo'
7 |
8 | ec2 = boto3.resource('ec2', region_name=region)
9 | client = boto3.client('ec2')
10 |
11 | response = client.describe_route_tables()
12 | print(json.dumps(response['RouteTables'][0], sort_keys=True, indent=4))
13 |
14 |
--------------------------------------------------------------------------------
/Chapter11/Chapter11_3_cloud_formation.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Description: Create VPC in us-west-1
3 | Resources:
4 | myVPC:
5 | Type: AWS::EC2::VPC
6 | Properties:
7 | CidrBlock: '10.1.0.0/16'
8 | EnableDnsSupport: 'false'
9 | EnableDnsHostnames: 'false'
10 | Tags:
11 | - Key: Name
12 | Value: 'mastering_python_networking_demo_2'
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Chapter11/Chapter11_4_cloud_formation_full.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: '2010-09-09'
2 | Description: Create subnet in us-west-1
3 | Resources:
4 | myVPC:
5 | Type: AWS::EC2::VPC
6 | Properties:
7 | CidrBlock: '10.1.0.0/16'
8 | EnableDnsSupport: 'false'
9 | EnableDnsHostnames: 'false'
10 | Tags:
11 | - Key: Name
12 | Value: 'mastering_python_networking_demo_2'
13 |
14 | mySubnet:
15 | Type: AWS::EC2::Subnet
16 | Properties:
17 | VpcId: !Ref myVPC
18 | CidrBlock: '10.1.0.0/24'
19 | AvailabilityZone: 'us-west-1a'
20 | Tags:
21 | - Key: Name
22 | Value: 'mpn_demo_subnet_1'
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Chapter11/Chapter11_5_security_group.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import boto3
4 |
5 | ec2 = boto3.client('ec2')
6 |
7 | response = ec2.describe_vpcs()
8 | vpc_id = response.get('Vpcs', [{}])[0].get('VpcId', '')
9 |
10 | # Query for security group id
11 | response = ec2.create_security_group(GroupName='mpn_security_group',
12 | Description='mpn_demo_sg',
13 | VpcId=vpc_id)
14 | security_group_id = response['GroupId']
15 | data = ec2.authorize_security_group_ingress(
16 | GroupId=security_group_id,
17 | IpPermissions=[
18 | {'IpProtocol': 'tcp',
19 | 'FromPort': 80,
20 | 'ToPort': 80,
21 | 'IpRanges': [{'CidrIp': '0.0.0.0/0'}]},
22 | {'IpProtocol': 'tcp',
23 | 'FromPort': 22,
24 | 'ToPort': 22,
25 | 'IpRanges': [{'CidrIp': '0.0.0.0/0'}]}
26 | ])
27 | print('Ingress Successfully Set %s' % data)
28 |
29 | # Describe security group
30 | #response = ec2.describe_security_groups(GroupIds=[security_group_id])
31 | print(security_group_id)
32 |
33 |
--------------------------------------------------------------------------------
/Chapter12/Chapter12_1_auth.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import os
3 | import azure.mgmt.network
4 | from azure.identity import ClientSecretCredential
5 |
6 | credential = ClientSecretCredential(
7 | tenant_id=os.environ.get("AZURE_TENANT_ID"),
8 | client_id=os.environ.get("AZURE_CLIENT_ID"),
9 | client_secret=os.environ.get("AZURE_CLIENT_SECRET")
10 | )
11 | subscription_id = os.environ.get("SUBSCRIPTION_ID")
12 | network_client = azure.mgmt.network.NetworkManagementClient(credential=credential, subscription_id=subscription_id)
13 | print("Network Management Client API Version: " + network_client.DEFAULT_API_VERSION)
14 |
--------------------------------------------------------------------------------
/Chapter12/Chapter12_2_subnet.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Reference example: https://github.com/Azure-Samples/azure-samples-python-management/blob/main/samples/network/virtual_network/manage_subnet.py
3 | #
4 | import os
5 | from azure.identity import ClientSecretCredential
6 | import azure.mgmt.network
7 | from azure.identity import DefaultAzureCredential
8 | from azure.mgmt.network import NetworkManagementClient
9 | from azure.mgmt.resource import ResourceManagementClient
10 |
11 | credential = ClientSecretCredential(
12 | tenant_id=os.environ.get("AZURE_TENANT_ID"),
13 | client_id=os.environ.get("AZURE_CLIENT_ID"),
14 | client_secret=os.environ.get("AZURE_CLIENT_SECRET")
15 | )
16 | subscription_id = os.environ.get("SUBSCRIPTION_ID")
17 | GROUP_NAME = "Mastering-Python-Networking"
18 | VIRTUAL_NETWORK_NAME = "WEST-US-2_VNet_1"
19 | SUBNET = "WEST-US-2_VNet_1_Subnet_2"
20 | network_client = azure.mgmt.network.NetworkManagementClient(
21 | credential=credential, subscription_id=subscription_id)
22 |
23 | # Get subnet
24 | subnet = network_client.subnets.get(
25 | GROUP_NAME,
26 | VIRTUAL_NETWORK_NAME,
27 | SUBNET
28 | )
29 | print("Get subnet:\n{}".format(subnet))
30 |
31 | subnet = network_client.subnets.begin_create_or_update(
32 | GROUP_NAME,
33 | VIRTUAL_NETWORK_NAME,
34 | SUBNET,
35 | {
36 | "address_prefix": "192.168.0.128/25"
37 | }
38 | ).result()
39 | print("Create subnet:\n{}".format(subnet))
40 |
--------------------------------------------------------------------------------
/Chapter12/Chapter12_3_vnet.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #
3 | # Referenced: https://github.com/Azure-Samples/azure-samples-python-management/blob/main/samples/network/virtual_network/manage_virtual_network_peering.py
4 | #
5 | import os
6 | from azure.identity import ClientSecretCredential
7 | import azure.mgmt.network
8 | from azure.identity import DefaultAzureCredential
9 | from azure.mgmt.network import NetworkManagementClient
10 | from azure.mgmt.resource import ResourceManagementClient
11 |
12 | credential = ClientSecretCredential(
13 | tenant_id=os.environ.get("AZURE_TENANT_ID"),
14 | client_id=os.environ.get("AZURE_CLIENT_ID"),
15 | client_secret=os.environ.get("AZURE_CLIENT_SECRET")
16 | )
17 | subscription_id = os.environ.get("SUBSCRIPTION_ID")
18 | GROUP_NAME = "Mastering-Python-Networking"
19 | VIRTUAL_NETWORK_NAME = "WEST-US-2_VNet_1"
20 | SUBNET = "WEST-US-2_VNet_1_Subnet_2"
21 | LOCATION = 'eastus'
22 | network_client = azure.mgmt.network.NetworkManagementClient(
23 | credential=credential, subscription_id=subscription_id)
24 |
25 |
26 | def create_vnet(network_client):
27 | vnet_params = {
28 | 'location': LOCATION,
29 | 'address_space': {
30 | 'address_prefixes': ['10.0.0.0/16']
31 | }
32 | }
33 | creation_result = network_client.virtual_networks.create_or_update(
34 | GROUP_NAME,
35 | 'EAST-US_VNet_1',
36 | vnet_params
37 | )
38 | return creation_result.result()
39 |
40 |
41 | creation_result = create_vnet(network_client)
42 | print("------------------------------------------------------")
43 | print(creation_result)
44 | input('Press enter to continue...')
45 |
46 |
47 | def create_subnet(network_client):
48 | subnet_params = {
49 | 'address_prefix': '10.0.1.0/24'
50 | }
51 | creation_result = network_client.subnets.create_or_update(
52 | GROUP_NAME,
53 | 'EAST-US_VNet_1',
54 | 'EAST-US_VNet_1_Subnet_1',
55 | subnet_params
56 | )
57 |
58 | return creation_result.result()
59 |
60 |
61 | creation_result = create_subnet(network_client)
62 | print("------------------------------------------------------")
63 | print(creation_result)
64 | input('Press enter to continue...')
65 |
66 |
67 |
--------------------------------------------------------------------------------
/Chapter13/Chapter13_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import requests
3 | from requests.packages.urllib3.exceptions import InsecureRequestWarning
4 |
5 | # disable https verification check warning
6 | requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
7 |
8 | def current_indices_list(es_host, index_prefix):
9 | current_indices = []
10 | http_header = {'content-type': 'application/json'}
11 | response = requests.get(es_host + "/_cat/indices/" + index_prefix + "*", headers=http_header, verify=False)
12 | for line in response.text.split('\n'):
13 | if line:
14 | current_indices.append(line.split()[2])
15 | return current_indices
16 |
17 | if __name__ == "__main__":
18 | username = 'elastic'
19 | password = '-Rel0twWMUk8L-ZtZr=I'
20 | es_host = 'https://'+username+':'+password+'@192.168.2.126:9200'
21 | indices_list = current_indices_list(es_host, 'kibana')
22 | print(indices_list)
23 |
24 |
--------------------------------------------------------------------------------
/Chapter13/Chapter13_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from elasticsearch import Elasticsearch
3 |
4 | es_host = Elasticsearch(["https://elastic:-Rel0twWMUk8L-ZtZr=I@192.168.2.126:9200/"],
5 | ca_certs=False, verify_certs=False)
6 |
7 | res = es_host.search(index="kibana*", body={"query": {"match_all": {}}})
8 | print("Hits Total: " + str(res['hits']['total']['value']))
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Chapter13/Chapter13_3.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from elasticsearch import Elasticsearch
3 | from datetime import datetime
4 | import argparse
5 | import json
6 | import logging
7 |
8 | # parse options
9 | parser = argparse.ArgumentParser(description='Elsticsearch Query Options')
10 | parser.add_argument("-i", "--index", help="index to query")
11 | parser.add_argument("-q", "--query", help="query file")
12 |
13 | args = parser.parse_args()
14 |
15 |
16 | # logging
17 | today = str(datetime.today())
18 | logging.basicConfig(filename='elastic_query.log', level=logging.INFO)
19 |
20 |
21 | # load elastic index and query body information
22 | query_file = args.query
23 | with open(query_file) as f:
24 | query_body = json.loads(f.read())
25 |
26 |
27 | # Elasticsearch instance
28 | es = Elasticsearch(["https://elastic:-Rel0twWMUk8L-ZtZr=I@192.168.2.126:9200/"],
29 | ca_certs=False, verify_certs=False)
30 |
31 | # Query both index and put into dictionary
32 | index = args.index
33 | res = es.search(index=index, body=query_body)
34 | print(res['hits']['total']['value'])
35 |
36 |
37 |
--------------------------------------------------------------------------------
/Chapter13/Chapter13_4.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from elasticsearch import Elasticsearch
3 | from datetime import datetime
4 | import argparse
5 | import json
6 | import logging
7 | from pprint import pprint
8 |
9 | # parse options
10 | parser = argparse.ArgumentParser(description='Elsticsearch Query Options')
11 | parser.add_argument("-i", "--index", help="index to query")
12 | parser.add_argument("-q", "--query", help="query file")
13 |
14 | args = parser.parse_args()
15 |
16 |
17 | # logging
18 | today = str(datetime.today())
19 | logging.basicConfig(filename='elastic_query.log', level=logging.INFO)
20 |
21 |
22 | # load elastic index and query body information
23 | query_file = args.query
24 | with open(query_file) as f:
25 | query_body = json.loads(f.read())
26 |
27 |
28 | # Elasticsearch instance
29 | es = Elasticsearch(["https://elastic:-Rel0twWMUk8L-ZtZr=I@192.168.2.126:9200/"],
30 | ca_certs=False, verify_certs=False)
31 |
32 | # Query both index and put into dictionary
33 | index = args.index
34 | res = es.search(index=index, body=query_body)
35 | print("Total hits: " + str(res['hits']['total']['value']))
36 | for hit in res['hits']['hits']:
37 | pprint(hit)
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/Chapter13/Chapter13_5.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from elasticsearch import Elasticsearch
3 | from datetime import datetime
4 | import argparse
5 | import json
6 | import logging
7 |
8 | # parse options
9 | parser = argparse.ArgumentParser(description='Elsticsearch Query Options')
10 | parser.add_argument("-i", "--index", help="index to query")
11 | parser.add_argument("-q", "--query", help="query file")
12 |
13 | args = parser.parse_args()
14 |
15 |
16 | # logging
17 | today = str(datetime.today())
18 | logging.basicConfig(filename='elastic_query.log', level=logging.INFO)
19 |
20 |
21 | # load elastic index and query body information
22 | query_file = args.query
23 | with open(query_file) as f:
24 | query_body = json.loads(f.read())
25 |
26 |
27 | # Elasticsearch instance
28 | es = Elasticsearch(["https://elastic:-Rel0twWMUk8L-ZtZr=I@192.168.2.126:9200/"],
29 | ca_certs=False, verify_certs=False)
30 |
31 | # Query both index and put into dictionary
32 | index = args.index
33 | res = es.search(index=index, body=query_body)
34 | print(res['aggregations']['network_bytes_sum']['value'])
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/Chapter13/Chapter13_6.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | from elasticsearch import Elasticsearch
3 | from datetime import datetime
4 | import argparse
5 | import json
6 | import logging
7 | from pprint import pprint
8 |
9 | # parse options
10 | parser = argparse.ArgumentParser(description='Elsticsearch Query Options')
11 | parser.add_argument("-i", "--index", help="index to query")
12 | parser.add_argument("-q", "--query", help="query file")
13 |
14 | args = parser.parse_args()
15 |
16 |
17 | # logging
18 | today = str(datetime.today())
19 | logging.basicConfig(filename='elastic_query.log', level=logging.INFO)
20 |
21 |
22 | # load elastic index and query body information
23 | query_file = args.query
24 | with open(query_file) as f:
25 | query_body = json.loads(f.read())
26 |
27 |
28 | # Elasticsearch instance
29 | es = Elasticsearch(["https://elastic:-Rel0twWMUk8L-ZtZr=I@192.168.2.126:9200/"],
30 | ca_certs=False, verify_certs=False)
31 |
32 | # Query both index and put into dictionary
33 | index = args.index
34 | res = es.search(index=index, body=query_body)
35 | for hit in res['aggregations']['3']['buckets']:
36 | pprint(hit)
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Chapter13/network_configs/simple_config.conf:
--------------------------------------------------------------------------------
1 | input {
2 | udp {
3 | port => 5144
4 | type => "syslog-ios"
5 | }
6 | }
7 | output {
8 | stdout { codec => json }
9 | elasticsearch {
10 | hosts => ["https://192.168.2.126:9200"]
11 | ssl => true
12 | ssl_certificate_verification => false
13 | user => "elastic"
14 | password => "-Rel0twWMUk8L-ZtZr=I"
15 | index => "cisco-syslog-%{+YYYY.MM.dd}"
16 | }
17 | }
18 |
19 |
--------------------------------------------------------------------------------
/Chapter13/query_body_1.json:
--------------------------------------------------------------------------------
1 | {
2 | "query": {
3 | "match_all": {}
4 | }
5 | }
6 |
7 |
--------------------------------------------------------------------------------
/Chapter13/query_body_2.json:
--------------------------------------------------------------------------------
1 | {
2 | "query": {
3 | "bool": {
4 | "filter": [
5 | {
6 | "range": {
7 | "@timestamp": {
8 | "gte": "now-10m"
9 | }
10 | }
11 | }
12 | ]
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Chapter13/query_body_3.json:
--------------------------------------------------------------------------------
1 | {
2 | "query": {
3 | "bool": {
4 | "must": {
5 | "term": {
6 | "source.ip": "192.168.0.1"
7 | }
8 | },
9 | "filter": [
10 | {
11 | "range": {
12 | "@timestamp": {
13 | "gte": "now-10m"
14 | }
15 | }
16 | }
17 | ]
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Chapter13/query_body_4.json:
--------------------------------------------------------------------------------
1 | {
2 | "aggs": {
3 | "network_bytes_sum": {
4 | "sum": {
5 | "field": "network.bytes"
6 | }
7 | }
8 | },
9 | "query": {
10 | "bool": {
11 | "must": {
12 | "term": {
13 | "source.ip": "192.168.0.1"
14 | }
15 | },
16 | "filter": [
17 | {
18 | "range": {
19 | "@timestamp": {
20 | "gte": "now-10m"
21 | }
22 | }
23 | }
24 | ]
25 | }
26 | }
27 | }
28 |
29 |
--------------------------------------------------------------------------------
/Chapter13/query_body_5.json:
--------------------------------------------------------------------------------
1 | {
2 | "aggs": {
3 | "3": {
4 | "terms": {
5 | "field": "source.ip",
6 | "order": {
7 | "1": "desc"
8 | },
9 | "size": 50
10 | },
11 | "aggs": {
12 | "1": {
13 | "sum": {
14 | "field": "network.bytes"
15 | }
16 | }
17 | }
18 | }
19 | },
20 | "size": 0,
21 | "_source": {
22 | "excludes": []
23 | },
24 | "stored_fields": [
25 | "*"
26 | ],
27 | "script_fields": {},
28 | "docvalue_fields": [
29 | {
30 | "field": "@timestamp",
31 | "format": "date_time"
32 | },
33 | {
34 | "field": "event.created",
35 | "format": "date_time"
36 | },
37 | {
38 | "field": "event.end",
39 | "format": "date_time"
40 | },
41 | {
42 | "field": "event.start",
43 | "format": "date_time"
44 | },
45 | {
46 | "field": "file.accessed",
47 | "format": "date_time"
48 | },
49 | {
50 | "field": "file.created",
51 | "format": "date_time"
52 | },
53 | {
54 | "field": "file.ctime",
55 | "format": "date_time"
56 | },
57 | {
58 | "field": "file.mtime",
59 | "format": "date_time"
60 | },
61 | {
62 | "field": "kafka.block_timestamp",
63 | "format": "date_time"
64 | },
65 | {
66 | "field": "netflow.collection_time_milliseconds",
67 | "format": "date_time"
68 | },
69 | {
70 | "field": "netflow.exporter.timestamp",
71 | "format": "date_time"
72 | },
73 | {
74 | "field": "netflow.flow_end_microseconds",
75 | "format": "date_time"
76 | },
77 | {
78 | "field": "netflow.flow_end_milliseconds",
79 | "format": "date_time"
80 | },
81 | {
82 | "field": "netflow.flow_end_nanoseconds",
83 | "format": "date_time"
84 | },
85 | {
86 | "field": "netflow.flow_end_seconds",
87 | "format": "date_time"
88 | },
89 | {
90 | "field": "netflow.flow_start_microseconds",
91 | "format": "date_time"
92 | },
93 | {
94 | "field": "netflow.flow_start_milliseconds",
95 | "format": "date_time"
96 | },
97 | {
98 | "field": "netflow.flow_start_nanoseconds",
99 | "format": "date_time"
100 | },
101 | {
102 | "field": "netflow.flow_start_seconds",
103 | "format": "date_time"
104 | },
105 | {
106 | "field": "netflow.max_export_seconds",
107 | "format": "date_time"
108 | },
109 | {
110 | "field": "netflow.max_flow_end_microseconds",
111 | "format": "date_time"
112 | },
113 | {
114 | "field": "netflow.max_flow_end_milliseconds",
115 | "format": "date_time"
116 | },
117 | {
118 | "field": "netflow.max_flow_end_nanoseconds",
119 | "format": "date_time"
120 | },
121 | {
122 | "field": "netflow.max_flow_end_seconds",
123 | "format": "date_time"
124 | },
125 | {
126 | "field": "netflow.min_export_seconds",
127 | "format": "date_time"
128 | },
129 | {
130 | "field": "netflow.min_flow_start_microseconds",
131 | "format": "date_time"
132 | },
133 | {
134 | "field": "netflow.min_flow_start_milliseconds",
135 | "format": "date_time"
136 | },
137 | {
138 | "field": "netflow.min_flow_start_nanoseconds",
139 | "format": "date_time"
140 | },
141 | {
142 | "field": "netflow.min_flow_start_seconds",
143 | "format": "date_time"
144 | },
145 | {
146 | "field": "netflow.monitoring_interval_end_milli_seconds",
147 | "format": "date_time"
148 | },
149 | {
150 | "field": "netflow.monitoring_interval_start_milli_seconds",
151 | "format": "date_time"
152 | },
153 | {
154 | "field": "netflow.observation_time_microseconds",
155 | "format": "date_time"
156 | },
157 | {
158 | "field": "netflow.observation_time_milliseconds",
159 | "format": "date_time"
160 | },
161 | {
162 | "field": "netflow.observation_time_nanoseconds",
163 | "format": "date_time"
164 | },
165 | {
166 | "field": "netflow.observation_time_seconds",
167 | "format": "date_time"
168 | },
169 | {
170 | "field": "netflow.system_init_time_milliseconds",
171 | "format": "date_time"
172 | },
173 | {
174 | "field": "process.start",
175 | "format": "date_time"
176 | },
177 | {
178 | "field": "suricata.eve.flow.end",
179 | "format": "date_time"
180 | },
181 | {
182 | "field": "suricata.eve.flow.start",
183 | "format": "date_time"
184 | },
185 | {
186 | "field": "suricata.eve.timestamp",
187 | "format": "date_time"
188 | },
189 | {
190 | "field": "suricata.eve.tls.notafter",
191 | "format": "date_time"
192 | },
193 | {
194 | "field": "suricata.eve.tls.notbefore",
195 | "format": "date_time"
196 | }
197 | ],
198 | "query": {
199 | "bool": {
200 | "must": [
201 | {
202 | "query_string": {
203 | "query": "*",
204 | "analyze_wildcard": true,
205 | "time_zone": "America/Los_Angeles"
206 | }
207 | }
208 | ],
209 | "filter": [
210 | {
211 | "range": {
212 | "@timestamp": {
213 | "format": "strict_date_optional_time",
214 | "gte": "2019-11-04T03:48:13.218Z",
215 | "lte": "2019-11-05T03:48:13.218Z"
216 | }
217 | }
218 | }
219 | ],
220 | "should": [],
221 | "must_not": []
222 | }
223 | }
224 | }
225 |
226 |
--------------------------------------------------------------------------------
/Chapter14/Chapter14_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # reference: https://stackoverflow.com/questions/38594717/how-do-i-push-new-files-to-github
3 |
4 | from github import Github, InputGitTreeElement
5 | import os
6 |
7 | github_token = os.environ.get('GITHUB_TOKEN')
8 | configs_dir = 'configs'
9 | github_repo = 'TestRepo'
10 |
11 | # Retrieve the list of files in configs directory
12 | file_list = []
13 | for dirpath, dirname, filenames in os.walk(configs_dir):
14 | for f in filenames:
15 | file_list.append(configs_dir + "/" + f)
16 |
17 | g = Github(github_token)
18 | repo = g.get_user().get_repo(github_repo)
19 |
20 | commit_message = 'add configs'
21 | master_ref = repo.get_git_ref('heads/master')
22 | master_sha = master_ref.object.sha
23 | base_tree = repo.get_git_tree(master_sha)
24 |
25 | element_list = list()
26 |
27 | for entry in file_list:
28 | with open(entry, 'r') as input_file:
29 | data = input_file.read()
30 | element = InputGitTreeElement(entry, '100644', 'blob', data)
31 | element_list.append(element)
32 |
33 | # Create tree and commit
34 | tree = repo.create_git_tree(element_list, base_tree)
35 | parent = repo.get_git_commit(master_sha)
36 | commit = repo.create_git_commit(commit_message, tree, [parent])
37 | master_ref.edit(commit.sha)
38 |
39 |
--------------------------------------------------------------------------------
/Chapter16/chapter15_topology.virl:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | flat
5 |
6 |
7 |
8 | 172.16.1.20
9 | ! IOS Config generated on 2018-07-24 00:23
10 | ! by autonetkit_0.24.0
11 | !
12 | hostname iosv-1
13 | boot-start-marker
14 | boot-end-marker
15 | !
16 | vrf definition Mgmt-intf
17 | !
18 | address-family ipv4
19 | exit-address-family
20 | !
21 | address-family ipv6
22 | exit-address-family
23 | !
24 | !
25 | !
26 | no aaa new-model
27 | !
28 | !
29 | ip cef
30 | ipv6 unicast-routing
31 | ipv6 cef
32 | !
33 | !
34 | service timestamps debug datetime msec
35 | service timestamps log datetime msec
36 | no service password-encryption
37 | no service config
38 | enable password cisco
39 | ip classless
40 | ip subnet-zero
41 | no ip domain lookup
42 | ip domain name virl.info
43 | crypto key generate rsa modulus 768
44 | ip ssh server algorithm authentication password
45 | username cisco privilege 15 secret cisco
46 | line vty 0 4
47 | transport input ssh telnet
48 | exec-timeout 720 0
49 | password cisco
50 | login local
51 | line con 0
52 | password cisco
53 | !
54 | no cdp run
55 | !
56 | !
57 | interface Loopback0
58 | description Loopback
59 | ip address 192.168.0.3 255.255.255.255
60 | !
61 | interface GigabitEthernet0/0
62 | description OOB Management
63 | vrf forwarding Mgmt-intf
64 | ! Configured on launch
65 | no ip address
66 | duplex full
67 | speed auto
68 | no shutdown
69 | !
70 | interface GigabitEthernet0/1
71 | description to host1
72 | ip address 10.0.0.10 255.255.255.252
73 | ip ospf cost 1
74 | duplex full
75 | speed auto
76 | no shutdown
77 | !
78 | interface GigabitEthernet0/2
79 | description to nx-osv-1
80 | ip address 10.0.0.5 255.255.255.252
81 | ip ospf cost 1
82 | duplex full
83 | speed auto
84 | no shutdown
85 | !
86 | !
87 | !
88 | router ospf 1
89 | network 192.168.0.3 0.0.0.0 area 0
90 | log-adjacency-changes
91 | passive-interface Loopback0
92 | network 10.0.0.8 0.0.0.3 area 0
93 | network 10.0.0.4 0.0.0.3 area 0
94 | !
95 | !
96 | router bgp 1
97 | bgp router-id 192.168.0.3
98 | no synchronization
99 | ! ibgp
100 | ! ibgp peers
101 | !
102 | neighbor 192.168.0.1 remote-as 1
103 | neighbor 192.168.0.1 description iBGP peer nx-osv-1
104 | neighbor 192.168.0.1 update-source Loopback0
105 | !
106 | !
107 | !
108 | address-family ipv4
109 | network 192.168.0.3 mask 255.255.255.255
110 | neighbor 192.168.0.1 activate
111 | exit-address-family
112 | !
113 | !
114 | !
115 | end
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 | 172.16.1.21
124 | ! NX-OSv Config generated on 2018-07-24 00:23
125 | ! by autonetkit_0.24.0
126 | !
127 | version 6.2(1)
128 | license grace-period
129 | !
130 | hostname nx-osv-1
131 | vdc nx-osv-1 id 1
132 | allocate interface Ethernet2/1-48
133 | allocate interface Ethernet3/1-48
134 | limit-resource vlan minimum 16 maximum 4094
135 | limit-resource vrf minimum 2 maximum 4096
136 | limit-resource port-channel minimum 0 maximum 768
137 | limit-resource u4route-mem minimum 96 maximum 96
138 | limit-resource u6route-mem minimum 24 maximum 24
139 | limit-resource m4route-mem minimum 58 maximum 58
140 | limit-resource m6route-mem minimum 8 maximum 8
141 |
142 | feature telnet
143 |
144 | feature ospf
145 | feature bgp
146 |
147 | username adminbackup password 5 ! role network-operator
148 | username admin password 5 $1$KuOSBsvW$Cy0TSD..gEBGBPjzpDgf51 role network-admin
149 | username cisco password 5 $1$Nk7ZkwH0$fyiRmMMfIheqE3BqvcL0C1 role network-operator
150 | username cisco role network-admin
151 | username lab password 5 $1$buoy/oqy$.EXQz8rCn72ii8qtdldj00 role network-admin
152 | no password strength-check
153 | ip domain-lookup
154 | copp profile strict
155 | snmp-server user lab network-admin auth md5 0x5ceb414591539ee35159fca86fdfa101 priv 0x5ceb414591539ee35159fca86fdfa101 localizedkey
156 | snmp-server user admin network-admin auth md5 0x328945d53e05e8e7207f8c20b142f0b7 priv 0x328945d53e05e8e7207f8c20b142f0b7 localizedkey
157 | snmp-server user cisco network-operator auth md5 0x55b3c64a53fb95518e75358ee75e82e9 priv 0x55b3c64a53fb95518e75358ee75e82e9 localizedkey
158 | snmp-server user cisco network-admin
159 | rmon event 1 log trap public description FATAL(1) owner PMON@FATAL
160 | rmon event 2 log trap public description CRITICAL(2) owner PMON@CRITICAL
161 | rmon event 3 log trap public description ERROR(3) owner PMON@ERROR
162 | rmon event 4 log trap public description WARNING(4) owner PMON@WARNING
163 | rmon event 5 log trap public description INFORMATION(5) owner PMON@INFO
164 |
165 |
166 | vlan 1
167 |
168 | vrf context management
169 | hardware forwarding unicast trace
170 |
171 | interface Loopback0
172 | description Loopback
173 | ip address 192.168.0.1/32
174 | ip router ospf 1 area 0
175 |
176 | interface Ethernet2/1
177 | description to iosv-1
178 | ip address 10.0.0.6/30
179 | ip router ospf 1 area 0
180 | duplex full
181 | mac-address fa16.3e00.0001
182 | no shutdown
183 |
184 | interface Ethernet2/2
185 | description to host2
186 | ip address 10.0.0.14/30
187 | ip router ospf 1 area 0
188 | duplex full
189 | mac-address fa16.3e00.0002
190 | no shutdown
191 |
192 | interface mgmt0
193 | description OOB Management
194 | ! Configured on launch
195 | no ip address
196 | duplex full
197 | mac-address fa16.3e00.0003
198 | no shutdown
199 | vrf member management
200 |
201 |
202 | line console
203 | line vty
204 | router ospf 1
205 | router-id 192.168.0.1
206 | router bgp 1
207 | router-id 192.168.0.1
208 | address-family ipv4 unicast
209 | network 192.168.0.1/32
210 | !
211 | ! iBGP
212 | !
213 | ! iBGP peers
214 | !
215 | neighbor 192.168.0.3 remote-as 1
216 | description iBGP peer iosv-1
217 | update-source Loopback0
218 | address-family ipv4 unicast
219 | !
220 | !
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 | 172.16.1.22
230 | #cloud-config
231 | bootcmd:
232 | - ln -s -t /etc/rc.d /etc/rc.local
233 | hostname: host1
234 | manage_etc_hosts: true
235 | runcmd:
236 | - start ttyS0
237 | - systemctl start getty@ttyS0.service
238 | - systemctl start rc-local
239 | - sed -i '/^\s*PasswordAuthentication\s\+no/d' /etc/ssh/sshd_config
240 | - echo "UseDNS no" >> /etc/ssh/sshd_config
241 | - service ssh restart
242 | - service sshd restart
243 | users:
244 | - default
245 | - gecos: User configured by VIRL Configuration Engine 0.23.10
246 | lock-passwd: false
247 | name: cisco
248 | plain-text-passwd: cisco
249 | shell: /bin/bash
250 | ssh-authorized-keys:
251 | - VIRL-USER-SSH-PUBLIC-KEY
252 | sudo: ALL=(ALL) ALL
253 | write_files:
254 | - path: /etc/init/ttyS0.conf
255 | owner: root:root
256 | content: |
257 | # ttyS0 - getty
258 | # This service maintains a getty on ttyS0 from the point the system is
259 | # started until it is shut down again.
260 | start on stopped rc or RUNLEVEL=[12345]
261 | stop on runlevel [!12345]
262 | respawn
263 | exec /sbin/getty -L 115200 ttyS0 vt102
264 | permissions: '0644'
265 | - path: /etc/systemd/system/dhclient@.service
266 | content: |
267 | [Unit]
268 | Description=Run dhclient on %i interface
269 | After=network.target
270 | [Service]
271 | Type=oneshot
272 | ExecStart=/sbin/dhclient %i -pf /var/run/dhclient.%i.pid -lf /var/lib/dhclient/dhclient.%i.lease
273 | RemainAfterExit=yes
274 | owner: root:root
275 | permissions: '0644'
276 | - path: /etc/rc.local
277 | owner: root:root
278 | permissions: '0755'
279 | content: |-
280 | #!/bin/sh
281 | ifconfig eth1 up 10.0.0.9 netmask 255.255.255.252
282 | route add -net 10.0.0.0/8 gw 10.0.0.10 dev eth1
283 | route add -net 192.168.0.0/29 gw 10.0.0.10 dev eth1
284 | exit 0
285 |
286 |
287 |
288 |
289 |
290 |
291 | 172.16.1.23
292 | #cloud-config
293 | bootcmd:
294 | - ln -s -t /etc/rc.d /etc/rc.local
295 | hostname: host2
296 | manage_etc_hosts: true
297 | runcmd:
298 | - start ttyS0
299 | - systemctl start getty@ttyS0.service
300 | - systemctl start rc-local
301 | - sed -i '/^\s*PasswordAuthentication\s\+no/d' /etc/ssh/sshd_config
302 | - echo "UseDNS no" >> /etc/ssh/sshd_config
303 | - service ssh restart
304 | - service sshd restart
305 | users:
306 | - default
307 | - gecos: User configured by VIRL Configuration Engine 0.23.10
308 | lock-passwd: false
309 | name: cisco
310 | plain-text-passwd: cisco
311 | shell: /bin/bash
312 | ssh-authorized-keys:
313 | - VIRL-USER-SSH-PUBLIC-KEY
314 | sudo: ALL=(ALL) ALL
315 | write_files:
316 | - path: /etc/init/ttyS0.conf
317 | owner: root:root
318 | content: |
319 | # ttyS0 - getty
320 | # This service maintains a getty on ttyS0 from the point the system is
321 | # started until it is shut down again.
322 | start on stopped rc or RUNLEVEL=[12345]
323 | stop on runlevel [!12345]
324 | respawn
325 | exec /sbin/getty -L 115200 ttyS0 vt102
326 | permissions: '0644'
327 | - path: /etc/systemd/system/dhclient@.service
328 | content: |
329 | [Unit]
330 | Description=Run dhclient on %i interface
331 | After=network.target
332 | [Service]
333 | Type=oneshot
334 | ExecStart=/sbin/dhclient %i -pf /var/run/dhclient.%i.pid -lf /var/lib/dhclient/dhclient.%i.lease
335 | RemainAfterExit=yes
336 | owner: root:root
337 | permissions: '0644'
338 | - path: /etc/rc.local
339 | owner: root:root
340 | permissions: '0755'
341 | content: |-
342 | #!/bin/sh
343 | ifconfig eth1 up 10.0.0.13 netmask 255.255.255.252
344 | route add -net 10.0.0.0/8 gw 10.0.0.14 dev eth1
345 | route add -net 192.168.0.0/29 gw 10.0.0.14 dev eth1
346 | exit 0
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_10_ping.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import subprocess
4 |
5 | host_list = ['www.cisco.com', 'www.google.com']
6 |
7 | ping_time = []
8 |
9 | for host in host_list:
10 | p = subprocess.Popen(['ping', '-c', '1', host], stdout=subprocess.PIPE)
11 | result = p.communicate()[0]
12 | host = result.split()[1]
13 | time = result.split()[13]
14 | ping_time.append((host, time))
15 |
16 | print(ping_time)
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_1_xml.py:
--------------------------------------------------------------------------------
1 | #!/usr/env/bin python3
2 |
3 | import xml.etree.ElementTree as ET
4 | import pprint
5 |
6 | with open('chapter15_topology.virl', 'rt') as f:
7 | tree = ET.parse(f)
8 |
9 | devices = {}
10 |
11 | for node in tree.findall('./{http://www.cisco.com/VIRL}node'):
12 | name = node.attrib.get('name')
13 | devices[name] = {}
14 | for attr_name, attr_value in sorted(node.attrib.items()):
15 | devices[name][attr_name] = attr_value
16 |
17 | # Custom attributes
18 | devices['iosv-1']['os'] = '15.6(3)M2'
19 | devices['nx-osv-1']['os'] = '7.3(0)D1(1)'
20 | devices['host1']['os'] = '16.04'
21 | devices['host2']['os'] = '16.04'
22 |
23 | pprint.pprint(devices)
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_2_validation.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import requests
4 | import json
5 | import xml.etree.ElementTree as ET
6 | import unittest
7 |
8 | # Getting NX-OSv Version with NXAPI
9 | url='http://192.168.2.50/ins'
10 | switchuser='cisco'
11 | switchpassword='cisco'
12 |
13 | myheaders={'content-type':'application/json-rpc'}
14 | payload=[
15 | {
16 | "jsonrpc": "2.0",
17 | "method": "cli",
18 | "params": {
19 | "cmd": "show version",
20 | "version": 1.2
21 | },
22 | "id": 1
23 | }
24 | ]
25 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json()
26 |
27 | nxos_version = response['result']['body']['sys_ver_str']
28 |
29 | # Parse VIRL file, same as in example 1
30 | with open('chapter15_topology.virl', 'rt') as f:
31 | tree = ET.parse(f)
32 |
33 | devices = {}
34 |
35 | for node in tree.findall('./{http://www.cisco.com/VIRL}node'):
36 | name = node.attrib.get('name')
37 | devices[name] = {}
38 | for attr_name, attr_value in sorted(node.attrib.items()):
39 | devices[name][attr_name] = attr_value
40 |
41 | # Custom attributes
42 | devices['iosv-1']['os'] = '15.6(3)M2'
43 | devices['nx-osv-1']['os'] = '7.3(0)D1(1)'
44 | devices['host1']['os'] = '16.04'
45 | devices['host2']['os'] = '16.04'
46 |
47 | # Unittest Test case
48 | class TestNXOSVersion(unittest.TestCase):
49 | def test_version(self):
50 | self.assertEqual(nxos_version, devices['nx-osv-1']['os'])
51 |
52 | if __name__ == '__main__':
53 | unittest.main()
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_3_test_fail.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import requests
4 | import json
5 | import xml.etree.ElementTree as ET
6 | import unittest
7 |
8 | # Getting NX-OSv Version with NXAPI
9 | url='http://192.168.2.50/ins'
10 | switchuser='cisco'
11 | switchpassword='cisco'
12 |
13 | myheaders={'content-type':'application/json-rpc'}
14 | payload=[
15 | {
16 | "jsonrpc": "2.0",
17 | "method": "cli",
18 | "params": {
19 | "cmd": "show version",
20 | "version": 1.2
21 | },
22 | "id": 1
23 | }
24 | ]
25 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json()
26 |
27 | nxos_version = response['result']['body']['sys_ver_str']
28 |
29 | # Parse VIRL file, same as in example 1
30 | with open('chapter15_topology.virl', 'rt') as f:
31 | tree = ET.parse(f)
32 |
33 | devices = {}
34 |
35 | for node in tree.findall('./{http://www.cisco.com/VIRL}node'):
36 | name = node.attrib.get('name')
37 | devices[name] = {}
38 | for attr_name, attr_value in sorted(node.attrib.items()):
39 | devices[name][attr_name] = attr_value
40 |
41 | # Custom attributes
42 | devices['iosv-1']['os'] = '15.6(3)M2'
43 | devices['nx-osv-1']['os'] = '7.4(0)D1(1)'
44 | devices['host1']['os'] = '16.04'
45 | devices['host2']['os'] = '16.04'
46 |
47 | # Unittest Test case
48 | class TestNXOSVersion(unittest.TestCase):
49 | def test_version(self):
50 | self.assertEqual(nxos_version, devices['nx-osv-1']['os'])
51 |
52 | if __name__ == '__main__':
53 | unittest.main()
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_4_unittest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import unittest
4 |
5 | class SimpleTest(unittest.TestCase):
6 | def test(self):
7 | one = 'a'
8 | two = 'a'
9 | self.assertEqual(one, two)
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_5_more_unittest.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Examples from https://pymotw.com/3/unittest/index.html#module-unittest
3 |
4 | import unittest
5 |
6 | class Output(unittest.TestCase):
7 | def testPass(self):
8 | return
9 |
10 | def testFail(self):
11 | self.assertFalse(True, 'this is a failed message')
12 |
13 | def testError(self):
14 | raise RuntimeError('Test error!')
15 |
16 | def testAssesrtTrue(self):
17 | self.assertTrue(True)
18 |
19 | def testAssertFalse(self):
20 | self.assertFalse(False)
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_5_more_unittest_mocks.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | # Reference Johannes Fahrenkrug's answer on https://stackoverflow.com/questions/15753390/how-can-i-mock-requests-and-the-response
3 | import requests
4 | import unittest
5 | from unittest import mock
6 |
7 | # Our class making API Call using requests
8 | class MyClass:
9 | def fetch_json(self, url):
10 | response = requests.get(url)
11 | return response.json()
12 |
13 | # This method will be used by the mock to replace requests.get
14 | def mocked_requests_get(*args, **kwargs):
15 | class MockResponse:
16 | def __init__(self, json_data, status_code):
17 | self.json_data = json_data
18 | self.status_code = status_code
19 |
20 | def json(self):
21 | return self.json_data
22 |
23 | if args[0] == 'http://url-1.com/test.json':
24 | return MockResponse({"key1": "value1"}, 200)
25 | elif args[0] == 'http://url-2.com/test.json':
26 | return MockResponse({"key2": "value2"}, 200)
27 |
28 | return MockResponse(None, 404)
29 |
30 | # Our test case class
31 | class MyClassTestCase(unittest.TestCase):
32 | # We patch 'requests.get' with our own method. The mock object is passed in to our test case method.
33 | @mock.patch('requests.get', side_effect=mocked_requests_get)
34 | def test_fetch(self, mock_get):
35 | # Assert requests.get calls
36 | my_class = MyClass()
37 | # call to url-1
38 | json_data = my_class.fetch_json('http://url-1.com/test.json')
39 | self.assertEqual(json_data, {"key1": "value1"})
40 | # call to url-2
41 | json_data = my_class.fetch_json('http://url-2.com/test.json')
42 | self.assertEqual(json_data, {"key2": "value2"})
43 | # call to url-3 that we did not mock
44 | json_data = my_class.fetch_json('http://url-3.com/test.json')
45 | self.assertIsNone(json_data)
46 |
47 |
48 | if __name__ == '__main__':
49 | unittest.main()
50 |
51 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_6_pytest_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | def test_passing():
4 | assert(1, 2, 3) == (1, 2, 3)
5 |
6 | def test_failing():
7 | assert(1, 2, 3) == (3, 2, 1)
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_7_pytest_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | class router(object):
4 | def __init__(self, hostname=None, os=None, device_type='cisco_ios'):
5 | self.hostname = hostname
6 | self.os = os
7 | self.device_type = device_type
8 | self.interfaces = 24
9 |
10 | def test_defaults():
11 | r1 = router()
12 | assert r1.hostname == None
13 | assert r1.os == None
14 | assert r1.device_type == 'cisco_ios'
15 | assert r1.interfaces == 24
16 |
17 | def test_non_defaults():
18 | r2 = router(hostname='lax-r2', os='nxos', device_type='cisco_nxos')
19 | assert r2.hostname == 'lax-r2'
20 | assert r2.os == 'nxos'
21 | assert r2.device_type == 'cisco_nxos'
22 | assert r2.interfaces == 24
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_8_pytest_3.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 |
3 | import requests
4 | import json
5 | import xml.etree.ElementTree as ET
6 |
7 | # Getting NX-OSv Version with NXAPI
8 | url='http://192.168.2.50/ins'
9 | switchuser='cisco'
10 | switchpassword='cisco'
11 |
12 | myheaders={'content-type':'application/json-rpc'}
13 | payload=[
14 | {
15 | "jsonrpc": "2.0",
16 | "method": "cli",
17 | "params": {
18 | "cmd": "show version",
19 | "version": 1.2
20 | },
21 | "id": 1
22 | }
23 | ]
24 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json()
25 |
26 | nxos_version = response['result']['body']['sys_ver_str']
27 |
28 | # Parse VIRL file, same as in example 1
29 | with open('chapter15_topology.virl', 'rt') as f:
30 | tree = ET.parse(f)
31 |
32 | devices = {}
33 |
34 | for node in tree.findall('./{http://www.cisco.com/VIRL}node'):
35 | name = node.attrib.get('name')
36 | devices[name] = {}
37 | for attr_name, attr_value in sorted(node.attrib.items()):
38 | devices[name][attr_name] = attr_value
39 |
40 | # Custom attributes
41 | devices['iosv-1']['os'] = '15.6(3)M2'
42 | devices['nx-osv-1']['os'] = '7.3(0)D1(1)'
43 | devices['host1']['os'] = '16.04'
44 | devices['host2']['os'] = '16.04'
45 |
46 | # pytest test case
47 | def test_version():
48 | assert devices['nx-osv-1']['os'] == nxos_version
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Chapter16/chapter16_9_pytest_4.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import requests
3 | import json
4 | import pytest
5 |
6 | # Getting NX-OSv Version with NXAPI
7 | url='http://172.16.1.21/ins'
8 | switchuser='cisco'
9 | switchpassword='cisco'
10 |
11 | myheaders={'content-type':'application/json-rpc'}
12 | payload=[
13 | {
14 | "jsonrpc": "2.0",
15 | "method": "cli",
16 | "params": {
17 | "cmd": "show version",
18 | "version": 1.2
19 | },
20 | "id": 1
21 | }
22 | ]
23 | response = requests.post(url,data=json.dumps(payload), headers=myheaders,auth=(switchuser,switchpassword)).json()
24 | nxos_version = response['result']['body']['sys_ver_str']
25 |
26 | def test_transaction():
27 | assert nxos_version != False
28 |
29 |
--------------------------------------------------------------------------------
/Chapter16/pyATS/chapter16_11_pyats_1.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | #
3 | # derived from https://devnet-pubhub-site.s3.amazonaws.com/media/pyats/docs/getting_started/index.html
4 | #
5 | from pyats.topology import loader
6 |
7 | # load testbed
8 | testbed = loader.load('chapter16_pyats_testbed_1.yml')
9 |
10 | # access the device
11 | testbed.devices
12 | lax_edg_r1 = testbed.devices['lax-edg-r1']
13 |
14 | # establish connectivity
15 | lax_edg_r1.connect()
16 |
17 | # issue command
18 | print(lax_edg_r1.execute('show version'))
19 |
20 | # disconnect
21 | lax_edg_r1.disconnect()
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Chapter16/pyATS/chapter16_12_pyats_2.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | #
3 | # from https://devnet-pubhub-site.s3.amazonaws.com/media/pyats/docs/getting_started/index.html
4 | #
5 |
6 | from pyats import aetest
7 | import re
8 |
9 | class CommonSetup(aetest.CommonSetup):
10 |
11 | @aetest.subsection
12 | def check_topology(self,
13 | testbed,
14 | iosv1_name = 'lax-edg-r1',
15 | nxosv1_name = 'lax-cor-r1'):
16 | ios1 = testbed.devices[iosv1_name]
17 | nxos1 = testbed.devices[nxosv1_name]
18 |
19 | # add them to testscript parameters
20 | self.parent.parameters.update(ios1 = ios1, nxos1 = nxos1)
21 |
22 | # get corresponding links
23 | links = ios1.find_links(nxos1)
24 |
25 | assert len(links) >= 1, 'require one link between ios1 and nxos1'
26 |
27 |
28 | @aetest.subsection
29 | def establish_connections(self, steps, ios1):
30 | with steps.start('Connecting to %s' % ios1.name):
31 | ios1.connect()
32 |
33 |
34 | @aetest.loop(device = ('ios1',))
35 | class PingTestcase(aetest.Testcase):
36 |
37 | @aetest.test.loop(destination = ('10.0.0.1', '10.0.0.2'))
38 | def ping(self, device, destination):
39 | try:
40 | result = self.parameters[device].ping(destination)
41 |
42 | except Exception as e:
43 | self.failed('Ping {} from device {} failed with error: {}'.format(
44 | destination,
45 | device,
46 | str(e),
47 | ),
48 | goto = ['exit'])
49 | else:
50 | match = re.search(r'Success rate is (?P\d+) percent', result)
51 | success_rate = match.group('rate')
52 |
53 |
54 | class CommonCleanup(aetest.CommonCleanup):
55 |
56 | @aetest.subsection
57 | def disconnect(self, steps, ios1):
58 | with steps.start('Disconnecting from %s' % ios1.name):
59 | ios1.disconnect()
60 |
61 |
62 | if __name__ == '__main__':
63 | import argparse
64 | from pyats.topology import loader
65 |
66 | parser = argparse.ArgumentParser()
67 | parser.add_argument('--testbed', dest = 'testbed',
68 | type = loader.load)
69 |
70 | args, unknown = parser.parse_known_args()
71 |
72 | aetest.main(**vars(args))
73 |
74 |
--------------------------------------------------------------------------------
/Chapter16/pyATS/chapter16_pyats_testbed_1.yml:
--------------------------------------------------------------------------------
1 | testbed:
2 | name: Chapter_16_pyATS
3 | tacacs:
4 | username: cisco
5 | passwords:
6 | tacacs: cisco
7 | enable: cisco
8 |
9 | devices:
10 | lax-edg-r1:
11 | alias: lax-edg-r1
12 | type: ios
13 | connections:
14 | defaults:
15 | class: unicon.Unicon
16 | management:
17 | ip: 192.168.2.51
18 | protocol: ssh
19 |
20 | topology:
21 | lax-edg-r1:
22 | interfaces:
23 | GigabitEthernet0/1:
24 | ipv4: 10.0.0.1/30
25 | link: link-1
26 | type: ethernet
27 | Loopback0:
28 | ipv4: 192.168.0.10/32
29 | link: iosv-1_Loopback0
30 | type: loopback
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Chapter16/pyATS/chapter16_pyats_testbed_2.yml:
--------------------------------------------------------------------------------
1 | testbed:
2 | name: Chapter_16_pyATS
3 | tacacs:
4 | username: cisco
5 | passwords:
6 | tacacs: cisco
7 | enable: cisco
8 |
9 | devices:
10 | lax-edg-r1:
11 | alias: iosv-1
12 | type: ios
13 | connections:
14 | defaults:
15 | class: unicon.Unicon
16 | vty:
17 | ip: 192.168.2.50
18 | protocol: ssh
19 |
20 | lax-cor-r1:
21 | alias: nxosv-1
22 | type: ios
23 | connections:
24 | defaults:
25 | class: unicon.Unicon
26 | vty:
27 | ip: 192.168.2.51
28 | protocol: ssh
29 |
30 | topology:
31 | lax-edg-r1:
32 | interfaces:
33 | GigabitEthernet0/1:
34 | ipv4: 10.0.0.1/30
35 | link: link-1
36 | type: ethernet
37 | Loopback0:
38 | ipv4: 192.168.0.10/32
39 | link: lax-edg-r1_Loopback0
40 | type: loopback
41 | lax-cor-r1:
42 | interfaces:
43 | Eth2/1:
44 | ipv4: 10.0.0.2/30
45 | link: link-1
46 | type: ethernet
47 | Loopback0:
48 | ipv4: 192.168.0.100/32
49 | link: lax-cor-r1_Loopback0
50 | type: loopback
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Chapter16/result.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/Errata.md:
--------------------------------------------------------------------------------
1 | # Errata and Improvements
2 |
3 | ## Page 173, Chapter 5
4 | The book stated the Nokia SR Linux default username and password are both admin. This is true prior to release 22.11.1.
5 | The username for releases after that version is still admin, but the password is set to NokiaSr1!, please see https://containerlab.dev/manual/kinds/srl/.
6 |
7 | ## CML NX-OSv no longer available in the latest CML
8 | Thank you [Grana2codes](https://github.com/Grana2codes), Paolo G., Jilles C. for pointing out the issue. In the latest CML, NX-OSv is still available for use within CML, but no longer included in teh refplat IOS image (more information [here](https://developer.cisco.com/docs/modeling-labs/#!nx-os/overview)). You can still download the image [here](https://developer.cisco.com/docs/modeling-labs/#!downloading-files-for-cml-installation). Or you can update the image definition from nxosv to nxosv9000 instead.
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mastering-Python-Networking-Fourth-Edition
2 | This is the code repository for [Mastering Python Networking Fourth Edition](https://www.amazon.com/Mastering-Python-Networking-frameworks-automation/dp/180323461X/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=), published by Packt.
3 |
4 | **Utilize Python packages and frameworks for network automation,monitoring, cloud, and management**
5 |
6 | ## About the book
7 |
8 | The new edition is revised and updated with Python 3.10 along with new chapters on AsyncIO and Docker containers. The book will provide examples and guidance to manage your network, improve reliability, and simplify your task. The fourth edition also includes network context examples of Ansible, Flask, Elasticsearch, Git, GitLab and more.
9 |
10 | ## What you will learn
11 |
12 | - Use Python to interact with network devices
13 | - Understand Docker as a tool that you can use for the development and deployment
14 | - Use Python and various other tools to obtain information from the network
15 | - Learn how to use ELK for network data analysis
16 | - Utilize Flask and construct high-level API to interact with in-house applications
17 | - Discover the new AsyncIO feature and its concepts in Python 3
18 | - Explore Test-Driven Development concepts and use PyTest to drive code test coverage
19 | - Understand how GitLab can be used with DevOps practices in networking
20 |
21 |
22 | ## Table of Contents
23 | ### Chapters
24 | 1. Review of TCP/IP Protocol Suite and Python
25 | 2. Low-Level Network Device Interactions
26 | 3. APIs and Intent-Driven Networking
27 | 4. The Python Automation Framework – Ansible
28 | 5. Docker Containers for Network Engineers
29 | 6. Network Security with Python
30 | 7. Network Monitoring with Python – Part 1
31 | 8. Network Monitoring with Python – Part 2
32 | 9. Building Network Web Services with Python
33 | 10. Introduction to Async IO
34 | 11. AWS Cloud Networking
35 | 12. Azure Cloud Networking
36 | 13. Network Data Analysis with Elastic Stack
37 | 14. Working with Git
38 | 15. Continuous Integration with GitLab
39 | 16. Test-Driven Development for Networks
40 |
41 | > If you feel this book is for you, get your [copy](https://www.amazon.com/Mastering-Python-Networking-frameworks-automation/dp/180323461X/ref=tmm_pap_swatch_0?_encoding=UTF8&qid=&sr=) today!
42 |
43 |
44 | ### Following is what you need for this book: ###
45 |
46 | To get the most out of this book, some basic hands-on network operation knowledge and Python knowledge is recommended. Most of the chapters can be read in any order, except for Chapter 4 and Chapter 5, which introduce base technologies that will be used later in the book. Besides the basic software and hardware tools introduced at the beginning of the book, new tools relevant to each of the chapters will be introduced in the respective chapters.
47 | It is highly recommended to follow and practice the examples shown in your network lab.
48 |
49 | ### Important Information
50 |
51 | Refer to the [Errata](https://github.com/PacktPublishing/Mastering-Python-Networking-Fourth-Edition/blob/main/Errata.md) for corrections of typos and other mistakes and improvements like refactoring code.
52 |
53 |
54 | ## Know more on the Discord server
55 |
56 | You can get more engaged on the discord server for more latest updates and discussions in the community at [Discord link as text here](https://packt.link/networkautomationcommunity)
57 |
58 | ## Download a free PDF
59 |
60 | _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._
61 | [https://packt.link/free-ebook/9781803234618](https://packt.link/free-ebook/9781803234618)
62 |
63 | We also provide a PDF file that has color images of the screenshots/diagrams used in this book at "https://packt.link/D2Ttl"
64 |
65 |
66 | ## Get to Know the Author
67 |
68 | Eric Chou is a seasoned technologist with over 20 years of experience. He has worked on some of the largest networks in the industry while working at Amazon, Azure, and other Fortune 500 companies. Eric is passionate about network automation, Python, DevOps, and helping companies build better security postures. In addition to being the author of Mastering Python Networking (Packt), he is the author or co-author of other top-selling books and highly-rated online classes. Eric is the primary inventor or co-inventor for three U.S. patents in IP telephony and networking. He shares his deep interest in technology through his books, classes, blog, and contributes to some of the popular Python open source projects.
69 |
--------------------------------------------------------------------------------