def initialize(self): """ .. seealso:: :func:`AbstractAPI.initialize() <escape.util.api.AbstractAPI.initialize>` """ log.debug("Initializing Resource Orchestration Sublayer...") self.resource_orchestrator = ResourceOrchestrator(self) if self._nffg_file: try: service_request = self._read_data_from_file(self._nffg_file) service_request = NFFG.parse(service_request) self.__proceed_instantiation(nffg=service_request) except (ValueError, IOError, TypeError) as e: log.error("Can't load service request from file because of: " + str(e)) else: log.info("Graph representation is loaded successfully!") # Initiate ROS REST-API if needed if self._agent or self._rosapi: self._initiate_ros_api() # Initiate Cf-Or REST-API if needed if self._cfor: self._initiate_cfor_api() log.info("Resource Orchestration Sublayer has been initialized!") if self._agent: log.warning( "In AGENT mode Service Layer is not going to be initialized!")
def collect_mappings(self, mappings, slor_topo): dov = self.virtualizerManager.dov.get_resource_info() response = mappings.full_copy() log.debug("Start checking mappings...") for mapping in response: bb, nf = detect_bb_nf_from_path(path=mapping.object.get_value(), topo=slor_topo) if not nf: # mapping.target.object.set_value("NOT_FOUND") mapping.target.domain.set_value("N/A") continue m_result = self.__collect_binding(dov=dov, nfs=[nf]) if not m_result: log.warning("Mapping is not found for NF: %s!" % nf) # mapping.target.object.set_value("NOT_FOUND") mapping.target.domain.set_value("N/A") continue try: node = m_result[0]['bisbis']['id'] domain = m_result[0]['bisbis']['domain'] except KeyError: log.warning("Missing mapping element from: %s" % m_result) # mapping.target.object.set_value("NOT_FOUND") mapping.target.domain.set_value("N/A") continue log.debug("Found mapping: %s@%s (domain: %s)" % (nf, node, domain)) mapping.target.object.set_value(NF_PATH_TEMPLATE % (node, nf)) mapping.target.domain.set_value( CONFIG.get_domain_url(domain=domain)) return response
def __update_nffg_domain(nffg_part, domain_name=None): """ Update domain descriptor of infras: REMOTE -> INTERNAL :param nffg_part: NF-FG need to be updated :type nffg_part: :any:`NFFG` :return: updated NFFG :rtype: :any:`NFFG` """ rewritten = [] if domain_name is None: local_mgr = CONFIG.get_local_manager() if local_mgr is None: log.error("No local Manager has been initiated! " "Skip domain rewriting!") elif len(local_mgr) > 1: log.warning("Multiple local Manager has been initiated: %s! " "Arbitrarily use the first..." % local_mgr) domain_name = local_mgr.pop() log.debug("Rewrite received NFFG domain to %s..." % domain_name) for infra in nffg_part.infras: infra.domain = domain_name rewritten.append(infra.id) log.debug("Rewritten infrastructure nodes: %s" % rewritten) return nffg_part
def __detect_neo4j_service_name(): """ Detect the name of the neo4j service. :return: name of the service :rtype: str """ import os.path if os.path.isfile('/etc/neo4j/neo4j.conf'): return "neo4j" elif os.path.isfile('/etc/neo4j/neo4j-server.properties'): return "neo4j-service" else: log.warning("No configuration file was found for neo4j service!")
def initialize(self): """ Initialize NFIB with test data. """ try: try: host, port = CONFIG.get_neo4j_host_port() host = host if host else self.DB_HOST port = port if port else self.DB_PORT log.debug("Initiating Graph database connection[%s:%s]..." % (host, port)) self.graph_db = Graph(host=host, http_port=port) except Unauthorized as e: quit_with_error( "Got Unauthorized error on: %s from neo4j! Disable the authorization " "in /etc/neo4j/neoj4-server.properties!" % e) return self except SocketError: log.warning( "NFIBManager has not been initialized! Only cause problem " "if ESCAPE is used as a Local Orchestrator!") return self self.__initialize() except SocketError as e: log.error( "NFIB is not reachable due to failed neo4j service! Cause: " + str(e)) except KeyboardInterrupt: log.warning("NFIB was interrupted by user!") except Unauthorized: log.error( "neo4j responded with Unauthorized error! Maybe you forgot disabling " "authentication in '/etc/neo4j/neo4j.conf' ?") except IOError as e: if ".neo4j/known_hosts" in str(e): # Skip Permission denied in case of accessing neo4j cache file (v3.0.2) pass else: raise except: log.exception("Got unexpected error during NFIB initialization!") return self
def collect_mapping_info(self, service_id): """ Return with collected information of mapping of a given service. :param service_id: service request ID :type service_id: str :return: mapping info :rtype: dict """ # Get the service NFFG based on service ID request = self.nffgManager.get(service_id) if request is None: log.warning("Service request(id: %s) is not found!" % service_id) return "Service request is not found!" # Get the overall view a.k.a. DoV dov = self.virtualizerManager.dov.get_resource_info() # Collect NFs nfs = [nf.id for nf in request.nfs] log.debug("Collected NFs: %s" % nfs) return self.__collect_binding(dov=dov, nfs=nfs)
def initialize (self): """ Initialize NFIB with test data. """ try: self.__initialize() except SocketError as e: log.error( "NFIB is not reachable due to failed neo4j service! Cause: " + str(e)) except KeyboardInterrupt: log.warning("NFIB was interrupted by user!") except Unauthorized: log.error( "neo4j responded with Unauthorized error! Maybe you forgot disabling " "authentication in '/etc/neo4j/neo4j.conf' ?") except IOError as e: if ".neo4j/known_hosts" in str(e): # Skip Permission denied in case of accessing neo4j cache file (v3.0.2) pass else: raise except: log.exception("Got unexpected error during NFIB initialization!")
def __proceed_instantiation(self, nffg): """ Helper function to instantiate the NFFG mapping from different source. :param nffg: pre-mapped service request :type nffg: :any:`NFFG` :return: None """ log.getChild('API').info( "Invoke instantiate_nffg on %s with NF-FG: %s " % (self.__class__.__name__, nffg.name)) # Initiate request mapping mapped_nffg = self.resource_orchestrator.instantiate_nffg(nffg=nffg) log.getChild('API').debug( "Invoked instantiate_nffg on %s is finished" % self.__class__.__name__) # If mapping is not threaded and finished with OK if mapped_nffg is not None and not \ self.resource_orchestrator.mapper.threaded: self._proceed_to_install_NFFG(mapped_nffg=mapped_nffg) else: log.warning( "Something went wrong in service request instantiation: " "mapped service request is missing!")
def map(cls, graph, resource): """ Default mapping algorithm of ESCAPEv2. :param graph: Network Function forwarding Graph :type graph: :any:`NFFG` :param resource: global virtual resource info :type resource: :any:`NFFG` :return: mapped Network Function Forwarding Graph :rtype: :any:`NFFG` """ log.debug("Invoke mapping algorithm: %s - request: %s resource: %s" % (cls.__name__, graph, resource)) if graph is None: log.error("Missing request NFFG! Abort mapping process...") return if resource is None: log.error("Missing resource NFFG! Abort mapping process...") return try: # print graph.dump() mapper_params = CONFIG.get_mapping_config(layer=LAYER_NAME) mapped_nffg = MAP(request=graph.copy(), network=resource.copy(), **mapper_params) # Set mapped NFFG id for original SG request tracking mapped_nffg.id = graph.id mapped_nffg.name = graph.name + "-ros-mapped" log.debug("Mapping algorithm: %s is finished on NF-FG: %s" % (cls.__name__, graph)) # print mapped_nffg.dump() return mapped_nffg except MappingException as e: log.error( "Mapping algorithm unable to map given request! Cause:\n%s" % e.msg) log.warning("Mapping algorithm on %s is aborted!" % graph) return except BadInputException as e: log.error("Mapping algorithm refuse given input! Cause:\n%s" % e.msg) log.warning("Mapping algorithm on %s is aborted!" % graph) return except InternalAlgorithmException as e: log.critical( "Mapping algorithm fails due to implementation error or conceptual " "error! Cause:\n%s" % e.msg) log.warning("Mapping algorithm on %s is aborted!" % graph) raise except: log.exception("Got unexpected error during mapping process!")
def __collect_binding(dov, nfs): """ Collect mapping of given NFs on the global view(DoV). :param dov: global topology :type dov: :class:`NFFG` :param nfs: list of NFs :type nfs: list :return: mapping :rtype: list of dict """ mappings = [] # Process NFs for nf_id in nfs: mapping = {} # Get the connected infra node if nf_id not in dov: log.warning( "NF: %s is not found in the global topology(DoV)!" % nf_id) continue bisbis = [n.id for n in dov.infra_neighbors(nf_id)] log.log(VERBOSE, "Detected mapped BiSBiS node:" % bisbis) if len(bisbis) != 1: log.warning("Detected unexpected number of BiSBiS node: %s!" % bisbis) continue bisbis = bisbis.pop() # Add NF id nf = {"id": nf_id, "ports": []} for dyn_link in dov.network[nf_id][bisbis].itervalues(): port = OrderedDict(id=dyn_link.src.id) if dyn_link.src.l4 is not None: try: port['management'] = ast.literal_eval(dyn_link.src.l4) except SyntaxError: log.warning( "L4 address entry: %s is not valid Python expression! " "Add the original string..." % dyn_link.src.l4) port['management'] = dyn_link.src.l4 nf['ports'].append(port) mapping['nf'] = nf # Add infra node ID and domain name bisbis = bisbis.split('@') bb_mapping = { "id": bisbis[0], "domain": bisbis[1] if len(bisbis) > 1 else None } mapping['bisbis'] = bb_mapping mappings.append(mapping) return mappings
def collect_mappings(self, mappings, slor_topo): """ Collect mapping information of NF in given mappings and return with an extended mapping structure. :param mappings: received mapping object :type mappings: :class:`Virtualizer` :param slor_topo: layer view of topology :type slor_topo: :class:`NFFG` :return: mapping structure extended with embedding info :rtype: :class:`Virtualizer` """ dov = self.virtualizerManager.dov.get_resource_info() response = mappings.yang_copy() log.debug("Start checking mappings...") for mapping in response: bb, nf = detect_bb_nf_from_path(path=mapping.object.get_value(), topo=slor_topo) if not nf: mapping.target.object.set_value("NOT_FOUND") mapping.target.domain.set_value("N/A") continue m_result = self.__collect_binding(dov=dov, nfs=[nf]) if not m_result: log.warning("Mapping is not found for NF: %s!" % nf) mapping.target.object.set_value("NOT_FOUND") mapping.target.domain.set_value("N/A") continue try: node = m_result[0]['bisbis']['id'] except KeyError: log.warning("Missing mapping node element from: %s" % m_result) node = "NOT_FOUND" try: domain = m_result[0]['bisbis']['domain'] except KeyError: log.warning("Missing mapping domain element from: %s" % m_result) domain = "N/A" log.debug("Found mapping: %s@%s (domain: %s)" % (nf, node, domain)) mapping.target.object.set_value(NF_PATH_TEMPLATE % (node, nf)) mapping.target.domain.set_value(domain) return response
def preprocess_nffg(self, nffg): try: # if there is at least ONE SGHop in the graph, we don't do SGHop retrieval. next(nffg.sg_hops) except StopIteration: # retrieve the SGHops from the TAG values of the flow rules, in case they # are cannot be found in the request graph and can only be deduced from the # flows log.warning("No SGHops were given in the Service Graph, retrieving them" " based on the flowrules...") sg_hops_given = False sg_hop_info = NFFGToolBox.retrieve_all_SGHops(nffg) if len(sg_hop_info) == 0: raise uet.BadInputException("If SGHops are not given, flowrules should be" " in the NFFG", "No SGHop could be retrieved based on the " "flowrules of the NFFG.") for k, v in sg_hop_info.iteritems(): # VNF ports are given to the function nffg.add_sglink(v[0], v[1], flowclass=v[2], bandwidth=v[3], delay=v[4], id=k[2]) DPDK_COST=0 VHOST_COST=0 KNI_COST=0 for sap in nffg.saps: if sap.id.startswith("dpdk"): dpdk=sap.id.split('-')[0] dpdk_nf=nffg.add_nf(id=dpdk) dpdk_nf.resources.cpu=DPDK_COST dpdk_nf.resources.mem=0 dpdk_nf.resources.storage=0 dpdk_nf.functional_type=dpdk hops= [hop for hop in nffg.sg_hops] for hop in hops: if hop.src.node.id.startswith("dpdk") and hop.dst.node.id.startswith("dpdk"): dpdk_in_id=hop.src.node.id.split('-')[0] dpdk_out_id=hop.dst.node.id.split('-')[0] dpdk_in=nffg.network.node[dpdk_in_id] dpdk_out=nffg.network.node[dpdk_out_id] link_in=nffg.add_sglink(hop.src,dpdk_in.add_port()) link_out=nffg.add_sglink(dpdk_out.add_port(), hop.dst) link=hop.copy() link.src=dpdk_in.add_port() link.dst=dpdk_out.add_port() nffg.del_edge(hop.src,hop.dst,hop.id) nffg.add_sglink(link.src,link.dst,hop=link) for req in nffg.reqs: if hop.id in req.sg_path: req.sg_path.insert(0,link_in.id) req.sg_path.insert(len(req.sg_path),link_out.id) nfs = [nf for nf in nffg.nfs if not nf.functional_type.startswith("dpdk")] for nf in nfs: if nf.functional_type.endswith("KNI"): NF_TYPE="KNI" PORT_RES=KNI_COST elif nf.functional_type.endswith("VHOST"): NF_TYPE="VHOST" PORT_RES=VHOST_COST if len(nf.ports) > 1: in_nf = nf.copy() in_nf.ports.clear() in_nf.resources.cpu=PORT_RES in_nf.resources.mem=0 in_nf.id=nf.id + "-in" in_nf.functional_type=NF_TYPE nffg.add_nf(nf=in_nf) out_nf = nf.copy() out_nf.ports.clear() out_nf.resources.cpu=PORT_RES out_nf.resources.mem=0 out_nf.functional_type=NF_TYPE out_nf.id=nf.id + "-out" nffg.add_nf(nf=out_nf) else: in_nf = nf.copy() in_nf.ports.clear() in_nf.resources.cpu=PORT_RES in_nf.resources.mem=0 in_nf.id=nf.id + "-inout" in_nf.functional_type=NF_TYPE nffg.add_nf(nf=in_nf) out_nf=in_nf in_tag=None out_tag=None dpdk_in=None dpdk_out=None hops= [hop for hop in nffg.sg_hops] for hop in hops: if hop.dst.node.id == nf.id: in_tag=hop.id try: in_port = nffg.network.node[in_nf.id].ports[hop.dst.id] except KeyError: in_port = nffg.network.node[in_nf.id].add_port(id=hop.dst.id) old_hop=hop.copy() old_hop.dst=in_port nffg.del_edge(hop.src,hop.dst,hop.id) if hop.src.node.id.startswith("dpdk"): dpdk_nf_id=hop.src.node.id.split('-')[0] dpdk_nf = nffg.network.node[dpdk_nf_id] port1=dpdk_nf.add_port() old_hop.src=port1 nffg.add_sglink(port1,in_port,hop=old_hop) port2=dpdk_nf.add_port() dpdk_in=nffg.add_sglink(hop.src,port2) else: nffg.add_sglink(hop.src,in_port,hop=old_hop) if hop.src.node.id == nf.id: out_tag=hop.id try: out_port = nffg.network.node[out_nf.id].ports[hop.src.id] except KeyError: out_port = nffg.network.node[out_nf.id].add_port(id=hop.src.id) old_hop=hop.copy() old_hop.src=out_port nffg.del_edge(hop.src,hop.dst,hop.id) if hop.dst.node.id.startswith("dpdk"): dpdk_nf_id= hop.dst.node.id.split('-')[0] dpdk_nf = nffg.network.node[dpdk_nf_id] port1=dpdk_nf.add_port() old_hop.dst=port1 nffg.add_sglink(out_port,port1,hop=old_hop) port2=dpdk_nf.add_port() dpdk_out=nffg.add_sglink(port2,hop.dst) else: nffg.add_sglink(out_port,hop.dst,hop=old_hop) vport_in=in_nf.add_port() vport_out=out_nf.add_port() aff_reqs=[] prev=None for req in nffg.reqs: for elem in req.sg_path: if prev == in_tag and elem == out_tag: aff_reqs.append(req) prev=elem cpu_req=int(nf.resources.cpu) if cpu_req==0: num_cpu=1 else: num_cpu=cpu_req for i in range(num_cpu): new_nf=nf.copy() new_nf.ports.clear() new_nf.resources.cpu=cpu_req / num_cpu new_nf.resources.mem=nf.resources.mem / num_cpu new_nf.id=nf.id + "-core" + str(i) nffg.add_nf(nf=new_nf) new_port1=new_nf.add_port() sg1=nffg.add_sglink(vport_in,new_port1) new_port2=new_nf.add_port() sg2=nffg.add_sglink(new_port2,vport_out) for req in aff_reqs: new_req=req.copy() new_req.regenerate_id() poz=new_req.sg_path.index(in_tag) new_req.sg_path.insert(poz+1, sg1.id) new_req.sg_path.insert(poz+2, sg2.id) if dpdk_in is not None: new_req.sg_path.insert(0, dpdk_in.id) if dpdk_out is not None: new_req.sg_path.insert(len(new_req.sg_path), dpdk_out.id) nffg.add_req(req.src,req.dst,req=new_req) nffg.del_node(nf.id) for req in aff_reqs: nffg.del_edge(req.src,req.dst,req.id) print nffg.dump() return nffg
def instantiate_nffg (self, nffg): """ Main API function for NF-FG instantiation. :param nffg: NFFG instance :type nffg: :any:`NFFG` :return: mapped NFFG instance :rtype: :any:`NFFG` """ log.debug("Invoke %s to instantiate given NF-FG" % self.__class__.__name__) # Store newly created NF-FG self.nffgManager.save(nffg) # Get Domain Virtualizer to acquire global domain view global_view = self.virtualizerManager.dov # Notify remote visualizer about resource view of this layer if it's needed notify_remote_visualizer(data=global_view.get_resource_info(), id=LAYER_NAME) # Log verbose mapping request log.log(VERBOSE, "Orchestration Layer request graph:\n%s" % nffg.dump()) # Start Orchestrator layer mapping print nffg.dump() if global_view is not None: if isinstance(global_view, AbstractVirtualizer): # If the request is a bare NFFG, it is probably an empty topo for domain # deletion --> skip mapping to avoid BadInputException and forward # topo to adaptation layer if nffg.is_bare(): log.warning("No valid service request (VNFs/Flowrules/SGhops) has " "been detected in SG request! Skip orchestration in " "layer: %s and proceed with the bare %s..." % (LAYER_NAME, nffg)) if nffg.is_virtualized(): if nffg.is_SBB(): log.debug("Request is a bare SingleBiSBiS representation!") else: log.warning( "Detected virtualized representation with multiple BiSBiS " "nodes! Currently this type of virtualization is nut fully" "supported!") else: log.debug("Detected full view representation!") # Return with the original request return nffg else: log.info("Request check: detected valid content!") try: # Run Nf-FG mapping orchestration log.debug("Starting request preprocession...") log.info(int(round(time.time() * 1000))) self.preprocess_nffg(nffg) log.debug("Preprocession ended, start mapping") log.info(int(round(time.time() * 1000))) mapped_nffg = self.mapper.orchestrate(nffg, global_view) log.debug("NF-FG instantiation is finished by %s" % self.__class__.__name__) log.info(int(round(time.time() * 1000))) return mapped_nffg except ProcessorError as e: log.warning("Mapping pre/post processing was unsuccessful! " "Cause: %s" % e) else: log.warning("Global view is not subclass of AbstractVirtualizer!") else: log.warning("Global view is not acquired correctly!") log.error("Abort orchestration process!")
def instantiate_nffg(self, nffg, continued_request_id=None): """ Main API function for NF-FG instantiation. :param nffg: NFFG instance :type nffg: :class:`NFFG` :param continued_request_id: use explicit request id if request is continued after a trial and error (default: False) :type continued_request_id: str or None :return: mapped NFFG instance :rtype: :class:`NFFG` """ log.debug("Invoke %s to instantiate given NF-FG" % self.__class__.__name__) if not continued_request_id: # Store newly created NF-FG self.nffgManager.save(nffg) else: # Use the original NFFG requested for getting the original request nffg = self.nffgManager.get(nffg_id=continued_request_id) log.info("Using original request for remapping: %s" % nffg) # Get Domain Virtualizer to acquire global domain view global_view = self.virtualizerManager.dov # Notify remote visualizer about resource view of this layer if it's needed # notify_remote_visualizer(data=global_view.get_resource_info(), # id=LAYER_NAME) # Log verbose mapping request log.log(VERBOSE, "Orchestration Layer request graph:\n%s" % nffg.dump()) # Start Orchestrator layer mapping if global_view is not None: # If the request is a bare NFFG, it is probably an empty topo for domain # deletion --> skip mapping to avoid BadInputException and forward # topo to adaptation layer if not continued_request_id: if nffg.is_bare(): log.warning( "No valid service request (VNFs/Flowrules/SGhops) has " "been detected in SG request! Skip orchestration in " "layer: %s and proceed with the bare %s..." % (LAYER_NAME, nffg)) if nffg.is_virtualized(): if nffg.is_SBB(): log.debug( "Request is a bare SingleBiSBiS representation!" ) else: log.warning( "Detected virtualized representation with multiple BiSBiS " "nodes! Currently this type of virtualization is nut fully " "supported!") else: log.debug("Detected full view representation!") # Return with the original request return nffg else: log.info("Request check: detected valid NFFG content!") try: # Run NF-FG mapping orchestration mapped_nffg = self.mapper.orchestrate( input_graph=nffg, resource_view=global_view, continued=bool(continued_request_id)) log.debug("NF-FG instantiation is finished by %s" % self.__class__.__name__) return mapped_nffg except ProcessorError as e: log.warning("Mapping pre/post processing was unsuccessful! " "Cause: %s" % e) # Propagate the ProcessError to API layer raise else: log.warning("Global view is not acquired correctly!") log.error("Abort orchestration process!")
def _perform_mapping(self, input_graph, resource_view): """ Orchestrate mapping of given NF-FG on given global resource. :param input_graph: Network Function Forwarding Graph :type input_graph: :any:`NFFG` :param resource_view: global resource view :type resource_view: :any:`DomainVirtualizer` :return: mapped Network Function Forwarding Graph :rtype: :any:`NFFG` """ if input_graph is None: log.error( "Missing mapping request information! Abort mapping process!") return None log.debug("Request %s to launch orchestration on NF-FG: %s with View: " "%s" % (self.__class__.__name__, input_graph, resource_view)) # Steps before mapping (optional) log.debug("Request global resource info...") virt_resource = resource_view.get_resource_info() if virt_resource is None: log.error("Missing resource information! Abort mapping process!") return None # log a warning if resource is empty --> possibly mapping will be failed if virt_resource.is_empty(): log.warning("Resource information is empty!") # Log verbose resource view if it is exist log.log( VERBOSE, "Orchestration Layer resource graph:\n%s" % virt_resource.dump()) # Check if the mapping algorithm is enabled if not CONFIG.get_mapping_enabled(LAYER_NAME): log.warning("Mapping algorithm in Layer: %s is disabled! " "Skip mapping step and return service request " "to lower layer..." % LAYER_NAME) # virt_resource.id = input_graph.id # return virt_resource # Send request forward (probably to Remote ESCAPE) return input_graph # Run actual mapping algorithm if self._threaded: # Schedule a microtask which run mapping algorithm in a Python thread log.info("Schedule mapping algorithm: %s in a worker thread" % self.strategy.__name__) call_as_coop_task(self._start_mapping, graph=input_graph, resource=virt_resource) log.info("NF-FG: %s orchestration is finished by %s" % (input_graph, self.__class__.__name__)) # Return with None return None else: mapped_nffg = self.strategy.map(graph=input_graph, resource=virt_resource) if mapped_nffg is None: log.error( "Mapping process is failed! Abort orchestration process.") else: # Steps after mapping (optional) log.info( "NF-FG: %s orchestration is finished by %s successfully!" % (input_graph, self.__class__.__name__)) return mapped_nffg
def __collect_binding(dov, nfs): """ Collect mapping of given NFs on the global view(DoV) with the structure: .. code-block:: json [ { "bisbis": { "domain": null, "id": "EE2" }, "nf": { "id": "fwd", "ports": [ { "id": 1, "management": { "22/tcp": [ "0.0.0.0", 20000 ] } } ] } } ] :param dov: global topology :type dov: :class:`NFFG` :param nfs: list of NFs :type nfs: list :return: mapping :rtype: list of dict """ mappings = [] # Process NFs for nf_id in nfs: mapping = {} # Get the connected infra node if nf_id not in dov: log.warning("NF: %s in to in the global topology(DoV)!" % nf_id) continue bisbis = [n.id for n in dov.infra_neighbors(nf_id)] log.log(VERBOSE, "Detected mapped BiSBiS node:" % bisbis) if len(bisbis) != 1: log.warning("Detected unexpected number of BiSBiS node: %s!" % bisbis) continue bisbis = bisbis.pop() # Add NF id nf = {"id": nf_id, "ports": []} for dyn_link in dov.network[nf_id][bisbis].itervalues(): port = OrderedDict(id=dyn_link.src.id) if dyn_link.src.l4 is not None: try: port['management'] = ast.literal_eval(dyn_link.src.l4) except SyntaxError: log.warning( "L4 address entry: %s is not valid Python expression! " "Add the original string..." % dyn_link.src.l4) port['management'] = dyn_link.src.l4 nf['ports'].append(port) mapping['nf'] = nf # Add infra node ID and domain name bisbis = bisbis.split('@') bb_mapping = { "id": bisbis[0], "domain": bisbis[1] if len(bisbis) > 1 else None } if bb_mapping.get("domain"): domain_url = CONFIG.get_domain_url( domain=bb_mapping.get("domain")) log.debug("Domain: %s - Detected URL: %s" % (bb_mapping.get("domain"), domain_url)) if domain_url is not None: bb_mapping["url"] = domain_url else: log.warning("Missing URL for domain: %s!" % bb_mapping["domain"]) mapping['bisbis'] = bb_mapping mappings.append(mapping) return mappings
def __proceed_instantiation(self, nffg, resource_nffg): """ Helper function to instantiate the NFFG mapping from different source. :param nffg: pre-mapped service request :type nffg: :class:`NFFG` :return: None """ self.log.info("Invoke instantiation on %s with NF-FG: %s" % (self.__class__.__name__, nffg.name)) stats.add_measurement_start_entry(type=stats.TYPE_ORCHESTRATION, info=LAYER_NAME) # Get shown topology view if resource_nffg is None: log.error("Missing resource for difference calculation!") return log.debug("Got resource view for difference calculation: %s" % resource_nffg) self.log.debug("Store received NFFG request info...") msg_id = self.api_mgr.request_cache.cache_request_by_nffg(nffg=nffg) if msg_id is not None: self.api_mgr.request_cache.set_in_progress(id=msg_id) self.log.debug("Request is stored with id: %s" % msg_id) else: self.log.debug("No request info detected.") # Check if mapping mode is set globally in CONFIG mapper_params = CONFIG.get_mapping_config(layer=LAYER_NAME) if 'mode' in mapper_params and mapper_params['mode'] is not None: mapping_mode = mapper_params['mode'] log.info("Detected mapping mode from configuration: %s" % mapping_mode) elif nffg.mode is not None: mapping_mode = nffg.mode log.info("Detected mapping mode from NFFG: %s" % mapping_mode) else: mapping_mode = None log.info("No mapping mode was defined explicitly!") if not CONFIG.get_mapping_enabled(layer=LAYER_NAME): log.warning("Mapping is disabled! Skip difference calculation...") elif nffg.status == NFFG.MAP_STATUS_SKIPPED: log.debug("Detected NFFG map status: %s! " "Skip difference calculation and " "proceed with original request..." % nffg.status) elif mapping_mode != NFFG.MODE_REMAP: # Calculated ADD-DELETE difference log.debug("Calculate ADD - DELETE difference with mapping mode...") # Recreate SG-hops for diff calc. log.debug("Recreate SG hops for difference calculation...") NFFGToolBox.recreate_all_sghops(nffg=nffg) NFFGToolBox.recreate_all_sghops(nffg=resource_nffg) log.log(VERBOSE, "New NFFG:\n%s" % nffg.dump()) log.log(VERBOSE, "Resource NFFG:\n%s" % resource_nffg.dump()) # Calculate difference add_nffg, del_nffg = NFFGToolBox.generate_difference_of_nffgs( old=resource_nffg, new=nffg, ignore_infras=True) log.log(VERBOSE, "Calculated ADD NFFG:\n%s" % add_nffg.dump()) log.log(VERBOSE, "Calculated DEL NFFG:\n%s" % del_nffg.dump()) if not add_nffg.is_bare() and del_nffg.is_bare(): nffg = add_nffg log.info("DEL NFFG is bare! Calculated mapping mode: %s" % nffg.mode) elif add_nffg.is_bare() and not del_nffg.is_bare(): nffg = del_nffg log.info("ADD NFFG is bare! Calculated mapping mode: %s" % nffg.mode) elif not add_nffg.is_bare() and not del_nffg.is_bare(): log.warning("Both ADD / DEL mode is not supported currently") self.__process_mapping_result(nffg_id=nffg.id, fail=True) stats.add_measurement_end_entry(type=stats.TYPE_ORCHESTRATION, info=LAYER_NAME + "-FAILED") self.raiseEventNoErrors( InstantiationFinishedEvent, id=nffg.id, result=InstantiationFinishedEvent.ABORTED) return else: log.debug("Difference calculation resulted empty subNFFGs!") log.warning( "No change has been detected in request! Skip mapping...") self.log.debug("Invoked instantiation on %s is finished!" % self.__class__.__name__) self.__process_mapping_result(nffg_id=nffg.id, fail=False) stats.add_measurement_end_entry(type=stats.TYPE_ORCHESTRATION, info=LAYER_NAME + "-SKIPPED") return else: log.debug( "Mode: %s detected from config! Skip difference calculation..." % mapping_mode) try: if CONFIG.get_mapping_enabled(layer=LAYER_NAME): # Initiate request mapping mapped_nffg = self.orchestrator.instantiate_nffg(nffg=nffg) else: log.warning("Mapping is disabled! Skip instantiation step...") mapped_nffg = nffg mapped_nffg.status = NFFG.MAP_STATUS_SKIPPED log.debug("Mark NFFG status: %s!" % mapped_nffg.status) # Rewrite REMAP mode for backward compatibility if mapped_nffg is not None and mapping_mode == NFFG.MODE_REMAP: mapped_nffg.mode = mapping_mode log.debug("Rewrite mapping mode: %s into mapped NFFG..." % mapped_nffg.mode) else: log.debug("Skip mapping mode rewriting! Mode remained: %s" % mapping_mode) self.log.debug("Invoked instantiate_nffg on %s is finished!" % self.__class__.__name__) # If mapping is not threaded and finished with OK if mapped_nffg is not None and not self.orchestrator.mapper.threaded: self._proceed_to_install_NFFG(mapped_nffg=mapped_nffg, original_request=nffg) else: log.warning( "Something went wrong in service request instantiation: " "mapped service request is missing!") self.__process_mapping_result(nffg_id=nffg.id, fail=True) stats.add_measurement_end_entry(type=stats.TYPE_ORCHESTRATION, info=LAYER_NAME + "-FAILED") self.raiseEventNoErrors( InstantiationFinishedEvent, id=nffg.id, result=InstantiationFinishedEvent.MAPPING_ERROR) except ProcessorError as e: self.__process_mapping_result(nffg_id=nffg.id, fail=True) stats.add_measurement_end_entry(type=stats.TYPE_ORCHESTRATION, info=LAYER_NAME + "-DENIED") self.raiseEventNoErrors( InstantiationFinishedEvent, id=nffg.id, result=InstantiationFinishedEvent.REFUSED_BY_VERIFICATION, error=e)
def map(cls, graph, resource): """ Default mapping algorithm of ESCAPEv2. :param graph: Network Function forwarding Graph :type graph: :class:`NFFG` :param resource: global virtual resource info :type resource: :class:`NFFG` :return: mapped Network Function Forwarding Graph :rtype: :class:`NFFG` """ log.info("Invoke mapping algorithm: %s - request: %s resource: %s" % (cls.__name__, graph, resource)) if graph is None: log.error("Missing request NFFG! Abort mapping process...") return if resource is None: log.error("Missing resource NFFG! Abort mapping process...") return try: # Run pre-mapping step to resolve target-less flowrules cls._resolve_external_ports(graph, resource) # Copy mapping config mapper_params = CONFIG.get_mapping_config( layer=cls.LAYER_NAME).copy() if 'mode' in mapper_params and mapper_params['mode']: log.debug("Setup mapping mode from configuration: %s" % mapper_params['mode']) elif graph.mode: mapper_params['mode'] = graph.mode log.debug("Setup mapping mode based on request: %s" % mapper_params['mode']) mapped_nffg = cls.call_mapping_algorithm(request=graph.copy(), topology=resource.copy(), **mapper_params) # Set mapped NFFG id for original SG request tracking log.debug("Move request metadata into mapping result...") mapped_nffg.id = graph.id mapped_nffg.name = "%s-%s-mapped" % (graph.name, cls.LAYER_NAME) # Explicitly copy metadata mapped_nffg.metadata = graph.metadata.copy() # Explicit copy of SAP data for sap in graph.saps: if sap.id in mapped_nffg: mapped_nffg[sap.id].metadata = graph[ sap.id].metadata.copy() log.info("Mapping algorithm: %s is finished on NF-FG: %s" % (cls.__name__, mapped_nffg)) return mapped_nffg except MappingException as e: log.error( "Mapping algorithm unable to map given request! Cause:\n%s" % e.msg) log.warning("Mapping algorithm on %s is aborted!" % graph) return except BadInputException as e: log.error("Mapping algorithm refuse given input! Cause:\n%s" % e.msg) log.warning("Mapping algorithm on %s is aborted!" % graph) return except InternalAlgorithmException as e: log.critical( "Mapping algorithm fails due to internal error! Cause:\n%s" % e.msg) log.warning("Mapping algorithm on %s is aborted!" % graph) return except: log.exception("Got unexpected error during mapping process!")
def edit_config(self): """ Receive configuration and initiate orchestration. :return: None """ self.log.info("Call %s function: edit-config" % self.LOGGER_NAME) # Obtain NFFG from request body self.log.debug("Detected response format: %s" % self.headers.get("Content-Type")) raw_body = self._get_body() # log.getChild("REST-API").debug("Request body:\n%s" % body) if raw_body is None or not raw_body: log.warning("Received data is empty!") self.send_error(400, "Missing body!") return # Expect XML format --> need to convert first if self.virtualizer_format_enabled: if self.headers.get("Content-Type") != "application/xml" and \ not raw_body.startswith("<?xml version="): self.log.error( "Received data is not in XML format despite of the " "UNIFY interface is enabled!") self.send_error(415) return # Get received Virtualizer received_cfg = Virtualizer.parse_from_text(text=raw_body) self.log.log(VERBOSE, "Received request for 'edit-config':\n%s" % raw_body) # If there was not get-config request so far if self.DEFAULT_DIFF: if self.server.last_response is None: self.log.info( "Missing cached Virtualizer! Acquiring topology now..." ) config = self._proceed_API_call(self.API_CALL_GET_CONFIG) if config is None: self.log.error("Requested resource info is missing!") self.send_error(404, message="Resource info is missing!") return elif config is False: self.log.warning( "Requested info is unchanged but has not found!") self.send_error(404, message="Resource info is missing!") else: # Convert required NFFG if needed if self.virtualizer_format_enabled: self.log.debug( "Convert internal NFFG to Virtualizer...") converter = NFFGConverter(domain=None, logger=log) v_topology = converter.dump_to_Virtualizer( nffg=config) # Cache converted data for edit-config patching self.log.debug("Cache converted topology...") self.server.last_response = v_topology else: self.log.debug("Cache acquired topology...") self.server.last_response = config # Perform patching full_cfg = self.__recreate_full_request(diff=received_cfg) else: full_cfg = received_cfg self.log.log(VERBOSE, "Generated request:\n%s" % full_cfg.xml()) # Convert response's body to NFFG self.log.info("Converting full request data...") converter = NFFGConverter(domain="REMOTE", logger=log) nffg = converter.parse_from_Virtualizer(vdata=full_cfg) else: if self.headers.get("Content-Type") != "application/json": self.log.error( "Received data is not in JSON format despite of the " "UNIFY interface is disabled!") self.send_error(415) return # Initialize NFFG from JSON representation self.log.info("Parsing request into internal NFFG format...") nffg = NFFG.parse(raw_body) self.log.debug("Parsed NFFG install request: %s" % nffg) self._proceed_API_call(self.API_CALL_EDIT_CONFIG, nffg) self.send_acknowledge() self.log.debug("%s function: edit-config ended!" % self.LOGGER_NAME)
def _perform_mapping(self, input_graph, resource_view, continued=False): """ Orchestrate mapping of given NF-FG on given global resource. :param input_graph: Network Function Forwarding Graph :type input_graph: :class:`NFFG` :param resource_view: global resource view :type resource_view: :any:`DomainVirtualizer` :return: mapped Network Function Forwarding Graph :rtype: :class:`NFFG` """ if input_graph is None: log.error( "Missing mapping request information! Abort mapping process!") return None log.debug( "Request %s to launch orchestration on NF-FG: %s with View: " "%s, continued remap: %s" % (self.__class__.__name__, input_graph, resource_view, continued)) # Steps before mapping (optional) log.debug("Request global resource info...") virt_resource = resource_view.get_resource_info() if virt_resource is None: log.error("Missing resource information! Abort mapping process!") return None # log a warning if resource is empty --> possibly mapping will be failed if virt_resource.is_empty(): log.warning("Resource information is empty!") # Log verbose resource view if it is exist log.log( VERBOSE, "Orchestration Layer resource graph:\n%s" % virt_resource.dump()) # Check if the mapping algorithm is enabled if not CONFIG.get_mapping_enabled(LAYER_NAME): log.warning("Mapping algorithm in Layer: %s is disabled! " "Skip mapping step and return service request " "to lower layer..." % LAYER_NAME) # virt_resource.id = input_graph.id # return virt_resource # Send request forward (probably to Remote ESCAPE) input_graph.status = NFFG.MAP_STATUS_SKIPPED log.debug("Mark NFFG status: %s!" % input_graph.status) return input_graph # Run actual mapping algorithm if self._threaded: # Schedule a microtask which run mapping algorithm in a Python thread log.info("Schedule mapping algorithm: %s in a worker thread" % self.strategy.__name__) call_as_coop_task(self._start_mapping, graph=input_graph, resource=virt_resource) log.info("NF-FG: %s orchestration is finished by %s" % (input_graph, self.__class__.__name__)) # Return with None return None else: state = self.last_mapping_state if continued else None mapping_result = self.strategy.map( graph=input_graph, resource=virt_resource, persistent=self.persistent_state, pre_state=state) if isinstance(mapping_result, tuple or list): if len(mapping_result) == 2: mapped_nffg = mapping_result[0] self.persistent_state = mapping_result[1] log.debug("Cache returned persistent state: %s" % self.persistent_state) elif len(mapping_result) == 3: mapped_nffg = mapping_result[0] self.persistent_state = mapping_result[1] log.debug("Cache returned persistent state: %s" % self.persistent_state) self.last_mapping_state = mapping_result[2] log.debug("Cache returned mapping state: %s" % self.last_mapping_state) else: log.error("Mapping result is invalid: %s" % repr(mapping_result)) mapped_nffg = None else: mapped_nffg = mapping_result # Check error result if mapped_nffg is None: log.error( "Mapping process is failed! Abort orchestration process.") else: # Steps after mapping (optional) log.info( "NF-FG: %s orchestration is finished by %s successfully!" % (input_graph, self.__class__.__name__)) log.debug("Last mapping state: %s" % self.last_mapping_state) if self.last_mapping_state: log.debug("Mapping iteration: %s" % self.last_mapping_state.get_number_of_trials() if self.last_mapping_state else None) return mapped_nffg