<commit_before># Copyright 2015 <NAME> <<EMAIL>>
#
# 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.
"""
from escape import CONFIG
from escape.nffg_lib.nffg import NFFG
from escape.orchest import LAYER_NAME, log as log  # Orchestration layer logger
from escape.orchest.ros_orchestration import ResourceOrchestrator
from escape.util.api import AbstractAPI, RESTServer, AbstractRequestHandler
from escape.util.conversion import NFFGConverter
from escape.util.misc import schedule_as_coop_task, notify_remote_visualizer, \
  VERBOSE
from pox.lib.revent.revent import Event
from virtualizer import Virtualizer


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

  def __init__ (self, mapped_nffg):
    """
    Init

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


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`
    """
    super(VirtResInfoEvent, self).__init__()
    self.virtualizer = virtualizer


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


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

  def __init__ (self, id, result, error=None):
    super(InstantiationFinishedEvent, self).__init__()
    self.id = id
    self.result = result
    self.error = error


class CfOrRequestHandler(AbstractRequestHandler):
  """
  Request Handler for the Cf-OR interface.

  .. 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.

  Contains handler functions for REST-API.
  """
  # Bind HTTP verbs to UNIFY's API functions
  request_perm = {
    'GET': ('ping', 'version', 'operations', 'get_config'),
    'POST': ('ping', 'get_config', 'edit_config')
  }
  # Statically defined layer component to which this handler is bounded
  # Need to be set by container class
  bounded_layer = 'orchestration'
  static_prefix = "cfor"
  # Logger name
  LOGGER_NAME = "Cf-Or"
  log = log.getChild("[%s]" % LOGGER_NAME)
  # Use Virtualizer format
  virtualizer_format_enabled = False
  # Default communication approach
  DEFAULT_DIFF = False
  # Name mapper to avoid Python naming constraint
  rpc_mapper = {
    'get-config': "get_config",
    'edit-config': "edit_config"
  }

  def __init__ (self, request, client_address, server):
    """
    Init.
    """
    AbstractRequestHandler.__init__(self, request, client_address, server)

  def get_config (self):
    """
    Response configuration.
    """
    self.log.info("Call %s function: get-config" % self.LOGGER_NAME)
    config = self._proceed_API_call('api_cfor_get_config')
    if config is None:
      self.send_error(404, message="Resource info is missing!")
      return
    self.send_response(200)
    data = config.dump()
    self.log.log(VERBOSE, "Generated config for 'get-config:\n%s" % data)
    self.send_header('Content-Type', 'application/json')
    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 edit_config (self):
    """
    Receive configuration and initiate orchestration.
    """
    self.log.info("Call %s function: edit-config" % self.LOGGER_NAME)
    body = self._get_body()
    # log.getChild("REST-API").debug("Request body:\n%s" % body)
    nffg = NFFG.parse(body)  # Initialize NFFG from JSON representation
    self.log.log(VERBOSE, "Received request for 'edit-config':\n%s" % nffg)
    self._proceed_API_call('api_cfor_edit_config', nffg)
    self.send_acknowledge()
    self.log.debug("%s function: edit-config ended!" % self.LOGGER_NAME)


class ROSAgentRequestHandler(AbstractRequestHandler):
  """
  Request Handler for agent behaviour in Resource Orchestration 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.

  Contains handler functions for REST-API.
  """
  # Bind HTTP verbs to UNIFY's API functions
  request_perm = {
    'GET': ('ping', 'version', 'operations', 'get_config'),
    'POST': ('ping', 'get_config', 'edit_config')
  }
  # Statically defined layer component to which this handler is bounded
  # Need to be set by container class
  bounded_layer = 'orchestration'
  # Set special prefix to imitate OpenStack agent API
  static_prefix = "escape"
  # Logger name
  LOGGER_NAME = "Sl-Or"
  log = log.getChild("[%s]" % LOGGER_NAME)
  # Use Virtualizer format
  virtualizer_format_enabled = False
  # Default communication approach
  DEFAULT_DIFF = False
  # Name mapper to avoid Python naming constraint
  rpc_mapper = {
    'get-config': "get_config",
    'edit-config': "edit_config"
  }

  def __init__ (self, request, client_address, server):
    """
    Init.
    """
    AbstractRequestHandler.__init__(self, request, client_address, server)

  def get_config (self):
    """
    Response configuration.

    :return: None
    """
    self.log.info("Call %s function: get-config" % self.LOGGER_NAME)
    # Forward call to main layer class
    config = self._proceed_API_call('api_ros_get_config')
    if config is None:
      self.send_error(404, message="Resource info is missing!")
      return
    # Setup OK status for HTTP response
    self.send_response(200)
    # Global resource has not changed -> respond with the cached topo
    if config is False:
      self.log.info(
        "Global 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:
      # Convert required NFFG if needed
      if self.virtualizer_format_enabled:
        self.log.debug("Convert internal NFFG to Virtualizer...")
        converter = NFFGConverter(domain=None, logger=log)
        v_topology = converter.dump_to_Virtualizer(nffg=config)
        # 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 acquired topology...")
        self.server.last_response = config
        data = config.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 edit_config (self):
    """
    Receive configuration and initiate orchestration.

    :return: None
    """
    self.log.info("Call %s function: edit-config" % self.LOGGER_NAME)
    # Obtain NFFG from request body
    self.log.debug("Detected response format: %s" %
                   self.headers.get("Content-Type"))
    raw_body = self._get_body()
    # log.getChild("REST-API").debug("Request body:\n%s" % body)
    if raw_body is None or not raw_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" and \
         not raw_body.startswith("<?xml version="):
        self.log.error("Received data is not in XML format despite of the "
                       "UNIFY interface is enabled!")
        self.send_error(415)
        return
      # Get received Virtualizer
      received_cfg = Virtualizer.parse_from_text(text=raw_body)
      self.log.log(VERBOSE,
                   "Received request for 'edit-config':\n%s" % raw_body)
      # If there was not get-config request so far
      if self.DEFAULT_DIFF:
        if self.server.last_response is None:
          self.log.info("Missing cached Virtualizer! Acquiring topology now...")
          config = self._proceed_API_call('api_ros_get_config')
          if config is None:
            self.log.error("Requested resource info is missing!")
            self.send_error(404, message="Resource info is missing!")
            return
          elif config is False:
            self.log.warning("Requested info is unchanged but has not found!")
            self.send_error(404, message="Resource info is missing!")
          else:
            # Convert required NFFG if needed
            if self.virtualizer_format_enabled:
              self.log.debug("Convert internal NFFG to Virtualizer...")
              converter = NFFGConverter(domain=None, logger=log)
              v_topology = converter.dump_to_Virtualizer(nffg=config)
              # Cache converted data for edit-config patching
              self.log.debug("Cache converted topology...")
              self.server.last_response = v_topology
            else:
              self.log.debug("Cache acquired topology...")
              self.server.last_response = config
        # Perform patching
        full_cfg = self.__recreate_full_request(diff=received_cfg)
      else:
        full_cfg = received_cfg
      self.log.log(VERBOSE, "Generated request:\n%s" % full_cfg.xml())
      # Convert response's body to NFFG
      self.log.info("Converting full request data...")
      converter = NFFGConverter(domain="REMOTE", logger=log)
      nffg = converter.parse_from_Virtualizer(vdata=full_cfg)
    else:
      if self.headers.get("Content-Type") != "application/json":
        self.log.error("Received data is not in JSON format despite of the "
                       "UNIFY interface is disabled!")
        self.send_error(415)
        return
      # Initialize NFFG from JSON representation
      self.log.info("Parsing request into internal NFFG format...")
      nffg = NFFG.parse(raw_body)
    self.log.debug("Parsed NFFG install request: %s" % nffg)
    self._proceed_API_call('api_ros_edit_config', nffg)
    self.send_acknowledge()
    self.log.debug("%s function: edit-config ended!" % self.LOGGER_NAME)

  def __recreate_full_request (self, diff):
    """
    Recreate the full domain install request based on previously sent
    topology config and received diff request.

    :return: recreated request
    :rtype: :any:`NFFG`
    """
    self.log.info("Patching cached topology with received diff...")
    full_request = self.server.last_response.full_copy()
    full_request.bind(relative=True)
    diff.bind(relative=True)
    # Adapt changes on  the local config
    full_request.patch(source=diff)
    full_request.bind(relative=True)
    return full_request


class ResourceOrchestrationAPI(AbstractAPI):<commit_msg>Remove diff binding from patch wrapper to avoid resolve bug in Virtualizer<commit_after># Copyright 2015 <NAME> <<EMAIL>>
#
# 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.
"""
from escape import CONFIG
from escape.nffg_lib.nffg import NFFG
from escape.orchest import LAYER_NAME, log as log  # Orchestration layer logger
from escape.orchest.ros_orchestration import ResourceOrchestrator
from escape.util.api import AbstractAPI, RESTServer, AbstractRequestHandler
from escape.util.conversion import NFFGConverter
from escape.util.misc import schedule_as_coop_task, notify_remote_visualizer, \
  VERBOSE
from pox.lib.revent.revent import Event
from virtualizer import Virtualizer


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

  def __init__ (self, mapped_nffg):
    """
    Init

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


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`
    """
    super(VirtResInfoEvent, self).__init__()
    self.virtualizer = virtualizer


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


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

  def __init__ (self, id, result, error=None):
    super(InstantiationFinishedEvent, self).__init__()
    self.id = id
    self.result = result
    self.error = error


class CfOrRequestHandler(AbstractRequestHandler):
  """
  Request Handler for the Cf-OR interface.

  .. 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.

  Contains handler functions for REST-API.
  """
  # Bind HTTP verbs to UNIFY's API functions
  request_perm = {
    'GET': ('ping', 'version', 'operations', 'get_config'),
    'POST': ('ping', 'get_config', 'edit_config')
  }
  # Statically defined layer component to which this handler is bounded
  # Need to be set by container class
  bounded_layer = 'orchestration'
  static_prefix = "cfor"
  # Logger name
  LOGGER_NAME = "Cf-Or"
  log = log.getChild("[%s]" % LOGGER_NAME)
  # Use Virtualizer format
  virtualizer_format_enabled = False
  # Default communication approach
  DEFAULT_DIFF = False
  # Name mapper to avoid Python naming constraint
  rpc_mapper = {
    'get-config': "get_config",
    'edit-config': "edit_config"
  }

  def __init__ (self, request, client_address, server):
    """
    Init.
    """
    AbstractRequestHandler.__init__(self, request, client_address, server)

  def get_config (self):
    """
    Response configuration.
    """
    self.log.info("Call %s function: get-config" % self.LOGGER_NAME)
    config = self._proceed_API_call('api_cfor_get_config')
    if config is None:
      self.send_error(404, message="Resource info is missing!")
      return
    self.send_response(200)
    data = config.dump()
    self.log.log(VERBOSE, "Generated config for 'get-config:\n%s" % data)
    self.send_header('Content-Type', 'application/json')
    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 edit_config (self):
    """
    Receive configuration and initiate orchestration.
    """
    self.log.info("Call %s function: edit-config" % self.LOGGER_NAME)
    body = self._get_body()
    # log.getChild("REST-API").debug("Request body:\n%s" % body)
    nffg = NFFG.parse(body)  # Initialize NFFG from JSON representation
    self.log.log(VERBOSE, "Received request for 'edit-config':\n%s" % nffg)
    self._proceed_API_call('api_cfor_edit_config', nffg)
    self.send_acknowledge()
    self.log.debug("%s function: edit-config ended!" % self.LOGGER_NAME)


class ROSAgentRequestHandler(AbstractRequestHandler):
  """
  Request Handler for agent behaviour in Resource Orchestration 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.

  Contains handler functions for REST-API.
  """
  # Bind HTTP verbs to UNIFY's API functions
  request_perm = {
    'GET': ('ping', 'version', 'operations', 'get_config'),
    'POST': ('ping', 'get_config', 'edit_config')
  }
  # Statically defined layer component to which this handler is bounded
  # Need to be set by container class
  bounded_layer = 'orchestration'
  # Set special prefix to imitate OpenStack agent API
  static_prefix = "escape"
  # Logger name
  LOGGER_NAME = "Sl-Or"
  log = log.getChild("[%s]" % LOGGER_NAME)
  # Use Virtualizer format
  virtualizer_format_enabled = False
  # Default communication approach
  DEFAULT_DIFF = False
  # Name mapper to avoid Python naming constraint
  rpc_mapper = {
    'get-config': "get_config",
    'edit-config': "edit_config"
  }

  def __init__ (self, request, client_address, server):
    """
    Init.
    """
    AbstractRequestHandler.__init__(self, request, client_address, server)

  def get_config (self):
    """
    Response configuration.

    :return: None
    """
    self.log.info("Call %s function: get-config" % self.LOGGER_NAME)
    # Forward call to main layer class
    config = self._proceed_API_call('api_ros_get_config')
    if config is None:
      self.send_error(404, message="Resource info is missing!")
      return
    # Setup OK status for HTTP response
    self.send_response(200)
    # Global resource has not changed -> respond with the cached topo
    if config is False:
      self.log.info(
        "Global 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:
      # Convert required NFFG if needed
      if self.virtualizer_format_enabled:
        self.log.debug("Convert internal NFFG to Virtualizer...")
        converter = NFFGConverter(domain=None, logger=log)
        v_topology = converter.dump_to_Virtualizer(nffg=config)
        # 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 acquired topology...")
        self.server.last_response = config
        data = config.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 edit_config (self):
    """
    Receive configuration and initiate orchestration.

    :return: None
    """
    self.log.info("Call %s function: edit-config" % self.LOGGER_NAME)
    # Obtain NFFG from request body
    self.log.debug("Detected response format: %s" %
                   self.headers.get("Content-Type"))
    raw_body = self._get_body()
    # log.getChild("REST-API").debug("Request body:\n%s" % body)
    if raw_body is None or not raw_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" and \
         not raw_body.startswith("<?xml version="):
        self.log.error("Received data is not in XML format despite of the "
                       "UNIFY interface is enabled!")
        self.send_error(415)
        return
      # Get received Virtualizer
      received_cfg = Virtualizer.parse_from_text(text=raw_body)
      self.log.log(VERBOSE,
                   "Received request for 'edit-config':\n%s" % raw_body)
      # If there was not get-config request so far
      if self.DEFAULT_DIFF:
        if self.server.last_response is None:
          self.log.info("Missing cached Virtualizer! Acquiring topology now...")
          config = self._proceed_API_call('api_ros_get_config')
          if config is None:
            self.log.error("Requested resource info is missing!")
            self.send_error(404, message="Resource info is missing!")
            return
          elif config is False:
            self.log.warning("Requested info is unchanged but has not found!")
            self.send_error(404, message="Resource info is missing!")
          else:
            # Convert required NFFG if needed
            if self.virtualizer_format_enabled:
              self.log.debug("Convert internal NFFG to Virtualizer...")
              converter = NFFGConverter(domain=None, logger=log)
              v_topology = converter.dump_to_Virtualizer(nffg=config)
              # Cache converted data for edit-config patching
              self.log.debug("Cache converted topology...")
              self.server.last_response = v_topology
            else:
              self.log.debug("Cache acquired topology...")
              self.server.last_response = config
        # Perform patching
        full_cfg = self.__recreate_full_request(diff=received_cfg)
      else:
        full_cfg = received_cfg
      self.log.log(VERBOSE, "Generated request:\n%s" % full_cfg.xml())
      # Convert response's body to NFFG
      self.log.info("Converting full request data...")
      converter = NFFGConverter(domain="REMOTE", logger=log)
      nffg = converter.parse_from_Virtualizer(vdata=full_cfg)
    else:
      if self.headers.get("Content-Type") != "application/json":
        self.log.error("Received data is not in JSON format despite of the "
                       "UNIFY interface is disabled!")
        self.send_error(415)
        return
      # Initialize NFFG from JSON representation
      self.log.info("Parsing request into internal NFFG format...")
      nffg = NFFG.parse(raw_body)
    self.log.debug("Parsed NFFG install request: %s" % nffg)
    self._proceed_API_call('api_ros_edit_config', nffg)
    self.send_acknowledge()
    self.log.debug("%s function: edit-config ended!" % self.LOGGER_NAME)

  def __recreate_full_request (self, diff):
    """
    Recreate the full domain install request based on previously sent
    topology config and received diff request.

    :return: recreated request
    :rtype: :any:`NFFG`
    """
    self.log.info("Patching cached topology with received diff...")
    full_request = self.server.last_response.full_copy()
    full_request.bind(relative=True)
    # Do not call bind on diff to avoid resolve error in Virtualizer
    # diff.bind(relative=True)
    # Adapt changes on  the local config
    full_request.patch(source=diff)
    full_request.bind(relative=True)
    return full_request


class ResourceOrchestrationAPI(AbstractAPI):
{'fraction_non_alphanumeric': 0.09879839786381843, 'fraction_numerical': 0.002748763056624519, 'mean_word_length': 3.2445, 'pattern_counts': {'":': 2, '<': 11, '<?xml version=': 2, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 12}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7637298', 'n_tokens_mistral': 7620, 'n_tokens_neox': 7397, 'n_words': 2198}