Network Automation Fundamentals Part 4
Network Automation Fundamentals Part 4
Python Fundamentals
Like any programming language, Python supports different data types. Each supported type can be
divided into two categories: primitive and nonprimitive. Primitive data types are represented in their
basic form. Nonprimitive data types are intended to organize and manage primitive data types.
These primitive types store single values.
These examples of data types from each category will help you understand how to define and use
them in Python code.
Primitive Data Types
Integer: Any whole number is an integer.
>>> ssh_timeout = 30
>>> error_code= -1
Float: This data type is a floating-point number, and any number with a decimal point is
considered a float.
>>> ios_version = 15.8
String: Any alphanumeric characters that are enclosed within a pair of single or double
quotation marks is a string.
>>> leaf_switch = "Nexus 9300"
>>> spine_switch = "Nexus 9336PQ"
Boolean: This data type can accept only two values: True or False.
>>> code_upgraded = True
>>> errors_observed = False
Nonprimitive Data Types
List: Any data that is enclosed within square brackets ( [ ] ) and separated by a comma is
considered a list. In Python, the objects in a list are indexed, where the first object in the list
starts with index 0, the proceeding object is 1, and so on.
>>> devices = ["ASA", "NEXUS", "CATALYST", "ASR"]
Tuple: Any data that is enclosed within parenthesis ( ( ) ) and separated by a comma is
considered a tuple. Tuples are immutable, meaning that data is stored in a tuple cannot be
modified at run time. The following is a tuple of IP addresses.
>>> ip_addr = ("10.254.0.1", "10.254.0.2", "10.254.0.3")
>>> ip_addr[1]
'10.254.0.2'
>>> ip_addr[1] = "10.254.1.2"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
Dictionary: Any key-value pairs that are enclosed in curly brackets ( { } ) and separated by
a comma is a dictionary. The following dictionary has keys that are equal to interface names
and values with the desired state of the interface.
>>> if_state = {"Gi0/1":"shutdown", "Gi0/2":"no shutdown"}
Set: A collection of unique objects that is enclosed curly brackets ( { } ) and separated by a
comma is considered a set.
>>> if_names = {"Gi0/1", "Gi0/2", "Gi0/3", "Gi0/1"}
>>> if_names
{'Gi0/1', 'Gi0/2', 'Gi0/3'}
Primitive data types can be converted to other primitive types using built-in functions, assuming
that the converted value is valid.
>>> bool(1)
True
>>> int(False)
0
>>> float("3.14")
3.14
Some nonprimitive data types can also be converted to other (similar) data types. For example, a list
can be converted to a set or a tuple but cannot be converted to a dictionary.
>>> devices= ["ASA", "NEXUS", "CATALYST", "NEXUS", "asa"]
>>> set(devices)
{'NEXUS', 'ASA', 'asa', 'CATALYST'}
As shown in the output, the devices list contains a duplicate entry of "NEXUS", but it was removed
when converted to the set data type. Note that set removes items based on case sensitivity. The
previous output shows that 'ASA' and 'asa' are still present in the set because they are different
values.
It is possible to convert a list to a tuple, but the variable that holds the converted tuple can no longer
be modified. Here you can see an example:
>>> cisco_devices = tuple(devices)
('ASA', 'NEXUS', 'CATALYST')
>>> cisco_devices
('ASA', 'NEXUS', 'CATALYST', 'NEXUS', 'asa')
>>> cisco_devices[-1] = "FTD"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
It is not possible to convert one data type to another if the converted value is invalid. For example,
an error will be raised if you attempt to convert a list to a dictionary. Here is an example of such an
error:
>>> dict(devices)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: dictionary update sequence element #0 has length 3; 2 is required
Each data type that was mentioned previously supports different built-in methods and attributes. To
list the methods that can be used on a particular data type, create a variable with that type and then
issue the dir() built-in method. Here you see an example of methods that can be used on the string
data types.
>>> vendor = "Cisco"
>>> dir(vendor)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__',
'__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find',
'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal', 'isdigit', 'isidentifier', 'islower',
'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans',
'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith',
'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
If you want to convert a string to all capital letters, you need to use the upper() method.
>>> vendor.upper()
CISCO
To learn how to use each method, you can use the help() function.
>>> help(vendor.upper)
Nested data structures are only applicable to nonprimitive types. Each nonprimitive type can
contain the same or other nonprimitive data types as nested entries.
For example, a nested list can contain a dictionary as a nested item:
>>> if_state = ["Gi0/1", [{"state": "shutdown"}]]
To access the value inside the nested list without looping through the list, you first need to identify
its position in the list. Because lists are ordered, and the positions, starting from 0, are incremented
from left to right by 1, the position for the nested dictionary will be 1.
Now, to refer to the position, you need to put an integer within the square brackets:
>>> if_state[1]
[{"state": "shutdown"}]
However, that will only give you the [{"state": "shutdown"}] item, because it is also a list, and has
only one value that can be referenced with its positional number:
>>> if_state[1][0]
{"state": "shutdown"}
At this point, what remains is a dictionary. Now you can print the value of the key by appending the
name of the key to the variable that precedes the position in the list:
>>> if_state[1][0]["state"]
shutdown
Now, consider a nested dictionary. As you know, dictionaries are not sorted, so the position of a key
cannot be referenced. Instead, it can be referenced directly by specifying its name and enclosing it
in square brackets. Here is an example:
>>> facts ={"csr1kv1": {"os":"ios-xe", "version":"16.09.03"}, "if_state": [{"name": "Gi0/1",
"state":"shutdown"}]}
You can obtain nested values that are stored under each root key. In this example, start with
csr1kv1. To return the value stored in the key, you enclose the key’s name in square brackets and
append it to the variable.
>>> facts["csr1kv1"]
{"os":"ios-xe", "version":"16.09.03"}
The returned value is another dictionary. To return the value of the nested key, you need to add its
name after the name of the root key.
>>> facts["csr1kv1"]["os"]
ios-xe
>>> facts["csr1kv1"]["version"]
16.09.03
Now consider the second root key. As you can see, the value is a list, so you need to act accordingly.
You need to obtain the value, pick the position within the list, and then use the key name to return
the value.
>>> facts["if_state"]
[{"name”: "Gi0/1", "state":"shutdown"}]
>>> facts["if_state"][0]
{"name": "Gi0/1", "state":"shutdown"}
>>> facts["if_state"][0]["name"]
Gi0/1
Understanding how to find the position in nested lists is crucial in day-to-day programming,
especially when dealing with API calls. Therefore, you are encouraged to open the Python
interpreter and create a nested list with different depths and try finding the values that interest you.
Python Conditionals
In Python, conditional statements perform different actions depending on whether a condition
evaluates as true or false.
Conditional Statements
if, elif, else
Comparison Operators
==, !=, <, >, <=, >=
Boolean Operators
and, or, not
Membership Operators
in, not in
Identity Operators
is, is not
To easily evaluate a statement, you can use different built-in operators like comparison ( == ). This
operator compares the value on the left side to the value on the right side and determines if they are
equal (True) or not equal (False).
Here is a list of operators that can be used during different evaluations:
Comparison operators:
1. == : This operator compares whether values on the left and right side are equal.
2. != : This operator compares whether values on the left and right side are not equal.
3. < : This operator compares whether the value on the left is less than the value on the
right.
4. > : This operator compares whether the value on the left is greater than the value on
the right.
5. <= : This operator compares whether the value on the left is less than or equal to the
value on the right.
6. >= : This operator compares whether the value on the left is greater than or equal to
the value on the right.
Boolean operators:
1. and: This operator requires that both statements are true.
2. or: This operator allows either of the statements to be true.
3. not: This operator returns the value that is the opposite of the original statement
(true for false; false for true).
Membership operators:
1. in: This operator checks whether the value on the left exists in the value on the right.
2. not in: This operator checks whether the value on the left does not exist in the value
on the right.
Identity operators:
1. is: This operator compares whether the value that is assigned to the variable on the
left is the same as the value that is assigned to the variable to the right
2. is not: This operator compares whether the value assigned to the variable on the left
is not the same as the value assigned to the variable to the right.
Now you can apply the operators to the conditional statements. Python has three conditional
statements: if, elif, and else. The "if" condition starts the statement evaluation process. If the
statement is true, an action is executed, and the code block is exited. If the statement is false, then
an optional elif conditional check can be used to evaluate another statement for being true or false.
If all conditional statements evaluate as false, then the else clause can be used to execute a line of
code.
In addition, the conditional checks can be nested:
>>> if hostname == "csr1kv-1":
… if os_version == "16.09.03":
… print("Device {hostname} is running version {os_version}".format(hostname=hostname,
os_version=os_version))
… else:
… print("Device {hostname} is running unknown version of
os".format(hostname=hostname))
Python Loops
Manually obtaining a value from a nested dictionary or list is not very practical. In addition, it is not
very practical to have a repetitive code that checks if a value has changed. To address those types of
scenarios, you will need to use proper tools within Python. To address the first problem, you will
use a for loop and to address the second one, you will use a while loop. The explanation of each
loop type and supporting examples are as follows:
For Loops
Unlike other programming languages, the Python "for" loop does not evaluate a statement before
running the underlying code; instead, iterates over a provided object. The Python "for" loop is like a
“for each” loop in other languages.
The given object being “looped over” or “iterated over” can be a primitive string, range, or
nonprimitive (list, dictionary, tuple, or a set) data type. The syntax of a for loop is as follows: for
variable_name in object_of_iteration:
variable_name: This temporary variable holds data that are relevant to that iteration cycle.
A good general principle is to give the temporary variable a name that describes the data it
will hold. For example, if you are iterating over VLANs, then name the variable “vlan,” for
example, for vlan in vlans: A more generic name that is often used is “item,” for example,
for item in vlans:.
in: This variable is a membership operator.
object_of_iteration: This variable name is being looped (iterated) through.
Example:
>>> vlans = [100, 200, 300]
>>> for vlan in vlans:
. . . print(vlan)
100
200
300
While Loops
“While” is another looping method that Python supports, but instead of iterating over an object, the
"While" loop executes underlying code until the supplied condition is true. This mechanism is very
useful, but it requires attention. Because the underlying code is running while the condition is true,
it is possible to enter an infinite-looping state during which code runs forever and can negatively
affect the host on which the code is running.
Example:
>>> interface_id =1
>>> while interface_id <=4:
... print('Ethernet1/{}'.format(interface_id))
... interface_id += 1
Ethernet1/1
Ethernet1/2
Ethernet1/3
Ethernet1/4
Python Functions
All programming languages, including Python, can create blocks of organized and reusable code
called functions. Functions provide efficiency, consistency, and modularity. Functions can be built-
in, written in the code, or imported from other Python scripts.
Create reusable code
Wrap standard Python code within a function definition
>>> def issue_command(hostname, command):
... print("Connecting to device: {}".format(hostname))
... print("Issuing the following command: {}".format(command))
...
>>>
>>> issue_command('nycr1', 'show version')
Connecting to device: nycr1
Issuing the following command: show version
>>>
Functions might require arguments to be provided when called and optionally return data for further
processing.
The following examples illustrate functions and how to use them:
User-defined function:
>>> def hostname_conf(name):
... return 'hostname {}'.format(name)
...
>>> print(hostname_conf('csr1kv-1'))
hostname csr1kv-1
Built-in functions:
>>> len(vlans)
1
>>> str(100)
'100'
>>> int(3.14)
3
Content Review Question
Correct
A variable of the list data type that is named devices has the following value:
[{"hostname":"csr1kv1"}]. Which Python command will print the value of the "hostname" key?
print(devices["hostname"])
print(devices[0]["hostname"]) <<<<<<<<<<<<<
print(devices[0])
print(devices[0][hostname])
Submit
Content Review Question
Correct
Refer to the following Python script. Which message will be printed?
models=["ASA", "NEXUS", "CATALYST"]
if "asa" in models:
print("models variable contains ASA")
elif "nexus".upper() in models:
print("models variable contains NEXUS")
elif "CATALYST" == models[-1]:
print("models variable contains CATALYST")
else:
print("No Match Was Found!")
No Match Was Found!
models variable contains CATALYST
models variable contains NEXUS <<<<<<<<<<<<<<<<
models variable contains ASA
Submit
Content Review Question
Correct
Which is the correct syntax to define a function?
function print_hostname():
define print_hostname():
type print_hostname():
def print_hostname(): <<<<<<<<<<<<
Enforce Python Fundamentals on the Interactive Interpreter
Play Video
Open Transcript
Play Video
Open Transcript
Play Video
Open Transcript
Interact with Data Types
You will create different Python data types and interact with them directly with the Python
interpreter.
Navigate to the Python Script Directory
Step 1
In the Student Workstation, open a terminal window and change the directory to /labs/lab02/task01
using the cd $HOME/labs/lab02/task01 command.
Answer
student@student-vm:~$ cd labs/lab02/task01/
(lab_02) student@student-vm:~/labs/lab02/task01$
Start the Python Interpreter
Step 2
In the terminal window, issue the python command to start the interpreter.
Answer
(lab_02) student@student-vm:~/labs/lab02/task01$ python
Python 3.6.8 (default, Jan 14 2019, 11:02:34)
[GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
Step 3
Create a variable hostname and assign the "R" string to it. Then create another variable
called ios_version and assign a string "15.8" to it.
Answer
>>> hostname = "R"
>>> ios_version = "15.8"
Step 4
Using built-in methods, create a new variable of type string and name it hostname_upper. Then
assign it the value of variable hostname, but with all uppercase letters. Print both variables
(hostname_upper and ios_version) in a single sentence, and append a descriptive word for the
variables. You can use the following commands to complete the step:
hostname_upper = hostname.upper()
print("Hostname: {} and IOS Version: {}".format(hostname_upper,ios_version))
Answer
>>> hostname_upper = hostname.upper()
>>> print("Hostname: {} and IOS Version:{}".format(hostname_upper,ios_version))
Hostname: R and IOS Version: 15.8
Step 5
Using the dir() built-in function, print all valid built-in methods, and attributes of the string. Notice
that there is a method capitalize; it will capitalize the first letter only. Apply the method to the
hostname variable and observe the output.
Answer
>>> dir(hostname)
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__',
'__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__',
'__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__',
'__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode',
'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isdecimal',
'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust',
'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip',
'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
>>> hostname.capitalize()
‘R’
Step 6
Create a variable of the type list and name it vlan_names. Assign these six VLAN names to the
list: "HR", "R_and_D", "Marketing", "Accounting", "Sales", and "Engineering". Obtain the
length of the newly created variable by using the len() built-in function.
Answer
>>> vlan_names = ["HR", "R_and_D", "Marketing", "Accounting", "Sales",
"Engineering"]
>>> len(vlan_names)
6
Step 7
Print the last value of the list by using a positive positional number.
Note
Positive positional numbers are assigned to the items from left to right, and start with 0. The last
value in the list has a positional value, or index, which is one less than the length that
the len() method previously reported.
Answer
>>> print(vlan_names[5])
Engineering
Step 8
With two statements, print the last value and then the last three values of the list, using only the
negative index values.
Note
The negative positional numbers are assigned to the items in the list from right to left and start with
-1. Use a colon (:) to refer to all numbers after a specified positional number.
Answer
>>> vlan_names[-1]
'Engineering'
>>> vlan_names[-3:]
['Accounting', 'Sales', 'Engineering']
Step 9
Find the index value of both the “Sales” and “HR” list elements by using the index() method.
Answer
>>> vlan_names.index("Sales")
4
>>> vlan_names.index("HR")
0
Step 10
Change the value of the first entry in the vlan_names variable to a new name. Confirm the change
by printing the entire variable.
Note
Remember, the first entry in that list has an index of 0, not 1.
Answer
>>> vlan_names[0] = "Accounting"
>>> print(vlan_names)
['Accounting', 'R_and_D', 'Marketing', 'Accounting', 'Sales', 'Engineering']
Step 11
Create a dictionary with the name device_facts that has following keys and their respective values.
All values except 16.8 are strings.
Keys: vendor, os_version, platform, os_type, and hostname.
Values: cisco, 16.8, cisco catalyst, ios, and R1.
Note
In the dictionary, the value is assigned to the key with a colon (:), and separated from other key-
value pairs with a comma (,).
Answer
>>> device_facts = {"vendor": "cisco", "os_version": 16.8, "platform": "cisco catalyst",
"os_type": "ios", "hostname": "R1"}
>>>
Step 12
Print the values using the built-in keys() and values() methods on the device_facts dictionary.
Answer
>>> print(device_facts.keys())
dict_keys(['vendor', 'os_version', 'platform', 'os_type', 'hostname'])
>>> print(device_facts.values())
dict_values(['cisco', 16.8, 'cisco catalyst', 'ios', 'R1'])
Step 13
Now, print the value of the platform key using first a direct reference, and then the get() method.
Note
The get() method searches the dictionary for a specified key and returns its value when the key
exists. If the key does not exist, it returns None or an optional value that you can provide.
Answer
>>> print(device_facts["platform"])
cisco catalyst
>>> print(device_facts.get("platform"))
cisco catalyst
Step 14
Supply the name of a nonexistent key, interfaces, to the get() method and observe the output. Print
the value of the nonexistent key using the get() method. Then do the same thing, but instead of
printing nothing, make the get() method return UNKNOWN.
Note
When a nonexistent key is supplied to the get() method, it does not print anything, but it is possible
to alter that behavior by supplying a second argument as a fallback option.
Answer
>>> device_facts.get("interfaces")
>>>
>>> print(device_facts.get("interfaces"))
None
>>>
>>> print(device_facts.get("interfaces", "UNKNOWN"))
UNKNOWN
Step 15
You can remove any key and its value by using the pop() method. Remove the platform key from
device_facts, and confirm that it was removed.
Answer
>>> device_facts.pop("platform")
'cisco catalyst'
>>> print(device_facts)
{'vendor': 'cisco', 'os_version': 16.8, 'os_type': 'ios', 'hostname': 'R1'}
Step 16
Exit the Python interpreter using the exit() command from the shell.
Answer
>>> exit()
(lab_02)student@student-vm:~/labs/lab02/task01$
Explore Nested Data Structures
You will interact with and explore nested data including complex lists and dictionaries.
Note
The inventory and neighbor_data variable entries that you will see and use in this task are just
sample values for the purpose of exploring the nested data structures. They do not relate to actual
devices in the topology.
Step 17
In the Student Workstation terminal window, change the directory to /labs/lab02/task02 using the cd
$HOME/labs/lab02/task02 command.
Answer
(lab_02)student@student-vm:~/labs/lab02/task01$ cd $HOME/labs/lab02/task02
(lab_02)student@student-vm:~/labs/lab02/task02$
Start the Python Interpreter and Load Predefined Variables
Step 18
In the terminal window, issue the python -i task02_variables.py command to start the interpreter
in interactive mode, and load predefined commands. Confirm that variables are loaded properly by
printing the inventory and neighbor_data variables.
Note
The -i argument keeps the Python interpreter open after running the script. In this case, the script
holds variables, so Python loads them and allows you to interact with them. Otherwise, you would
normally copy and paste the code from that script into the interpreter directly.
Answer
student@student-vm:~/labs/lab02/task02$ python –i task02_variables.py
>>> print(inventory)
{'nycr01': {'vendor': 'cisco', 'os_version': 16.8, 'platform': 'cisco catalyst', 'os_type': 'ios', 'hostname':
'R1'}, 'nxos-spine1': {'vendor': 'cisco', 'os_version': 9.0, 'platform': 'nexus', 'os_type': 'nxos',
'hostname': 'nxos-spine1'}}
>>> print(neighbor_data)
{'Eth1': [{'neighbor': 'r1', 'neighbor_interface': 'Eth1'}, {'neighbor': 'r2', 'neighbor_interface':
'Eth2'}], 'Eth2': [{'neighbor': 'r1', 'neighbor_interface': 'Eth5'}], 'Eth3': [{'neighbor': 'r3',
'neighbor_interface': 'Eth3'}]}
>>>
Step 19
From within the inventory variable, print the os_version of the nycr01 device.
Answer
>>> print(inventory['nycr01']['os_version'])
16.8
Step 20
Earlier, you used the get() method to find the value of a specified key. Using the same method, find
the platform of the nxos-spine1 device.
Note
When using the get() method on a nested dictionary, you will need to specify the level on which
you want the search to be executed. You will need to extract the data that is assigned to the nxos-
spine1 device, then use get().
Answer
>>> print(inventory['nxos-spine1'].get('platform'))
nexus
Step 21
Add a new entry to the inventory variable. Name the root key nycr02, and then assign the following
keys with their respective values. All values, except the os_version, are strings.
Keys: vendor, os_version, platform, os_type, and hostname
Values: cisco, 15.8, cisco catalyst, ios, and R2
Note
Note: Because the nycr02 key does not exist in the dictionary, you will need to initialize the empty
key with the inventory['nycr02'] = {} command. Then add each subsequent key using the same
method.
Answer
>>> inventory['nycr02'] = {}
>>> inventory['nycr02']['vendor'] = 'cisco'
>>> inventory['nycr02']['os_version'] = 15.8
>>> inventory['nycr02']['platform'] = 'cisco catalyst'
>>> inventory['nycr02']['os_type'] = 'ios'
>>> inventory['nycr02']['hostname'] = 'R2'
>>> print(inventory['nycr02'])
{'vendor': 'cisco', 'os_version': 15.8, 'platform': 'cisco catalyst', 'os_type': 'ios', 'hostname': 'R2'}
Step 22
Examine the neighbor_data variable for the Eth1 interface. Then, print out name of each neighbor
that is connected to each of the three interfaces.
Note
When dealing with several layers of nested data, start by removing each layer one-by-one until you
get to your desired value. Be aware that each layer can be a different data type, so you will need to
use proper syntax to progress to the next layer.
Answer
>>> neighbor_data['Eth1']
[{'neighbor': 'r1', 'neighbor_interface': 'Eth1'}, {'neighbor': 'r2', 'neighbor_interface': 'Eth2'}]
>>> neighbor_data['Eth1'][0]
{'neighbor': 'r1', 'neighbor_interface': 'Eth1'}
>>> neighbor_data['Eth1'][0][ 'neighbor']
'r1'
>>> neighbor_data['Eth1'][1]
{'neighbor': 'r2', 'neighbor_interface': 'Eth2'}
>>> neighbor_data['Eth1'][1][ 'neighbor']
'r2'
>>> neighbor_data['Eth2'][0][ 'neighbor']
'r1'
>>> neighbor_data['Eth3'][0][ 'neighbor']
'r3'
Step 23
Update the inventory variable with the contents of the neighbor_data variable using the built-in
method called update. Examine the updated inventory variable using
the print(inventory) or inventory command.
Answer
>>> inventory.update(neighbor_data)
>>> inventory
{'nycr01': {'vendor': 'cisco', 'os_version': 16.8, 'platform': 'cisco catalyst', 'os_type': 'ios', 'hostname':
'R1'}, 'nxos-spine1': {'vendor': 'cisco', 'os_version': 9.0, 'platform': 'nexus', 'os_type': 'nxos',
'hostname': 'nxos-spine1'}, 'nycr02': {'vendor': 'cisco', 'os_version': 15.8, 'platform': 'cisco catalyst',
'os_type': 'ios', 'hostname': 'R2'}, 'Eth1': [{'neighbor': 'r1', 'neighbor_interface': 'Eth1'}, {'neighbor':
'r2', 'neighbor_interface': 'Eth2'}], 'Eth2': [{'neighbor': 'r1', 'neighbor_interface': 'Eth5'}], 'Eth3':
[{'neighbor': 'r3', 'neighbor_interface': 'Eth3'}]}
Step 24
Printed dictionaries have many entries, and are not easy to read, but there is a way to overcome this
problem. You can convert the dictionary to JavaScript Object Notation (JSON) format using the
json module dumps() method. If you indent text, the result will be readable printed data. Use the
following Python statements:
import json
print(json.dumps(inventory, indent=4))
Answer
>>> import json
>>> print(json.dumps(inventory, indent=4))
{
"nycr01": {
"vendor": "cisco",
"os_version": 16.8,
"platform": "cisco catalyst",
"os_type": "ios",
"hostname": "R1"
},
"nxos-spine1": {
"vendor": "cisco",
"os_version": 9.0,
"platform": "nexus",
"os_type": "nxos",
"hostname": "nxos-spine1"
},
"nycr02": {
"vendor": "cisco",
"os_version": 15.8,
"platform": "cisco catalyst",
"os_type": "ios",
"hostname": "R2"
},
"Eth1": [
{
"neighbor": "r1",
"neighbor_interface": "Eth1"
},
{
"neighbor": "r2",
"neighbor_interface": "Eth2"
}
],
"Eth2": [
{
"neighbor": "r1",
"neighbor_interface": "Eth5"
}
],
"Eth3": [
{
"neighbor": "r3",
"neighbor_interface": "Eth3"
}
]
}
Step 25
Exit the Python interpreter using the exit() command from the shell.
>>> exit()
(lab_02)student@student-vm:~/labs/lab02/task02$
Use Python Loops
Now, you will use different Python loops to iterate over lists and dictionaries. Some errors have
been intentionally introduced in the scripts, so that you will have an opportunity to troubleshoot and
make them functional.
Step 26
In the Student Workstation terminal window, change the directory to /labs/lab02/task03 using the cd
$HOME/labs/lab02/task03 command.
Answer
(lab_02)student@student-vm:~/labs/lab02/task02$ cd $HOME/labs/lab02/task03
(lab_02)student@student-vm:~/labs/lab02/task03$
Step 27
To list all files in the current folder, use the ls -al command.
Answer
(lab_02)student@student-vm:~/labs/lab02/task03$ ls -al
total 28
drwxrwxr-x 2 student student 4096 Oct 22 14:14 .
drwxrwxr-x 6 student student 4096 Aug 14 01:08 ..
-rw-rw-r-- 1 student student 425 Aug 13 20:39 task03_for_and_while_loop.py
-rw-rw-r-- 1 student student 847 Oct 22 14:12 task03_for_loop_nested_dictionary.py
-rw-rw-r-- 1 student student 946 Aug 13 11:08 task03_nested_for_loop.py
-rw-rw-r-- 1 student student 196 Aug 13 10:09 task03_while_loop_device_list.py
-rw-rw-r-- 1 student student 150 Aug 24 17:57 task03_while_loop_vlan_ids.py
Step 28
Print the contents of the file task03_while_loop_device_list.py using the cat command. The script
will help you understand how to use while loops.
Answer
(lab_02)student@student-vm:~/labs/lab02/task03$ cat task03_while_loop_device_list.py
device_list = ["R-1", "R-2", "R-3"]
#Iterate over the list and print all the items using while-loop
num = 0
while num < len(device_list):
print(device_list[num])
num += 1
Step 29
Run the script using the python task03_while_loop_device_list.py command to see how it works.
Answer
(lab_02)student@student-vm:~/labs/lab02/task03$ python task03_while_loop_device_list.py
R-1
R-2
R-3
(lab_02)student@student-vm:~/labs/lab02/task03$
Step 30
Run the task03_while_loop_vlan_ids.py command. It should print five VLANs.
Note
The script will return an empty list.
Answer
student@student-vm:~/labs/lab02/task03$ python task03_while_loop_vlan_ids.py
[]
Step 31
Examine the contents of the task03_while_loop_vlan_ids.py script, in the Visual Studio Code text
editor using the code task03_while_loop_vlan_ids.py command. The purpose of the script is to
add five VLANs to vlan_ids, and then print the contents. Looking at the while loop statement,
identify the problem, and fix it. Choose File > Save to save your changes. Rerun the script. The
properly working script should print five VLANs.
Note
To execute the code block under the while loop, the provided statement should evaluate to True.
Answer
After editing, the file should contain the following code:
student@student-vm:~/labs/lab02/task03$ cat task03_while_loop_vlan_ids.py
vlan_ids = []
print(device_ips)
for ip in device_ips:
print("\nAttempting to establish_connection with {}".format(ip[0]))
attempt_count = 0
while attempt_count < 5:
print("Establishing connection...")
attempt_count += 1
print("Connection Established!")
ip[1]="connected"
print(device ips)
(lab_02)student@student-vm:~/labs/lab02/task03$ python task03_for_and_while_loop.py
[['10.254.0.1', 'not connected'], ['10.254.0.2', 'not connected'], ['10.254.0.3', 'not connected']]
# Iterate over the dictionary and print all models that reached the end-of-life
# and respective dates
Network Libraries
A software library is a collection of a prewritten code to assist developers during their software
creation process. From Python’s perspective, here is a breakdown of the available prewritten code.
A module is a single file that contains Python objects that can be reused in scripts.
A package is a collection of modules in one or more directories to streamline and modularize the
development of the package.
The term “library” is often used to refer to a collection of modules or packages. The standard
modules that come with an installation of Python are often referred to as the “Python standard
library.”
However, Python is not limited to the standard library. Developers can download libraries from
open source, and public communities often found on GitHub or the Python Package Index (PyPI).
Finally, some libraries are developed in a company for a specific purpose. These libraries are
customized in-house libraries. For example, large clients may have in-house libraries and code to
provision different pods for requesting users. This code is customized, not public, and is usually
specific to a use case.
Modules and Packages
The difference between the Python module and the Python package was mentioned earlier. A
module is a single file with the .py extension that contains some usable code. A package is a
collection of modules stored in a folder where the name of the folder is the name of the package.
You might be wondering how you can differentiate a package from a module in the Python code
itself. When a module is imported, and a single file is referenced, you can make this distinction. For
example, import Netmiko or from Netmiko, import ConnectHandler. If you want to import a
module from a package, you must use “dotted module names.” For example, you could use
>>> import cobra.mit.access
or
>>> from cobra.mit.access import MoDirectory
where each name followed by a dot represents a folder, and the last entry in the chain represents a
file (module).
Use the PYTHONPATH
By default, you can be in any directory to use the Python standard library. You may write a script
for other software and custom modules. The script must be stored in one of two locations: the local
directory where the script is saved or within the PYTHONPATH environment variable. The
PYTHONPATH is a list of directories that the system will search for Python modules and packages
when doing imports in a script. It is quite common to update the PYTHONPATH in a login script
such as .profile.
You can check the PYTHONPATH on the Linux shell using the env command to see any custom
paths added. To see all available directories that Python will search, you can easily check from the
Python shell using the sys.path command:
>>> import sys
>>> print(sys.path)
['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload',
'/home/student/.local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/dist-packages',
'/usr/lib/python3/dist-packages']
Import Statements
Import statements help you perform many tasks.
The following describes the different import methods and what they do:
import netmiko: This method will import everything in the Netmiko module. Suppose that
Netmiko has a variable called VERSION that is defined in its file. To use that variable, you
will need to use the following format to refer to it: netmiko.VERSION. The same
requirement applies to the classes and functions. ConnectHandler is part of the Netmiko
module, and you can use it by following the same syntax: netmiko.ConnectHandler().
from netmiko import ConnectHandler: This method tells python to extract the object (in
this instance, the object is the ConnectHandler) from the module and make it directly
callable. Directly callable means that you do not need to refer to the module name and the
object. Instead, you can use the class name directly. The same approach applies to the
functions and variables.
from netmiko import ConnectHandler as ch: Some objects have long names, and to make
them shorter, you can create aliases of the original object. Now, instead of ConnectHandler,
you can use ch.
Free Open Source Software
Python software is available in many places. A growing community of network developers hosts
their code on GitHub, including Cisco DevNet, which has several free and open-source projects on
the DevNet Code Exchange, a curated collection of example code and projects.
pip is a command-line tool that helps you interact with PyPI and manage Python software. With it,
you can install software and any package that is available in PyPI.
Common pip Commands
There are several pip commands that you should know as you go on your network automation
journey:
pip search <PACKAGE_NAME>: This command searches for the package name in the
PyPI.
pip install <PACKAGE_NAME>: This command installs the latest version of the package
and its dependencies on the machine.
pip install <PACKAGE_NAME>==1.0.0: This command installs a specific version of the
package and its dependencies on the machine.
pip install ––upgrade <PACKAGE_NAME>: This command upgrades the package to the
latest version.
pip install –r <REQUIREMENTS_FILE>: This command installs all packages with
specified versions from <REQUIREMENTS_FILE>.
pip uninstall <PACKAGE_NAME>: This command uninstalls the package.
pip freeze: This command allows you to show and preserve currently installed packages
and their respective versions.
pip freeze –r <REQUIREMENTS_FILE>: This command allows you to show and
preserve currently installed packages and their respective versions using the order in the
given requirements file and its comments when generating output.
pip list: This command shows the installed packages and their versions.
pip list -o: This command lists all outdated packages.
pip install package-name
pip install package-name --upgrade
pip install –r requirements.txt
pip freeze
pip freeze > reqirements.txt
Although pip is a very useful program, it is not always available on the host machine, or you may
want the latest software with a bug fix that is not yet posted on PyPI. In this case, you have another
option available for installing the desired package. You must first use the git clone command to
obtain the software.
The process is as follows:
1. First, you need to find the source code of the package. Netmiko is used for this example.
Then you will copy it to your local machine.
2. Because the source code is stored on GitHub, you can clone it with the git command.
3. student@student-vm:~$ git clone https://github.com/ktbyers/netmiko.git netmiko
4. Cloning into 'netmiko'...
5. remote: Enumerating objects: 30, done.
6. remote: Counting objects: 100% (30/30), done.
7. remote: Compressing objects: 100% (23/23), done.
8. remote: Total 10779 (delta 12), reused 13 (delta 7), pack-reused 10749
9. Receiving objects: 100% (10779/10779), 3.13 MiB | 1.95 MiB/s, done.
10. Resolving deltas: 100% (7052/7052), done.
Checking connectivity... done.
The explanation of arguments that are supplied to git command are as follows:
1. clone: This argument clones a repository into a newly created directory.
2. https://github.com/ktbyers/netmiko.git: This argument a full path to the GitHub
repository.
3. netmiko: This argument is the folder name where the files should be cloned. If not
supplied, then the name of the repository is used.
11. After successful cloning, the netmiko folder contains following files and folders.
12. student@student-vm:~$ cd netmiko
13. student@student-vm:~/netmiko$ ls -al
14. drwxrwxrwx 1 student student 4096 Aug 15 17:44 ./
15. drwxrwxrwx 1 student student 4096 Aug 15 17:43 ../
16. drwxrwxrwx 1 student student 4096 Aug 15 17:44 build/
17. -rw-rw-rw- 1 student student 2957 Aug 15 17:43 COMMON_ISSUES.md
18. drwxrwxrwx 1 student student 4096 Aug 15 17:44 dist/
19. drwxrwxrwx 1 student student 4096 Aug 15 17:43 docs/
20. drwxrwxrwx 1 student student 4096 Aug 15 17:43 examples/
21. drwxrwxrwx 1 student student 4096 Aug 15 17:47 .git/
22. -rw-rw-rw- 1 student student 417 Aug 15 17:43 .gitignore
23. -rw-rw-rw- 1 student student 1078 Aug 15 17:43 LICENSE
24. -rw-rw-rw- 1 student student 55 Aug 15 17:43 MANIFEST.in
25. drwxrwxrwx 1 student student 4096 Aug 15 17:43 netmiko/
26. drwxrwxrwx 1 student student 4096 Aug 15 17:44 netmiko.egg-info/
27. -rw-rw-rw- 1 student student 1075 Aug 15 17:43 PLATFORMS.md
28. -rw-rw-rw- 1 student student 8712 Aug 15 17:43 README.md
29. -rw-rw-rw- 1 student student 711 Aug 15 17:43 release_process.txt
30. -rwxrwxrwx 1 student student 3641 Aug 15 17:43 _release.sh*
31. -rw-rw-rw- 1 student student 138 Aug 15 17:43 requirements-dev.txt
32. -rw-rw-rw- 1 student student 70 Aug 15 17:43 requirements-genie.txt
33. -rw-rw-rw- 1 student student 50 Aug 15 17:43 requirements.txt
34. -rw-rw-rw- 1 student student 230 Aug 15 17:43 setup.cfg
35. -rw-rw-rw- 1 student student 1953 Aug 15 17:43 setup.py
36. -rw-rw-rw- 1 student student 997 Aug 15 17:43 TESTING.md
37. drwxrwxrwx 1 student student 4096 Aug 15 17:43 tests/
38. -rw-rw-rw- 1 student student 472 Aug 15 17:43 tox.ini
39. -rw-rw-rw- 1 student student 976 Aug 15 17:43 travis_ci_process.txt
40. -rw-rw-rw- 1 student student 10256 Aug 15 17:43 travis_test_env.tar.enc
41. -rw-rw-rw- 1 student student 1704 Aug 15 17:43 travis_test.py
42. -rwxrwxrwx 1 student student 151 Aug 15 17:43 travis_test.sh*
43. -rw-rw-rw- 1 student student 237 Aug 15 17:43 .travis.yml
-rw-rw-rw- 1 student student 2300 Aug 15 17:43 VENDOR.md
The setup.py file allows you to do the following:
1. build: This option creates a symbolic link in the site-packages folder and you can
test the package as you modify it. It is used to add functionality and test without
installing it in a system path.
2. install: This option installs the package and the dependencies in the site-packages
folder.
Note
Please be aware that this is an active project and things may or may not look exactly the same.
44. When you want to install it, you need to use the install option:
45. student@student-vm:~/netmiko$ python setup.py install
46. running install
47. running bdist_egg
48. running egg_info
49. creating netmiko.egg-info
50. writing requirements to netmiko.egg-info/requires.txt
51. writing netmiko.egg-info/PKG-INFO
52. writing dependency_links to netmiko.egg-info/dependency_links.txt
53. writing top-level names to netmiko.egg-info/top_level.txt
54. writing manifest file 'netmiko.egg-info/SOURCES.txt'
55. reading manifest file 'netmiko.egg-info/SOURCES.txt'
56. reading manifest template 'MANIFEST.in'
57. writing manifest file 'netmiko.egg-info/SOURCES.txt'
58. installing library code to build/bdist.linux-x86_64/egg
59. running install_lib
60. running build_py
<... Output omitted ...>
61. After the installation is complete, you can start to use it. To test, run the Python interpreter
and import the Netmiko package. If it is present, you should not see any errors.
62. student@student-vm:~/netmiko$ python
63. Python 3.6.8 (default, Jan 14 2019, 11:02:34)
64. [GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux
65. Type "help", "copyright", "credits" or "license" for more information.
66. >>> import netmiko
>>>
It is important to note that modules that are installed from GitHub often have a default branch of
“development” that contains the latest code or functionality. This code or functionality might be in a
phase as early as the alpha or beta testing stage. The module that is installed from PyPI using
the pip install command is always the stable version.
Content Review Question
Correct
What is the correct syntax used to have pip install required modules listed in a file named
requirements.txt?
pip install -r requirements.txt <<<<<<<<<<<
pip freeze -r requirements.txt
pip freeze > requirements.txt
pip install requirements.txt