def invoke_hook(self, msg_id, result, body=None): try: result = int(result) except ValueError: log.error( "Received response code is not valid: %s! Abort callback..." % result) return if msg_id not in self.__register: log.warning( "Received unregistered callback with id: %s from domain: %s" % (msg_id, self.domain_name)) return log.debug( "Received valid callback with id: %s, result: %s from domain: %s" % (msg_id, "TIMEOUT" if not result else result, self.domain_name)) cb = self.__register.get(msg_id) if cb is None: log.error("Missing callback: %s from register!" % msg_id) return cb.result_code = result cb.body = body if cb.hook is None: log.debug("No hook was defined!") self.__blocking_mutex.set() return elif callable(cb.hook): log.debug("Schedule callback hook: %s" % cb.short()) cb.hook(callback=cb) else: log.warning( "No callable hook was defined for the received callback: %s!" % msg_id)
def _detect_topology(self): """ Check the undetected topology is up or not. If the domain is confirmed and detected, the ``internal_topo`` attribute will be updated with the new topology. .. warning:: No :any:`DomainChangedEvent` will be raised internally if the domain is confirmed! :return: detected or not :rtype: bool """ if self.topoAdapter.check_domain_reachable(): log.info(">>> %s domain confirmed!" % self.domain_name) self._detected = True log.info("Requesting resource information from %s domain..." % self.domain_name) topo_nffg = self.topoAdapter.get_topology_resource() if topo_nffg: log.debug("Save detected topology: %s..." % topo_nffg) # Update the received new topo self.internal_topo = topo_nffg else: log.warning("Resource info is missing!") return self._detected
def start_mgr(self, name, mgr_params=None, autostart=True): """ Create, initialize and start a DomainManager with given name and start the manager by default. :param name: name of domain manager :type name: str :param mgr_params: mgr parameters :type mgr_params: dict :param autostart: also start the domain manager (default: True) :type autostart: bool :return: domain manager :rtype: :any:`AbstractDomainManager` """ # If not started if not self.is_started(name): # Load from CONFIG mgr = self.load_component(name, params=mgr_params) if mgr is not None: # Call init - give self for the DomainManager to initiate the # necessary DomainAdapters itself mgr.init(self) # Autostart if needed if autostart: mgr.run() # Save into repository self.__repository[name] = mgr else: log.warning("%s domain component has been already started! Skip " "reinitialization..." % name) # Return with manager return self.__repository[name]
def add_domain(self, domain, nffg): """ Update the global view data with the specific domain info. :param domain: domain name :type domain: str :param nffg: infrastructure info collected from the domain :type nffg: :any:`NFFG` :return: None """ # If the domain is not tracked if domain not in self.__tracked_domains: if not nffg: log.warning("Got empty data. Skip domain addition...") return log.info("Append %s domain to DoV..." % domain) # If DoV is empty if not self.__dov.is_empty(): # Merge domain topo into global view self.__dov.merge_new_domain_into_dov(nffg=nffg) else: # No other domain detected, set NFFG as the whole Global view log.debug( "DoV is empty! Add new domain: %s as the global view!" % domain) self.__dov.set_domain_as_global_view(domain=domain, nffg=nffg) # Add detected domain to cached domains self.__tracked_domains.add(domain) else: log.error( "New domain: %s has already tracked: %s! Abort adding..." % (domain, self.__tracked_domains))
def set_domain_as_global_view(self, domain, nffg): """ Set the copy of given NFFG as the global view of DoV. Add the specific :attr:`DoV` id and generated name to the global view. :param nffg: NFFG instance intended to use as the global view :type nffg: :class:`NFFG` :param domain: name of the merging domain :type domain: str :return: updated Dov :rtype: :class:`NFFG` """ log.debug("Set domain: %s as the global view!" % domain) if not self.__global_nffg.is_empty(): log.warning( "Global view is not empty! Current state will be lost!") self.__global_nffg = nffg.copy() self.__global_nffg.id = DoV self.__global_nffg.name = DoV log.debug("DoV stat:\n%s" % self.__global_nffg.get_stat()) # Raise event for observing Virtualizers about topology change self.raiseEventNoErrors(DoVChangedEvent, cause=DoVChangedEvent.TYPE.UPDATE) return self.__global_nffg
def remerge_domain_in_dov(self, domain, nffg): """ Update the existing domain in the merged Global view with explicit domain remove and re-add. :param nffg: changed infrastructure info :type nffg: :class:`NFFG` :param domain: name of the merging domain :type domain: str :return: updated Dov :rtype: :class:`NFFG` """ NFFGToolBox.remove_domain(base=self.__global_nffg, domain=domain, log=log) # log.log(VERBOSE, "Reduced Dov:\n%s" % self.__global_nffg.dump()) NFFGToolBox.merge_new_domain(base=self.__global_nffg, nffg=nffg, log=log) log.debug("DoV stat:\n%s" % self.__global_nffg.get_stat()) log.log(VERBOSE, "Re-merged DoV:\n%s" % self.__global_nffg.dump()) if self.__global_nffg.is_empty(): log.warning( "No Node had been remained after updating the domain part: " "%s! DoV is empty!" % domain) # Raise event for observing Virtualizers about topology change self.raiseEventNoErrors(DoVChangedEvent, cause=DoVChangedEvent.TYPE.CHANGE) return self.__global_nffg
def _acquire_resource(self): """ Compute and return with the Single BiS-BiS view based on the global view. :return: single BiSBiS representation of the global view :rtype: :class:`NFFG` """ dov = self.global_view.get_resource_info() if dov.is_empty(): # DoV is not initialized yet! Probably only just remote Mgrs has been # enabled! return with the default empty DoV log.warning( "Requested global resource view is empty! Return the default empty " "topology!") return dov else: if str(self.sbb_id).startswith('$'): if str(self.sbb_id)[1:] in os.environ: self.sbb_id = os.environ.get(str(self.sbb_id)[1:]) log.debug("Detected SBB id from environment variable: %s" % self.sbb_id) # Generate the Single BiSBiS representation sbb = NFFGToolBox.generate_SBB_representation(nffg=dov, sbb_id=self.sbb_id, log=log) log.log(VERBOSE, "Generated SBB:\n%s" % sbb.dump()) return sbb
def init(self, configurator, **kwargs): """ Abstract function for component initialization. :param configurator: component configurator for configuring adapters :type configurator: :any:`ComponentConfigurator` :param kwargs: optional parameters :type kwargs: dict :return: None """ # Load and initiate adapters using the initiate_adapters() template func self._load_adapters(configurator=configurator, **kwargs) # Skip to start polling if it's set if not self._poll: # Try to request/parse/update Mininet topology if not self._detect_topology(): log.warning("%s domain not confirmed during init!" % self.domain_name) else: # Notify all components for topology change --> this event causes # the DoV updating self.raiseEventNoErrors( DomainChangedEvent, domain=self.domain_name, data=self.internal_topo, cause=DomainChangedEvent.TYPE.DOMAIN_UP) else: log.info("Start polling %s domain..." % self.domain_name) self.start_polling(self.POLL_INTERVAL)
def setup_timer(self, timeout, hook, **kwargs): if not timeout: log.debug("Timeout disabled for request callback: %s" % self.request_id) return if not self.__timer: log.debug("Setup timeout: %s for callback: %s" % (timeout, self.callback_id)) self.__timer = Timer(timeout, hook, kwargs=kwargs) self.__timer.start() else: log.warning("Callback timer has already been set up!")
def __get_domain(self): """ Return the relevant domain name coded in the callback URL. :return: domain name :rtype: str """ path = urlparse.urlparse(self.path).path.split('/') if len(path) < 2: log.warning("Domain part is missing from URL: %s!" % self.path) return None else: return path[1]
def __process_request(self): log.debug("Received callback request with path: %s" % self.path) params = self.__get_request_params() if self.RESULT_PARAM_NAME in params and self.MESSAGE_ID_NAME in params: body = self._get_body() if body: log.debug("Received callback body size: %s" % len(body)) else: log.debug("No callback body") self.server.invoke_hook(msg_id=params.get(self.MESSAGE_ID_NAME), result=params.get(self.RESULT_PARAM_NAME), body=body) else: log.warning("Received callback with missing params: %s" % params)
def invoke_hook(self, msg_id, domain, result, body=None): """ Main entry point to invoke a callback based on the extracted data from received message. :param msg_id: message id :type msg_id: str :param domain: domain name :type domain: str :param result: result of the callback :type result: str :param body: parsed callback body (optional) :type body: str or None :return: None """ try: result = int(result) except ValueError: log.error( "Received response code is not valid: %s! Abort callback..." % result) return if (domain, msg_id) not in self.__register: log.warning( "Received unregistered callback with id: %s from domain: %s" % (msg_id, domain)) return log.debug( "Received valid callback with id: %s, result: %s from domain: %s" % (msg_id, "TIMEOUT" if not result else result, domain)) cb = self.__register.get((domain, msg_id)) if cb is None: log.error("Missing callback: %s from register!" % msg_id) return stats.add_measurement_start_entry(type=stats.TYPE_DEPLOY_CALLBACK, info="%s-callback_received" % domain) cb.result_code = result cb.body = body if cb.hook is None: log.debug("No hook was defined!") self.__blocking_mutex.set() return elif callable(cb.hook): log.debug("Schedule callback hook: %s" % cb.short()) cb.hook(callback=cb) else: log.warning( "No callable hook was defined for the received callback: %s!" % msg_id)
def remove_domain(self, domain): """ Remove the detected domain from the global view. :param domain: domain name :type domain: str :return: None """ if domain in self.__tracked_domains: log.info("Remove domain: %s from DoV..." % domain) self.__dov.remove_domain_from_dov(domain=domain) self.__tracked_domains.remove(domain) else: log.warning( "Removing domain: %s is not included in tracked domains: %s! " "Skip removing..." % (domain, self.__tracked_domains))
def _generate_global_view(self, id): """ Generate a Global View virtualizer, store and return with it. :param id: unique virtualizer id :type id: int or str :return: generated Virtualizer :rtype: :any:`GlobalViewVirtualizer` """ if id in self._virtualizers: log.warning("Requested Virtualizer with ID: %s is already exist! " "Virtualizer creation skipped..." % id) else: log.debug("Generating Global View Virtualizer with id: %s" % id) self._virtualizers[id] = GlobalViewVirtualizer(self.dov, id) return self._virtualizers[id]
def send_with_timeout(self, method, url=None, body=None, timeout=None, **kwargs): """ Send REST request with handling exceptions except the TimeoutError. :param method: HTTP method :type method: str :param url: valid URL or relevant part follows ``self.base_url`` :type url: str :param body: request body :type body: :any:`NFFG` or dict or bytes or str :param timeout: optional timeout param can be given also here :type timeout: int :raises: :any:`requests.Timeout` :return: raw response data :rtype: str """ try: if timeout is not None: kwargs['timeout'] = timeout self.send_request(method, url, body, **kwargs) # return self._response.status_code if self._response is not None else # None return self._response.text if self._response is not None else None except ConnectionError: log.error("Remote agent(adapter: %s, url: %s) is not reachable!" % (self.name, self._base_url)) return None except HTTPError as e: log.error( "Remote agent(adapter: %s, url: %s) responded with an error: %s" % (self.name, self._base_url, e.message)) return None except Timeout: raise except RequestException as e: log.error("Got unexpected exception: %s" % e) return None except KeyboardInterrupt: log.warning("Request to remote agent(adapter: %s, url: %s) is " "interrupted by user!" % (self.name, self._base_url)) return None
def delete_flowrules(self, id): """ Delete all flowrules from the first (default) table of an OpenFlow switch. :param id: ID of the infra element stored in the NFFG :type id: str :return: None """ conn = self.openflow.getConnection(dpid=self.infra_to_dpid[id]) if not conn: log.warning("Missing connection for node element: %s! " "Skip deletion of flowrules..." % id) return log.debug("Delete flow entries from INFRA %s on connection: %s ..." % (id, conn)) msg = of.ofp_flow_mod(command=of.OFPFC_DELETE) conn.send(msg)
def rewrite_domain(self, nffg): """ Rewrite the DOMAIN information in nodes of the given :any:`NFFG`. :param nffg: topology description :type nffg: :any:`NFFG` :return: the rewritten description :rtype: :any:`NFFG` """ log.debug("Rewrite domain of Infrastructure nodes to: %s" % self.domain_name) if self.domain_name == "UNDEFINED": log.warning( "Domain name is not set for Adapter(name: %s)! Skip domain rewrite " "for %s..." % (self.name, nffg)) for infra in nffg.infras: infra.domain = self.domain_name return nffg
def stop_mgr(self, name): """ Stop and derefer a DomainManager with given name and remove from the repository also. :param name: name of domain manager :type name: str :return: None """ # If started if self.is_started(name): # Call finalize self.__repository[name].finit() # Remove from repository # del self.__repository[domain_name] else: log.warning("Missing domain component: %s! Skipping stop task..." % name)
def _acquire_resource(self): """ Compute and return with the Single BiS-BiS view based on the global view. :return: single BiSBiS representation of the global view :rtype: :any:`NFFG` """ dov = self.global_view.get_resource_info() if dov.is_empty(): # DoV is not initialized yet! Probably only just remote Mgrs has been # enabled! return with the default empty DoV log.warning( "Requested global resource view is empty! Return the default empty " "topology!") return dov else: # Generate the Single BiSBiS representation return NFFGToolBox.generate_SBB_representation(nffg=dov, log=log)
def subscribe_callback(self, hook, cb_id, domain, type, req_id=None, data=None, timeout=None): """ Register for a callback. :param hook: hook function :type hook: callable :param cb_id: callback ID :type cb_id: str :param domain: domain name :type domain: str or int :param type: callback type :type type: str :param req_id: original request ID (optional) :type req_id: str or int :param data: optional callback data (optional) :type data: object :param timeout: explicit timeout value (optional) :type timeout: float :return: created callback object :rtype: :class:`Callback` """ log.debug("Register callback for response: %s on domain: %s" % (cb_id, domain)) if cb_id not in self.__register: cb = Callback(hook=hook, callback_id=cb_id, type=type, domain=domain, request_id=req_id, data=data) _timeout = timeout if timeout is not None else self.wait_timeout cb.setup_timer(_timeout, self.invoke_hook, msg_id=cb_id, result=0) self.__register[(domain, cb_id)] = cb return cb else: log.warning("Hook is already registered for id: %s on domain: %s" % (cb_id, domain))
def get_virtual_view(self, virtualizer_id, type=None, cls=None): """ Return the Virtual View as a derived class of :class:`AbstractVirtualizer <escape.orchest.virtualization_mgmt.AbstractVirtualizer>`. :param virtualizer_id: unique id of the requested Virtual view :type virtualizer_id: int or str :param type: type of the Virtualizer predefined in this class :type type: str :param cls: specific Virtualizer class if type is not given :type cls: :any:`AbstractVirtualizer` :return: virtual view :rtype: :any:`AbstractVirtualizer` """ log.debug("Invoke %s to get <Virtual View> (for layer ID: %s)" % (self.__class__.__name__, virtualizer_id)) # If this is the first request, need to generate the view if virtualizer_id not in self._virtualizers: if type is not None: # SINGLE: generate a trivial Single BiS-BiS virtualizer log.debug("Requested virtualizer type: %s" % type) if type == AbstractVirtualizer.SINGLE_VIRTUALIZER: self._generate_single_view(id=virtualizer_id) # GLOBAL: generate a non-filtering Global View Virtualizer elif type == AbstractVirtualizer.GLOBAL_VIRTUALIZER: self._generate_global_view(id=virtualizer_id) # Not supported format else: log.warning("Unsupported Virtualizer type: %s" % type) return # If a specific AbstractVirtualizer type was given elif cls is not None: log.debug("Generating Virtualizer type: %s with id: %s" % (cls.__name__, virtualizer_id)) self._virtualizers[virtualizer_id] = cls( self.dov, virtualizer_id) # Generate a Single BiS-BiS Virtualizer by default else: # Virtualizer type is not defined: Use SingleBiSBiSVirtualizer by # default self._generate_single_view(id=virtualizer_id) # Return Virtualizer return self._virtualizers[virtualizer_id]
def load_default_mgrs(self): """ Initiate and start default DomainManagers defined in CONFIG. :return: None """ log.info("Initialize additional DomainManagers from config...") # very dummy initialization mgrs = CONFIG.get_managers() if not mgrs: log.info("No DomainManager has been configured!") return for mgr_name in mgrs: # Get manager parameters from config mgr_cfg = CONFIG.get_component_params(component=mgr_name) if 'domain_name' in mgr_cfg: if mgr_cfg['domain_name'] in self.domains: log.warning( "Domain name collision! Domain Manager: %s has already " "initiated with the domain name: %s" % (self.get_component_by_domain( domain_name=mgr_cfg['domain_name']), mgr_cfg['domain_name'])) else: # If no domain name was given, use the manager config name by default mgr_cfg['domain_name'] = mgr_name # Get manager class mgr_class = CONFIG.get_component(component=mgr_name) if mgr_class.IS_LOCAL_MANAGER: loaded_local_mgr = [ name for name, mgr in self.__repository.iteritems() if mgr.IS_LOCAL_MANAGER ] if loaded_local_mgr: log.warning( "A local DomainManager has already been initiated with " "the name: %s! Skip initiating DomainManager: %s" % (loaded_local_mgr, mgr_name)) return log.debug("Load DomainManager based on config: %s" % mgr_name) # Start domain manager self.start_mgr(name=mgr_name, mgr_params=mgr_cfg)
def send_no_error(self, method, url=None, body=None, **kwargs): """ Send REST request with handling exceptions. :param method: HTTP method :type method: str :param url: valid URL or relevant part follows ``self.base_url`` :type url: str :param body: request body :type body: :class:`NFFG` or dict or bytes or str :return: raw response data :rtype: str """ try: return self.send_with_timeout(method, url, body, **kwargs) except Timeout: log.warning( "Remote agent(adapter: %s, url: %s) reached timeout limit!" % (self.name, self._base_url)) return None
def load_local_domain_mgr(self): """ Initiate the DomainManager for the internal domain. :return: None """ loaded_local_mgr = [ name for name, mgr in self.__repository.iteritems() if mgr.IS_LOCAL_MANAGER ] if loaded_local_mgr: log.warning( "A local DomainManager has already been initiated with the " "name: %s! Skip initiation of default local DomainManager: %s" % (loaded_local_mgr, mgrs.InternalDomainManager.name)) return log.debug("Init DomainManager for local domain based on config: %s" % mgrs.InternalDomainManager.name) # Internal domain is hard coded with the name: INTERNAL self.start_mgr(name=mgrs.InternalDomainManager.name)
def remove_domain_from_dov(self, domain): """ Remove the nodes and edges with the given from Global view. :param domain: domain name :type domain: str :return: updated Dov :rtype: :any:`NFFG` """ NFFGToolBox.remove_domain(base=self.__global_nffg, domain=domain, log=log) if self.__global_nffg.is_empty(): log.warning( "No Node had been remained after updating the domain part: " "%s! DoV is empty!" % domain) log.log(VERBOSE, "Reduced Dov:\n%s" % self.__global_nffg.dump()) # Raise event for observing Virtualizers about topology change self.raiseEventNoErrors(DoVChangedEvent, cause=DoVChangedEvent.TYPE.REDUCE) return self.__global_nffg
def _acquire_resource(self): """ Compute and return with the Single BiS-BiS view based on the global view. :return: single BiSBiS representation of the global view :rtype: :class:`NFFG` """ dov = self.global_view.get_resource_info() if dov.is_empty(): # DoV is not initialized yet! Probably only just remote Mgrs has been # enabled! return with the default empty DoV log.warning( "Requested global resource view is empty! Return the default empty " "topology!") return dov else: filtered_dov = self.__filter_external_domains(nffg=dov) # Generate the Single BiSBiS representation sbb = NFFGToolBox.generate_SBB_representation(nffg=filtered_dov, log=log) log.log(VERBOSE, "Generated SBB:\n%s" % sbb.dump()) return sbb
def register_url(self, domain, host=None, port=None): """ Register callback URL for given `domain` instead of using a calculated one. :param domain: domain name :type domain: str :param host: host name :type host: str :param port: port :type port: str or int :return: None """ if not host: host = self.server_address[0] if not port: port = self.server_address[1] if domain in self.__domain_proxy: log.warning("Overriding domain address: %s for domain %s" % (self.__domain_proxy[domain], domain)) url = "http://%s:%s/%s/%s" % (host, port, domain, self.DEFAULT_POSTFIX) log.debug("Register explicit URL: %s for domain: %s" % (url, domain)) self.__domain_proxy[domain] = url
def subscribe_callback(self, hook, cb_id, type, req_id=None, data=None, timeout=None): log.debug("Register callback for response: %s on domain: %s" % (cb_id, self.domain_name)) if cb_id not in self.__register: cb = Callback(hook=hook, callback_id=cb_id, type=type, request_id=req_id, data=data) _timeout = timeout if timeout is not None else self.wait_timeout cb.setup_timer(_timeout, self.invoke_hook, msg_id=cb_id, result=0) self.__register[cb_id] = cb return cb else: log.warning("Hook is already registered for id: %s on domain: %s" % (cb_id, self.domain_name))
def update_domain_in_dov(self, domain, nffg): """ Update the existing domain in the merged Global view. :param nffg: NFFG object need to be updated with :type nffg: :any:`NFFG` :param domain: name of the merging domain :type domain: str :return: updated Dov :rtype: :any:`NFFG` """ NFFGToolBox.update_domain(base=self.__global_nffg, updated=nffg, log=log) if self.__global_nffg.is_empty(): log.warning( "No Node had been remained after updating the domain part: " "%s! DoV is empty!" % domain) log.log(VERBOSE, "Updated DoV:\n%s" % self.__global_nffg.dump()) # Raise event for observing Virtualizers about topology change self.raiseEventNoErrors(DoVChangedEvent, cause=DoVChangedEvent.TYPE.CHANGE) return self.__global_nffg
def __process_request(self): """ Process the received message and invoke the registered hook function. :return: None """ log.debug("Received callback request with path: %s" % self.path) params = self.__get_request_params() if self.RESULT_PARAM_NAME in params and self.MESSAGE_ID_NAME in params: body = self._get_body() if body: log.debug("Received callback body size: %s" % len(body)) else: log.debug("No callback body") domain = self.__get_domain() if not domain: log.warning("No domain was detected in URL: %s!" % self.path) self.server.invoke_hook(msg_id=params.get(self.MESSAGE_ID_NAME), result=params.get(self.RESULT_PARAM_NAME), domain=domain, body=body) else: log.warning("Received callback with missing params: %s" % params)