def addRelationship(self, relationship): """ Add relationship between two existing nodes :param relationship: relationship to be added between two nodes :type relationship: dict :return: success of the addition :rtype: Boolean """ node1 = list( self.graph_db.find(relationship['src_label'], 'node_id', relationship['src_id'])) node2 = list( self.graph_db.find(relationship['dst_label'], 'node_id', relationship['dst_id'])) if len(node1) > 0 and len(node2) > 0: rel = Relationship(node1[0], relationship['rel_type'], node2[0]) for key, value in relationship.items(): rel.properties[key] = value self.graph_db.create(rel) return True else: log.debug("nodes do not exist in the DB") return False
def removeRelationship(self, relationship): """ Remove the relationship between two nodes in the DB. :param relationship: the relationship to be removed :type relationship: dict :return: the success of the removal :rtype: Boolean """ node1 = list( self.graph_db.find(relationship['src_label'], 'node_id', relationship['src_id'])) node2 = list( self.graph_db.find(relationship['dst_label'], 'node_id', relationship['dst_id'])) if len(node1) > 0 and len(node2) > 0: rels = list( self.graph_db.match(start_node=node1[0], end_node=node2[0], rel_type=relationship['rel_type'])) for r in rels: r.delete() return True else: log.debug("nodes do not exist in the DB") return False
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 getSingleDecomp(self, decomp_id): """ Get a decomposition with id decomp_id. : param decomp_id: the id of the decomposition to be returned : type decomp_id: str : return: decomposition with id equal to decomp_id : rtype: tuple of networkx.DiGraph and Relationships """ graph = networkx.DiGraph() node = list(self.graph_db.find('graph', 'node_id', decomp_id)) if len(node) != 0: rels = list( self.graph_db.match(start_node=node[0], rel_type='CONTAINS')) for rel in rels: graph.add_node(rel.end_node.properties['node_id']) graph.node[rel.end_node.properties['node_id']][ 'properties'] = rel.end_node.properties for rel in rels: rel_CONNECTED = list( self.graph_db.match(start_node=rel.end_node, rel_type='CONNECTED')) for rel_c in rel_CONNECTED: if rel_c.end_node.properties['node_id'] in graph.nodes(): graph.add_edge(rel_c.start_node.properties['node_id'], rel_c.end_node.properties['node_id']) graph.edge[rel_c.start_node.properties['node_id']][ rel_c.end_node.properties['node_id']][ 'properties'] = rel_c.properties return graph, rels else: log.debug("decomposition %s does not exist in the DB" % decomp_id) return None
def removeDecomp (self, decomp_id): """ Remove a decomposition from the DB. :param decomp_id: the id of the decomposition to be removed from the DB :type decomp_id: string :return: the success of the removal :rtype: Boolean """ node = list(self.graph_db.find('graph', 'node_id', decomp_id)) if len(node) > 0: queue = deque([node[0]]) while len(queue) > 0: node = queue.popleft() # we search for all the nodes with relationship CONTAINS or DECOMPOSED rels_CONTAINS = list( self.graph_db.match(start_node=node, rel_type='CONTAINS')) rels_DECOMPOSED = list( self.graph_db.match(start_node=node, rel_type='DECOMPOSED')) if len(rels_CONTAINS) > 0: rels = rels_CONTAINS else: rels = rels_DECOMPOSED for rel in rels: if len(list(self.graph_db.match(end_node=rel.end_node, rel_type='CONTAINS'))) <= 1: queue.append(rel.end_node) node.isolate() node.delete() return True else: log.debug("decomposition %s does not exist in the DB" % decomp_id) return False
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 _handle_InstallationFinishedEvent(self, event): """ Get information from NFFG installation process. :param event: event object info :type event: :any:`InstallationFinishedEvent` :return: None """ if not InstantiationFinishedEvent.is_error(event.result): self.log.info( "NF-FG(%s) instantiation has been finished successfully " "with result: %s!" % (event.id, event.result)) else: self.log.error( "NF-FG(%s) instantiation has been finished with error " "result: %s!" % (event.id, event.result)) if InstantiationFinishedEvent.is_deploy_error(event.result): if CONFIG.get_trial_and_error(layer=LAYER_NAME): log.info( "TRIAL_AND_ERROR is enabled! Reschedule for mapping..." ) self.__proceed_trial_and_error( original_request_id=event.id) return else: log.debug("TRIAL_AND_ERROR is disabled! Proceeding...") if not event.is_pending(event.result): self.__process_mapping_result(nffg_id=event.id, fail=event.is_error(event.result)) self.raiseEventNoErrors(InstantiationFinishedEvent, id=event.id, result=event.result)
def __init__ (self): """ Init. """ super(NFFGManager, self).__init__() log.debug("Init %s" % self.__class__.__name__) self._nffgs = dict()
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 filter_info_request(info, slor_topo): """ Filter out non-existent NFs from original request. :param info: parsed info object :type info: :class:`Info` :param slor_topo: layer view of topology :type slor_topo: :class:`NFFG` :return: original and modified Info object :rtype: :class:`Info` """ log.debug("Filter info request based on layer view: %s..." % slor_topo.id) info = info.yang_copy() for attr in (getattr(info, e) for e in info._sorted_children): deletable = [] for element in attr: if hasattr(element, "object"): bb, nf = detect_bb_nf_from_path(element.object.get_value(), slor_topo) if not nf: log.debug("Remove element: %s from request..." % element._tag) deletable.append(element) for d in deletable: attr.remove(d) return info
def __init__(self, strategy=None): """ Init Resource Orchestrator mapper. :return: None """ super(ResourceOrchestrationMapper, self).__init__(layer_name=LAYER_NAME, strategy=strategy) log.debug("Init %s with strategy: %s" % (self.__class__.__name__, self.strategy.__name__))
def __get_slor_resource_view(self): """ :return: Return with the Virtualizer object assigned to the Sl-Or interface. :rtype: :any:`AbstractVirtualizer` """ virt_mgr = self.orchestrator.virtualizerManager virtualizer_type = CONFIG.get_api_virtualizer(layer=self._core_name) params = CONFIG.get_virtualizer_params(layer=self._core_name) log.debug("Acquired Virtualizer type: %s, params: %s" % (virtualizer_type, params)) return virt_mgr.get_virtual_view(virtualizer_id=self._core_name, type=virtualizer_type, **params)
def __init__(self): """ Init. :return: None """ super(NFIBManager, self).__init__() log.debug("Init %s" % self.__class__.__name__) # Suppress low level logging self.__suppress_neo4j_logging() self.service_name = self.__detect_neo4j_service_name() self.__manage_neo4j_service() self.graph_db = None
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 __init__ (self): """ Init. """ super(NFIBManager, self).__init__() log.debug("Init %s based on neo4j" % self.__class__.__name__) # Suppress low level logging self.__suppress_neo4j_logging() try: self.graph_db = Graph() except Unauthorized as e: quit_with_error( "Got Unauthorozed error on: %s from neo4j! Disable the authorization " "in /etc/neo4j/neoj4-server.properties!" % e)
def save (self, nffg): """ Save NF-FG in a dict. :param nffg: Network Function Forwarding Graph :type nffg: :any:`NFFG` :return: generated ID of given NF-FG :rtype: int """ nffg_id = self._generate_id(nffg) self._nffgs[nffg_id] = nffg log.debug("NF-FG: %s is saved by %s with id: %s" % (nffg, self.__class__.__name__, nffg_id)) return nffg.id
def shutdown(self, event): """ .. seealso:: :func:`AbstractAPI.shutdown() <escape.util.api.AbstractAPI.shutdown>` :param event: event object """ log.info("Resource Orchestration Sublayer is going down...") if self._agent or self._rosapi: log.debug("REST-API: %s is shutting down..." % self.ros_api.api_id) # self.ros_api.stop() if self._cfor: log.debug("REST-API: %s is shutting down..." % self.cfor_api.api_id)
def __manage_neo4j_service(self): """ Manage neo4j service. :return: None """ if not CONFIG.get_manage_neo4j_service(): log.debug("Skip Neo4j service management...") return log.debug("Detected Neo4j service name: %s" % self.service_name) if check_service_status(self.service_name): log.debug("%s service is already running..." % self.service_name) return log.info("Starting service: %s..." % self.service_name) ret = run_cmd('sudo service %s start' % self.service_name) if "failed" in ret: log.error("Neo4j service initiation status: %s" % ret) return log.log(VERBOSE, "Neo4j service initiation status: %s" % ret) # Check if the service has been started - only 5 try if port_tester(host=self.DB_HOST, port=self.DB_PORT, interval=1, period=10, log=log): log.debug("Neo4j service has been verified!") else: log.error("Neo4j service has not started correctly!")
def getNF(self, nf_id): """ Get the information for the NF with id equal to nf_id. :param nf_id: the id of the NF to get the information for :type nf_id: string :return: the information of NF with id equal to nf_id :rtype: dict """ node = list(self.graph_db.find('NF', 'node_id', nf_id)) if len(node) > 0: return node[0].properties else: log.debug("node %s does not exist in the DB" % nf_id) return None
def rest_api_get_config(self): """ Implementation of REST-API RPC: get-config. Return with the global resource as an :class:`NFFG` if it has been changed otherwise return with False. :return: global resource view (DoV) :rtype: :class:`NFFG` or False """ self.log.debug("Requesting Virtualizer for %s" % self._core_name) slor_virt = self.__get_slor_resource_view() if slor_virt is not None: # Check the topology is initialized if slor_virt.revision is None: self.log.debug("DoV has not initialized yet! " "Force to get default topology...") else: # Check if the resource is changed if self.api_mgr.topology_revision == slor_virt.revision: # If resource has not been changed return False # This causes to response with the cached topology self.log.debug( "Global resource has not changed (revision: %s)! " % slor_virt.revision) log.debug("Send topology from cache...") if self.api_mgr.last_response is None: log.error("Cached topology is missing!") return else: return self.api_mgr.last_response else: self.log.debug( "Response cache is outdated (new revision: %s)!" % slor_virt.revision) # Get topo view as NFFG res = slor_virt.get_resource_info() self.api_mgr.topology_revision = slor_virt.revision self.log.debug("Updated revision number: %s" % self.api_mgr.topology_revision) if CONFIG.get_rest_api_config(self._core_name)['unify_interface']: self.log.info("Convert internal NFFG to Virtualizer...") res = self.api_mgr.converter.dump_to_Virtualizer(nffg=res) log.debug("Cache acquired topology...") self.api_mgr.last_response = res return res else: log.error("Virtualizer assigned to %s is not found!" % self._core_name)
def updateNF(self, nf): """ Update the information of a NF. :param nf: the information for the NF to be updated :type nf: dict :return: success of the update :rtype: Boolean """ node = list(self.graph_db.find(nf['label'], 'node_id', nf['node_id'])) if len(node) > 0: node[0].set_properties(nf) return True else: log.debug("node %s does not exist in the DB" % nf['node_id']) return False
def filter_info_request(self, info, slor_topo): log.debug("Filter info request based on layer view: %s..." % slor_topo.id) info = info.full_copy() for attr in (getattr(info, e) for e in info._sorted_children): deletable = [] for element in attr: if hasattr(element, "object"): bb, nf = detect_bb_nf_from_path(element.object.get_value(), slor_topo) if not nf: log.debug("Remove element: %s from request..." % element._tag) deletable.append(element) for d in deletable: attr.remove(d) return info
def _mapping_finished(self, mapped_nffg): """ Called from a separate thread when the mapping process is finished. :param mapped_nffg: mapped NF-FG :type mapped_nffg: :any:`NFFG` :return: None """ # TODO - rethink threaded/non-threaded function call paths to call port # mapping functions in a joint way only once if mapped_nffg is None: log.error( "Mapping process is failed! Abort orchestration process.") return None # Steps after mapping (optional) if the mapping was threaded log.debug( "Inform actual layer API that NFFG mapping has been finished...") self.raiseEventNoErrors(NFFGMappingFinishedEvent, mapped_nffg)
def rest_api_info(self, info, id, params): """ Main entry point to process a received Info request. :param info: parsed Info structure :type info: :class:`Info` :param id: unique id :type id: str or int """ self.log.debug("Cache 'info' request params with id: %s" % id) self.api_mgr.request_cache.cache_request(message_id=id, params=params) slor_topo = self.__get_slor_resource_view().get_resource_info() splitted = self.orchestrator.filter_info_request(info=info, slor_topo=slor_topo) log.debug("Propagate info request to adaptation layer...") self.raiseEventNoErrors(CollectMonitoringDataEvent, info=splitted, id=id)
def __init__(self, layer_API): """ Initialize main Resource Orchestration Layer components. :param layer_API: layer API instance :type layer_API: :any:`ResourceOrchestrationAPI` :return: None """ super(ResourceOrchestrator, self).__init__(layer_API=layer_API) log.debug("Init %s" % self.__class__.__name__) self.nffgManager = NFFGManager() # Init virtualizer manager # Listeners must be weak references in order the layer API can garbage # collected self.virtualizerManager = VirtualizerManager() self.virtualizerManager.addListeners(layer_API, weak=True) # Init NFIB manager self.nfibManager = NFIBManager().initialize()
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 addNode(self, node): """ Add new node to the DB. :param node: node to be added to the DB :type node: dict :return: success of addition :rtype: Boolean """ node_db = list( self.graph_db.find(node['label'], 'node_id', node['node_id'])) if len(node_db) > 0: log.debug("node %s exists in the DB" % node['node_id']) return False node_new = py2neo.Node(node['label'], node_id=node['node_id']) for key, value in node.items(): node_new.properties[key] = value self.graph_db.create(node_new) return True
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 removeNF(self, nf_id): """ Remove an NF and all its decompositions from the DB. :param nf_id: the id of the NF to be removed from the DB :type nf_id: string :return: success of removal :rtype: Boolean """ node = list(self.graph_db.find('NF', 'node_id', nf_id)) if len(node) > 0: rels_DECOMPOSE = list( self.graph_db.match(start_node=node[0], rel_type='DECOMPOSED')) for rel in rels_DECOMPOSE: self.removeDecomp(rel.end_node.properties['node_id']) node[0].delete_related() return True else: log.debug("node %s does not exist in the DB" % nf_id) return False
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.error("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)