def find_interdomain_paths_from_stps_and_dpids(stp, dpid_constraints): # If STPs involved in the request use heterogeneous types for a given # connection (e.g. NSI and GRE), warn user and raise exception stps_gre = TNUtils.determine_stp_gre([stp.get("src_name"), stp.get("dst_name")]) if any(stps_gre) and not all(stps_gre): e = "Mapper SDN-SE-TN: attempting to connect 2 STPs" e += " of different type (e.g. GRE and NSI)" raise geni_ex.GENIv3GeneralError(e) pathfinder_options = {} src_auth = URNUtils.get_felix_authority_from_ogf_urn(stp.get("src_name")) dst_auth = URNUtils.get_felix_authority_from_ogf_urn(stp.get("dst_name")) # Perform a loose match, based on authorities of DPIDs # (not on exact DPIDs; as those should be automatically added) pathfinder_options["of_switch_cids_check_by_auth"] = True pathfinder_options["link_type"] = stp.get("link_type") for dpid_constraint in dpid_constraints: dpid_constraint_auth = set(URNUtils.get_felix_authority_from_urn(x) for x in dpid_constraint["ids"]) # If authority of contraints (list of DPIDs) matches with those of STPs, continue if dpid_constraint_auth == set([src_auth]): pathfinder_options["src_of_switch_cids"] = dpid_constraint["ids"] elif dpid_constraint_auth == set([dst_auth]): pathfinder_options["dst_of_switch_cids"] = dpid_constraint["ids"] paths = TNUtils.find_path_stps(stp.get("src_name"), stp.get("dst_name"), \ stp.get("link_type"), pathfinder_options) return paths
def find_interdomain_paths_from_stps_and_dpids(stp, dpid_constraints): # If STPs involved in the request use heterogeneous types for a given # connection (e.g. NSI and GRE), warn user and raise exception stps_gre = TNUtils.determine_stp_gre( [stp.get("src_name"), stp.get("dst_name")]) if any(stps_gre) and not all(stps_gre): e = "Mapper SDN-SE-TN: attempting to connect 2 STPs" e += " of different type (e.g. GRE and NSI)" raise geni_ex.GENIv3GeneralError(e) pathfinder_options = {} src_auth = URNUtils.get_felix_authority_from_ogf_urn( stp.get("src_name")) dst_auth = URNUtils.get_felix_authority_from_ogf_urn( stp.get("dst_name")) # Perform a loose match, based on authorities of DPIDs # (not on exact DPIDs; as those should be automatically added) pathfinder_options["of_switch_cids_check_by_auth"] = True pathfinder_options["link_type"] = stp.get("link_type") for dpid_constraint in dpid_constraints: dpid_constraint_auth = set( URNUtils.get_felix_authority_from_urn(x) for x in dpid_constraint["ids"]) # If authority of contraints (list of DPIDs) matches with those of STPs, continue if dpid_constraint_auth == set([src_auth]): pathfinder_options["src_of_switch_cids"] = dpid_constraint[ "ids"] elif dpid_constraint_auth == set([dst_auth]): pathfinder_options["dst_of_switch_cids"] = dpid_constraint[ "ids"] paths = TNUtils.find_path_stps(stp.get("src_name"), stp.get("dst_name"), \ stp.get("link_type"), pathfinder_options) return paths
def prune_unlinked_dpids(mapping_path_structure, dpids_src_list, dpids_dst_list, check_by_auth=False): """ Prunes those paths that do not conform to the passed SRC and DST DPID list. That is, the resulting paths must contain at least all the required SRC and DST DPIDs. @param check_by_auth when True, checks if the path contains a DPID with the required authority when False, looks for exact DPIDs on the path """ new_mapping_path_structure = [] for idx, mapping_path_element in enumerate(mapping_path_structure): # Source sets src_dpids_avail = set([ x["sdn"] for x in mapping_path_element["src"]["links"] ]) # Destination sets dst_dpids_avail = set([ x["sdn"] for x in mapping_path_element["dst"]["links"] ]) if check_by_auth: src_dpids_auth_avail = set(URNUtils.get_felix_authority_from_urn(x) for x in src_dpids_avail) src_dpids_auth_req = set(URNUtils.get_felix_authority_from_urn(x) for x in dpids_src_list) dst_dpids_auth_avail = set(URNUtils.get_felix_authority_from_urn(x) for x in dst_dpids_avail) dst_dpids_auth_req = set(URNUtils.get_felix_authority_from_urn(x) for x in dpids_dst_list) src_dpids_auth_match = src_dpids_auth_avail.intersection(src_dpids_auth_req) dst_dpids_auth_match = dst_dpids_auth_avail.intersection(dst_dpids_auth_req) # Loose check -> the authorities of the requested DPIDs must match full_match = len(src_dpids_auth_match) == len(src_dpids_auth_req) and len(dst_dpids_auth_match) == len(dst_dpids_auth_req) else: src_dpids_match = src_dpids_avail.intersection(dpids_src_list) dst_dpids_match = dst_dpids_avail.intersection(dpids_dst_list) # Tight check -> all requested DPIDs must be present (i.e. intersected with the available DPIDs) full_match = len(src_dpids_match) == len(dpids_src_list) and len(dst_dpids_match) == len(dpids_dst_list) if full_match: new_mapping_path_structure.append(mapping_path_element) return new_mapping_path_structure
def prune_unlinked_dpids(mapping_path_structure, dpids_src_list, dpids_dst_list, check_by_auth=False): """ Prunes those paths that do not conform to the passed SRC and DST DPID list. That is, the resulting paths must contain at least all the required SRC and DST DPIDs. @param check_by_auth when True, checks if the path contains a DPID with the required authority when False, looks for exact DPIDs on the path """ new_mapping_path_structure = [] for idx, mapping_path_element in enumerate(mapping_path_structure): # Source sets src_dpids_avail = set( [x["sdn"] for x in mapping_path_element["src"]["links"]]) # Destination sets dst_dpids_avail = set( [x["sdn"] for x in mapping_path_element["dst"]["links"]]) if check_by_auth: src_dpids_auth_avail = set( URNUtils.get_felix_authority_from_urn(x) for x in src_dpids_avail) src_dpids_auth_req = set( URNUtils.get_felix_authority_from_urn(x) for x in dpids_src_list) dst_dpids_auth_avail = set( URNUtils.get_felix_authority_from_urn(x) for x in dst_dpids_avail) dst_dpids_auth_req = set( URNUtils.get_felix_authority_from_urn(x) for x in dpids_dst_list) src_dpids_auth_match = src_dpids_auth_avail.intersection( src_dpids_auth_req) dst_dpids_auth_match = dst_dpids_auth_avail.intersection( dst_dpids_auth_req) # Loose check -> the authorities of the requested DPIDs must match full_match = len(src_dpids_auth_match) == len( src_dpids_auth_req) and len(dst_dpids_auth_match) == len( dst_dpids_auth_req) else: src_dpids_match = src_dpids_avail.intersection(dpids_src_list) dst_dpids_match = dst_dpids_avail.intersection(dpids_dst_list) # Tight check -> all requested DPIDs must be present (i.e. intersected with the available DPIDs) full_match = len(src_dpids_match) == len( dpids_src_list) and len(dst_dpids_match) == len( dpids_dst_list) if full_match: new_mapping_path_structure.append(mapping_path_element) return new_mapping_path_structure
def __add_virtual_link(self, link_subset): # Retrieve the SDN endpoints of the slice ("abstract link" in M/MS) se_link_urns = [] # and obtain the SDN resources from those domains that are linked to the TN link for se_link in self.__hybrid_links: se_link_urns.extend( [se_link.get("source"), se_link.get("destination")]) vlink_orgs = [] sdn_link_urns = [] # XXX It would be necessary to iterate over the TN links, identify their domain for se_link in se_link_urns: se_link_auth = URNUtils.get_authority_from_urn(se_link) if ":ofam" in se_link and se_link_auth not in vlink_orgs: vlink_orgs.append(se_link_auth) sdn_link_urns.append(se_link) # TODO Check: what about >2 islands involved in the slice? # For loop (step: 2, reason: finding SDN#1 port <--> SDN#2 port) for i in xrange(0, len(sdn_link_urns), 2): try: self.__add_link_info(link_subset, sdn_link_urns[i], sdn_link_urns[i + 1]) except: pass
def add_vlan_to_link(self, link_cid, iface_cid): """ Add vlan to component ID of link's interface when using a newly formatted RSpec. This format is used: "urn+...+datapath+<dpid>_<port>+vlan=<vlan>. """ if "vlan=" in link_cid: urn_src, vlan_src, urn_dst, vlan_dst = URNUtils.get_fields_from_domain_link_id(link_cid) if_vlan_pairs = {urn_src: vlan_src, urn_dst: vlan_dst} # if_dpid = URNUtils.get_datapath_from_datapath_id(if_cid) if_dpid, if_port = URNUtils.get_datapath_and_port_from_datapath_id(iface_cid) if_dpid_port = "%s_%s" % (if_dpid, if_port) for if_vlan in if_vlan_pairs.keys(): if if_dpid_port in if_vlan: iface_cid += "+vlan=%s" % if_vlan_pairs[if_vlan] break return iface_cid
def __enforce_se_consistency(self, route): route_original = etree.tostring(route) if isinstance(route, str): root = etree.fromstring(route) else: root = route # Use GENIv3 namespace as used in base request formatter ns = root.nsmap[None] # Verify that interfaces (from node) are all defined in links interfaces = root.xpath("//x:node//x:interface", namespaces={"x": ns}) links = root.xpath("//x:link", namespaces={"x": ns}) for link in links: local_interfaces = link.xpath( "x:interface_ref", namespaces={"x": ns}) for interface in interfaces: # Check for inconsistent interfaces and remove them, i.e. # not defined in link's client_id or on its interfaces if URNUtils.get_datapath_and_port_from_datapath_id( interface.get("client_id"))[0] not in \ link.get("client_id") \ or interface.get("client_id") not in \ [x.get("client_id") for x in local_interfaces]: logger.debug("SE request: removing interface not in use \ (%s)" % etree.tostring(interface, pretty_print=True)) interface.getparent().remove(interface) if len(links) == 0: try: interfaces[0].getparent().getparent().remove( interfaces[0].getparent()) logger.debug("SE request: removing request due to \ missing links") except: pass if len(interfaces) == 0: try: links[0].getparent().remove(links[0]) logger.debug("SE request: removing request due to \ missing interfaces") except: pass route_new = etree.tostring(route) if route_original != route_new: logger.info( "Route (after consistency check)=%s" % (etree.tostring(route, pretty_print=True),))
def get_links(self, rspec): links_ = [] for l in rspec.findall(".//{%s}link" % (self.none)): manager_ = l.find("{%s}component_manager" % (self.none)) if manager_ is None: self.raise_exception("Component-Mgr tag not found in link!") if not self.check_se_link_resource(l, manager_): logger.info("Skipping this link, not a SE-res: %s", (l,)) continue type_ = l.find("{%s}link_type" % (self.none)) if type_ is None: self.raise_exception("Link-Type tag not found in link!") # Note: client_id for a link may follow one of the formats: # Original: "urn:publicid:IDN+fms:psnc:serm+link+ # <dpid1>_6_<dpid2>_7", # New: "urn:publicid:IDN+fms:psnc:serm+link+ # <dpid1>_6?vlan=1_<dpid2>_7?vlan=2", # In case of receiving the new one, it must be translated to # the original one (advertised by SERM); otherwise it will not be # possible to find the link in the Mongo serm.link collection client_id = l.attrib.get("client_id") # client_id_2 = URNUtils.convert_se_link_id_to_adv_id(client_id) l_ = SELink(client_id, type_.attrib.get("name"), manager_.attrib.get("name")) self.update_protogeni_cm_uuid(l, l_) # FIXME: VLAN seems not properly added to interface for i in l.iterfind("{%s}interface_ref" % (self.none)): # 1st check new SERM RSpec format client_id, vlan = URNUtils.get_fields_from_domain_iface_id(i.attrib.get("client_id")) if not vlan: vlan = i.attrib.get("{%s}vlan" % (self.__felix)) l_.add_interface_ref(client_id, vlan) [ l_.add_property(p.attrib.get("source_id"), p.attrib.get("dest_id"), p.attrib.get("capacity")) for p in l.iterfind("{%s}property" % (self.none)) ] links_.append(l_.serialize()) return links_
def add_link(self, rspec, link, inner_call=True): """ Translates incoming dictionary: {"src_name": "urn:publicid:IDN+fms:src_auth:mapper+domain+domain1", "dst_name": "urn:publicid:IDN+fms:src_auth:mapper+domain+domain2", "link_type": "(gre|nsi)") into the expected tree structure: <link component_id="urn:publicid:IDN+fms:src_auth:mapper+link+domain1_domain2"> <component_manager name="urn:publicid:IDN+fms:src_auth:mapper+authority+cm"/> <link_type name="urn:felix+virtual_link+type+(gre|nsi)"/> <interface_ref component_id="urn:publicid:IDN+fms:src_auth:mapper+domain+domain1"/> <interface_ref component_id="urn:publicid:IDN+fms:src_auth:mapper+domain+domain2"/> </link> and embeds it into the advertisement RSpec """ l = etree.SubElement(rspec, "{%s}link" % (self.xmlns)) # Retrieve list of connected domains per link end_links = [link.get("src_name"), link.get("dst_name")] link_doms = map(lambda x: x.split("+")[-1], end_links) src_auth = URNUtils.get_felix_authority_from_urn(link.get("src_name")) component_id = "urn:publicid:IDN+fms:%s:mapper+link+%s_%s" % ( src_auth, link_doms[0], link_doms[1]) l.attrib["component_id"] = component_id if inner_call and link.get("component_manager_uuid") is not None: l.attrib["{%s}component_manager_uuid" % (self.__proto)] =\ link.get("component_manager_uuid") m = etree.SubElement(l, "{%s}component_manager" % (self.xmlns)) m.attrib[ "name"] = "urn:publicid:IDN+fms:%s:mapper+authority+cm" % src_auth t = etree.SubElement(l, "{%s}link_type" % (self.xmlns)) t.attrib["name"] = "urn:felix+virtual_link+type+%s" % link.get( "link_type") for i in end_links: interface = etree.SubElement(l, "{%s}interface_ref" % (self.xmlns)) interface.attrib["component_id"] = i
def __group_peers_by_domain(self): for peer in self.peers_info: filter_params = { "_id": peer.get("_id"), } domain_peer = db_sync_manager.get_domain_info(filter_params) peer_domain_urn = domain_peer.get("domain_urn") authority = URNUtils.get_felix_authority_from_urn(peer_domain_urn) # If authority (domain name) does not exist yet, create if not self.peers_by_domain.get(authority): self.peers_by_domain[authority] = [] # Extend list of peers with new one self.peers_by_domain[authority].append(peer_domain_urn) # Stores last_update_time for the physical topology # on a given domain try: last_update_time = self._get_timestamp() db_sync_manager.store_physical_info(peer_domain_urn, last_update_time) except Exception as e: logger.error( "Error storing last_update_time for phy-topology.") logger.error("Exception: %s" % e) # XXX: (M)MS assumes one TNRM per island # With this, (M)MS receives at least one TNRM per island type_resource_peer_tnrm = self.urn_type_resources_variations.get( "tnrm") for peer in self.peers: filter_params = { "_ref_peer": peer.get("_id"), } domain_peer = db_sync_manager.get_domain_info(filter_params) peer_domain_urn = domain_peer.get("domain_urn") peer_is_tnrm = any( [rt in peer_domain_urn for rt in type_resource_peer_tnrm]) # MRO: TNRM added at this level. Use information from peer to add it as a TNRM per domain if peer_is_tnrm: # Add the TNRM peer to each authority that does not have it yet for authority in self.peers_by_domain: if peer_domain_urn not in self.peers_by_domain.get( authority): self.peers_by_domain[authority].append(peer_domain_urn)
def add_link(self, rspec, link, inner_call=True): """ Translates incoming dictionary: {"src_name": "urn:publicid:IDN+fms:src_auth:mapper+domain+domain1", "dst_name": "urn:publicid:IDN+fms:src_auth:mapper+domain+domain2", "link_type": "(gre|nsi)") into the expected tree structure: <link component_id="urn:publicid:IDN+fms:src_auth:mapper+link+domain1_domain2"> <component_manager name="urn:publicid:IDN+fms:src_auth:mapper+authority+cm"/> <link_type name="urn:felix+virtual_link+type+(gre|nsi)"/> <interface_ref component_id="urn:publicid:IDN+fms:src_auth:mapper+domain+domain1"/> <interface_ref component_id="urn:publicid:IDN+fms:src_auth:mapper+domain+domain2"/> </link> and embeds it into the advertisement RSpec """ l = etree.SubElement(rspec, "{%s}link" % (self.xmlns)) # Retrieve list of connected domains per link end_links = [link.get("src_name"), link.get("dst_name")] link_doms = map(lambda x: x.split("+")[-1], end_links) src_auth = URNUtils.get_felix_authority_from_urn(link.get("src_name")) component_id = "urn:publicid:IDN+fms:%s:mapper+link+%s_%s" % (src_auth, link_doms[0], link_doms[1]) l.attrib["component_id"] = component_id if inner_call and link.get("component_manager_uuid") is not None: l.attrib["{%s}component_manager_uuid" % (self.__proto)] =\ link.get("component_manager_uuid") m = etree.SubElement(l, "{%s}component_manager" % (self.xmlns)) m.attrib["name"] = "urn:publicid:IDN+fms:%s:mapper+authority+cm" % src_auth t = etree.SubElement(l, "{%s}link_type" % (self.xmlns)) t.attrib["name"] = "urn:felix+virtual_link+type+%s" % link.get("link_type") for i in end_links: interface = etree.SubElement(l, "{%s}interface_ref" % (self.xmlns)) interface.attrib["component_id"] = i
def __group_peers_by_domain(self): for peer in self.peers_info: filter_params = {"_id": peer.get("_id")} domain_peer = db_sync_manager.get_domain_info(filter_params) peer_domain_urn = domain_peer.get("domain_urn") authority = URNUtils.get_felix_authority_from_urn(peer_domain_urn) # If authority (domain name) does not exist yet, create if not self.peers_by_domain.get(authority): self.peers_by_domain[authority] = [] # Extend list of peers with new one self.peers_by_domain[authority].append(peer_domain_urn) # Stores last_update_time for the physical topology # on a given domain try: last_update_time = self._get_timestamp() db_sync_manager.store_physical_info(peer_domain_urn, last_update_time) except Exception as e: logger.error("Error storing last_update_time in phy-topology.") logger.error("Exception: %s" % e) # XXX: (M)MS assumes one TNRM per island # With this, (M)MS receives at least one TNRM per island type_resource_peer_tnrm = \ self.urn_type_resources_variations.get("tnrm") for peer in self.peers: filter_params = {"_ref_peer": peer.get("_id")} domain_peer = db_sync_manager.get_domain_info(filter_params) peer_domain_urn = domain_peer.get("domain_urn") peer_is_tnrm = any( [rt in peer_domain_urn for rt in type_resource_peer_tnrm]) # MRO: TNRM added at this level. Use information from peer to add # it as a TNRM per domain if peer_is_tnrm: # Add the TNRM peer to each authority that does not have it yet for authority in self.peers_by_domain: if peer_domain_urn not in self.\ peers_by_domain.get(authority): self.peers_by_domain[authority].append(peer_domain_urn)
def __add_virtual_link(self, link_subset): # Retrieve the SDN endpoints of the slice ("abstract link" in M/MS) se_link_urns = [] # and obtain the SDN resources from those domains that are linked to the TN link for se_link in self.__hybrid_links: se_link_urns.extend([se_link.get("source"), se_link.get("destination")]) vlink_orgs = [] sdn_link_urns = [] # XXX It would be necessary to iterate over the TN links, identify their domain for se_link in se_link_urns: se_link_auth = URNUtils.get_authority_from_urn(se_link) if ":ofam" in se_link and se_link_auth not in vlink_orgs: vlink_orgs.append(se_link_auth) sdn_link_urns.append(se_link) # TODO Check: what about >2 islands involved in the slice? # For loop (step: 2, reason: finding SDN#1 port <--> SDN#2 port) for i in xrange(0, len(sdn_link_urns), 2): try: self.__add_link_info(link_subset, sdn_link_urns[i], sdn_link_urns[i+1]) except: pass
def __check_ids_same_domain(self, src_id, dst_id): auth_src = URNUtils.get_felix_authority_from_urn(src_id) auth_dst = URNUtils.get_felix_authority_from_urn(dst_id) return auth_src == auth_dst
def __get_island_name(self, urn): return URNUtils.get_felix_authority_from_urn(urn)
def get_authority(urn): return URNUtils.get_felix_authority_from_urn(urn)
def add_se_resources(self, slice_urn, nodes, links): """ Inserts one or more nodes with SERM resources information. @param slice_urn URN identifier of a given slice @param nodes Structure containing a list of SERM nodes and its attributes @param links Structure containing a list of SERM links and its attributes """ if slice_urn not in self.__stored: logger.error("Unable to find Topology info from %s!" % slice_urn) return topology = self.__stored.get(slice_urn) logger.debug("dd_se_resources Nodes=%d" % (len(nodes),)) for n in nodes: logger.debug("Node=%s" % (n,)) node_ = etree.SubElement( topology, "node", id=n.get("component_id"), type=self.SE_LINK_TYPE) if n.get("host_name"): self.__add_snmp_management(node_, n.get("host_name")) for ifs in n.get("interfaces"): interface_ = etree.SubElement( node_, "interface", id=ifs.get("component_id")) # Try to extract port info from the component id! self.__add_se_port_from_interface( ifs.get("component_id"), interface_) # vlan = ifs.get("vlan")[0].get("tag") # vlan1, vlan2 = l.get("component_id") # m_ = etree.SubElement(node_, "match") # etree.SubElement(m_, "vlan", start=vlan, end=vlan) for l in links: # FIXME: incorrect comparison link_id = l.get("component_id") if link_id == ifs.get("component_id"): urn_src, vlan_src, urn_dst, vlan_dst = \ URNUtils.get_fields_from_domain_link_id(link_id) m_ = etree.SubElement(node_, "match") etree.SubElement(m_, "vlan", start=vlan_src, end=vlan_src) m_ = etree.SubElement(node_, "match") etree.SubElement(m_, "vlan", start=vlan_dst, end=vlan_dst) logger.debug("add_se_resources Links=%d" % (len(links),)) for l in links: logger.debug("Link=%s" % (l,)) if len(l.get("interface_ref")) != 2: logger.warning("Unable to manage extra-list of info (%d)!" % len(l.get("interface_ref"))) continue # is it really necessary to put bidirectional links? # self.__add_link_info(topology, # l.get("interface_ref")[0].get("component_id"), # l.get("interface_ref")[1].get("component_id"), # l.get("link_type")) # store the values for the virtual island-to-island link self.__se_links.append( {'id': l.get("component_id"), 'source': l.get("interface_ref")[0].get("component_id"), 'destination': l.get("interface_ref")[1].get("component_id")}) # we need to add "special" links here: SE-to-SDN, SE-to-TN # and an "abstract" link self.__add_se_external_link_info( topology, [l.get("interface_ref")[0].get("component_id"), l.get("interface_ref")[1].get("component_id")])