def _initiate_cfor_api(self): """ Initialize and setup REST API in a different thread. :return: None """ # set bounded layer name here to avoid circular dependency problem handler = CONFIG.get_cfor_api_class() handler.bounded_layer = self._core_name params = CONFIG.get_cfor_agent_params() # can override from global config if 'prefix' in params: handler.prefix = params['prefix'] if 'unify_interface' in params: handler.virtualizer_format_enabled = params['unify_interface'] if 'diff' in params: handler.DEFAULT_DIFF = bool(params['diff']) address = (params.get('address'), params.get('port')) self.cfor_api = RESTServer(handler, *address) # Virtualizer ID of the Cf-Or interface self.cfor_api.api_id = handler.LOGGER_NAME = "Cf-Or" # Virtualizer type for Cf-Or API self.cfor_api.virtualizer_type = CONFIG.get_api_virtualizer( layer_name=LAYER_NAME, api_name=self.cfor_api.api_id) handler.log.info("Init REST-API for %s on %s:%s!" % (self.cfor_api.api_id, address[0], address[1])) self.cfor_api.start() handler.log.debug( "Enforced configuration for %s: virtualizer type: %s, interface: %s, " "diff: %s" % (self.cfor_api.api_id, self.cfor_api.virtualizer_type, "UNIFY" if handler.virtualizer_format_enabled else "Internal-NFFG", handler.DEFAULT_DIFF))
def _initiate_rest_api(self): """ Initialize and set up REST API in a different thread. :return: None """ # set bounded layer name here to avoid circular dependency problem handler = CONFIG.get_sas_api_class() handler.bounded_layer = self._core_name params = CONFIG.get_sas_agent_params() # can override from global config if 'prefix' in params: handler.prefix = params['prefix'] if 'unify_interface' in params: handler.virtualizer_format_enabled = params['unify_interface'] address = (params.get('address'), params.get('port')) self.rest_api = RESTServer(handler, *address) self.rest_api.api_id = handler.LOGGER_NAME = "U-Sl" handler.log.info("Init REST-API for %s on %s:%s!" % (self.rest_api.api_id, address[0], address[1])) self.rest_api.start() handler.log.debug( "Enforced configuration for %s: interface: %s" % (self.rest_api.api_id, "UNIFY" if handler.virtualizer_format_enabled else "Internal-NFFG"))
class ResourceOrchestrationAPI(AbstractAPI): """ Entry point for Resource Orchestration Sublayer (ROS). Maintain the contact with other UNIFY layers. Implement the Sl - Or reference point. """ # Define specific name for core object i.e. pox.core.<_core_name> _core_name = LAYER_NAME # Events raised by this class _eventMixin_events = { InstallNFFGEvent, GetGlobalResInfoEvent, VirtResInfoEvent, InstantiationFinishedEvent } # Dependencies dependencies = ('adaptation', ) def __init__(self, standalone=False, **kwargs): """ .. seealso:: :func:`AbstractAPI.__init__() <escape.util.api.AbstractAPI.__init__>` """ log.info("Starting Resource Orchestration Sublayer...") # Mandatory super() call self.resource_orchestrator = None super(ResourceOrchestrationAPI, self).__init__(standalone, **kwargs) 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 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) # self.cfor_api.stop() def _initiate_ros_api(self): """ Initialize and setup REST API in a different thread. If agent_mod is set rewrite the received NFFG domain from REMOTE to INTERNAL. :return: None """ # set bounded layer name here to avoid circular dependency problem handler = CONFIG.get_ros_agent_class() handler.bounded_layer = self._core_name params = CONFIG.get_ros_agent_params() # can override from global config if 'prefix' in params: handler.prefix = params['prefix'] if 'unify_interface' in params: handler.virtualizer_format_enabled = params['unify_interface'] if 'diff' in params: handler.DEFAULT_DIFF = bool(params['diff']) address = (params.get('address'), params.get('port')) # Virtualizer ID of the Sl-Or interface self.ros_api = RESTServer(handler, *address) self.ros_api.api_id = handler.LOGGER_NAME = "Sl-Or" # Virtualizer type for Sl-Or API self.ros_api.virtualizer_type = CONFIG.get_api_virtualizer( layer_name=LAYER_NAME, api_name=self.ros_api.api_id) handler.log.info("Init REST-API for %s on %s:%s!" % (self.ros_api.api_id, address[0], address[1])) self.ros_api.start() handler.log.debug( "Enforced configuration for %s: virtualizer type: %s, interface: %s, " "diff: %s" % (self.ros_api.api_id, self.ros_api.virtualizer_type, "UNIFY" if handler.virtualizer_format_enabled else "Internal-NFFG", handler.DEFAULT_DIFF)) if self._agent: log.info("REST-API is set in AGENT mode") def _initiate_cfor_api(self): """ Initialize and setup REST API in a different thread. :return: None """ # set bounded layer name here to avoid circular dependency problem handler = CONFIG.get_cfor_api_class() handler.bounded_layer = self._core_name params = CONFIG.get_cfor_agent_params() # can override from global config if 'prefix' in params: handler.prefix = params['prefix'] if 'unify_interface' in params: handler.virtualizer_format_enabled = params['unify_interface'] if 'diff' in params: handler.DEFAULT_DIFF = bool(params['diff']) address = (params.get('address'), params.get('port')) self.cfor_api = RESTServer(handler, *address) # Virtualizer ID of the Cf-Or interface self.cfor_api.api_id = handler.LOGGER_NAME = "Cf-Or" # Virtualizer type for Cf-Or API self.cfor_api.virtualizer_type = CONFIG.get_api_virtualizer( layer_name=LAYER_NAME, api_name=self.cfor_api.api_id) handler.log.info("Init REST-API for %s on %s:%s!" % (self.cfor_api.api_id, address[0], address[1])) self.cfor_api.start() handler.log.debug( "Enforced configuration for %s: virtualizer type: %s, interface: %s, " "diff: %s" % (self.cfor_api.api_id, self.cfor_api.virtualizer_type, "UNIFY" if handler.virtualizer_format_enabled else "Internal-NFFG", handler.DEFAULT_DIFF)) def _handle_NFFGMappingFinishedEvent(self, event): """ Handle NFFGMappingFinishedEvent and proceed with :class:`NFFG <escape.util.nffg.NFFG>` installation. :param event: event object :type event: :any:`NFFGMappingFinishedEvent` :return: None """ self._proceed_to_install_NFFG(event.nffg) ############################################################################## # Agent API functions starts here ############################################################################## def api_ros_get_config(self): """ Implementation of REST-API RPC: get-config. Return with the global resource as an :any:`NFFG` if it has been changed otherwise return with False. :return: global resource view (DoV) :rtype: :any:`NFFG` or False """ log.getChild('[Sl-Or]').info("Requesting Virtualizer for REST-API") virt = self.resource_orchestrator.virtualizerManager.get_virtual_view( virtualizer_id=self.ros_api.api_id, type=self.ros_api.virtualizer_type) if virt is not None: # Check if the resource is changed if virt.is_changed(): log.getChild('[Sl-Or]').info("Generate topo description...") res = virt.get_resource_info() return res # If resource has not been changed return False # This causes to response with the cached topology else: return False else: log.error("Virtualizer(id=%s) assigned to REST-API is not found!" % self.ros_api.api_id) def api_ros_edit_config(self, nffg): """ Implementation of REST-API RPC: edit-config :param nffg: NFFG need to deploy :type nffg: :any:`NFFG` """ log.getChild('[Sl-Or]').info("Invoke install_nffg on %s with SG: %s " % (self.__class__.__name__, nffg)) if self._agent: # ESCAPE serves as a local orchestrator, probably with infrastructure # layer --> rewrite domain nffg = self.__update_nffg_domain(nffg_part=nffg) # ESCAPE serves as a global or proxy orchestrator self.__proceed_instantiation(nffg=nffg) @staticmethod 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 ############################################################################## # Cf-Or API functions starts here ############################################################################## def api_cfor_get_config(self): """ Implementation of Cf-Or REST-API RPC: get-config. :return: dump of a single BiSBiS view based on DoV :rtype: str """ log.getChild('[Cf-Or]').info("Requesting Virtualizer for REST-API...") virt = self.resource_orchestrator.virtualizerManager.get_virtual_view( virtualizer_id=self.cfor_api.api_id, type=self.cfor_api.virtualizer_type) if virt is not None: log.getChild('[Cf-Or]').info("Generate topo description...") return virt.get_resource_info() else: log.error("Virtualizer(id=%s) assigned to REST-API is not found!" % self.cfor_api.api_id) def api_cfor_edit_config(self, nffg): """ Implementation of Cf-Or REST-API RPC: edit-config :param nffg: NFFG need to deploy :type nffg: :any:`NFFG` """ log.getChild('[Cf-Or]').info("Invoke install_nffg on %s with SG: %s " % (self.__class__.__name__, nffg)) self.__proceed_instantiation(nffg=nffg) ############################################################################## # UNIFY Sl- Or API functions starts here ############################################################################## def _handle_InstantiateNFFGEvent(self, event): """ Instantiate given NF-FG (UNIFY Sl - Or API). :param event: event object contains NF-FG :type event: :any:`InstantiateNFFGEvent` :return: None """ log.getChild('API').info( "Received NF-FG: %s from %s layer" % (event.nffg, str(event.source._core_name).title())) self.__proceed_instantiation(nffg=event.nffg) @schedule_as_coop_task 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 _proceed_to_install_NFFG(self, mapped_nffg): """ Send mapped :any:`NFFG` to Controller Adaptation Sublayer in an implementation-specific way. General function which is used from microtask and Python thread also. This function contains the last steps before the mapped NFFG will be sent to the next layer. :param mapped_nffg: mapped NF-FG :type mapped_nffg: :any:`NFFG` :return: None """ # Non need to rebind req links --> it will be done in Adaptation layer # Log verbose mapping result in unified way (threaded/non-threaded) log.log( VERBOSE, "Mapping result of Orchestration Layer:\n%s" % mapped_nffg.dump()) # Notify remote visualizer about the mapping result if it's needed notify_remote_visualizer(data=mapped_nffg, id=LAYER_NAME) # Sending NF-FG to Adaptation layer as an Event # Exceptions in event handlers are caught by default in a non-blocking way self.raiseEventNoErrors(InstallNFFGEvent, mapped_nffg) log.getChild('API').info( "Mapped NF-FG: %s has been sent to Adaptation..." % mapped_nffg) def _handle_GetVirtResInfoEvent(self, event): """ Generate virtual resource info and send back to SAS. :param event: event object contains service layer id :type event: :any:`GetVirtResInfoEvent` :return: None """ log.getChild('API').debug( "Received <Virtual View> request from %s layer" % str(event.source._core_name).title()) # Currently view is a Virtualizer to keep ESCAPE fast # Virtualizer type for Sl-Or API virtualizer_type = CONFIG.get_api_virtualizer(layer_name=LAYER_NAME, api_name=event.sid) v = self.resource_orchestrator.virtualizerManager.get_virtual_view( event.sid, type=virtualizer_type) log.getChild('API').debug("Sending back <Virtual View>: %s..." % v) self.raiseEventNoErrors(VirtResInfoEvent, v) ############################################################################## # UNIFY Or - Ca API functions starts here ############################################################################## def _handle_MissingGlobalViewEvent(self, event): """ Request Global infrastructure View from CAS (UNIFY Or - CA API). Invoked when a :class:`MissingGlobalViewEvent` raised. :param event: event object :type event: :any:`MissingGlobalViewEvent` :return: None """ log.getChild('API').debug("Send DoV request to Adaptation layer...") self.raiseEventNoErrors(GetGlobalResInfoEvent) def _handle_GlobalResInfoEvent(self, event): """ Save requested Global Infrastructure View as the :class:`DomainVirtualizer`. :param event: event object contains resource info :type event: :any:`GlobalResInfoEvent` :return: None """ log.getChild('API').debug("Received DoV from %s Layer" % str(event.source._core_name).title()) self.resource_orchestrator.virtualizerManager.dov = event.dov def _handle_InstallationFinishedEvent(self, event): """ Get information from NFFG installation process. :param event: event object info :type event: :any:`InstallationFinishedEvent` :return: None """ if event.result: log.getChild('API').info( "NF-FG instantiation has been finished successfully!") else: log.getChild('API').error( "NF-FG instantiation has been finished with error!") self.raiseEventNoErrors(InstantiationFinishedEvent, id=event.id, result=event.result)
class ServiceLayerAPI(AbstractAPI): """ Entry point for Service Adaptation Sublayer. Maintain the contact with other UNIFY layers. Implement the U - Sl reference point. """ # Defined specific name for core object as pox.core.<_core_name> _core_name = LAYER_NAME """Defined specific name for core object """ # Layer id constant LAYER_ID = "ESCAPE-" + LAYER_NAME """Layer id constant""" # Events raised by this class _eventMixin_events = { InstantiateNFFGEvent, GetVirtResInfoEvent, PreMapEvent, PostMapEvent } """Events raised by this class""" # Dependencies dependencies = ('orchestration', ) """Layer dependencies""" def __init__(self, standalone=False, **kwargs): """ .. seealso:: :func:`AbstractAPI.__init__() <escape.util.api.AbstractAPI.__init__>` """ log.info("Starting Service Layer...") # Mandatory super() call self.last_sg = NFFG(id=0, name='empty') # Set element manager self.__sid = None self.elementManager = None self.service_orchestrator = None """:type ServiceOrchestrator""" self.gui_proc = None super(ServiceLayerAPI, self).__init__(standalone, **kwargs) def initialize(self): """ .. seealso:: :func:`AbstractAPI.initialize() <escape.util.api.AbstractAPI.initialize>` """ log.debug("Initializing Service Layer...") self.__sid = CONFIG.get_service_layer_id() if self.__sid is not None: log.debug("Setup ID for Service Layer: %s" % self.__sid) else: self.__sid = self.LAYER_ID log.error( "Missing ID of Service Layer from config. Using default value: %s" % self.__sid) # Set element manager self.elementManager = ClickManager() # Init central object of Service layer self.service_orchestrator = ServiceOrchestrator(self) # Read input from file if it's given and initiate SG if self._sg_file: try: service_request = self._read_data_from_file(self._sg_file) log.info("Graph representation is loaded successfully!") if service_request.startswith('{'): log.debug( "Detected format: JSON - Parsing from NFFG format...") nffg = NFFG.parse(raw_data=service_request) elif service_request.startswith('<'): log.debug( "Detected format: XML - Parsing from Virtualizer format..." ) converter = NFFGConverter(domain="INTERNAL", logger=log) nffg = converter.parse_from_Virtualizer( vdata=service_request) else: log.warning("Detected unexpected format...") return if nffg.mode is not None: log.info('Detected mapping mode in NFFG: %s' % nffg.mode) else: nffg.mode = NFFG.MODE_ADD log.info("No mapping mode has been detected in NFFG! " "Set default mode: %s" % nffg.mode) log.info("Schedule service request delayed by %d seconds..." % SCHEDULED_SERVICE_REQUEST_DELAY) self.api_sas_sg_request_delayed(service_nffg=nffg) except (ValueError, IOError, TypeError) as e: log.error("Can't load service request from file because of: " + str(e)) quit_with_error(msg=str(e), logger=log) else: # Init REST-API if no input file is given self._initiate_rest_api() # Init GUI if self._gui: self._initiate_gui() log.info("Service Layer has been initialized!") def shutdown(self, event): """ .. seealso:: :func:`AbstractAPI.shutdown() <escape.util.api.AbstractAPI.shutdown>` :param event: event object """ log.info("Service Layer is going down...") if hasattr(self, 'rest_api') and self.rest_api: log.debug("REST-API: %s is shutting down..." % self.rest_api.api_id) # self.rest_api.stop() if self.gui_proc: log.debug("Shut down GUI process - PID: %s" % self.gui_proc.pid) self.gui_proc.terminate() def _initiate_rest_api(self): """ Initialize and set up REST API in a different thread. :return: None """ # set bounded layer name here to avoid circular dependency problem handler = CONFIG.get_sas_api_class() handler.bounded_layer = self._core_name params = CONFIG.get_sas_agent_params() # can override from global config if 'prefix' in params: handler.prefix = params['prefix'] if 'unify_interface' in params: handler.virtualizer_format_enabled = params['unify_interface'] address = (params.get('address'), params.get('port')) self.rest_api = RESTServer(handler, *address) self.rest_api.api_id = handler.LOGGER_NAME = "U-Sl" handler.log.info("Init REST-API for %s on %s:%s!" % (self.rest_api.api_id, address[0], address[1])) self.rest_api.start() handler.log.debug( "Enforced configuration for %s: interface: %s" % (self.rest_api.api_id, "UNIFY" if handler.virtualizer_format_enabled else "Internal-NFFG")) def _initiate_gui(self): """ Initiate and set up GUI. :return: None """ # TODO - set up and initiate MiniEdit here??? devnull = open(os.devnull, 'r+') gui_path = os.path.abspath(os.getcwd() + "/gui/gui.py") self.gui_proc = Popen(gui_path, stdin=devnull, stdout=devnull, stderr=devnull, close_fds=True) log.info("GUI has been initiated!") def _handle_SGMappingFinishedEvent(self, event): """ Handle SGMappingFinishedEvent and proceed with :class:`NFFG <escape.util.nffg.NFFG>` instantiation. :param event: event object :type event: :any:`SGMappingFinishedEvent` :return: None """ self._proceed_to_instantiate_NFFG(event.nffg) ############################################################################## # UNIFY U - Sl API functions starts here ############################################################################## @schedule_as_coop_task def api_sas_sg_request(self, service_nffg, *args, **kwargs): """ Initiate service graph in a cooperative micro-task. :param service_nffg: service graph instance :type service_nffg: :class:`NFFG` :return: None """ self.__proceed_sg_request(service_nffg) @schedule_delayed_as_coop_task(delay=SCHEDULED_SERVICE_REQUEST_DELAY) def api_sas_sg_request_delayed(self, service_nffg, *args, **kwargs): """ Initiate service graph in a cooperative micro-task. :param service_nffg: service graph instance :type service_nffg: :class:`NFFG` :return: None """ return self.__proceed_sg_request(service_nffg) def __proceed_sg_request(self, service_nffg): """ Initiate a Service Graph (UNIFY U-Sl API). :param service_nffg: service graph instance :type service_nffg: :class:`NFFG` :return: None """ log.getChild('API').info("Invoke request_service on %s with SG: %s " % (self.__class__.__name__, service_nffg)) # 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 service_nffg.mode is not None: mapping_mode = service_nffg.mode log.info("Detected mapping mode from NFFG: %s" % mapping_mode) else: mapping_mode = None log.info("No mapping mode was detected!") self.__sg_preprocessing(nffg=service_nffg) # Store request if it is received on REST-API if hasattr(self, 'rest_api') and self.rest_api: log.getChild('API').debug("Store received NFFG request info...") msg_id = self.rest_api.request_cache.cache_request_by_nffg( nffg=service_nffg) if msg_id is not None: self.rest_api.request_cache.set_in_progress(id=msg_id) log.getChild('API').debug("Request is stored with id: %s" % msg_id) else: log.getChild('API').debug("No request info detected.") try: # Initiate service request mapping mapped_nffg = self.service_orchestrator.initiate_service_graph( service_nffg) # 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) log.getChild('API').debug( "Invoked request_service 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.service_orchestrator.mapper.threaded: self._proceed_to_instantiate_NFFG(mapped_nffg) self.last_sg = mapped_nffg else: log.warning( "Something went wrong in service request initiation: " "mapped service data is missing!") self.__handle_mapping_result(nffg_id=service_nffg.id, fail=True) self._handle_InstantiationFinishedEvent( event=InstantiationFinishedEvent( id=service_nffg.id, result=InstantiationFinishedEvent.MAPPING_ERROR)) except ProcessorError as e: self.__handle_mapping_result(nffg_id=service_nffg.id, fail=True) self._handle_InstantiationFinishedEvent( event=InstantiationFinishedEvent( id=service_nffg.id, result=InstantiationFinishedEvent.REFUSED_BY_VERIFICATION, error=e)) def __sg_preprocessing(self, nffg): if nffg.mode == NFFG.MODE_DEL: log.debug("Explicitly mark NF nodes in DELETE request...") for nf in nffg.nfs: nf.operation = NFFG.OP_DELETE log.debug("%s --> %s" % (nf.id, nf.operation)) def __handle_mapping_result(self, nffg_id, fail): if not (hasattr(self, 'rest_api') and self.rest_api): return log.getChild('API').debug("Cache request status...") req_status = self.rest_api.request_cache.get_request_by_nffg_id( nffg_id) if req_status is None: log.getChild('API').debug( "Request status is missing for NFFG: %s! " "Skip result processing..." % nffg_id) return log.getChild('API').debug("Process mapping result...") message_id = req_status.message_id if message_id is not None: if fail: self.rest_api.request_cache.set_error_result(id=message_id) else: self.rest_api.request_cache.set_success_result(id=message_id) ret = self.rest_api.invoke_callback(message_id=message_id) if ret is None: log.getChild('API').debug("No callback was defined!") else: log.getChild('API').debug( "Callback: %s has invoked with return value: %s" % (req_status.get_callback(), ret)) RequestScheduler().set_orchestration_finished(id=nffg_id) def __get_sas_resource_view(self): """ Return with the resource view of SAS layer. :return: resource view :rtype: :any:`AbstractVirtualizer` """ return self.service_orchestrator.virtResManager.virtual_view def api_sas_get_topology(self): """ Return with the topology description. :return: topology description requested from the layer's Virtualizer :rtype: :class:`NFFG` """ log.getChild('[U-Sl]').debug("Requesting Virtualizer for REST-API...") # Get or if not available then request the layer's Virtualizer sas_virt = self.__get_sas_resource_view() if sas_virt is not None: log.getChild('[U-Sl]').debug("Generate topo description...") # return with the virtual view as an NFFG return sas_virt.get_resource_info() else: log.getChild('[U-Sl]').error( "Virtualizer(id=%s) assigned to REST-API is not found!" % self.rest_api.api_id) def api_sas_status(self, message_id): """ Return the state of a request given by ``message_id``. Function is not invoked in coop-microtask, only write-type operations must not be used. :param message_id: request id :type message_id: str or int :return: state :rtype: str """ status = self.rest_api.request_cache.get_domain_status(id=message_id) if status == RequestStatus.SUCCESS: return 200, None elif status == RequestStatus.UNKNOWN: return 404, None elif status == RequestStatus.ERROR: return 500, status else: # PROCESSING or INITIATED return 202, None def _proceed_to_instantiate_NFFG(self, mapped_nffg): """ Send NFFG to Resource Orchestration Sublayer in an implementation-specific way. General function which is used from microtask and Python thread also. This function contains the last steps before the mapped NFFG will be sent to the next layer. :param mapped_nffg: mapped Service Graph :type mapped_nffg: :class:`NFFG` :return: None """ # Rebind requirement link fragments for lower layer mapping mapped_nffg = NFFGToolBox.rebind_e2e_req_links(nffg=mapped_nffg, log=log) # Log verbose mapping result in unified way (threaded/non-threaded) log.log(VERBOSE, "Mapping result of Service Layer:\n%s" % mapped_nffg.dump()) # Notify remote visualizer about the mapping result if it's needed # notify_remote_visualizer(data=mapped_nffg, id=LAYER_NAME) sas_res = self.__get_sas_resource_view().get_resource_info() # Sending mapped SG / NF-FG to Orchestration layer as an Event # Exceptions in event handlers are caught by default in a non-blocking way self.raiseEventNoErrors(InstantiateNFFGEvent, mapped_nffg, sas_res) log.getChild('API').info( "Generated NF-FG: %s has been sent to Orchestration..." % mapped_nffg) ############################################################################## # UNIFY Sl - Or API functions starts here ############################################################################## def _handle_MissingVirtualViewEvent(self, event): """ Request virtual resource info from Orchestration layer (UNIFY Sl - Or API). Invoked when a :class:`MissingVirtualViewEvent` raised. Service layer is identified with the sid value automatically. :param event: event object :type event: :any:`MissingVirtualViewEvent` :return: None """ log.getChild('API').debug( "Send <Virtual View> request(with layer ID: %s) to Orchestration " "layer..." % self.__sid) self.raiseEventNoErrors(GetVirtResInfoEvent, self.__sid) def _handle_VirtResInfoEvent(self, event): """ Save requested virtual resource info as an :class:`AbstractVirtualizer <escape.orchest.virtualization_mgmt.AbstractVirtualizer>`. :param event: event object :type event: :any:`VirtResInfoEvent` :return: None """ log.getChild('API').debug( "Received <Virtual View>: %s from %s layer" % (event.virtualizer, str(event.source._core_name).title())) self.service_orchestrator.virtResManager.virtual_view = event.virtualizer def _handle_InstantiationFinishedEvent(self, event): """ Receive the result of the instantiated NFFG and save it. :param event: event object :type event: :any:`InstantiationFinishedEvent` :return: None """ if not BaseResultEvent.is_error(event.result): log.getChild('API').info( "Service request(id=%s) has been finished successfully with result: %s!" % (event.id, event.result)) else: log.getChild('API').error( "Service request(id=%s) has been finished with error result: %s!" % (event.id, event.result)) if not event.is_pending(event.result): self.__handle_mapping_result(nffg_id=event.id, fail=event.is_error(event.result)) # Quit ESCAPE if test mode is active if get_global_parameter(name="QUIT_AFTER_PROCESS"): quit_with_ok("Detected QUIT mode! Exiting ESCAPE...")
class ServiceLayerAPI(AbstractAPI): """ Entry point for Service Adaptation Sublayer. Maintain the contact with other UNIFY layers. Implement the U - Sl reference point. """ # Define specific name for core object as pox.core.<_core_name> _core_name = LAYER_NAME # Layer id constant LAYER_ID = "ESCAPE-" + LAYER_NAME # Events raised by this class _eventMixin_events = {InstantiateNFFGEvent, GetVirtResInfoEvent, PreMapEvent, PostMapEvent} # Dependencies dependencies = ('orchestration',) def __init__ (self, standalone=False, **kwargs): """ .. seealso:: :func:`AbstractAPI.__init__() <escape.util.api.AbstractAPI.__init__>` """ log.info("Starting Service Layer...") # Mandatory super() call self.last_sg = NFFG(id=0, name='empty') # Set element manager self.__sid = None self.elementManager = None self.service_orchestrator = None self.gui_proc = None super(ServiceLayerAPI, self).__init__(standalone, **kwargs) def initialize (self): """ .. seealso:: :func:`AbstractAPI.initialize() <escape.util.api.AbstractAPI.initialize>` """ log.debug("Initializing Service Layer...") self.__sid = CONFIG.get_service_layer_id() if self.__sid is not None: log.debug("Setup ID for Service Layer: %s" % self.__sid) else: self.__sid = self.LAYER_ID log.error( "Missing ID of Service Layer from config. Using default value: %s" % self.__sid) # Set element manager self.elementManager = ClickManager() # Init central object of Service layer self.service_orchestrator = ServiceOrchestrator(self) # Read input from file if it's given and initiate SG if self._sg_file: try: service_request = self._read_data_from_file(self._sg_file) log.info("Graph representation is loaded successfully!") if service_request.startswith('{'): log.debug("Detected format: JSON - Parsing from NFFG format...") nffg = NFFG.parse(raw_data=service_request) elif service_request.startswith('<'): log.debug("Detected format: XML - Parsing from Virtualizer format...") converter = NFFGConverter(domain="INTERNAL", logger=log) nffg = converter.parse_from_Virtualizer(vdata=service_request) else: log.warning("Detected unexpected format...") return log.info("Schedule service request delayed by 3 seconds...") self.api_sas_sg_request_delayed(service_nffg=nffg) except (ValueError, IOError, TypeError) as e: log.error( "Can't load service request from file because of: " + str(e)) else: # Init REST-API if no input file is given self._initiate_rest_api() # Init GUI if self._gui: self._initiate_gui() log.info("Service Layer has been initialized!") def shutdown (self, event): """ .. seealso:: :func:`AbstractAPI.shutdown() <escape.util.api.AbstractAPI.shutdown>` :param event: event object """ log.info("Service Layer is going down...") if hasattr(self, 'rest_api') and self.rest_api: log.debug("REST-API: %s is shutting down..." % self.rest_api.api_id) # self.rest_api.stop() if self.gui_proc: log.debug("Shut down GUI process - PID: %s" % self.gui_proc.pid) self.gui_proc.terminate() def _initiate_rest_api (self): """ Initialize and set up REST API in a different thread. :return: None """ # set bounded layer name here to avoid circular dependency problem handler = CONFIG.get_sas_api_class() handler.bounded_layer = self._core_name params = CONFIG.get_sas_agent_params() # can override from global config if 'prefix' in params: handler.prefix = params['prefix'] if 'unify_interface' in params: handler.virtualizer_format_enabled = params['unify_interface'] address = (params.get('address'), params.get('port')) self.rest_api = RESTServer(handler, *address) self.rest_api.api_id = handler.LOGGER_NAME = "U-Sl" handler.log.debug("Init REST-API for %s on %s:%s!" % ( self.rest_api.api_id, address[0], address[1])) self.rest_api.start() handler.log.debug("Enforced configuration for %s: interface: %s" % ( self.rest_api.api_id, "UNIFY" if handler.virtualizer_format_enabled else "Internal-NFFG")) def _initiate_gui (self): """ Initiate and set up GUI. """ # TODO - set up and initiate MiniEdit here??? devnull = open(os.devnull, 'r+') gui_path = os.path.abspath(os.getcwd() + "/gui/gui.py") self.gui_proc = Popen(gui_path, stdin=devnull, stdout=devnull, stderr=devnull, close_fds=True) log.info("GUI has been initiated!") def _handle_SGMappingFinishedEvent (self, event): """ Handle SGMappingFinishedEvent and proceed with :class:`NFFG <escape.util.nffg.NFFG>` instantiation. :param event: event object :type event: :any:`SGMappingFinishedEvent` :return: None """ self._proceed_to_instantiate_NFFG(event.nffg) ############################################################################## # UNIFY U - Sl API functions starts here ############################################################################## @schedule_as_coop_task def api_sas_sg_request (self, service_nffg): """ Initiate service graph in a cooperative micro-task. :param service_nffg: service graph instance :type service_nffg: :any:`NFFG` :return: None """ self.__proceed_sg_request(service_nffg) @schedule_delayed_as_coop_task(delay=3) def api_sas_sg_request_delayed (self, service_nffg): """ Initiate service graph in a cooperative micro-task. :param service_nffg: service graph instance :type service_nffg: :any:`NFFG` :return: None """ return self.__proceed_sg_request(service_nffg) def __proceed_sg_request (self, service_nffg): """ Initiate a Service Graph (UNIFY U-Sl API). :param service_nffg: service graph instance :type service_nffg: :any:`NFFG` :return: None """ # Store request if it is received on REST-API if hasattr(self, 'rest_api') and self.rest_api: self.rest_api.request_cache.add_request(id=service_nffg.id) self.rest_api.request_cache.set_in_progress(id=service_nffg.id) log.getChild('API').info("Invoke request_service on %s with SG: %s " % (self.__class__.__name__, service_nffg)) # Initiate service request mapping mapped_nffg = self.service_orchestrator.initiate_service_graph( service_nffg) log.getChild('API').debug("Invoked request_service 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.service_orchestrator.mapper.threaded: self._proceed_to_instantiate_NFFG(mapped_nffg) self.last_sg = mapped_nffg else: log.warning("Something went wrong in service request initiation: " "mapped service data is missing!") def api_sas_get_topology (self): """ Return with the topology description. :return: topology description requested from the layer's Virtualizer :rtype: :any:`NFFG` """ log.getChild('[U-Sl]').info("Requesting Virtualizer for REST-API...") # Get or if not available then request the layer's Virtualizer sas_virtualizer = self.service_orchestrator.virtResManager.virtual_view if sas_virtualizer is not None: log.getChild('[U-Sl]').info("Generate topo description...") # return with the virtual view as an NFFG return sas_virtualizer.get_resource_info() else: log.getChild('[U-Sl]').error( "Virtualizer(id=%s) assigned to REST-API is not found!" % self.cfor_api.api_id) def get_result (self, id): """ Return the state of a request given by ``id``. :param id: request id :type id: str or int :return: state :rtype: str """ return self.rest_api.request_cache.get_result(id=id) def _proceed_to_instantiate_NFFG (self, mapped_nffg): """ Send NFFG to Resource Orchestration Sublayer in an implementation-specific way. General function which is used from microtask and Python thread also. This function contains the last steps before the mapped NFFG will be sent to the next layer. :param mapped_nffg: mapped Service Graph :type mapped_nffg: :any:`NFFG` :return: None """ # Rebind requirement link fragments for lower layer mapping mapped_nffg = NFFGToolBox.rebind_e2e_req_links(nffg=mapped_nffg, log=log) # Log verbose mapping result in unified way (threaded/non-threaded) log.log(VERBOSE, "Mapping result of Service Layer:\n%s" % mapped_nffg.dump()) # Notify remote visualizer about the mapping result if it's needed notify_remote_visualizer(data=mapped_nffg, id=LAYER_NAME) # Sending mapped SG / NF-FG to Orchestration layer as an Event # Exceptions in event handlers are caught by default in a non-blocking way self.raiseEventNoErrors(InstantiateNFFGEvent, mapped_nffg) log.getChild('API').info( "Generated NF-FG: %s has been sent to Orchestration..." % mapped_nffg) ############################################################################## # UNIFY Sl - Or API functions starts here ############################################################################## def _handle_MissingVirtualViewEvent (self, event): """ Request virtual resource info from Orchestration layer (UNIFY Sl - Or API). Invoked when a :class:`MissingVirtualViewEvent` raised. Service layer is identified with the sid value automatically. :param event: event object :type event: :any:`MissingVirtualViewEvent` :return: None """ log.getChild('API').debug( "Send <Virtual View> request(with layer ID: %s) to Orchestration " "layer..." % self.__sid) self.raiseEventNoErrors(GetVirtResInfoEvent, self.__sid) def _handle_VirtResInfoEvent (self, event): """ Save requested virtual resource info as an :class:`AbstractVirtualizer <escape.orchest.virtualization_mgmt.AbstractVirtualizer>`. :param event: event object :type event: :any:`VirtResInfoEvent` :return: None """ log.getChild('API').debug("Received <Virtual View>: %s from %s layer" % ( event.virtualizer, str(event.source._core_name).title())) self.service_orchestrator.virtResManager.virtual_view = event.virtualizer def _handle_InstantiationFinishedEvent (self, event): """ """ if hasattr(self, 'rest_api') and self.rest_api: self.rest_api.request_cache.set_result(id=event.id, result=event.result) if event.result: log.getChild('API').info( "Service request(id=%s) has been finished successfully!" % event.id) else: log.getChild('API').error( "Service request(id=%s) has been finished with error!" % event.id)