예제 #1
0
파일: ros_API.py 프로젝트: hsnlab/fero
    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))
예제 #2
0
    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"))
예제 #3
0
파일: ros_API.py 프로젝트: hsnlab/fero
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)
예제 #4
0
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...")
예제 #5
0
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)