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/services/sfc/common/__init__.py | 0 networking_sfc/services/sfc/common/config.py | 27 ++++ networking_sfc/services/sfc/common/context.py | 85 ++++++++++ networking_sfc/services/sfc/common/exceptions.py | 46 ++++++ networking_sfc/services/sfc/common/ovs_ext_lib.py | 187 ++++++++++++++++++++++ 5 files changed, 345 insertions(+) create mode 100644 networking_sfc/services/sfc/common/__init__.py create mode 100644 networking_sfc/services/sfc/common/config.py create mode 100644 networking_sfc/services/sfc/common/context.py create mode 100644 networking_sfc/services/sfc/common/exceptions.py create mode 100644 networking_sfc/services/sfc/common/ovs_ext_lib.py (limited to 'networking_sfc/services/sfc/common') diff --git a/networking_sfc/services/sfc/common/__init__.py b/networking_sfc/services/sfc/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/networking_sfc/services/sfc/common/config.py b/networking_sfc/services/sfc/common/config.py new file mode 100644 index 0000000..29acd1c --- /dev/null +++ b/networking_sfc/services/sfc/common/config.py @@ -0,0 +1,27 @@ +# 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 oslo_config import cfg + + +SFC_DRIVER_OPTS = [ + cfg.ListOpt('drivers', + default=['dummy'], + help=_("An ordered list of service chain drivers " + "entrypoints to be loaded from the " + "networking_sfc.sfc.drivers namespace.")), +] + + +cfg.CONF.register_opts(SFC_DRIVER_OPTS, "sfc") diff --git a/networking_sfc/services/sfc/common/context.py b/networking_sfc/services/sfc/common/context.py new file mode 100644 index 0000000..7d3b451 --- /dev/null +++ b/networking_sfc/services/sfc/common/context.py @@ -0,0 +1,85 @@ +# 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. + + +class SfcPluginContext(object): + """SFC context base class.""" + def __init__(self, plugin, plugin_context): + self._plugin = plugin + self._plugin_context = plugin_context + + +class PortChainContext(SfcPluginContext): + + def __init__(self, plugin, plugin_context, portchain, + original_portchain=None): + super(PortChainContext, self).__init__(plugin, plugin_context) + self._portchain = portchain + self._original_portchain = original_portchain + + @property + def current(self): + return self._portchain + + @property + def original(self): + return self._original_portchain + + +class FlowClassifierContext(SfcPluginContext): + def __init__(self, plugin, plugin_context, flowclassifier, + original_flowclassifier=None): + super(FlowClassifierContext, self).__init__(plugin, plugin_context) + self._flowclassifier = flowclassifier + self._original_flowclassifier = original_flowclassifier + + @property + def current(self): + return self._flowclassifier + + @property + def original(self): + return self._original_flowclassifier + + +class PortPairContext(SfcPluginContext): + def __init__(self, plugin, plugin_context, portpair, + original_portpair=None): + super(PortPairContext, self).__init__(plugin, plugin_context) + self._portpair = portpair + self._original_portpair = original_portpair + + @property + def current(self): + return self._portpair + + @property + def original(self): + return self._original_portpair + + +class PortPairGroupContext(SfcPluginContext): + def __init__(self, plugin, plugin_context, portpairgroup, + original_portpairgroup=None): + super(PortPairGroupContext, self).__init__(plugin, plugin_context) + self._portpairgroup = portpairgroup + self._original_portpairgroup = original_portpairgroup + + @property + def current(self): + return self._portpairgroup + + @property + def original(self): + return self._original_portpairgroup diff --git a/networking_sfc/services/sfc/common/exceptions.py b/networking_sfc/services/sfc/common/exceptions.py new file mode 100644 index 0000000..7d1b9d9 --- /dev/null +++ b/networking_sfc/services/sfc/common/exceptions.py @@ -0,0 +1,46 @@ +# 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. +"""Exceptions used by SFC plugin and drivers.""" + +from neutron.common import exceptions + + +class SfcDriverError(exceptions.NeutronException): + """SFC driver call failed.""" + message = _("%(method)s failed.") + + +class SfcException(exceptions.NeutronException): + """Base for SFC driver exceptions returned to user.""" + pass + + +class SfcBadRequest(exceptions.BadRequest, SfcException): + """Base for SFC driver bad request exceptions returned to user.""" + pass + + +class SfcNoSubnetGateway(SfcDriverError): + """No subnet gateway.""" + message = _("There is no %(type)s of ip prefix %(cidr)s.") + + +class SfcNoSuchSubnet(SfcDriverError): + """No such subnet.""" + message = _("There is no %(type)s of %(cidr)s.") + + +class FlowClassifierInvalid(SfcDriverError): + """Invalid flow classifier.""" + message = _("There is no %(type)s assigned.") diff --git a/networking_sfc/services/sfc/common/ovs_ext_lib.py b/networking_sfc/services/sfc/common/ovs_ext_lib.py new file mode 100644 index 0000000..01fbd04 --- /dev/null +++ b/networking_sfc/services/sfc/common/ovs_ext_lib.py @@ -0,0 +1,187 @@ +# Copyright 2015 Huawei. 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. + +import collections +from neutron.agent.common import ovs_lib +from neutron.agent.common import utils +from neutron.common import exceptions +from neutron.i18n import _LE +from neutron.plugins.common import constants +from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import ( + ovs_bridge) +from oslo_log import log as logging +import six + + +# Special return value for an invalid OVS ofport +INVALID_OFPORT = '-1' + +LOG = logging.getLogger(__name__) + + +def get_port_mask(min_port, max_port): + """get port/mask serial by port range.""" + if min_port < 1 or max_port > 0xffff or min_port > max_port: + msg = _("the port range is invalid") + raise exceptions.InvalidInput(error_message=msg) + masks = [] + while min_port <= max_port: + mask = 0xffff + while mask != 0: + next_mask = (mask << 1) & 0xffff + port_start = min_port & next_mask + port_end = min_port + (next_mask ^ 0xffff) + if port_start == min_port and port_end <= max_port: + mask = next_mask + else: + break + masks.append('0x%x/0x%x' % (min_port, mask)) + min_port = min_port + (mask ^ 0xffff) + 1 + + return masks + + +class OVSBridgeExt(ovs_bridge.OVSAgentBridge): + def setup_controllers(self, conf): + self.set_protocols("[]") + self.del_controller() + + def dump_flows_full_match(self, flow_str): + retval = None + flows = self.run_ofctl("dump-flows", [flow_str]) + if flows: + retval = '\n'.join(item for item in flows.splitlines() + if 'NXST' not in item and 'OFPST' not in item) + return retval + + def mod_flow(self, **kwargs): + flow_copy = kwargs.copy() + flow_copy.pop('actions') + flow_str = ovs_lib._build_flow_expr_str(flow_copy, 'del') + dump_flows = self.dump_flows_full_match(flow_str) + if dump_flows == '': + self.do_action_flows('add', [kwargs]) + else: + self.do_action_flows('mod', [kwargs]) + + def add_nsh_tunnel_port(self, port_name, remote_ip, local_ip, + tunnel_type=constants.TYPE_GRE, + vxlan_udp_port=constants.VXLAN_UDP_PORT, + dont_fragment=True, + in_nsp=None, + in_nsi=None): + attrs = [('type', tunnel_type)] + # This is an OrderedDict solely to make a test happy + options = collections.OrderedDict() + vxlan_uses_custom_udp_port = ( + tunnel_type == constants.TYPE_VXLAN and + vxlan_udp_port != constants.VXLAN_UDP_PORT + ) + if vxlan_uses_custom_udp_port: + options['dst_port'] = vxlan_udp_port + options['df_default'] = str(dont_fragment).lower() + options['remote_ip'] = 'flow' + options['local_ip'] = local_ip + options['in_key'] = 'flow' + options['out_key'] = 'flow' + if in_nsp is not None and in_nsi is not None: + options['nsp'] = str(in_nsp) + options['nsi'] = str(in_nsi) + elif in_nsp is None and in_nsi is None: + options['nsp'] = 'flow' + options['nsi'] = 'flow' + attrs.append(('options', options)) + ofport = self.add_port(port_name, *attrs) + if ( + tunnel_type == constants.TYPE_VXLAN and + ofport == INVALID_OFPORT + ): + LOG.error( + _LE('Unable to create VXLAN tunnel port for service chain. ' + 'Please ensure that an openvswitch version that supports ' + 'VXLAN for service chain is installed.') + ) + return ofport + + def run_ofctl(self, cmd, args, process_input=None): + # We need to dump-groups according to group Id, + # which is a feature of OpenFlow1.5 + full_args = [ + "ovs-ofctl", "-O openflow13", cmd, self.br_name + ] + args + try: + return utils.execute(full_args, run_as_root=True, + process_input=process_input) + except Exception as e: + LOG.exception(e) + LOG.error(_LE("Unable to execute %(args)s."), + {'args': full_args}) + + def do_action_groups(self, action, kwargs_list): + group_strs = [_build_group_expr_str(kw, action) for kw in kwargs_list] + if action == 'add' or action == 'del': + self.run_ofctl('%s-groups' % action, ['-'], '\n'.join(group_strs)) + elif action == 'mod': + self.run_ofctl('%s-group' % action, ['-'], '\n'.join(group_strs)) + else: + msg = _("Action is illegal") + raise exceptions.InvalidInput(error_message=msg) + + def add_group(self, **kwargs): + self.do_action_groups('add', [kwargs]) + + def mod_group(self, **kwargs): + self.do_action_groups('mod', [kwargs]) + + def delete_group(self, **kwargs): + self.do_action_groups('del', [kwargs]) + + def dump_group_for_id(self, group_id): + retval = None + group_str = "%d" % group_id + group = self.run_ofctl("dump-groups", [group_str]) + if group: + retval = '\n'.join(item for item in group.splitlines() + if 'NXST' not in item) + return retval + + +def _build_group_expr_str(group_dict, cmd): + group_expr_arr = [] + buckets = None + groupId = None + + if cmd != 'del': + if "group_id" not in group_dict: + msg = _("Must specify one groupId on groupo addition" + " or modification") + raise exceptions.InvalidInput(error_message=msg) + groupId = "group_id=%s" % group_dict.pop('group_id') + + if "buckets" not in group_dict: + msg = _("Must specify one or more buckets on group addition" + " or modification") + raise exceptions.InvalidInput(error_message=msg) + buckets = "%s" % group_dict.pop('buckets') + + if groupId: + group_expr_arr.append(groupId) + + for key, value in six.iteritems(group_dict): + group_expr_arr.append("%s=%s" % (key, value)) + + if buckets: + group_expr_arr.append(buckets) + + return ','.join(group_expr_arr) -- cgit 1.2.3-korg