|
| 1 | +import requests |
| 2 | +import json |
| 3 | +from string import Template |
| 4 | +requests.urllib3.disable_warnings() |
| 5 | +""" |
| 6 | +Author: Ganesh Mohan |
| 7 | +Date: 12/23/2020 |
| 8 | +Purpose: Send API calls to APIC and print status |
| 9 | +Version: 1.3 |
| 10 | +""" |
| 11 | + |
| 12 | +class AuthenticationError(Exception): |
| 13 | + pass |
| 14 | +class Client: |
| 15 | + def __init__(self, host, usr, pwd): |
| 16 | + #self.jar = requests.cookies.RequestsCookieJar() |
| 17 | + self.host = host |
| 18 | + self.usr = usr |
| 19 | + self.pwd = pwd |
| 20 | + self.client = requests.Session() |
| 21 | +#Pushing the configuration in the APIC controller |
| 22 | + def POST(self, url, data,Role): |
| 23 | + response= self.client.post('https://%s%s' % (self.host, url),data=json.dumps(data),timeout=5,verify=False) |
| 24 | + resp=response.text |
| 25 | + if 'error' in resp: |
| 26 | + print("\n!!!!{}: Config already exist or config issue..Code{}\n".format(Role,response)) |
| 27 | + print("!!!!Error code:{}\n".format(resp)) |
| 28 | + #raise AuthenticationError |
| 29 | + else: |
| 30 | + print(">>>>{}:>>>>>Done # Status Code>{}".format(Role,response)) |
| 31 | + return response |
| 32 | +#Pulling the data in the APIC controller |
| 33 | + def get(self, url): |
| 34 | + print("getting into the controller:{}".format(url)) |
| 35 | + r=self.client.get('https://%s%s' % (self.host, url),timeout=5,verify=False) |
| 36 | + print("get response {}".format(r)) |
| 37 | + return r |
| 38 | +#Login into APIC using static Credentials. |
| 39 | + def login(self): |
| 40 | + data = {"aaaUser": {"attributes": {"name": self.usr, "pwd": self.pwd}}} |
| 41 | + res= self.client.post('https://%s/api/aaaLogin.json' % (self.host),data=json.dumps(data),timeout=5,verify=False) |
| 42 | + print(res) |
| 43 | + if res.status_code != 200 or 'error' in res.json()['imdata'][0]: |
| 44 | + raise AuthenticationError |
| 45 | + |
| 46 | + |
| 47 | +#T1:Create Tenant,VRF |
| 48 | + def tenant(self,Tname,VRF): |
| 49 | + Role='T1:tenant:{},VRF:{}'.format(Tname,VRF) |
| 50 | + print("\nCreating the tenant:{}\n".format(Tname)) |
| 51 | + data = { "fvTenant":{"attributes":{"dn":"uni/tn-"+Tname,"status":"created,modified"},"children":[ |
| 52 | + #{"fvBD":{"attributes":{"dn":"uni/tn-"+Tname+"/BD-"+BD+"_bd","name":BD+"_bd","arpFlood":"true","unicastRoute":"true","rn":"BD-"+BD+"_bd","status":"created,modified"}, |
| 53 | + #"children":[{"fvRsCtx":{"attributes":{"tnFvCtxName":VRF+"_vrf","status":"created,modified"},"children":[] }}], |
| 54 | + {"fvCtx":{"attributes":{"dn":"uni/tn-"+Tname+"/ctx-"+VRF+"_vrf","name": VRF+"_vrf","rn":"ctx-"+VRF+"_vrf","status":"created,modified"},"children":[] |
| 55 | + }}]}} |
| 56 | + return self.POST('/api/mo/uni/tn-{}.json'.format(Tname), data,Role) |
| 57 | + |
| 58 | +#T2:Create L3 BD,Subnet and Scope of the subnet. |
| 59 | + def bd_Subnet(self,Tname,BD,Subnet,Scope): |
| 60 | + Role='T2: Creating the L3 BD:{} with Anycast GW:{} on Scope:{}'.format(BD,Subnet,Scope) |
| 61 | + scope_list = ['public', 'shared','private'] |
| 62 | + if Scope in scope_list: |
| 63 | + data = {"fvSubnet": {"attributes": {"dn": "uni/tn-"+Tname+"/BD-"+BD+"_bd/subnet-["+Subnet+"]", "ctrl": "", "ip": Subnet, "rn": "subnet-["+Subnet+"]","scope":Scope, "status": "created,modified"},"children": [] } } |
| 64 | + else: |
| 65 | + data = {"fvSubnet": {"attributes": {"dn": "uni/tn-"+Tname+"/BD-"+BD+"_bd/subnet-["+Subnet+"]", "ctrl": "", "ip": Subnet, "rn": "subnet-["+Subnet+"]", "status": "created"},"children": [] } } |
| 66 | + return self.POST('/api/node/mo/uni/tn-{}/BD-{}_bd/subnet-[{}].json'.format(Tname,BD,Subnet), data,Role) |
| 67 | + |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | +#T19 Create L2 BD domain. |
| 73 | + def bd_L2(self,Tname,BD): |
| 74 | + Role="T19:{}-L2 BD domain(Flood/Unicast disabled):".format(BD) |
| 75 | + data = {"fvBD":{"attributes":{"dn":"uni/tn-"+Tname+"/BD-"+BD+"_bd","arpFlood":"true","unicastRoute":"false","unkMacUcastAct":"flood","status":"created,modified"},"children":[]}} |
| 76 | + return self.POST('/api/mo/uni/tn-{}/BD-{}_bd.json'.format(Tname,BD), data,Role) |
| 77 | + |
| 78 | +#T20 Create L3OUT Primary IP & Secondary IP |
| 79 | + def L3OUT_Config(self,Tname,VRF,L3OUT_NAME,L3OUT_DOMAIN,L3OUT_SUBNETS,Node_ID,PORT,SVI_VLAN,SVI_IP,MTU,ROUTER_ID): |
| 80 | + Role="T20:{}-Configure the L3OUT:".format(L3OUT_NAME) |
| 81 | + data = {"l3extOut":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME,"name":L3OUT_NAME,"rn":"out-"+L3OUT_NAME,"status":"created,modified"}, |
| 82 | + "children":[ |
| 83 | + #{"l3extInstP":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/instP-"+L3OUT_SUBNETS,"name":L3OUT_SUBNETS,"rn":"instP-"+L3OUT_SUBNETS,"status":"created,modified"},"children":[{"l3extSubnet":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/instP-"+L3OUT_SUBNETS+"/extsubnet-[0.0.0.0/0]","ip":"0.0.0.0/0","status":"created,modified"},"children":[]}}]}}, |
| 84 | + {"l3extInstP":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/instP-"+L3OUT_SUBNETS,"name":L3OUT_SUBNETS,"rn":"instP-"+L3OUT_SUBNETS,"status":"created,modified"},"children":[]}}, |
| 85 | + {"l3extLNodeP":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/lnodep-"+L3OUT_NAME+"_nodeProfile","name":L3OUT_NAME+"_nodeProfile","status":"created,modified"}, |
| 86 | + "children":[{"l3extLIfP":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/lnodep-"+L3OUT_NAME+"_nodeProfile/lifp-"+L3OUT_NAME+"_interfaceProfile","name":L3OUT_NAME+"_interfaceProfile","status":"created,modified"}, |
| 87 | + "children":[{"l3extRsPathL3OutAtt":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/lnodep-"+L3OUT_NAME+"_nodeProfile/lifp-"+L3OUT_NAME+"_interfaceProfile/rspathL3OutAtt-[topology/pod-1/paths-"+Node_ID+"/pathep-[eth"+PORT+"]]","tDn":"topology/pod-1/paths-"+Node_ID+"/pathep-[eth"+PORT+"]","addr":SVI_IP,"ifInstT":"ext-svi","mtu":MTU,"encap":"vlan-"+SVI_VLAN,"status":"created,modified","rn":"rspathL3OutAtt-[topology/pod-1/paths-"+Node_ID+"/pathep-[eth"+PORT+"]]"}, |
| 88 | + "children":[] }}]}}, |
| 89 | + {"l3extRsNodeL3OutAtt":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/lnodep-"+L3OUT_NAME+"_nodeProfile/rsnodeL3OutAtt-[topology/pod-1/node-"+Node_ID+"]","tDn":"topology/pod-1/node-"+Node_ID,"rtrId":ROUTER_ID,"rtrIdLoopBack":"false","status":"created,modified"}, |
| 90 | + "children":[]}}]}}, |
| 91 | + {"l3extRsEctx":{"attributes":{"tnFvCtxName":VRF+"_vrf","status":"created,modified"},"children":[]}},{"l3extRsL3DomAtt":{"attributes":{"tDn":"uni/l3dom-"+L3OUT_DOMAIN,"status":"created,modified"},"children":[]}}]}} |
| 92 | + return self.POST('/api/node/mo/uni/tn-{}/out-{}.json'.format(Tname,L3OUT_NAME), data,Role) |
| 93 | + |
| 94 | + def L3OUT_GW_IP(self,Tname,L3OUT_NAME,Node_ID,PORT,SVI_GW_IP): |
| 95 | + Role="T21:{}-L3OUT_Configure the secondary IP:".format(L3OUT_NAME) |
| 96 | + data = {"l3extIp":{"attributes":{"addr":SVI_GW_IP,"status":"created"},"children":[]}} |
| 97 | + return self.POST('/api/node/mo/uni/tn-{}/out-{}/lnodep-{}_nodeProfile/lifp-{}_interfaceProfile/rspathL3OutAtt-[topology/pod-1/paths-{}/pathep-[eth{}]].json'.format(Tname,L3OUT_NAME,L3OUT_NAME,L3OUT_NAME,Node_ID,PORT),data,Role) |
| 98 | + |
| 99 | +#T22 Create Static L3OUT |
| 100 | + def L3OUT_Static(self,Tname,L3OUT_NAME,Node_ID,Dest_Network,Next_hop): |
| 101 | + Role= "T22:{}-L3 Static Route Configure Network {} as Next-hop:{}:".format(L3OUT_NAME,Dest_Network,Next_hop) |
| 102 | + data = {"ipRouteP":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/lnodep-"+L3OUT_NAME+"_nodeProfile/rsnodeL3OutAtt-[topology/pod-1/node-"+Node_ID+"]/rt-["+Dest_Network+"]","ip":Dest_Network,"rn":"rt-["+Dest_Network+"]","status":"created,modified"}, |
| 103 | + "children":[{"ipNexthopP":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/lnodep-"+L3OUT_NAME+"_nodeProfile/rsnodeL3OutAtt-[topology/pod-1/node-"+Node_ID+"]/rt-["+Dest_Network+"]/nh-["+Next_hop+"]","nhAddr":Next_hop,"rn":"nh-["+Next_hop+"]","status":"created,modified"},"children":[]}}]}} |
| 104 | + return self.POST('/api/node/mo/uni/tn-{}/out-{}/lnodep-{}_nodeProfile/rsnodeL3OutAtt-[topology/pod-1/node-{}]/rt-[{}].json'.format(Tname,L3OUT_NAME,L3OUT_NAME,Node_ID,Dest_Network), data,Role) |
| 105 | + #T23 Create Static L3OUT |
| 106 | + def L3OUT_Subnets(self,Tname,L3OUT_NAME,L3OUT_SUBNETS,Dest_Network): |
| 107 | + Role= "T23:{}-L3out Subnets for Network {}:".format(L3OUT_NAME,Dest_Network) |
| 108 | + Dest_Network_list = ['0.0.0.0/0'] |
| 109 | + if Dest_Network in Dest_Network_list: |
| 110 | + data= {"l3extSubnet":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/instP-"+L3OUT_SUBNETS+"/extsubnet-["+Dest_Network+"]","ip":Dest_Network,"scope":"import-security,shared-security,shared-rtctrl","aggregate":"shared-rtctrl","rn":"extsubnet-["+Dest_Network+"]","status":"created,modified"},"children":[]}} |
| 111 | + else: |
| 112 | + data= {"l3extSubnet":{"attributes":{"dn":"uni/tn-"+Tname+"/out-"+L3OUT_NAME+"/instP-"+L3OUT_SUBNETS+"/extsubnet-["+Dest_Network+"]","ip":Dest_Network,"scope":"import-security,shared-security,shared-rtctrl","aggregate":"","rn":"extsubnet-["+Dest_Network+"]","status":"created,modified"},"children":[]}} |
| 113 | + return self.POST('/api/node/mo/uni/tn-{}/out-{}/instP-{}/extsubnet-[{}].json'.format(Tname,L3OUT_NAME,L3OUT_SUBNETS,Dest_Network),data,Role) |
| 114 | + |
| 115 | +#T24 Create Contract |
| 116 | + def Contract(self,Tname,Contract_name,Contract_sub,Scope): |
| 117 | + Role= "T24:Create Contract {} under Tenant {}:".format(Contract_name,Tname) |
| 118 | + scope_list = ['tenant','global'] |
| 119 | + if Scope in scope_list: |
| 120 | + data ={"vzBrCP":{"attributes":{"dn":"uni/tn-"+Tname+"/brc-"+Contract_name,"name":Contract_name,"scope":Scope,"rn":"brc-"+Contract_name,"status":"created,modified"}, |
| 121 | + "children":[{"vzSubj":{"attributes":{"dn":"uni/tn-"+Tname+"/brc-"+Contract_name+"/subj-"+Contract_sub+"","name":Contract_sub,"rn":"subj-"+Contract_sub,"status":"created,modified"}, |
| 122 | + "children":[{"vzRsSubjFiltAtt":{"attributes":{"status":"created,modified","tnVzFilterName":"default","directives":"none"},"children":[]}}]}}]}} |
| 123 | + else: |
| 124 | + data ={"vzBrCP":{"attributes":{"dn":"uni/tn-"+Tname+"/brc-"+Contract_name,"name":Contract_name,"rn":"brc-"+Contract_name,"status":"created,modified"}, |
| 125 | + "children":[{"vzSubj":{"attributes":{"dn":"uni/tn-"+Tname+"/brc-"+Contract_name+"/subj-"+Contract_sub+"","name":Contract_sub,"rn":"subj-"+Contract_sub,"status":"created,modified"}, |
| 126 | + "children":[{"vzRsSubjFiltAtt":{"attributes":{"status":"created,modified","tnVzFilterName":"default","directives":"none"},"children":[]}}]}}]}} |
| 127 | + return self.POST('/api/node/mo/uni/tn-{}/brc-{}.json'.format(Tname,Contract_name),data,Role) |
| 128 | + |
| 129 | +#T25 Provider Contract |
| 130 | + def Prov_Contract(self,Tname,L3OUT_NAME,Contract_name,P_EPG): |
| 131 | + Role= "T25:Apply the Provider Contract under EPG {}:".format(P_EPG) |
| 132 | + data={"fvRsProv":{"attributes":{"tnVzBrCPName":Contract_name,"status":"created,modified"},"children":[]}} |
| 133 | + return self.POST('/api/node/mo/uni/tn-{}/out-{}/instP-{}.json'.format(Tname,L3OUT_NAME,P_EPG),data,Role) |
| 134 | +#T25 Consumer Contract |
| 135 | + def Cons_Contract(self,Tname,Contract_name,APP,C_EPG): |
| 136 | + #print ("Calling Cons_Contract") |
| 137 | + Role= "T26:Apply the Consumer Contract under EPG {}:".format(C_EPG) |
| 138 | + data ={"fvRsCons":{"attributes":{"tnVzBrCPName":Contract_name,"status":"created,modified"},"children":[]}} |
| 139 | + return self.POST('/api/node/mo/uni/tn-{}/ap-{}/epg-{}.json'.format(Tname,APP,C_EPG),data,Role) |
| 140 | + |
| 141 | +def main(): |
| 142 | + |
| 143 | + import apic_cfg as cfg |
| 144 | + client = Client(cfg.host, cfg.usr, cfg.pwd) |
| 145 | + print("\n Authenication in to the controller: {}\n".format(cfg.host)) |
| 146 | + client.login() |
| 147 | + |
| 148 | +if __name__ == '__main__': |
| 149 | + main() |
0 commit comments