def main(): """ Main execution routine :return: None """ creds = Credentials('apic') creds.add_argument('--tenant', help='The name of Tenant') creds.add_argument('--app', help='The name of ApplicationProfile') creds.add_argument('--bd', help='The name of BridgeDomain') creds.add_argument('--epg', help='The name of EPG') creds.add_argument('--json', const='false', nargs='?', help='Json output only') args = creds.get() session = Session(args.url, args.login, args.password) session.login() tenant = Tenant(args.tenant) app = AppProfile(args.app, tenant) bd = BridgeDomain(args.bd, tenant) epg = EPG(args.epg, app) epg.add_bd(bd) if args.json: print(tenant.get_json()) else: resp = session.push_to_apic(tenant.get_url(), tenant.get_json()) if not resp.ok: print('%% Error: Could not push configuration to APIC') print(resp.text)
def main(): """ Main execution routine """ creds = Credentials('apic') args = creds.get() session = Session(args.url, args.login, args.password) session.login() tenant = Tenant('ATX16_l3Out') context = Context('vrf', tenant) outside_l3 = OutsideL3('out-1', tenant) outside_l3.add_context(context) phyif = Interface('eth', '1', '104', '1', '41') phyif.speed = '1G' l2if = L2Interface('eth 1/104/1/41', 'vlan', '1330') l2if.attach(phyif) l3if = L3Interface('l3if') #l3if.set_l3if_type('l3-port') l3if.set_l3if_type('sub-interface') l3if.set_mtu('1500') l3if.set_addr('1.1.1.2/30') l3if.add_context(context) l3if.attach(l2if) rtr = OSPFRouter('rtr-1') rtr.set_router_id('23.23.23.23') rtr.set_node_id('101') ifpol = OSPFInterfacePolicy('myospf-pol', tenant) ifpol.set_nw_type('p2p') ospfif = OSPFInterface('ospfif-1', router=rtr, area_id='1') ospfif.set_area_type('nssa') ospfif.auth_key = 'password' ospfif.int_policy_name = ifpol.name ospfif.auth_keyid = '1' ospfif.auth_type = 'simple' tenant.attach(ospfif) ospfif.networks.append('55.5.5.0/24') ospfif.attach(l3if) contract1 = Contract('contract-1') outside_epg = OutsideEPG('outepg', outside_l3) outside_epg.provide(contract1) contract2 = Contract('contract-2') outside_epg.consume(contract2) outside_l3.attach(ospfif) print(tenant.get_json()) resp = session.push_to_apic(tenant.get_url(), tenant.get_json()) if not resp.ok: print('%% Error: Could not push configuration to APIC') print(resp.text)
def main(): """ Main execution routine """ creds = Credentials('apic') args = creds.get() session = Session(args.url, args.login, args.password) session.login() tenant = Tenant('Cisco-Demo') context = Context('ctx1', tenant) outside_l3 = OutsideL3('out-1', tenant) outside_l3.add_context(context) phyif = Interface('eth', '1', '101', '1', '46') phyif.speed = '1G' l2if = L2Interface('eth 1/101/1/46', 'vlan', '1') l2if.attach(phyif) l3if = L3Interface('l3if') l3if.set_l3if_type('l3-port') l3if.set_mtu('1500') l3if.set_addr('1.1.1.2/30') l3if.add_context(context) l3if.attach(l2if) rtr = OSPFRouter('rtr-1') rtr.set_router_id('23.23.23.23') rtr.set_node_id('101') ifpol = OSPFInterfacePolicy('myospf-pol', tenant) ifpol.set_nw_type('p2p') ospfif = OSPFInterface('ospfif-1', router=rtr, area_id='1') ospfif.set_area_type('nssa') ospfif.auth_key = 'password' ospfif.int_policy_name = ifpol.name ospfif.auth_keyid = '1' ospfif.auth_type = 'simple' tenant.attach(ospfif) ospfif.networks.append('55.5.5.0/24') ospfif.attach(l3if) contract1 = Contract('contract-1') outside_epg = OutsideEPG('outepg', outside_l3) outside_epg.provide(contract1) contract2 = Contract('contract-2') outside_epg.consume(contract2) outside_l3.attach(ospfif) print(tenant.get_json()) resp = session.push_to_apic(tenant.get_url(), tenant.get_json()) if not resp.ok: print('%% Error: Could not push configuration to APIC') print(resp.text)
def main(): """ Main execution routine :return: None """ creds = Credentials('apic') creds.add_argument('--tenant', help='The name of Tenant') creds.add_argument('--vrf', help='The name of VRF') creds.add_argument('--bd', help='The name of BridgeDomain') creds.add_argument('--address', help='Subnet IPv4 Address') creds.add_argument('--scope', help='The scope of subnet ("public", "private", "shared", "public,shared", "private,shared", "shared,public", "shared,private")') creds.add_argument('--json', const='false', nargs='?', help='Json output only') args = creds.get() session = Session(args.url, args.login, args.password) session.login() tenant = Tenant(args.tenant) vrf = Context(args.vrf) bd = BridgeDomain(args.bd, tenant) bd.add_context(vrf) if args.address is None: bd.set_arp_flood('yes') bd.set_unicast_route('no') else: bd.set_arp_flood('no') bd.set_unicast_route('yes') subnet = Subnet('', bd) subnet.addr = args.address if args.scope is None: subnet.set_scope("private") else: subnet.set_scope(args.scope) if args.json: print(tenant.get_json()) else: resp = session.push_to_apic(tenant.get_url(), tenant.get_json()) if not resp.ok: print('%% Error: Could not push configuration to APIC') print(resp.text)
def main(): """ Main execution routine :return: None """ creds = Credentials('apic') args = creds.get() session = Session(args.url, args.login, args.password) session.login() tenant = Tenant('cisco') context = Context('ctx1', tenant) outside_l3 = OutsideL3('out-1', tenant) phyif = Interface('eth', '1', '101', '1', '46') phyif.speed = '1G' l2if = L2Interface('eth 1/101/1/46', 'vlan', '1') l2if.attach(phyif) l3if = L3Interface('l3if') l3if.set_l3if_type('l3-port') l3if.set_addr('1.1.1.2/30') l3if.add_context(context) l3if.attach(l2if) bgpif = BGPSession('test', peer_ip='1.1.1.1', node_id='101') bgpif.router_id = '172.1.1.1' bgpif.attach(l3if) bgpif.options = 'send-ext-com' bgpif.networks.append('0.0.0.0/0') contract1 = Contract('icmp') outside_epg = OutsideEPG('outepg', outside_l3) outside_epg.provide(contract1) outside_l3.add_context(context) outside_epg.consume(contract1) outside_l3.attach(bgpif) bgp_json = bgpif.get_json() resp = session.push_to_apic(tenant.get_url(), tenant.get_json()) if not resp.ok: print('%% Error: Could not push configuration to APIC') print(resp.text)
class Monitor(threading.Thread): """ Thread responsible for monitoring EPG-to-Contract relations. """ def __init__(self, cdb): threading.Thread.__init__(self) self.cdb = cdb self._monitor_frequency = 1 self._exit = False self._relation_subscriptions = [] self._inheritance_tag_subscriptions = [] self._subnet_subscriptions = [] self._relations = RelationDB() self._inheritance_tags = TagDB() self._subnets = SubnetDB() self._old_relations = {} self.apic = None def exit(self): """ Indicate that the thread should exit. """ self._exit = True def subscribe(self, policy): query_url = '' epg = policy.epg epg_data = (epg.tenant, epg.epg_container_name, epg.name) if epg.epg_container_type == 'app': query_url = '/api/mo/uni/tn-%s/ap-%s/epg-%s.json' % epg_data elif epg.epg_container_type == 'l3out': subnet_query_url = '/api/mo/uni/tn-%s/out-%s/instP-%s.json' % epg_data subnet_query_url += '?query-target=subtree&target-subtree-class=l3extSubnet' subnet_query_url += '&subscription=yes' if subnet_query_url not in self._subnet_subscriptions: self.apic.subscribe(subnet_query_url) self._subnet_subscriptions.append(subnet_query_url) query_url = '/api/mo/uni/tn-%s/out-%s/instP-%s.json' % epg_data elif epg.epg_container_type == 'l2out': query_url = '/api/mo/uni/tn-%s/l2out-%s/instP-%s.json' % epg_data query_url += '?query-target=subtree&target-subtree-class=fvRsProv,fvRsCons,fvRsProtBy,fvRsConsIf' query_url += '&subscription=yes' if query_url not in self._relation_subscriptions: self.apic.subscribe(query_url) self._relation_subscriptions.append(query_url) def connect_to_apic(self): logging.debug('Connecting to APIC...') # Connect to APIC apic_config = self.cdb.get_apic_config() url = apic_config.ip_address if apic_config.use_https: url = 'https://' + url else: url = 'http://' + url if self.apic is not None: logging.debug('APIC is previously connected') self.apic = Session(url, apic_config.user_name, apic_config.password) resp = self.apic.login() # TODO: need to clear out the databases first assert len(self._inheritance_tags.db) == 0 # Get all of the subnets query_url = '/api/mo/uni.json?query-target=subtree&target-subtree-class=l3extSubnet' subnets = self.apic.get(query_url) for subnet in subnets.json()['imdata']: subnet_event = SubnetEvent(subnet) self._subnets.store_subnet_event(subnet_event) # Get all of the inherited relations tag_query_url = '/api/class/tagInst.json?query-target-filter=wcard(tagInst.name,"inherited:")' tags = self.apic.get(tag_query_url) for tag in tags.json()['imdata']: tag_event = TagEvent(tag) # Create a database entry for the inherited relations self._inheritance_tags.store_tag(tag_event) self._old_relations = self._inheritance_tags.get_relations() # Get all of the relations. We need this to track relations that are already present # i.e. configured but not through inheritance so that we can tell the difference query_url = '/api/mo/uni.json?query-target=subtree&target-subtree-class=fvRsProv,fvRsCons,fvRsProtBy,fvRsConsIf' relations = self.apic.get(query_url) for relation in relations.json()['imdata']: # Skip any in-band and out-of-band interfaces if '/mgmtp-' in relation[relation.keys()[0]]['attributes']['dn']: continue self._relations.store_relation(RelationEvent(relation)) # Get all of the policies that are inherited from but not inheriting parent_only_policies = [] for policy in self.cdb.get_inheritance_policies(): inherited_from = False if policy.allowed and not policy.enabled: for child_policy in self.cdb.get_inheritance_policies(): if child_policy.has_inherit_from( ) and child_policy.inherit_from == policy.epg: inherited_from = True if inherited_from: parent_only_policies.append(policy) # Issue all of the subscriptions for policy in self.cdb.get_inheritance_policies(): self.subscribe(policy) for parent_only_policy in parent_only_policies: self.subscribe(parent_only_policy) tag_query_url += '&subscription=yes' self.apic.subscribe(tag_query_url) self._inheritance_tag_subscriptions.append(tag_query_url) def _does_tenant_have_contract(self, tenant_name, contract_name, contract_type='brc'): logging.debug('tenant: %s contract: %s contract_type: %s', tenant_name, contract_name, contract_type) query_url = '/api/mo/uni/tn-%s/%s-%s.json' % ( tenant_name, contract_type, contract_name) resp = self.apic.get(query_url) return resp.ok and int(resp.json()['totalCount']) > 0 def does_tenant_have_contract_if(self, tenant_name, contract_if_name): return self._does_tenant_have_contract(tenant_name, contract_if_name, contract_type='cif') def does_tenant_have_contract(self, tenant_name, contract_name): return self._does_tenant_have_contract(tenant_name, contract_name) def _add_inherited_relation(self, tenants, epg, relation, deleted=False): tenant_found = False # Find the tenant. Add if necessary for tenant in tenants: if tenant.name == epg.tenant: tenant_found = True break if not tenant_found: tenant = Tenant(epg.tenant) tenants.append(tenant) # Find the EPG Container. Add if necessary if epg.is_l3out(): epg_container_class = OutsideL3 else: epg_container_class = AppProfile epg_containers = tenant.get_children(only_class=epg_container_class) epg_container_found = False for epg_container in epg_containers: if epg_container.name == epg.epg_container_name: epg_container_found = True break if not epg_container_found: epg_container = epg_container_class(epg.epg_container_name, tenant) # Find the EPG. Add if necessary if epg.is_l3out(): epg_class = OutsideEPG else: epg_class = EPG epgs = tenant.get_children(only_class=epg_class) epg_found = False for tenant_epg in epgs: if tenant_epg.name == epg.name: epg_found = True break if not epg_found: tenant_epg = epg_class(epg.name, epg_container) # Add the relation (relation_type, relation_name) = relation if relation_type == 'fvRsProv': contract = Contract(relation_name, tenant) tenant_epg.provide(contract) if deleted: tenant_epg.provide(contract) tenant_epg.dont_provide(contract) elif relation_type == 'fvRsCons': contract = Contract(relation_name, tenant) tenant_epg.consume(contract) if deleted: tenant_epg.consume(contract) tenant_epg.dont_consume(contract) elif relation_type == 'fvRsConsIf': contract_interface = ContractInterface(relation_name, tenant) tenant_epg.consume_cif(contract_interface) if deleted: tenant_epg.consume_cif(contract_interface) tenant_epg.dont_consume_cif(contract_interface) elif relation_type == 'fvRsProtBy': taboo = Taboo(relation_name, tenant) tenant_epg.protect(taboo) if deleted: tenant_epg.protect(taboo) tenant_epg.dont_protect(taboo) tenant_epg.add_tag('inherited:%s:%s' % (relation_type, relation_name)) if deleted: tenant_epg.delete_tag('inherited:%s:%s' % (relation_type, relation_name)) return tenants def add_inherited_relation(self, tenants, epg, relation): return self._add_inherited_relation(tenants, epg, relation) def remove_inherited_relation(self, tenants, epg, relation): return self._add_inherited_relation(tenants, epg, relation, deleted=True) def _calculate_relations_for_l3out_policy(self, inheritance_policy): logging.debug('_calculate_relations_for_l3out_policy: %s', inheritance_policy) # Get all of the EPGs covering this policy's EPG's subnets covering_epgs = self._subnets.get_all_covering_epgs( inheritance_policy.epg) # Remove any EPGs that are not allowed to be inherited for covering_epg in covering_epgs: if not self.cdb.is_inheritance_allowed(covering_epg): covering_epgs.remove(covering_epg) # Get all of the relations for the remaining covering EPGs relations = [] for covering_epg in covering_epgs: epg_relations = self._relations.get_relations_for_epg(covering_epg) for epg_relation in epg_relations: if epg_relation not in relations: relations.append(epg_relation) logging.debug('Relations to be inherited: %s', relations) # Need to add any configured relations configured_relations = self._relations.get_relations_for_epg( inheritance_policy.epg) for configured_relation in configured_relations: if configured_relation not in relations: logging.debug('Adding configured relation: %s', configured_relation) if not self._inheritance_tags.is_inherited( inheritance_policy.epg, configured_relation): relations.append(configured_relation) return relations def _calculate_relations_for_app_policy(self, inheritance_policy): logging.debug('policy: %s', inheritance_policy) relations = [] # Check if this policy is even inheritance enabled if not inheritance_policy.has_inherit_from(): logging.warning('EPG is not inheriting from a parent EPG') return relations # Get the EPG that this policy is inheriting from parent_epg = inheritance_policy.inherit_from # Is inheritance allowed on that EPG ? if not self.cdb.is_inheritance_allowed(parent_epg): logging.warning('Parent EPG policy does not allow inheritance') return relations # Get the relations belonging to that EPG return self._relations.get_relations_for_epg(parent_epg) def calculate_relations(self): relations = {} for inheritance_policy in self.cdb.get_inheritance_policies(): if not inheritance_policy.enabled: continue if inheritance_policy.epg.is_l3out(): epg_relations = self._calculate_relations_for_l3out_policy( inheritance_policy) else: # TODO: may eventually need to process l2out epg_relations = self._calculate_relations_for_app_policy( inheritance_policy) relations[inheritance_policy.epg.get_json()] = epg_relations return relations def process_relation_event(self, event): logging.debug('process_event EVENT: %s', event.event) self._relations.store_relation(event) def process_subnet_event(self, event): logging.debug('Received subnet event: %s', event) # Store the subnet in the SubnetDB self._subnets.store_subnet_event(event) def process_inheritance_tag_event(self, event): logging.debug('Received subnet event: %s', event) # Store the tag in the TagDB self._inheritance_tags.store_tag(event) def _process_events(self, old_relations): while self.apic is None: pass if old_relations is None: old_relations = self._inheritance_tags.get_relations() # Check for any tag events for subscription in self._inheritance_tag_subscriptions: while self.apic.has_events(subscription): event = TagEvent( self.apic.get_event(subscription)['imdata'][0]) self.process_inheritance_tag_event(event) # Check for relation events for subscription in self._relation_subscriptions: while self.apic.has_events(subscription): event = RelationEvent( self.apic.get_event(subscription)['imdata'][0]) self.process_relation_event(event) # Check for subnet events for subscription in self._subnet_subscriptions: while self.apic.has_events(subscription): event = SubnetEvent( self.apic.get_event(subscription)['imdata'][0]) self.process_subnet_event(event) # Calculate the new set of relations new_relations = self.calculate_relations() for old_epg in old_relations: logging.debug(old_epg) for new_epg in new_relations: logging.debug(new_epg) # Compare the old and the new relations for changes tenants = [] for new_epg in new_relations: if new_epg not in old_relations: # New EPG, so we need to add all of the relations for new_relation in new_relations[new_epg]: if self._inheritance_tags.is_inherited( EPGPolicy(json.loads(new_epg)), new_relation): # If it's inherited, but we don't have the relation. We are likely coming up and not populated # the old_relations yet pass # If just configured, we will have a relationDB entry. Otherwise, we need to inherit it if self._relations.has_relation_for_epg( EPGPolicy(json.loads(new_epg)), new_relation): continue tenants = self.add_inherited_relation( tenants, EPGPolicy(json.loads(new_epg)), new_relation) else: # Handle any new added relations for new_relation in new_relations[new_epg]: if new_relation not in old_relations[new_epg]: if self._relations.has_relation_for_epg( EPGPolicy(json.loads(new_epg)), new_relation): continue tenants = self.add_inherited_relation( tenants, EPGPolicy(json.loads(new_epg)), new_relation) # Handle any deleted relations for old_relation in old_relations[new_epg]: if old_relation not in new_relations[new_epg]: if self._inheritance_tags.is_inherited( EPGPolicy(json.loads(new_epg)), old_relation): tenants = self.remove_inherited_relation( tenants, EPGPolicy(json.loads(new_epg)), old_relation) else: # Must have been configured and manually deleted pass for old_epg in old_relations: if old_epg not in new_relations: for old_relation in old_relations[old_epg]: if self._inheritance_tags.is_inherited( EPGPolicy(json.loads(old_epg)), old_relation): tenants = self.remove_inherited_relation( tenants, EPGPolicy(json.loads(old_epg)), old_relation) # Push the necessary config to the APIC for tenant in tenants: tenant_json = tenant.get_json() # Check that the tenant actually has the contracts since they may actually be tenant common contracts. # If tenant common is used, we need to clean up the tenant JSON to not create an empty contract within # this tenant for child in tenant_json['fvTenant']['children']: if 'vzBrCP' in child: if not self.does_tenant_have_contract( tenant.name, child['vzBrCP']['attributes']['name']): tenant_json['fvTenant']['children'].remove(child) elif 'vzCPIf' in child: if not self.does_tenant_have_contract_if( tenant.name, child['vzCPIf']['attributes']['name']): tenant_json['fvTenant']['children'].remove(child) logging.debug('Pushing tenant configuration to the APIC: %s', tenant_json) resp = self.apic.push_to_apic(tenant.get_url(), tenant_json) if resp.ok: logging.debug('Pushed to APIC successfully') else: logging.error('Error pushing to APIC', resp.text) return new_relations def run(self): loop_count = 0 accelerated_cleanup_done = False while not self._exit: time.sleep(self._monitor_frequency) self._old_relations = self._process_events(self._old_relations)
from acitoolkit.acitoolkit import Session, Credentials, Tenant creds = Credentials('apic', 'Opis co skrypt robi.') args = creds.get() session = Session(args.url, args.login, args.password) resp = session.login() if not resp.ok: print 'Could not login to APIC' tenant = Tenant('mytenat') resp = session.push_to_apic(tenant.get_url(), tenant.get_json()) if not resp.ok: print 'Could not push configuration to APIC' print resp.text tenants = Tenant.get(session) for tenant in tenants: print tenant.name session.close()
sn = existing_tenants[t]['bds'][b]['subnets'][s] # Mark the subnet as deleted sn.mark_as_deleted() # Add the corresponding subnet to the BD so # we can push it with the deleted status bd.add_child(sn) else: print(" |_ Existing Subnet %s/%s/%s will be kept intact because no '--remove yes' was supplied." % (t,b,s)) if args.commit == True: for tenant_name in tenants_modified: output("[+] Pushing configuration for Tenant '%s'" % tenant_name) tenant = tenants_modified[tenant_name] debug(tenant.get_json()) r = session.push_to_apic(tenant.get_url(), data=tenant.get_json()) if r.status_code>299: try: resp_data = json.loads(r.text) errmsg = resp_data['imdata'][0]['error']['attributes']['text'] except: errmsg = r.text fatal("[E] ERROR: %s" % errmsg) else: output("[+] Configuration for Tenant '%s' pushed successfully" % tenant_name) else: output("[+] No configuration pushed because '--commit yes' not supplied.") sys.exit(0)
class Monitor(threading.Thread): """ Thread responsible for monitoring EPG-to-Contract relations. """ def __init__(self, cdb): threading.Thread.__init__(self) self.cdb = cdb self._monitor_frequency = 1 self._exit = False self._relation_subscriptions = [] self._inheritance_tag_subscriptions = [] self._subnet_subscriptions = [] self._relations = RelationDB() self._inheritance_tags = TagDB() self._subnets = SubnetDB() self._old_relations = {} self.apic = None def exit(self): """ Indicate that the thread should exit. """ self._exit = True def subscribe(self, policy): query_url = '' epg = policy.epg epg_data = (epg.tenant, epg.epg_container_name, epg.name) if epg.epg_container_type == 'app': query_url = '/api/mo/uni/tn-%s/ap-%s/epg-%s.json' % epg_data elif epg.epg_container_type == 'l3out': subnet_query_url = '/api/mo/uni/tn-%s/out-%s/instP-%s.json' % epg_data subnet_query_url += '?query-target=subtree&target-subtree-class=l3extSubnet' subnet_query_url += '&subscription=yes' if subnet_query_url not in self._subnet_subscriptions: self.apic.subscribe(subnet_query_url) self._subnet_subscriptions.append(subnet_query_url) query_url = '/api/mo/uni/tn-%s/out-%s/instP-%s.json' % epg_data elif epg.epg_container_type == 'l2out': query_url = '/api/mo/uni/tn-%s/l2out-%s/instP-%s.json' % epg_data query_url += '?query-target=subtree&target-subtree-class=fvRsProv,fvRsCons,fvRsProtBy,fvRsConsIf' query_url += '&subscription=yes' if query_url not in self._relation_subscriptions: self.apic.subscribe(query_url) self._relation_subscriptions.append(query_url) def connect_to_apic(self): logging.debug('Connecting to APIC...') # Connect to APIC apic_config = self.cdb.get_apic_config() url = apic_config.ip_address if apic_config.use_https: url = 'https://' + url else: url = 'http://' + url if self.apic is not None: logging.debug('APIC is previously connected') self.apic = Session(url, apic_config.user_name, apic_config.password) resp = self.apic.login() # TODO: need to clear out the databases first assert len(self._inheritance_tags.db) == 0 # Get all of the subnets query_url = '/api/mo/uni.json?query-target=subtree&target-subtree-class=l3extSubnet' subnets = self.apic.get(query_url) for subnet in subnets.json()['imdata']: subnet_event = SubnetEvent(subnet) self._subnets.store_subnet_event(subnet_event) # Get all of the inherited relations tag_query_url = '/api/class/tagInst.json?query-target-filter=wcard(tagInst.name,"inherited:")' tags = self.apic.get(tag_query_url) for tag in tags.json()['imdata']: tag_event = TagEvent(tag) # Create a database entry for the inherited relations self._inheritance_tags.store_tag(tag_event) self._old_relations = self._inheritance_tags.get_relations() # Get all of the relations. We need this to track relations that are already present # i.e. configured but not through inheritance so that we can tell the difference query_url = '/api/mo/uni.json?query-target=subtree&target-subtree-class=fvRsProv,fvRsCons,fvRsProtBy,fvRsConsIf' relations = self.apic.get(query_url) for relation in relations.json()['imdata']: # Skip any in-band and out-of-band interfaces if '/mgmtp-' in relation[relation.keys()[0]]['attributes']['dn']: continue self._relations.store_relation(RelationEvent(relation)) # Get all of the policies that are inherited from but not inheriting parent_only_policies = [] for policy in self.cdb.get_inheritance_policies(): inherited_from = False if policy.allowed and not policy.enabled: for child_policy in self.cdb.get_inheritance_policies(): if child_policy.has_inherit_from() and child_policy.inherit_from == policy.epg: inherited_from = True if inherited_from: parent_only_policies.append(policy) # Issue all of the subscriptions for policy in self.cdb.get_inheritance_policies(): self.subscribe(policy) for parent_only_policy in parent_only_policies: self.subscribe(parent_only_policy) tag_query_url += '&subscription=yes' self.apic.subscribe(tag_query_url) self._inheritance_tag_subscriptions.append(tag_query_url) def _does_tenant_have_contract(self, tenant_name, contract_name, contract_type='brc'): logging.debug('tenant: %s contract: %s contract_type: %s', tenant_name, contract_name, contract_type) query_url = '/api/mo/uni/tn-%s/%s-%s.json' % (tenant_name, contract_type, contract_name) resp = self.apic.get(query_url) return resp.ok and int(resp.json()['totalCount']) > 0 def does_tenant_have_contract_if(self, tenant_name, contract_if_name): return self._does_tenant_have_contract(tenant_name, contract_if_name, contract_type='cif') def does_tenant_have_contract(self, tenant_name, contract_name): return self._does_tenant_have_contract(tenant_name, contract_name) def _add_inherited_relation(self, tenants, epg, relation, deleted=False): tenant_found = False # Find the tenant. Add if necessary for tenant in tenants: if tenant.name == epg.tenant: tenant_found = True break if not tenant_found: tenant = Tenant(epg.tenant) tenants.append(tenant) # Find the EPG Container. Add if necessary if epg.is_l3out(): epg_container_class = OutsideL3 else: epg_container_class = AppProfile epg_containers = tenant.get_children(only_class=epg_container_class) epg_container_found = False for epg_container in epg_containers: if epg_container.name == epg.epg_container_name: epg_container_found = True break if not epg_container_found: epg_container = epg_container_class(epg.epg_container_name, tenant) # Find the EPG. Add if necessary if epg.is_l3out(): epg_class = OutsideEPG else: epg_class = EPG epgs = tenant.get_children(only_class=epg_class) epg_found = False for tenant_epg in epgs: if tenant_epg.name == epg.name: epg_found = True break if not epg_found: tenant_epg = epg_class(epg.name, epg_container) # Add the relation (relation_type, relation_name) = relation if relation_type == 'fvRsProv': contract = Contract(relation_name, tenant) tenant_epg.provide(contract) if deleted: tenant_epg.provide(contract) tenant_epg.dont_provide(contract) elif relation_type == 'fvRsCons': contract = Contract(relation_name, tenant) tenant_epg.consume(contract) if deleted: tenant_epg.consume(contract) tenant_epg.dont_consume(contract) elif relation_type == 'fvRsConsIf': contract_interface = ContractInterface(relation_name, tenant) tenant_epg.consume_cif(contract_interface) if deleted: tenant_epg.consume_cif(contract_interface) tenant_epg.dont_consume_cif(contract_interface) elif relation_type == 'fvRsProtBy': taboo = Taboo(relation_name, tenant) tenant_epg.protect(taboo) if deleted: tenant_epg.protect(taboo) tenant_epg.dont_protect(taboo) tenant_epg.add_tag('inherited:%s:%s' % (relation_type, relation_name)) if deleted: tenant_epg.delete_tag('inherited:%s:%s' % (relation_type, relation_name)) return tenants def add_inherited_relation(self, tenants, epg, relation): return self._add_inherited_relation(tenants, epg, relation) def remove_inherited_relation(self, tenants, epg, relation): return self._add_inherited_relation(tenants, epg, relation, deleted=True) def _calculate_relations_for_l3out_policy(self, inheritance_policy): logging.debug('_calculate_relations_for_l3out_policy: %s', inheritance_policy) # Get all of the EPGs covering this policy's EPG's subnets covering_epgs = self._subnets.get_all_covering_epgs(inheritance_policy.epg) # Remove any EPGs that are not allowed to be inherited for covering_epg in covering_epgs: if not self.cdb.is_inheritance_allowed(covering_epg): covering_epgs.remove(covering_epg) # Get all of the relations for the remaining covering EPGs relations = [] for covering_epg in covering_epgs: epg_relations = self._relations.get_relations_for_epg(covering_epg) for epg_relation in epg_relations: if epg_relation not in relations: relations.append(epg_relation) logging.debug('Relations to be inherited: %s', relations) # Need to add any configured relations configured_relations = self._relations.get_relations_for_epg(inheritance_policy.epg) for configured_relation in configured_relations: if configured_relation not in relations: logging.debug('Adding configured relation: %s', configured_relation) if not self._inheritance_tags.is_inherited(inheritance_policy.epg, configured_relation): relations.append(configured_relation) return relations def _calculate_relations_for_app_policy(self, inheritance_policy): logging.debug('policy: %s', inheritance_policy) relations = [] # Check if this policy is even inheritance enabled if not inheritance_policy.has_inherit_from(): logging.warning('EPG is not inheriting from a parent EPG') return relations # Get the EPG that this policy is inheriting from parent_epg = inheritance_policy.inherit_from # Is inheritance allowed on that EPG ? if not self.cdb.is_inheritance_allowed(parent_epg): logging.warning('Parent EPG policy does not allow inheritance') return relations # Get the relations belonging to that EPG return self._relations.get_relations_for_epg(parent_epg) def calculate_relations(self): relations = {} for inheritance_policy in self.cdb.get_inheritance_policies(): if not inheritance_policy.enabled: continue if inheritance_policy.epg.is_l3out(): epg_relations = self._calculate_relations_for_l3out_policy(inheritance_policy) else: # TODO: may eventually need to process l2out epg_relations = self._calculate_relations_for_app_policy(inheritance_policy) relations[inheritance_policy.epg.get_json()] = epg_relations return relations def process_relation_event(self, event): logging.debug('process_event EVENT: %s', event.event) self._relations.store_relation(event) def process_subnet_event(self, event): logging.debug('Received subnet event: %s', event) # Store the subnet in the SubnetDB self._subnets.store_subnet_event(event) def process_inheritance_tag_event(self, event): logging.debug('Received subnet event: %s', event) # Store the tag in the TagDB self._inheritance_tags.store_tag(event) def _process_events(self, old_relations): while self.apic is None: pass if old_relations is None: old_relations = self._inheritance_tags.get_relations() # Check for any tag events for subscription in self._inheritance_tag_subscriptions: while self.apic.has_events(subscription): event = TagEvent(self.apic.get_event(subscription)['imdata'][0]) self.process_inheritance_tag_event(event) # Check for relation events for subscription in self._relation_subscriptions: while self.apic.has_events(subscription): event = RelationEvent(self.apic.get_event(subscription)['imdata'][0]) self.process_relation_event(event) # Check for subnet events for subscription in self._subnet_subscriptions: while self.apic.has_events(subscription): event = SubnetEvent(self.apic.get_event(subscription)['imdata'][0]) self.process_subnet_event(event) # Calculate the new set of relations new_relations = self.calculate_relations() for old_epg in old_relations: logging.debug(old_epg) for new_epg in new_relations: logging.debug(new_epg) # Compare the old and the new relations for changes tenants = [] for new_epg in new_relations: if new_epg not in old_relations: # New EPG, so we need to add all of the relations for new_relation in new_relations[new_epg]: if self._inheritance_tags.is_inherited(EPGPolicy(json.loads(new_epg)), new_relation): # If it's inherited, but we don't have the relation. We are likely coming up and not populated # the old_relations yet pass # If just configured, we will have a relationDB entry. Otherwise, we need to inherit it if self._relations.has_relation_for_epg(EPGPolicy(json.loads(new_epg)), new_relation): continue tenants = self.add_inherited_relation(tenants, EPGPolicy(json.loads(new_epg)), new_relation) else: # Handle any new added relations for new_relation in new_relations[new_epg]: if new_relation not in old_relations[new_epg]: if self._relations.has_relation_for_epg(EPGPolicy(json.loads(new_epg)), new_relation): continue tenants = self.add_inherited_relation(tenants, EPGPolicy(json.loads(new_epg)), new_relation) # Handle any deleted relations for old_relation in old_relations[new_epg]: if old_relation not in new_relations[new_epg]: if self._inheritance_tags.is_inherited(EPGPolicy(json.loads(new_epg)), old_relation): tenants = self.remove_inherited_relation(tenants, EPGPolicy(json.loads(new_epg)), old_relation) else: # Must have been configured and manually deleted pass for old_epg in old_relations: if old_epg not in new_relations: for old_relation in old_relations[old_epg]: if self._inheritance_tags.is_inherited(EPGPolicy(json.loads(old_epg)), old_relation): tenants = self.remove_inherited_relation(tenants, EPGPolicy(json.loads(old_epg)), old_relation) # Push the necessary config to the APIC for tenant in tenants: tenant_json = tenant.get_json() # Check that the tenant actually has the contracts since they may actually be tenant common contracts. # If tenant common is used, we need to clean up the tenant JSON to not create an empty contract within # this tenant for child in tenant_json['fvTenant']['children']: if 'vzBrCP' in child: if not self.does_tenant_have_contract(tenant.name, child['vzBrCP']['attributes']['name']): tenant_json['fvTenant']['children'].remove(child) elif 'vzCPIf' in child: if not self.does_tenant_have_contract_if(tenant.name, child['vzCPIf']['attributes']['name']): tenant_json['fvTenant']['children'].remove(child) logging.debug('Pushing tenant configuration to the APIC: %s', tenant_json) resp = self.apic.push_to_apic(tenant.get_url(), tenant_json) if resp.ok: logging.debug('Pushed to APIC successfully') else: logging.error('Error pushing to APIC', resp.text) return new_relations def run(self): loop_count = 0 accelerated_cleanup_done = False while not self._exit: time.sleep(self._monitor_frequency) self._old_relations = self._process_events(self._old_relations)