Python Training
Python Training
>>> RU = 4.45
>>> RU
4.45
>>> type(RU)
<type 'float'>
>>>
LAB 1 - INTEGERS
>>> hostname="ex4200-1"
>>> type(hostname)
<type 'str'>
>>> hostname
'ex4200-1'
>>> print hostname
ex4200-1
>>>
>>> byte="192"
>>> type(byte)
<type 'str'>
>>>
LAB 2 - STRINGS
>>> byte="192"
>>> type(byte)
<type 'str'>
>>> int(byte)
192
>>> type (int(byte))
<type 'int'>
>>>
STRINGS
• To get the list of available functions for strings,
use the built-in function dir with the argument str
(you can also use an instance of str as the
argument)
• Some functions for strings are: upper, lower, join,
split, splitlines, strip, …
>>> dir()
['__builtins__', '__doc__', '__name__', '__package__', 'a', 'b',
'banner', 'byte', 'hostname', 'ip']
>>> dir(str)
[ ... 'capitalize', 'center', 'count', 'decode', 'encode', 'endswith',
'expandtabs', 'find', 'format', 'index', 'isalnum', 'isalpha', 'isdigit',
'islower', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower',
'lstrip', 'partition', 'replace', 'rfind', 'rindex', 'rjust',
'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith',
'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']
STRINGS
upper(...)
S.upper() -> string
upper(...)
S.upper() -> string
• A collection of items
• Items are ordered
• Items separated by commas and enclosed within
square brackets ([])
• A list is iterable: a “for loop” iterates over its items
LAB 3 - LISTS
• Create a list
>>> my_devices_list=["172.30.108.11", "172.30.108.14", "172.30.108.141",
"172.30.108.133", "172.30.108.254"]
>>> my_devices_list
['172.30.108.11', '172.30.108.14', '172.30.108.141', '172.30.108.133',
'172.30.108.254']
>>> type (my_devices_list)
<type 'list'>
>>>
LAB 3 - LISTS
• Use [] to create an empty list
>>> this_is_an_empty_list=[]
>>> this_is_an_empty_list
[]
>>>
LAB 3 - LISTS
>>> list("whatever")
['w', 'h', 'a', 't', 'e', 'v', 'e', 'r']
• Replace an element
>>> my_devices_list
['172.30.108.11', '172.30.108.14', '172.30.108.141', '172.30.108.133',
'172.30.108.254']
>>> my_devices_list[2]='172.30.108.254'
>>> my_devices_list
['172.30.108.11', '172.30.108.14', '172.30.108.254', '172.30.108.133',
'172.30.108.254']
>>>
LISTS
• Remove an element
>>> my_devices_list
['172.30.108.11', '172.30.108.14', '172.30.108.254', '172.30.108.133',
'172.30.108.254']
>>> del my_devices_list[2]
>>> my_devices_list
['172.30.108.11', '172.30.108.14', '172.30.108.133', '172.30.108.254']
>>>
LAB 3 -LISTS
>>> dir(my_devices_list)
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__',
'__delslice__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__',
'__getitem__', '__getslice__', '__gt__', '__hash__', '__iadd__', '__imul__',
'__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__',
'__setattr__', '__setitem__', '__setslice__', '__sizeof__', '__str__',
'__subclasshook__', 'append', 'count', 'extend', 'index', 'insert', 'pop', 'remove',
'reverse', 'sort']
LAB 3 - LISTS
• Get help
>>> help(my_devices_list)
>>> my_devices_list.index("172.30.108.14")
1
>>> help (my_devices_list.index)
LISTS
>>> this_is_a_dictionary
{'domain': 'jnpr.net', 'hostname': 'LAB-EX-VC-Backbone', 'time_zone':
'Europe/Paris', 'name_server': '195.68.0.1'}
>>> this_is_a_dictionary["ntp_server"]="172.17.28.5"
>>> this_is_a_dictionary
{'ntp_server': '172.17.28.5', 'domain': 'jnpr.net', 'hostname': 'LAB-EX-VC-
Backbone', 'time_zone': 'Europe/Paris', 'name_server': '195.68.0.1'}
>>> this_is_a_dictionary["ntp_server"]
'172.17.28.5'
>>>
LAB 4 - DICTIONARIES
>>> this_is_a_dictionary
{'ntp_server': '172.17.28.5', 'domain': 'jnpr.net', 'hostname': 'LAB-EX-VC-
Backbone', 'time_zone': 'Europe/Paris', 'name_server': '195.68.0.1'}
>>> del this_is_a_dictionary["name_server"]
>>> this_is_a_dictionary
{'ntp_server': '172.17.28.5', 'hostname': 'LAB-EX-VC-Backbone', 'domain':
'jnpr.net', 'time_zone': 'Europe/Paris‘}
>>>
LAB 4 - DICTIONARIES
>>> my_devices_list
['172.30.108.11', '172.30.108.133', '172.30.108.133', '172.30.108.14',
'172.30.108.176', '172.30.108.254']
>>> for device in my_devices_list:
print ("the current device is: " + device)
ntp_server
hostname
domain
name_server
time_zone
>>>
LAB 5 - FOR LOOPS WITH A DICTIONARY
• If we use a for loop with a dictionary, it iterates over its keys
>>> this_is_a_dictionary
{'ntp_server': '172.17.28.5', 'hostname': 'LAB-EX-VC-Backbone',
'domain': 'jnpr.net', 'name_server': '195.68.0.1', 'time_zone':
'Europe/Paris'}
>>> for key in this_is_a_dictionary:
print this_is_a_dictionary[key]
172.17.28.5
LAB-EX-VC-Backbone
jnpr.net
195.68.0.1
Europe/Paris
>>>
LAB 5 - FOR LOOPS WITH A FILE
• If we use a for loop with a file, it iterates over its lines
pytraining@py-automation-master:~$ more "python_basics/list_of_ip.txt"
172.30.179.101
172.30.179.102
172.30.179.103
172.30.179.104
172.30.179.105
>>> f=open("python_basics/list_of_ip.txt")
>>> type(f)
<type 'file'>
>>> f
<open file 'python_basics/listofip.txt', mode 'r' at 0x000000000317A810>
>>> for line in f:
print line
172.30.179.101
172.30.179.102
172.30.179.103
172.30.179.104
172.30.179.105
>>> f.close()
LAB 5 - FOR LOOPS WITH A FILE
Execute the extract_hostname.py file and look at its contents
It search for the hostname of a device in a junos
configuration file with the format set
more python_basics/regex_hostname.py
# show_config.txt is a JUNOS conf file in set format
f=open("python_basics/show_config.txt")
for line in f:
if "host-name" in line:
hostname=line.split(" ")[-1].strip()
print hostname
f.close()
LAB 5 - FOR LOOPS WITH A FILE
These are the details about the previous program
>>> f=open("python_basics/show_config.txt") # this file is a junos configuration in set format
>>> # lets iterate the file line by line. Lets get the line that matches "host-name"
>>> for line in f:
if "host-name" in line:
hostname_line=line
>>> hostname_line
'set system host-name FR-EX2200-110\n'
>>> help(str.split)
>>> hostname_line_list = hostname_line.split(" ")
>>> hostname_line_list
['set', 'system', 'host-name', 'FR-EX2200-110\n']
>>> hostname_line_list[-1]
'FR-EX2200-110\n'
>>> help(str.strip)
>>> hostname=hostname_line_list[-1].strip()
>>> hostname
'FR-EX2200-110'
>>> print hostname
FR-EX2200-110
>>> f.close()
LAB 5 - FOR LOOPS WITH A STRING
• If we use a for loop with a string, it iterates over its characters.
w
h
a
t
e
v
e
r
>>>
WHILE LOOPS
• Syntax:
while (expression):
statement(s)
>>> my_devices_list
['172.30.108.11', '172.30.108.133', '172.30.108.133', '172.30.108.14', '172.30.108.176',
'172.30.108.254']
>>> '172.30.108.14' in my_devices_list
True
>>> if '172.30.108.14' in my_devices_list:
print "172.30.108.14 was found in my_devices_list"
else:
print "172.30.108.14 was not found in my_devices_list "
>>> this_is_a_dictionary
{'ntp_server': '172.17.28.5', 'hostname': 'LAB-EX-VC-Backbone', 'domain': 'jnpr.net',
'time_zone': 'Europe/Paris'}
>>> "domain" in this_is_a_dictionary
True
>>> if "domain" in this_is_a_dictionary :
print "domain is " + this_is_a_dictionary["domain"]
else:
print "domain was not found in my_devices_list "
domain is jnpr.net
PYTHON BUILDING BLOCKS
PYTHON BUILDING BLOCKS
• Module:
• A file with Python code. A python file.
• The file name is the module name with the suffix .py
appended (module.py).
• A module can define functions, classes, variables ...
• Package: several python modules all together in a
directory, accompanied with a file named __init__.py.
The file __init__.py can be empty.
• Function:
• A function returns a value. Call a function passing
arguments.
• There are many built-in functions. You can also create your
own functions.
• A function is defined once and can be called multiple times.
PYTHON BUILDING BLOCKS
• Class:
• Classes define objects.
• Call a class passing arguments. The returned value is an
object. So each instance of a class is an object.
• A class defines functions available for this object (in a class,
these functions are called methods)
• Method:
• A method is a function defined in a class.
• To call a method, we first need to create an instance of the
class. Once you have an instance of a class, you can call a
method for this object.
MODULE __builtin__
• Python has a number of functions built into it that are
always available.
• The module __builtins__ contains built-in functions
which are automatically available. You don’t have to
import this module. You don’t have to import these
built-in functions.
MODULE __builtin__
• Some classes of the module __builtin__
• Str (methods upper, lower, join, split, splitlines, strip, …)
• Int
• List (methods append, count, index, insert, pop, remove,
sort …)
• File (methods: open, close, read, readline, readlines, write,
…)
• Some functions of the module __builtin__
• bin
• hex
• dir
• range
• open
• raw_input
MODULES FOR NETWORK ENGINEERS
• Python allows you to import modules to reuse code.
• Good programmers write good code; great programmers
reuse/steal code
• Importing a module is done without using the .py extension
• Anyone can create modules for private uses or to share
with community
• Some very nice Python modules for network engineers:
• netaddr: a Python library for representing and manipulating
network addresses
• re: regular expressions
• requests: rest api manipulation
• jinja2: generate documents based on templates
• Yaml: “users to programs” communication (to define variables)
• PyEZ: Python library to interact with Junos devices
HOW PYTHON FINDS ITS MODULES
• Python looks for its modules and packages in $PYTHONPATH.
• To find out what is included in $PYTHONPATH, run the following code in python
>>> import sys
>>> type(sys)
<type 'module'>
>>> type(sys.path)
<type 'list'>
>>> from pprint import pprint
>>> pprint (sys.path)
['',
'/usr/lib/python2.7',
'/usr/lib/python2.7/plat-x86_64-linux-gnu',
'/usr/lib/python2.7/lib-tk',
'/usr/lib/python2.7/lib-old',
'/usr/lib/python2.7/lib-dynload',
'/usr/local/lib/python2.7/dist-packages',
'/usr/lib/python2.7/dist-packages']
>>> sys.path[6]
'/usr/local/lib/python2.7/dist-packages'
• Then you can easily play with the created objects using
methods and properties.
THE CLASS IPADDRESS
• Use dir() to check if the class IPAddress and its instance the
variable ip are the current scope
• Use dir (IPAddress) to list the available methods and properties
with this class.
• Use help (IPAddress) to get help with this class.
• To get help about a method or property of this class, use help
(IPAddress.method) or help (IPAddress.property).
>>> dir()
>>> dir(IPAddress)
>>> help(IPAddress)
• Change the type to string. Use the class str from the
module __builtin__
>>> ip
IPAddress('192.0.2.1')
>>> my_ip_in_string=str(ip)
>>> my_ip_in_string
'192.0.2.1'
>>> type (my_ip_in_string)
<type 'str'>
LAB 7 - THE CLASS IPNEWORK
• The class IPNetwork is define in the package
netaddr.ip
• Each instance of the class IPNetwork is an object (a
subnet)
• Once you have created an instance of the class
IPNetwork, you can use the methods defined in the
class IPNetwork with this subnet.
• The method “next” returns the adjacent subnet succeeding
the `IPNetwork` object.
• the method “previous” returns the adjacent subnet
preceding the `IPNetwork` object.
LAB 7 - THE CLASS IPNEWORK
• Import the class IPNetwork
>>> from netaddr import IPNetwork
>>> dir(IPNetwork)
>>> help(IPNetwork)
192.0.2.1 is in 192.0.2.0/24
LAB 7 - MANIPULATE IP ADDRESSES
• Generates the IP addresses for a subnet
192.0.2.0
192.0.2.1
192.0.2.2
192.0.2.3
192.0.2.4
192.0.2.5
192.0.2.6
192.0.2.7
LAB 7 - MANIPULATE IP ADDRESSES
• Generates the IP addresses that can be assigned to
hosts within a subnet. The method iter_hosts provides
all the IP addresses that can be assigned to hosts
within a subnet: for IPv4, the network and broadcast
addresses are always excluded.
>>> from netaddr import IPNetwork
>>> net=IPNetwork('192.0.2.0/29')
>>> help(net.iter_hosts)
>>> for ip in net.iter_hosts():
print ip
192.0.2.1
192.0.2.2
192.0.2.3
192.0.2.4
192.0.2.5
192.0.2.6
MANIPULATE FILES
WITH PYTHON
LAB 8 - OPEN A FILE
>>> f.name
'python_basics/list_of_ip.txt'
>>> f.closed
False
>>> f.mode
'r'
LAB 8 - CLOSE A FILE
>>> f.close()
>>> f
<closed file 'python_basics/list_of_ip.txt', mode 'r' at 0x000000000317AD20>
>>> f.closed
True
LAB 8 - GET THE AVAILABLE METHODS
Use the dir builtin function with the argument file to get
the list of available methods for files (f is an instance of
the class file. So you can also use f as the argument)
• Some methods are: close, write, read, readline, seek, ….
>>> dir(file)
LAB 8 - GET HELP
Use help(f) to get help on the object f. You can also use
help(file) to get help with the class file.
>>> help(file)
>>> help(f)
>>> f=open("python_basics/list_of_ip.txt","r")
>>> f
<open file 'python_basics/list_of_ip.txt', mode 'r' at 0x000000000317AE40>
>>> help(f.read)
>>> s=f.read()
>>> type(s)
<type 'str'>
>>> s
'172.30.179.101\n172.30.179.102\n172.30.179.103\n172.30.179.104\n172.30.179.105\n'
>>> print s
172.30.179.101
172.30.179.102
172.30.179.103
172.30.179.104
172.30.179.105
>>> f.close()
LAB 8 - WRITE CONTENT ON A FILE
To open a file with write mode, use "w".
• If the file doesn’t exist, python will create it.
• If the file already exists, python will overwrite its content.
To open a file with append mode, use "a".
• The file pointer is at the end of the file if the file exists. That is,
the file is in the append mode. You can write content without
overwriting the file content.
• If the file does not exist, it creates a new file for writing.
>>> f=open("python_basics/list_of_ip.txt","a")
>>> f
<open file 'python_basics/list_of_ip.txt', mode 'a' at 0x000000000317AD20>
>>> help(f.write)
>>> f.write("172.30.179.106\n")
>>> f.write("172.30.179.107\n")
>>> f.close()
LAB 8 - TRANSFORM IT INTO A LIST
The class str defines the method splitlines.
This method returns a list of strings.
Each line of the string is an item of the list.
>>> f=open("list_of_ip.txt","r")
>>> s=f.read()
>>> s
'172.30.179.101\n172.30.179.102\n172.30.179.103\n172.30.179.104\n172.30.179.105\n172.30.1
79.106\n172.30.179.107\n'
>>> help(s.splitlines)
>>> list_ip=s.splitlines()
>>> type(list_ip)
<type 'list'>
>>> list_ip
['172.30.179.101', '172.30.179.102', '172.30.179.103', '172.30.179.104', '172.30.179.105'
'172.30.179.106', '172.30.179.107']
>>> list_ip[1]
'172.30.179.102'
>>> f.close()
LAB 8 - POINTER POSITION
When you read data from a file with methods read, readline,
readlines, … this moves the position of pointer within the file.
You can use the method seek to move the position of the
read/write pointer within the file. This method doesn't return any
value. Seek(0) places the file pointer at the beginning of the file.
Use help (file.seek) to get help about this method of the class file.
>>> f=open("python_basics/list_of_ip.txt","r") # the file pointer is at the start of the
file.
>>> s=f.read() # the method read moved the file pointer to the end of the file.
>>> s
'172.30.179.101\n172.30.179.102\n172.30.179.103\n172.30.179.104\n172.30.179.105\n172.30.179.1
06\n172.30.179.107\n'
>>> s1=f.read() # s1 is an empty string (the file pointer was at the end of the file)
>>> s1
'‘
>>> help(f.seek)
>>> f.seek(0)
>>> s1=f.read()
>>> s1
'172.30.179.101\n172.30.179.102\n172.30.179.103\n172.30.179.104\n172.30.179.105\n172.30.179.1
06\n172.30.179.107\n'
LAB 8 - POINTER POSITION
The method tell returns the current position of the file
read/write pointer within the file.
The method readline reads a line from a file and returns a
string. It returns an empty string at EOF. it moves the
position of pointer within the file to the next line.
>>> f=open("python_basics/list_of_ip.txt","r")
>>> help(f.tell)
>>> f.tell()
0
>>> help(f.readline)
>>> f.readline()
'172.30.179.101\n'
>>> f.tell()
15
>>> f.readline()
'172.30.179.102\n'
>>> f.tell()
30
LAB 8 - TRANSFORM A FILE INTO A LIST
>>> f.closed
True
>>>
USE TEMPLATES WITH PYTHON
JINJA2 PACKAGE
>>> from jinja2 import Template # import the class Template from module jinja2.environment
>>> f=open("jinja2_basics/template_int_vlan.j2") # template_int_vlan.j2 is a jinja2 file
>>> s=f.read() # s is a string with the content of the file template_interf_and_vlan.j2
>>> type(s)
<type 'str'>
>>> print s
set interfaces {{ interface }} unit 0 family ethernet-switching port-mode access vlan
members {{ vlan_name }}
>>> template=Template(s) # template is an instance of the class Template.
>>> print template.render(interface="ge-0/0/2", vlan_name="v14")
set interfaces ge-0/0/2 unit 0 family ethernet-switching port-mode access vlan members v14
>>> print template.render(interface="ge-0/0/3", vlan_name="v14")
set interfaces ge-0/0/3 unit 0 family ethernet-switching port-mode access vlan members v14
>>>
>>> f.close() # close the file
LAB 9 - JINJA2
Lets use a for loop in the jinja2 template
more jinja2_basics/template_int_vlan_2.j2
{%- for interface in interfaces_list %}
set interfaces {{ interface }} unit 0 family ethernet-switching port-mode access vlan members {{
vlan_name }}
{%- endfor %}
>>> from jinja2 import Template # import the class Template from module jinja2.environment
>>> s=open("jinja2_basics/template_int_vlan_2.j2").read()
>>> print s
{%- for interface in interfaces_list %}
set interfaces {{ interface }} unit 0 family ethernet-switching port-mode access vlan members
{{ vlan_name }}
{%- endfor %}
>>> template=Template(s)
>>> print template.render(interfaces_list=["ge-0/0/4", "ge-0/0/5", "ge-0/0/6"], vlan_name="v14")
set interfaces ge-0/0/4 unit 0 family ethernet-switching port-mode access vlan members v14
set interfaces ge-0/0/5 unit 0 family ethernet-switching port-mode access vlan members v14
set interfaces ge-0/0/6 unit 0 family ethernet-switching port-mode access vlan members v14
>>>
DEFINE PYTHON LISTS AND
DICTIONARIES USING YAML FILES
YAML
YAML stands for "Yaml Ain't Markup Language"
Yaml is human-readable language.
• Less markup than XML.
• A superset of JSON.
Used for “users to programs” communication
• For users to read/change data.
• Used to communicate with program.
• Designed to translate to structures which are common to
various languages (cross language: Python, Perl, Ruby, etc).
• Used to define variables value.
YAML SYNTAX
Yaml files use a .yaml or .yml extension
Yaml documents begin with three dashes ---
Comments begin with #
Strings are unquoted
Indentation with one or more spaces
• never with tabulations
Lists: one member per line.
• Hyphen + space for each item.
Keys are separated from values by a colon + space.
YAML SYNTAX FOR A LIST
device_list.yml is a yaml file.
• This is a YAML list
• There is one item per line
• Hyphen + space for each new item
more yaml_basics/device_list.yml
---
#IPs of the devices in the lab environment
- 172.30.179.101
- 172.30.179.102
- 172.30.179.103
- 172.30.179.104
- 172.30.179.105
TRANSFORM A YAML FILE INTO A
PYTHON STRUCTURE
Open a yaml file
>>> f=open('yaml_basics/device_list.yml')
>>> f
<open file 'yaml_basics/device_list.yml', mode 'r' at 0x00000000044468A0>
>>> type (f)
<type 'file'>
vlan_name: v14
CREATE A PYTHON DICTIONNARY WITH YAML
>>> import yaml
>>> s=open('this_is_a_dictionary.yml').read()
>>> print s
---
interfaces:
- ge-0/0/9
- ge-0/0/10
- ge-0/0/16
- ge-0/0/18
vlan_name: v14
>>> my_vars=yaml.load (s)
>>> type(my_vars)
<type 'dict'>
>>> from pprint import pprint
>>> pprint (my_vars)
{'interfaces': ['ge-0/0/9', 'ge-0/0/10', 'ge-0/0/16', 'ge-0/0/18'],
'vlan_name': 'v14'}
>>> my_vars['interfaces']
['ge-0/0/9', 'ge-0/0/10', 'ge-0/0/16', 'ge-0/0/18']
>>> my_vars['vlan_name']
'v14'
CREATE JUNOS CONFIGURATION FILES
WITH JINJA2 AND YAML
JINJA2 AND YAML
Lets use a jinja2 template and a yaml file to build the initial junos
configuration files we can use with a ZTP setup to configure new
devices (build phase).
We need to provide to each new device (factory default configuration)
at least the following:
• -a root password (otherwise we can not commit the conf).
• -a management ip @ and subnet, and a route (to be able to reach remotely the new
device).
• -allow ssh connection (in case we want to ssh it).
• -enable netconf over ssh (to be able then to use PyEZ in the run and audit phases).
• -an hostname.
Only the hostname and the management ip @ are unique per device.
• So only these 2 details are define as variables in the jinja2 template.
• The yaml file define their values for each device.
LAB 10 - JINJA2 AND YAML
configuration_builder/variables_build.yml is a yaml file.
• This is a yaml list. With 3 items. Each item is a device.
• Each item of the list is a dictionary with the device hostname and
management ip @.
• It is extremely easy to add other devices.
• You can use another yaml structure (i.e instead of a list of dictionaries) but
in that case you’ll need to parse it differently from the jinja2 and python
files.
pytraining@py-automation-master:~$ more configuration_builder/variables_build.yml
LAB 10 - JINJA2 AND YAML
configuration_builder/template_build.j2 is a jinja2 template.
• this is the template to build the initial junos configuration file.
• It uses the variables defined in the yaml file.
pytraining@py-automation-master:~$ more configuration_builder/template_build.j2
LAB 10 - JINJA2 AND YAML
configuration_builder/configuration_builder.py is a python script.
• It uses the jinja2 template and the yaml file to create the initial junos
configuration file for each device defined in the yaml file.
• You can use these files with a ZTP setup to configure automatically new devices
(build phase).
pytraining@py-automation-master:~$ more configuration_builder/configuration_builder.py
LAB 10 - JINJA2 AND YAML
Use the python to generate the junos configuration file.
pytraining@py-automation-master:~$ python configuration_builder/configuration_builder.py
Start configuration building
generate config file for device ex4300-4 : conf_file_build_phase_ex4300-4.conf
generate config file for device ex4300-9 : conf_file_build_phase_ex4300-9.conf
generate config file for device ex4300-10 : conf_file_build_phase_ex4300-10.conf
done
call the dir function without argument to get the list of the
names defined in the current scope. The class Config is
now in the current scope.
>>>dir()
METHODS DEFINED IN THE CLASS CONFIG
>>> # confjunos.conf is a file with junos commands with the format set that define vlan 911
>>> cfg.load(path="configuration_management/confjunos.conf", format='set')
<Element load-configuration-results at 0x7f77c84317a0>
COMPARE CONFIGURATIONS
Compare the candidate configuration and the active
configuration (or a provided rollback) with the method
pdiff. Examples:
>>> cfg.pdiff()
[edit interfaces]
+ ge-0/0/23 {
+ description PyEZ;
+ }
[edit vlans]
+ vlan-911 {
+ description "created with python";
+ vlan-id 911;
+ }
+ vlan-927 {
+ description "created with python";
+ vlan-id 927;
+ }
>>> cfg.pdiff(rb_id=1)
ROLLBACK THE CANDIDATE CONFIGURATION
vlan:
name: v14
vlan_id: 14
MERGE A YAML FILE AND A JINJA2 FILE
View the jinja2 file (template_int_vlan.j2).
It has the junos template
We can use {{vlan.name }} or {{ vlan['name'] }}. You can use a dot (.) in
addition to the standard Python syntax ([]): Both are valid and do the same
thing
pytraining@py-automation-master:~$ more configuration_management/template_int_vlan.j2
set vlans {{ vlan['name'] }} vlan-id {{ vlan['vlan_id'] }}
{%- for iface in host_ports %}
set interfaces {{ iface }} unit 0 family ethernet-switching port-mode access vlan
members {{ vlan['name'] }}
{%- endfor %}
MERGE A YAML FILE AND A JINJA2 FILE
This is the Python program
(configuration_management/conf_int_with_vlan.py).
from jnpr.junos import Device
from jnpr.junos.utils.config import Config
import yaml
ip=raw_input("ip address of the device:")
a_device=Device (host=ip, user="pytraining", password="Poclab123")
a_device.open()
# cfg is the candidate configuration for a_device
cfg = Config(a_device)
# rollback any pending or uncommitted change
cfg.rollback()
# s is a string with the content of the file configuration_management/list_int_vlan.yml
s=open('configuration_management/list_int_vlan.yml').read()
# yaml.load transforms a string into a structure that python can use myvars is a python dictionary
myvars=yaml.load(s)
# cfg.load merges the junos template (Jinja2) and the variables dictionary (YAML) and updates the
candidate configuration
cfg.load(template_path='configuration_management/template_int_vlan.j2', template_vars=myvars,
format='set')
cfg.pdiff()
#cfg.commit()
cfg.rollback()
LAB 14 –USE PYEZ AND JINJA2 AND YAML
TO ENABLE LLDP
Use PyEZ with a .j2 file and a .yml file to enable lldp
on a list of several interfaces.
If you need help, please have a look at the next slides.
LAB 14 – JINJA2 AND YAML – ENABLE LLDP
ge-0//0/0
192.168.0.4/31 ge-0//0/1
192.168.0.1/31
ge-0//0/1
192.168.0.3/31 ge-0//0/0
192.168.0.0/31
ge-0//0/1 EX-4300-4
192.168.0.2/31
ASN 104
DEVICE DETAILS
• login: pytraining-N, password: Poclab123
Device Address
ex4300-4 172.30.179.65
ex4300-9 172.30.179.95
ex4300-10 172.30.179.96
DEMO JINJA2 AND YAML
configuration_builder/variables.yml is a yaml file.
• It defines the variables used in a jinja2 template.
• This is a yaml list of 3 devices.
• Each item of the list is a dictionary with some details for a device.
• Very easy to add other devices
• You can use another structure (i.e instead of a list of dictionaries) but in that case
you’ll need to parse it differently from the jinja2 file.
Device Address
ex4300-4 172.30.179.65
ex4300-9 172.30.179.95
ex4300-10 172.30.179.96
CLI, XML, RPC
Use the JUNOS command “show lldp neighbors | display xml rpc”
to know the equivalent RPC.
So the RPC to get the lldp neighbors with an XML representation is “get-
lldp-neighbors-information”
mgmt-13
ex4300-9
ex4300-10
TABLES AND VIEWS: DEMO WITH LLDP
more tables_and_views/lldp_neighbor_status.py
from jnpr.junos import Device
from jnpr.junos.op.lldp import LLDPNeighborTable
import yaml
my_list_of_devices=open('tables_and_views/devices.yml').read()
my_list_of_switches=yaml.load (my_list_of_devices)
BGPneighborView:
fields:
neighbor: peer-address
state: peer-state
type: peer-type
flap_count: flap-count
LAB 16 – TABLES AND VIEWS WITH BGP
For each device in a device list, this program prints:
• The list of its BGP neighbors
• The status of its BGP connections
python tables_and_views/bgp_states.py
my_list_of_devices=open('tables_and_views/devices.yml').read()
my_list_of_switches=yaml.load (my_list_of_devices)
172.30.179.65 (hostname ex4300-4) has these BGP neighbors with a non established connection:
172.30.179.95 (hostname ex4300-9) has these BGP neighbors with a non established connection:
172.30.179.96 (hostname ex4300-10) has these BGP neighbors with a non established connection:
test done accross all the devices and all their configured neighbors.
LAB 17 - TABLES AND VIEWS WITH BGP
more tables_and_views/bgp_non_established.py
from jnpr.junos import Device
from jnpr.junos.op.bgp import *
import yaml
from datetime import datetime
my_list_of_devices=open('tables_and_views/devices.yml').read()
my_list_of_switches=yaml.load (my_list_of_devices)
python tables_and_views/search_an_lldp_neighbor.py
more tables_and_views/search_an_lldp_neighbor.py
python tables_and_views/lldp_neighbor_status.py
more tables_and_views/lldp_neighbor_status.py
python tables_and_views/bgp_states.py
more tables_and_views/bgp_states.py
python tables_and_views/bgp_non_established.py
more tables_and_views/bgp_non_established.py
SOFTWARE UPGRADE MANAGEMENT
WITH PYEZ
SOFTWARE UPGRADE MANAGEMENT
more exceptions/exceptions_handling2.py
# mydeviceslist is a list of devices. for each device, PyEZ will connect to the device
and print some facts. if a device is not reachable, PyEZ will print something and
continue the "for" loop.
from jnpr.junos import Device
from jnpr.junos.exception import *
mydeviceslist=["172.30.179.101", "172.30.179.102", "172.30.205.102", "172.30.179.104"]
for item in mydeviceslist:
dev=Device(host=item, user="pytraining", password="Poclab123")
try:
dev.open()
except ConnectTimeoutError:
print("failed to connect to " + item)
continue
print ("the device "+ item + " runs " + dev.facts["version"])
dev.close()
REST CALLS
WITH PYTHON REQUESTS PACKAGE
REST APIs
Many systems have REST APIs : JUNOS, Junos Space, Cloud
Analytics Engine, Openclos, Contrail, Openstack, NSX …
You first need to have the REST API documentation for your
system.
Then you can use a graphical REST Client (browser add-on:
REST Easy, RESTClient, Postman) to start playing with REST
APIs and learn more about REST APIs.
• Graphical REST clients are for humans.
• If you need automation and programmatic access, you have to use a
command line REST client.
You can then use Python as a REST Client to handle REST
Calls. It is easy to parse the REST servers answers if they use
a json format (json format is a dictionary).
REST APIs ON JUNOS 15.1
JUNOS 15.1 supports REST API to submit RPCs
• You can only read the database
• The other 3 basic database operations (create-update-delete) are not
supported
• You can use HTTP get and post methods to submit RPCs to the REST
Server.
• You can retrieve data in XML or JSON
The documentation is here:
• https://www.juniper.net/documentation/en_US/junos15.1/information-products/pathway-
pages/rest-api/rest-api.pdf
REST configuration is under “system services” (default port is 3000)
REST Explorer is an optional tool (GUI) for testing
JUNOS CLI ouput with “| display json” is also available
LAB 18 - REST APIs ON JUNOS 15.1
You can copy from the REST explorer the “cURL request”, and paste
it to the server. cURL is a command line REST client.
pytraining@py-automation-master:~$ curl http://172.30.177.170:3000/rpc/get-software-
information -u "pytraining:Poclab123" -H "Content-Type: application/xml" -H "Accept:
application/json“
LAB 18 - REST CALLS TO JUNOS WITH PYTHON
Use the dir builtin function with the argument re to get the list of
available functions for regular expressions. Some methods are
search, findall, …
>>> dir (re)
>>> f=open("python_basics/show_config_vlans.txt")
>>> s=f.read()
>>> import re
>>> help(re.findall)
>>> vlans_list=re.findall("vlan-id .+", s)
>>> vlans_list
['vlan-id 3333', 'vlan-id 1001', 'vlan-id 1002', 'vlan-id 1003', 'vlan-id 1004', 'vlan-id 1005',
'vlan-id 101', 'vlan-id 102', 'vlan-id 103', 'vlan-id 104', 'vlan-id 109', 'vlan-id 110', 'vlan-
id 111', 'vlan-id 1111', 'vlan-id 150', 'vlan-id 2001', 'vlan-id 2002', 'vlan-id 201', 'vlan-id
202', 'vlan-id 1898']
>>> vlans_list=re.findall("vlan-id (.+)", s)
>>> vlans_list
['3333', '1001', '1002', '1003', '1004', '1005', '101', '102', '103', '104', '109', '110', '111',
'1111', '150', '2001', '2002', '201', '202', '1898']
>>> f.close()
THANK YOU!
EASILY RESTORE PYTHON SESSION
if your python session is closed, you can use the python file
training_variables.py to reimport/restore easily all the variables, modules,
classes that you need for the labs.
pytraining@py-automation-master:~$ more training_variables.py
pytraining@py-automation-master:~$ python
Python 2.7.6 (default, Jun 22 2015, 17:58:13)
[GCC 4.8.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from training_variables import *
>>> dir()
['BGPNeighborTable', 'BGPneighborView', 'Config', 'Device', 'HTTPBasicAuth', 'IPAddress',
'IPNetwork', 'LLDPNeighborTable', 'LLDPNeighborView', 'Template', '__builtins__',
'__doc__', '__name__', '__package__', 'a', 'a_device', 'b', 'banner', 'byte', 'devices',
'domain', 'ex_hostname', 'hostname', 'ip', 'ip_address', 'loadyaml', 'my_devices_list',
'my_vlan_id_list', 'num', 'number', 'pprint', 'readline', 'requests', 'rlcompleter',
'splitext', 'this_is_a_dictionary', 'this_is_an_empty_list', 'yaml']
>>>
>>> devices["ex4200-13"]
'172.30.179.113'