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 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 __proceed_installation (self, mapped_nffg, original_request=None): """ Helper function to instantiate the NFFG mapping from different source. :param mapped_nffg: pre-mapped service request :type mapped_nffg: :class:`NFFG` :return: None """ log.getChild('API').info("Invoke install_nffg on %s with NF-FG: %s " % ( self.__class__.__name__, mapped_nffg)) stats.add_measurement_start_entry(type=stats.TYPE_DEPLOY, info=LAYER_NAME) try: deploy_status = self.controller_adapter.install_nffg(mapped_nffg, original_request) except Exception: log.error("Something went wrong during NFFG installation!") self.raiseEventNoErrors(InstallationFinishedEvent, result=InstallationFinishedEvent.DEPLOY_ERROR) raise log.getChild('API').debug("Invoked install_nffg on %s is finished!" % self.__class__.__name__) if not deploy_status.still_pending: id = mapped_nffg.id result = InstallationFinishedEvent.get_result_from_status(deploy_status) log.info("Overall installation result: %s" % result) self.raiseEventNoErrors(InstallationFinishedEvent, id=id, result=result)
def run(self): try: log.debug("Start %s for domain: %s on %s:%s" % (self.__class__.__name__, self.domain_name, self.server_address[0], self.server_address[1])) self.serve_forever() except KeyboardInterrupt: raise except Exception as e: log.error("Got exception in %s: %s" % (self.__class__.__name__, e)) finally: self.server_close()
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 __proceed_installation(self, mapped_nffg, original_request=None, direct_deploy=False): """ Helper function to instantiate the NFFG mapping from different source. :param mapped_nffg: pre-mapped service request :type mapped_nffg: :class:`NFFG` :return: None """ log.getChild('API').info("Invoke install_nffg on %s with NF-FG: %s " % (self.__class__.__name__, mapped_nffg)) stats.add_measurement_start_entry(type=stats.TYPE_DEPLOY, info=LAYER_NAME) try: deploy_status = self.controller_adapter.install_nffg( mapped_nffg=mapped_nffg, original_request=original_request, direct_deploy=direct_deploy) except Exception as e: log.error("Something went wrong during NFFG installation: %s" % e) self._process_mapping_result(nffg_id=mapped_nffg.id, fail=True) self.raiseEventNoErrors( InstallationFinishedEvent, id=mapped_nffg.id, result=InstallationFinishedEvent.DEPLOY_ERROR) return log.getChild('API').debug("Invoked install_nffg on %s is finished!" % self.__class__.__name__) if deploy_status is None: log.error("Deploy status is missing!") self._process_mapping_result(nffg_id=mapped_nffg.id, fail=True) self.raiseEventNoErrors( InstallationFinishedEvent, id=mapped_nffg.id, result=InstallationFinishedEvent.DEPLOY_ERROR) elif not deploy_status.still_pending: result = InstallationFinishedEvent.get_result_from_status( deploy_status) log.info("Overall installation result: %s" % result) is_fail = InstallationFinishedEvent.is_error(result) self._process_mapping_result(nffg_id=mapped_nffg.id, fail=is_fail) self.raiseEventNoErrors(InstallationFinishedEvent, id=mapped_nffg.id, result=result) elif deploy_status.standby: if self._dovapi: RequestScheduler().set_orchestration_standby()
def get_virtual_view(self, virtualizer_id, type=None, cls=None, **kwargs): """ 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` :param kwargs: optional parameters for Virtualizer :type kwargs: dict :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: if type in self.VIRTUALIZERS: virtualizer_class = self.VIRTUALIZERS[type] self._virtualizers[virtualizer_id] = virtualizer_class( self.dov, virtualizer_id, **kwargs) log.debug( "Generated Virtualizer with type: %s id: %s, params: %s" % (type, virtualizer_id, kwargs)) # Not supported format else: log.error("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, params: %s" % (cls.__name__, virtualizer_id, kwargs)) self._virtualizers[virtualizer_id] = cls( self.dov, virtualizer_id, **kwargs) # Generate a Single BiS-BiS Virtualizer by default else: # Virtualizer type is not defined: Use SingleBiSBiSVirtualizer by # default log.error( "Virtualizer type is missing for requested Virtualizer: %s!" % virtualizer_id) return None # Return Virtualizer return self._virtualizers[virtualizer_id]
def rest_api_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 """ # return self.controller_adapter.DoVManager.dov.get_resource_info() log.getChild('[DOV-API]').debug("Requesting Virtualizer for DoV-API") if self.dov_api_view is not None: # Check the topology is initialized if self.dov_api_view.revision is None: log.getChild('[DOV-API]').debug( "DoV has not initialized yet! " "Force to get default topology...") else: # Check if the resource is changed if self.api_mgr.topology_revision == self.dov_api_view.revision: # If resource has not been changed return False # This causes to response with the cached topology log.debug( "Global resource has not changed (revision: %s)! " % self.dov_api_view.revision) log.debug("Send topology from cache...") if self.api_mgr.last_response is None: log.error("Cached topology is missing!") return else: return self.api_mgr.last_response else: log.getChild('[DOV-API]').debug( "Response cache is outdated " "(new revision: %s)!" % self.dov_api_view.revision) res = self.dov_api_view.get_resource_info() self.api_mgr.topology_revision = self.dov_api_view.revision log.debug("Updated revision number: %s" % self.api_mgr.topology_revision) if CONFIG.get_rest_api_config(self._core_name)['unify_interface']: log.info("Convert internal NFFG to Virtualizer...") res = self.api_mgr.converter.dump_to_Virtualizer(nffg=res) log.debug("Cache acquired topology...") self.api_mgr.last_response = res return res else: log.error("Virtualizer(id=%s) assigned to DoV-API is not found!" % self._core_name)
def clean_domain(self, domain): """ Clean given domain. :param domain: domain name :type domain: str :return: None """ if domain in self.__tracked_domains: log.info( "Remove initiated VNFs and flowrules from the domain: %s" % domain) self.__dov.clean_domain_from_dov(domain=domain) else: log.error( "Detected domain: %s is not included in tracked domains: %s! Abort " "cleaning..." % (domain, self.__tracked_domains))
def initialize (self): """ .. seealso:: :func:`AbstractAPI.initialize() <escape.util.api.AbstractAPI.initialize>` """ log.debug("Initializing Controller Adaptation Sublayer...") self.controller_adapter = ControllerAdapter(self, with_infr=self._with_infr) if self._mapped_nffg: try: mapped_request = self._read_data_from_file(self._mapped_nffg) mapped_request = NFFG.parse(mapped_request) self.__proceed_installation(mapped_nffg=mapped_request) except (ValueError, IOError, TypeError) as e: log.error("Can't load service request from file because of: " + str(e)) else: log.debug("Graph representation is loaded successfully!") log.info("Controller Adaptation Sublayer has been initialized!")
def run(self): """ Start waiting for callbacks. :return: None """ self.bind_and_activate() try: log.debug("Start %s on %s:%s" % (self.__class__.__name__, self.server_address[0], self.server_address[1])) self.serve_forever() except KeyboardInterrupt: raise except Exception as e: log.error("Got exception in %s: %s" % (self.__class__.__name__, e)) finally: self.server_close()
def update_domain(self, domain, nffg): """ Update the detected domain in the global view with the given info. :param domain: domain name :type domain: str :param nffg: changed infrastructure info :type nffg: :any:`NFFG` :return: None """ if domain in self.__tracked_domains: log.info("Update domain: %s in DoV..." % domain) if self._remerge: log.debug("Using REMERGE strategy for DoV update...") self.__dov.remerge_domain_in_dov(domain=domain, nffg=nffg) else: log.debug("Using UPDATE strategy for DoV update...") self.__dov.update_domain_in_dov(domain=domain, nffg=nffg) else: log.error( "Detected domain: %s is not included in tracked domains: %s! Abort " "updating..." % (domain, self.__tracked_domains))
def __proceed_installation (self, mapped_nffg): """ Helper function to instantiate the NFFG mapping from different source. :param mapped_nffg: pre-mapped service request :type mapped_nffg: :any:`NFFG` :return: None """ log.getChild('API').info("Invoke install_nffg on %s with NF-FG: %s " % ( self.__class__.__name__, mapped_nffg)) log.info(int(round(time.time() * 1000))) try: install_result = self.controller_adapter.install_nffg(mapped_nffg) except Exception as e: log.error("Something went wrong during NFFG installation!") self.raiseEventNoErrors(InstallationFinishedEvent, result=False, error=e) raise log.getChild('API').debug("Invoked install_nffg on %s is finished" % self.__class__.__name__) log.info(int(round(time.time() * 1000))) self.raiseEventNoErrors(InstallationFinishedEvent, id=mapped_nffg.id, result=install_result)
def wrapper(*args, **kwargs): """ Wrapper function which call policy checking functions if they exist. """ if len(args) > 0: # Call Policy checking function before original if hooks[0]: log.debug("Invoke Policy checking function: [PRE] %s" % (hooks[0].__name__.split('pre_', 1)[1])) hooks[0](args, kwargs) # Call original function ret_value = orig_func(*args, **kwargs) # Call Policy checking function after original if hooks[1]: log.debug("Invoke Policy checking function: [POST] %s" % (hooks[1].__name__.split('post_', 1)[1])) hooks[1](args, kwargs, ret_value) return ret_value else: log.warning( "Something went wrong during binding Policy checker!") log.error("Abort policy enforcement checking!") raise PolicyEnforcementError( "Policy enforcement checking is aborted")
def load_component(self, component_name, params=None, parent=None): """ Load given component (DomainAdapter/DomainManager) from config. Initiate the given component class, pass the additional attributes, register the event listeners and return with the newly created object. :param component_name: component's config name :type component_name: str :param params: component parameters :type params: dict :param parent: define the parent of the actual component's configuration :type parent: dict :return: initiated component :rtype: :any:`AbstractESCAPEAdapter` or :any:`AbstractDomainManager` """ try: # Get component class component_class = CONFIG.get_component(component=component_name, parent=parent) # If it's found if component_class is not None: # Get optional parameters of this component if not params: params = CONFIG.get_component_params( component=component_name, parent=parent) # Initialize component component = component_class(**params) # Set up listeners for e.g. DomainChangedEvents component.addListeners(self._ca) # Set up listeners for DeployNFFGEvent component.addListeners(self._ca._layer_API) # Return the newly created object return component else: log.error( "Configuration of '%s' is missing. Skip initialization!" % component_name) raise ConfigurationError("Missing component configuration!") except AttributeError: log.error("%s is not found. Skip component initialization!" % component_name) raise except ImportError: log.error( "Could not import module: %s. Skip component initialization!" % component_name) raise
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 rest_api_edit_config(self, id, data, params=None): """ Implement edit-config call for CAS layer. Receive edit-config request from external component and directly forward data for deployment. :param params: request params :type params: dict :return: None """ log.getChild('[DOV-API]').info( "Invoke instantiation on %s with NF-FG: " "%s " % (self.__class__.__name__, id)) if CONFIG.get_rest_api_config(self._core_name)['unify_interface']: log.debug("Virtualizer format enabled! Start conversion step...") if CONFIG.get_rest_api_config(self._core_name)['diff']: log.debug("Diff format enabled! Start patching step...") if self.api_mgr.last_response is None: log.info( "Missing cached Virtualizer! Acquiring topology now..." ) self.rest_api_get_config() stats.add_measurement_start_entry(type=stats.TYPE_PROCESSING, info="RECREATE-FULL-REQUEST") log.info("Patching cached topology with received diff...") full_req = self.api_mgr.last_response.yang_copy() full_req.patch(source=data) stats.add_measurement_end_entry(type=stats.TYPE_PROCESSING, info="RECREATE-FULL-REQUEST") else: full_req = data log.info("Converting full request data...") stats.add_measurement_start_entry(type=stats.TYPE_CONVERSION, info="VIRTUALIZER-->NFFG") nffg = self.api_mgr.converter.parse_from_Virtualizer( vdata=full_req) stats.add_measurement_end_entry(type=stats.TYPE_CONVERSION, info="VIRTUALIZER-->NFFG") else: nffg = data log.debug("Set NFFG id: %s" % id) if nffg.service_id is None: nffg.service_id = nffg.id nffg.id = id if params: nffg.add_metadata(name="params", value=params) log.info("Proceeding request: %s to instantiation..." % id) if CONFIG.get_vnfm_enabled(): deploy_status = self.controller_adapter.status_mgr.get_last_status( ) if deploy_status is None: log.warning( "Received direct DoV rewrite request from external " "component without any preliminary deploy request!") else: if deploy_status.id != nffg.id: log.error( "Received direct deploy request id: %s is different from " "service request under deploy: %s" % (nffg.id, deploy_status.id)) return else: self.controller_adapter.cancel_vnfm_timer() log.getChild('API').debug("Store received DoV request...") msg_id = self.api_mgr.request_cache.cache_request_by_nffg(nffg=nffg) if msg_id is not None: self.api_mgr.request_cache.set_in_progress(id=msg_id) log.getChild('API').debug("Request is stored with id: %s" % msg_id) else: log.getChild('API').warning("No request info detected.") self.__proceed_installation(mapped_nffg=nffg, direct_deploy=True)
def install_nffg(self, mapped_nffg): """ Start NF-FG installation. Process given :any:`NFFG`, slice information self.__global_nffg on domains and invoke DomainManagers to install domain specific parts. :param mapped_nffg: mapped NF-FG instance which need to be installed :type mapped_nffg: NFFG :return: mapping result :rtype: bool """ log.debug("Invoke %s to install NF-FG(%s)" % (self.__class__.__name__, mapped_nffg.name)) # # Notify remote visualizer about the deployable NFFG if it's needed # notify_remote_visualizer(data=mapped_nffg, id=LAYER_NAME) slices = NFFGToolBox.split_into_domains(nffg=mapped_nffg, log=log) if slices is None: log.warning("Given mapped NFFG: %s can not be sliced! " "Skip domain notification steps" % mapped_nffg) return log.debug("Notify initiated domains: %s" % [d for d in self.domains.initiated]) # TODO - abstract/inter-domain tag rewrite # NFFGToolBox.rewrite_interdomain_tags(slices) mapping_result = True for domain, part in slices: log.debug( "Recreate missing TAG matching fields in domain part: %s..." % domain) # Temporarily rewrite/recreate TAGs here NFFGToolBox.recreate_match_TAGs(nffg=part, log=log) # Get Domain Manager domain_mgr = self.domains.get_component_by_domain( domain_name=domain) if domain_mgr is None: log.warning( "No DomainManager has been initialized for domain: %s! " "Skip install domain part..." % domain) continue log.log(VERBOSE, "Splitted domain: %s part:\n%s" % (domain, part.dump())) log.info("Delegate splitted part: %s to %s" % (part, domain_mgr)) # Rebind requirement link fragments as e2e reqs part = NFFGToolBox.rebind_e2e_req_links(nffg=part, log=log) # Check if need to reset domain before install if CONFIG.reset_domains_before_install(): log.debug("Reset %s domain before deploying mapped NFFG..." % domain_mgr.domain_name) domain_mgr.clear_domain() # Invoke DomainAdapter's install res = domain_mgr.install_nffg(part) # Update the DoV based on the mapping result covering some corner case if not res: log.error("Installation of %s in %s was unsuccessful!" % (part, domain)) # Note result according to others before mapping_result = mapping_result and res # If installation of the domain was performed without error if not res: log.warning("Skip DoV update with domain: %s! Cause: " "Domain installation was unsuccessful!" % domain) continue # If the domain manager does not poll the domain update here # else polling takes care of domain updating if isinstance(domain_mgr, AbstractRemoteDomainManager) and domain_mgr._poll: log.info("Skip explicit DoV update for domain: %s. " "Cause: polling enabled!" % domain) continue # If the internalDM is the only initiated mgr, we can override the # whole DoV if domain_mgr.IS_LOCAL_MANAGER: if mapped_nffg.is_SBB(): # If the request was a cleanup request, we can simply clean the DOV if mapped_nffg.is_bare(): log.debug( "Detected cleanup topology (no NF/Flowrule/SG_hop)! Clean DoV..." ) self.DoVManager.clean_domain(domain=domain) # If the reset contains some VNF, cannot clean or override else: log.warning( "Detected SingleBiSBiS topology! Local domain has been already " "cleared, skip DoV update...") # If the the topology was a GLOBAL view, just override the whole DoV elif not mapped_nffg.is_virtualized(): self.DoVManager.set_global_view(nffg=mapped_nffg) else: log.warning( "Detected virtualized Infrastructure node in mapped NFFG! Skip " "DoV update...") # In case of Local manager skip the rest of the update continue # Explicit domain update self.DoVManager.update_domain(domain=domain, nffg=part) log.debug("NF-FG installation is finished by %s" % self.__class__.__name__) # Post-mapping steps if mapping_result: log.info( "All installation process has been finished with success! ") # Notify remote visualizer about the installation result if it's needed notify_remote_visualizer( data=self.DoVManager.dov.get_resource_info(), id=LAYER_NAME) else: log.error("%s installation was not successful!" % mapped_nffg) return mapping_result