CCIE DevNet Reference Guide v1.6
CCIE DevNet Reference Guide v1.6
Section Overview
Section 1.0: Setting up and Getting Started
Section 1.1: Terraform - Docker, ACI
Section 1.2: YANG, NSO
Section 2.1: REST APIs with Python, Click API
Section 2.2: Python NCCLIENT, YANG, NETCONF, RESTCONF
Section 2.3: Python Libraries Jinja2, Scrapli, Netmiko
Section 3.1: Git, Gitlab, CI/CD Pipelines
Section 3.2: Cisco APIC APIs
Section 3.3: Network Test Automation with PyATS
Section 3.4: Secret Management with Vault, Ansible
Section 3.5: NX-API, , GitLab Authentication, Secure REST APIs
Section 4.1: Zammad Python SDK
Section 4.2: YANG Model Driven Telemetry
Section 4.3: Docker, Docker Compose
What is Terraform
● Terraform is an infrastructure as code tool
● It lets you build, change, and version infrastructure, both on cloud and on-prem, safely
and efficiently
● Resources are defined in human-readable configuration files that can be versioned,
reused, and shared
● Can manage both
○ Low level resources like compute, storage and networking
○ High level resources like DNS entries, SaaS features etc.
Installation
● If you have downloaded the DevNet VMImage mentioned above, Terraform is already
installed.
● If you are using any other system, you can follow the below instructions to install
Terraform.
○ https://developer.hashicorp.com/terraform/downloads
Terraform workflow
● Write
○ Create a config file in HCL (Hashciorp Config Language) which defines the
infrastructure in a declarative way
● Plan
○ Terraform creates an execution plan describing the infrastructure it will create,
update, or destroy based on the existing infrastructure and your configuration.
● Apply
○ On approval, Terraform performs the proposed operations in the correct order,
respecting any resource dependencies
Resource Blocks
● Resources are the most important element in the Terraform language
● Each resource block describes one or more infrastructure objects, such as virtual
networks, compute instances, or higher-level components such as DNS records
Resource Syntax
● A resource block declares a resource of a given type ("docker_container") with a
given local name ("nginx_container")
● The local name can only be used to refer this resource from elsewhere in the same
module but has no significance outside
Providers
● Each resource type is implemented by a provider, which is a terraform plugin
● Providers are distributed separately from Terraform itself
● Terraform can automatically install most providers when initializing a working directory
● In order to manage resources, a Terraform module must specify which providers it
requires
● Terraform usually automatically determines which provider to use based on a resource
type's name
● By convention, resource type names start with their provider's preferred local name
● Every Terraform provider has its own documentation, describing its resource types and
their arguments
● Most publicly available providers are distributed on the Terraform Registry, which also
hosts their documentation
Unset
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
version = "~> 3.0.2"
}
}
}
provider "docker" {}
# initialize terraform
$ terraforrm init
Input Variables
● Lets us customize aspects of Terraform modules without altering the module's source
code
● This functionality allows you to share modules across different Terraform configurations
● If you're familiar with traditional programming languages, it can be useful to compare
Terraform modules to function definitions
○ Input variables are like function arguments
○ Output values are like function return values
○ Local values are like a function's temporary local variables
● Each input variable accepted by a module must be declared using a variable block
● The label after the variable keyword is a name for the variable, which must be unique
among all variables in the same module
● Using Cisco ACI provider, create an ACI tenant and an application profile
● Names of the tenant and the application profile should be taken from variables file set as
default values
Refer
● https://developer.cisco.com/docs/nso/guides/#!start/start
● https://developer.cisco.com/learning/tracks/get_started_with_nso
● https://developer.cisco.com/docs/nso/guides/#!basic-operations/setup
● Go to NSO installation folder and execute the following command to add NSO
executable to the PATH
● Alternately you can also add it to the end of bash profile so that NSO executables are
available on system boot up
○ cd nso
○ source ncsrc
● Now we are ready to start using NSO commands that start with its earlier name nsc
○ ncs-setup --help
● If we don't see a command not found error, we are good
Getting Started
● Change to nso installation dir and source ncsrc file to set nso commands in path
$ cd nso
$ source ncsrc
(main) expert@devnet-lab:~/nso$ cd
(main) expert@devnet-lab:~$ cd work
(main) expert@devnet-lab:~/work$ ls
my_nso my_nso_new
(main) expert@devnet-lab:~/work$ cd my_nso
(main) expert@devnet-lab:~/work/my_nso$
● In this directory create a simple network with a couple of devices using netsim by
choosing some ned available with the installation
(main) expert@devnet-lab:~/work/my_nso$ ls
logs ncs-cdb ncs.conf packages README.ncs README.netsim
scripts state
(main) expert@devnet-lab:~/work/my_nso$ ncs-netsim create-network
cisco-ios-cli-3.8 2 ios
DEVICE ios0 CREATED
DEVICE ios1 CREATED
(main) expert@devnet-lab:~/work/my_nso$ ls
logs ncs-cdb ncs.conf netsim packages README.ncs
README.netsim scripts state
(main) expert@devnet-lab:~/work/my_nso$ ls netsim/ios/
ios0 ios1
(main) expert@devnet-lab:~/work/my_nso$
● Make this as NSO runtime by setting nso in this dir by pointing to netsim directory as
inventory of devices
● Start the devices and then nso using the following commands
● At any point if you wish to connect to any device directly, you can do it as follows
Creating Services
● Service is a way of simplifying device configuration by automating service provisioning in
NSO
● To add a service, you need to create a service package, which will have some pre-built
files and directories
● We will use the NSO built in command ncs-make-package with the option
service-skeleton to create a blank service
● Open simple-service.yang
module simple-service {
namespace "http://com/example/simpleservice";
prefix simple-service;
import ietf-inet-types {
prefix inet;
}
import tailf-ncs {
prefix ncs;
}
list simple-service {
key name;
uses ncs:service-data;
ncs:servicepoint "simple-service";
leaf name {
type string;
}
leaf secret {
type string;
tailf:info "Enable secret for this device";
}
(main) expert@devnet-lab:~/work/my_nso/packages/simple-service$ ls
templates/
simple-service-template.xml
(main) expert@devnet-lab:~/work/my_nso/packages/simple-service$
nano templates/simple-service-template.xml
<config-template xmlns="http://tail-f.com/ns/config/1.0"
servicepoint="simple-service">
<devices xmlns="http://tail-f.com/ns/ncs">
<device>
<!--
Select the devices from some data structure in the
service
model. In this skeleton the devices are specified in a
leaf-list.
Select all devices in that leaf-list:
-->
<name>{/device}</name>
<config>
<!--
Add device-specific parameters here.
In this skeleton the service has a leaf "dummy"; use that
to set something on the device e.g.:
<ip-address-on-device>{/dummy}</ip-address-on-device>
-->
</config>
</device>
</devices>
</config-template>
<config-template xmlns="http://tail-f.com/ns/config/1.0"
servicepoint="simple-service">
<devices xmlns="http://tail-f.com/ns/ncs">
<device>
<name>{./device}</name>
<config>
<enable xmlns="urn:ios">
<password>
<secret>{./secret}</secret>
</password>
</enable>
</config>
</device>
</devices>
</config-template>
● After we change yang model, we have to invoke the command make to check for any
errors and compile it
● For instance we made some mistake in yang file which is shown like below
(main) expert@devnet-lab:~/work/my_nso/packages/simple-service$
make -C src
make: Entering directory
'/home/expert/work/my_nso/packages/simple-service/src'
mkdir -p ../load-dir
/home/expert/nso/bin/ncsc `ls simple-service-ann.yang > /dev/null
2>&1 && echo "-a simple-service-ann.yang"` \
--fail-on-warnings \
\
-c -o ../load-dir/simple-service.fxs yang/simple-service.yang
yang/simple-service.yang:34: error: premature end of file
make: *** [Makefile:26: ../load-dir/simple-service.fxs] Error 1
make: Leaving directory
'/home/expert/work/my_nso/packages/simple-service/src'
● We can also validate yang file correctness using python module for yang called pyang
● We will use to display yang file in tree format to better understand the hierarchy and
relations
(main) expert@devnet-lab:~/work/my_nso/packages/simple-service$
pyang -f tree --tree-depth=2 src/yang/simple-service.yang
module: simple-service
+--rw simple-service* [name]
+---x check-sync
| ...
+---x deep-check-sync
| ...
+---x re-deploy
| ...
+---x reactive-re-deploy
| ...
+---x touch
| ...
+--ro modified
| ...
+--ro directly-modified
| ...
+---x get-modifications
| ...
+---x un-deploy
| ...
+--ro used-by-customer-service* ->
/ncs:services/customer-service/object-id
+--ro commit-queue
| ...
+--rw private
| ...
+--ro plan-location? instance-identifier
+--ro log
| ...
+--rw name string
+--rw device* -> /ncs:devices/device/name
+--rw secret? string
● We have now defined our service using yang data model and xml config template as a
package
● We now need to connect to nso cli and reload the packages for the changes to take
effect
(main) expert@devnet-lab:~/work/my_nso/packages/simple-service$
ncs_cli -u admin -C
admin@ncs(config)# si
Possible completions:
side-effect-queue simple-service
admin@ncs(config)# simple-service ?
% No entries found
Possible completions:
<name:string>
admin@ncs(config)# simple-service
● Use the below command to set the password on the device ios0 to mypasswd
● Verify the config change by moving to parent prompt and using the command show
configuration
● Commit the config changes using the commit command
Modifying a service
● Once we have a service in place, making config modifications is very easy
● Just re invoke the service with the changed config
● For instance if we want to change the password, we can do it as below
admin@ncs(config)# simple-service test1 device ios0 secret
securepasswd
admin@ncs(config-simple-service-test1)# commit
Commit complete.
admin@ncs(config-simple-service-test1)# top
admin@ncs(config)# simple-service test1 get-modifications
cli {
local-node {
data
}
}
● It is showing that what has been changed is the actual password, which is nothing but
data
● We can now apply the changed configuration by redeploying the changed service
NETCONF
● NETCONF is a configuration management protocol that defines a mechanism through
which:
○ A network device can be managed
○ configuration data can be retrieved
○ new configuration data can be uploaded and manipulated
● NETCONF uses SSH for secure communication and data is encoded in XML
● This data should be consistent, standardized and as per certain agreed upon rules, if the
sender and receiver have to be on the same page
● So it can be modeled using YANG
YANG
list person {
key email;
leaf first-name {
tailf:info "Person's first name";
mandatory true;
type string;
}
leaf last-name {
tailf:info "Person's last name";
mandatory true;
type string;
}
leaf date-of-birth {
tailf:info "Date of birth as dd/mm/yyyy";
type string {
pattern "[0-9][0-9]/[0-9][0-9]/[0-9][0-9][0-9][0-9]";
}
}
leaf email {
tailf:info "Contact email";
mandatory true;
type string;
}
leaf job-role {
tailf:info "Persons job role within organization";
type enumeration {
enum admin;
enum developer;
enum manager;
}
}
}
● Data models form the foundation for model-driven APIs and network programmability
● They define the syntax, semantics, and constraints of the data interchanged
● They define data attributes and answer questions, such as the following:
○ What is the range of a valid VLAN ID?
○ Can a VLAN name have spaces in it?
○ Should the values be enumerated and only support "up" or "down" for an admin
state?
○ Should the value be a string or an integer?
● Yet Another Next Generation (YANG) has become a widely used data modeling
language in the networking industry
● Although it can describe any data model, it was originally designed for networking data
models
● It is used to create models of configuration and state data
● It has a structured format that is also human-readable
● The below example shows how YANG defines a model of a network interface
module ietf-interfaces {
import ietf-yang-types {
prefix yang;
}
container interfaces {
list interface {
key "name";
leaf name {
type string;
description
"The name of the interface";
}
leaf description {
type string;
description
"A textual description of the interface";
}
leaf type {
type identityref {
base interface-type;
}
mandatory true;
description
"The type of the interface";
}
leaf enabled {
type boolean;
default true;
description
"The configured state of the interface";
}
}
}
YANG Structure
Modules
● In YANG, all data modeling definitions reside inside YANG modules
● Modules organize related items and place them in groups
● Often a single module describes a complete part of a functionality, such as the Border
Gateway Protocol (BGP) or interface IP configuration
● Other times, a device, especially a simpler one, could implement everything in a single
module
● Module can be defines as shown below
Unset
module ip-access-list {
namespace "http://example.com/ns/yang/ip-access-list";
prefix acl;
// ...
}
Unset
organization
"Example, Inc.";
contact
"Example, Inc.
Customer Service
E-mail: [email protected]";
description
"Access Control List (ACL) YANG model.";
revision 2021-07-06 {
description
"Initial revision";
}
Unset
container acl {
description
Unset
container acl {
...
leaf acl-description {
type string {
length "0..64";
pattern "[0-9a-zA-Z]*";
}
● The leaf-list statement defines an array of values, which have the same, specific
type
Unset
container acl {
...
leaf acl-description {
...
leaf-list maintainers {
type string;
● The list node specifies a sequence of list entries and is similar to Python's dictionary
data structure
Enumeration
● Used to define a finite set of values allowed
○ true/ false
○ enabled/ disabled
○ ipv4/ ipv6
What is Program?
● A program is a set of instructions given to a computer to perform a specific operation
using some data
● When the program is executed, raw data is processed into a desired output format
● These programs are written in high-level programming languages which are close to
human languages
● They are then converted to machine understandable low level languages and
executed
What is Python?
● Python is an interpreted, object oriented, high-level, scripting language used for
general-purpose programming
● It has wide range of applications ranging from Web Development, Scientific and
Mathematical Computing to Desktop Graphical User Interfaces
● It was created by Guido van Rossum and first released in 1991
Why Python?
● Python interpreters are available for many operating systems including network
operating systems
● Cisco puts Python on many of its devices and releases many tools using Python
● This makes Python a great choice for network engineers looking to add programming
to their skillset
● For network and IT infrastructure engineers, there is a well-established online community
of infrastructure engineers using Python to deploy and maintain IT systems
Install Python
● If you are using Cisco CWS, Python is already installed and ready for use the moment
you connect to CWS as user expert
● If You want to set it up on any other VM or your laptop you can follow the steps below
● On Windows
○ Download the installer from https://www.python.org/downloads/
○ Invoke the installer executable and follow the steps in the installation wizard
● On Ubuntu
○ By default Ubuntu comes with Python older version that is python2
○ Now a days certain Ubuntu distributions come preinstalled with python3
○ Check which version is already installed using the below command
■ python -–version
○ If you get a message like command not found or you get the result showing
python version as 2.x, you can proceed further
○ If the result shows the version as 3.x, you are good to go, unless you want to
update to the latest version
○ To install latest version follow the link
https://www.makeuseof.com/install-python-ubuntu/
○ Or any other link of your choice
Getting started
● Check which version of python are you using
(main) expert@devnet-lab:~$ python -V
Python 3.9.10
(main) expert@devnet-lab:~$
Data Types
● All we are doing as part of our work is using and manipulating data
● Data comes in various types like:
○ Names of network devices: Group of alphabets - Strings (str)
○ Number of interfaces: Number (Integers) (int)
○ Information like whether certain interface is enabled or not: True or False
(Boolean) (bool)
○ OS version of certain router or switch: Number (Decimals or Floating point
numbers) (float)
○ List of spine and leaf switches: Collection of names (List)
○ A switch with all its attributes like: Name of the attribute mapped to its value
■ Make or manufacturer: Cisco/ Juniper etc.
■ Model: Cisco CSR 1000
■ OS Version: 16.9
■ Name: brch_rtr_01
■ And so on
● Looking at a piece of data it is easy for humans to understand its type, but for machines
it is not
● So we need to explicitly let the systems know what is the type of data we are dealing
with
● To do this every programming language uses some data types
● Python has the following basic data types
○ Text Type : Strings (str)
○ Numeric Types : Numbers (int, float, complex)
○ Sequence Types : Lists, Tuples, Set (list, tuple, set)
○ Mapping Type : Dictionaries (dict)
○ Boolean Type : bool
Variables
● Now that we are talking about different types of data, we need a way to store this data
during the program (or code as we call it) execution
● We use variables for this purpose
● Variables are named locations used to store data in memory
● Think of them like containers that holds data
● Data held by the variable can change later
● When we create a variable, we are telling the system to keep some storage aside
● How much storage to keep aside depends on the type of data that is going to be stored
in that variable
● Let us create few variables and display them on the console using an inbuilt utility or
function called print()
● By the way, we can use Python Interpreter CLI to give any python commands and
perform some basic operations
● In CLI mode you do not need to use print() function. Directly type the name of the
variable and the value stored in it will be displayed
● Use suggestive names for the variables based on what they are used to store
● We can check the type of data stored in a variable using another built in function called
type()
>>> type(model)
<class 'str'>
>>> type(os_version)
<class 'float'>
>>> type(num_intfcs)
<class 'int'>
>>>
Operators
● Data manipulation requires that we are able to perform certain operations on the data
● We will use operators for this purpose
● Accordingly every programming language, including python has certain operators
defined
● Depending on the types of operations they are classified as follows
○ Arithmetic Operators
○ Assignment Operators
○ Comparison Operators
○ Logical Operators
○ Special Operators
Arithmetic Operators
● Used to perform arithmetic operations on variables
+ Addition 5 + 2 = 7
- Subtraction 4 - 2 = 2
* Multiplication 2 * 3 = 6
/ Division 4 / 2 = 2
// Floor Division 10 // 3 = 3
% Modulo 5 % 2 = 1
** Power 4 ** 2 = 16
a = 7
b = 2
# addition
print ('Sum: ', a + b)
# subtraction
print ('Subtraction: ', a - b)
# multiplication
print ('Multiplication: ', a * b)
# division
print ('Division: ', a / b)
# floor division
print ('Floor Division: ', a // b)
# modulo
print ('Modulo: ', a % b)
# a to the power b
print ('Power: ', a ** b)
Assignment Operators
● Used to assign values to variables
= Assignment Operator a = 7
+= Addition Assignment a += 1 # a = a + 1
-= Subtraction Assignment a -= 3 # a = a - 3
*= Multiplication Assignment a *= 4 # a = a * 4
/= Division Assignment a /= 3 # a = a / 3
%= Remainder Assignment a %= 10 # a = a % 10
# assign 5 to b
b = 5
print(a)
# Output: 15
Comparison Operators
● Used to compare the value or variables
● Result of the comparison operators is always true or false (boolean)
a = 5
b = 2
# equal to operator
print('a == b =', a == b)
Logical Operators
● Used to check truthiness or falseness of expressions
and a and b Logical AND: True only if both the operands are True
# logical AND
print(True and True) # True
print(True and False) # False
# logical OR
print(True or False) # True
# logical NOT
print(not True) # False
Special Operators
● Used to verify identity or check membership
is True if the operands are identical (refer to the same object) x is True
is not True if the operands are not identical (do not refer to the same x is not
object) True
x = 'Hello world'
Strings
● Strings are sequence of characters enclosed by quotes
● Single or double quotes are accepted, but be consistent
● Create a variable called hostname and assign it the value of "ROUTER1"
● Strings are very useful in Python as most of the time we end up working with strings
● Some of the built in methods on strings are shown below
Methods Description
Lists
● Lists are sequence data type used to store multiple values or objects
● It's one of the most frequently used and versatile data type
● It is an ordered sequence, meaning elements of a list are indexed by integers starting at
0
● Similar to arrays in other programming languages
● But elements of a list can be of any data type
● An example is shown below
● List is an ordered collection, which means that the elements in the list are indexed
● We can retrieve elements of a list using an index
● Indexing in lists starts from 0
● Lists also support negative indexing which helps us to traverse the list in reverse
● Some useful methods on lists are shown below
Method Description
extend() add items of lists and other iterables to the end of the
list
Dictionaries
● Dictionaries are mapping data types where data is stored as combination of key, value
pairs
● We can think of them as unordered lists
● Unlike lists instead being indexed by a number, dictionaries are indexed by a name
known as key
● Also known with other names like hashes or associative arrays
● Let us create a simple dictionary to store the details of a network device
● But why do we need a new data type like a dictionary? Why can't we use one of the
existing ones like a list?
● Let us relook at a list which stores similar details
● Looking at the values stored in the list, we have to guess what they stand for
● Instead we can associate them with appropriate keys in the dictionary
Conditional Statements
● Decision making is a crucial component of programming for building logic
● We can alter the course the program using this decision making feature
● if...elif...else statements are used in Python for decision making
● Syntax is
if test expression:
statement(s)
● The statement(s) are executed only if the test expression is evaluated to True (Python
Boolean value)
if Statement
● As per the syntax seen above, the if statement should end with a colon ( : ) symbol
● The statements that follow must be indented (usually 4 by spaces)
● The combination of the if keyword and the statements forms a block
● TODO:
○ Check the os version of a device and add it to the list only if the version is equal
to a predefined value
if - else
● We have instructed the code what to do when the condition is satisfied
● We have seen that when the condition is not satisfied, the code does not do anything
● We will use if - else to rectify this
>>> command = ''
>>> if command:
... print('Command to send', command)
... else:
... print('No command to send')
...
No command to send
>>> #note that 0, empty string, None all evaluate to False
Loops
● In programing loops are used to repeat the execution of a block of code
● Two types of loops are used in Python
○ for loop
○ while loop
● For loop is used to iterate over a sequence or through a given set of objects
● While loop is used to iterate a block of code as long a test expression evaluates to True
● In that sense while is similar to if except that if stops execution after once whereas while
continues execution till the condition continues to be True
for Loop
● Used to iterate through a given set of objects
● Uses a user defined variable to hold each object temporarily
● Syntax
for item in sequence:
statement(s)
● TODO:
○ Create a list of routers and iterate through the list to print the name of each router
>>> vlan_count = 0
>>> while vlan_count < 10:
... print(f"Creating vlan_{vlan_count}")
... vlan_count += 1
...
Creating 0
Creating vlan_1
Creating vlan_2
Creating vlan_3
Creating vlan_4
Creating vlan_5
Creating vlan_6
Creating vlan_7
Creating vlan_8
Creating vlan_9
>>>
Functions
● Helps to break down large programs into small modular chunks
● Helps keep code organized and manageable
● More importantly helps us follow DRY (Don’t Repeat Yourself) principle
● We write function once and call it multiple times
● Syntax
def function_name(parameters):
"""docstring"""
statement(s)
● Functions are not new for us, as we have used quite a few of them already
A Few Built-in Functions
● print(), len()
● We can also chain the functions together
● len() returns the length which in turn can be passed to print for printing it to the console
Writing a Function
● Functions that we will create become user defined functions
● Follow the syntax shown above
● Starts with keyword def followed by a name to identify the function
● () for passing optional arguments followed by :
● Statements within the function will be indented uniformly
● Function definition ends with an optional return statement which returns some data to the
caller
● Defining a function will not execute the code in it, unless it is called elsewhere
Libraries
● Modules
○ Collection of functions and global variables stored as a file with .py extension
○ It is an executable file
● Package
○ Simple directory containing collection of modules and sub packages (sub
directories)
○ It has a __init__.py file which is used by Python interpreter to identify this
directory as Python package
● As we are going to see further, json format is quite similar to Python dictionaries
● One use of json module is to pretty print Python dictionaries
● json objects are quite similar to Python dictionaries
● We “dump” Python objects into json string using dumps() method
● This process is called serialization
● We can also deserialize data and load them into the program during the run time
● We will use loads() method for this
>>> json_str = '{"chipset": "t2", "hostname": "NYC301", "vendor":
"arista"}'
>>> json_obj= json.loads(json_str)
>>> json_obj
{'chipset': 't2', 'hostname': 'NYC301', 'vendor': 'arista'}
>>> type(json_obj)
<class 'dict'>
>>> json_obj['chipset']
't2'
>>>
$ cat conf.txt
en
conf t
hostname CR2
int g 0/1
ip address dhcp
no shut
● Open the file using the read() command and check the details
>>> conf_file = open('conf.txt')
>>> conf_file
<_io.TextIOWrapper name='conf.txt' mode='r' encoding='UTF-8'>
>>> dir(conf_file)
['_CHUNK_SIZE', '__class__', '__del__', '__delattr__', '__dict__',
'__dir__', '__doc__', '__enter__', '__eq__', '__exit__',
'__format__', '__ge__', '__getattribute__', '__gt__', '__hash__',
'__init__', '__init_subclass__', '__iter__', '__le__', '__lt__',
'__ne__', '__new__', '__next__', '__reduce__', '__reduce_ex__',
'__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', '_checkClosed', '_checkReadable',
'_checkSeekable', '_checkWritable', '_finalizing', 'buffer',
'close', 'closed', 'detach', 'encoding', 'errors', 'fileno',
'flush', 'isatty', 'line_buffering', 'mode', 'name', 'newlines',
'read', 'readable', 'readline', 'readlines', 'reconfigure', 'seek',
'seekable', 'tell', 'truncate', 'writable', 'write',
'write_through', 'writelines']
>>>
>>> print(conf_data)
en
conf t
hostname CR2
int g 0/1
ip address dhcp
no shut
>>> type(conf_data)
<class 'str'>
>>>
● Try readline() now
● We are getting blank line because we already read the data using read() method
● So the file pointer is at the bottom of the file now
● We can move the pointer back to the top or anywhere within the file using seek() method
>>> conf_file.seek(0)
0
>>> conf_line = conf_file.readline()
>>> print(conf_line)
en
>>>
>>> conf_file.seek(0)
0
>>> conf_lines = conf_file.readlines()
>>> print(conf_lines)
['en\n', 'conf t\n', 'hostname CR2\n', 'int g 0/1\n', 'ip address
dhcp\n', 'no shut\n']
>>> type(conf_lines)
<class 'list'>
>>>
● We can also write data to files using the write mode
● Open a file for writing using the "w" value instead "r" for reading
● Opened the file in write mode, wrote data and closed the file
● Closing the file is not mandatory but is recommended as a coding best practice
● Without using the context manager working with files opening them for read write
operations and forgetting to close them can leave them in inconsistent state
● The with statement ensures that files is closed as soon as we are done working with it
● We can read the data from the CSV file using a built in library called csv as follows
● The csv library also offers a better method to read csv data directly as Python
dictionaries as shown in the below example
● If the data is available as dictionaries, we can also use DictWriter method to write data to
CSV files
# Data to be written
data = [
{'Name': 'John Doe', 'Email': '[email protected]'},
{'Name': 'Jane Smith', 'Email': '[email protected]'},
{'Name': 'Bob Johnson', 'Email': '[email protected]'}
]
# Field names/columns
fieldnames = ['Name', 'Email']
{
"devices": [
{
"name": "Router1",
"ip": "192.168.1.1"
},
{
"name": "Switch1",
"ip": "192.168.1.2"
},
{
"name": "Firewall1",
"ip": "192.168.1.3"
}
]
}
● We can read the data from json files as shown below
import json
import json
# Add a new device
new_device = {
"name": "Switch2",
"ip": "192.168.1.4"
}
devices.append(new_device)
import yaml
import yaml
Netmiko
● Paramiko is a Python implementation of SSHv2 protocol
● Netmiko is a multi-vendor library to simplify Paramiko SSH connections to network
devices
● Netmiko simplifies:
○ Establishing ssh connection to various devices
○ Execution of show commands used to retrieve config data
○ Execution of configuration commands sent to the device
○ Working with a broad range of devices from different vendors
Getting Started
● Install netmiko using the following command
○ python -m pip install netmiko
● You would then use ConnectHandler to pick the class and establish the SSH connection
● ConnectHandler needs the following details to connect to the device
○ device_type
○ host (hostname or IP)
○ username
○ password
● We are now ready to start issuing commands to the device from our code
● Let us see which prompt we are currently at
>>> conn.find_prompt()
'brch-rtr-01#'
>>>
>>> cfg_list = [
...
... "ip access-list extended TEST1",
...
... "permit ip any host 1.1.1.1",
...
... "permit ip any host 1.1.1.2",
...
... "permit ip any host 1.1.1.3",
...
... "permit ip any host 1.1.1.4",
...
... "permit ip any host 1.1.1.5",
...
... ]
>>> cfg_list
['ip access-list extended TEST1', 'permit ip any host 1.1.1.1',
'permit ip any host 1.1.1.2', 'permit ip any host 1.1.1.3', 'permit
ip any host 1.1.1.4', 'permit ip any host 1.1.1.5']
>>> cfg_output = conn.send_config_set(cfg_list)
>>> print(cfg_output)
configure terminal
Enter configuration commands, one per line. End with CNTL/Z.
brch-rtr-01(config)#ip access-list extended TEST1
brch-rtr-01(config-ext-nacl)#permit ip any host 1.1.1.1
brch-rtr-01(config-ext-nacl)#permit ip any host 1.1.1.2
brch-rtr-01(config-ext-nacl)#permit ip any host 1.1.1.3
brch-rtr-01(config-ext-nacl)#permit ip any host 1.1.1.4
brch-rtr-01(config-ext-nacl)#permit ip any host 1.1.1.5
brch-rtr-01(config-ext-nacl)#end
brch-rtr-01#
>>> conn.save_config()
'write mem\n\nBuilding configuration...\n[OK]\nbrch-rtr-01#'
>>>