Getting the python and virtual environment set up
These instructions are for a Windows set up.
Use these instructions for setting up your Python virtual environment and for obtaining and installing the Cobra SDK and model.
https://cobra.readthedocs.io/en/latest/install.html
But essentially, if you’re doing this for the first time, then do the following:
Download and install python 2.7 from here (yes, version 2.7)
https://www.python.org/downloads/
Download this python script to a chosen location, and run it using python. This will install pip.
https://bootstrap.pypa.io/get-pip.py
Run these pip commands. I found that I needed to run the pip2 executable as opposed to version 3 pip that was already installed, in order to successfuly install the requirements and run using python2 etc.
pip install virtualenv
pip install virtualenv-clone
pip install virtualenvwrapper-win
Run this command to create your virtual environment (See how it creates the acienv folder in your C:\Users\username\Envs folder)
mkvirtualenv acienv
Upgrade pip
python -m pip install –upgrade pip
Download the Cobra SDK & model files from these locations:
and then do a ‘pip install’ using the paths to these whl files, or just copy them to the directory you’re in:
pip install acicobra-4.2_3h-py2.py3-none-any.whl
pip install acimodel-4.2_3h-py2.py3-none-any.whl
Then run your scripts. You might have to rename your python.exe installed inside Python27 to python2.exe to run inside the command line.
Connect to the APIC with the Cobra SDK for an MO Query
cobra_tenants.py
#!/usr/bin/python2.7 import requests from credentials import * from sys import argv from cobra.mit.session import LoginSession from cobra.mit.access import MoDirectory # Disable warnings for insecure certificates (self-signed) requests.packages.urllib3.disable_warnings( category=requests.packages.urllib3.exceptions.InsecureRequestWarning ) # Argument handling if len(argv) != 2: print('Please provide exactly one parameter: the Tenant name!') exit() tenant_name = argv[1] print('Checking the APIC for tenant {}'.format(tenant_name)) # Login session = LoginSession(APIC_HOST, APIC_USERNAME, APIC_PASSWORD) moDir = MoDirectory(session) moDir.login() # Tenant lookup by DN tenant = moDir.lookupByDn('uni/tn-{}'.format(tenant_name)) if tenant: print('Found tenant {} with DN: {}'.format(tenant_name, tenant.dn)) else: print('Tenant {} does not exist!'.format(tenant_name))
credentials.py
APIC_HOST = "https://sandboxapicdc.cisco.com" APIC_USERNAME = "admin" APIC_PASSWORD = "!v3G@!4@Y"
I then run the cobra_tenants.py script with the Sales argument, or any argument for which I know a tenant does not yet exist:
I check the APIC sandbox to see which tenants do actually exist, and use that name instead; in this example the tenant named Heroes:
I then run the cobra_tenants.py script with the Heroes argument:
Perform Class-Based Queries with the Cobra SDK
We previously performed a managed object query specifically by DN.
To search for objects by their class name, useful when you do not know the exact DN or you want the full list of such objects, write a new script to implement class-based queries with filters.
class_query.py
The ClassQuery from cobra.mit.request (documented at https://cobra.readthedocs.io/en/latest/api-ref/request.html#classquery) will allow you to build more advanced queries for the APIC MIT.
The first query that you are going to write is one that retrieves all EPG objects (class fvAEPg) currently created on the APIC. This query returns a list of EPG objects (specifically the type of cobra.modelimpl.fv.aepg.AEPg), which can be iterated through to obtain their configuration attributes, such as the Name and DN parameters. Create a ClassQuery object with the fvAEPg string argument, use it in the query() method of the MoDirectory object.
Add another query to the script that returns all Fabric Nodes (class fabricNode)—these nodes are all the leaf and spine switches plus the controllers. Print the number of nodes returned by the query, iterate through them using a for loop to print the nodes’ Name and DN.
Add the filter to only query leaf nodes. The property filter can be applied to the previous fabricNode query to return only the leaf nodes in the fabric. Add the code to query the fabric nodes with the role leaf. Use the propFilter parameter to set the query, print the result and iterate through them to print their name, role, and DN.
#!/usr/bin/python2.7 from credentials import * from cobra.mit.session import LoginSession from cobra.mit.access import MoDirectory from cobra.mit.request import ClassQuery import requests # Disable warnings for insecure certificates (self-signed) requests.packages.urllib3.disable_warnings( category=requests.packages.urllib3.exceptions.InsecureRequestWarning ) # Login session = LoginSession(APIC_HOST, APIC_USERNAME, APIC_PASSWORD) moDir = MoDirectory(session) moDir.login() epgs_query = ClassQuery('fvAEPg') epgs = moDir.query(epgs_query) # Print all EPGs objects returned by the query print('fvAEPg class query returned {} EPGs total.'.format(len(epgs))) for epg in epgs: print('Name: {:20} DN: {}'.format(epg.name, epg.dn)) print('\n') # Fabric Node query fabric_query = ClassQuery('fabricNode') nodes = moDir.query(fabric_query) # Print all the node objects returned by the query print('fabricNode class query returned {} nodes total.'.format(len(nodes))) for node in nodes: print('Name: {:20} Role: {:20} DN: {}'.format(node.name, node.role, node.dn)) # Property filter print('\nAdding filter to only query for leaf nodes.\n') fabric_query.propFilter = 'eq(fabricNode.role,"leaf")' # Run the query against the API nodes = moDir.query(fabric_query) # Print all the node objects returned by the query print('fabricNode class query returned {} nodes total.'.format(len(nodes))) for node in nodes: print('Name: {:20} Role: {:20} DN: {}'.format(node.name, node.role, node.dn)) # You can confirm the final query options by looking at fabric_query.options # and compile the full query URL as you would write it in Postman for example print('The final query is: {}'.format(fabric_query.getUrl(session)))
Giving the following output:
Use Arya to Generate the Python Code
To prepare for using the Arya tool to generate Cobra code from APIC configuration, first export an existing Tenant configuration as JSON from the APIC, then proceed to use it as a template to create a new identical Tenant using Cobra code.
Open the APIC sandbox GUI, navigate to the Tenants > All Tenants. Right-click the Marketing tenant and select Save as…
In the Save As dialog window that opens, make sure that Only Configuration, Subtree, and json are selected before clicking Download.
Save the file as tn-Marketing.json.
Open and inspect the tn-Marketing.json file in VS Code or Notepad++ etc.
Note that it includes the full Tenant configuration, with all child objects, such as VRF, Bridge Domain, EPGs, and so on.
Format the document by- press Ctrl+Shift+I in VS Code or by using Plugins > JSTool > JSformat in Notepad++.
Use Arya to Generate the Cobra Code and Create a New Support Tenant
I used the instructions given at github for installing arya:
https://github.com/datacenter/arya
Once I’d cloned it and run the python setup I still couldn’t get it to run.
What I found worked was to run the arya.py script directly:
c:\Temp\CiscoTraining\code\cobra\arya\arya\arya.py -f tn-Marketing.json > arya_code.py
Which generates this python script:
arya_code.py
#!/usr/bin/env python ''' Autogenerated code using arya.py Original Object Document Input: { "totalCount": "1", "imdata": [{ "fvTenant": { "attributes": { "annotation": "", "descr": "", "dn": "uni/tn-Marketing", "name": "Marketing", "nameAlias": "", "ownerKey": "", "ownerTag": "", "userdom": ":all:" }, "children": [{ "vzBrCP": { "attributes": { "annotation": "", "descr": "", "intent": "install", "name": "Portal", "nameAlias": "", "ownerKey": "", "ownerTag": "", "prio": "unspecified", "scope": "context", "targetDscp": "unspecified", "userdom": ":all:" }, "children": [{ "vzSubj": { "attributes": { "annotation": "", "consMatchT": "AtleastOne", "descr": "", "name": "http", "nameAlias": "", "prio": "unspecified", "provMatchT": "AtleastOne", "revFltPorts": "yes", "targetDscp": "unspecified", "userdom": ":all:" }, "children": [{ "vzRsSubjFiltAtt": { "attributes": { "action": "permit", "annotation": "", "directives": "", "priorityOverride": "default", "tnVzFilterName": "http", "userdom": ":all:" } } } ] } } ] } }, { "vnsSvcCont": { "attributes": { "annotation": "", "userdom": "all" } } }, { "fvEpTags": { "attributes": { "annotation": "", "userdom": "all" } } }, { "fvCtx": { "attributes": { "annotation": "", "bdEnforcedEnable": "no", "descr": "", "ipDataPlaneLearning": "enabled", "knwMcastAct": "permit", "name": "Marketing_VRF", "nameAlias": "", "ownerKey": "", "ownerTag": "", "pcEnfDir": "ingress", "pcEnfPref": "enforced", "userdom": ":all:", "vrfIndex": "0" }, "children": [{ "fvRsVrfValidationPol": { "attributes": { "annotation": "", "tnL3extVrfValidationPolName": "", "userdom": "all" } } }, { "vzAny": { "attributes": { "annotation": "", "descr": "", "matchT": "AtleastOne", "name": "", "nameAlias": "", "prefGrMemb": "disabled", "userdom": "all" } } }, { "fvRsOspfCtxPol": { "attributes": { "annotation": "", "tnOspfCtxPolName": "", "userdom": "all" } } }, { "fvRsCtxToEpRet": { "attributes": { "annotation": "", "tnFvEpRetPolName": "", "userdom": "all" } } }, { "fvRsCtxToExtRouteTagPol": { "attributes": { "annotation": "", "tnL3extRouteTagPolName": "", "userdom": "all" } } }, { "fvRsBgpCtxPol": { "attributes": { "annotation": "", "tnBgpCtxPolName": "", "userdom": "all" } } } ] } }, { "fvBD": { "attributes": { "OptimizeWanBandwidth": "no", "annotation": "", "arpFlood": "no", "descr": "", "epClear": "no", "epMoveDetectMode": "", "hostBasedRouting": "no", "intersiteBumTrafficAllow": "no", "intersiteL2Stretch": "no", "ipLearning": "yes", "ipv6McastAllow": "no", "limitIpLearnToSubnets": "yes", "llAddr": "::", "mac": "00:22:BD:F8:19:FF", "mcastAllow": "no", "multiDstPktAct": "bd-flood", "name": "Marketing_BD", "nameAlias": "", "ownerKey": "", "ownerTag": "", "type": "regular", "unicastRoute": "yes", "unkMacUcastAct": "proxy", "unkMcastAct": "flood", "userdom": ":all:", "v6unkMcastAct": "flood", "vmac": "not-applicable" }, "children": [{ "fvSubnet": { "attributes": { "annotation": "", "ctrl": "nd", "descr": "", "ip": "20.23.21.1/24", "ipDPLearning": "enabled", "name": "Marketing_Subnet", "nameAlias": "", "preferred": "no", "scope": "public", "userdom": ":all:", "virtual": "no" } } }, { "fvRsMldsn": { "attributes": { "annotation": "", "tnMldSnoopPolName": "", "userdom": "all" } } }, { "fvRsIgmpsn": { "attributes": { "annotation": "", "tnIgmpSnoopPolName": "", "userdom": "all" } } }, { "fvRsCtx": { "attributes": { "annotation": "", "tnFvCtxName": "Marketing_VRF", "userdom": "all" } } }, { "fvRsBdToEpRet": { "attributes": { "annotation": "", "resolveAct": "resolve", "tnFvEpRetPolName": "", "userdom": "all" } } }, { "fvRsBDToNdP": { "attributes": { "annotation": "", "tnNdIfPolName": "", "userdom": "all" } } } ] } }, { "vzFilter": { "attributes": { "annotation": "", "descr": "", "name": "http", "nameAlias": "", "ownerKey": "", "ownerTag": "", "userdom": ":all:" }, "children": [{ "vzEntry": { "attributes": { "annotation": "", "applyToFrag": "no", "arpOpc": "unspecified", "dFromPort": "http", "dToPort": "http", "descr": "", "etherT": "ip", "icmpv4T": "unspecified", "icmpv6T": "unspecified", "matchDscp": "unspecified", "name": "tcp-80", "nameAlias": "", "prot": "tcp", "sFromPort": "unspecified", "sToPort": "unspecified", "stateful": "no", "tcpRules": "", "userdom": ":all:" } } } ] } }, { "fvRsTenantMonPol": { "attributes": { "annotation": "", "tnMonEPGPolName": "", "userdom": "all" } } }, { "fvAp": { "attributes": { "annotation": "", "descr": "", "name": "Marketing_APP", "nameAlias": "", "ownerKey": "", "ownerTag": "", "prio": "unspecified", "userdom": ":all:" }, "children": [{ "fvAEPg": { "attributes": { "annotation": "", "descr": "", "exceptionTag": "", "floodOnEncap": "disabled", "fwdCtrl": "", "hasMcastSource": "no", "isAttrBasedEPg": "no", "matchT": "AtleastOne", "name": "Users_EPG", "nameAlias": "", "pcEnfPref": "unenforced", "prefGrMemb": "exclude", "prio": "unspecified", "shutdown": "no", "userdom": ":all:" }, "children": [{ "fvRsCons": { "attributes": { "annotation": "", "intent": "install", "prio": "unspecified", "tnVzBrCPName": "Portal", "userdom": ":all:" } } }, { "fvRsCustQosPol": { "attributes": { "annotation": "", "tnQosCustomPolName": "", "userdom": "all" } } }, { "fvRsBd": { "attributes": { "annotation": "", "tnFvBDName": "Marketing_BD", "userdom": "all" } } } ] } }, { "fvAEPg": { "attributes": { "annotation": "", "descr": "", "exceptionTag": "", "floodOnEncap": "disabled", "fwdCtrl": "", "hasMcastSource": "no", "isAttrBasedEPg": "no", "matchT": "AtleastOne", "name": "Portal_EPG", "nameAlias": "", "pcEnfPref": "unenforced", "prefGrMemb": "exclude", "prio": "unspecified", "shutdown": "no", "userdom": ":all:" }, "children": [{ "fvRsProv": { "attributes": { "annotation": "", "intent": "install", "matchT": "AtleastOne", "prio": "unspecified", "tnVzBrCPName": "Portal", "userdom": ":all:" } } }, { "fvRsCustQosPol": { "attributes": { "annotation": "", "tnQosCustomPolName": "", "userdom": "all" } } }, { "fvRsBd": { "attributes": { "annotation": "", "tnFvBDName": "Marketing_BD", "userdom": "all" } } } ] } } ] } } ] } } ] } ''' raise RuntimeError('Please review the auto generated code before ' + 'executing the output. Some placeholders will ' + 'need to be changed') # list of packages that should be imported for this code to work import cobra.mit.access import cobra.mit.request import cobra.mit.session import cobra.model.fv import cobra.model.pol import cobra.model.vns import cobra.model.vz from cobra.internal.codec.xmlcodec import toXMLStr # log into an APIC and create a directory object ls = cobra.mit.session.LoginSession('https://1.1.1.1', 'admin', 'password') md = cobra.mit.access.MoDirectory(ls) md.login() # the top level object on which operations will be made polUni = cobra.model.pol.Uni('') # build the request using cobra syntax fvTenant = cobra.model.fv.Tenant(polUni, ownerKey=u'', name=u'Marketing', descr=u'', userdom=u':all:', nameAlias=u'', ownerTag=u'', annotation=u'') vzBrCP = cobra.model.vz.BrCP(fvTenant, ownerKey=u'', userdom=u':all:', name=u'Portal', descr=u'', targetDscp=u'unspecified', intent=u'install', nameAlias=u'', ownerTag=u'', prio=u'unspecified', annotation=u'') vzSubj = cobra.model.vz.Subj(vzBrCP, revFltPorts=u'yes', descr=u'', prio=u'unspecified', targetDscp=u'unspecified', userdom=u':all:', nameAlias=u'', consMatchT=u'AtleastOne', annotation=u'', provMatchT=u'AtleastOne', name=u'http') vzRsSubjFiltAtt = cobra.model.vz.RsSubjFiltAtt(vzSubj, tnVzFilterName=u'http', priorityOverride=u'default', userdom=u':all:', action=u'permit', directives=u'', annotation=u'') vnsSvcCont = cobra.model.vns.SvcCont(fvTenant, userdom=u'all', annotation=u'') fvEpTags = cobra.model.fv.EpTags(fvTenant, userdom=u'all', annotation=u'') fvCtx = cobra.model.fv.Ctx(fvTenant, ownerKey=u'', name=u'Marketing_VRF', descr=u'', nameAlias=u'', knwMcastAct=u'permit', vrfIndex=u'0', pcEnfDir=u'ingress', userdom=u':all:', ipDataPlaneLearning=u'enabled', ownerTag=u'', annotation=u'', pcEnfPref=u'enforced', bdEnforcedEnable=u'no') fvRsVrfValidationPol = cobra.model.fv.RsVrfValidationPol(fvCtx, userdom=u'all', tnL3extVrfValidationPolName=u'', annotation=u'') vzAny = cobra.model.vz.Any(fvCtx, matchT=u'AtleastOne', name=u'', descr=u'', prefGrMemb=u'disabled', userdom=u'all', nameAlias=u'', annotation=u'') fvRsOspfCtxPol = cobra.model.fv.RsOspfCtxPol(fvCtx, userdom=u'all', annotation=u'', tnOspfCtxPolName=u'') fvRsCtxToEpRet = cobra.model.fv.RsCtxToEpRet(fvCtx, userdom=u'all', annotation=u'', tnFvEpRetPolName=u'') fvRsCtxToExtRouteTagPol = cobra.model.fv.RsCtxToExtRouteTagPol(fvCtx, userdom=u'all', annotation=u'', tnL3extRouteTagPolName=u'') fvRsBgpCtxPol = cobra.model.fv.RsBgpCtxPol(fvCtx, tnBgpCtxPolName=u'', userdom=u'all', annotation=u'') fvBD = cobra.model.fv.BD(fvTenant, multiDstPktAct=u'bd-flood', mcastAllow=u'no', ipv6McastAllow=u'no', userdom=u':all:', limitIpLearnToSubnets=u'yes', unicastRoute=u'yes', unkMcastAct=u'flood', v6unkMcastAct=u'flood', descr=u'', hostBasedRouting=u'no', llAddr=u'::', nameAlias=u'', type=u'regular', ipLearning=u'yes', vmac=u'not-applicable', mac=u'00:22:BD:F8:19:FF', epMoveDetectMode=u'', ownerTag=u'', intersiteBumTrafficAllow=u'no', annotation=u'', ownerKey=u'', name=u'Marketing_BD', epClear=u'no', unkMacUcastAct=u'proxy', arpFlood=u'no', intersiteL2Stretch=u'no', OptimizeWanBandwidth=u'no') fvSubnet = cobra.model.fv.Subnet(fvBD, name=u'Marketing_Subnet', descr=u'', ctrl=u'nd', ip=u'20.23.21.1/24', preferred=u'no', annotation=u'', userdom=u':all:', virtual=u'no', nameAlias=u'', ipDPLearning=u'enabled') fvRsMldsn = cobra.model.fv.RsMldsn(fvBD, tnMldSnoopPolName=u'', userdom=u'all', annotation=u'') fvRsIgmpsn = cobra.model.fv.RsIgmpsn(fvBD, tnIgmpSnoopPolName=u'', userdom=u'all', annotation=u'') fvRsCtx = cobra.model.fv.RsCtx(fvBD, userdom=u'all', annotation=u'', tnFvCtxName=u'Marketing_VRF') fvRsBdToEpRet = cobra.model.fv.RsBdToEpRet(fvBD, resolveAct=u'resolve', userdom=u'all', annotation=u'', tnFvEpRetPolName=u'') fvRsBDToNdP = cobra.model.fv.RsBDToNdP(fvBD, userdom=u'all', annotation=u'', tnNdIfPolName=u'') vzFilter = cobra.model.vz.Filter(fvTenant, ownerKey=u'', name=u'http', descr=u'', userdom=u':all:', nameAlias=u'', ownerTag=u'', annotation=u'') vzEntry = cobra.model.vz.Entry(vzFilter, tcpRules=u'', arpOpc=u'unspecified', applyToFrag=u'no', dToPort=u'http', descr=u'', nameAlias=u'', matchDscp=u'unspecified', prot=u'tcp', icmpv4T=u'unspecified', sFromPort=u'unspecified', stateful=u'no', userdom=u':all:', icmpv6T=u'unspecified', sToPort=u'unspecified', etherT=u'ip', dFromPort=u'http', annotation=u'', name=u'tcp-80') fvRsTenantMonPol = cobra.model.fv.RsTenantMonPol(fvTenant, userdom=u'all', annotation=u'', tnMonEPGPolName=u'') fvAp = cobra.model.fv.Ap(fvTenant, ownerKey=u'', name=u'Marketing_APP', descr=u'', userdom=u':all:', nameAlias=u'', ownerTag=u'', prio=u'unspecified', annotation=u'') fvAEPg = cobra.model.fv.AEPg(fvAp, shutdown=u'no', isAttrBasedEPg=u'no', matchT=u'AtleastOne', name=u'Users_EPG', descr=u'', fwdCtrl=u'', prefGrMemb=u'exclude', exceptionTag=u'', floodOnEncap=u'disabled', userdom=u':all:', nameAlias=u'', hasMcastSource=u'no', prio=u'unspecified', annotation=u'', pcEnfPref=u'unenforced') fvRsCons = cobra.model.fv.RsCons(fvAEPg, userdom=u':all:', tnVzBrCPName=u'Portal', intent=u'install', annotation=u'', prio=u'unspecified') fvRsCustQosPol = cobra.model.fv.RsCustQosPol(fvAEPg, userdom=u'all', annotation=u'', tnQosCustomPolName=u'') fvRsBd = cobra.model.fv.RsBd(fvAEPg, userdom=u'all', annotation=u'', tnFvBDName=u'Marketing_BD') fvAEPg2 = cobra.model.fv.AEPg(fvAp, shutdown=u'no', isAttrBasedEPg=u'no', matchT=u'AtleastOne', name=u'Portal_EPG', descr=u'', fwdCtrl=u'', prefGrMemb=u'exclude', exceptionTag=u'', floodOnEncap=u'disabled', userdom=u':all:', nameAlias=u'', hasMcastSource=u'no', prio=u'unspecified', annotation=u'', pcEnfPref=u'unenforced') fvRsProv = cobra.model.fv.RsProv(fvAEPg2, matchT=u'AtleastOne', prio=u'unspecified', tnVzBrCPName=u'Portal', userdom=u':all:', intent=u'install', annotation=u'') fvRsCustQosPol2 = cobra.model.fv.RsCustQosPol(fvAEPg2, userdom=u'all', annotation=u'', tnQosCustomPolName=u'') fvRsBd2 = cobra.model.fv.RsBd(fvAEPg2, userdom=u'all', annotation=u'', tnFvBDName=u'Marketing_BD') # commit the generated code to APIC print(toXMLStr(polUni)) c = cobra.mit.request.ConfigRequest() c.addMo(polUni) md.commit(c)
Do a global replace of “Marketing” with “Support”
And remove this block of code:
raise RuntimeError('Please review the auto generated code before ' + 'executing the output. Some placeholders will ' + 'need to be changed')
Run the update script in python2 to generate the new tenant:
Inspect the Tenants > All Tenants list again and see that the Tenant called Support has been added: