def _retrieve_tenant_rn(self, data): if isinstance(data, dict): if data.keys()[0] == 'tagInst': # Retrieve tag parent dn = data.values()[0]['attributes']['dn'] decomposed = apic_client.DNManager().aci_decompose_dn_guess( dn, 'tagInst') parent_type = decomposed[1][-2][0] data = { parent_type: { 'attributes': { 'dn': apic_client.DNManager().build( decomposed[1][:-1])}}} data = self._aim_converter.convert([data]) data = data[0] if data else None if isinstance(data, resource.AciResourceBase): return tree_manager.AimHashTreeMaker().get_root_key(data)
def mock_get_data(inst, dn, **kwargs): # Expected kwargs: query_target [subtree], target_subtree_class try: inst._data_stash except Exception: inst._data_stash = {} dn_mgr = apic_client.DNManager() # Decompose the DN, remove the mo/ in front decomposed = decompose_aci_dn(dn[3:]) try: # Find the proper root node curr = copy.deepcopy(inst._data_stash[decomposed[0][1]])[0] for index, part in enumerate(decomposed[1:]): # Look at the current's children and find the proper node. if part[0] in AMBIGUOUS_TYPES: partial_dn = ( dn_mgr.build(decomposed[:index + 1]) + '/' + apic_client.ManagedObjectClass.mos_to_prefix[part[0]] + '-' + decomposed[index + 1][1]) else: partial_dn = dn_mgr.build(decomposed[:index + 2]) for child in curr.values()[0]['children']: if child.values()[0]['attributes']['dn'] == partial_dn: curr = child break else: raise KeyError # Curr is the looked up node. Look at the query params to filter the # result query_target = kwargs.get('query_target', 'self') if query_target == 'subtree': # Look at the target subtree class target_subtree_class = kwargs.get('target_subtree_class', '').split(',') if not target_subtree_class: # Return everything return _flat_result(curr) else: # Only return the expected objects return [ x for x in _flat_result(curr) if x.keys()[0] in target_subtree_class ] else: curr.values()[0].pop('children', []) return [curr] except KeyError: # Simulate 404 if 'fault' in dn: # non existing faults return empty data return [] raise apic_client.cexc.ApicResponseNotOk(request='get', status='404', reason='Not Found', err_text='Not Found', err_code='404')
def fv_rs_master_epg_converter(object_dict, otype, helper, source_identity_attributes, destination_identity_attributes, to_aim=True): result = [] if to_aim: res_dict = {} try: id = default_identity_converter(object_dict, otype, helper, to_aim=True) except apic_client.DNManager.InvalidNameFormat: return [] for index, attr in enumerate(destination_identity_attributes): res_dict[attr] = id[index] if object_dict.get('tDn'): master_id = apic_client.DNManager().aci_decompose_with_type( object_dict['tDn'], 'fvAEPg') res_dict['epg_contract_masters'] = [{ 'app_profile_name': master_id[1][1], 'name': master_id[2][1] }] result.append(default_to_resource(res_dict, helper, to_aim=True)) else: for p in object_dict['epg_contract_masters']: if p.get('app_profile_name') and p.get('name'): try: attr = [ object_dict.get('tenant_name'), p.get('app_profile_name'), p.get('name') ] path = apic_client.ManagedObjectClass('fvAEPg').dn(*attr) except Exception as e: LOG.error('Failed to make DN for %s with %s: %s', helper['resource'], attr, e) raise dn = default_identity_converter(object_dict, otype, helper, extra_attributes=[path], aci_mo_type=helper['resource'], to_aim=False)[0] result.append({ helper['resource']: { 'attributes': { 'dn': dn, 'tDn': path } } }) return result
def decompose_dn(mo_type, dn): try: return apic_client.DNManager().aci_decompose_dn_guess(dn, mo_type)[1] except (apic_client.DNManager.InvalidNameFormat, KeyError, apic_client.cexc.ApicManagedObjectNotSupported, IndexError): log_ = LOG.warning if mo_type == 'faultDelegate': log_ = LOG.debug log_("Failed to transform DN %s to key for type %s" % (dn, mo_type)) return
def root_key_funct(key): """Key funct for Tree Maker Utility function for TreeManager initialization :param tree: :return: """ splits = key.split('-', 1) mo = apic_client.ManagedObjectClass.prefix_to_mos[splits[0]] dn = apic_client.DNManager().build([[mo, splits[-1]]]) return AimHashTreeMaker._build_hash_tree_key_from_dn(dn, mo)
def from_dn(cls, dn): dn_mgr = apic_client.DNManager() try: mos_and_rns = dn_mgr.aci_decompose_with_type(dn, cls._aci_mo_name) rns = dn_mgr.filter_rns(mos_and_rns) if len(rns) < len(cls.identity_attributes): raise exc.InvalidDNForAciResource(dn=dn, cls=cls) attr = {p[0]: p[1] for p in zip(cls.identity_attributes, rns)} return cls(**attr) except apic_client.DNManager.InvalidNameFormat: raise exc.InvalidDNForAciResource(dn=dn, cls=cls)
def __init__(self, tenant_name, apic_config, apic_session, ws_context, creation_succeeded=None, creation_failed=None, aim_system_id=None, get_resources=None, *args, **kwargs): super(AciTenantManager, self).__init__(*args, **kwargs) LOG.info("Init manager for tenant %s" % tenant_name) self.get_resources = get_resources self.apic_config = apic_config # Each tenant has its own sessions self.aci_session = apic_session self.dn_manager = apic_client.DNManager() self.tenant_name = tenant_name children_mos = get_children_mos(self.aci_session, self.tenant_name) ws_subscription_to = self.apic_config.get_option( 'websocket_subscription_timeout', 'aim') or DEFAULT_WS_TO self.tenant = Root(self.tenant_name, filtered_children=children_mos, rn=self.tenant_name, ws_subscription_to=ws_subscription_to) self._state = structured_tree.StructuredHashTree() self._operational_state = structured_tree.StructuredHashTree() self._monitored_state = structured_tree.StructuredHashTree() self.polling_yield = self.apic_config.get_option( 'aci_tenant_polling_yield', 'aim') self.to_aim_converter = converter.AciToAimModelConverter() self.to_aci_converter = converter.AimToAciModelConverter() self._reset_object_backlog() self.tree_builder = tree_manager.HashTreeBuilder(None) self.tag_name = aim_system_id or self.apic_config.get_option( 'aim_system_id', 'aim') self.tag_set = set() self.failure_log = {} def noop(*args, **kwargs): pass self.creation_succeeded = creation_succeeded or noop self.creation_failed = creation_failed or noop # Warm bit to avoid rushed synchronization before receiving the first # batch of APIC events self._warm = False self.ws_context = ws_context self.recovery_retries = None self.max_retries = 5 self.error_handler = error.APICAPIErrorHandler() # For testing purposes self.num_loop_runs = float('inf')
def _check_parent_type(self, aci_object, parent_types): dn = list(aci_object.values())[0]['attributes']['dn'] type = list(aci_object.keys())[0] try: decomposed = (apic_client.DNManager().aci_decompose_dn_guess( dn, type)) except apic_client.DNManager.InvalidNameFormat: LOG.debug("Type %s with DN %s is not supported." % (type, dn)) return False if len(decomposed[1]) <= 1: return False return decomposed[1][-2][0] in parent_types
def set_ownership_change(self, to_push): to_update = [] if self.use_annotation: for obj in to_push: if list(obj.keys())[0].startswith(TAG_KEY): dn = list(obj.values())[0]['attributes']['dn'] if dn.endswith('/tag-%s' % self.tag_key): dec = apic_client.DNManager().aci_decompose_dn_guess( dn, list(obj.keys())[0]) parent_dec = dec[1][:-1] parent_dn = apic_client.DNManager().build(parent_dec) parent_type = parent_dec[-1][0] to_update.append({ parent_type: { 'attributes': { 'dn': parent_dn, 'annotation': '' } } }) return to_push, to_update
def __init__(self, *args, **kwargs): self.filtered_children = kwargs.pop('filtered_children', []) rn = kwargs.pop('rn') super(Root, self).__init__(*args, **kwargs) try: rn_base = apic_client.DNManager().get_rn_base(rn) if rn.startswith(rn_base): self.dn = rn else: self.dn = rn_base + '/' + rn except KeyError: self.dn = apic_client.DN_BASE + 'rn' self.urls = self._get_instance_subscription_urls()
def _post_with_transaction(self, to_push, modified=False): if not to_push: return dn_mgr = apic_client.DNManager() decompose = dn_mgr.aci_decompose_dn_guess with self.aci_session.transaction(top_send=True) as trs: for obj in to_push: attr = list(obj.values())[0]['attributes'] if modified: attr['status'] = converter.MODIFIED_STATUS mo, parents_rns = decompose(attr.pop('dn'), list(obj.keys())[0]) rns = dn_mgr.filter_rns(parents_rns) getattr(self.aci_session, mo).create(*rns, transaction=trs, **attr)
def __init__(self, *args, **kwargs): self.filtered_children = kwargs.pop('filtered_children', []) rn = kwargs.pop('rn') ws_subscription_to = kwargs.pop('ws_subscription_to') super(Root, self).__init__(*args, **kwargs) try: rn_base = apic_client.DNManager().get_rn_base(rn) if rn.startswith(rn_base): self.dn = rn else: self.dn = rn_base + '/' + rn except KeyError: self.dn = apic_client.DN_BASE + 'rn' self.type = apic_client.ManagedObjectClass.prefix_to_mos[rn.split('-') [0]] self.urls = self._get_instance_subscription_urls( ws_subscription_to=ws_subscription_to)
def _tdn_rs_converter(object_dict, otype, helper, source_identity_attributes, destination_identity_attributes, to_aim=True): result = [] if to_aim: # Converting a fvRsDomAtt into an EPG res_dict = {} try: id = default_identity_converter(object_dict, otype, helper, to_aim=True) except apic_client.DNManager.InvalidNameFormat: return [] for index, attr in enumerate(destination_identity_attributes): res_dict[attr] = id[index] tdn = object_dict.get('tDn') if tdn: dnm = apic_client.DNManager() mos_and_rns = dnm.aci_decompose_with_type(tdn, aci_mo) rns = dnm.filter_rns(mos_and_rns) res_dict.update(dict(zip(aim_attr_list, rns))) to_res = helper.get('to_resource', default_to_resource) result.append(to_res(res_dict, helper, to_aim=True)) else: dn = default_identity_converter(object_dict, otype, helper, to_aim=False)[0] dn_attrs = [ object_dict[a] for a in aim_attr_list if object_dict.get(a) ] if len(dn_attrs) == len(aim_attr_list): tdn = apic_client.ManagedObjectClass(aci_mo).dn(*dn_attrs) result.append({ helper['resource']: { 'attributes': { 'dn': dn, 'tDn': tdn } } }) return result
def _keys_to_bare_aci_objects(self, keys): # Transforms hashtree keys into minimal ACI objects aci_objects = [] for key in keys: fault_code = None key_parts = self._split_key(key) mo_type = key_parts[-1][0] aci_object = {mo_type: {'attributes': {}}} if mo_type == 'faultInst': fault_code = key_parts[-1][1] key_parts = key_parts[:-1] dn = apic_client.DNManager().build(key_parts) if fault_code: dn += '/fault-%s' % fault_code aci_object[mo_type]['attributes']['code'] = fault_code aci_object[mo_type]['attributes']['dn'] = dn aci_objects.append(aci_object) return aci_objects
def func(object_dict, attribute, to_aim=True): if to_aim: dn = object_dict.get(attribute) if dn: dnm = apic_client.DNManager() mos_and_rns = dnm.aci_decompose_with_type(dn, aci_mo) rns = dnm.filter_rns(mos_and_rns) return dict(zip(aim_attr_list, rns)) else: return {} else: dn_attrs = [ object_dict[a] for a in aim_attr_list if object_dict.get(a) ] if len(dn_attrs) == len(aim_attr_list): dn = apic_client.ManagedObjectClass(aci_mo).dn(*dn_attrs) else: dn = '' return dn
def get_children_mos(apic_session, root): root_type = 'uni' try: root_type = apic_client.DNManager().get_rn_base(root) except KeyError: pass global CHILDREN_MOS_UNI global CHILDREN_MOS_TOPOLOGY if root_type in ['uni']: if CHILDREN_MOS_UNI is None: CHILDREN_MOS_UNI = set() for mo in CHILDREN_LIST: if mo in apic_client.ManagedObjectClass.supported_mos: mo_name = apic_client.ManagedObjectClass(mo).klass_name else: mo_name = mo try: # Verify class support apic_session.GET('/mo/uni/tn-common.json?' 'target-subtree-class=%s' % mo_name) except apic_exc.ApicResponseNotOk as e: if int(e.err_status) == 400 and int(e.err_code) == 12: continue raise e CHILDREN_MOS_UNI.add(mo_name) return CHILDREN_MOS_UNI elif root_type in ['topology']: if CHILDREN_MOS_TOPOLOGY is None: CHILDREN_MOS_TOPOLOGY = set() for mo in TOPOLOGY_CHILDREN_LIST: if mo in apic_client.ManagedObjectClass.supported_mos: mo_name = apic_client.ManagedObjectClass(mo).klass_name else: mo_name = mo try: apic_session.GET('/node/class/%s.json?' % mo_name) except apic_exc.ApicResponseNotOk as e: if int(e.err_status) == 400 and int(e.err_code) == 12: continue raise e CHILDREN_MOS_TOPOLOGY.add(mo_name) return CHILDREN_MOS_TOPOLOGY
def retrieve_fault_parent(fault_dn, resource_map): # external is the DN of the ACI resource dn_mgr = apic_client.DNManager() mos_rns = dn_mgr.aci_decompose_with_type(fault_dn, ACI_FAULT)[:-1] rns = dn_mgr.filter_rns(mos_rns) conv_info = None step = -1 while conv_info is None or len(conv_info) > 1: aci_klass = mos_rns[step][0] conv_info = resource_map[aci_klass] step -= 1 conv_info = conv_info[0] klasses = [conv_info['resource']] if conv_info.get('alt_resource'): klasses.append(conv_info['alt_resource']) parents = [] for klass in klasses: a_obj = klass(**{y: rns[x] for x, y in enumerate(klass.identity_attributes)}) parents.append(a_obj) return parents
def default_identity_converter(object_dict, otype, helper, extra_attributes=None, aci_mo_type=None, to_aim=True): """Default identity converter This converter uses the DN and splits it in its fundamental parts to retrieve the identity names. :param object_dict: dictionarty of the resource to be converted :param otype: Type of the object. Can be an AIM resource class or a APIC class name. :param extra_attributes: Ordered list of additional attribute values needed to create the identity attribute :param aci_mo_type: ACI ManagedObjectType to use when creating ACI identity attribute :param to_aim: Boolean indicating whether we are converting ACI/AIM (True) or AIM/ACI (False) :return: list with exactly all the attributes that need to be assigned to the resource class 'identity_attributes' """ if to_aim: dn_mgr = apic_client.DNManager() aci_type = aci_mo_type or otype mos_and_rns = dn_mgr.aci_decompose_with_type(object_dict['dn'], aci_type) return dn_mgr.filter_rns(mos_and_rns) else: attr = [object_dict[x] for x in otype.identity_attributes] if extra_attributes: attr.extend(extra_attributes) mo_type = aci_mo_type or helper['resource'] try: return [apic_client.ManagedObjectClass(mo_type).dn(*attr)] except Exception as e: LOG.error('Failed to make DN for %s with %s: %s', mo_type, attr, e) raise
def action(result, aci_object, node): if node and not node.dummy: # Monitored state transition -> Delete the TAG instead LOG.debug("Deleting tag for transitioning object %s", list(aci_object.values())[0]['attributes']['dn']) aci_type = 'tagInst' dn = list(aci_object.values( ))[0]['attributes']['dn'] + '/tag-' + self.aim_system_id result.append({aci_type: {'attributes': {'dn': dn}}}) else: # If the parent object was already deleted in the config # desired tree then we don't have to send this child deletion # to APIC dn = list(aci_object.values())[0]['attributes']['dn'] res_type = list(aci_object.keys())[0] dn_mgr = apic_client.DNManager() mo, rns = dn_mgr.aci_decompose_dn_guess(dn, res_type) if len(rns) > 1: parent_dn = dn_mgr.build(rns[:-1]) parent_type, xxx = rns[-2] parent_key = tree_manager.AimHashTreeMaker._dn_to_key( parent_type, parent_dn) root = tree_manager.AimHashTreeMaker._extract_root_rn( parent_key) config_desire = self.multiverse[ base.CONFIG_UNIVERSE]['desired'].state node = config_desire[root].find(parent_key) if node: result.append(aci_object) # Also has to make sure the parent is not in the monitor # desired tree before we skip it else: mon_desire = self.multiverse[ base.MONITOR_UNIVERSE]['desired'].state node = mon_desire[root].find(parent_key) if node: result.append(aci_object) else: result.append(aci_object)
def _push_aim_resources(self): dn_mgr = apic_client.DNManager() decompose = dn_mgr.aci_decompose_dn_guess with utils.get_rlock(lcon.ACI_BACKLOG_LOCK_NAME_PREFIX + self.tenant_name): while not self.object_backlog.empty(): request = self.object_backlog.get() for method, aim_objects in request.items(): # Method will be either "create" or "delete" # sort the aim_objects based on DN first for DELETE method sorted_aim_objs = aim_objects if method == base_universe.DELETE: sorted_aim_objs = sorted(aim_objects, key=lambda x: list(x.values()) [0]['attributes']['dn']) potential_parent_dn = ' ' for aim_object in sorted_aim_objs: # get MO from ACI client, identify it via its DN parts # and push the new body if method == base_universe.DELETE: # If a parent is also being deleted then we don't # have to send those children requests to APIC dn = list( aim_object.values())[0]['attributes']['dn'] res_type = list(aim_object.keys())[0] decomposed = decompose(dn, res_type) parent_dn = dn_mgr.build(decomposed[1][:-1]) if parent_dn.startswith(potential_parent_dn): continue else: potential_parent_dn = dn to_push = [copy.deepcopy(aim_object)] else: if getattr(aim_object, 'monitored', False): # When pushing to APIC, treat monitored # objects as pre-existing aim_object.monitored = False aim_object.pre_existing = True to_push = self.to_aci_converter.convert( [aim_object]) LOG.debug('%s AIM object %s in APIC' % (method, repr(aim_object))) try: if method == base_universe.CREATE: # Set ownership before pushing the request to_push = self.ownership_mgr.set_ownership_key( to_push) LOG.debug("POSTING into APIC: %s" % to_push) self._post_with_transaction(to_push) self.creation_succeeded(aim_object) else: to_delete, to_update = ( self.ownership_mgr.set_ownership_change( to_push)) LOG.debug("DELETING from APIC: %s" % to_delete) for obj in to_delete: attr = list(obj.values())[0]['attributes'] self.aci_session.DELETE('/mo/%s.json' % attr.pop('dn')) LOG.debug("UPDATING in APIC: %s" % to_update) # Update object ownership self._post_with_transaction(to_update, modified=True) if to_update: self.creation_succeeded(aim_object) except Exception as e: LOG.debug(traceback.format_exc()) LOG.error("An error has occurred during %s for " "object %s: %s" % (method, aim_object, str(e))) if method == base_universe.CREATE: err_type = ( self.error_handler.analyze_exception(e)) # REVISIT(ivar): for now, treat UNKNOWN errors # the same way as OPERATION_TRANSIENT. # Investigate a way to understand when such # errors might require agent restart. self.creation_failed(aim_object, str(e), err_type)
def _push_aim_resources(self): with utils.get_rlock(lcon.ACI_BACKLOG_LOCK_NAME_PREFIX + self.tenant_name): while not self.object_backlog.empty(): request = self.object_backlog.get() for method, aim_objects in request.iteritems(): # Method will be either "create" or "delete" for aim_object in aim_objects: # get MO from ACI client, identify it via its DN parts # and push the new body LOG.debug('%s AIM object %s in APIC' % (method, repr(aim_object))) if method == base_universe.DELETE: to_push = [copy.deepcopy(aim_object)] else: if getattr(aim_object, 'monitored', False): # When pushing to APIC, treat monitored # objects as pre-existing aim_object.monitored = False aim_object.pre_existing = True to_push = self.to_aci_converter.convert( [aim_object]) # Set TAGs before pushing the request tags = [] if method == base_universe.CREATE: # No need to deal with tags on deletion for obj in to_push: if not obj.keys()[0].startswith(TAG_KEY): dn = obj.values()[0]['attributes']['dn'] dn += '/tag-%s' % self.tag_name tags.append({ "tagInst__%s" % obj.keys()[0]: { "attributes": { "dn": dn } } }) LOG.debug("Pushing %s into APIC: %s" % (method, to_push + tags)) # Multiple objects could result from a conversion, push # them in a single transaction dn_mgr = apic_client.DNManager() decompose = dn_mgr.aci_decompose_dn_guess try: if method == base_universe.CREATE: with self.aci_session.transaction( top_send=True) as trs: for obj in to_push + tags: attr = obj.values()[0]['attributes'] mo, parents_rns = decompose( attr.pop('dn'), obj.keys()[0]) rns = dn_mgr.filter_rns(parents_rns) getattr(getattr(self.aci_session, mo), method)(*rns, transaction=trs, **attr) else: for obj in to_push + tags: attr = obj.values()[0]['attributes'] self.aci_session.DELETE('/mo/%s.json' % attr.pop('dn')) # Object creation was successful, change object # state if method == base_universe.CREATE: self.creation_succeeded(aim_object) except Exception as e: LOG.debug(traceback.format_exc()) LOG.error("An error has occurred during %s for " "object %s: %s" % (method, aim_object, e.message)) if method == base_universe.CREATE: err_type = ( self.error_handler.analyze_exception(e)) # REVISIT(ivar): for now, treat UNKNOWN errors # the same way as OPERATION_TRANSIENT. # Investigate a way to understand when such # errors might require agent restart. self.creation_failed(aim_object, e.message, err_type)
def _manipulate_server_data(self, data, manager=None, add=True, tag=True, create_parents=False): removed = None manager = manager if manager is not None else self.manager try: manager.aci_session._data_stash except Exception: manager.aci_session._data_stash = {} def _tag_format(dn): return { 'tagInst': { 'attributes': { 'dn': (dn + '/tag-' + self.sys_id) }, 'children': [] } } dn_mgr = apic_client.DNManager() for resource in copy.deepcopy(data): resource.values()[0]['attributes'].pop('status', None) data_type = resource.keys()[0] if data_type == 'tagInst' and tag and add: continue decomposed = dn_mgr.aci_decompose_dn_guess( resource.values()[0]['attributes']['dn'], data_type)[1] if add: curr = manager.aci_session._data_stash.setdefault( decomposed[0][1], []) else: curr = manager.aci_session._data_stash.get( decomposed[0][1], []) prev = None child_index = None last_index = len(decomposed) - 1 is_new = False for out_index, part in enumerate(decomposed): # Look at the current's children and find the proper node. # if not found, it's a new node if part[0] in AMBIGUOUS_TYPES: partial_dn = ( dn_mgr.build(decomposed[:out_index]) + '/' + apic_client.ManagedObjectClass.mos_to_prefix[part[0]] + '-' + decomposed[out_index][1]) else: partial_dn = dn_mgr.build(decomposed[:out_index + 1]) for index, child in enumerate(curr): if child.values()[0]['attributes']['dn'] == partial_dn: child_index = index prev = curr curr = child.values()[0]['children'] break else: if add: if out_index < last_index: # Parent is missing if create_parents: next = { part[0]: { 'attributes': { 'dn': partial_dn }, 'children': [] if not tag else [_tag_format(partial_dn)] } } curr.append(next) prev = curr curr = next[part[0]]['children'] else: raise apic_client.cexc.ApicResponseNotOk( status=400, reason='bad request', request='create', err_code=104, err_text='bad request') else: # Append newly created object obj = { part[0]: { 'attributes': { 'dn': partial_dn }, 'children': [] if not tag else [_tag_format(partial_dn)] } } curr.append(obj) resource.values()[0].pop('children', None) obj[part[0]].update(resource.values()[0]) is_new = True else: # Not found return # Update body if not add: if child_index is not None: removed = prev.pop(child_index) if prev is manager.aci_session._data_stash[decomposed[0] [1]]: # Tenant is now empty manager.aci_session._data_stash.pop(decomposed[0][1]) else: # Root node removed = manager.aci_session._data_stash.pop( decomposed[0][1]) elif child_index is not None and not is_new: children = prev[child_index].values()[0]['children'] prev[child_index].update(resource) prev[child_index].values()[0]['children'] = children return removed
def _extract_root_rn(root_key): root_split = root_key[0].split('|') return apic_client.DNManager().build([root_split]).split('/')[-1]
def retrieve_aci_objects(self, events): result = {} for event in events: resource = list(event.values())[0] res_type = list(event.keys())[0] status = (resource['attributes'].get(STATUS_FIELD) or '').lower() raw_dn = resource['attributes'].get('dn') if self.is_child_object(res_type) and res_type != FAULT_KEY: # We need to make sure to retrieve the parent object as well try: decomposed = ( apic_client.DNManager().aci_decompose_dn_guess( raw_dn, res_type)) parent_dn = apic_client.DNManager().build( decomposed[1][:-1]) if parent_dn not in result: events.append({ decomposed[1][-2][0]: { 'attributes': { 'dn': parent_dn, 'status': converter.MODIFIED_STATUS, '_avoid_print_not_found': True } } }) except (apic_client.DNManager.InvalidNameFormat, KeyError): LOG.debug("Object with DN %s is not supported." % raw_dn) continue if res_type == FAULT_KEY: # Make sure we support the parent object try: apic_client.DNManager().aci_decompose_dn_guess( raw_dn, res_type) utils.retrieve_fault_parent(raw_dn, converter.resource_map) except (apic_client.DNManager.InvalidNameFormat, KeyError): LOG.debug("Fault with DN %s is not supported." % raw_dn) continue if res_type == TAG_KEY: # Add to the result and go ahead to the next object result[raw_dn] = event continue if status == converter.DELETED_STATUS: # Add to the result but keep evaluating result[raw_dn] = event if status == converter.MODIFIED_STATUS: event_attrs = copy.deepcopy( list(event.values())[0]['attributes']) event_attrs.pop(STATUS_FIELD) apnf = event_attrs.pop('_avoid_print_not_found', False) if raw_dn in result: # Update with changes list(result[raw_dn].values())[0]['attributes'].update( event_attrs) key = tree_manager.AimHashTreeMaker._dn_to_key( res_type, raw_dn) data = [] if key: # Search within the TenantManager state, which is the most # up to date. data = self.get_resources( [key], desired_state=self._get_full_state()) if not data and not apnf: LOG.debug("Resource %s not found or not supported", raw_dn) for item in data: dn = list(item.values())[0]['attributes']['dn'] if dn not in result: result[dn] = item if dn == raw_dn: list(result[raw_dn].values() )[0]['attributes'].update(event_attrs) if not status or status == converter.CREATED_STATUS: result[raw_dn] = event LOG.debug("Result for retrieving ACI resources: %s\n %s" % (events, result)) return list(result.values())