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)
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 __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 __handle_mapping_result(self, nffg_id, fail): """ Perform necessary task for callback and cache functionality based on mapping result. :param nffg_id: request ID :type nffg_id: str or int :param fail: mapping result :type fail: bool :return: None """ log.getChild('API').debug("Cache request status...") req_status = self.api_mgr.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.api_mgr.request_cache.set_error_result(id=message_id) else: self.api_mgr.request_cache.set_success_result(id=message_id) ret = self.api_mgr.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 _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)
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_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_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...")
def rest_api_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: if sas_virt.revision is None: log.debug("Not initialized yet!") else: # Check if the resource is changed if self.api_mgr.topology_revision == sas_virt.revision: # If resource has not been changed return False # This causes to response with the cached topology log.debug( "Global resource has not changed (revision: %s)! " % sas_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: log.debug( "Response cache is outdated (new revision: %s)!" % sas_virt.revision) log.getChild('[U-Sl]').debug("Generate topo description...") # return with the virtual view as an NFFG res = sas_virt.get_resource_info() self.api_mgr.topology_revision = sas_virt.revision log.debug("Updated revision number: %s" % self.api_mgr.topology_revision) if CONFIG.get_rest_api_config(self._core_name)['unify_interface']: 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.getChild('[U-Sl]').error( "Virtualizer(id=%s) assigned to REST-API is not found!" % self._core_name)
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_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)
class ServiceRequestHandler(BasicUnifyRequestHandler): """ Request Handler for Service Adaptation SubLayer. .. warning:: This class is out of the context of the recoco's co-operative thread context! While you don't need to worry much about synchronization between recoco tasks, you do need to think about synchronization between recoco task and normal threads. Synchronisation is needed to take care manually: use relevant helper function of core object: `callLater`/`raiseLater` or use `schedule_as_coop_task` decorator defined in util.misc on the called function. """ # Bind HTTP verbs to UNIFY's API functions request_perm = { 'GET': ('ping', 'version', 'operations', 'topology', 'status'), 'POST': ('ping', 'sg', 'topology'), # 'DELETE': ('sg',), 'PUT': ('sg', ) } """Bind HTTP verbs to UNIFY's API functions""" # Statically defined layer component to which this handler is bounded # Need to be set by container class bounded_layer = 'service' """Statically defined layer component to which this handler is bounded""" static_prefix = "escape" # Logger name LOGGER_NAME = "U-Sl" """Logger name""" log = log.getChild("[%s]" % LOGGER_NAME) # Use Virtualizer format virtualizer_format_enabled = False """Use Virtualizer format""" # Default communication approach DEFAULT_DIFF = True """Default communication approach""" # Bound function API_CALL_RESOURCE = 'api_sas_get_topology' API_CALL_REQUEST = 'api_sas_sg_request' def __init__(self, request, client_address, server): """ Init. :param request: request type :type request: str :param client_address: client address :type client_address: str :param server: server object :type server: :any:`BaseHTTPServer.HTTPServer` :return: None """ AbstractRequestHandler.__init__(self, request, client_address, server) def status(self, params): """ Return status of the given request. :param params: :return: """ message_id = params.get('message-id') if not message_id: self.send_error(code=httplib.BAD_REQUEST, message="message-id is missing") return code, result = self._proceed_API_call('api_sas_status', message_id) if not result: self.send_acknowledge(code=code, message_id=message_id) self.log.debug("Responded status code: %s" % code) else: self.send_json_response(code=code, data=result) self.log.debug("Responded status code: %s, data: %s" % (code, result)) def topology(self, params): """ Provide internal topology description Same functionality as "get-config" in UNIFY interface. :return: None """ self.log.debug("Call %s function: topology" % self.LOGGER_NAME) # Forward call to main layer class resource = self._proceed_API_call(self.API_CALL_RESOURCE) self._topology_view_responder(resource_nffg=resource, message_id=params.get( self.MESSAGE_ID_NAME)) self.log.debug("%s function: topology ended!" % self.LOGGER_NAME) def sg(self, params): """ Main API function for Service Graph initiation. Same functionality as "get-config" in UNIFY interface. Bounded to POST HTTP verb. :return: None """ self.log.debug("Call %s function: sg" % self.LOGGER_NAME) nffg = self._service_request_parser() if nffg: if nffg.service_id is None: nffg.service_id = nffg.id nffg.id = params[self.MESSAGE_ID_NAME] nffg.metadata['params'] = params # self._proceed_API_call(self.API_CALL_REQUEST, # service_nffg=nffg, # params=params) self.server.scheduler.schedule_request( id=nffg.id, layer=self.bounded_layer, function=self.API_CALL_REQUEST, service_nffg=nffg, params=params) self.send_acknowledge(message_id=params[self.MESSAGE_ID_NAME]) self.log.debug("%s function: sg ended!" % self.LOGGER_NAME)
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))
class ServiceRequestHandler(AbstractRequestHandler): """ Request Handler for Service Adaptation SubLayer. .. warning:: This class is out of the context of the recoco's co-operative thread context! While you don't need to worry much about synchronization between recoco tasks, you do need to think about synchronization between recoco task and normal threads. Synchronisation is needed to take care manually: use relevant helper function of core object: `callLater`/`raiseLater` or use `schedule_as_coop_task` decorator defined in util.misc on the called function. """ # Bind HTTP verbs to UNIFY's API functions request_perm = { 'GET': ('ping', 'version', 'operations', 'topology'), 'POST': ('ping', 'result', 'sg', 'topology') } # Statically defined layer component to which this handler is bounded # Need to be set by container class bounded_layer = 'service' # Logger name LOGGER_NAME = "U-Sl" log = log.getChild("[%s]" % LOGGER_NAME) # Use Virtualizer format virtualizer_format_enabled = False def __init__ (self, request, client_address, server): """ Init. """ AbstractRequestHandler.__init__(self, request, client_address, server) def result (self): """ Return the result of a request given by the id. """ params = json.loads(self._get_body()) try: id = params["id"] except: id = None res = self._proceed_API_call('get_result', id) self._send_json_response({'id': id, 'result': res}) def topology (self): """ Provide internal topology description Same functionality as "get-config" in UNIFY interface. :return: None """ self.log.info("Call %s function: topology" % self.LOGGER_NAME) # Forward call to main layer class topology = self._proceed_API_call('api_sas_get_topology') if topology is None: self.send_error(404, message="Resource info is missing!") return # Setup OK status for HTTP response self.send_response(200) if topology is False: self.log.info( "Requested resource has not changed! Respond with cached topology...") if self.virtualizer_format_enabled: data = self.server.last_response.xml() else: data = self.server.last_response.dump() else: if self.virtualizer_format_enabled: self.log.debug("Convert internal NFFG to Virtualizer...") converter = NFFGConverter(domain=None, logger=log) # Dump to plain text format v_topology = converter.dump_to_Virtualizer(nffg=topology) # Cache converted data for edit-config patching self.log.debug("Cache converted topology...") self.server.last_response = v_topology # Dump to plain text format data = v_topology.xml() # Setup HTTP response format else: self.log.debug("Cache converted topology...") self.server.last_response = topology data = topology.dump() if self.virtualizer_format_enabled: self.send_header('Content-Type', 'application/xml') else: self.send_header('Content-Type', 'application/json') self.log.log(VERBOSE, "Responded topology for 'get-config':\n%s" % data) # Setup length for HTTP response self.send_header('Content-Length', len(data)) self.end_headers() self.log.info("Send back topology description...") self.wfile.write(data) self.log.debug("%s function: get-config ended!" % self.LOGGER_NAME) def sg (self): """ Main API function for Service Graph initiation. Same functionality as "get-config" in UNIFY interface. Bounded to POST HTTP verb. :return: None """ self.log.debug("Called REST-API function: sg") self.log.info(int(round(time.time() * 1000))) # Obtain NFFG from request body log.debug("Detected response format: %s" % self.headers.get("Content-Type")) body = self._get_body() # log.getChild("REST-API").debug("Request body:\n%s" % body) if body is None or not 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" or \ not body.startswith("<?xml version="): log.error("Received data is not in XML format despite of the UNIFY " "interface is enabled!") self.send_error(415) return # Convert response's body to NFFG nffg = NFFGConverter(domain="INTERNAL", logger=log).parse_from_Virtualizer(vdata=body) else: try: nffg = NFFG.parse(body) # Initialize NFFG from JSON representation except Exception as e: self.log.error( "Abort request! Received exception during payload parsing: %s" % e) return self.log.debug("Parsed service request: %s" % nffg) self._proceed_API_call('api_sas_sg_request', nffg) self.send_acknowledge() self.log.debug("%s function: get-config ended!" % self.LOGGER_NAME)
def __proceed_sg_request(self, id, data, params=None): """ Initiate a Service Graph (UNIFY U-Sl API). :return: None """ log.info("Invoke preprocessing on %s with SG: %s " % (self.__class__.__name__, id)) stats.add_measurement_start_entry(type=stats.TYPE_SERVICE, info=LAYER_NAME) if CONFIG.get_rest_api_config(self._core_name)['unify_interface']: log.debug("Virtualizer format enabled! Start conversion step...") if CONFIG.get_rest_api_config(self._core_name)['diff']: log.debug("Diff format enabled! Start patching step...") if self.api_mgr.last_response is None: log.info( "Missing cached Virtualizer! Acquiring topology now..." ) self.rest_api_topology() stats.add_measurement_start_entry(type=stats.TYPE_PROCESSING, info="RECREATE-FULL-REQUEST") log.info("Patching cached topology with received diff...") full_req = self.api_mgr.last_response.yang_copy() full_req.patch(source=data) stats.add_measurement_end_entry(type=stats.TYPE_PROCESSING, info="RECREATE-FULL-REQUEST") else: full_req = data log.info("Converting full request data...") stats.add_measurement_start_entry(type=stats.TYPE_CONVERSION, info="VIRTUALIZER-->NFFG") service_nffg = self.api_mgr.converter.parse_from_Virtualizer( vdata=full_req) stats.add_measurement_end_entry(type=stats.TYPE_CONVERSION, info="VIRTUALIZER-->NFFG") else: service_nffg = data log.debug("Set NFFG id: %s" % id) if service_nffg.service_id is None: service_nffg.service_id = service_nffg.id service_nffg.id = id service_nffg.add_metadata(name="params", value=params) # 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 log.getChild('API').debug("Store received NFFG request info...") msg_id = self.api_mgr.request_cache.cache_request_by_nffg( nffg=service_nffg) if msg_id is not None: self.api_mgr.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: if CONFIG.get_mapping_enabled(layer=LAYER_NAME): # Initiate service request mapping mapped_nffg = self.service_orchestrator.initiate_service_graph( service_nffg) else: log.warning("Mapping is disabled! Skip instantiation step...") mapped_nffg = service_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) 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) stats.add_measurement_end_entry(type=stats.TYPE_SERVICE, info=LAYER_NAME + "-FAILED") 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) stats.add_measurement_end_entry(type=stats.TYPE_SERVICE, info=LAYER_NAME + "-DENIED") self._handle_InstantiationFinishedEvent( event=InstantiationFinishedEvent( id=service_nffg.id, result=InstantiationFinishedEvent.REFUSED_BY_VERIFICATION, error=e))