<commit_before># Copyright 2017 <NAME>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Implements the platform and POX dependent logic for the Resource Orchestration
Sublayer.
"""
import httplib
import json
import pprint

from escape.adapt.adaptation import InfoRequestFinishedEvent
from escape.nffg_lib.nffg import NFFG
from escape.nffg_lib.nffg import NFFGToolBox
from escape.orchest import LAYER_NAME  # Orchestration layer logger
from escape.orchest import log as log
from escape.orchest.ros_orchestration import ResourceOrchestrator
from escape.util.api import AbstractAPI, RESTServer, RequestStatus, \
  RequestScheduler
from escape.util.api import AbstractRequestHandler
from escape.util.com_logger import MessageDumper
from escape.util.config import CONFIG
from escape.util.conversion import NFFGConverter
from escape.util.domain import BaseResultEvent
from escape.util.mapping import ProcessorError
from escape.util.misc import VERBOSE, schedule_as_coop_task, quit_with_error
from pox.lib.revent.revent import Event
from virtualizer import Virtualizer
from virtualizer_info import Info
from virtualizer_mappings import Mappings


class InstallNFFGEvent(Event):
  """
  Event for passing mapped :class:`NFFG` to Controller
  Adaptation Sublayer.
  """

  def __init__ (self, mapped_nffg, original_request=None):
    """
    Init

    :param mapped_nffg: NF-FG graph need to be installed
    :type mapped_nffg: :class:`NFFG`
    :return: None
    """
    super(InstallNFFGEvent, self).__init__()
    self.mapped_nffg = mapped_nffg
    self.original_request = original_request


class VirtResInfoEvent(Event):
  """
  Event for sending back requested Virtual view an a specific Virtualizer.
  """

  def __init__ (self, virtualizer):
    """
    Init.

    :param virtualizer: virtual resource info
    :type virtualizer: :any:`AbstractVirtualizer`
    :return: None
    """
    super(VirtResInfoEvent, self).__init__()
    self.virtualizer = virtualizer


class GetGlobalResInfoEvent(Event):
  """
  Event for requesting :class:`DomainVirtualizer` from CAS.
  """
  pass


class CollectMonitoringDataEvent(Event):
  def __init__ (self, info, id):
    super(CollectMonitoringDataEvent, self).__init__()
    self.info = info
    self.id = id


class InstantiationFinishedEvent(BaseResultEvent):
  """
  Event for signalling end of mapping process finished with success.
  """

  def __init__ (self, id, result, error=None):
    """
    Init.

    :param id: NFFG id
    :type id: str or int
    :param result: result of the instantiation
    :type result: str
    :param error: optional Error object
    :type error: :any:`exceptions.BaseException`
    """
    super(InstantiationFinishedEvent, self).__init__()
    self.id = id
    self.result = result
    self.error = error


class ResourceOrchestrationAPI(AbstractAPI):
  """
  Entry point for Resource Orchestration Sublayer (ROS).

  Maintain the contact with other UNIFY layers.

  Implement the Sl - Or reference point.
  """
  # Defined specific name for core object i.e. pox.core.<_core_name>
  _core_name = LAYER_NAME
  """Defined specific name for core object"""
  # Events raised by this class
  _eventMixin_events = {InstallNFFGEvent, GetGlobalResInfoEvent,
                        VirtResInfoEvent, InstantiationFinishedEvent,
                        CollectMonitoringDataEvent}
  """Events raised by this class"""
  # Dependencies
  dependencies = ('adaptation',)
  """Layer dependencies"""

  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.orchestrator = None
    """:type: ResourceOrchestrator"""
    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.orchestrator = ResourceOrchestrator(self)
    if self._nffg_file:
      try:
        service_request = self._read_data_from_file(self._nffg_file)
        service_request = NFFG.parse(service_request)
        dov = self.orchestrator.virtualizerManager.dov
        self.__proceed_instantiation(nffg=service_request,
                                     resource_view=dov.get_resource_info())
      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:
        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...")
    self.orchestrator.finalize()
    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()
    if not handler:
      log.error("Missing handler class for server in ROS layer!")
      return
    handler.bounded_layer = self._core_name
    params = CONFIG.get_ros_agent_params()
    # can override from global config
    if 'prefix' in params:
      handler.static_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.static_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 __get_slor_resource_view (self):
    """
    Return with the Virtualizer object assigned to the Sl-Or interface.
    """
    virt_mgr = self.orchestrator.virtualizerManager
    return virt_mgr.get_virtual_view(virtualizer_id=self.ros_api.api_id,
                                     type=self.ros_api.virtualizer_type)

  def api_ros_get_config (self):
    """
    Implementation of REST-API RPC: get-config. Return with the global
    resource as an :class:`NFFG` if it has been changed otherwise return with
    False.

    :return: global resource view (DoV)
    :rtype: :class:`NFFG` or False
    """
    log.getChild('[Sl-Or]').debug("Requesting Virtualizer for REST-API")
    slor_virt = self.__get_slor_resource_view()
    if slor_virt is not None:
      # Check the topology is initialized
      if slor_virt.revision is None:
        log.getChild('[Sl-Or]').debug("DoV has not initialized yet! "
                                      "Force to get default topology...")
      else:
        # Check if the resource is changed
        if self.ros_api.topology_revision == slor_virt.revision:
          # If resource has not been changed return False
          # This causes to response with the cached topology
          return False
        else:
          log.getChild('[Sl-Or]').debug("Response cache is outdated "
                                        "(new revision: %s)!"
                                        % slor_virt.revision)
      res = slor_virt.get_resource_info()
      self.ros_api.topology_revision = slor_virt.revision
      return res
    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, params):
    """
    Implementation of REST-API RPC: edit-config

    :param nffg: NFFG need to deploy
    :type nffg: :class:`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)
    # Get resource view of the interface
    res = self.__get_slor_resource_view().get_resource_info()
    # ESCAPE serves as a global or proxy orchestrator
    self.__proceed_instantiation(nffg=nffg, resource_nffg=res)

  @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: :class:`NFFG`
    :return: updated NFFG
    :rtype: :class:`NFFG`
    """
    rewritten = []
    if domain_name is None:
      local_mgr = CONFIG.get_internal_manager()
      if local_mgr is None:
        log.error("No local Manager has been initiated! "
                  "Skip domain rewriting!")
      elif len(local_mgr) > 1:
        log.warning("Multiple local Manager has been initiated: %s! "
                    "Arbitrarily use the first..." % local_mgr)
      domain_name = local_mgr.pop()
    log.debug("Rewrite received NFFG domain to %s..." % domain_name)
    for infra in nffg_part.infras:
      infra.domain = domain_name
      rewritten.append(infra.id)
    log.debug("Rewritten infrastructure nodes: %s" % rewritten)
    return nffg_part

  def api_ros_status (self, message_id):
    """
    Return the state of a request given by ``message_id``.

    :param message_id: request id
    :type message_id: str or int
    :return: state
    :rtype: str
    """
    status = self.ros_api.request_cache.get_domain_status(id=message_id)
    if status == RequestStatus.SUCCESS:
      return httplib.OK, None
    elif status == RequestStatus.UNKNOWN:
      return httplib.NOT_FOUND, None
    elif status == RequestStatus.ERROR:
      return httplib.INTERNAL_SERVER_ERROR, status
    else:
      # PROCESSING or INITIATED
      return httplib.ACCEPTED, None

  def api_ros_mapping_info (self, service_id):
    """
    Return with collected information of mapping of a given service.

    :param service_id: service request ID
    :type service_id: str
    :return: mapping info
    :rtype: dict
    """
    # Create base response structure
    ret = {"service_id": service_id}
    mapping = self.orchestrator.collect_mapping_info(service_id=service_id)
    ret['mapping'] = mapping
    # Collect NF management data
    log.debug("Collected mapping info:\n%s" % pprint.pformat(ret))
    return ret

  def api_ros_mappings (self, mappings):
    """
    Calculate the mappings of NFs given in the mappings structure.

    :param mappings: requested mappings
    :type mappings: Mappings
    :return: new mappings with extended info
    :rtype: Mappings
    """
    slor_topo = self.__get_slor_resource_view().get_resource_info()
    response = self.orchestrator.collect_mappings(mappings=mappings,
                                                  slor_topo=slor_topo)
    return response

  @schedule_as_coop_task
  def api_ros_info (self, info, id):
    slor_topo = self.__get_slor_resource_view().get_resource_info()
    splitted = self.orchestrator.filter_info_request(info=info,
                                                     slor_topo=slor_topo)
    log.debug("Propagate info request to adaptation layer...")
    self.raiseEventNoErrors(CollectMonitoringDataEvent, info=splitted, id=id)

  ##############################################################################
  # Cf-Or API functions starts here
  ##############################################################################

  def __get_cfor_resource_view (self):
    """
    Return with the Virtualizer object assigned to the Cf-Or interface.

    :return: Virtualizer of Cf-Or interface
    :rtype: :any:`AbstractVirtualizer`
    """
    virt_mgr = self.orchestrator.virtualizerManager
    return virt_mgr.get_virtual_view(virtualizer_id=self.cfor_api.api_id,
                                     type=self.cfor_api.virtualizer_type)

  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]').debug("Requesting Virtualizer for REST-API...")
    cfor_virt = self.__get_cfor_resource_view()
    if cfor_virt is not None:
      log.getChild('[Cf-Or]').debug("Generate topo description...")
      return cfor_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: :class:`NFFG`
    """
    log.getChild('[Cf-Or]').info("Invoke install_nffg on %s with SG: %s " % (
      self.__class__.__name__, nffg))
    # Get resource view of the interface
    res = self.__get_cfor_resource_view().get_resource_info()
    self.__proceed_instantiation(nffg=nffg, resource_nffg=res)

  ##############################################################################
  # 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,
                                 resource_nffg=event.resource_nffg)

  @schedule_as_coop_task
  def __proceed_instantiation (self, nffg, resource_nffg):
    """
    Helper function to instantiate the NFFG mapping from different source.

    :param nffg: pre-mapped service request
    :type nffg: :class:`NFFG`
    :return: None
    """
    log.getChild('API').info("Invoke instantiate_nffg on %s with NF-FG: %s " % (
      self.__class__.__name__, nffg.name))
    # Get shown topology view
    if resource_nffg is None:
      log.error("Missing resource for difference calculation!")
      return
    log.debug("Got resource view for difference calculation: %s" %
              resource_nffg)
    if hasattr(self, 'ros_api') and self.ros_api:
      log.getChild('API').debug("Store received NFFG request info...")
      msg_id = self.ros_api.request_cache.cache_request_by_nffg(nffg=nffg)
      if msg_id is not None:
        self.ros_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.")
    # Check if mapping mode is set globally in CONFIG
    mapper_params = CONFIG.get_mapping_config(layer=LAYER_NAME)
    if 'mode' in mapper_params and mapper_params['mode'] is not None:
      mapping_mode = mapper_params['mode']
      log.info("Detected mapping mode from configuration: %s" % mapping_mode)
    elif nffg.mode is not None:
      mapping_mode = nffg.mode
      log.info("Detected mapping mode from NFFG: %s" % mapping_mode)
    else:
      mapping_mode = None
      log.info("No mapping mode was detected!")
    if nffg.status == NFFG.MAP_STATUS_SKIPPED:
      log.debug("Detected NFFG map status: %s! "
                "Skip difference calculation and "
                "proceed with original request..." % nffg.status)
    elif mapping_mode != NFFG.MODE_REMAP:
      # Calculated ADD-DELETE difference
      log.debug("Calculate ADD - DELETE difference with mapping mode...")
      # Recreate SG-hops for diff calc.
      log.debug("Recreate SG hops for difference calculation...")
      NFFGToolBox.recreate_all_sghops(nffg=nffg)
      NFFGToolBox.recreate_all_sghops(nffg=resource_nffg)
      log.log(VERBOSE, "New NFFG:\n%s" % nffg.dump())
      log.log(VERBOSE, "Resource NFFG:\n%s" % resource_nffg.dump())
      # Calculate difference
      add_nffg, del_nffg = NFFGToolBox.generate_difference_of_nffgs(
        old=resource_nffg, new=nffg, ignore_infras=True)
      log.log(VERBOSE, "Calculated ADD NFFG:\n%s" % add_nffg.dump())
      log.log(VERBOSE, "Calculated DEL NFFG:\n%s" % del_nffg.dump())
      if not add_nffg.is_bare() and del_nffg.is_bare():
        nffg = add_nffg
        log.info("DEL NFFG is bare! Calculated mapping mode: %s" % nffg.mode)
      elif add_nffg.is_bare() and not del_nffg.is_bare():
        nffg = del_nffg
        log.info("ADD NFFG is bare! Calculated mapping mode: %s" % nffg.mode)
      elif not add_nffg.is_bare() and not del_nffg.is_bare():
        log.warning("Both ADD / DEL mode is not supported currently")
        self.__process_mapping_result(nffg_id=nffg.id, fail=True)
        self.raiseEventNoErrors(InstantiationFinishedEvent,
                                id=nffg.id,
                                result=InstantiationFinishedEvent.ABORTED)
      else:
        log.debug("Difference calculation resulted empty subNFFGs!")
        log.info("No change has been detected in request! Skip mapping...")
        log.getChild('API').debug("Invoked instantiate_nffg on %s is finished!"
                                  % self.__class__.__name__)
        self.__process_mapping_result(nffg_id=nffg.id, fail=False)<commit_msg>Add missing return<commit_after># Copyright 2017 <NAME>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Implements the platform and POX dependent logic for the Resource Orchestration
Sublayer.
"""
import httplib
import json
import pprint

from escape.adapt.adaptation import InfoRequestFinishedEvent
from escape.nffg_lib.nffg import NFFG
from escape.nffg_lib.nffg import NFFGToolBox
from escape.orchest import LAYER_NAME  # Orchestration layer logger
from escape.orchest import log as log
from escape.orchest.ros_orchestration import ResourceOrchestrator
from escape.util.api import AbstractAPI, RESTServer, RequestStatus, \
  RequestScheduler
from escape.util.api import AbstractRequestHandler
from escape.util.com_logger import MessageDumper
from escape.util.config import CONFIG
from escape.util.conversion import NFFGConverter
from escape.util.domain import BaseResultEvent
from escape.util.mapping import ProcessorError
from escape.util.misc import VERBOSE, schedule_as_coop_task, quit_with_error
from pox.lib.revent.revent import Event
from virtualizer import Virtualizer
from virtualizer_info import Info
from virtualizer_mappings import Mappings


class InstallNFFGEvent(Event):
  """
  Event for passing mapped :class:`NFFG` to Controller
  Adaptation Sublayer.
  """

  def __init__ (self, mapped_nffg, original_request=None):
    """
    Init

    :param mapped_nffg: NF-FG graph need to be installed
    :type mapped_nffg: :class:`NFFG`
    :return: None
    """
    super(InstallNFFGEvent, self).__init__()
    self.mapped_nffg = mapped_nffg
    self.original_request = original_request


class VirtResInfoEvent(Event):
  """
  Event for sending back requested Virtual view an a specific Virtualizer.
  """

  def __init__ (self, virtualizer):
    """
    Init.

    :param virtualizer: virtual resource info
    :type virtualizer: :any:`AbstractVirtualizer`
    :return: None
    """
    super(VirtResInfoEvent, self).__init__()
    self.virtualizer = virtualizer


class GetGlobalResInfoEvent(Event):
  """
  Event for requesting :class:`DomainVirtualizer` from CAS.
  """
  pass


class CollectMonitoringDataEvent(Event):
  def __init__ (self, info, id):
    super(CollectMonitoringDataEvent, self).__init__()
    self.info = info
    self.id = id


class InstantiationFinishedEvent(BaseResultEvent):
  """
  Event for signalling end of mapping process finished with success.
  """

  def __init__ (self, id, result, error=None):
    """
    Init.

    :param id: NFFG id
    :type id: str or int
    :param result: result of the instantiation
    :type result: str
    :param error: optional Error object
    :type error: :any:`exceptions.BaseException`
    """
    super(InstantiationFinishedEvent, self).__init__()
    self.id = id
    self.result = result
    self.error = error


class ResourceOrchestrationAPI(AbstractAPI):
  """
  Entry point for Resource Orchestration Sublayer (ROS).

  Maintain the contact with other UNIFY layers.

  Implement the Sl - Or reference point.
  """
  # Defined specific name for core object i.e. pox.core.<_core_name>
  _core_name = LAYER_NAME
  """Defined specific name for core object"""
  # Events raised by this class
  _eventMixin_events = {InstallNFFGEvent, GetGlobalResInfoEvent,
                        VirtResInfoEvent, InstantiationFinishedEvent,
                        CollectMonitoringDataEvent}
  """Events raised by this class"""
  # Dependencies
  dependencies = ('adaptation',)
  """Layer dependencies"""

  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.orchestrator = None
    """:type: ResourceOrchestrator"""
    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.orchestrator = ResourceOrchestrator(self)
    if self._nffg_file:
      try:
        service_request = self._read_data_from_file(self._nffg_file)
        service_request = NFFG.parse(service_request)
        dov = self.orchestrator.virtualizerManager.dov
        self.__proceed_instantiation(nffg=service_request,
                                     resource_view=dov.get_resource_info())
      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:
        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...")
    self.orchestrator.finalize()
    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()
    if not handler:
      log.error("Missing handler class for server in ROS layer!")
      return
    handler.bounded_layer = self._core_name
    params = CONFIG.get_ros_agent_params()
    # can override from global config
    if 'prefix' in params:
      handler.static_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.static_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 __get_slor_resource_view (self):
    """
    Return with the Virtualizer object assigned to the Sl-Or interface.
    """
    virt_mgr = self.orchestrator.virtualizerManager
    return virt_mgr.get_virtual_view(virtualizer_id=self.ros_api.api_id,
                                     type=self.ros_api.virtualizer_type)

  def api_ros_get_config (self):
    """
    Implementation of REST-API RPC: get-config. Return with the global
    resource as an :class:`NFFG` if it has been changed otherwise return with
    False.

    :return: global resource view (DoV)
    :rtype: :class:`NFFG` or False
    """
    log.getChild('[Sl-Or]').debug("Requesting Virtualizer for REST-API")
    slor_virt = self.__get_slor_resource_view()
    if slor_virt is not None:
      # Check the topology is initialized
      if slor_virt.revision is None:
        log.getChild('[Sl-Or]').debug("DoV has not initialized yet! "
                                      "Force to get default topology...")
      else:
        # Check if the resource is changed
        if self.ros_api.topology_revision == slor_virt.revision:
          # If resource has not been changed return False
          # This causes to response with the cached topology
          return False
        else:
          log.getChild('[Sl-Or]').debug("Response cache is outdated "
                                        "(new revision: %s)!"
                                        % slor_virt.revision)
      res = slor_virt.get_resource_info()
      self.ros_api.topology_revision = slor_virt.revision
      return res
    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, params):
    """
    Implementation of REST-API RPC: edit-config

    :param nffg: NFFG need to deploy
    :type nffg: :class:`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)
    # Get resource view of the interface
    res = self.__get_slor_resource_view().get_resource_info()
    # ESCAPE serves as a global or proxy orchestrator
    self.__proceed_instantiation(nffg=nffg, resource_nffg=res)

  @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: :class:`NFFG`
    :return: updated NFFG
    :rtype: :class:`NFFG`
    """
    rewritten = []
    if domain_name is None:
      local_mgr = CONFIG.get_internal_manager()
      if local_mgr is None:
        log.error("No local Manager has been initiated! "
                  "Skip domain rewriting!")
      elif len(local_mgr) > 1:
        log.warning("Multiple local Manager has been initiated: %s! "
                    "Arbitrarily use the first..." % local_mgr)
      domain_name = local_mgr.pop()
    log.debug("Rewrite received NFFG domain to %s..." % domain_name)
    for infra in nffg_part.infras:
      infra.domain = domain_name
      rewritten.append(infra.id)
    log.debug("Rewritten infrastructure nodes: %s" % rewritten)
    return nffg_part

  def api_ros_status (self, message_id):
    """
    Return the state of a request given by ``message_id``.

    :param message_id: request id
    :type message_id: str or int
    :return: state
    :rtype: str
    """
    status = self.ros_api.request_cache.get_domain_status(id=message_id)
    if status == RequestStatus.SUCCESS:
      return httplib.OK, None
    elif status == RequestStatus.UNKNOWN:
      return httplib.NOT_FOUND, None
    elif status == RequestStatus.ERROR:
      return httplib.INTERNAL_SERVER_ERROR, status
    else:
      # PROCESSING or INITIATED
      return httplib.ACCEPTED, None

  def api_ros_mapping_info (self, service_id):
    """
    Return with collected information of mapping of a given service.

    :param service_id: service request ID
    :type service_id: str
    :return: mapping info
    :rtype: dict
    """
    # Create base response structure
    ret = {"service_id": service_id}
    mapping = self.orchestrator.collect_mapping_info(service_id=service_id)
    ret['mapping'] = mapping
    # Collect NF management data
    log.debug("Collected mapping info:\n%s" % pprint.pformat(ret))
    return ret

  def api_ros_mappings (self, mappings):
    """
    Calculate the mappings of NFs given in the mappings structure.

    :param mappings: requested mappings
    :type mappings: Mappings
    :return: new mappings with extended info
    :rtype: Mappings
    """
    slor_topo = self.__get_slor_resource_view().get_resource_info()
    response = self.orchestrator.collect_mappings(mappings=mappings,
                                                  slor_topo=slor_topo)
    return response

  @schedule_as_coop_task
  def api_ros_info (self, info, id):
    slor_topo = self.__get_slor_resource_view().get_resource_info()
    splitted = self.orchestrator.filter_info_request(info=info,
                                                     slor_topo=slor_topo)
    log.debug("Propagate info request to adaptation layer...")
    self.raiseEventNoErrors(CollectMonitoringDataEvent, info=splitted, id=id)

  ##############################################################################
  # Cf-Or API functions starts here
  ##############################################################################

  def __get_cfor_resource_view (self):
    """
    Return with the Virtualizer object assigned to the Cf-Or interface.

    :return: Virtualizer of Cf-Or interface
    :rtype: :any:`AbstractVirtualizer`
    """
    virt_mgr = self.orchestrator.virtualizerManager
    return virt_mgr.get_virtual_view(virtualizer_id=self.cfor_api.api_id,
                                     type=self.cfor_api.virtualizer_type)

  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]').debug("Requesting Virtualizer for REST-API...")
    cfor_virt = self.__get_cfor_resource_view()
    if cfor_virt is not None:
      log.getChild('[Cf-Or]').debug("Generate topo description...")
      return cfor_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: :class:`NFFG`
    """
    log.getChild('[Cf-Or]').info("Invoke install_nffg on %s with SG: %s " % (
      self.__class__.__name__, nffg))
    # Get resource view of the interface
    res = self.__get_cfor_resource_view().get_resource_info()
    self.__proceed_instantiation(nffg=nffg, resource_nffg=res)

  ##############################################################################
  # 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,
                                 resource_nffg=event.resource_nffg)

  @schedule_as_coop_task
  def __proceed_instantiation (self, nffg, resource_nffg):
    """
    Helper function to instantiate the NFFG mapping from different source.

    :param nffg: pre-mapped service request
    :type nffg: :class:`NFFG`
    :return: None
    """
    log.getChild('API').info("Invoke instantiate_nffg on %s with NF-FG: %s " % (
      self.__class__.__name__, nffg.name))
    # Get shown topology view
    if resource_nffg is None:
      log.error("Missing resource for difference calculation!")
      return
    log.debug("Got resource view for difference calculation: %s" %
              resource_nffg)
    if hasattr(self, 'ros_api') and self.ros_api:
      log.getChild('API').debug("Store received NFFG request info...")
      msg_id = self.ros_api.request_cache.cache_request_by_nffg(nffg=nffg)
      if msg_id is not None:
        self.ros_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.")
    # Check if mapping mode is set globally in CONFIG
    mapper_params = CONFIG.get_mapping_config(layer=LAYER_NAME)
    if 'mode' in mapper_params and mapper_params['mode'] is not None:
      mapping_mode = mapper_params['mode']
      log.info("Detected mapping mode from configuration: %s" % mapping_mode)
    elif nffg.mode is not None:
      mapping_mode = nffg.mode
      log.info("Detected mapping mode from NFFG: %s" % mapping_mode)
    else:
      mapping_mode = None
      log.info("No mapping mode was detected!")
    if nffg.status == NFFG.MAP_STATUS_SKIPPED:
      log.debug("Detected NFFG map status: %s! "
                "Skip difference calculation and "
                "proceed with original request..." % nffg.status)
    elif mapping_mode != NFFG.MODE_REMAP:
      # Calculated ADD-DELETE difference
      log.debug("Calculate ADD - DELETE difference with mapping mode...")
      # Recreate SG-hops for diff calc.
      log.debug("Recreate SG hops for difference calculation...")
      NFFGToolBox.recreate_all_sghops(nffg=nffg)
      NFFGToolBox.recreate_all_sghops(nffg=resource_nffg)
      log.log(VERBOSE, "New NFFG:\n%s" % nffg.dump())
      log.log(VERBOSE, "Resource NFFG:\n%s" % resource_nffg.dump())
      # Calculate difference
      add_nffg, del_nffg = NFFGToolBox.generate_difference_of_nffgs(
        old=resource_nffg, new=nffg, ignore_infras=True)
      log.log(VERBOSE, "Calculated ADD NFFG:\n%s" % add_nffg.dump())
      log.log(VERBOSE, "Calculated DEL NFFG:\n%s" % del_nffg.dump())
      if not add_nffg.is_bare() and del_nffg.is_bare():
        nffg = add_nffg
        log.info("DEL NFFG is bare! Calculated mapping mode: %s" % nffg.mode)
      elif add_nffg.is_bare() and not del_nffg.is_bare():
        nffg = del_nffg
        log.info("ADD NFFG is bare! Calculated mapping mode: %s" % nffg.mode)
      elif not add_nffg.is_bare() and not del_nffg.is_bare():
        log.warning("Both ADD / DEL mode is not supported currently")
        self.__process_mapping_result(nffg_id=nffg.id, fail=True)
        self.raiseEventNoErrors(InstantiationFinishedEvent,
                                id=nffg.id,
                                result=InstantiationFinishedEvent.ABORTED)
        return
      else:
        log.debug("Difference calculation resulted empty subNFFGs!")
        log.info("No change has been detected in request! Skip mapping...")
        log.getChild('API').debug("Invoked instantiate_nffg on %s is finished!"
                                  % self.__class__.__name__)
        self.__process_mapping_result(nffg_id=nffg.id, fail=False)
{'fraction_non_alphanumeric': 0.11948137983248946, 'fraction_numerical': 0.0006151516585435102, 'mean_word_length': 3.156047197640118, 'pattern_counts': {'":': 4, '<': 15, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 26, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1188439', 'n_tokens_mistral': 13334, 'n_tokens_neox': 12924, 'n_words': 3350}