From c772a1dbc7ace58d099570d41a889adf851c8ba8 Mon Sep 17 00:00:00 2001 From: Ulas Kozat Date: Mon, 28 Dec 2015 16:05:13 -0800 Subject: Added networking-sfc from openstack project with merge date Dec 23 2015 Added patch 13 for subject "add missing db migration files" Change-Id: Id51a160335a14870c1dd816a44baf9b1958b9ac6 --- networking_sfc/extensions/__init__.py | 0 networking_sfc/extensions/flowclassifier.py | 304 ++++++++++++++++++++++ networking_sfc/extensions/sfc.py | 382 ++++++++++++++++++++++++++++ 3 files changed, 686 insertions(+) create mode 100644 networking_sfc/extensions/__init__.py create mode 100644 networking_sfc/extensions/flowclassifier.py create mode 100644 networking_sfc/extensions/sfc.py (limited to 'networking_sfc/extensions') diff --git a/networking_sfc/extensions/__init__.py b/networking_sfc/extensions/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/networking_sfc/extensions/flowclassifier.py b/networking_sfc/extensions/flowclassifier.py new file mode 100644 index 0000000..93d2284 --- /dev/null +++ b/networking_sfc/extensions/flowclassifier.py @@ -0,0 +1,304 @@ +# Copyright 2015 Futurewei. All rights reserved. +# +# 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. + +from abc import ABCMeta +from abc import abstractmethod + +import six + +from oslo_config import cfg + +from neutron.api import extensions as neutron_ext +from neutron.api.v2 import attributes as attr +from neutron.api.v2 import resource_helper +from neutron.common import constants as const +from neutron.common import exceptions as neutron_exc +from neutron.services import service_base + +import networking_sfc + + +cfg.CONF.import_opt('api_extensions_path', 'neutron.common.config') +neutron_ext.append_api_extensions_path(networking_sfc.extensions.__path__) +FLOW_CLASSIFIER_EXT = "flow_classifier" +FLOW_CLASSIFIER_PREFIX = "/sfc" + +fc_supported_protocols = [const.PROTO_NAME_TCP, + const.PROTO_NAME_UDP, const.PROTO_NAME_ICMP] +fc_supported_ethertypes = ['IPv4', 'IPv6'] +SUPPORTED_L7_PARAMETERS = [] +DEFAULT_L7_PARAMETER = {} + + +# Flow Classifier Exceptions +class FlowClassifierNotFound(neutron_exc.NotFound): + message = _("Flow Classifier %(id)s not found.") + + +class FlowClassifierPortNotFound(neutron_exc.NotFound): + message = _("Flow Classifier Neutron Port %(id)s not found.") + + +class FlowClassifierInvalidPortRange(neutron_exc.InvalidInput): + message = _("Invalid IP protocol port range. min_port_range=" + "%(port_range_min)s must be lesser or equal to " + "max_port_range=%(port_range_max)s.") + + +class FlowClassifierInvalidPortValue(neutron_exc.InvalidInput): + message = _("Flow Classifier has invalid port value %(port)s") + + +class FlowClassiferDuplicateInformation(neutron_exc.InvalidInput): + message = _("Flow Classfier has duplicate information: " + "Neutron Port id %(port_id)s and ip prefix %(ip_prefix)s") + + +class FlowClassifierInUse(neutron_exc.InUse): + message = _("Flow Classifier %(id)s in use.") + + +class FlowClassifierInvalidProtocol(neutron_exc.InvalidInput): + message = _("Flow Classifier does not support protocol %(protocol)s. " + "Supported protocol values are %(values)s.") + + +class FlowClassifierInvalidEthertype(neutron_exc.InvalidInput): + message = _("Flow Classifier does not support ethertype %(ethertype)s. " + "Supported ethertype values are %(values)s.") + + +class FlowClassifierProtocolRequiredWithPorts(neutron_exc.InvalidInput): + message = _("IP protocol must be TCP or UDP, if port range is given.") + + +class FlowClassifierInvalidL7Parameter(neutron_exc.InvalidInput): + message = _( + "Flow classifier does not support L7 parameter " + "(%%(key)s, %%(value)s). Supported L7 parameters are " + "%(supported_parameters)s." + ) % {'supported_parameters': SUPPORTED_L7_PARAMETERS} + + +def normalize_protocol(value): + if value is None: + return None + if isinstance(value, six.string_types): + if value.lower() in fc_supported_protocols: + return value.lower() + raise FlowClassifierInvalidProtocol( + protocol=value, values=fc_supported_protocols) + + +def normalize_ethertype(value): + if value is None: + return 'IPv4' + if isinstance(value, six.string_types): + for ether_type in fc_supported_ethertypes: + if value.lower() == ether_type.lower(): + return ether_type + raise FlowClassifierInvalidEthertype( + ethertype=value, values=fc_supported_ethertypes) + + +def normalize_string(value): + if value is None: + return '' + return value + + +def normalize_port_value(port): + if port is None: + return None + try: + val = int(port) + except (ValueError, TypeError): + raise FlowClassifierInvalidPortValue(port=port) + + if 0 <= val <= 65535: + return val + else: + raise FlowClassifierInvalidPortValue(port=port) + + +def normalize_l7parameters(parameters): + parameters = attr.convert_none_to_empty_dict(parameters) + if not parameters: + return DEFAULT_L7_PARAMETER + for key, value in six.iteritems(parameters): + if (key, value) not in SUPPORTED_L7_PARAMETERS: + raise FlowClassifierInvalidL7Parameter(key=key, value=value) + return parameters + + +# Attribute Map +RESOURCE_ATTRIBUTE_MAP = { + 'flow_classifiers': { + 'id': { + 'allow_post': False, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:uuid': None}, + 'primary_key': True}, + 'name': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:string': attr.NAME_MAX_LEN}, + 'convert_to': normalize_string}, + 'description': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:string': attr.DESCRIPTION_MAX_LEN}, + 'convert_to': normalize_string}, + 'tenant_id': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:string': attr.TENANT_ID_MAX_LEN}, + 'required_by_policy': True}, + 'ethertype': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'convert_to': normalize_ethertype}, + 'protocol': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'convert_to': normalize_protocol}, + 'source_port_range_min': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'convert_to': normalize_port_value}, + 'source_port_range_max': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'convert_to': normalize_port_value}, + 'destination_port_range_min': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'convert_to': normalize_port_value}, + 'destination_port_range_max': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'convert_to': normalize_port_value}, + 'source_ip_prefix': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'validate': {'type:subnet_or_none': None}}, + 'destination_ip_prefix': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'validate': {'type:subnet_or_none': None}}, + 'logical_source_port': { + 'allow_post': True, 'allow_put': False, + 'is_visible': False, 'default': None, + 'validate': {'type:uuid_or_none': None}}, + 'logical_destination_port': { + 'allow_post': True, 'allow_put': False, + 'is_visible': False, 'default': None, + 'validate': {'type:uuid_or_none': None}}, + 'l7_parameters': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'validate': {'type:dict': None}, + 'convert_to': normalize_l7parameters}, + }, +} + +flow_classifier_quota_opts = [ + cfg.IntOpt('quota_flow_classifier', + default=100, + help=_('Maximum number of flow classifiers per tenant. ' + 'A negative value means unlimited.')), +] +cfg.CONF.register_opts(flow_classifier_quota_opts, 'QUOTAS') + + +class Flowclassifier(neutron_ext.ExtensionDescriptor): + """Flow Classifier extension.""" + + @classmethod + def get_name(cls): + return FLOW_CLASSIFIER_EXT + + @classmethod + def get_alias(cls): + return FLOW_CLASSIFIER_EXT + + @classmethod + def get_description(cls): + return "Flow Classifier Extension." + + @classmethod + def get_plugin_interface(cls): + return FlowClassifierPluginBase + + @classmethod + def get_updated(cls): + return "2015-10-05T10:00:00-00:00" + + def update_attributes_map(self, attributes): + super(Flowclassifier, self).update_attributes_map( + attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP) + + @classmethod + def get_resources(cls): + """Returns Ext Resources.""" + plural_mappings = resource_helper.build_plural_mappings( + {}, RESOURCE_ATTRIBUTE_MAP) + plural_mappings['flow_classifiers'] = 'flow_classifier' + attr.PLURALS.update(plural_mappings) + return resource_helper.build_resource_info( + plural_mappings, + RESOURCE_ATTRIBUTE_MAP, + FLOW_CLASSIFIER_EXT, + register_quota=True) + + def get_extended_resources(self, version): + if version == "2.0": + return RESOURCE_ATTRIBUTE_MAP + else: + return {} + + +@six.add_metaclass(ABCMeta) +class FlowClassifierPluginBase(service_base.ServicePluginBase): + + def get_plugin_name(self): + return FLOW_CLASSIFIER_EXT + + def get_plugin_type(self): + return FLOW_CLASSIFIER_EXT + + def get_plugin_description(self): + return 'Flow classifier plugin' + + @abstractmethod + def create_flow_classifier(self, context, flow_classifier): + pass + + @abstractmethod + def update_flow_classifier(self, context, id, flow_classifier): + pass + + @abstractmethod + def delete_flow_classifier(self, context, id): + pass + + @abstractmethod + def get_flow_classifiers(self, context, filters=None, fields=None, + sorts=None, limit=None, marker=None, + page_reverse=False): + pass + + @abstractmethod + def get_flow_classifier(self, context, id, fields=None): + pass diff --git a/networking_sfc/extensions/sfc.py b/networking_sfc/extensions/sfc.py new file mode 100644 index 0000000..67808b3 --- /dev/null +++ b/networking_sfc/extensions/sfc.py @@ -0,0 +1,382 @@ +# Copyright 2015 Futurewei. All rights reserved. +# +# 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. + +from abc import ABCMeta +from abc import abstractmethod + +import six + +from oslo_config import cfg + +from neutron.api import extensions as neutron_ext +from neutron.api.v2 import attributes as attr +from neutron.api.v2 import resource_helper +from neutron.common import exceptions as neutron_exc +from neutron.services import service_base + +import networking_sfc + + +cfg.CONF.import_opt('api_extensions_path', 'neutron.common.config') +neutron_ext.append_api_extensions_path(networking_sfc.extensions.__path__) + +SFC_EXT = "sfc" +SFC_PREFIX = "/sfc" + +SUPPORTED_CHAIN_PARAMETERS = [('correlation', 'mpls')] +DEFAULT_CHAIN_PARAMETER = {'correlation': 'mpls'} +SUPPORTED_SF_PARAMETERS = [('correlation', None)] +DEFAULT_SF_PARAMETER = {'correlation': None} + + +# Port Chain Exceptions +class PortChainNotFound(neutron_exc.NotFound): + message = _("Port chain %(id)s not found.") + + +class InvalidChainParameter(neutron_exc.InvalidInput): + message = _( + "Chain parameter does not support (%%(key)s, %%(value)s). " + "Supported chain parameters are %(supported_paramters)s" + ) % {'supported_paramters': SUPPORTED_CHAIN_PARAMETERS} + + +class InvalidServiceFunctionParameter(neutron_exc.InvalidInput): + message = _( + "Service function parameter does not support (%%(key)s, %%(value)s). " + "Supported service function parameters are %(supported_paramters)s" + ) % {'supported_paramters': SUPPORTED_SF_PARAMETERS} + + +class PortPairGroupNotSpecified(neutron_exc.InvalidInput): + message = _("Port pair group is not specified in port chain") + + +class InvalidPortPairGroups(neutron_exc.InUse): + message = _("Port pair groups %(port_pair_groups)s in use by " + "port chain %(port_chain)s.") + + +class PortPairPortNotFound(neutron_exc.NotFound): + message = _("Port pair port %(id)s not found.") + + +class PortPairIngressEgressDifferentHost(neutron_exc.InvalidInput): + message = _("Port pair inegress port %(ingress)s " + "egress port %(egress)s not in the same host.") + + +class PortPairIngressNoHost(neutron_exc.InvalidInput): + message = _("Port pair ingress port %(ingress)s does not " + "belong to a host.") + + +class PortPairEgressNoHost(neutron_exc.InvalidInput): + message = _("Port pair egress port %(egress)s does not " + "belong to a host.") + + +class PortPairNotFound(neutron_exc.NotFound): + message = _("Port pair %(id)s not found.") + + +class PortPairGroupNotFound(neutron_exc.NotFound): + message = _("Port pair group %(id)s not found.") + + +class PortPairGroupInUse(neutron_exc.InUse): + message = _("Port pair group %(id)s in use.") + + +class PortPairInUse(neutron_exc.InUse): + message = _("Port pair %(id)s in use.") + + +def normalize_string(value): + if value is None: + return '' + return value + + +def normalize_port_pair_groups(port_pair_groups): + port_pair_groups = attr.convert_none_to_empty_list(port_pair_groups) + if not port_pair_groups: + raise PortPairGroupNotSpecified() + return port_pair_groups + + +def normalize_chain_parameters(parameters): + parameters = attr.convert_none_to_empty_dict(parameters) + if not parameters: + return DEFAULT_CHAIN_PARAMETER + for key, value in six.iteritems(parameters): + if (key, value) not in SUPPORTED_CHAIN_PARAMETERS: + raise InvalidChainParameter(key=key, value=value) + return parameters + + +def normalize_sf_parameters(parameters): + parameters = attr.convert_none_to_empty_dict(parameters) + if not parameters: + return DEFAULT_SF_PARAMETER + for key, value in six.iteritems(parameters): + if (key, value) not in SUPPORTED_SF_PARAMETERS: + raise InvalidServiceFunctionParameter(key=key, value=value) + return parameters + + +RESOURCE_ATTRIBUTE_MAP = { + 'port_pairs': { + 'id': { + 'allow_post': False, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:uuid': None}, + 'primary_key': True}, + 'name': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:string': attr.NAME_MAX_LEN}, + 'convert_to': normalize_string}, + 'description': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:string': attr.DESCRIPTION_MAX_LEN}, + 'convert_to': normalize_string}, + 'tenant_id': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:string': attr.TENANT_ID_MAX_LEN}, + 'required_by_policy': True}, + 'ingress': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:uuid': None}}, + 'egress': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:uuid': None}}, + 'service_function_parameters': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'validate': {'type:dict': None}, + 'convert_to': normalize_sf_parameters}, + }, + 'port_chains': { + 'id': { + 'allow_post': False, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:uuid': None}, + 'primary_key': True}, + 'name': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:string': attr.NAME_MAX_LEN}, + 'convert_to': normalize_string}, + 'description': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:string': attr.DESCRIPTION_MAX_LEN}, + 'convert_to': normalize_string}, + 'tenant_id': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:string': attr.TENANT_ID_MAX_LEN}, + 'required_by_policy': True}, + 'port_pair_groups': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:uuid_list': None}, + 'convert_to': normalize_port_pair_groups}, + 'flow_classifiers': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:uuid_list': None}, + 'convert_to': attr.convert_none_to_empty_list}, + 'chain_parameters': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, 'default': None, + 'validate': {'type:dict': None}, + 'convert_to': normalize_chain_parameters}, + }, + 'port_pair_groups': { + 'id': { + 'allow_post': False, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:uuid': None}, + 'primary_key': True}, + 'name': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:string': attr.NAME_MAX_LEN}, + 'convert_to': normalize_string}, + 'description': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:string': attr.DESCRIPTION_MAX_LEN}, + 'convert_to': normalize_string}, + 'tenant_id': { + 'allow_post': True, 'allow_put': False, + 'is_visible': True, + 'validate': {'type:string': attr.TENANT_ID_MAX_LEN}, + 'required_by_policy': True}, + 'port_pairs': { + 'allow_post': True, 'allow_put': True, + 'is_visible': True, 'default': None, + 'validate': {'type:uuid_list': None}, + 'convert_to': attr.convert_none_to_empty_list}, + }, +} + +sfc_quota_opts = [ + cfg.IntOpt('quota_port_chain', + default=10, + help=_('Maximum number of port chains per tenant. ' + 'A negative value means unlimited.')), + cfg.IntOpt('quota_port_pair_group', + default=10, + help=_('maximum number of port pair group per tenant. ' + 'a negative value means unlimited.')), + cfg.IntOpt('quota_port_pair', + default=100, + help=_('maximum number of port pair per tenant. ' + 'a negative value means unlimited.')) +] + +cfg.CONF.register_opts(sfc_quota_opts, 'QUOTAS') + + +class Sfc(neutron_ext.ExtensionDescriptor): + """Service Function Chain extension.""" + + @classmethod + def get_name(cls): + return SFC_EXT + + @classmethod + def get_alias(cls): + return SFC_EXT + + @classmethod + def get_description(cls): + return "service function chains extension." + + @classmethod + def get_plugin_interface(cls): + return SfcPluginBase + + @classmethod + def get_updated(cls): + return "2015-10-05T10:00:00-00:00" + + def update_attributes_map(self, attributes): + super(Sfc, self).update_attributes_map( + attributes, extension_attrs_map=RESOURCE_ATTRIBUTE_MAP) + + @classmethod + def get_resources(cls): + """Returns Ext Resources.""" + plural_mappings = resource_helper.build_plural_mappings( + {}, RESOURCE_ATTRIBUTE_MAP) + plural_mappings['sfcs'] = 'sfc' + attr.PLURALS.update(plural_mappings) + return resource_helper.build_resource_info( + plural_mappings, + RESOURCE_ATTRIBUTE_MAP, + SFC_EXT, + register_quota=True) + + def get_extended_resources(self, version): + if version == "2.0": + return RESOURCE_ATTRIBUTE_MAP + else: + return {} + + +@six.add_metaclass(ABCMeta) +class SfcPluginBase(service_base.ServicePluginBase): + + def get_plugin_name(self): + return SFC_EXT + + def get_plugin_type(self): + return SFC_EXT + + def get_plugin_description(self): + return 'SFC service plugin for service chaining' + + @abstractmethod + def create_port_chain(self, context, port_chain): + pass + + @abstractmethod + def update_port_chain(self, context, id, port_chain): + pass + + @abstractmethod + def delete_port_chain(self, context, id): + pass + + @abstractmethod + def get_port_chains(self, context, filters=None, fields=None, + sorts=None, limit=None, marker=None, + page_reverse=False): + pass + + @abstractmethod + def get_port_chain(self, context, id, fields=None): + pass + + @abstractmethod + def create_port_pair_group(self, context, port_pair_group): + pass + + @abstractmethod + def update_port_pair_group(self, context, id, port_pair_group): + pass + + @abstractmethod + def delete_port_pair_group(self, context, id): + pass + + @abstractmethod + def get_port_pair_groups(self, context, filters=None, fields=None, + sorts=None, limit=None, marker=None, + page_reverse=False): + pass + + @abstractmethod + def get_port_pair_group(self, context, id, fields=None): + pass + + @abstractmethod + def create_port_pair(self, context, port_pair): + pass + + @abstractmethod + def update_port_pair(self, context, id, port_pair): + pass + + @abstractmethod + def delete_port_pair(self, context, id): + pass + + @abstractmethod + def get_port_pairs(self, context, filters=None, fields=None, + sorts=None, limit=None, marker=None, + page_reverse=False): + pass + + @abstractmethod + def get_port_pair(self, context, id, fields=None): + pass -- cgit 1.2.3-korg