diff --git a/dragonflow/neutron/agent/l2/__init__.py b/dragonflow/neutron/agent/l2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dragonflow/neutron/agent/l2/ovs_dragonflow_neutron_agent.py b/dragonflow/neutron/agent/l2/ovs_dragonflow_neutron_agent.py new file mode 100644 index 000000000..4af880053 --- /dev/null +++ b/dragonflow/neutron/agent/l2/ovs_dragonflow_neutron_agent.py @@ -0,0 +1,319 @@ +# Copyright 2012 VMware, Inc. 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 netaddr +import signal +import sys +import threading + +import eventlet + + +eventlet.monkey_patch() + +from dragonflow.neutron.common.config import SDNCONTROLLER + +from oslo_config import cfg + +from neutron.agent.common import config +from neutron.agent.linux import ip_lib +from neutron.agent.linux import ovs_lib +from neutron.agent.ovsdb import api as ovsdb +from neutron.agent import rpc as agent_rpc +from neutron.agent import securitygroups_rpc as sg_rpc +from neutron.api.rpc.handlers import dvr_rpc + +from neutron.common import config as common_config +from neutron.common import topics +from neutron.common import utils as q_utils +from neutron import context + +from neutron.i18n import _LE, _LI, _LW +from neutron.openstack.common import log as logging +from neutron.plugins.openvswitch.agent import ovs_neutron_agent +from neutron.plugins.openvswitch.agent.ovs_neutron_agent import OVSNeutronAgent +from neutron.plugins.openvswitch.common import constants + +LOG = logging.getLogger(__name__) + +agent_additional_opts = [ + cfg.BoolOpt('enable_l3_controller', default=True, + help=_("L3 SDN Controller")) +] + +cfg.CONF.register_opts(agent_additional_opts, "AGENT") + + +class L2OVSControllerAgent(OVSNeutronAgent): + def __init__(self, integ_br, tun_br, local_ip, + bridge_mappings, polling_interval, tunnel_types=None, + veth_mtu=None, l2_population=False, + enable_distributed_routing=False, + minimize_polling=False, + ovsdb_monitor_respawn_interval=( + constants.DEFAULT_OVSDBMON_RESPAWN), + arp_responder=False, + use_veth_interconnection=False, + quitting_rpc_timeout=None): + + # Initialize controller Ip List + self.controllers_ip_list = None + ''' + Sync lock for Race condition set_controller <--> check_ovs_status + 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 + + super(L2OVSControllerAgent, self) \ + .__init__(integ_br, + tun_br, local_ip, + bridge_mappings, + polling_interval, + tunnel_types, + veth_mtu, l2_population, + enable_distributed_routing, + minimize_polling, + ovsdb_monitor_respawn_interval, + arp_responder, + use_veth_interconnection, + quitting_rpc_timeout) + + 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} + br.add_flow(table=table_id, + priority=100, + proto='arp', + metadata=segmentation_id, + 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") + 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)) + # Set controller out-of-band mode in new way + self.set_connection_mode(bridge, "out-of-band") + self.set_controller_lock.release() + + 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 check_ovs_status(self): + if not self.enable_l3_controller: + return super(L2OVSControllerAgent, self).check_ovs_status() + + # Check for the canary flow + # Add lock to avoid race condition of flows + self.set_controller_lock.acquire() + canary_flow = self.int_br.dump_flows_for_table(constants.CANARY_TABLE) + self.set_controller_lock.release() + + if canary_flow == '': + LOG.error("flow == null") + LOG.warn(_LW("OVS is restarted. OVSNeutronAgent will reset " + "bridges and recover ports.")) + return constants.OVS_RESTARTED + elif canary_flow is None: + LOG.error("flow == is none") + LOG.warn(_LW("OVS is dead. OVSNeutronAgent will keep running " + "and checking OVS status periodically.")) + return constants.OVS_DEAD + else: + # OVS is in normal status + return constants.OVS_NORMAL + + def setup_rpc(self): + self.agent_id = 'ovs-agent-%s' % cfg.CONF.host + self.topic = topics.AGENT + self.plugin_rpc = ovs_neutron_agent.OVSPluginApi(topics.PLUGIN) + self.sg_plugin_rpc = sg_rpc.SecurityGroupServerRpcApi(topics.PLUGIN) + self.dvr_plugin_rpc = dvr_rpc.DVRServerRpcApi(topics.PLUGIN) + self.state_rpc = agent_rpc.PluginReportStateAPI(topics.PLUGIN) + + # RPC network init + self.context = context.get_admin_context_without_session() + # Handle updates from service + self.endpoints = [self] + # Define the listening consumers for the agent + consumers = [[topics.PORT, topics.UPDATE], + [topics.NETWORK, topics.DELETE], + [constants.TUNNEL, topics.UPDATE], + [constants.TUNNEL, topics.DELETE], + [topics.SECURITY_GROUP, topics.UPDATE], + [topics.DVR, topics.UPDATE]] + if self.l2_pop: + consumers.append([topics.L2POPULATION, + topics.UPDATE, cfg.CONF.host]) + if self.enable_l3_controller: + consumers.append([SDNCONTROLLER, + topics.UPDATE]) + + self.connection = agent_rpc.create_consumers(self.endpoints, + self.topic, + consumers, + start_listening=False) + + def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type): + ofport = br.add_tunnel_port(port_name, + remote_ip, + self.local_ip, + tunnel_type, + self.vxlan_udp_port, + self.dont_fragment) + if ofport == ovs_lib.INVALID_OFPORT: + LOG.error(_LE("Failed to set-up %(type)s tunnel port to %(ip)s"), + {'type': tunnel_type, 'ip': remote_ip}) + return 0 + + self.tun_br_ofports[tunnel_type][remote_ip] = ofport + # Add flow in default table to resubmit to the right + # tunnelling table (lvid will be set in the latter) + br.add_flow(priority=1, + in_port=ofport, + actions="resubmit(,%s)" % + constants.TUN_TABLE[tunnel_type]) + + ofports = ovs_neutron_agent. \ + _ofport_set_to_str(self.tun_br_ofports[tunnel_type].values()) + if ofports and not self.l2_pop: + # Update flooding flows to include the new tunnel + for network_id, vlan_mapping in self.local_vlan_map.iteritems(): + if vlan_mapping.network_type == tunnel_type: + br.mod_flow(table=constants.FLOOD_TO_TUN, + 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 set_connection_mode(self, bridge, connection_mode): + ovsdb_api = ovsdb.API.get(bridge) + attrs = [('connection-mode', connection_mode)] + ovsdb_api.db_set('controller', bridge.br_name, *attrs).execute( + check_error=True) + + +def main(): + cfg.CONF.register_opts(ip_lib.OPTS) + config.register_root_helper(cfg.CONF) + common_config.init(sys.argv[1:]) + common_config.setup_logging() + q_utils.log_opt_values(LOG) + + try: + agent_config = ovs_neutron_agent.create_agent_config_map(cfg.CONF) + except ValueError as e: + LOG.error(_LE('%s Agent terminated!'), e) + sys.exit(1) + + is_xen_compute_host = 'rootwrap-xen-dom0' in cfg.CONF.AGENT.root_helper + if is_xen_compute_host: + # Force ip_lib to always use the root helper to ensure that ip + # commands target xen dom0 rather than domU. + cfg.CONF.set_default('ip_lib_force_root', True) + + agent = L2OVSControllerAgent(**agent_config) + + signal.signal(signal.SIGTERM, agent._handle_sigterm) + + # Start everything. + LOG.info(_LI("Agent initialized successfully, now running... ")) + agent.daemon_loop() + + +if __name__ == "__main__": + main() diff --git a/dragonflow/neutron/agent/l3/l3_controller_agent.py b/dragonflow/neutron/agent/l3/l3_controller_agent.py index d95f2a149..7b7b7d086 100644 --- a/dragonflow/neutron/agent/l3/l3_controller_agent.py +++ b/dragonflow/neutron/agent/l3/l3_controller_agent.py @@ -34,7 +34,6 @@ from neutron.openstack.common import log as logging from neutron.openstack.common import loopingcall from neutron.openstack.common import periodic_task - LOG = logging.getLogger(__name__) NET_CONTROL_L3_OPTS = [ @@ -48,7 +47,6 @@ NET_CONTROL_L3_OPTS = [ "element Currently supports only OpenFlow")) ] - cfg.CONF.register_opts(NET_CONTROL_L3_OPTS) diff --git a/dragonflow/neutron/common/__init__.py b/dragonflow/neutron/common/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/dragonflow/neutron/common/config.py b/dragonflow/neutron/common/config.py new file mode 100644 index 000000000..03144faaa --- /dev/null +++ b/dragonflow/neutron/common/config.py @@ -0,0 +1 @@ +SDNCONTROLLER = 'sdncontrol' diff --git a/dragonflow/neutron/services/l3/l3_controller_plugin.py b/dragonflow/neutron/services/l3/l3_controller_plugin.py index 226a737f7..706961dff 100755 --- a/dragonflow/neutron/services/l3/l3_controller_plugin.py +++ b/dragonflow/neutron/services/l3/l3_controller_plugin.py @@ -16,6 +16,8 @@ from oslo.config import cfg from oslo import messaging from oslo.utils import importutils +from dragonflow.neutron.common.config import SDNCONTROLLER + from neutron import context from neutron import manager @@ -35,7 +37,6 @@ from neutron.openstack.common import log as logging LOG = logging.getLogger(__name__) - NET_CONTROL_L3_OPTS = [ cfg.StrOpt('L3controller_ip_list', default='tcp:172.16.10.10:6633', @@ -206,7 +207,7 @@ class ControllerL3ServicePlugin(common_db_mixin.CommonDbMixin, ip_address): topic_port_update = topics.get_topic_name(topics.AGENT, - topics.PORT, + SDNCONTROLLER, topics.UPDATE) target = messaging.Target(topic=topic_port_update) rpcapi = n_rpc.get_client(target) @@ -231,7 +232,7 @@ class ControllerL3ServicePlugin(common_db_mixin.CommonDbMixin, def send_set_controllers_update(self, _context, force_reconnect): topic_port_update = topics.get_topic_name(topics.AGENT, - topics.PORT, + SDNCONTROLLER, topics.UPDATE) target = messaging.Target(topic=topic_port_update) rpcapi = n_rpc.get_client(target) diff --git a/setup.cfg b/setup.cfg index 58f9d6ea0..5bcd83c73 100644 --- a/setup.cfg +++ b/setup.cfg @@ -47,3 +47,4 @@ output_file = dragonflow/locale/dragonflow.pot [entry_points] console_scripts = neutron-l3-controller-agent = dragonflow.neutron.agent.l3_sdn_agent:main + neutron-l2-controller-agent = dragonflow.neutron.agent.l2.ovs_dragonflow_neutron_agent:main