|
| 1 | +import requests |
| 2 | +import json |
| 3 | +from string import Template |
| 4 | +requests.urllib3.disable_warnings() |
| 5 | +""" |
| 6 | +Author1: George Siaw |
| 7 | +Date: 1/28/2019 |
| 8 | +Purpose: Send API calls to APIC and print status |
| 9 | +Version: 1.2 |
| 10 | +! |
| 11 | +Author2: Ganesh Mohan |
| 12 | +Date: 12/23/2020 |
| 13 | +Purpose: Send API calls to APIC and print status |
| 14 | +Version: 1.3 |
| 15 | +""" |
| 16 | + |
| 17 | +class AuthenticationError(Exception): |
| 18 | + pass |
| 19 | +class Client: |
| 20 | + def __init__(self, host, usr, pwd): |
| 21 | + #self.jar = requests.cookies.RequestsCookieJar() |
| 22 | + self.host = host |
| 23 | + self.usr = usr |
| 24 | + self.pwd = pwd |
| 25 | + self.client = requests.Session() |
| 26 | +#Pushing the configuration in the APIC controller |
| 27 | + def POST(self, url, data,Role): |
| 28 | + response= self.client.post('https://%s%s' % (self.host, url),data=json.dumps(data),timeout=5,verify=False) |
| 29 | + resp=response.text |
| 30 | + if 'error' in resp: |
| 31 | + print("\n!!!!{}: Config already exist or config issue..Code{}\n".format(Role,response)) |
| 32 | + #print("!!!!Error code:{}\n".format(resp)) |
| 33 | + #raise AuthenticationError |
| 34 | + else: |
| 35 | + print(">>>>{}:>>>>>Done # Status Code>{}".format(Role,response)) |
| 36 | + return response |
| 37 | +#Pulling the data in the APIC controller |
| 38 | + def get(self, url): |
| 39 | + print("getting into the controller:{}".format(url)) |
| 40 | + r=self.client.get('https://%s%s' % (self.host, url),timeout=5,verify=False) |
| 41 | + print("get response {}".format(r)) |
| 42 | + return r |
| 43 | +#Login into APIC using static Credentials. |
| 44 | + def login(self): |
| 45 | + data = {"aaaUser": {"attributes": {"name": self.usr, "pwd": self.pwd}}} |
| 46 | + res= self.client.post('https://%s/api/aaaLogin.json' % (self.host),data=json.dumps(data),timeout=5,verify=False) |
| 47 | + print(res) |
| 48 | + if res.status_code != 200 or 'error' in res.json()['imdata'][0]: |
| 49 | + raise AuthenticationError |
| 50 | +#T1:Create Tenant,VRF and L3 BD. |
| 51 | + def tenant(self,Tname,VRF,BD): |
| 52 | + Role='T1:tenant:{},VRF:{},L3_BD:{}'.format(Tname,VRF,BD) |
| 53 | + print("\nCreating the tenant:{}\n".format(Tname)) |
| 54 | + data = { "fvTenant":{"attributes":{"dn":"uni/tn-"+Tname,"status":"created,modified"},"children":[ |
| 55 | + {"fvBD":{"attributes":{"dn":"uni/tn-"+Tname+"/BD-"+BD+"_bd","name":BD+"_bd","arpFlood":"true","unicastRoute":"true","rn":"BD-"+BD+"_bd","status":"created,modified"}, |
| 56 | + "children":[{"fvRsCtx":{"attributes":{"tnFvCtxName":VRF+"_vrf","status":"created,modified"},"children":[] }}]}}, |
| 57 | + {"fvCtx":{"attributes":{"dn":"uni/tn-"+Tname+"/ctx-"+VRF+"_vrf","name": VRF+"_vrf","rn":"ctx-"+VRF+"_vrf","status":"created,modified"},"children":[] |
| 58 | + }}]}} |
| 59 | + return self.POST('/api/mo/uni/tn-{}.json'.format(Tname), data,Role) |
| 60 | +#T2:Create L3 BD,Subnet and Scope of the subnet. |
| 61 | + def bd_Subnet(self,Tname,BD,Subnet,Scope): |
| 62 | + Role='T2: Creating the L3 BD:{} with Anycast GW:{} on Scope:{}'.format(BD,Subnet,Scope) |
| 63 | + scope_list = ['public', 'shared','private'] |
| 64 | + if Scope in scope_list: |
| 65 | + data = {"fvSubnet": {"attributes": {"dn": "uni/tn-"+Tname+"/BD-"+BD+"_bd/subnet-["+Subnet+"]", "ctrl": "", "ip": Subnet, "rn": "subnet-["+Subnet+"]","scope":Scope, "status": "created,modified"},"children": [] } } |
| 66 | + else: |
| 67 | + data = {"fvSubnet": {"attributes": {"dn": "uni/tn-"+Tname+"/BD-"+BD+"_bd/subnet-["+Subnet+"]", "ctrl": "", "ip": Subnet, "rn": "subnet-["+Subnet+"]", "status": "created"},"children": [] } } |
| 68 | + return self.POST('/api/node/mo/uni/tn-{}/BD-{}_bd/subnet-[{}].json'.format(Tname,BD,Subnet), data,Role) |
| 69 | +#T3:Create APP policy. |
| 70 | + def app_profile(self,Tname,APP): |
| 71 | + Role="T3:{}-Application profile:".format(APP) |
| 72 | + data = {"fvAp":{"attributes":{"dn":"uni/tn-"+Tname+"/ap-"+APP+"_ap","name":APP+"_ap","rn":"ap-"+APP+"_ap","status":"created,modified"},"children":[] }} |
| 73 | + return self.POST('/api/mo/uni/tn-{}/ap-{}_ap.json'.format(Tname,APP), data,Role) |
| 74 | +#T4:Create EPG and assoicate the BD. |
| 75 | + def epg(self,Tname,BD,APP,EPG): |
| 76 | + Role="T4:EPG:{}".format(EPG) |
| 77 | + data = {"fvAEPg":{"attributes":{"dn":"uni/tn-"+Tname+"/ap-"+APP+"_ap/epg-"+EPG+"_epg","name":EPG+"_epg","rn":"epg-"+EPG+"_epg","status":"created,modified"}, |
| 78 | + "children":[{"fvRsBd":{"attributes":{"tnFvBDName":BD+"_bd","status":"created,modified"},"children":[] }}]}} |
| 79 | + return self.POST('/api/mo/uni/tn-{}/ap-{}_ap.json'.format(Tname,APP), data,Role) |
| 80 | +#T5 Create Static vlan pool. Domain is mapped 1:1 to vlan. |
| 81 | + def Static_vlan(self,VLAN_NAME,VLAN_ID): |
| 82 | + Role="T5: Static vlan:{}".format(VLAN_ID) |
| 83 | + data = {"fvnsVlanInstP":{"attributes":{"dn":"uni/infra/vlanns-["+VLAN_NAME+"_vlans]-static","name":VLAN_NAME+"_vlans", |
| 84 | + "allocMode":"static","rn":"vlanns-["+VLAN_NAME+"_vlans]-static","status":"created"}, |
| 85 | + "children":[ |
| 86 | + {"fvnsEncapBlk":{"attributes":{"dn":"uni/infra/vlanns-["+VLAN_NAME+"_vlans]-static/from-[vlan-"+VLAN_ID+"]-to-[vlan-"+VLAN_ID+"]", |
| 87 | + "from":"vlan-"+VLAN_ID,"to":"vlan-"+VLAN_ID,"rn":"from-[vlan-"+VLAN_ID+"]-to-[vlan-"+VLAN_ID+"]","status":"created,modified"}, |
| 88 | + "children":[] |
| 89 | + }}]}} |
| 90 | + return self.POST('/api/node/mo/uni/infra/vlanns-[{}_vlans]-static.json'.format(VLAN_NAME), data,Role) |
| 91 | +#T6 Create physical domain. Domain is mapped 1:1 to vlan. |
| 92 | + def PhysDomain(self,PHY_D,VLAN_NAME): |
| 93 | + Role="T6:Physical Domain:{}".format(PHY_D) |
| 94 | + #url='https://{}/api/node/mo/uni/phys-PHY_D_phys.json'.format(APIC) |
| 95 | + data= {"physDomP":{"attributes":{"dn":"uni/phys-"+PHY_D+"_phys","name":PHY_D+"_phys","rn":"phys-"+PHY_D+"_phys","status":"created,modified"}, |
| 96 | + "children":[ {"infraRsVlanNs":{ "attributes":{"tDn":"uni/infra/vlanns-["+VLAN_NAME+"_vlans]-static","status":"created,modified"},"children":[] |
| 97 | + }}]}} |
| 98 | + return self.POST('/api/node/mo/uni/phys-{}_phys.json'.format(PHY_D), data,Role) |
| 99 | +#T7 Attach EPG to bare metal/Physical domain |
| 100 | + def AttPhyEPG(self,Tname,PHY_D,APP,EPG): |
| 101 | + Role="T7: Attaching the Physical domain :{} into EPG: {}".format(PHY_D,EPG) |
| 102 | + data = {"fvRsDomAtt":{"attributes":{"resImedcy":"immediate","tDn":"uni/phys-"+PHY_D+"_phys","status":"created"},"children":[]}} |
| 103 | + return self.POST('/api/node/mo/uni/tn-{}/ap-{}_ap/epg-{}_epg.json'.format(Tname,APP,EPG), data,Role) |
| 104 | +#T8 Configure the Leaf_ID_FROM as a Trunk Interface |
| 105 | + def EpgStaticPort(self,Tname,APP,EPG,LEAF_ID_FROM,LEAF_ID_TO,PORT,VLAN,Mode): |
| 106 | + if Mode == "Trunk" or Mode == "trunk": |
| 107 | + #Configure the Leaf_ID_FROM as a Trunk Interface |
| 108 | + Role="T8.1: Enabling Trunk interface vlan {} on Leaf_ID:{} on port eth1/{} into EPG: {}".format(VLAN,LEAF_ID_FROM,PORT,EPG) |
| 109 | + data = { "fvRsPathAtt":{"attributes":{"encap":"vlan-"+VLAN,"instrImedcy":"immediate","tDn":"topology/pod-1/paths-"+LEAF_ID_FROM+"/pathep-[eth1/"+PORT+"]","status":"created"},"children":[] } } |
| 110 | + self.POST('/api/node/mo/uni/tn-{}/ap-{}_ap/epg-{}_epg.json'.format(Tname,APP,EPG), data,Role) |
| 111 | + #Configure the Leaf_ID_TO as a Trunk Interface |
| 112 | + Role="T8.2: Enabling Trunk interface vlan {} on Leaf_ID:{} on port eth1/{} into EPG: {}".format(VLAN,LEAF_ID_TO,PORT,EPG) |
| 113 | + data = { "fvRsPathAtt":{"attributes":{"encap":"vlan-"+VLAN,"instrImedcy":"immediate","tDn":"topology/pod-1/paths-"+LEAF_ID_TO+"/pathep-[eth1/"+PORT+"]","status":"created"},"children":[] } } |
| 114 | + return self.POST('/api/node/mo/uni/tn-{}/ap-{}_ap/epg-{}_epg.json'.format(Tname,APP,EPG), data,Role) |
| 115 | + elif Mode == "Access" or Mode == "access": |
| 116 | + #Configure the Leaf_ID_FROM as a Access Interface |
| 117 | + Role="T8.3: Enabling Access interface vlan {} on Leaf_ID:{} on port eth1/{} into EPG: {}".format(VLAN,LEAF_ID_FROM,PORT,EPG) |
| 118 | + data = {"fvRsPathAtt":{"attributes":{"encap":"vlan-"+VLAN,"mode":"native","instrImedcy":"immediate","tDn":"topology/pod-1/paths-"+LEAF_ID_FROM+"/pathep-[eth1/"+PORT+"]","status":"created"},"children":[]}} |
| 119 | + self.POST('/api/node/mo/uni/tn-{}/ap-{}_ap/epg-{}_epg.json'.format(Tname,APP,EPG), data,Role) |
| 120 | + #Configure the Leaf_ID_TO as a Access Interface |
| 121 | + Role="T8.4: Enabling Access interface vlan {} on Leaf_ID:{} on port eth1/{} into EPG: {}".format(VLAN,LEAF_ID_TO,PORT,EPG) |
| 122 | + data = {"fvRsPathAtt":{"attributes":{"encap":"vlan-"+VLAN,"mode":"native","instrImedcy":"immediate","tDn":"topology/pod-1/paths-"+LEAF_ID_TO+"/pathep-[eth1/"+PORT+"]","status":"created"},"children":[]}} |
| 123 | + return self.POST('/api/node/mo/uni/tn-{}/ap-{}_ap/epg-{}_epg.json'.format(Tname,APP,EPG), data,Role) |
| 124 | + else: |
| 125 | + Print("Please specify interface mode as : access or trunk ") |
| 126 | +#T9 Create/Verify AAEP |
| 127 | + def AAEP(self,AAEP): |
| 128 | + Role="T9: Creating AAEP:{}".format(AAEP) |
| 129 | + data = {"infraAttEntityP":{"attributes":{"dn":"uni/infra/attentp-"+AAEP+"_aaep","name":AAEP+"_aaep","rn":"attentp-"+AAEP+"_aaep","status":"created,modified"},"children":[ ] } } |
| 130 | + return self.POST('/api/node/mo/uni/infra/attentp-{}_aaep.json'.format(AAEP),data,Role) |
| 131 | +#T10 AAEP attaching with Physical domain |
| 132 | + def AAEPPhy(self,AAEP,PHY_D): |
| 133 | + Role="T10: Attaching the AAEP:{} and physical domain:{}".format(AAEP,PHY_D) |
| 134 | + data = {"infraRsDomP":{"attributes":{"tDn":"uni/phys-"+PHY_D+"_phys","status":"created,modified"},"children":[]}} |
| 135 | + return self.POST('/api/node/mo/uni/infra/attentp-{}_aaep.json'.format(AAEP), data,Role) |
| 136 | +#T11 Create switch profile |
| 137 | + def Switch_pro(self,Switch_Prof,LEAF_ID_FROM,LEAF_ID_TO): |
| 138 | + Role="T11 Switch profile:{}".format(Switch_Prof) |
| 139 | + data ={ "infraNodeP":{ "attributes":{"dn":"uni/infra/nprof-"+Switch_Prof+"_leafProf","name":Switch_Prof+"_leafProf","rn":"nprof-"+Switch_Prof+"_leafProf","status":"created,modified"}, |
| 140 | + "children":[{ "infraLeafS":{"attributes":{ |
| 141 | + "dn":"uni/infra/nprof-"+Switch_Prof+"_leafProf/leaves-"+Switch_Prof+"_LeafSel-typ-range", |
| 142 | + "type":"range","name":Switch_Prof+"_LeafSel","rn":"leaves-"+Switch_Prof+"_LeafSel-typ-range","status":"created"}, |
| 143 | + "children":[{"infraNodeBlk":{"attributes":{ |
| 144 | + "dn":"uni/infra/nprof-"+Switch_Prof+"_leafProf/leaves-"+Switch_Prof+"_LeafSel-typ-range/nodeblk-f6ff793728a065ed", |
| 145 | + "from_":LEAF_ID_FROM,"to_":LEAF_ID_TO,"name":"f6ff793728a065ed","rn":"nodeblk-f6ff793728a065ed","status":"created"}, |
| 146 | + "children":[] } } ] } } ] }} |
| 147 | + return self.POST('/api/node/mo/uni/infra/nprof-{}_leafProf.json'.format(Switch_Prof), data,Role) |
| 148 | +#T12 Create interface profile |
| 149 | + def Interface_pro(self,Int_Prof,port): |
| 150 | + Role="T12: Interface profile:{} and port {}".format(Int_Prof,port) |
| 151 | + data= { "infraAccPortP":{"attributes":{"dn":"uni/infra/accportprof-"+Int_Prof+"_intProf","name":Int_Prof+"_intProf", |
| 152 | + "rn":"accportprof-"+Int_Prof+"_intProf","status":"created,modified"}, |
| 153 | + "children":[{"infraHPortS":{"attributes":{ |
| 154 | + "dn":"uni/infra/accportprof-"+Int_Prof+"_intProf/hports-"+Int_Prof+"_port"+port+"-typ-range", |
| 155 | + "name":Int_Prof+"_port"+port,"rn":"hports-"+Int_Prof+"_port"+port+"-typ-range","status":"created,modified"}, |
| 156 | + "children":[{"infraPortBlk":{ |
| 157 | + "attributes":{"dn":"uni/infra/accportprof-"+Int_Prof+"_intProf/hports-"+Int_Prof+"_port"+port+"-typ-range/portblk-block2", |
| 158 | + "fromPort":port,"toPort":port,"name":"block2","rn":"portblk-block2","status":"created,modified"}, "children":[ ] } }, |
| 159 | + {"infraRsAccBaseGrp":{"attributes":{"status":"created,modified"}, "children":[ ] } } ] } } ] } } |
| 160 | + return self.POST('/api/node/mo/uni/infra/accportprof-{}_intProf.json'.format(Int_Prof), data,Role) |
| 161 | +#T13 Associate interface profile to switch profile |
| 162 | + def IntP2SWP(self,Int_Prof,Switch_Prof): |
| 163 | + Role="T13: Associate interface profile:{} and switch profile {}".format(Int_Prof,Switch_Prof) |
| 164 | + data ={"infraRsAccPortP":{"attributes":{"tDn":"uni/infra/accportprof-"+Int_Prof+"_intProf","status":"created,modified"},"children":[ ] }} |
| 165 | + return self.POST('/api/node/mo/uni/infra/nprof-{}_leafProf.json'.format(Switch_Prof), data,Role) |
| 166 | +#T14 Switch profile modified to attach AAEP |
| 167 | + def IntP2AAEP(self,Int_Prof,AAEP): |
| 168 | + Role="T14: Attaching the interface profile:{} and AAEP {}".format(Int_Prof,AAEP) |
| 169 | + data={ "infraAttEntityP":{"attributes":{"dn":"uni/infra/attentp-"+AAEP+"_aaep","name":AAEP+"_aaep","rn":"attentp-"+AAEP+"_aaep","status":"created,modified"},"children":[] } } |
| 170 | + return self.POST('/api/node/mo/uni/infra/nprof-"+Int_Prof+"_leafProf.json'.format(Int_Prof), data,Role) |
| 171 | +#T15 Create LLDP interface policy |
| 172 | + def LLDP(self,LLDP_ON): |
| 173 | + Role="T15: Creating LLDP profile." |
| 174 | + data = {"lldpIfPol":{"attributes":{"dn":"uni/infra/lldpIfP-"+LLDP_ON+"_lldplfPol","name":LLDP_ON+"_lldplfPol","rn":"lldpIfP-"+LLDP_ON+"_lldplfPol","status":"created,modified"},"children":[] } } |
| 175 | + return self.POST('/api/node/mo/uni/infra/lldpIfP-{}_lldplfPol.json'.format(LLDP_ON), data,Role) |
| 176 | +#T16 Create L2 VLAN scope local policy |
| 177 | + def VLAN_SC_L(self,VSCOPEL): |
| 178 | + Role="T16: Creating L2 VLAN scope profile." |
| 179 | + data= { "l2IfPol":{ "attributes":{ "dn":"uni/infra/l2IfP-"+VSCOPEL+"_vlanScope","name":VSCOPEL+"_vlanScope", |
| 180 | + "vlanScope":"portlocal","rn":"l2IfP-"+VSCOPEL+"_vlanScope","status":"created,modified"},"children":[]}} |
| 181 | + return self.POST('/api/node/mo/uni/infra/l2IfP-{}_vlanScope.json'.format(VSCOPEL), data,Role) |
| 182 | +#T17 Create the policy group and assign AAEP,LLDP and L2VLAN scope to policy group |
| 183 | + def POLICY_GP(self,POLICY,VSCOPEL,LLDP_ON,AAEP): |
| 184 | + Role="T17: Attaching the policy group:{} and AAEP {}".format(POLICY,AAEP) |
| 185 | + data = {"infraAccPortGrp":{"attributes":{"dn":"uni/infra/funcprof/accportgrp-"+POLICY+"_polGrp","status":"created,modified"}, |
| 186 | + "children":[{"infraRsL2IfPol":{"attributes":{ "tnL2IfPolName":VSCOPEL+"_vlanScope"},"children":[]}}, |
| 187 | + { "infraRsLldpIfPol":{"attributes":{"tnLldpIfPolName":LLDP_ON+"_lldplfPol"},"children":[] }}, |
| 188 | + { "infraRsAttEntP":{"attributes":{"tDn":"uni/infra/attentp-"+AAEP+"_aaep","status":"created,modified"},"children":[]}} |
| 189 | + ] }} |
| 190 | + return self.POST('/api/node/mo/uni/infra/funcprof/accportgrp-{}_polGrp.json'.format(POLICY), data,Role) |
| 191 | +#T18 Assign policy group to interface profile |
| 192 | + def GPolicy2IntP(self,Int_Prof,POLICY,port): |
| 193 | + Role="T18: Policy Group:{} and interface profile {}".format(POLICY,Int_Prof) |
| 194 | + data = {"infraRsAccBaseGrp":{"attributes":{ "tDn":"uni/infra/funcprof/accportgrp-"+POLICY+"_polGrp"},"children":[] } } |
| 195 | + return self.POST('/api/node/mo/uni/infra/accportprof-{}_intProf/hports-{}_port{}-typ-range/rsaccBaseGrp.json'.format(Int_Prof,Int_Prof,port), data,Role) |
| 196 | +#T19 Create L2 BD domain. |
| 197 | + def bd_L2(self,Tname,BD): |
| 198 | + Role="T19:{}-L2 BD domain(Flood/Unicast disabled):".format(BD) |
| 199 | + data = {"fvBD":{"attributes":{"dn":"uni/tn-"+Tname+"/BD-"+BD+"_bd","arpFlood":"true","unicastRoute":"false","unkMacUcastAct":"flood","status":"created,modified"},"children":[]}} |
| 200 | + return self.POST('/api/mo/uni/tn-{}/BD-{}_bd.json'.format(Tname,BD), data,Role) |
| 201 | + |
| 202 | +def main(): |
| 203 | + import apic_cfg as cfg |
| 204 | + client = Client(cfg.host, cfg.usr, cfg.pwd) |
| 205 | + print("\n Authenication in to the controller: {}\n".format(cfg.host)) |
| 206 | + client.login() |
| 207 | + t1=client.tenant(cfg.tn,cfg.vrf,cfg.bd) |
| 208 | + t2=client.bd_Subnet(cfg.tn,cfg.bd,cfg.subnet,cfg.scope) |
| 209 | + t3=client.app_profile(cfg.tn,cfg.app) |
| 210 | + t4=client.epg(cfg.tn,cfg.bd,cfg.app,cfg.epg) |
| 211 | + t5=client.Static_vlan(cfg.vname,cfg.vlanid) |
| 212 | + t6=client.PhysDomain(cfg.phydomain,cfg.vname) |
| 213 | + t7=client.AttPhyEPG(cfg.tn,cfg.phydomain,cfg.app,cfg.epg) |
| 214 | + t8=client.EpgStaticPort(cfg.tn,cfg.app,cfg.epg,cfg.LEAF_ID_FROM,cfg.LEAF_ID_TO,cfg.port,cfg.vlanid,cfg.Mode) |
| 215 | + t9=client.AAEP(cfg.aaep) |
| 216 | + t10=client.AAEPPhy(cfg.aaep,cfg.phydomain) |
| 217 | + t11=client.Switch_pro(cfg.Switch_Prof,cfg.LEAF_ID_FROM,cfg.LEAF_ID_TO) |
| 218 | + t12=client.Interface_pro(cfg.Int_Prof,cfg.port) |
| 219 | + t13=client.IntP2SWP(cfg.Int_Prof,cfg.Switch_Prof) |
| 220 | + #t14=client.IntP2AAEP(cfg.Int_Prof,cfg.aaep) |
| 221 | + t15=client.LLDP(cfg.lldp) |
| 222 | + t16=client.VLAN_SC_L(cfg.vlanscope) |
| 223 | + t17=client.POLICY_GP(cfg.gp_policy,cfg.vlanscope,cfg.lldp,cfg.aaep) |
| 224 | + t18=client.GPolicy2IntP(cfg.Int_Prof,cfg.gp_policy,cfg.port) |
| 225 | + #t19=client.bd_L2(cfg.tn,cfg.bd) |
| 226 | + |
| 227 | +if __name__ == '__main__': |
| 228 | + main() |
0 commit comments