CloudVision Deployment Guide Using Python API
CloudVision Deployment Guide Using Python API
eos.arista.com/cloudvision-deployment-guide-using-python-api
Ryan
Contents [hide]
Audience
Purpose
Topology
CloudVision Web Portal – Network Provisioning(Logical View)
Pre-Requisites
Known Caveats:
First steps
Working with CVP variables
Working with CVP premade functions
Creating a Configlet
Creating a Container
Working with CVP Devices
Generating ECMP fabric(Tier2-Tier3)
Generating Bowtie MLAG(Tier1-Tier2)
Handling CVP related Errors
Sample prompts from CVP server
Further Reading
Appendix
Configlet Appearance in CloudVision Portal
CVP Python Script
Audience
This document is intended for those that are familiar with, or have intermediate
abilities with python scripting. This script is meant for deployment in a greenfield
environment, but can be modified to be used in a brownfield environment as well.
With either deployment scenario it is advisable to contact your SE/TAC for a code
review or advice prior to deployment.
Purpose
The purpose of this document is to build automated configlets to push configurations to a
massive ECMP(L3), MLAG(L2) infrastructure using the CVP Python API doc. As the scale
of switches increases, the script comes in handy by automating the entire configuration of
switches rather than manually configuring configlets or templates of configurations for every
switch in the traditional way.
1/11
The script below is meant to generate a spine leaf topology containing 3 tiers. Tier 1, rack
level MLAG, Tier 2 Pod Spine, Tier 3 Datacenter Spine connecting all the Tier 2 spines
together. Tiers 1 and 2 are connected in a Bowtie Mlag. The number of Tier 1 MLAG pairs
connected to the Tier 2 Pod-Spine or the number of pods(consisting of pod-spine & pod-
leaf) can scale out and the script can configure the configlets to be pushed to the switches
in an automated way. Every production deployment will have its own unique configuration
requirements and the script will have to be tailored according to those requirements.
Topology
The Tier3 (DC-spines) and Tier2 (Pod-spines) are connected via Layer 3 BGP ECMP.
The Tier2 (Pod-spines) and Tier1 (Pod-leafs) are connected via L2 MLAG with VARP.
2/11
Pre-Requisites
There are a few prerequisites that are required to be installed on the CVP server before
scripts can be run.
ipaddress-1.0.16.tar.gz (IP address module for python 2.7). Latest version can be
found on this link.
Enable eAPI on each switch and assign a username & password for CVP to
communicate with each of them.
Known Caveats:
On certain switches that have QSFP ports, on interfaces participating in the script
e#/1 the slash and 1 (Lane 1 ID) need to be added manually to each interface in the
configlets affected. The current script will create the appropriate configuration
without the /1.
First steps
For any script to work it needs to be able to login to the server to execute commands. The
following code, positioned at the start of the script will keep the necessary authentication
information to execute the script.
If you have configured ZTP on the CloudVision Portal(Greenfield deployment), switches will
be populated automatically. For switches that were not configured by ZTP(Brownfield
deployment), the best practise is to reconcile the current switch configurations before
configlets can be applied to them.
3/11
Working with CVP variables
There are several variables that you can use to work within the CVP Python-API
environment and they are as follows:
cvp.Image – This variable holds information of EOS image versions on the CVP
server and can be used to make EOS version upgrade possible using Python-API
cvp.Container – This variable is used to create containers within CVP, applying
container level configlets and images can be done with this variable.
cvp.Task – This variable generates a task that can be queued within the tasks Tile
within CVP or can be used to execute within the script.
cvp.Device – This variable is used to identify the switches within the topology. the
unique key for each device is the MAC address.
cvp.Configlet – This variable generates the configlets within CVP which then can be
used to be applied on devices .
cvp.ImageBundle – This variable holds the information of an Image bundle that
can be applied to devices.
Full list of variables can be found in the CloudVision Portal Python API
Documentation
For the following examples, the code below will use the following to make calling functions
easier.
Line 1 defines the server to login to, Line 2 compresses the server that the command will
be run on into a single variable. Line 3 is executing the function; server.authenticate()
function call can be also expanded out to cvp.Cvp(‘cvpdev’).authenticate() but the server
variable way makes calling cvp functions easier.
The functions reconcile(), deviceMove() were made using the available CVP variables and
functions as shown below:
The function reconcile aims to work with devices in the undefined container. The first line in
reconcile() gets a list of all devices (switches) that are in the undefined container. For every
switch in the undefined container we define a default hostname to it using the current ip
address, and the device is given a FQDN comprised of the hostname. This can be
changed to how the devices will be named in the environment. Next is a simple configlet to
assign comprised of the information that is required for the device to be managed within
4/11
CVP. The mgmtIP variable generates a string to be added in the configlet so the device
can be managed within the Portal. The mgmtApi variable is the string that allows CVP to
talk to the switches, and cvpUser is adding a user onto the device so that the device can be
authenticated and remotely managed. Reconcile is creating a cvp.Configlet variable with
blank attributes and reconcile.name assigns a name to the configlet, reconcile.config builds
the configlet that will appear in CVP using the host, mgmtIp, mgmtApi and cvpUser
variables. The check_for_configlet(reconcile) function checks to see if an error will occur if
the configlet will be added and asks the user for for further steps (not highlighted here).
def reconcile():
undefined = [d for d in server.getDevices() if d.containerId ==
'undefined_container']
for i in undefined:
hostname = 'Switch-%s' % (i.ipAddress)
host = 'hostname %s\n!\n' % hostname
netmask = 24
i.fqdn=hostname
mgmtIp = 'interface Management1\n ip address %s/%s\n!\n' % (i.ipAddress,
netmask)
mgmtApi = 'management api http-commands\n no shutdown\n!\n'
cvpUser = 'username cvpuser secret 5 <omitted>'
reconcile = cvp.Configlet('','','',[],[])
reconcile.name = 'BaseCfg-%s' % i.ipAddress
reconcile.config = host + mgmtIp + mgmtApi + cvpUser
check_for_configlet(reconcile)
deviceMove('Lab',reconcile.name,i)
The function deviceMove shows how one can manage device moves between containers.
The deviceMove() function moves the device from the undefined container to a container
we call will define as ‘Lab’. To move a device it requires a few items to generate the task to
move the device. server.deployDevice(device to move, management IP address to assign,
destination container unique key, lists of configlets to apply against device being moved,
image bundle to apply on device being moved). The deployDevice function returns a task #
which is then passed to server.executeTask() function which carries out the task, thus
automating a device deployment using CVP.
Creating a Configlet
5/11
Creating a configlet requires a configlet variable cfg1 = cvp.Configlet(‘configlet
name’,’configlet information’,’unique key to identify configlet’,[list of containers where the
configlet is applied],[list of devices where container is applied]). The variable can be
referenced by cfg1.name, cfg1.config, etc, depending on which parameter you wish to use.
The following function creates a configlet after checking if the configlet exists and prompts
the user if they want to overwrite the existing. The error codes that are matched can be
found on the CVP-Python API guide.
def check_for_configlet(key):
#cvp configlet variable being passed
#Verify if the device specific configlet is on cvp already and prompt to update if
so.
try:
print "Adding configlet %s to CVP" % key.name
server.addConfiglet(key)
except cvp.cvpServices.CvpError as e:
if str(e)=='1001':
print "Configlet %s already exists\n" % key.name
mkconfigletanyway = raw_input("Do you want to overwrite existing configlet?
(y/n)")
if mkconfigletanyway.startswith('y'):
server.updateConfiglet(key)
else:
print "***Configlet %s Unchanged***\n" % key.name
elif str(e) == '1002':
print 'Invalid Configlet Name'
else:
print 'Please refer to cvp python-api documentation for error code: %s' %
str(e)
Creating a Container
Creating a container is the same process as creating configlets. The only difference being
is the function accepts a variable of container type. This function also checks if the
container exists and warns user to overwrite the existing information for the container. The
variable to create a container takes the following inputs; cvp.Container(‘name of the
container’, ‘unique key to identify container’, ‘parent container id’, ‘configlets assigned to
container’, ‘imageBundle assigned to container’, ‘parent container name’)
6/11
def check_for_container(key):
#cvp container variable being passed
#Verify if the device specific configlet is on cvp already and prompt to update if
so.
try:
print "Adding container %s to CVP" % key.name
server.addContainer(key)
except cvp.cvpServices.CvpError as e:
if str(e)=='3001':
print "Container %s already exists\n" % key.name
chkcontainer= raw_input("Do you want to overwrite existing container?
(y/n)")
if chkcontainer.startswith('y'):
server.deleteContainer(key) #update method does not exist
server.addContainer(key)
else:
print "***Container %s Unchanged***\n" % key.name
elif str(e) == '3002':
print 'Invalid Container Name'
else:
print 'Please refer to cvp python-api documentation for error code: %s'
% str(e)
Highlighting the CVP-Python API related configuration; creating a name for the configlet,
creating the configuration for the loopback link, ethernet links, and merging it into the config
part of the configlet variable.
7/11
def configlet(self):
#Link Config Section
c2.name = 'link_config_for_'+self.name
c2.config = "interface Loopback0 \n description loopback interface for BGP
peering \n ip address %s/32 \n" % (self.loopback)
c2var=[]
for intf in self.interfaces:
c2var.append( 'interface Ethernet%d \n description Link to %s \n no
switchport\n ip address %s/%d\n' % (intf['interface_number'],
intf['peer_switch'], intf['address'],link_mask))
#merging loopback info and interface info to single config state
builder = ''
bl=len(c2var)
for eh in range(bl):
builder = builder + c2var[eh]
c2.config = c2.config + builder
check_for_configlet(c2) #new verification and add function use this as of v2
#BGP Config Section
#CVP BGP INFO BUILDER
bgp1.name = "bgp_config_for_"+self.name
bgpvar = []
bgp1.config='router bgp %d\n maximum-paths 4\n' % self.asn
for peer in self.bgp_peerlist:
bgpvar.append(' neighbor %s remote-as %d\n' %
(peer['peer_address'],peer['peer_as']))
bgpvar.append(' network %s/32' % (self.loopback))
bgpbuilder = ''
bgpbl = len(bgpvar)
for t in range(bgpbl):
bgpbuilder = bgpbuilder + bgpvar[t]
bgp1.config = bgp1.config + bgpbuilder
check_for_configlet(bgp1)
On the start of the function it will generate the Tier 1 then the Tier 2 configurations as
configlets using the supplied information. Once completed the configurations will appear
under configlets within CVP. For each tier the process is the same; generate the mlag
configuration for the 1st MLAG pair, generate the portchannel configuration for the mlag
peers, generate the portchannel for the uplink/downlink between Tier2 and Tier 1, the mlag
configuration for the 2nd MLAG pair and lastly the number of top of rack pairs. The
configuration below is used for the Tier 2 MLAG configlets.
8/11
mlagp3 = cvp.Configlet('','','',[],[]) #Create configlet variable for MLAG pair 1
mlagp4 = cvp.Configlet('','','',[],[]) #Create configlet variable for MLAG pair 2
#Combine individual configuration steps to a single variable
makemlag3 = t2makevlan + t2makeportchann + foo + t2mlagconfig
makemlag4 = t2makevlan2 + t2makeportchann +foo + t2mlagconfig2
#Name the configlet and assign the combined configurations into the configlet
variable for MLAG pair 1
mlagp3.name = 'Tier2_Pair_%s_a' % pair
mlagp3.config = makemlag3
#Name the configlet and assign the combined configurations into the configlet
variable for MLAG pair 2
mlagp4.name = 'Tier2_Pair_%s_b' % pair
mlagp4.config = makemlag4
#Send the configlet variable to the check_for_configlet function to create the
configlet and also to handle any errors that may occur.
check_for_configlet(mlagp3)
check_for_configlet(mlagp4)
def check_for_configlet(key):
#cvp configlet variable being passed
#Verify if the device specific configlet is on cvp already and prompt to update if
so.
try:
print "Adding configlet %s to CVP" % key.name
server.addConfiglet(key)
except cvp.cvpServices.CvpError as e:
if str(e)=='1001':
print "Configlet %s already exists\n" % key.name
mkconfigletanyway = raw_input("Do you want to overwrite existing
configlet?(y/n)")
if mkconfigletanyway.startswith('y'):
server.updateConfiglet(key)
else:
print "***Configlet %s Unchanged***\n" % key.name
elif str(e) == '1002':
print 'Invalid Configlet Name'
else:
print 'Please refer to cvp python-api documentation for error code: %s'
% str(e)
9/11
[root@cvptest13]# python makeDC.py
Generating Link config, BGP config, MLAG config
Please see code for the particulars
Adding configlet link_config_for_spine1 to CVP
Configlet link_config_for_spine1 already exists
…
Adding configlet Tier2_Pair_1_b to CVP
Configlet Tier2_Pair_1_b already exists
Do you want to overwrite existing configlet?(y/n)
***Configlet Tier2_Pair_1_b Unchanged***
Time to execute script 29.5110750198 seconds
Further Reading
CloudVision Portal Python-API guide
CloudVision Portal API guide for non-programmers
IPaddress library
Post Questions on EOS Central.
Appendix
User output of script for a 8 spine (T3), 8 leafs (T2), 8 TORs (T1) deployment
[root@cvpdev ryan]# python MakeDCv2.16.py
Generating Link config, BGP config, MLAG config
Please see code for the particulars
Adding configlet link_config_for_spine1 to CVP
Adding configlet bgp_config_for_spine1 to CVP
Adding configlet link_config_for_spine2 to CVP
<omitted for brevity>
Adding configlet bgp_config_for_spine7 to CVP
Adding configlet link_config_for_spine8 to CVP
Adding configlet bgp_config_for_spine8 to CVP
Adding configlet link_config_for_leaf1 to CVP
Adding configlet bgp_config_for_leaf1 to CVP
<omitted for brevity>
Adding configlet link_config_for_leaf8 to CVP
Adding configlet bgp_config_for_leaf8 to CVP
Adding configlet Tier1_MLAG_Pair_1_a to CVP
Adding configlet Tier1_MLAG_Pair_1_b to CVP
<omitted for brevity>
Adding configlet Tier1_MLAG_Pair_4_a to CVP
Adding configlet Tier1_MLAG_Pair_4_b to CVP
Adding configlet Tier2_Pair_1_a to CVP
Adding configlet Tier2_Pair_1_b to CVP
<omitted for brevity>
Adding configlet Tier2_Pair_4_a to CVP
Adding configlet Tier2_Pair_4_b to CVP
Time to execute script 1.27535700798 seconds
10/11
CVP Python Script
CVP-python-configurator.py
inShare
11/11