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 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 clean_domain_from_dov(self, domain): """ Clean domain by removing initiated NFs and flowrules related to BiSBiS nodes of the given domain :param domain: domain name :type domain: str :return: updated Dov :rtype: :class:`NFFG` """ if self.__global_nffg.is_empty(): log.debug("DoV is empty! Skip cleanup domain: %s" % domain) return self.__global_nffg if self.__global_nffg.is_bare(): log.debug("No initiated service has been detected in DoV! " "Skip cleanup domain: %s" % domain) return self.__global_nffg NFFGToolBox.clear_domain(base=self.__global_nffg, domain=domain, log=log) log.debug("DoV stat:\n%s" % self.__global_nffg.get_stat()) log.log(VERBOSE, "Cleaned Dov:\n%s" % self.__global_nffg.dump()) self.raiseEventNoErrors(DoVChangedEvent, cause=DoVChangedEvent.TYPE.CHANGE) return self.__global_nffg
def send_quietly(self, method, url=None, body=None, timeout=None, **kwargs): """ Send REST request with handling exceptions and logging only in VERBOSE mode. :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 :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 return self.send_request(method, url, body, **kwargs) except Timeout: log.log( VERBOSE, "Remote agent(adapter: %s, url: %s) reached timeout limit!" % (self.name, self._base_url)) return None except ConnectionError: log.log( VERBOSE, "Remote agent(adapter: %s, url: %s) is not reachable!" % (self.name, self._base_url)) return None except HTTPError as e: log.log( VERBOSE, "Remote agent(adapter: %s, url: %s) responded with an error: %s" % (self.name, self._base_url, e.message)) return None except RequestException as e: log.log(VERBOSE, "Got unexpected exception: %s" % e) return None except KeyboardInterrupt: log.log( VERBOSE, "Request to remote agent(adapter: %s, url: %s) is " "interrupted by user!" % (self.name, self._base_url)) return None
def remove_deployed_elements(self): """ Remove all the NFs, flowrules and dynamic ports from DoV. :return: updated Dov :rtype: :class:`NFFG` """ if self.__global_nffg.is_empty(): log.debug("DoV is empty! Skip DoV cleanup") return self.__global_nffg NFFGToolBox.remove_deployed_services(nffg=self.__global_nffg, log=log) log.debug("DoV stat:\n%s" % self.__global_nffg.get_stat()) log.log(VERBOSE, "Cleared Dov:\n%s" % self.__global_nffg.dump()) self.raiseEventNoErrors(DoVChangedEvent, cause=DoVChangedEvent.TYPE.CHANGE) return self.__global_nffg
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 merge_new_domain_into_dov(self, nffg): """ Add a newly detected domain to DoV. Based on the feature: escape.util.nffg.NFFGToolBox#merge_domains :param nffg: NFFG object need to be merged into DoV :type nffg: :any:`NFFG` :return: updated Dov :rtype: :any:`NFFG` """ # Using general merging function from NFFGToolBox and return the updated # NFFG NFFGToolBox.merge_new_domain(base=self.__global_nffg, nffg=nffg, log=log) # Raise event for observing Virtualizers about topology change log.log(VERBOSE, "Merged Dov:\n%s" % self.__global_nffg.dump()) self.raiseEventNoErrors(DoVChangedEvent, cause=DoVChangedEvent.TYPE.EXTEND) 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 update_domain_status_in_dov(self, domain, nffg): """ Set status of initiated NFs and flowrules related to BiSBiS nodes of the given domain. :param domain: domain name :type domain: str :param nffg: changed infrastructure info :type nffg: :class:`NFFG` :return: updated Dov :rtype: :class:`NFFG` """ if self.__global_nffg.is_empty(): log.debug("DoV is empty! Skip cleanup domain: %s" % domain) return self.__global_nffg NFFGToolBox.update_status_info(nffg=nffg, status=NFFG.STATUS_DEPLOY) NFFGToolBox.update_nffg_by_status(base=self.__global_nffg, updated=nffg, log=log) log.log(VERBOSE, "Updated Dov:\n%s" % self.__global_nffg.dump()) self.raiseEventNoErrors(DoVChangedEvent, cause=DoVChangedEvent.TYPE.CHANGE) return self.__global_nffg
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 install_flowrule(self, id, match, action): """ Install a flowrule in an OpenFlow switch. :param id: ID of the infra element stored in the NFFG :type id: str :param match: match part of the rule (keys: in_port, vlan_id) :type match: dict :param action: action part of the rule (keys: out, vlan_push, vlan_pop) :type action: dict :return: None """ conn = self.openflow.getConnection(dpid=self.infra_to_dpid[id]) if not conn: log.warning( "Missing connection for node element: %s! Skip flowrule " "installation..." % id) return msg = of.ofp_flow_mod() msg.match.in_port = match['in_port'] if 'vlan_id' in match: try: vlan_id = int(match['vlan_id']) except ValueError: log.warning( "VLAN_ID: %s in match field is not a valid number! " "Skip flowrule installation..." % match['vlan_id']) return msg.match.dl_vlan = vlan_id # Append explicit matching parameters to OF flowrule if 'flowclass' in match: for ovs_match_entry in match['flowclass'].split(','): kv = ovs_match_entry.split('=') # kv = [field, value] ~ ['dl_src', '00:0A:E4:25:6B:B6'] # msg.match.dl_src = "00:00:00:00:00:01" if kv[0] in ('dl_src', 'dl_dst'): setattr(msg.match, kv[0], EthAddr(kv[1])) elif kv[0] in ('in_port', 'dl_vlan', 'dl_type', 'ip_proto', 'nw_proto', 'nw_tos', 'nw_ttl', 'tp_src', 'tp_dst'): setattr(msg.match, kv[0], int(kv[1], 0)) elif kv[0] in ('nw_src', 'nw_dst'): setattr(msg.match, kv[0], IPAddr(kv[1])) else: setattr(msg.match, kv[0], kv[1]) if 'vlan_push' in action: try: vlan_push = int(action['vlan_push']) except ValueError: log.warning( "VLAN_PUSH: %s in action field is not a valid number! " "Skip flowrule installation..." % action['vlan_push']) return msg.actions.append(of.ofp_action_vlan_vid(vlan_vid=vlan_push)) # msg.actions.append(of.ofp_action_vlan_vid()) out = action['out'] # If out is in the detected saps --> always remove the VLAN tags if 'vlan_pop' in action: msg.actions.append(of.ofp_action_strip_vlan()) else: try: # If next node is a SAP we need to setup the MAC addresses and # strip VLAN from the frame explicitly if out in self.saps[id]: msg.actions.append(of.ofp_action_strip_vlan()) except KeyError: pass try: if out in self.saps[id]: dl_dst = self.saps[id][str(out)]['dl_dst'] dl_src = self.saps[id][str(out)]['dl_src'] msg.actions.append( of.ofp_action_dl_addr.set_dst(EthAddr(dl_dst))) msg.actions.append( of.ofp_action_dl_addr.set_src(EthAddr(dl_src))) except KeyError: pass try: out_port = int(action['out']) except ValueError: log.warning( "Output port: %s is not a valid port in flowrule action: %s! " "Skip flowrule installation..." % (action['out'], action)) return msg.actions.append(of.ofp_action_output(port=out_port)) log.debug("Install flow entry into INFRA: %s on connection: %s ..." % (id, conn)) conn.send(msg) log.log(VERBOSE, "Sent raw OpenFlow flowrule:\n%s" % msg)
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
def poll(self): """ Poll the defined domain agent. Handle different connection errors and go to slow/rapid poll. When an agent is (re)detected update the current resource information. :return: None """ # If domain is not detected if not self._detected: # Check the topology is reachable if self._detect_topology(): # Domain is detected and topology is updated -> restart domain polling self.restart_polling() # 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) return # If domain has already detected else: # Check the domain is still reachable changed = self.topoAdapter.check_topology_changed() # No changes if changed is False: # Nothing to do log.log( VERBOSE, "Remote domain: %s has not changed!" % self.domain_name) return # Domain has changed elif isinstance(changed, NFFG): log.info( "Remote domain: %s has changed. Update global domain view..." % self.domain_name) log.debug("Save changed topology: %s" % changed) # Update the received new topo self.internal_topo = changed # 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_CHANGED) return # If changed is None something went wrong, probably remote domain is not # reachable. Step to the other half of the function elif changed is None: log.warning( "Lost connection with %s agent! Going to slow poll..." % self.domain_name) # Clear internal topology log.debug("Clear topology from domain: %s" % self.domain_name) self.internal_topo = None self.raiseEventNoErrors( DomainChangedEvent, domain=self.domain_name, cause=DomainChangedEvent.TYPE.DOMAIN_DOWN) else: log.warning( "Got unexpected return value from check_topology_changed(): %s" % type(changed)) return # If this is the first call of poll() if self._detected is None: log.warning( "Local agent in domain: %s is not detected! Keep trying..." % self.domain_name) self._detected = False elif self._detected: # Detected before -> lost connection = big Problem self._detected = False self.restart_polling() else: # No success but not for the first try -> keep trying silently pass