From cc38589f3632ef6054524c13de9d2bd793c094aa Mon Sep 17 00:00:00 2001 From: Eran Gampel Date: Sun, 11 Jan 2015 09:49:21 +0200 Subject: [PATCH 1/8] merge Change-Id: Ibb3d4bab9f094de0081cc3fb8a49f75a01ef5cdf --- neutron/agent/linux/ovs_lib.py | 22 +- neutron/agent/rpc.py | 11 +- neutron/api/rpc/handlers/l3_rpc.py | 18 +- .../plugins/openvswitch/agent/ovs_neutron_agent.py | 130 +- neutron/plugins/openvswitch/common/config.py | 3 + neutron/plugins/openvswitch/common/constants.py | 5 +- .../services/l3_router/README.l3_cont_dvr_plugin | 63 + neutron/services/l3_router/l3_cont_dvr_plugin.py | 360 ++++++ neutron/services/l3_router/l3_reactive_app.py | 1274 ++++++++++++++++++++ neutron/tests/unit/openvswitch/test_ovs_tunnel.py | 2 + 10 files changed, 1869 insertions(+), 19 deletions(-) create mode 100644 neutron/services/l3_router/README.l3_cont_dvr_plugin create mode 100755 neutron/services/l3_router/l3_cont_dvr_plugin.py create mode 100755 neutron/services/l3_router/l3_reactive_app.py diff --git a/neutron/agent/linux/ovs_lib.py b/neutron/agent/linux/ovs_lib.py index 7b2d2a2..f745cf3 100644 --- a/neutron/agent/linux/ovs_lib.py +++ b/neutron/agent/linux/ovs_lib.py @@ -138,6 +138,7 @@ class BaseOVS(object): class OVSBridge(BaseOVS): + def __init__(self, br_name, root_helper): super(OVSBridge, self).__init__(root_helper) self.br_name = br_name @@ -158,6 +159,11 @@ class OVSBridge(BaseOVS): return res.strip().split('\n') return res + def set_controller_mode(self, mode): + self.run_vsctl(['--', 'set', 'controller', self.br_name, + "connection-mode=%s" % mode], + check_error=True) + def set_secure_mode(self): self.run_vsctl(['--', 'set-fail-mode', self.br_name, 'secure'], check_error=True) @@ -209,8 +215,11 @@ class OVSBridge(BaseOVS): args = ["clear", table_name, record, column] self.run_vsctl(args) - def run_ofctl(self, cmd, args, process_input=None): - full_args = ["ovs-ofctl", cmd, self.br_name] + args + def run_ofctl(self, cmd, args, process_input=None, protocols=None): + if protocols: + full_args = ["ovs-ofctl", cmd, protocols, self.br_name] + args + else: + full_args = ["ovs-ofctl", cmd, self.br_name] + args try: return utils.execute(full_args, root_helper=self.root_helper, process_input=process_input) @@ -245,12 +254,13 @@ class OVSBridge(BaseOVS): return self.db_get_val('Bridge', self.br_name, 'datapath_id').strip('"') - def do_action_flows(self, action, kwargs_list): + def do_action_flows(self, action, kwargs_list, protocols=None): flow_strs = [_build_flow_expr_str(kw, action) for kw in kwargs_list] - self.run_ofctl('%s-flows' % action, ['-'], '\n'.join(flow_strs)) + self.run_ofctl('%s-flows' % action, ['-'], '\n'.join(flow_strs), + protocols) - def add_flow(self, **kwargs): - self.do_action_flows('add', [kwargs]) + def add_flow(self, protocols=None, **kwargs): + self.do_action_flows('add', [kwargs], protocols) def mod_flow(self, **kwargs): self.do_action_flows('mod', [kwargs]) diff --git a/neutron/agent/rpc.py b/neutron/agent/rpc.py index 98e641a..deae0e4 100644 --- a/neutron/agent/rpc.py +++ b/neutron/agent/rpc.py @@ -22,7 +22,6 @@ from neutron.common import topics from neutron.i18n import _LW from neutron.openstack.common import log as logging - LOG = logging.getLogger(__name__) @@ -121,3 +120,13 @@ class PluginApi(object): cctxt = self.client.prepare() return cctxt.call(context, 'tunnel_sync', tunnel_ip=tunnel_ip, tunnel_type=tunnel_type) + + def update_agent_port_mapping_done(self, context, agent_id, + ip_address, host=None): + LOG.debug(("Notify controller on new/updated endpoint %s"), host) + topic = topics.L3PLUGIN + cctxt = self.client.prepare(topic=topic, fanout=True) + cctxt.cast(context, 'update_agent_port_mapping_done', + agent_id=agent_id, + ip_address=ip_address, + host=host) diff --git a/neutron/api/rpc/handlers/l3_rpc.py b/neutron/api/rpc/handlers/l3_rpc.py index aebb670..9e08eed 100644 --- a/neutron/api/rpc/handlers/l3_rpc.py +++ b/neutron/api/rpc/handlers/l3_rpc.py @@ -28,11 +28,11 @@ from neutron import manager from neutron.openstack.common import log as logging from neutron.plugins.common import constants as plugin_constants - LOG = logging.getLogger(__name__) class L3RpcCallback(object): + """L3 agent RPC callback in plugin implementations.""" # 1.0 L3PluginApi BASE_RPC_API_VERSION @@ -80,7 +80,7 @@ class L3RpcCallback(object): else: routers = self.l3plugin.get_sync_data(context, router_ids) if utils.is_extension_supported( - self.plugin, constants.PORT_BINDING_EXT_ALIAS): + self.plugin, constants.PORT_BINDING_EXT_ALIAS): self._ensure_host_set_on_ports(context, host, routers) LOG.debug("Routers returned to l3 agent:\n %s", jsonutils.dumps(routers, indent=5)) @@ -206,7 +206,7 @@ class L3RpcCallback(object): self._ensure_host_set_on_port(admin_ctx, host, agent_port) LOG.debug('Agent Gateway port returned : %(agent_port)s with ' 'host %(host)s', {'agent_port': agent_port, - 'host': host}) + 'host': host}) return agent_port def get_snat_router_interface_ports(self, context, **kwargs): @@ -228,7 +228,7 @@ class L3RpcCallback(object): self._ensure_host_set_on_port(admin_ctx, host, p) LOG.debug('SNAT interface ports returned : %(snat_port_list)s ' 'and on host %(host)s', {'snat_port_list': snat_port_list, - 'host': host}) + 'host': host}) return snat_port_list def update_router_state(self, context, **kwargs): @@ -238,3 +238,13 @@ class L3RpcCallback(object): return self.l3plugin.update_router_state(context, router_id, state, host=host) + + def update_agent_port_mapping_done(self, context, **kwargs): + agent_id = kwargs.get('agent_id') + ip_address = kwargs.get('ip_address') + host = kwargs.get('host') + try: + return self.l3plugin.update_agent_port_mapping_done( + context, agent_id, ip_address, host) + except AttributeError: + LOG.debug("No Handle for:update_agent_port_mapping_done L3 Serv") diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index 4fd77e3..dce7feb 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -14,12 +14,12 @@ # License for the specific language governing permissions and limitations # under the License. +import eventlet import hashlib import signal import sys +import threading import time - -import eventlet eventlet.monkey_patch() import netaddr @@ -47,8 +47,6 @@ from neutron.openstack.common import log as logging from neutron.openstack.common import loopingcall from neutron.plugins.common import constants as p_const from neutron.plugins.openvswitch.common import constants - - LOG = logging.getLogger(__name__) cfg.CONF.import_group('AGENT', 'neutron.plugins.openvswitch.common.config') @@ -194,15 +192,25 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, # Keep track of int_br's device count for use by _report_state() self.int_br_device_count = 0 + self.local_vlan_map = {} + # Initialize controller Ip List + self.controllers_ip_list = None + ''' + Sync lock for Race condition set_controller <--> check_ovs_restart + when setting the controller all the flow table are deleted + by the time we set the CANARY_TABLE again. + ''' + self.set_controller_lock = threading.Lock() + self.enable_l3_controller = cfg.CONF.AGENT.enable_l3_controller self.int_br = ovs_lib.OVSBridge(integ_br, self.root_helper) + self.setup_integration_br() # Stores port update notifications for processing in main rpc loop self.updated_ports = set() self.setup_rpc() self.bridge_mappings = bridge_mappings self.setup_physical_bridges(self.bridge_mappings) - self.local_vlan_map = {} self.tun_br_ofports = {p_const.TYPE_GRE: {}, p_const.TYPE_VXLAN: {}} @@ -453,6 +461,103 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, else: LOG.warning(_LW('Action %s not supported'), action) + def get_bridge_by_name(self, br_id): + bridge = None + if self.int_br.br_name == br_id: + bridge = self.int_br + elif self.tun_br.br_name == br_id: + bridge = self.tun_br + else: + for physical_network in self.phys_brs: + if self.phys_brs[physical_network].br_name == br_id: + bridge = self.phys_brs[physical_network] + break + return bridge + + def setup_entry_for_arp_reply_remote(self, context, br_id, action, + table_id, segmentation_id, net_uuid, + mac_address, ip_address): + '''Set the ARP respond entry. + :param br_id: the bridge id. + :param action: add or remove. + :param table_id: Id of the table to insert the ARP responder rule. + :param segmentation_id: the segmentation id of the req network. + :param net_uuid: the uuid of the network associated with this vlan. + :param mac_address: the resolved mac addressby arp. + :param ip address: the ip address to resolve ARP for . + ''' + br = self.get_bridge_by_name(br_id) + if not br: + LOG.errror("Failure Could not find bridge name <%s>", br_id) + return + lvm = self.local_vlan_map.get(net_uuid) + if lvm: + local_vid = lvm.vlan + else: + LOG.debug(("Network %s not used on agent."), net_uuid) + return + mac = netaddr.EUI(mac_address, dialect=netaddr.mac_unix) + ip = netaddr.IPAddress(ip_address) + if action == 'add': + actions = constants.ARP_RESPONDER_ACTIONS % {'mac': mac, 'ip': ip} + actions = "strip_vlan,%s" % actions + br.add_flow(table=table_id, + priority=100, + proto='arp', + dl_vlan=local_vid, + nw_dst='%s' % ip, + actions=actions) + elif action == 'remove': + br.delete_flows(table=table_id, + proto='arp', + dl_vlan=local_vid, + nw_dst='%s' % ip) + else: + LOG.warning(_LW('Action %s not supported'), action) + + def set_controller_for_br(self, context, br_id, ip_address_list, + force_reconnect=False, protocols="OpenFlow13"): + '''Set OpenFlow Controller on the Bridge . + :param br_id: the bridge id . + :param ip_address_list: tcp:ip_address:port;tcp:ip_address2:port + :param force_reconnect: Force re setting the controller,remove i + all flows + ''' + if not self.enable_l3_controller: + LOG.info(_LI("Controller Base l3 is disabled on Agent")) + return + bridge = None + if (force_reconnect or not self.controllers_ip_list + or self.controllers_ip_list != ip_address_list): + self.controllers_ip_list = ip_address_list + bridge = self.get_bridge_by_name(br_id) + if not bridge: + LOG.errror("set_controller_for_br failur! no bridge %s ", + br_id) + return + ip_address_ = ip_address_list.split(";") + LOG.debug(("Set Controllers on br %s to %s"), br_id, ip_address_) + self.set_controller_lock.acquire() + bridge.del_controller() + bridge.set_controller(ip_address_) + #bridge.set_protocols(protocols) + if bridge.br_name == "br-int": + bridge.add_flow(priority=0, actions="normal") + bridge.add_flow(table=constants.CANARY_TABLE, priority=0, + actions="drop") + self.update_metadata_vlan_map_table(bridge) + bridge.set_controller_mode("out-of-band") + self.set_controller_lock.release() + + def update_metadata_vlan_map_table(self, bridge): + for net_id, vlan_mapping in self.local_vlan_map.iteritems(): + seg_id_hex = hex(vlan_mapping.segmentation_id) + bridge.add_flow(table=constants.BR_INT_METADATA_TABLE, + priority=100, + dl_vlan=vlan_mapping.vlan, + actions="write_metadata:%s" % + (seg_id_hex), protocols="-OOpenFlow13") + def provision_local_vlan(self, net_uuid, network_type, physical_network, segmentation_id): '''Provisions a local VLAN. @@ -648,7 +753,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.dvr_agent.bind_port_to_dvr(port, network_type, fixed_ips, device_owner, local_vlan_id=lvm.vlan) - + if self.enable_l3_controller: + self.update_metadata_vlan_map_table(self.int_br) # Do not bind a port if it's already bound cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag") if cur_tag != str(lvm.vlan): @@ -712,7 +818,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, # which does nothing if bridge already exists. self.int_br.create() self.int_br.set_secure_mode() - + if not self.enable_l3_controller: + self.int_br.del_controller() self.int_br.delete_port(cfg.CONF.OVS.int_peer_patch_port) self.int_br.remove_all_flows() # switch all traffic using L2 learning @@ -1341,8 +1448,11 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, port_info.get('updated')) def check_ovs_status(self): + # Sync lock for race condition with set_controller + self.set_controller_lock.acquire() # Check for the canary flow canary_flow = self.int_br.dump_flows_for_table(constants.CANARY_TABLE) + self.set_controller_lock.release() if canary_flow == '': LOG.warning(_LW("OVS is restarted. OVSNeutronAgent will reset " "bridges and recover ports.")) @@ -1467,6 +1577,12 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, len(port_info.get('updated', []))) port_stats['regular']['removed'] = ( len(port_info.get('removed', []))) + if self.enable_l3_controller: + rpc = self.plugin_rpc + rpc.update_agent_port_mapping_done(self.context, + self.agent_id, + self.local_ip, + cfg.CONF.host) ports = port_info['current'] # Treat ancillary devices if they exist if self.ancillary_brs: diff --git a/neutron/plugins/openvswitch/common/config.py b/neutron/plugins/openvswitch/common/config.py index a5d1e92..8926981 100644 --- a/neutron/plugins/openvswitch/common/config.py +++ b/neutron/plugins/openvswitch/common/config.py @@ -79,6 +79,9 @@ agent_opts = [ "outgoing IP packet carrying GRE/VXLAN tunnel.")), cfg.BoolOpt('enable_distributed_routing', default=False, help=_("Make the l2 agent run in DVR mode.")), + cfg.BoolOpt('enable_l3_controller', default=False, + help=_("Allow the l3 controller Mode on the" + "integration_bridge")), ] diff --git a/neutron/plugins/openvswitch/common/constants.py b/neutron/plugins/openvswitch/common/constants.py index 98c122d..d709d87 100644 --- a/neutron/plugins/openvswitch/common/constants.py +++ b/neutron/plugins/openvswitch/common/constants.py @@ -52,7 +52,10 @@ FLOOD_TO_TUN = 22 # Tables for integration bridge # Table 0 is used for forwarding. CANARY_TABLE = 23 - +BR_INT_CLASSIFIER_TABLE = 40 +BR_INT_METADATA_TABLE = 50 +BR_INT_ARP_TABLE = 51 +BR_INT_L3_FLOWS = 52 # Map tunnel types to tables number TUN_TABLE = {p_const.TYPE_GRE: GRE_TUN_TO_LV, p_const.TYPE_VXLAN: VXLAN_TUN_TO_LV} diff --git a/neutron/services/l3_router/README.l3_cont_dvr_plugin b/neutron/services/l3_router/README.l3_cont_dvr_plugin new file mode 100644 index 0000000..e02efa6 --- /dev/null +++ b/neutron/services/l3_router/README.l3_cont_dvr_plugin @@ -0,0 +1,63 @@ +####### +In order to enable Neutron Controlle-based DVR, you need to make the + following change in ``neutron.conf``: + + 1. Comment-out loading of the ``L3RouterPlugin`` + + 2. Add the ``neutron.services.l3_router.l3_cont_dvr_plugin.ControllerL3ServicePlugin`` + to the service plugin list: + + **neutron.conf** + + :literal:`[default]` + + :literal:`#service_plugins = neutron.services.l3_router.l3_router_plugin.L3RouterPlugin` + + :literal:`service_plugins=neutron.services.l3_router.l3_cont_dvr_plugin.ControllerL3ServicePlugin` + +2. In addition, make the following change in ``ml2_conf.ini``: + + * Set the ``enable_l3_controller`` to ``True``: + + **ml2_conf.ini** + + :literal:`[agent]` + + :literal:`# enable_l3_controller = True` + + +3. Deploy the **L3 Controller-based DVR Agent** on the Network Node + +4. Deploy the **Public Network Agent** on *each* Compute node + +5. Remove deployment of L3 Agent or DVR Agent + +6. Install Ryu on Network Node +% git clone git://github.com/osrg/ryu.git +The current implementation is embedded into the service plugin. will be moved into a Agent based implementation of the controller. +Until we do that we will have to apply the following patch on ryu +ryu/controller/controller.py and ryu/app/wsgi.py modify register_cli_opts to register_opts +--- a/ryu/app/wsgi.py ++++ b/ryu/app/wsgi.py +@@ -31,7 +31,7 @@ from tinyrpc.transports import ServerTransport, ClientTranspor + from tinyrpc.client import RPCClient + + CONF = cfg.CONF +-CONF.register_cli_opts([ ++CONF.register_opts([ + cfg.StrOpt('wsapi-host', default='', help='webapp listen host'), + cfg.IntOpt('wsapi-port', default=8080, help='webapp listen port') + ]) +diff --git a/ryu/controller/controller.py b/ryu/controller/controller.py +index 23418f5..a5bcda2 100644 +--- a/ryu/controller/controller.py ++++ b/ryu/controller/controller.py +@@ -48,7 +48,7 @@ from ryu.lib.dpid import dpid_to_str + LOG = logging.getLogger('ryu.controller.controller') + + CONF = cfg.CONF +-CONF.register_cli_opts([ ++CONF.register_opts([ + + +% cd ryu; python ./setup.py install diff --git a/neutron/services/l3_router/l3_cont_dvr_plugin.py b/neutron/services/l3_router/l3_cont_dvr_plugin.py new file mode 100755 index 0000000..e449e6f --- /dev/null +++ b/neutron/services/l3_router/l3_cont_dvr_plugin.py @@ -0,0 +1,360 @@ +# Copyright (c) 2014 OpenStack Foundation. +# 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 threading + +from ryu.base.app_manager import AppManager +from ryu.controller.ofp_handler import OFPHandler + +from oslo.config import cfg +from oslo import messaging +from oslo.utils import excutils +from oslo.utils import importutils + +from neutron import context +from neutron import manager + +from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api +from neutron.api.rpc.handlers import l3_rpc +from neutron.common import constants as q_const +from neutron.common import rpc as n_rpc +from neutron.common import topics +from neutron.common import utils +from neutron.plugins.common import constants + +from neutron.db import common_db_mixin +from neutron.db import l3_gwmode_db +from neutron.db import l3_hascheduler_db + +from neutron.openstack.common import log as logging +from neutron.openstack.common import loopingcall + +from neutron.services.l3_router.l3_reactive_app import L3ReactiveApp + +LOG = logging.getLogger(__name__) + + +NET_CONTROL_L3_OPTS = [ + cfg.StrOpt('L3controller_ip_list', + default='tcp:10.100.100.38:6633', + help=("L3 Controler IP list list tcp:ip_addr:port;" + "tcp:ip_addr:port..;..")), + cfg.StrOpt('net_controller_l3_southbound_protocol', + default='OpenFlow', + help=("Southbound protocol to connect the forwarding" + "element Currently supports only OpenFlow")) +] + +cfg.CONF.register_opts(NET_CONTROL_L3_OPTS) + + +L3_SDN_AGNET_TYPE = "SDN_app_l3" + + +class ControllerL3ServicePlugin(common_db_mixin.CommonDbMixin, + l3_gwmode_db.L3_NAT_db_mixin, + l3_hascheduler_db.L3_HA_scheduler_db_mixin): + + RPC_API_VERSION = '1.2' + supported_extension_aliases = ["router", "ext-gw-mode"] + + def __init__(self): + + self.setup_rpc() + self.router_scheduler = importutils.import_object( + cfg.CONF.router_scheduler_driver) + self.start_periodic_agent_status_check() + if cfg.CONF.net_controller_l3_southbound_protocol == "OpenFlow": + # Open Flow Controller + LOG.debug(("Using Southbound OpenFlow Protocol ")) + self.controllerThread = ControllerRunner("openflow") + self.controllerThread.start() + self.controllerThread.router_scheduler = self.router_scheduler + self.controllerThread.endpoints = self.endpoints + + elif cfg.CONF.net_controller_l3_southbound_protocol == "OVSDB": + LOG.error(("Southbound OVSDB Protocol not implemented yet")) + elif cfg.CONF.net_controller_l3_southbound_protocol == "OP-FLEX": + LOG.error(("Southbound OP-FLEX Protocol not implemented yet")) + + super(ControllerL3ServicePlugin, self).__init__() + + def setup_rpc(self): + # RPC support + self.topic = topics.L3PLUGIN + self.conn = n_rpc.create_connection(new=True) + self.agent_notifiers.update( + {q_const.AGENT_TYPE_L3: l3_rpc_agent_api.L3AgentNotifyAPI()}) + self.endpoints = [l3_rpc.L3RpcCallback()] + self.conn.create_consumer(self.topic, self.endpoints, + fanout=True) + self.conn.consume_in_threads() + + def get_plugin_type(self): + return constants.L3_ROUTER_NAT + + def get_plugin_description(self): + """Returns string description of the plugin.""" + return "Net Controler Plugin reactive mode l3 Implementation" + + def create_floatingip(self, _context, floatingip): + """Create floating IP. + + :param _context: Neutron request context + :param floatingip: data for the floating IP being created + :returns: A floating IP object on success + + """ + return super(ControllerL3ServicePlugin, self).create_floatingip( + _context, floatingip, + initial_status=q_const.FLOATINGIP_STATUS_DOWN) + + def add_router_interface_postcommit(self, _context, router_id, + interface_info): + # Update router's state first + LOG.debug(("add_router_interface_postcommit ")) + self.controllerThread.bind_unscheduled_routers() + self.controllerThread.update_device_up_by_port_id( + interface_info['port_id']) + # TODO(gampel) Add router info to Local datastore and abstruction layer + # sync local data + self.controllerThread.l3_r_app.notify_sync() + + def remove_router_interface_precommit(self, _context, router_id, + interface_info): + LOG.debug(("remove_router_interface_precommit")) + # TODO(gampel) Add router info to Local datastore and abstruction layer + + def delete_router_precommit(self, _context, router_id): + LOG.debug(("delete_router_precommit ")) + + def update_router_postcommit(self, _context, router): + self.controllerThread.bind_unscheduled_routers() + LOG.debug(("update_router_postcommit ")) + if router['admin_state_up']: + LOG.debug(("update_router_postcommit admin state up Enable ")) + else: + LOG.debug(("update_router_postcommit admin state down disable")) + + self.controllerThread.l3_r_app.notify_sync() + + # Router API + + def create_router(self, *args, **kwargs): + self.controllerThread.l3_r_app.create_router(self, *args, **kwargs) + return super(ControllerL3ServicePlugin, self).create_router( + *args, **kwargs) + + def update_router(self, _context, r_id, router): + + result = super(ControllerL3ServicePlugin, self).update_router(_context, + r_id, + router) + self.update_router_postcommit(_context, result) + return result + + def delete_router(self, _context, router_id): + self.delete_router_precommit(_context, router_id) + result = super(ControllerL3ServicePlugin, self).delete_router(_context, + router_id) + self.controllerThread.l3_r_app.notify_sync() + return result + + # Router Interface API + + def add_router_interface(self, _context, router_id, interface_info): + # Create interface in parent + result = super(ControllerL3ServicePlugin, self).add_router_interface( + _context, router_id, interface_info) + try: + self.add_router_interface_postcommit(_context, router_id, + result) + except Exception: + with excutils.save_and_reraise_exception(): + # Rollback db operation + super(ControllerL3ServicePlugin, self).remove_router_interface( + _context, router_id, interface_info) + return result + + def remove_router_interface(self, _context, router_id, interface_info): + self.remove_router_interface_precommit(_context, router_id, + interface_info) + res = super(ControllerL3ServicePlugin, self).remove_router_interface( + _context, router_id, interface_info) + self.controllerThread.l3_r_app.notify_sync() + return res + + def setup_vrouter_arp_responder(self, _context, br, action, table_id, + segmentation_id, net_uuid, mac_address, + ip_address): + + topic_port_update = topics.get_topic_name(topics.AGENT, + topics.PORT, + topics.UPDATE) + target = messaging.Target(topic=topic_port_update) + rpcapi = n_rpc.get_client(target) + rpcapi.cast(_context, + 'setup_entry_for_arp_reply_remote', + br_id="br-int", + action=action, + table_id=table_id, + segmentation_id=segmentation_id, + net_uuid=net_uuid, + mac_address=mac_address, + ip_address=ip_address) + + def update_agent_port_mapping_done( + self, _context, agent_id, ip_address, host=None): + LOG.debug(("::agent agent <%s> on ip <%s> host <%s> "), + agent_id, + ip_address, + host) + self.send_set_controllers_upadte(_context, False) + + def send_set_controllers_upadte(self, _context, force_reconnect): + + topic_port_update = topics.get_topic_name(topics.AGENT, + topics.PORT, + topics.UPDATE) + target = messaging.Target(topic=topic_port_update) + rpcapi = n_rpc.get_client(target) + iplist = cfg.CONF.L3controller_ip_list + rpcapi.cast(_context, + 'set_controller_for_br', + br_id="br-int", + ip_address_list=iplist, + force_reconnect=force_reconnect, + protocols="OpenFlow13") + + +class ControllerRunner(threading.Thread): + + def __init__(self, controllertype): + super(ControllerRunner, self).__init__() + self.controllertype = controllertype + self.ctx = context.get_admin_context() + self.hostname = utils.get_hostname() + self.agent_state = { + 'binary': 'neutron-l3-agent', + 'host': self.hostname, + 'topic': topics.L3_AGENT, + 'configurations': { + 'agent_mode': 'legacy', + 'use_namespaces': True, + 'router_id': 1, + 'handle_internal_only_routers': True, + 'external_network_bridge': 'br-ex', + 'gateway_external_network_id': '', + 'interface_driver': "OpenFlow"}, + 'start_flag': True, + 'agent_type': L3_SDN_AGNET_TYPE} + self.l3_rpc = l3_rpc.L3RpcCallback() + self.sync_active_state = False + self.sync_all = True + self.l3_r_app = None + self.heartbeat = None + self.open_flow_hand = None + + def start(self): + app_mgr = AppManager.get_instance() + LOG.debug(("running ryu openflow Controller lib ")) + self.open_flow_hand = app_mgr.instantiate(OFPHandler, None, None) + self.open_flow_hand.start() + self.l3_r_app = app_mgr.instantiate(L3ReactiveApp, None, None) + self.l3_r_app.start() + ''' TODO fix this is hack to let the scheduler schedule the virtual + router to L3 SDN app so this app will be in teh Agnet table as active + Will be change when we convert this implementation to Service + Plugin ----> l3 SDN agent for scalability Currently runs as tread + will be converted to run as a standalone agent + ''' + self.heartbeat = loopingcall.FixedIntervalLoopingCall( + self._report_state_and_bind_routers) + self.heartbeat.start(interval=30) + + def _report_state_and_bind_routers(self): + if self.sync_all: + l3plugin = manager.NeutronManager.get_service_plugins().get( + constants.L3_ROUTER_NAT) + l3plugin.send_set_controllers_upadte(self.ctx, True) + self.sync_all = False + plugin = manager.NeutronManager.get_plugin() + plugin.create_or_update_agent(self.ctx, self.agent_state) + self.bind_unscheduled_routers() + if not self.sync_active_state: + self.update_deviceup_on_all_vr_ports() + self.sync_active_state = True + + def bind_unscheduled_routers(self): + l3plugin = manager.NeutronManager.get_service_plugins().get( + constants.L3_ROUTER_NAT) + unscheduled_routers = [] + routers = l3plugin.get_routers(self.ctx, filters={}) + for router in routers: + l3_agents = l3plugin.get_l3_agents_hosting_routers( + self.ctx, [router['id']], admin_state_up=True) + + if l3_agents: + LOG.debug(('Router %(router_id)s has already been ' + 'hosted by L3 agent %(agent_id)s'), + {'router_id': router['id'], + 'agent_id': l3_agents[0]['id']}) + else: + unscheduled_routers.append(router) + + if unscheduled_routers: + + l3_agent = l3plugin.get_enabled_agent_on_host( + self.ctx, L3_SDN_AGNET_TYPE, utils.get_hostname()) + if l3_agent: + self.router_scheduler.bind_routers( + self.ctx, l3plugin, unscheduled_routers, l3_agent) + LOG.debug('Router %(router_id)s scheduled ' + 'to L3 SDN agent %(agent_id)s.', + {'agent_id': l3_agent.id, + 'router_id': unscheduled_routers}) + # Update Port binbding + + self.l3_rpc._ensure_host_set_on_ports( + self.ctx, utils.get_hostname(), routers) + else: + LOG.error(("could not find fake l3 agent for L3 SDN app can" + "not schedule router id %(router_ids)s"), + {'router_ids': unscheduled_routers}) + + def update_deviceup_on_all_vr_ports(self): + + l3plugin = manager.NeutronManager.get_service_plugins().get( + constants.L3_ROUTER_NAT) + routers = l3plugin.get_sync_data(self.ctx) + for router in routers: + for interface in router.get(q_const.INTERFACE_KEY, []): + self.update_device_up(interface) + + def update_device_up_by_port_id(self, port_id): + + plugin = manager.NeutronManager.get_plugin() + port = plugin._get_port(self.ctx, port_id) + self.update_device_up(port) + + def update_device_up(self, port): + plugin = manager.NeutronManager.get_plugin() + #plugin.update_device_up(self.ctx, device) + self.l3_rpc._ensure_host_set_on_port( + self.ctx, utils.get_hostname(), port) + plugin.update_port_status(self.ctx, port['id'], + q_const.PORT_STATUS_ACTIVE, + utils.get_hostname()) diff --git a/neutron/services/l3_router/l3_reactive_app.py b/neutron/services/l3_router/l3_reactive_app.py new file mode 100755 index 0000000..3dcd928 --- /dev/null +++ b/neutron/services/l3_router/l3_reactive_app.py @@ -0,0 +1,1274 @@ +# Copyright (c) 2014 OpenStack Foundation. +# 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 +import struct +import threading + +import time + +from ryu.base import app_manager +from ryu.controller.handler import CONFIG_DISPATCHER +from ryu.controller.handler import MAIN_DISPATCHER +from ryu.controller.handler import set_ev_cls +from ryu.controller import ofp_event +from ryu.ofproto import ether +from ryu.ofproto.ether import ETH_TYPE_8021Q +from ryu.ofproto import ofproto_v1_3 + +from ryu.lib.packet import ethernet +from ryu.lib.packet import packet + +from ryu.lib.mac import haddr_to_bin +from ryu.lib.packet import icmp +from ryu.lib.packet import ipv4 +from ryu.lib.packet import tcp +from ryu.lib.packet import udp +from ryu.lib.packet import vlan + +from ryu.lib import addrconv + +from neutron.openstack.common import log +from neutron.plugins.ml2 import driver_api as api + +from neutron import context +from neutron import manager +from neutron.plugins.common import constants as service_constants + + +LOG = log.getLogger(__name__) + +ETHERNET = ethernet.ethernet.__name__ +VLAN = vlan.vlan.__name__ +IPV4 = ipv4.ipv4.__name__ +ICMP = icmp.icmp.__name__ +TCP = tcp.tcp.__name__ +UDP = udp.udp.__name__ + +VLANID_NONE = 0 +VLANID_MIN = 2 +VLANID_MAX = 4094 +COOKIE_SHIFT_VLANID = 32 +UINT16_MAX = 0xffff +UINT32_MAX = 0xffffffff +UINT64_MAX = 0xffffffffffffffff +OFPFW_NW_PROTO = 1 << 5 + +HIGH_PRIOREITY_FLOW = 1000 +MEDIUM_PRIOREITY_FLOW = 100 +NORMAL_PRIOREITY_FLOW = 10 +LOW_PRIOREITY_FLOW = 1 +LOWEST_PRIOREITY_FLOW = 0 + + +# A class to represent a forwarding Elemnt Switch local state +class AgentDatapath(object): + + def __init__(self): + self.local_vlan_mapping = {} + self.local_ports = None + self.datapath = 0 + self.patch_port_num = 0 + + +# A class to represent tenat toplogie +class TenantTopo(object): + + def __init__(self): + self.nodes = set() + self.edges = collections.defaultdict(list) + self.routers = [] + self.distances = {} + self.mac_to_port_data = collections.defaultdict(set) + self.tenant_id = None + #self.segmentation_id = None + + def add_router(self, router, r_id): + self.routers.append(router) + + def add_node(self, value): + self.nodes.add(value) + + def add_edge(self, from_node, to_node, distance): + self.edges[from_node].append(to_node) + self.edges[to_node].append(from_node) + self.distances[(from_node, to_node)] = distance + + # we need dijsktra only for extra route + def dijsktra(self, graph, initial): + visited = {initial: 0} + path = {} + + nodes = set(graph.nodes) + + while nodes: + min_node = None + for node in nodes: + if node in visited: + if min_node is None: + min_node = node + elif visited[node] < visited[min_node]: + min_node = node + + if min_node is None: + break + + nodes.remove(min_node) + current_weight = visited[min_node] + + for edge in graph.edges[min_node]: + weight = current_weight + graph.distance[(min_node, edge)] + if edge not in visited or weight < visited[edge]: + visited[edge] = weight + path[edge] = min_node + + return visited, path + + +class Router(object): + + def __init__(self, data): + self.data = data + #self.subnets = defaultdict(list) + self.subnets = [] + + def add_subnet(self, subnet, sub_id): + self.subnets.append(subnet) + + +class Subnet(object): + + def __init__(self, data, port_data, segmentation_id): + self.data = data + self.port_data = port_data + self.segmentation_id = segmentation_id + + +class L3ReactiveApp(app_manager.RyuApp): + OFP_VERSIONS = [ofproto_v1_3.OFP_VERSION] + #OFP_VERSIONS = [ofproto_v1_2.OFP_VERSION] + #OFP_VERSIONS = [ofproto_v1_0.OFP_VERSION] + BASE_RPC_API_VERSION = '1.0' + + BASE_TABLE = 0 + CLASSIFIER_TABLE = 40 + METADATA_TABLE_ID = 50 + ARP_AND_BR_TABLE = 51 + L3_VROUTER_TABLE = 52 + + def __init__(self, *args, **kwargs): + super(L3ReactiveApp, self).__init__(*args, **kwargs) + self.mac_to_port = {} + + self.ctx = context.get_admin_context() + self.lock = threading.Lock() + self.tenants = collections.defaultdict(lambda: None) + self.need_sync = True + self.dp_list = {} + + def start(self): + super(L3ReactiveApp, self).start() + return 1 + + def create_router(self, *args, **kwargs): + self.logger.info("l3ReactiveApp create_router") + # self.sync_data() + + def notify_sync(self): + self.need_sync = True + for dpid in self.dp_list: + datapath = self.dp_list[dpid].datapath + self.send_features_request(datapath) + self.send_flow_stats_request( + datapath, table=self.METADATA_TABLE_ID) + + def sync_data(self): + self.logger.info(" l3_reactive_app sync router data ") + + self.lock.acquire() + # Lock + self.logger.debug('l3_reactive_app in the critical section the lock') + if self.need_sync: + l3plugin = manager.NeutronManager.get_service_plugins().get( + service_constants.L3_ROUTER_NAT) + + routers = l3plugin.get_sync_data(self.ctx, None) + self.core_plugin = manager.NeutronManager.get_plugin() + self.get_router_subnets(routers) + self.need_sync = False + del l3plugin + del self.core_plugin + self.core_plugin = None + self.logger.debug('l3_reactive_app releasing the lock') + # Release + self.lock.release() + + def get_router_subnets(self, router_data): + for router in router_data: + tenant_id = router['tenant_id'] + if not router['tenant_id'] in self.tenants: + self.tenants[router['tenant_id']] = TenantTopo() + tenant_topo = self.tenants[router['tenant_id']] + tenant_topo.tenant_id = tenant_id + router_cls = Router(router) + tenant_topo.add_router(router_cls, router['id']) + if "_interfaces" in router: + for interface in router['_interfaces']: + ports_data = self.get_ports_by_subnet( + interface['subnet']['id']) + segmentation_id = None + for device in ports_data: + port = self.get_port_bond_data( + self.ctx, device['id'], device['binding:host_id']) + if "mac_address" in port: + tenant_topo.mac_to_port_data[ + port['mac_address']] = port + segmentation_id = port['segmentation_id'] + else: + if (device['device_owner'] == + 'network:router_interface'): + # if this a router then bind it to our + # application + + LOG.error(("No binding for router %s"), device) + # tenant_topo.add_router(router,router['id']) + subnet_cls = Subnet(interface['subnet'], ports_data, + segmentation_id) + router_cls.add_subnet( + subnet_cls, interface['subnet']['id']) + + def get_port_bond_data(self, ctx, port_id, device_id): + port_context = self.core_plugin.get_bound_port_context( + ctx, port_id, device_id) + if not port_context: + LOG.warning(("Device %(device)s requested by agent " + "%(agent_id)s not found in database"), + {'device': device_id, 'agent_id': port_id}) + return {'device': device_id} + + segment = port_context.bound_segment + port = port_context.current + + if not segment: + LOG.warning(("Device %(device)s requested by agent " + " on network %(network_id)s not " + "bound, vif_type: "), + {'device': device_id, + 'network_id': port['network_id']}) + return {'device': device_id} + + entry = {'device': device_id, + 'network_id': port['network_id'], + 'port_id': port_id, + 'mac_address': port['mac_address'], + 'admin_state_up': port['admin_state_up'], + 'network_type': segment[api.NETWORK_TYPE], + 'segmentation_id': segment[api.SEGMENTATION_ID], + 'physical_network': segment[api.PHYSICAL_NETWORK], + 'fixed_ips': port['fixed_ips'], + 'device_owner': port['device_owner']} + LOG.debug(("Returning: %s"), entry) + return entry + + def get_ports_by_subnet(self, subnet_id): + filters = {'fixed_ips': {'subnet_id': [subnet_id]}} + return self.core_plugin.get_ports(self.ctx, filters=filters) + + @set_ev_cls(ofp_event.EventOFPPacketIn, MAIN_DISPATCHER) + def OF_packet_in_handler(self, ev): + if self.need_sync == 1: + self.sync_data() + msg = ev.msg + datapath = msg.datapath + ofproto = datapath.ofproto + if msg.reason == ofproto.OFPR_NO_MATCH: + reason = 'NO MATCH' + elif msg.reason == ofproto.OFPR_ACTION: + reason = 'ACTION' + elif msg.reason == ofproto.OFPR_INVALID_TTL: + reason = 'INVALID TTL' + else: + reason = 'unknown' + + LOG.debug('OFPPacketIn received: ' + 'buffer_id=%x total_len=%d reason=%s ' + 'table_id=%d cookie=%d match=%s', + msg.buffer_id, msg.total_len, reason, + msg.table_id, msg.cookie, msg.match) + # utils.hex_array(msg.data)) + in_port = msg.match['in_port'] + + pkt = packet.Packet(msg.data) + eth = pkt.get_protocols(ethernet.ethernet)[0] + + header_list = dict((p.protocol_name, p) + for p in pkt.protocols if not isinstance(p, str)) + if header_list: + try: + if "ipv4" in header_list: + self.handle_ipv4_packet_in( + datapath, + msg, + in_port, + header_list, + pkt, + eth) + return + if "ipv6" in header_list: + self.handle_ipv6_packet_in( + datapath, in_port, header_list, pkt, eth) + return + except Exception as exception: + + LOG.debug("Unable to handle packet %(msg): %(e)s", + {'msg': msg, 'e': exception}) + + LOG.error((">>>>>>>>>> Unhandled Packet>>>>> %s", pkt)) + + def handle_ipv6_packet_in(self, datapath, in_port, header_list, + pkt, eth): + # TODO(gampel)(gampel) add ipv6 support + LOG.error(("No handle for ipv6 yet should be offload to the" + "NORMAL path %s", pkt)) + return + + def handle_ipv4_packet_in(self, datapath, msg, in_port, header_list, pkt, + eth): + pkt_ipv4 = header_list['ipv4'] + pkt_ethernet = header_list['ethernet'] + + # Check vlan-tag + if VLAN in header_list: + vlan_id = header_list[VLAN].vid + self.logger.info("handle_ipv4_packet_in:: VLANID %s ", vlan_id) + switch = self.dp_list.get(datapath.id) + if switch: + if vlan_id not in switch.local_vlan_mapping: + # send request for loacl switch data + # self.send_port_desc_stats_request(datapath) + self.send_flow_stats_request( + datapath, table=self.METADATA_TABLE_ID) + LOG.error(("No local switch vlan mapping for vlan %s"), + vlan_id) + return + self.logger.info( + "packet segmentation_id %s ", + switch.local_vlan_mapping[vlan_id]) + segmentation_id = switch.local_vlan_mapping[vlan_id] + for tenantid in self.tenants: + tenant = self.tenants[tenantid] + for router in tenant.routers: + for subnet in router.subnets: + if segmentation_id == subnet.segmentation_id: + self.logger.info("packet from to tenant %s ", + tenant.tenant_id) + in_port_data = self.tenants[ + tenantid].mac_to_port_data[eth.src] + out_port_data = self.tenants[ + tenantid].mac_to_port_data[eth.dst] + LOG.debug(('Source port data <--- %s ', + in_port_data)) + LOG.debug(('Router Mac dest port data -> %s ', + out_port_data)) + if self.handle_router_interface(datapath, + in_port, + out_port_data, + pkt, + pkt_ethernet, + pkt_ipv4) == 1: + # trafic to the virtual routre handle only + # ping + return + (dst_p_data, + dst_sub_id) = self.get_port_data(tenant, + pkt_ipv4.dst) + for _subnet in router.subnets: + if dst_sub_id == _subnet.data['id']: + out_subnet = _subnet + subnet_gw = out_subnet.data[ + 'gateway_ip'] + + (dst_gw_port_data, + dst_gw_sub_id) = self.get_port_data( + tenant, subnet_gw) + + if self.handle_router_interface( + datapath, + in_port, + dst_gw_port_data, + pkt, + pkt_ethernet, + pkt_ipv4) == 1: + # this trafic to the virtual routre + return + if not dst_p_data: + LOG.error(("No local switch" + "mapping for %s"), + pkt_ipv4.dst) + return + if self.handle_router_interface( + datapath, + in_port, + dst_p_data, + pkt, + pkt_ethernet, + pkt_ipv4) != -1: + # case for vrouter that is not the + #gw and we are trying to ping + # this trafic to the virtual routre + return + + LOG.debug(("Route from %s to %s" + "exist installing flow ", + pkt_ipv4.src, + pkt_ipv4.dst)) + dst_vlan = self.get_l_vid_from_seg_id( + switch, + out_subnet.segmentation_id) + self.install_l3_forwarding_flows( + datapath, + msg, + in_port_data, + in_port, + vlan_id, + eth, + pkt_ipv4, + dst_gw_port_data, + dst_p_data, + dst_vlan) + return + + def install_l3_forwarding_flows( + self, + datapath, + msg, + in_port_data, + in_port, + vlan_id, + eth, + pkt_ipv4, + dst_gw_port_data, + dst_p_data, + dst_vlan): + if dst_p_data['local_dpid_switch'] == datapath.id: + # The dst VM and the source VM are on the same copute Node + # Send output flow directly to port iuse the same datapath + actions = self.add_flow_subnet_traffic( + datapath, + self.L3_VROUTER_TABLE, + MEDIUM_PRIOREITY_FLOW, + in_port, + vlan_id, + eth.src, + eth.dst, + pkt_ipv4.dst, + pkt_ipv4.src, + dst_gw_port_data['mac_address'], + dst_p_data['mac_address'], + dst_p_data['local_port_num']) + # Install the reverse flow return traffic + self.add_flow_subnet_traffic(datapath, + self.L3_VROUTER_TABLE, + MEDIUM_PRIOREITY_FLOW, + dst_p_data['local_port_num'], + dst_vlan, + dst_p_data['mac_address'], + dst_gw_port_data['mac_address'], + pkt_ipv4.src, + pkt_ipv4.dst, + eth.dst, + in_port_data['mac_address'], + in_port_data['local_port_num']) + self.handle_packet_out_l3(datapath, msg, in_port, actions) + else: + # The dst VM and the source VM are NOT on the same copute Node + # Send output to br-tun patch port and install reverse flow on the + # dst compute node + remoteSwitch = self.dp_list.get(dst_p_data['local_dpid_switch']) + localSwitch = self.dp_list.get(datapath.id) + actions = self.add_flow_subnet_traffic(datapath, + self.L3_VROUTER_TABLE, + MEDIUM_PRIOREITY_FLOW, + in_port, + vlan_id, + eth.src, + eth.dst, + pkt_ipv4.dst, + pkt_ipv4.src, + dst_gw_port_data[ + 'mac_address'], + dst_p_data[ + 'mac_address'], + localSwitch.patch_port, + dst_vlan) + # Remote reverse flow install + self.add_flow_subnet_traffic(remoteSwitch.datapath, + self.L3_VROUTER_TABLE, + MEDIUM_PRIOREITY_FLOW, + dst_p_data['local_port_num'], + dst_vlan, + dst_p_data['mac_address'], + dst_gw_port_data['mac_address'], + pkt_ipv4.src, + pkt_ipv4.dst, + eth.dst, + in_port_data['mac_address'], + in_port_data['local_port_num'], + vlan_id) + self.handle_packet_out_l3(datapath, msg, in_port, actions) + + def handle_packet_out_l3(self, datapath, msg, in_port, actions): + data = None + + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + if msg.buffer_id == ofproto.OFP_NO_BUFFER: + data = msg.data + out = parser.OFPPacketOut(datapath=datapath, buffer_id=msg.buffer_id, + in_port=in_port, actions=actions, data=data) + datapath.send_msg(out) + + def add_flow_subnet_traffic(self, datapath, table, priority, in_port, + match_vlan, match_src_mac, match_dst_mac, + match_dst_ip, match_src_ip, src_mac, + dst_mac, out_port_num, dst_vlan=None): + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + match = parser.OFPMatch() + # BUG in the Ryu lib constructor match do not work + match.set_dl_type(0x0800) + match.set_in_port(in_port) + match.set_dl_src(haddr_to_bin(match_src_mac)) + match.set_dl_dst(haddr_to_bin(match_dst_mac)) + match.set_ipv4_src(ipv4_text_to_int(str(match_src_ip))) + match.set_ipv4_dst(ipv4_text_to_int(str(match_dst_ip))) + match.set_vlan_vid(0x1000 | match_vlan) + actions = [parser.OFPActionPopVlan()] + actions.append(parser.OFPActionDecNwTtl()) + actions.append(parser.OFPActionSetField(eth_src=src_mac)) + actions.append(parser.OFPActionSetField(eth_dst=dst_mac)) + actions.append(parser.OFPActionOutput(out_port_num, + ofproto.OFPCML_NO_BUFFER)) + if dst_vlan: + field = parser.OFPMatchField.make( + ofproto.OXM_OF_VLAN_VID, dst_vlan) + actions.append(parser.OFPActionPushVlan(ETH_TYPE_8021Q)) + actions.append(parser.OFPActionSetField(field)) + + ofproto = datapath.ofproto + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + self.mod_flow( + datapath, + inst=inst, + table_id=table, + priority=priority, + match=match) + + return actions + + def add_flow_pop_vlan_to_normal(self, datapath, table, priority, vlan_id): + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + match = parser.OFPMatch(vlan_vid=0x1000 | vlan_id) + #match = parser.OFPMatch(vlan_pcp=0) + actions = [ + parser.OFPActionPopVlan(), + parser.OFPActionOutput( + ofproto.OFPP_NORMAL, + ofproto.OFPCML_NO_BUFFER)] + ofproto = datapath.ofproto + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + self.mod_flow( + datapath, + inst=inst, + table_id=table, + priority=priority, + match=match) + + def add_flow_normal_local_subnet( + self, datapath, table, priority, dst_net, dst_mask, vlan_id): + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + #match = parser.OFPMatch(vlan_vid=0x1000| vlan_id) + match = parser.OFPMatch(vlan_vid=0x1000 | vlan_id) + match.set_dl_type(0x0800) + match.set_vlan_vid(0x1000 | vlan_id) + match.set_ipv4_dst_masked(ipv4_text_to_int(str(dst_net)), + mask_ntob(int(dst_mask))) + #match = parser.OFPMatch(vlan_pcp=0) + actions = [ + parser.OFPActionPopVlan(), + parser.OFPActionOutput( + ofproto.OFPP_NORMAL)] + # actions = [parser.OFPActionOutput(ofproto.OFPP_NORMAL, + # ofproto.OFPCML_NO_BUFFER)] + ofproto = datapath.ofproto + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + self.mod_flow( + datapath, + inst=inst, + table_id=table, + priority=priority, + match=match) + + def add_flow_normal_by_port_num(self, datapath, table, priority, in_port): + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + match = parser.OFPMatch(in_port=in_port) + #match = parser.OFPMatch(vlan_pcp=0) + actions = [parser.OFPActionOutput(ofproto.OFPP_NORMAL)] + ofproto = datapath.ofproto + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + self.mod_flow( + datapath, + inst=inst, + table_id=table, + priority=priority, + match=match) + + def add_flow_push_vlan_by_port_num( + self, datapath, table, priority, in_port, dst_vlan, goto_table): + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + match = parser.OFPMatch() + match.set_in_port(in_port) + field = parser.OFPMatchField.make( + ofproto.OXM_OF_VLAN_VID, 0x1000 | dst_vlan) + actions = [datapath. ofproto_parser. OFPActionPushVlan( + ETH_TYPE_8021Q), datapath.ofproto_parser.OFPActionSetField(field)] + goto_inst = parser.OFPInstructionGotoTable(goto_table) + ofproto = datapath.ofproto + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions), goto_inst] + self.mod_flow( + datapath, + inst=inst, + table_id=table, + priority=priority, + match=match) + + def delete_all_flow_from_table(self, datapath, table_id): + + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + match = parser.OFPMatch() + instructions = [] + flow_mod = datapath.ofproto_parser.OFPFlowMod( + datapath, + 0, + 0, + table_id, + ofproto.OFPFC_DELETE, + 0, + 0, + 1, + ofproto.OFPCML_NO_BUFFER, + ofproto.OFPP_ANY, + ofproto.OFPG_ANY, + 0, + match, + instructions) + datapath.send_msg(flow_mod) + + def add_flow_normal(self, datapath, table, priority): + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + match = parser.OFPMatch(vlan_vid=0x1000) + #match = parser.OFPMatch(vlan_pcp=0) + actions = [ + parser.OFPActionPopVlan(), + parser.OFPActionOutput( + ofproto.OFPP_NORMAL)] + ofproto = datapath.ofproto + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + self.mod_flow( + datapath, + inst=inst, + table_id=table, + priority=priority, + match=match) + + def mod_flow(self, datapath, cookie=0, cookie_mask=0, table_id=0, + command=None, idle_timeout=0, hard_timeout=0, + priority=0xff, buffer_id=0xffffffff, match=None, + actions=None, inst_type=None, out_port=None, + out_group=None, flags=0, inst=None): + + if command is None: + command = datapath.ofproto.OFPFC_ADD + + if inst is None: + if inst_type is None: + inst_type = datapath.ofproto.OFPIT_APPLY_ACTIONS + + inst = [] + if actions is not None: + inst = [datapath.ofproto_parser.OFPInstructionActions( + inst_type, actions)] + + if match is None: + match = datapath.ofproto_parser.OFPMatch() + + if out_port is None: + out_port = datapath.ofproto.OFPP_ANY + + if out_group is None: + out_group = datapath.ofproto.OFPG_ANY + + message = datapath.ofproto_parser.OFPFlowMod(datapath, cookie, + cookie_mask, + table_id, command, + idle_timeout, + hard_timeout, + priority, + buffer_id, + out_port, + out_group, + flags, + match, + inst) + + datapath.send_msg(message) + + def add_flow_go_to_table2(self, datapath, table, priority, + goto_table_id, match=None): + inst = [datapath.ofproto_parser.OFPInstructionGotoTable(goto_table_id)] + self.mod_flow(datapath, inst=inst, table_id=table, priority=priority, + match=match) + + def add_flow_goto_table_on_broad(self, datapath, table, priority, + goto_table_id): + match = datapath.ofproto_parser.OFPMatch(eth_dst='ff:ff:ff:ff:ff:ff') + + self.add_flow_go_to_table2(datapath, table, priority, goto_table_id, + match) + + def add_flow_goto_table_on_mcast(self, datapath, table, priority, + goto_table_id): + #ofproto = datapath.ofproto + match = datapath.ofproto_parser.OFPMatch(eth_dst='01:00:00:00:00:00') + addint = haddr_to_bin('01:00:00:00:00:00') + match.set_dl_dst_masked(addint, addint) + self.add_flow_go_to_table2(datapath, table, priority, goto_table_id, + match) + + def add_flow_go_to_table_on_arp(self, datapath, table, priority, + goto_table_id): + match = datapath.ofproto_parser.OFPMatch(eth_type=0x0806) + self.add_flow_go_to_table2(datapath, table, priority, goto_table_id, + match) + + def add_flow_go_to_table(self, datapath, table, priority, goto_table_id): + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + match = parser.OFPMatch() + actions = [parser.OFPInstructionGotoTable(goto_table_id)] + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + mod = datapath.ofproto_parser.OFPFlowMod( + datapath=datapath, cookie=0, cookie_mask=0, table_id=table, + command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, + priority=priority, buffer_id=ofproto.OFP_NO_BUFFER, + out_port=ofproto.OFPP_ANY, + out_group=ofproto.OFPG_ANY, + flags=0, match=match, instructions=inst) + datapath.send_msg(mod) + + def add_flow_match_to_controller(self, datapath, table, priority, + match=None, _actions=None): + + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + + ofproto = datapath.ofproto + actions = [parser.OFPActionOutput(ofproto.OFPP_CONTROLLER, + ofproto.OFPCML_NO_BUFFER)] + + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + self.mod_flow( + datapath, + inst=inst, + table_id=table, + priority=priority, + match=match) + + def add_flow_match_gw_mac_to_cont(self, datapath, dst_mac, table, + priority, vlan_vid=None, + _actions=None): + parser = datapath.ofproto_parser + #ofproto = datapath.ofproto + vlan_id = 0x1000 | vlan_vid + match = parser.OFPMatch(eth_dst=dst_mac, vlan_vid=vlan_id) + + self.add_flow_match_to_controller( + datapath, table, priority, match=match, _actions=_actions) + + def add_flow_l3(self, datapath, in_port, dst_mac, src_mac, vlan_vid, + actions): + ofproto = datapath.ofproto + + match = datapath.ofproto_parser.OFPMatch(in_port=in_port, + eth_dst=dst_mac, + eth_src=src_mac, + vlan_vid=vlan_vid) + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + + mod = datapath.ofproto_parser.OFPFlowMod( + datapath=datapath, cookie=0, cookie_mask=0, table_id=0, + command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, + priority=0, buffer_id=ofproto.OFP_NO_BUFFER, + out_port=ofproto.OFPP_ANY, + out_group=ofproto.OFPG_ANY, + flags=0, match=match, instructions=inst) + datapath.send_msg(mod) + + def add_flow(self, datapath, port, dst, actions): + ofproto = datapath.ofproto + + match = datapath.ofproto_parser.OFPMatch(in_port=port, + eth_dst=dst) + inst = [datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)] + + mod = datapath.ofproto_parser.OFPFlowMod( + datapath=datapath, cookie=0, cookie_mask=0, table_id=0, + command=ofproto.OFPFC_ADD, idle_timeout=0, hard_timeout=0, + priority=0, buffer_id=ofproto.OFP_NO_BUFFER, + out_port=ofproto.OFPP_ANY, + out_group=ofproto.OFPG_ANY, + flags=0, match=match, instructions=inst) + datapath.send_msg(mod) + + @set_ev_cls(ofp_event.EventOFPPortStatus, MAIN_DISPATCHER) + def _port_status_handler(self, ev): + msg = ev.msg + reason = msg.reason + port_no = msg.desc.port_no + datapath = ev.msg.datapath + + ofproto = msg.datapath.ofproto + if reason == ofproto.OFPPR_ADD: + self.logger.info("port added %s", port_no) + elif reason == ofproto.OFPPR_DELETE: + self.logger.info("port deleted %s", port_no) + elif reason == ofproto.OFPPR_MODIFY: + self.logger.info("port modified %s", port_no) + else: + self.logger.info("Illeagal port state %s %s", port_no, reason) + # TODO(gampel) Currently we update all the agents on modification + LOG.info((" Updating flow table on agents got port update ")) + + switch = self.dp_list.get(datapath.id) + if switch: + self.send_flow_stats_request( + datapath, table=self.METADATA_TABLE_ID) + + @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) + def switch_features_handler(self, ev): + datapath = ev.msg.datapath + + if self.need_sync: + self.sync_data() + switch = self.dp_list.get(datapath.id) + if not switch: + self.dp_list[datapath.id] = AgentDatapath() + self.dp_list[datapath.id].datapath = datapath + + LOG.info(("Wait:: Let agent insert the meta data table ")) + time.sleep(1) # sleep during 500ms + LOG.info(("Done Wait .. will retry if meta table not set ")) + + self.send_flow_stats_request(datapath, table=self.METADATA_TABLE_ID) + # --> meta + #self.add_flow_go_to_table2(datapath, 0, 1 ,self.L3_VROUTER_TABLE) + # main table 0 to Arp On ARp or broadcat or multicast + self.add_flow_go_to_table_on_arp( + datapath, + self.CLASSIFIER_TABLE, + NORMAL_PRIOREITY_FLOW, + self.ARP_AND_BR_TABLE) + self.add_flow_goto_table_on_broad( + datapath, + self.CLASSIFIER_TABLE, + MEDIUM_PRIOREITY_FLOW, + self.ARP_AND_BR_TABLE) + self.add_flow_goto_table_on_mcast( + datapath, + self.CLASSIFIER_TABLE, + NORMAL_PRIOREITY_FLOW, + self.ARP_AND_BR_TABLE) + + # Meta Table to L3 router table on all other trafic + self.add_flow_go_to_table2( + datapath, self.METADATA_TABLE_ID, 1, self.L3_VROUTER_TABLE) + + # Normal flow on arp table in low priorety + self.add_flow_normal(datapath, self.ARP_AND_BR_TABLE, 1) + #del l3plugin + + def send_port_desc_stats_request(self, datapath): + ofp_parser = datapath.ofproto_parser + + req = ofp_parser.OFPPortDescStatsRequest(datapath, 0) + datapath.send_msg(req) + + @set_ev_cls(ofp_event.EventOFPPortDescStatsReply, MAIN_DISPATCHER) + def port_desc_stats_reply_handler(self, ev): + ports = [] + datapath = ev.msg.datapath + switch = self.dp_list.get(datapath.id) + self.delete_all_flow_from_table(datapath, self.BASE_TABLE) + for port in ev.msg.body: + ports.append('port_no=%d hw_addr=%s name=%s config=0x%08x ' + 'state=0x%08x curr=0x%08x advertised=0x%08x ' + 'supported=0x%08x peer=0x%08x curr_speed=%d ' + 'max_speed=%d' % + (port.port_no, port.hw_addr, + port.name, port.config, + port.state, port.curr, port.advertised, + port.supported, port.peer, port.curr_speed, + port.max_speed)) + + if "tap" in port.name: + LOG.debug(("Found DHCPD port %s using MAC %s" + "One machine install Special" + "(One Machine set up ) test use case"), + port.name, + port.hw_addr) + self.add_flow_normal_by_port_num( + datapath, 0, HIGH_PRIOREITY_FLOW, port.port_no) + elif "qvo" in port.name: + # this is a VM port start with qvo update the port + # data with the port num and the switch dpid + (port_id, mac, segmentation_id) = self.update_local_port_num( + port.name, port.port_no, datapath.id) + vlan_id = self.get_l_vid_from_seg_id(switch, segmentation_id) + LOG.debug(("Found VM port %s using MAC %s %d"), + port.name, port.hw_addr, datapath.id) + if vlan_id: + self.add_flow_push_vlan_by_port_num(datapath, + 0, + HIGH_PRIOREITY_FLOW, + port.port_no, + vlan_id, + self.CLASSIFIER_TABLE) + else: + LOG.error(("No local switch vlan mapping for port" + " %s on %d Sending to Normal PATH "), + port.name, + datapath.id) + self.add_flow_normal_by_port_num(datapath, 0, + HIGH_PRIOREITY_FLOW, + port.port_no) + elif "patch-tun" in port.name: + LOG.debug(("Found br-tun patch port " + "%s %s sending to NORMAL path"), + port.name, + port.hw_addr) + switch.patch_port_num = port.port_no + self.add_flow_normal_by_port_num( + datapath, 0, HIGH_PRIOREITY_FLOW, port.port_no) + self.logger.debug('OFPPortDescStatsReply received: %s', ports) + switch.local_ports = ports + self.add_flow_go_to_table2(datapath, 0, 1, self.CLASSIFIER_TABLE) + self.add_flow_match_to_controller(datapath, self.L3_VROUTER_TABLE, 0) + self.add_flow_go_to_table2( + datapath, self.CLASSIFIER_TABLE, 1, self.L3_VROUTER_TABLE) + + def send_features_request(self, datapath): + ofp_parser = datapath.ofproto_parser + + req = ofp_parser.OFPFeaturesRequest(datapath) + datapath.send_msg(req) + + def _send_packet(self, datapath, port, pkt): + ofproto = datapath.ofproto + parser = datapath.ofproto_parser + pkt.serialize() + self.logger.info("packet-out %s" % (pkt,)) + data = pkt.data + actions = [parser.OFPActionOutput(port=port)] + out = parser.OFPPacketOut(datapath=datapath, + buffer_id=ofproto.OFP_NO_BUFFER, + in_port=ofproto.OFPP_CONTROLLER, + actions=actions, + data=data) + datapath.send_msg(out) + + def get_l_vid_from_seg_id(self, switch, segmentation_id): + for local_vlan in switch.local_vlan_mapping: + if segmentation_id == switch.local_vlan_mapping[local_vlan]: + return local_vlan + return 0 + + def update_local_port_num(self, port_name, port_num, dpid): + + for tenantid in self.tenants: + tenant = self.tenants[tenantid] + for mac in tenant.mac_to_port_data: + port_data = tenant.mac_to_port_data[mac] + # print "port_data >>>>>>>>>>>>>>%s",port_data + if 'port_id' in port_data: + port_id = port_data['port_id'] + sub_str_port_id = str(port_id[0:11]) + port_id_from_name = port_name[3:] + if sub_str_port_id == port_id_from_name: + port_data['local_port_num'] = port_num + port_data['local_dpid_switch'] = dpid + return ( + port_data['port_id'], + mac, + port_data['segmentation_id']) + else: + LOG.error(("No data in port)data %s "), port_data) + return(0, 0, 0) + + def get_port_data(self, tenant, ip_address): + for mac in tenant.mac_to_port_data: + port_data = tenant.mac_to_port_data[mac] + if 'fixed_ips' in port_data: + for fixed_ips in port_data['fixed_ips']: + if ip_address == fixed_ips['ip_address']: + return (port_data, fixed_ips['subnet_id']) + + return(0, 0) + + @set_ev_cls(ofp_event.EventOFPFlowStatsReply, MAIN_DISPATCHER) + def flow_stats_reply_handler(self, ev): + + datapath = ev.msg.datapath + + if self.need_sync: + self.sync_data() + self.delete_all_flow_from_table(datapath, self.ARP_AND_BR_TABLE) + # TODO(gampel) for the moment we delete all the flows in the + #table TO delete only the relevant flows all ready installed + self.delete_all_flow_from_table(datapath, self.L3_VROUTER_TABLE) + # TODO(gampel) remove ARP responders + flows = [] + for stat in ev.msg.body: + for instruction in stat.instructions: + if hasattr(instruction, 'metadata'): + vlan_int = int(stat.match['vlan_vid']) + if vlan_int > 4096: + vlan_int -= 4096 + + switch = self.dp_list.get(datapath.id) + if switch: + switch.local_vlan_mapping[ + vlan_int] = instruction.metadata + flows.append( + 'table_id=%s ' + 'duration_sec=%d diuration_nsec=%d ' + 'priority=%d ' + 'idle_timeout=%d hard_timeout=%d flags=0x%04x ' + 'cookie=%d packet_count=%d byte_count=%d ' + 'match=%s instructions=%s' + 'vlan_id=%s metadata=%s' % + (stat.table_id, + stat.duration_sec, + stat.duration_nsec, + stat.priority, + stat.idle_timeout, + stat.hard_timeout, + stat.flags, + stat.cookie, + stat.packet_count, + stat.byte_count, + stat.match, + stat.instructions, + stat.match['vlan_vid'], + instruction.metadata)) + l3plugin = manager.NeutronManager.get_service_plugins().get( + service_constants.L3_ROUTER_NAT) + switch = self.dp_list.get(datapath.id) + + for tenantid in self.tenants: + for router in self.tenants[tenantid].routers: + for subnet in router.subnets: + for interface in router.data['_interfaces']: + if interface['subnet']['id'] == subnet.data['id']: + segmentation_id = subnet.segmentation_id + vlan_id = self.get_l_vid_from_seg_id( + switch, segmentation_id) + network, net_mask = self.get_subnet_from_cidr( + subnet.data['cidr']) + + if vlan_id: + self.add_flow_normal_local_subnet( + datapath, + self.L3_VROUTER_TABLE, + NORMAL_PRIOREITY_FLOW, + network, + net_mask, + vlan_id) + + self.add_flow_match_gw_mac_to_cont( + datapath, + interface['mac_address'], + self.L3_VROUTER_TABLE, + 99, + vlan_id) + l3plugin.setup_vrouter_arp_responder( + self.ctx, + "br-int", + "add", + self.ARP_AND_BR_TABLE, + segmentation_id, + interface['network_id'], + interface['mac_address'], + self.get_ip_from_router_interface(interface)) + # No match on table L3_VROUTER_TABLE go to normal flow + # No match on table L3_VROUTER_TABLE go to controller + # Patch to overcome OVS BUG not accepting match on tag vlans + # set Pop per taged vlan + + for local_vlan in switch.local_vlan_mapping: + self.add_flow_pop_vlan_to_normal( + datapath, self.ARP_AND_BR_TABLE, 1, local_vlan) + + if not switch.local_vlan_mapping: + LOG.error(("CRITICAL ERROR ***** Switch did not send local port" + "data dpid == <%s> sending flow req "), + datapath.id) + time.sleep(0.5) # sleep during 500ms + self.send_flow_stats_request( + datapath, table=self.METADATA_TABLE_ID) + else: + self.send_port_desc_stats_request(datapath) + del l3plugin + + def get_ip_from_router_interface(self, interface): + for fixed_ip in interface['fixed_ips']: + if "ip_address" in fixed_ip: + return fixed_ip['ip_address'] + + def is_router_interface(self, port): + if port['device_owner'] == 'network:router_interface': + return True + else: + return False + + def handle_router_interface(self, datapath, in_port, port_data, + pkt, pkt_ethernet, pkt_ipv4): + # retVal -1 -- dst is not a v Router + # retVal 1 -- The request was handled + # retVal 0 -- router interface and the request was not handled + retVal = -1 + if self.is_router_interface(port_data): + # router mac address + retVal = 0 + for fixed_ips in port_data['fixed_ips']: + if pkt_ipv4.dst == fixed_ips['ip_address']: + # The dst ip address is the router Ip address should be + # ping req + pkt_icmp = pkt.get_protocol(icmp.icmp) + if pkt_icmp: + # send ping responce + self._handle_icmp( + datapath, + in_port, + pkt_ethernet, + pkt_ipv4, + pkt_icmp) + LOG.info(("Sending ping echo -> ip %s "), pkt_ipv4.src) + retVal = 1 + else: + LOG.error(("any comunication to a router that" + " is not ping should be dropped from" + "ip %s", + pkt_ipv4.src)) + retVal = 1 + return retVal + + def send_flow_stats_request(self, datapath, table=None): + + ofp = datapath.ofproto + ofp_parser = datapath.ofproto_parser + if table is None: + table = ofp.OFPTT_ALL + cookie = cookie_mask = 0 + match = ofp_parser.OFPMatch() + req = ofp_parser.OFPFlowStatsRequest(datapath, 0, + table, + ofp.OFPP_ANY, ofp.OFPG_ANY, + cookie, cookie_mask, + match) + datapath.send_msg(req) + + def _handle_icmp(self, datapath, port, pkt_ethernet, pkt_ipv4, pkt_icmp): + if pkt_icmp.type != icmp.ICMP_ECHO_REQUEST: + return + pkt = packet.Packet() + pkt.add_protocol(ethernet.ethernet(ethertype=ether.ETH_TYPE_IP, + dst=pkt_ethernet.src, + src=pkt_ethernet.dst)) + pkt.add_protocol(ipv4.ipv4(dst=pkt_ipv4.src, + src=pkt_ipv4.dst, + proto=pkt_ipv4.proto)) + pkt.add_protocol(icmp.icmp(type_=icmp.ICMP_ECHO_REPLY, + code=icmp.ICMP_ECHO_REPLY_CODE, + csum=0, + data=pkt_icmp.data)) + self._send_packet(datapath, port, pkt) + + def check_direct_routing(self, tenant, from_subnet_id, to_subnet_id): + #from_subnet_cidr = from_subnet_id['cidr'] + #to_subnet_cidr = to_subnet_id['cidr'] + #split = m_subnet_cidr.split("/") + return + + def get_subnet_from_cidr(self, cidr): + split = cidr.split("/") + return (split[0], split[1]) + +# Base static + + +def ipv4_apply_mask(address, prefix_len, err_msg=None): + # import itertools + assert isinstance(address, str) + address_int = ipv4_text_to_int(address) + return ipv4_int_to_text(address_int & mask_ntob(prefix_len, err_msg)) + + +def ipv4_text_to_int(ip_text): + if ip_text == 0: + return ip_text + assert isinstance(ip_text, str) + return struct.unpack('!I', addrconv.ipv4.text_to_bin(ip_text))[0] + + +def ipv4_int_to_text(ip_int): + assert isinstance(ip_int, (int, long)) + return addrconv.ipv4.bin_to_text(struct.pack('!I', ip_int)) + + +def mask_ntob(mask, err_msg=None): + try: + return (UINT32_MAX << (32 - mask)) & UINT32_MAX + except ValueError: + msg = 'illegal netmask' + if err_msg is not None: + msg = '%s %s' % (err_msg, msg) + raise ValueError(msg) diff --git a/neutron/tests/unit/openvswitch/test_ovs_tunnel.py b/neutron/tests/unit/openvswitch/test_ovs_tunnel.py index ee020d8..3035f2b 100644 --- a/neutron/tests/unit/openvswitch/test_ovs_tunnel.py +++ b/neutron/tests/unit/openvswitch/test_ovs_tunnel.py @@ -147,6 +147,7 @@ class TunnelTest(base.BaseTestCase): self.mock_int_bridge_expected = [ mock.call.create(), mock.call.set_secure_mode(), + mock.call.del_controller(), mock.call.delete_port('patch-tun'), mock.call.remove_all_flows(), mock.call.add_flow(priority=1, actions='normal'), @@ -571,6 +572,7 @@ class TunnelTestUseVethInterco(TunnelTest): self.mock_int_bridge_expected = [ mock.call.create(), mock.call.set_secure_mode(), + mock.call.del_controller(), mock.call.delete_port('patch-tun'), mock.call.remove_all_flows(), mock.call.add_flow(priority=1, actions='normal'), -- 2.1.0 From 9eca2a7c519a562779f97ebf0b4ac75308cbf07e Mon Sep 17 00:00:00 2001 From: Eran Gampel Date: Sun, 7 Dec 2014 18:05:58 +0200 Subject: [PATCH 2/8] First move toward MetaDat and not local vlan Change-Id: Ia2c075b1bf7a310fe824cf433e971a29ab05e09a --- neutron/services/l3_router/l3_reactive_app.py | 51 +++++++++++++++++++++------ 1 file changed, 40 insertions(+), 11 deletions(-) diff --git a/neutron/services/l3_router/l3_reactive_app.py b/neutron/services/l3_router/l3_reactive_app.py index 3dcd928..9867e21 100755 --- a/neutron/services/l3_router/l3_reactive_app.py +++ b/neutron/services/l3_router/l3_reactive_app.py @@ -47,7 +47,7 @@ from neutron.plugins.ml2 import driver_api as api from neutron import context from neutron import manager from neutron.plugins.common import constants as service_constants - +import ipdb LOG = log.getLogger(__name__) @@ -548,7 +548,6 @@ class L3ReactiveApp(app_manager.RyuApp): parser = datapath.ofproto_parser ofproto = datapath.ofproto match = parser.OFPMatch() - # BUG in the Ryu lib constructor match do not work match.set_dl_type(0x0800) match.set_in_port(in_port) match.set_dl_src(haddr_to_bin(match_src_mac)) @@ -600,19 +599,21 @@ class L3ReactiveApp(app_manager.RyuApp): priority=priority, match=match) - def add_flow_normal_local_subnet( - self, datapath, table, priority, dst_net, dst_mask, vlan_id): + def add_flow_normal_local_subnet(self, datapath, table, priority, + dst_net, dst_mask, vlan_id): + ipdb.set_trace() parser = datapath.ofproto_parser ofproto = datapath.ofproto #match = parser.OFPMatch(vlan_vid=0x1000| vlan_id) - match = parser.OFPMatch(vlan_vid=0x1000 | vlan_id) - match.set_dl_type(0x0800) - match.set_vlan_vid(0x1000 | vlan_id) + match = parser.OFPMatch() + match.set_dl_type( ether.ETH_TYPE_IP) + #match.set_vlan_vid(0x1000 | vlan_id) + match.set_metadata(vlan_id) match.set_ipv4_dst_masked(ipv4_text_to_int(str(dst_net)), mask_ntob(int(dst_mask))) #match = parser.OFPMatch(vlan_pcp=0) actions = [ - parser.OFPActionPopVlan(), + #parser.OFPActionPopVlan(), parser.OFPActionOutput( ofproto.OFPP_NORMAL)] # actions = [parser.OFPActionOutput(ofproto.OFPP_NORMAL, @@ -643,8 +644,26 @@ class L3ReactiveApp(app_manager.RyuApp): priority=priority, match=match) - def add_flow_push_vlan_by_port_num( - self, datapath, table, priority, in_port, dst_vlan, goto_table): + def add_flow_metadata_by_port_num(self, datapath, table, priority, + in_port, metadata, + metadata_mask, goto_table): + parser = datapath.ofproto_parser + ofproto = datapath.ofproto + match = parser.OFPMatch() + match.set_in_port(in_port) + goto_inst = parser.OFPInstructionGotoTable(goto_table) + ofproto = datapath.ofproto + write_metadata = parser.OFPInstructionWriteMetadata(metadata,metadata_mask) + inst = [write_metadata, goto_inst] + self.mod_flow( + datapath, + inst=inst, + table_id=table, + priority=priority, + match=match) + + def add_flow_push_vlan_by_port_num(self, datapath, table, priority, + in_port, dst_vlan, goto_table): parser = datapath.ofproto_parser ofproto = datapath.ofproto match = parser.OFPMatch() @@ -962,16 +981,26 @@ class L3ReactiveApp(app_manager.RyuApp): # data with the port num and the switch dpid (port_id, mac, segmentation_id) = self.update_local_port_num( port.name, port.port_no, datapath.id) + self.add_flow_metadata_by_port_num(datapath, + 0, + HIGH_PRIOREITY_FLOW, + port.port_no, + segmentation_id, + 0xffff, + self.CLASSIFIER_TABLE) + vlan_id = self.get_l_vid_from_seg_id(switch, segmentation_id) LOG.debug(("Found VM port %s using MAC %s %d"), port.name, port.hw_addr, datapath.id) if vlan_id: - self.add_flow_push_vlan_by_port_num(datapath, + '''self.add_flow_push_vlan_by_port_num(datapath, 0, HIGH_PRIOREITY_FLOW, port.port_no, vlan_id, self.CLASSIFIER_TABLE) + ''' + else: LOG.error(("No local switch vlan mapping for port" " %s on %d Sending to Normal PATH "), -- 2.1.0 From 710cf49db58994935f9a4f38616a4ec4370111d4 Mon Sep 17 00:00:00 2001 From: Eran Gampel Date: Sun, 11 Jan 2015 09:53:58 +0200 Subject: [PATCH 3/8] merge Change-Id: Ie9dbe45c1568ab8366f25ca523f14220b5d2a487 --- neutron/common/topics.py | 2 +- .../plugins/openvswitch/agent/ovs_neutron_agent.py | 23 +- .../services/l3_router/README.l3_cont_dvr_plugin | 64 +++- neutron/services/l3_router/l3_cont_dvr_plugin.py | 6 +- neutron/services/l3_router/l3_reactive_app.py | 368 ++++++++++----------- 5 files changed, 244 insertions(+), 219 deletions(-) diff --git a/neutron/common/topics.py b/neutron/common/topics.py index 9bb1956..3ec424f 100644 --- a/neutron/common/topics.py +++ b/neutron/common/topics.py @@ -19,7 +19,7 @@ PORT = 'port' SECURITY_GROUP = 'security_group' L2POPULATION = 'l2population' DVR = 'dvr' - +SDNCONTROLLER='sdncontrol' CREATE = 'create' DELETE = 'delete' UPDATE = 'update' diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index dce7feb..0013280 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -192,7 +192,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, # Keep track of int_br's device count for use by _report_state() self.int_br_device_count = 0 - self.local_vlan_map = {} # Initialize controller Ip List self.controllers_ip_list = None ''' @@ -211,6 +210,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.setup_rpc() self.bridge_mappings = bridge_mappings self.setup_physical_bridges(self.bridge_mappings) + self.local_vlan_map = {} self.tun_br_ofports = {p_const.TYPE_GRE: {}, p_const.TYPE_VXLAN: {}} @@ -305,6 +305,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, if self.l2_pop: consumers.append([topics.L2POPULATION, topics.UPDATE, cfg.CONF.host]) + if self.enable_l3_controller: + consumers.append([topics.SDNCONTROLLER, + topics.UPDATE]) + self.connection = agent_rpc.create_consumers(self.endpoints, self.topic, consumers, @@ -504,7 +508,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, br.add_flow(table=table_id, priority=100, proto='arp', - dl_vlan=local_vid, + metadata=segmentation_id, nw_dst='%s' % ip, actions=actions) elif action == 'remove': @@ -545,19 +549,9 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, bridge.add_flow(priority=0, actions="normal") bridge.add_flow(table=constants.CANARY_TABLE, priority=0, actions="drop") - self.update_metadata_vlan_map_table(bridge) bridge.set_controller_mode("out-of-band") self.set_controller_lock.release() - def update_metadata_vlan_map_table(self, bridge): - for net_id, vlan_mapping in self.local_vlan_map.iteritems(): - seg_id_hex = hex(vlan_mapping.segmentation_id) - bridge.add_flow(table=constants.BR_INT_METADATA_TABLE, - priority=100, - dl_vlan=vlan_mapping.vlan, - actions="write_metadata:%s" % - (seg_id_hex), protocols="-OOpenFlow13") - def provision_local_vlan(self, net_uuid, network_type, physical_network, segmentation_id): '''Provisions a local VLAN. @@ -753,8 +747,6 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, self.dvr_agent.bind_port_to_dvr(port, network_type, fixed_ips, device_owner, local_vlan_id=lvm.vlan) - if self.enable_l3_controller: - self.update_metadata_vlan_map_table(self.int_br) # Do not bind a port if it's already bound cur_tag = self.int_br.db_get_val("Port", port.port_name, "tag") if cur_tag != str(lvm.vlan): @@ -1447,7 +1439,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, port_info.get('removed') or port_info.get('updated')) - def check_ovs_status(self): + def check_ovs_restart(self): + # Check for the canary flow # Sync lock for race condition with set_controller self.set_controller_lock.acquire() # Check for the canary flow diff --git a/neutron/services/l3_router/README.l3_cont_dvr_plugin b/neutron/services/l3_router/README.l3_cont_dvr_plugin index e02efa6..b5b9469 100644 --- a/neutron/services/l3_router/README.l3_cont_dvr_plugin +++ b/neutron/services/l3_router/README.l3_cont_dvr_plugin @@ -1,11 +1,11 @@ ####### -In order to enable Neutron Controlle-based DVR, you need to make the +In order to enable Neutron Controlle-based DVR, you need to make the following change in ``neutron.conf``: 1. Comment-out loading of the ``L3RouterPlugin`` - 2. Add the ``neutron.services.l3_router.l3_cont_dvr_plugin.ControllerL3ServicePlugin`` - to the service plugin list: + 2. Add the ``neutron.services.l3_router.l3_cont_dvr_plugin.ControllerL3ServicePlugin`` + to the service plugin list: **neutron.conf** @@ -17,7 +17,7 @@ In order to enable Neutron Controlle-based DVR, you need to make the 2. In addition, make the following change in ``ml2_conf.ini``: - * Set the ``enable_l3_controller`` to ``True``: + * Set the ``enable_l3_controller`` to ``True``: **ml2_conf.ini** @@ -25,17 +25,16 @@ In order to enable Neutron Controlle-based DVR, you need to make the :literal:`# enable_l3_controller = True` - 3. Deploy the **L3 Controller-based DVR Agent** on the Network Node 4. Deploy the **Public Network Agent** on *each* Compute node -5. Remove deployment of L3 Agent or DVR Agent +5. Remove deployment of L3 Agent or DVR Agent 6. Install Ryu on Network Node -% git clone git://github.com/osrg/ryu.git +% git clone git://github.com/osrg/ryu.git The current implementation is embedded into the service plugin. will be moved into a Agent based implementation of the controller. -Until we do that we will have to apply the following patch on ryu +Until we do that we will have to apply the following patch on ryu ryu/controller/controller.py and ryu/app/wsgi.py modify register_cli_opts to register_opts --- a/ryu/app/wsgi.py +++ b/ryu/app/wsgi.py @@ -60,4 +59,51 @@ index 23418f5..a5bcda2 100644 +CONF.register_opts([ -% cd ryu; python ./setup.py install +% cd ryu; python ./setup.py install + +For Tenant with two networks: +192.168.100.0/24 +VM1 :192.168.100.2 +VM3: 192.168.100.4 + + +192.168.200.0/24 +VM2:192.168.200.2 + + +On devstack one machine setup you will get following flows after boot strap +running the following command + +sudo ovs-ofctl dump-flows br-int + +NXST_FLOW reply (xid=0x4): + cookie=0x0, duration=9.970s, table=0, n_packets=0, n_bytes=0, idle_age=9, priority=1 actions=drop + cookie=0x0, duration=9.970s, table=0, n_packets=0, n_bytes=0, idle_age=9, priority=1000,in_port=10 actions=NORMAL + cookie=0x0, duration=9.970s, table=0, n_packets=0, n_bytes=0, idle_age=9, priority=1000,in_port=35 actions=NORMAL + cookie=0x0, duration=9.970s, table=0, n_packets=1, n_bytes=107, idle_age=4, priority=1000,in_port=30 actions=write_metadata:0xfa2/0xffff + cookie=0x0, duration=9.970s, table=0, n_packets=1, n_bytes=107, idle_age=7, priority=1000,in_port=6 actions=write_metadata:0xfa2/0xffff + cookie=0x0, duration=9.970s, table=0, n_packets=0, n_bytes=0, idle_age=9, priority=1000,in_port=1 actions=NORMAL + cookie=0x0, duration=9.970s, table=0, n_packets=0, n_bytes=0, idle_age=9, priority=1000,in_port=5 actions=NORMAL + cookie=0x0, duration=9.970s, table=0, n_packets=22, n_bytes=3974, idle_age=0, priority=1000,in_port=11 actions=write_metadata:0xfa3/0xffff + cookie=0x0, duration=10.804s, table=23, n_packets=0, n_bytes=0, idle_age=10, priority=0 actions=drop + cookie=0x0, duration=9.970s, table=40, n_packets=0, n_bytes=0, idle_age=9, priority=1 actions=drop + cookie=0x0, duration=9.977s, table=40, n_packets=24, n_bytes=4188, idle_age=0, priority=10,dl_dst=01:00:00:00:00:00/01:00:00:00:00:00 actions=drop + cookie=0x0, duration=9.977s, table=40, n_packets=0, n_bytes=0, idle_age=9, priority=100,dl_dst=ff:ff:ff:ff:ff:ff actions=drop + cookie=0x0, duration=9.977s, table=40, n_packets=0, n_bytes=0, idle_age=9, priority=10,arp actions=drop + cookie=0x0, duration=9.977s, table=51, n_packets=24, n_bytes=4188, idle_age=0, priority=1 actions=NORMAL + cookie=0x0, duration=9.817s, table=51, n_packets=0, n_bytes=0, idle_age=9, priority=100,arp,metadata=0xfa3,arp_tpa=192.168.200.1 actions=strip_vlan,move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:e9:74:9c,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:0xfa163ee9749c->NXM_NX_ARP_SHA[],load:0xc0a8c801->NXM_OF_ARP_SPA[],IN_PORT + cookie=0x0, duration=9.812s, table=51, n_packets=0, n_bytes=0, idle_age=9, priority=100,arp,metadata=0xfa2,arp_tpa=192.168.100.1 actions=strip_vlan,move:NXM_OF_ETH_SRC[]->NXM_OF_ETH_DST[],mod_dl_src:fa:16:3e:6f:f6:6e,load:0x2->NXM_OF_ARP_OP[],move:NXM_NX_ARP_SHA[]->NXM_NX_ARP_THA[],move:NXM_OF_ARP_SPA[]->NXM_OF_ARP_TPA[],load:0xfa163e6ff66e->NXM_NX_ARP_SHA[],load:0xc0a86401->NXM_OF_ARP_SPA[],IN_PORT + cookie=0x0, duration=9.966s, table=52, n_packets=0, n_bytes=0, idle_age=9, priority=99,metadata=0xfa2,dl_dst=fa:16:3e:6f:f6:6e actions=CONTROLLER:65535 + cookie=0x0, duration=9.970s, table=52, n_packets=0, n_bytes=0, idle_age=9, priority=99,metadata=0xfa3,dl_dst=fa:16:3e:e9:74:9c actions=CONTROLLER:65535 + cookie=0x0, duration=9.970s, table=52, n_packets=0, n_bytes=0, idle_age=9, priority=0 actions=CONTROLLER:65535 + cookie=0x0, duration=9.967s, table=52, n_packets=0, n_bytes=0, idle_age=9, priority=10,ip,metadata=0xfa2,nw_dst=192.168.100.0/24 actions=NORMAL + cookie=0x0, duration=9.970s, table=52, n_packets=0, n_bytes=0, idle_age=9, priority=10,ip,metadata=0xfa3,nw_dst=192.168.200.0/24 actions=NORMAL + + + +After ping from VM1 to VM3 you will get the following additional flows on table 52 + + sudo ovs-ofctl dump-flows br-int + + cookie=0x0, duration=3.606s, table=52, n_packets=0, n_bytes=0, idle_age=3, priority=100,ip,metadata=0xfa2,in_port=6,dl_src=fa:16:3e:59:a5:7e,dl_dst=fa:16:3e:6f:f6:6e,nw_src=192.168.100.2,nw_dst=192.168.200.2 actions=dec_ttl(0),mod_dl_src:fa:16:3e:e9:74:9c,mod_dl_dst:fa:16:3e:6b:e4:97,output:11 + cookie=0x0, duration=3.606s, table=52, n_packets=1, n_bytes=98, idle_age=3, priority=100,ip,metadata=0xfa3,in_port=11,dl_src=fa:16:3e:6b:e4:97,dl_dst=fa:16:3e:e9:74:9c,nw_src=192.168.200.2,nw_dst=192.168.100.2 actions=dec_ttl(0),mod_dl_src:fa:16:3e:6f:f6:6e,mod_dl_dst:fa:16:3e:59:a5:7e,output:6 diff --git a/neutron/services/l3_router/l3_cont_dvr_plugin.py b/neutron/services/l3_router/l3_cont_dvr_plugin.py index e449e6f..eae96dd 100755 --- a/neutron/services/l3_router/l3_cont_dvr_plugin.py +++ b/neutron/services/l3_router/l3_cont_dvr_plugin.py @@ -222,9 +222,9 @@ class ControllerL3ServicePlugin(common_db_mixin.CommonDbMixin, agent_id, ip_address, host) - self.send_set_controllers_upadte(_context, False) + self.send_set_controllers_update(_context, False) - def send_set_controllers_upadte(self, _context, force_reconnect): + def send_set_controllers_update(self, _context, force_reconnect): topic_port_update = topics.get_topic_name(topics.AGENT, topics.PORT, @@ -289,7 +289,7 @@ class ControllerRunner(threading.Thread): if self.sync_all: l3plugin = manager.NeutronManager.get_service_plugins().get( constants.L3_ROUTER_NAT) - l3plugin.send_set_controllers_upadte(self.ctx, True) + l3plugin.send_set_controllers_update(self.ctx, True) self.sync_all = False plugin = manager.NeutronManager.get_plugin() plugin.create_or_update_agent(self.ctx, self.agent_state) diff --git a/neutron/services/l3_router/l3_reactive_app.py b/neutron/services/l3_router/l3_reactive_app.py index 9867e21..a12f90f 100755 --- a/neutron/services/l3_router/l3_reactive_app.py +++ b/neutron/services/l3_router/l3_reactive_app.py @@ -27,6 +27,7 @@ from ryu.controller.handler import set_ev_cls from ryu.controller import ofp_event from ryu.ofproto import ether from ryu.ofproto.ether import ETH_TYPE_8021Q +from ryu.ofproto import nx_match from ryu.ofproto import ofproto_v1_3 from ryu.lib.packet import ethernet @@ -192,8 +193,9 @@ class L3ReactiveApp(app_manager.RyuApp): for dpid in self.dp_list: datapath = self.dp_list[dpid].datapath self.send_features_request(datapath) - self.send_flow_stats_request( - datapath, table=self.METADATA_TABLE_ID) + self.send_port_desc_stats_request(datapath) + #self.send_flow_stats_request( + # datapath, table=self.METADATA_TABLE_ID) def sync_data(self): self.logger.info(" l3_reactive_app sync router data ") @@ -349,129 +351,120 @@ class L3ReactiveApp(app_manager.RyuApp): eth): pkt_ipv4 = header_list['ipv4'] pkt_ethernet = header_list['ethernet'] - - # Check vlan-tag - if VLAN in header_list: - vlan_id = header_list[VLAN].vid - self.logger.info("handle_ipv4_packet_in:: VLANID %s ", vlan_id) - switch = self.dp_list.get(datapath.id) - if switch: - if vlan_id not in switch.local_vlan_mapping: - # send request for loacl switch data - # self.send_port_desc_stats_request(datapath) - self.send_flow_stats_request( - datapath, table=self.METADATA_TABLE_ID) - LOG.error(("No local switch vlan mapping for vlan %s"), - vlan_id) - return - self.logger.info( - "packet segmentation_id %s ", - switch.local_vlan_mapping[vlan_id]) - segmentation_id = switch.local_vlan_mapping[vlan_id] - for tenantid in self.tenants: - tenant = self.tenants[tenantid] - for router in tenant.routers: - for subnet in router.subnets: - if segmentation_id == subnet.segmentation_id: - self.logger.info("packet from to tenant %s ", - tenant.tenant_id) - in_port_data = self.tenants[ - tenantid].mac_to_port_data[eth.src] - out_port_data = self.tenants[ - tenantid].mac_to_port_data[eth.dst] - LOG.debug(('Source port data <--- %s ', - in_port_data)) - LOG.debug(('Router Mac dest port data -> %s ', - out_port_data)) - if self.handle_router_interface(datapath, - in_port, - out_port_data, - pkt, - pkt_ethernet, - pkt_ipv4) == 1: - # trafic to the virtual routre handle only - # ping - return - (dst_p_data, - dst_sub_id) = self.get_port_data(tenant, - pkt_ipv4.dst) - for _subnet in router.subnets: - if dst_sub_id == _subnet.data['id']: - out_subnet = _subnet - subnet_gw = out_subnet.data[ - 'gateway_ip'] - - (dst_gw_port_data, - dst_gw_sub_id) = self.get_port_data( - tenant, subnet_gw) - - if self.handle_router_interface( - datapath, - in_port, - dst_gw_port_data, - pkt, - pkt_ethernet, - pkt_ipv4) == 1: - # this trafic to the virtual routre - return - if not dst_p_data: - LOG.error(("No local switch" - "mapping for %s"), - pkt_ipv4.dst) - return - if self.handle_router_interface( - datapath, - in_port, - dst_p_data, - pkt, - pkt_ethernet, - pkt_ipv4) != -1: - # case for vrouter that is not the - #gw and we are trying to ping - # this trafic to the virtual routre - return - - LOG.debug(("Route from %s to %s" - "exist installing flow ", - pkt_ipv4.src, - pkt_ipv4.dst)) - dst_vlan = self.get_l_vid_from_seg_id( - switch, - out_subnet.segmentation_id) - self.install_l3_forwarding_flows( + #ipdb.set_trace() + switch = self.dp_list.get(datapath.id) + if switch: + if 'metadata' not in msg.match: + # send request for loacl switch data + # self.send_port_desc_stats_request(datapath) + #self.send_flow_stats_request( + # datapath, table=self.METADATA_TABLE_ID) + LOG.error(("No metadata on packet from %s"), + eth.src) + return + segmentation_id = msg.match['metadata'] + self.logger.info( + "packet segmentation_id %s ", + segmentation_id) + for tenantid in self.tenants: + tenant = self.tenants[tenantid] + for router in tenant.routers: + for subnet in router.subnets: + if segmentation_id == subnet.segmentation_id: + self.logger.info("packet from to tenant %s ", + tenant.tenant_id) + in_port_data = self.tenants[ + tenantid].mac_to_port_data[eth.src] + out_port_data = self.tenants[ + tenantid].mac_to_port_data[eth.dst] + LOG.debug(('Source port data <--- %s ', + in_port_data)) + LOG.debug(('Router Mac dest port data -> %s ', + out_port_data)) + if self.handle_router_interface(datapath, + in_port, + out_port_data, + pkt, + pkt_ethernet, + pkt_ipv4) == 1: + # trafic to the virtual routre handle only + # ping + return + (dst_p_data, + dst_sub_id) = self.get_port_data(tenant, + pkt_ipv4.dst) + for _subnet in router.subnets: + if dst_sub_id == _subnet.data['id']: + out_subnet = _subnet + subnet_gw = out_subnet.data[ + 'gateway_ip'] + + (dst_gw_port_data, + dst_gw_sub_id) = self.get_port_data( + tenant, subnet_gw) + + if self.handle_router_interface( datapath, - msg, - in_port_data, in_port, - vlan_id, - eth, - pkt_ipv4, dst_gw_port_data, + pkt, + pkt_ethernet, + pkt_ipv4) == 1: + # this trafic to the virtual routre + return + if not dst_p_data: + LOG.error(("No local switch" + "mapping for %s"), + pkt_ipv4.dst) + return + if self.handle_router_interface( + datapath, + in_port, dst_p_data, - dst_vlan) + pkt, + pkt_ethernet, + pkt_ipv4) != -1: + # case for vrouter that is not the + #gw and we are trying to ping + # this trafic to the virtual routre return - def install_l3_forwarding_flows( - self, - datapath, - msg, - in_port_data, - in_port, - vlan_id, - eth, - pkt_ipv4, - dst_gw_port_data, - dst_p_data, - dst_vlan): + LOG.debug(("Route from %s to %s" + "exist installing flow ", + pkt_ipv4.src, + pkt_ipv4.dst)) + self.install_l3_forwarding_flows( + datapath, + msg, + in_port_data, + in_port, + segmentation_id, + eth, + pkt_ipv4, + dst_gw_port_data, + dst_p_data, + out_subnet.segmentation_id) + return + + def install_l3_forwarding_flows(self, + datapath, + msg, + in_port_data, + in_port, + src_seg_id, + eth, + pkt_ipv4, + dst_gw_port_data, + dst_p_data, + dst_seg_id): if dst_p_data['local_dpid_switch'] == datapath.id: # The dst VM and the source VM are on the same copute Node # Send output flow directly to port iuse the same datapath - actions = self.add_flow_subnet_traffic( - datapath, + actions = self.add_flow_subnet_traffic(datapath, self.L3_VROUTER_TABLE, MEDIUM_PRIOREITY_FLOW, in_port, - vlan_id, + src_seg_id, eth.src, eth.dst, pkt_ipv4.dst, @@ -479,12 +472,12 @@ class L3ReactiveApp(app_manager.RyuApp): dst_gw_port_data['mac_address'], dst_p_data['mac_address'], dst_p_data['local_port_num']) - # Install the reverse flow return traffic + # Install the reverse flow return traffic self.add_flow_subnet_traffic(datapath, self.L3_VROUTER_TABLE, MEDIUM_PRIOREITY_FLOW, dst_p_data['local_port_num'], - dst_vlan, + dst_seg_id, dst_p_data['mac_address'], dst_gw_port_data['mac_address'], pkt_ipv4.src, @@ -503,7 +496,7 @@ class L3ReactiveApp(app_manager.RyuApp): self.L3_VROUTER_TABLE, MEDIUM_PRIOREITY_FLOW, in_port, - vlan_id, + src_seg_id, eth.src, eth.dst, pkt_ipv4.dst, @@ -513,13 +506,13 @@ class L3ReactiveApp(app_manager.RyuApp): dst_p_data[ 'mac_address'], localSwitch.patch_port, - dst_vlan) + dst_seg_id=dst_seg_id) # Remote reverse flow install self.add_flow_subnet_traffic(remoteSwitch.datapath, self.L3_VROUTER_TABLE, MEDIUM_PRIOREITY_FLOW, dst_p_data['local_port_num'], - dst_vlan, + dst_seg_id, dst_p_data['mac_address'], dst_gw_port_data['mac_address'], pkt_ipv4.src, @@ -527,7 +520,7 @@ class L3ReactiveApp(app_manager.RyuApp): eth.dst, in_port_data['mac_address'], in_port_data['local_port_num'], - vlan_id) + dst_seg_id=src_seg_id) self.handle_packet_out_l3(datapath, msg, in_port, actions) def handle_packet_out_l3(self, datapath, msg, in_port, actions): @@ -542,31 +535,28 @@ class L3ReactiveApp(app_manager.RyuApp): datapath.send_msg(out) def add_flow_subnet_traffic(self, datapath, table, priority, in_port, - match_vlan, match_src_mac, match_dst_mac, + src_seg_id, match_src_mac, match_dst_mac, match_dst_ip, match_src_ip, src_mac, - dst_mac, out_port_num, dst_vlan=None): + dst_mac, out_port_num, dst_seg_id=None): parser = datapath.ofproto_parser ofproto = datapath.ofproto match = parser.OFPMatch() - match.set_dl_type(0x0800) + match.set_dl_type( ether.ETH_TYPE_IP) match.set_in_port(in_port) + match.set_metadata(src_seg_id) match.set_dl_src(haddr_to_bin(match_src_mac)) match.set_dl_dst(haddr_to_bin(match_dst_mac)) match.set_ipv4_src(ipv4_text_to_int(str(match_src_ip))) match.set_ipv4_dst(ipv4_text_to_int(str(match_dst_ip))) - match.set_vlan_vid(0x1000 | match_vlan) - actions = [parser.OFPActionPopVlan()] + actions = [] + if dst_seg_id: + field = parser.OFPActionSetField(tunnel_id=dst_seg_id) + actions.append(parser.OFPActionSetField(field)) actions.append(parser.OFPActionDecNwTtl()) actions.append(parser.OFPActionSetField(eth_src=src_mac)) actions.append(parser.OFPActionSetField(eth_dst=dst_mac)) actions.append(parser.OFPActionOutput(out_port_num, ofproto.OFPCML_NO_BUFFER)) - if dst_vlan: - field = parser.OFPMatchField.make( - ofproto.OXM_OF_VLAN_VID, dst_vlan) - actions.append(parser.OFPActionPushVlan(ETH_TYPE_8021Q)) - actions.append(parser.OFPActionSetField(field)) - ofproto = datapath.ofproto inst = [datapath.ofproto_parser.OFPInstructionActions( ofproto.OFPIT_APPLY_ACTIONS, actions)] @@ -600,15 +590,14 @@ class L3ReactiveApp(app_manager.RyuApp): match=match) def add_flow_normal_local_subnet(self, datapath, table, priority, - dst_net, dst_mask, vlan_id): - ipdb.set_trace() + dst_net, dst_mask, seg_id): parser = datapath.ofproto_parser ofproto = datapath.ofproto #match = parser.OFPMatch(vlan_vid=0x1000| vlan_id) match = parser.OFPMatch() match.set_dl_type( ether.ETH_TYPE_IP) #match.set_vlan_vid(0x1000 | vlan_id) - match.set_metadata(vlan_id) + match.set_metadata(seg_id) match.set_ipv4_dst_masked(ipv4_text_to_int(str(dst_net)), mask_ntob(int(dst_mask))) #match = parser.OFPMatch(vlan_pcp=0) @@ -706,13 +695,12 @@ class L3ReactiveApp(app_manager.RyuApp): instructions) datapath.send_msg(flow_mod) - def add_flow_normal(self, datapath, table, priority): + def add_flow_normal(self, datapath, table, priority, match=None): parser = datapath.ofproto_parser ofproto = datapath.ofproto - match = parser.OFPMatch(vlan_vid=0x1000) + #match = parser.OFPMatch(vlan_vid=0x1000) #match = parser.OFPMatch(vlan_pcp=0) actions = [ - parser.OFPActionPopVlan(), parser.OFPActionOutput( ofproto.OFPP_NORMAL)] ofproto = datapath.ofproto @@ -831,12 +819,11 @@ class L3ReactiveApp(app_manager.RyuApp): match=match) def add_flow_match_gw_mac_to_cont(self, datapath, dst_mac, table, - priority, vlan_vid=None, + priority, seg_id=None, _actions=None): parser = datapath.ofproto_parser #ofproto = datapath.ofproto - vlan_id = 0x1000 | vlan_vid - match = parser.OFPMatch(eth_dst=dst_mac, vlan_vid=vlan_id) + match = parser.OFPMatch(eth_dst=dst_mac, metadata=seg_id) self.add_flow_match_to_controller( datapath, table, priority, match=match, _actions=_actions) @@ -899,8 +886,9 @@ class L3ReactiveApp(app_manager.RyuApp): switch = self.dp_list.get(datapath.id) if switch: - self.send_flow_stats_request( - datapath, table=self.METADATA_TABLE_ID) + self.send_port_desc_stats_request(datapath) + # self.send_flow_stats_request( + # datapath, table=self.METADATA_TABLE_ID) @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): @@ -917,7 +905,8 @@ class L3ReactiveApp(app_manager.RyuApp): time.sleep(1) # sleep during 500ms LOG.info(("Done Wait .. will retry if meta table not set ")) - self.send_flow_stats_request(datapath, table=self.METADATA_TABLE_ID) + self.send_port_desc_stats_request(datapath) + #self.send_flow_stats_request(datapath, table=self.METADATA_TABLE_ID) # --> meta #self.add_flow_go_to_table2(datapath, 0, 1 ,self.L3_VROUTER_TABLE) # main table 0 to Arp On ARp or broadcat or multicast @@ -937,10 +926,6 @@ class L3ReactiveApp(app_manager.RyuApp): NORMAL_PRIOREITY_FLOW, self.ARP_AND_BR_TABLE) - # Meta Table to L3 router table on all other trafic - self.add_flow_go_to_table2( - datapath, self.METADATA_TABLE_ID, 1, self.L3_VROUTER_TABLE) - # Normal flow on arp table in low priorety self.add_flow_normal(datapath, self.ARP_AND_BR_TABLE, 1) #del l3plugin @@ -992,14 +977,13 @@ class L3ReactiveApp(app_manager.RyuApp): vlan_id = self.get_l_vid_from_seg_id(switch, segmentation_id) LOG.debug(("Found VM port %s using MAC %s %d"), port.name, port.hw_addr, datapath.id) - if vlan_id: - '''self.add_flow_push_vlan_by_port_num(datapath, + '''if vlan_id: + self.add_flow_push_vlan_by_port_num(datapath, 0, HIGH_PRIOREITY_FLOW, port.port_no, vlan_id, self.CLASSIFIER_TABLE) - ''' else: LOG.error(("No local switch vlan mapping for port" @@ -1009,6 +993,7 @@ class L3ReactiveApp(app_manager.RyuApp): self.add_flow_normal_by_port_num(datapath, 0, HIGH_PRIOREITY_FLOW, port.port_no) + ''' elif "patch-tun" in port.name: LOG.debug(("Found br-tun patch port " "%s %s sending to NORMAL path"), @@ -1021,8 +1006,45 @@ class L3ReactiveApp(app_manager.RyuApp): switch.local_ports = ports self.add_flow_go_to_table2(datapath, 0, 1, self.CLASSIFIER_TABLE) self.add_flow_match_to_controller(datapath, self.L3_VROUTER_TABLE, 0) - self.add_flow_go_to_table2( - datapath, self.CLASSIFIER_TABLE, 1, self.L3_VROUTER_TABLE) + self.add_flow_go_to_table2(datapath, self.CLASSIFIER_TABLE, 1, + self.L3_VROUTER_TABLE) + l3plugin = manager.NeutronManager.get_service_plugins().get( + service_constants.L3_ROUTER_NAT) + + for tenantid in self.tenants: + for router in self.tenants[tenantid].routers: + for subnet in router.subnets: + for interface in router.data['_interfaces']: + if interface['subnet']['id'] == subnet.data['id']: + segmentation_id = subnet.segmentation_id + #vlan_id = self.get_l_vid_from_seg_id( + # switch, segmentation_id) + network, net_mask = self.get_subnet_from_cidr( + subnet.data['cidr']) + + self.add_flow_normal_local_subnet( + datapath, + self.L3_VROUTER_TABLE, + NORMAL_PRIOREITY_FLOW, + network, + net_mask, + segmentation_id) + + self.add_flow_match_gw_mac_to_cont( + datapath, + interface['mac_address'], + self.L3_VROUTER_TABLE, + 99, + segmentation_id) + l3plugin.setup_vrouter_arp_responder( + self.ctx, + "br-int", + "add", + self.ARP_AND_BR_TABLE, + segmentation_id, + interface['network_id'], + interface['mac_address'], + self.get_ip_from_router_interface(interface)) def send_features_request(self, datapath): ofp_parser = datapath.ofproto_parser @@ -1131,50 +1153,14 @@ class L3ReactiveApp(app_manager.RyuApp): l3plugin = manager.NeutronManager.get_service_plugins().get( service_constants.L3_ROUTER_NAT) switch = self.dp_list.get(datapath.id) - - for tenantid in self.tenants: - for router in self.tenants[tenantid].routers: - for subnet in router.subnets: - for interface in router.data['_interfaces']: - if interface['subnet']['id'] == subnet.data['id']: - segmentation_id = subnet.segmentation_id - vlan_id = self.get_l_vid_from_seg_id( - switch, segmentation_id) - network, net_mask = self.get_subnet_from_cidr( - subnet.data['cidr']) - - if vlan_id: - self.add_flow_normal_local_subnet( - datapath, - self.L3_VROUTER_TABLE, - NORMAL_PRIOREITY_FLOW, - network, - net_mask, - vlan_id) - - self.add_flow_match_gw_mac_to_cont( - datapath, - interface['mac_address'], - self.L3_VROUTER_TABLE, - 99, - vlan_id) - l3plugin.setup_vrouter_arp_responder( - self.ctx, - "br-int", - "add", - self.ARP_AND_BR_TABLE, - segmentation_id, - interface['network_id'], - interface['mac_address'], - self.get_ip_from_router_interface(interface)) - # No match on table L3_VROUTER_TABLE go to normal flow + # No match on table L3_VROUTER_TABLE go to normal flow # No match on table L3_VROUTER_TABLE go to controller # Patch to overcome OVS BUG not accepting match on tag vlans # set Pop per taged vlan - for local_vlan in switch.local_vlan_mapping: - self.add_flow_pop_vlan_to_normal( - datapath, self.ARP_AND_BR_TABLE, 1, local_vlan) + # for local_vlan in switch.local_vlan_mapping: + # self.add_flow_pop_vlan_to_normal( + # datapath, self.ARP_AND_BR_TABLE, 1, local_vlan) if not switch.local_vlan_mapping: LOG.error(("CRITICAL ERROR ***** Switch did not send local port" -- 2.1.0 From b3845322c8faae72971d8d7f48e576ec0609451a Mon Sep 17 00:00:00 2001 From: Eran Gampel Date: Mon, 12 Jan 2015 18:05:20 +0200 Subject: [PATCH 4/8] merge Change-Id: I92566ffca502fcb3fa9d9ff323652e6933fd54cc --- neutron/plugins/openvswitch/agent/ovs_neutron_agent.py | 2 +- neutron/services/l3_router/l3_cont_dvr_plugin.py | 4 ++-- neutron/services/l3_router/l3_reactive_app.py | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index 0013280..418032e 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -1439,7 +1439,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, port_info.get('removed') or port_info.get('updated')) - def check_ovs_restart(self): + def check_ovs_status(self): # Check for the canary flow # Sync lock for race condition with set_controller self.set_controller_lock.acquire() diff --git a/neutron/services/l3_router/l3_cont_dvr_plugin.py b/neutron/services/l3_router/l3_cont_dvr_plugin.py index eae96dd..d10e4e2 100755 --- a/neutron/services/l3_router/l3_cont_dvr_plugin.py +++ b/neutron/services/l3_router/l3_cont_dvr_plugin.py @@ -49,7 +49,7 @@ LOG = logging.getLogger(__name__) NET_CONTROL_L3_OPTS = [ cfg.StrOpt('L3controller_ip_list', - default='tcp:10.100.100.38:6633', + default='tcp:10.100.100.3:6633', help=("L3 Controler IP list list tcp:ip_addr:port;" "tcp:ip_addr:port..;..")), cfg.StrOpt('net_controller_l3_southbound_protocol', @@ -79,7 +79,7 @@ class ControllerL3ServicePlugin(common_db_mixin.CommonDbMixin, self.start_periodic_agent_status_check() if cfg.CONF.net_controller_l3_southbound_protocol == "OpenFlow": # Open Flow Controller - LOG.debug(("Using Southbound OpenFlow Protocol ")) + LOG.info(("Using Southbound OpenFlow Protocol ")) self.controllerThread = ControllerRunner("openflow") self.controllerThread.start() self.controllerThread.router_scheduler = self.router_scheduler diff --git a/neutron/services/l3_router/l3_reactive_app.py b/neutron/services/l3_router/l3_reactive_app.py index a12f90f..e37c449 100755 --- a/neutron/services/l3_router/l3_reactive_app.py +++ b/neutron/services/l3_router/l3_reactive_app.py @@ -48,7 +48,6 @@ from neutron.plugins.ml2 import driver_api as api from neutron import context from neutron import manager from neutron.plugins.common import constants as service_constants -import ipdb LOG = log.getLogger(__name__) @@ -181,6 +180,7 @@ class L3ReactiveApp(app_manager.RyuApp): self.dp_list = {} def start(self): + self.logger.info("Starting Virtual L3 Reactive OpenFlow APP ") super(L3ReactiveApp, self).start() return 1 @@ -893,7 +893,6 @@ class L3ReactiveApp(app_manager.RyuApp): @set_ev_cls(ofp_event.EventOFPSwitchFeatures, CONFIG_DISPATCHER) def switch_features_handler(self, ev): datapath = ev.msg.datapath - if self.need_sync: self.sync_data() switch = self.dp_list.get(datapath.id) -- 2.1.0 From 11f922622f8b4d59d1e857e3426a736653ab41b1 Mon Sep 17 00:00:00 2001 From: Eran Gampel Date: Sun, 25 Jan 2015 17:29:14 +0200 Subject: [PATCH 5/8] Fix cross Compute Node flow installation, order to communicate across virtual switch in the US we must use the mark action metadata and tunnel_id are removed from flow when sent to patch port Change-Id: Iab01fd387cb09a7bfdf0b99d38ce02fb3a9e1675 --- .../plugins/openvswitch/agent/ovs_neutron_agent.py | 12 +++++++ neutron/services/l3_router/l3_cont_dvr_plugin.py | 2 +- neutron/services/l3_router/l3_reactive_app.py | 38 +++++++++++++++------- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index 418032e..3f6c012 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -549,6 +549,10 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, bridge.add_flow(priority=0, actions="normal") bridge.add_flow(table=constants.CANARY_TABLE, priority=0, actions="drop") + bridge.add_flow(table="60", priority=1, + actions="move:NXM_NX_TUN_ID[0..31]->NXM_NX_PKT_MARK[]," + "output:%s" % + (self.patch_tun_ofport)) bridge.set_controller_mode("out-of-band") self.set_controller_lock.release() @@ -943,6 +947,7 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, constants.FLOOD_TO_TUN) # FLOOD_TO_TUN will handle flooding in tunnels based on lvid, # for now, add a default drop action + self.tun_br.add_flow(table=constants.FLOOD_TO_TUN, priority=0, actions="drop") @@ -1164,6 +1169,13 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, dl_vlan=vlan_mapping.vlan, actions="strip_vlan,set_tunnel:%s,output:%s" % (vlan_mapping.segmentation_id, ofports)) + if self.enable_l3_controller: + if ofports: + br.add_flow(table=constants.FLOOD_TO_TUN, + actions="move:NXM_NX_PKT_MARK[]->NXM_NX_TUN_ID[0..31]," + "output:%s" % + (ofports)) + return ofport def setup_tunnel_port(self, br, remote_ip, network_type): diff --git a/neutron/services/l3_router/l3_cont_dvr_plugin.py b/neutron/services/l3_router/l3_cont_dvr_plugin.py index d10e4e2..5a27543 100755 --- a/neutron/services/l3_router/l3_cont_dvr_plugin.py +++ b/neutron/services/l3_router/l3_cont_dvr_plugin.py @@ -49,7 +49,7 @@ LOG = logging.getLogger(__name__) NET_CONTROL_L3_OPTS = [ cfg.StrOpt('L3controller_ip_list', - default='tcp:10.100.100.3:6633', + default='tcp:172.16.10.10:6633', help=("L3 Controler IP list list tcp:ip_addr:port;" "tcp:ip_addr:port..;..")), cfg.StrOpt('net_controller_l3_southbound_protocol', diff --git a/neutron/services/l3_router/l3_reactive_app.py b/neutron/services/l3_router/l3_reactive_app.py index e37c449..5f3bac2 100755 --- a/neutron/services/l3_router/l3_reactive_app.py +++ b/neutron/services/l3_router/l3_reactive_app.py @@ -48,7 +48,6 @@ from neutron.plugins.ml2 import driver_api as api from neutron import context from neutron import manager from neutron.plugins.common import constants as service_constants - LOG = log.getLogger(__name__) ETHERNET = ethernet.ethernet.__name__ @@ -351,7 +350,6 @@ class L3ReactiveApp(app_manager.RyuApp): eth): pkt_ipv4 = header_list['ipv4'] pkt_ethernet = header_list['ethernet'] - #ipdb.set_trace() switch = self.dp_list.get(datapath.id) if switch: if 'metadata' not in msg.match: @@ -505,7 +503,7 @@ class L3ReactiveApp(app_manager.RyuApp): 'mac_address'], dst_p_data[ 'mac_address'], - localSwitch.patch_port, + localSwitch.patch_port_num, dst_seg_id=dst_seg_id) # Remote reverse flow install self.add_flow_subnet_traffic(remoteSwitch.datapath, @@ -520,6 +518,7 @@ class L3ReactiveApp(app_manager.RyuApp): eth.dst, in_port_data['mac_address'], in_port_data['local_port_num'], + remoteSwitch.patch_port_num, dst_seg_id=src_seg_id) self.handle_packet_out_l3(datapath, msg, in_port, actions) @@ -543,29 +542,44 @@ class L3ReactiveApp(app_manager.RyuApp): match = parser.OFPMatch() match.set_dl_type( ether.ETH_TYPE_IP) match.set_in_port(in_port) - match.set_metadata(src_seg_id) + match.set_metadata(src_seg_id ) match.set_dl_src(haddr_to_bin(match_src_mac)) match.set_dl_dst(haddr_to_bin(match_dst_mac)) match.set_ipv4_src(ipv4_text_to_int(str(match_src_ip))) match.set_ipv4_dst(ipv4_text_to_int(str(match_dst_ip))) actions = [] + inst = [] + write_metadata = 0; + ofproto = datapath.ofproto if dst_seg_id: - field = parser.OFPActionSetField(tunnel_id=dst_seg_id) - actions.append(parser.OFPActionSetField(field)) + #The best vm is on another compute machine so we must set the + #segmentation Id and set metadata for the tunnel bridge to flood this packet + field = parser.OFPActionSetField(tunnel_id=dst_seg_id ) + actions.append(field) + goto_inst = parser.OFPInstructionGotoTable(60) + #field = parser.OFPActionSetField(metadata=0x8000) + #actions.append(field) + #write_metadata = parser.OFPInstructionWriteMetadata(0x8000,0x8000) + #inst= [write_metadata] + inst.append(goto_inst) + #inst.append(write_metadata) + else: + actions.append(parser.OFPActionOutput(out_port_num, + ofproto.OFPCML_NO_BUFFER)) actions.append(parser.OFPActionDecNwTtl()) actions.append(parser.OFPActionSetField(eth_src=src_mac)) actions.append(parser.OFPActionSetField(eth_dst=dst_mac)) - actions.append(parser.OFPActionOutput(out_port_num, - ofproto.OFPCML_NO_BUFFER)) - ofproto = datapath.ofproto - inst = [datapath.ofproto_parser.OFPInstructionActions( - ofproto.OFPIT_APPLY_ACTIONS, actions)] + #inst.append( datapath.ofproto_parser.OFPInstructionActions( + # ofproto.OFPIT_APPLY_ACTIONS, actions)) + inst.append(datapath.ofproto_parser.OFPInstructionActions( + ofproto.OFPIT_APPLY_ACTIONS, actions)) self.mod_flow( datapath, inst=inst, table_id=table, priority=priority, - match=match) + match=match, + out_port=out_port_num) return actions -- 2.1.0 From 12f5dfce0226f379f0931dc9a5b1cbb267df2d2e Mon Sep 17 00:00:00 2001 From: Eran Gampel Date: Sun, 25 Jan 2015 17:54:51 +0200 Subject: [PATCH 6/8] fix local cross subnet VMs Change-Id: Icf05940b56973b76fd2754dd59fef7af5acfff52 --- neutron/services/l3_router/l3_reactive_app.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/neutron/services/l3_router/l3_reactive_app.py b/neutron/services/l3_router/l3_reactive_app.py index 5f3bac2..a9c23a3 100755 --- a/neutron/services/l3_router/l3_reactive_app.py +++ b/neutron/services/l3_router/l3_reactive_app.py @@ -551,6 +551,9 @@ class L3ReactiveApp(app_manager.RyuApp): inst = [] write_metadata = 0; ofproto = datapath.ofproto + actions.append(parser.OFPActionDecNwTtl()) + actions.append(parser.OFPActionSetField(eth_src=src_mac)) + actions.append(parser.OFPActionSetField(eth_dst=dst_mac)) if dst_seg_id: #The best vm is on another compute machine so we must set the #segmentation Id and set metadata for the tunnel bridge to flood this packet @@ -566,10 +569,7 @@ class L3ReactiveApp(app_manager.RyuApp): else: actions.append(parser.OFPActionOutput(out_port_num, ofproto.OFPCML_NO_BUFFER)) - actions.append(parser.OFPActionDecNwTtl()) - actions.append(parser.OFPActionSetField(eth_src=src_mac)) - actions.append(parser.OFPActionSetField(eth_dst=dst_mac)) - #inst.append( datapath.ofproto_parser.OFPInstructionActions( + #inst.append( datapath.ofproto_parser.OFPInstructionActions( # ofproto.OFPIT_APPLY_ACTIONS, actions)) inst.append(datapath.ofproto_parser.OFPInstructionActions( ofproto.OFPIT_APPLY_ACTIONS, actions)) -- 2.1.0 From c0fb88fcca7c657dcaa3a6bf1fd0d327b4c27500 Mon Sep 17 00:00:00 2001 From: Eran Gampel Date: Mon, 26 Jan 2015 11:21:26 +0200 Subject: [PATCH 7/8] send not forced connect message every 30 sec, temporary fix until we manage to detect new/restarted L2 agent Change-Id: I1189af681548d4adf406a7defa8feb082435de19 --- neutron/plugins/openvswitch/agent/ovs_neutron_agent.py | 4 ++-- neutron/services/l3_router/l3_cont_dvr_plugin.py | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py index 3f6c012..d7d19e1 100644 --- a/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py +++ b/neutron/plugins/openvswitch/agent/ovs_neutron_agent.py @@ -814,8 +814,8 @@ class OVSNeutronAgent(sg_rpc.SecurityGroupAgentRpcCallbackMixin, # which does nothing if bridge already exists. self.int_br.create() self.int_br.set_secure_mode() - if not self.enable_l3_controller: - self.int_br.del_controller() + #if not self.enable_l3_controller: + self.int_br.del_controller() self.int_br.delete_port(cfg.CONF.OVS.int_peer_patch_port) self.int_br.remove_all_flows() # switch all traffic using L2 learning diff --git a/neutron/services/l3_router/l3_cont_dvr_plugin.py b/neutron/services/l3_router/l3_cont_dvr_plugin.py index 5a27543..d2cb029 100755 --- a/neutron/services/l3_router/l3_cont_dvr_plugin.py +++ b/neutron/services/l3_router/l3_cont_dvr_plugin.py @@ -43,7 +43,6 @@ from neutron.openstack.common import log as logging from neutron.openstack.common import loopingcall from neutron.services.l3_router.l3_reactive_app import L3ReactiveApp - LOG = logging.getLogger(__name__) @@ -286,11 +285,13 @@ class ControllerRunner(threading.Thread): self.heartbeat.start(interval=30) def _report_state_and_bind_routers(self): - if self.sync_all: - l3plugin = manager.NeutronManager.get_service_plugins().get( + l3plugin = manager.NeutronManager.get_service_plugins().get( constants.L3_ROUTER_NAT) + if self.sync_all: l3plugin.send_set_controllers_update(self.ctx, True) self.sync_all = False + else: + l3plugin.send_set_controllers_update(self.ctx, False) plugin = manager.NeutronManager.get_plugin() plugin.create_or_update_agent(self.ctx, self.agent_state) self.bind_unscheduled_routers() -- 2.1.0 From d61d29e1694fe25eac675fa835a2080a80cbb962 Mon Sep 17 00:00:00 2001 From: Eran Gampel Date: Mon, 26 Jan 2015 14:05:28 +0200 Subject: [PATCH 8/8] remove unnecessary changes Change-Id: Idbefcb2c793b13b9ac147fedf4b068cf3a11be8f --- neutron/agent/linux/ovs_lib.py | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/neutron/agent/linux/ovs_lib.py b/neutron/agent/linux/ovs_lib.py index f745cf3..707291f 100644 --- a/neutron/agent/linux/ovs_lib.py +++ b/neutron/agent/linux/ovs_lib.py @@ -138,7 +138,6 @@ class BaseOVS(object): class OVSBridge(BaseOVS): - def __init__(self, br_name, root_helper): super(OVSBridge, self).__init__(root_helper) self.br_name = br_name @@ -148,6 +147,11 @@ class OVSBridge(BaseOVS): vsctl_command.extend(controller_names) self.run_vsctl(vsctl_command, check_error=True) + def set_controller_mode(self, mode): + self.run_vsctl(['--', 'set', 'controller', self.br_name, + "connection-mode=%s" % mode], + check_error=True) + def del_controller(self): self.run_vsctl(['--', 'del-controller', self.br_name], check_error=True) @@ -159,11 +163,6 @@ class OVSBridge(BaseOVS): return res.strip().split('\n') return res - def set_controller_mode(self, mode): - self.run_vsctl(['--', 'set', 'controller', self.br_name, - "connection-mode=%s" % mode], - check_error=True) - def set_secure_mode(self): self.run_vsctl(['--', 'set-fail-mode', self.br_name, 'secure'], check_error=True) @@ -215,11 +214,8 @@ class OVSBridge(BaseOVS): args = ["clear", table_name, record, column] self.run_vsctl(args) - def run_ofctl(self, cmd, args, process_input=None, protocols=None): - if protocols: - full_args = ["ovs-ofctl", cmd, protocols, self.br_name] + args - else: - full_args = ["ovs-ofctl", cmd, self.br_name] + args + def run_ofctl(self, cmd, args, process_input=None): + full_args = ["ovs-ofctl", cmd, self.br_name] + args try: return utils.execute(full_args, root_helper=self.root_helper, process_input=process_input) @@ -254,13 +250,12 @@ class OVSBridge(BaseOVS): return self.db_get_val('Bridge', self.br_name, 'datapath_id').strip('"') - def do_action_flows(self, action, kwargs_list, protocols=None): + def do_action_flows(self, action, kwargs_list): flow_strs = [_build_flow_expr_str(kw, action) for kw in kwargs_list] - self.run_ofctl('%s-flows' % action, ['-'], '\n'.join(flow_strs), - protocols) + self.run_ofctl('%s-flows' % action, ['-'], '\n'.join(flow_strs)) - def add_flow(self, protocols=None, **kwargs): - self.do_action_flows('add', [kwargs], protocols) + def add_flow(self, **kwargs): + self.do_action_flows('add', [kwargs]) def mod_flow(self, **kwargs): self.do_action_flows('mod', [kwargs]) -- 2.1.0