+ def new_classification(self, name, ctype, definition):
+ self.logger.debug(
+ "Adding a new (Traffic) Classification to VIM, named %s", name
+ )
+
+ try:
+ new_class = None
+ self._reload_connection()
+
+ if ctype not in supportedClassificationTypes:
+ raise vimconn.VimConnNotSupportedException(
+ "OpenStack VIM connector does not support provided "
+ "Classification Type {}, supported ones are: {}".format(
+ ctype, supportedClassificationTypes
+ )
+ )
+
+ if not self._validate_classification(ctype, definition):
+ raise vimconn.VimConnException(
+ "Incorrect Classification definition for the type specified."
+ )
+
+ classification_dict = definition
+ classification_dict["name"] = name
+ new_class = self.neutron.create_sfc_flow_classifier(
+ {"flow_classifier": classification_dict}
+ )
+
+ return new_class["flow_classifier"]["id"]
+ except (
+ neExceptions.ConnectionFailed,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ self.logger.error("Creation of Classification failed.")
+ self._format_exception(e)
+
+ def get_classification(self, class_id):
+ self.logger.debug(" Getting Classification %s from VIM", class_id)
+ filter_dict = {"id": class_id}
+ class_list = self.get_classification_list(filter_dict)
+
+ if len(class_list) == 0:
+ raise vimconn.VimConnNotFoundException(
+ "Classification '{}' not found".format(class_id)
+ )
+ elif len(class_list) > 1:
+ raise vimconn.VimConnConflictException(
+ "Found more than one Classification with this criteria"
+ )
+
+ classification = class_list[0]
+
+ return classification
+
+ def get_classification_list(self, filter_dict={}):
+ self.logger.debug(
+ "Getting Classifications from VIM filter: '%s'", str(filter_dict)
+ )
+
+ try:
+ filter_dict_os = filter_dict.copy()
+ self._reload_connection()
+
+ if self.api_version3 and "tenant_id" in filter_dict_os:
+ filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
+
+ classification_dict = self.neutron.list_sfc_flow_classifiers(
+ **filter_dict_os
+ )
+ classification_list = classification_dict["flow_classifiers"]
+ self.__classification_os2mano(classification_list)
+
+ return classification_list
+ except (
+ neExceptions.ConnectionFailed,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ self._format_exception(e)
+
+ def delete_classification(self, class_id):
+ self.logger.debug("Deleting Classification '%s' from VIM", class_id)
+
+ try:
+ self._reload_connection()
+ self.neutron.delete_sfc_flow_classifier(class_id)
+
+ return class_id
+ except (
+ neExceptions.ConnectionFailed,
+ neExceptions.NeutronException,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ self._format_exception(e)
+
+ def new_sfi(self, name, ingress_ports, egress_ports, sfc_encap=True):
+ self.logger.debug(
+ "Adding a new Service Function Instance to VIM, named '%s'", name
+ )
+
+ try:
+ new_sfi = None
+ self._reload_connection()
+ correlation = None
+
+ if sfc_encap:
+ correlation = "nsh"
+
+ if len(ingress_ports) != 1:
+ raise vimconn.VimConnNotSupportedException(
+ "OpenStack VIM connector can only have 1 ingress port per SFI"
+ )
+
+ if len(egress_ports) != 1:
+ raise vimconn.VimConnNotSupportedException(
+ "OpenStack VIM connector can only have 1 egress port per SFI"
+ )
+
+ sfi_dict = {
+ "name": name,
+ "ingress": ingress_ports[0],
+ "egress": egress_ports[0],
+ "service_function_parameters": {"correlation": correlation},
+ }
+ new_sfi = self.neutron.create_sfc_port_pair({"port_pair": sfi_dict})
+
+ return new_sfi["port_pair"]["id"]
+ except (
+ neExceptions.ConnectionFailed,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ if new_sfi:
+ try:
+ self.neutron.delete_sfc_port_pair(new_sfi["port_pair"]["id"])
+ except Exception:
+ self.logger.error(
+ "Creation of Service Function Instance failed, with "
+ "subsequent deletion failure as well."
+ )
+
+ self._format_exception(e)
+
+ def get_sfi(self, sfi_id):
+ self.logger.debug("Getting Service Function Instance %s from VIM", sfi_id)
+ filter_dict = {"id": sfi_id}
+ sfi_list = self.get_sfi_list(filter_dict)
+
+ if len(sfi_list) == 0:
+ raise vimconn.VimConnNotFoundException(
+ "Service Function Instance '{}' not found".format(sfi_id)
+ )
+ elif len(sfi_list) > 1:
+ raise vimconn.VimConnConflictException(
+ "Found more than one Service Function Instance with this criteria"
+ )
+
+ sfi = sfi_list[0]
+
+ return sfi
+
+ def get_sfi_list(self, filter_dict={}):
+ self.logger.debug(
+ "Getting Service Function Instances from VIM filter: '%s'", str(filter_dict)
+ )
+
+ try:
+ self._reload_connection()
+ filter_dict_os = filter_dict.copy()
+
+ if self.api_version3 and "tenant_id" in filter_dict_os:
+ filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
+
+ sfi_dict = self.neutron.list_sfc_port_pairs(**filter_dict_os)
+ sfi_list = sfi_dict["port_pairs"]
+ self.__sfi_os2mano(sfi_list)
+
+ return sfi_list
+ except (
+ neExceptions.ConnectionFailed,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ self._format_exception(e)
+
+ def delete_sfi(self, sfi_id):
+ self.logger.debug("Deleting Service Function Instance '%s' from VIM", sfi_id)
+
+ try:
+ self._reload_connection()
+ self.neutron.delete_sfc_port_pair(sfi_id)
+
+ return sfi_id
+ except (
+ neExceptions.ConnectionFailed,
+ neExceptions.NeutronException,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ self._format_exception(e)
+
+ def new_sf(self, name, sfis, sfc_encap=True):
+ self.logger.debug("Adding a new Service Function to VIM, named '%s'", name)
+
+ try:
+ new_sf = None
+ self._reload_connection()
+ # correlation = None
+ # if sfc_encap:
+ # correlation = "nsh"
+
+ for instance in sfis:
+ sfi = self.get_sfi(instance)
+
+ if sfi.get("sfc_encap") != sfc_encap:
+ raise vimconn.VimConnNotSupportedException(
+ "OpenStack VIM connector requires all SFIs of the "
+ "same SF to share the same SFC Encapsulation"
+ )
+
+ sf_dict = {"name": name, "port_pairs": sfis}
+ new_sf = self.neutron.create_sfc_port_pair_group(
+ {"port_pair_group": sf_dict}
+ )
+
+ return new_sf["port_pair_group"]["id"]
+ except (
+ neExceptions.ConnectionFailed,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ if new_sf:
+ try:
+ self.neutron.delete_sfc_port_pair_group(
+ new_sf["port_pair_group"]["id"]
+ )
+ except Exception:
+ self.logger.error(
+ "Creation of Service Function failed, with "
+ "subsequent deletion failure as well."
+ )
+
+ self._format_exception(e)
+
+ def get_sf(self, sf_id):
+ self.logger.debug("Getting Service Function %s from VIM", sf_id)
+ filter_dict = {"id": sf_id}
+ sf_list = self.get_sf_list(filter_dict)
+
+ if len(sf_list) == 0:
+ raise vimconn.VimConnNotFoundException(
+ "Service Function '{}' not found".format(sf_id)
+ )
+ elif len(sf_list) > 1:
+ raise vimconn.VimConnConflictException(
+ "Found more than one Service Function with this criteria"
+ )
+
+ sf = sf_list[0]
+
+ return sf
+
+ def get_sf_list(self, filter_dict={}):
+ self.logger.debug(
+ "Getting Service Function from VIM filter: '%s'", str(filter_dict)
+ )
+
+ try:
+ self._reload_connection()
+ filter_dict_os = filter_dict.copy()
+
+ if self.api_version3 and "tenant_id" in filter_dict_os:
+ filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
+
+ sf_dict = self.neutron.list_sfc_port_pair_groups(**filter_dict_os)
+ sf_list = sf_dict["port_pair_groups"]
+ self.__sf_os2mano(sf_list)
+
+ return sf_list
+ except (
+ neExceptions.ConnectionFailed,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ self._format_exception(e)
+
+ def delete_sf(self, sf_id):
+ self.logger.debug("Deleting Service Function '%s' from VIM", sf_id)
+
+ try:
+ self._reload_connection()
+ self.neutron.delete_sfc_port_pair_group(sf_id)
+
+ return sf_id
+ except (
+ neExceptions.ConnectionFailed,
+ neExceptions.NeutronException,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ self._format_exception(e)
+
+ def new_sfp(self, name, classifications, sfs, sfc_encap=True, spi=None):
+ self.logger.debug("Adding a new Service Function Path to VIM, named '%s'", name)
+
+ try:
+ new_sfp = None
+ self._reload_connection()
+ # In networking-sfc the MPLS encapsulation is legacy
+ # should be used when no full SFC Encapsulation is intended
+ correlation = "mpls"
+
+ if sfc_encap:
+ correlation = "nsh"
+
+ sfp_dict = {
+ "name": name,
+ "flow_classifiers": classifications,
+ "port_pair_groups": sfs,
+ "chain_parameters": {"correlation": correlation},
+ }
+
+ if spi:
+ sfp_dict["chain_id"] = spi
+
+ new_sfp = self.neutron.create_sfc_port_chain({"port_chain": sfp_dict})
+
+ return new_sfp["port_chain"]["id"]
+ except (
+ neExceptions.ConnectionFailed,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ if new_sfp:
+ try:
+ self.neutron.delete_sfc_port_chain(new_sfp["port_chain"]["id"])
+ except Exception:
+ self.logger.error(
+ "Creation of Service Function Path failed, with "
+ "subsequent deletion failure as well."
+ )
+
+ self._format_exception(e)
+
+ def get_sfp(self, sfp_id):
+ self.logger.debug(" Getting Service Function Path %s from VIM", sfp_id)
+
+ filter_dict = {"id": sfp_id}
+ sfp_list = self.get_sfp_list(filter_dict)
+
+ if len(sfp_list) == 0:
+ raise vimconn.VimConnNotFoundException(
+ "Service Function Path '{}' not found".format(sfp_id)
+ )
+ elif len(sfp_list) > 1:
+ raise vimconn.VimConnConflictException(
+ "Found more than one Service Function Path with this criteria"
+ )
+
+ sfp = sfp_list[0]
+
+ return sfp
+
+ def get_sfp_list(self, filter_dict={}):
+ self.logger.debug(
+ "Getting Service Function Paths from VIM filter: '%s'", str(filter_dict)
+ )
+
+ try:
+ self._reload_connection()
+ filter_dict_os = filter_dict.copy()
+
+ if self.api_version3 and "tenant_id" in filter_dict_os:
+ filter_dict_os["project_id"] = filter_dict_os.pop("tenant_id")
+
+ sfp_dict = self.neutron.list_sfc_port_chains(**filter_dict_os)
+ sfp_list = sfp_dict["port_chains"]
+ self.__sfp_os2mano(sfp_list)
+
+ return sfp_list
+ except (
+ neExceptions.ConnectionFailed,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ self._format_exception(e)
+
+ def delete_sfp(self, sfp_id):
+ self.logger.debug("Deleting Service Function Path '%s' from VIM", sfp_id)
+
+ try:
+ self._reload_connection()
+ self.neutron.delete_sfc_port_chain(sfp_id)
+
+ return sfp_id
+ except (
+ neExceptions.ConnectionFailed,
+ neExceptions.NeutronException,
+ ksExceptions.ClientException,
+ neExceptions.NeutronException,
+ ConnectionError,
+ ) as e:
+ self._format_exception(e)
+
+ def refresh_sfps_status(self, sfp_list):
+ """Get the status of the service function path
+ Params: the list of sfp identifiers
+ Returns a dictionary with:
+ vm_id: #VIM id of this service function path
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE,
+ # CREATING (on building process)
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)F
+ """
+ sfp_dict = {}
+ self.logger.debug(
+ "refresh_sfps status: Getting tenant SFP information from VIM"
+ )
+
+ for sfp_id in sfp_list:
+ sfp = {}
+
+ try:
+ sfp_vim = self.get_sfp(sfp_id)
+
+ if sfp_vim["spi"]:
+ sfp["status"] = vmStatus2manoFormat["ACTIVE"]
+ else:
+ sfp["status"] = "OTHER"
+ sfp["error_msg"] = "VIM status reported " + sfp["status"]
+
+ sfp["vim_info"] = self.serialize(sfp_vim)
+
+ if sfp_vim.get("fault"):
+ sfp["error_msg"] = str(sfp_vim["fault"])
+ except vimconn.VimConnNotFoundException as e:
+ self.logger.error("Exception getting sfp status: %s", str(e))
+ sfp["status"] = "DELETED"
+ sfp["error_msg"] = str(e)
+ except vimconn.VimConnException as e:
+ self.logger.error("Exception getting sfp status: %s", str(e))
+ sfp["status"] = "VIM_ERROR"
+ sfp["error_msg"] = str(e)
+
+ sfp_dict[sfp_id] = sfp
+
+ return sfp_dict
+
+ def refresh_sfis_status(self, sfi_list):
+ """Get the status of the service function instances
+ Params: the list of sfi identifiers
+ Returns a dictionary with:
+ vm_id: #VIM id of this service function instance
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE,
+ # CREATING (on building process)
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ """
+ sfi_dict = {}
+ self.logger.debug(
+ "refresh_sfis status: Getting tenant sfi information from VIM"
+ )
+
+ for sfi_id in sfi_list:
+ sfi = {}
+
+ try:
+ sfi_vim = self.get_sfi(sfi_id)
+
+ if sfi_vim:
+ sfi["status"] = vmStatus2manoFormat["ACTIVE"]
+ else:
+ sfi["status"] = "OTHER"
+ sfi["error_msg"] = "VIM status reported " + sfi["status"]
+
+ sfi["vim_info"] = self.serialize(sfi_vim)
+
+ if sfi_vim.get("fault"):
+ sfi["error_msg"] = str(sfi_vim["fault"])
+ except vimconn.VimConnNotFoundException as e:
+ self.logger.error("Exception getting sfi status: %s", str(e))
+ sfi["status"] = "DELETED"
+ sfi["error_msg"] = str(e)
+ except vimconn.VimConnException as e:
+ self.logger.error("Exception getting sfi status: %s", str(e))
+ sfi["status"] = "VIM_ERROR"
+ sfi["error_msg"] = str(e)
+
+ sfi_dict[sfi_id] = sfi
+
+ return sfi_dict
+
+ def refresh_sfs_status(self, sf_list):
+ """Get the status of the service functions
+ Params: the list of sf identifiers
+ Returns a dictionary with:
+ vm_id: #VIM id of this service function
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE,
+ # CREATING (on building process)
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ """
+ sf_dict = {}
+ self.logger.debug("refresh_sfs status: Getting tenant sf information from VIM")
+
+ for sf_id in sf_list:
+ sf = {}
+
+ try:
+ sf_vim = self.get_sf(sf_id)
+
+ if sf_vim:
+ sf["status"] = vmStatus2manoFormat["ACTIVE"]
+ else:
+ sf["status"] = "OTHER"
+ sf["error_msg"] = "VIM status reported " + sf_vim["status"]
+
+ sf["vim_info"] = self.serialize(sf_vim)
+
+ if sf_vim.get("fault"):
+ sf["error_msg"] = str(sf_vim["fault"])
+ except vimconn.VimConnNotFoundException as e:
+ self.logger.error("Exception getting sf status: %s", str(e))
+ sf["status"] = "DELETED"
+ sf["error_msg"] = str(e)
+ except vimconn.VimConnException as e:
+ self.logger.error("Exception getting sf status: %s", str(e))
+ sf["status"] = "VIM_ERROR"
+ sf["error_msg"] = str(e)
+
+ sf_dict[sf_id] = sf
+
+ return sf_dict
+
+ def refresh_classifications_status(self, classification_list):
+ """Get the status of the classifications
+ Params: the list of classification identifiers
+ Returns a dictionary with:
+ vm_id: #VIM id of this classifier
+ status: #Mandatory. Text with one of:
+ # DELETED (not found at vim)
+ # VIM_ERROR (Cannot connect to VIM, VIM response error, ...)
+ # OTHER (Vim reported other status not understood)
+ # ERROR (VIM indicates an ERROR status)
+ # ACTIVE,
+ # CREATING (on building process)
+ error_msg: #Text with VIM error message, if any. Or the VIM connection ERROR
+ vim_info: #Text with plain information obtained from vim (yaml.safe_dump)
+ """
+ classification_dict = {}
+ self.logger.debug(
+ "refresh_classifications status: Getting tenant classification information from VIM"
+ )
+
+ for classification_id in classification_list:
+ classification = {}
+
+ try:
+ classification_vim = self.get_classification(classification_id)
+
+ if classification_vim:
+ classification["status"] = vmStatus2manoFormat["ACTIVE"]
+ else:
+ classification["status"] = "OTHER"
+ classification["error_msg"] = (
+ "VIM status reported " + classification["status"]
+ )
+
+ classification["vim_info"] = self.serialize(classification_vim)
+
+ if classification_vim.get("fault"):
+ classification["error_msg"] = str(classification_vim["fault"])
+ except vimconn.VimConnNotFoundException as e:
+ self.logger.error("Exception getting classification status: %s", str(e))
+ classification["status"] = "DELETED"
+ classification["error_msg"] = str(e)
+ except vimconn.VimConnException as e:
+ self.logger.error("Exception getting classification status: %s", str(e))
+ classification["status"] = "VIM_ERROR"
+ classification["error_msg"] = str(e)
+
+ classification_dict[classification_id] = classification
+
+ return classification_dict
+