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)
response = session.login() if response.ok is False: fatal(response.content) else: output("Successfully connected to %s" % args.url) # Retrieve the list of existing tenants tenants = Tenant.get(session) # Subscribe to each one urls=[] for tn in tenants: url = "/api/mo/uni/tn-%s.json?query-target=subtree&subscription=yes" % tn.name try: debug("Subscribing to '%s'" % url) session.subscribe(url, only_new=True) urls.append(url) except: error("Error creating subscription for tenant '%s'" % tn.name) # Also, subscribe to the Tenant class so we can create new subscriptions # if new tenants get created. tn_url = "/api/class/fvTenant.json?subscription=yes" session.subscribe(tn_url, only_new=True) # Now we loop forever, waiting for any events output("Waiting for events...") while(True): # Check status of existing tenants for url in urls:
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)
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.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 # 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) # 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']: 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 _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): pass 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 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 # 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() logging.debug('OLD_RELATIONS: %s %s', len(old_relations), old_relations) for old_epg in old_relations: logging.debug(old_epg) logging.debug('NEW_RELATIONS: %s %s', len(new_relations), new_relations) 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 #continue 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: resp = tenant.push_to_apic(self.apic) 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 old_relations = {} while not self._exit: time.sleep(self._monitor_frequency) old_relations = self._process_events(old_relations)
response = session.login() if response.ok is False: fatal(response.content) else: output("Successfully connected to %s" % args.url) # Retrieve the list of existing tenants tenants = Tenant.get(session) # Subscribe to each one urls = [] for tn in tenants: url = "/api/mo/uni/tn-%s.json?query-target=subtree&subscription=yes" % tn.name try: debug("Subscribing to '%s'" % url) session.subscribe(url, only_new=True) urls.append(url) except: error("Error creating subscription for tenant '%s'" % tn.name) # Also, subscribe to the Tenant class so we can create new subscriptions # if new tenants get created. tn_url = "/api/class/fvTenant.json?subscription=yes" session.subscribe(tn_url, only_new=True) # Now we loop forever, waiting for any events output("Waiting for events...") while (True): # Check status of existing tenants for url in urls: