dragonflow/dragonflow/neutron/agent/l2/ovs_dragonflow_neutron_agen...

330 lines
14 KiB
Python

# Copyright (c) 2015 OpenStack Foundation.
#
# 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 signal
import sys
import threading
import eventlet
eventlet.monkey_patch()
from six import moves
from oslo_config import cfg
from neutron.agent.common import config
from neutron.agent.linux import ip_lib
from neutron.agent.ovsdb import api as ovsdb
from neutron.common import config as common_config
from neutron.common import constants as q_const
from neutron.common import utils as q_utils
from neutron.i18n import _, _LE, _LI
from neutron.plugins.common import constants as p_const
from neutron.plugins.openvswitch.agent import ovs_neutron_agent as ona
from neutron.plugins.openvswitch.common import constants
from oslo_log import log as logging
LOG = logging.getLogger(__name__)
agent_additional_opts = [
cfg.StrOpt('L3controller_ip_list',
default='tcp:localhost:6633',
help=("L3 Controler IP list list tcp:ip_addr:port;"
"tcp:ip_addr:port..;..")),
cfg.BoolOpt('enable_l3_controller', default=True,
help=_("L3 SDN Controller")),
cfg.IntOpt('tunnel_map_check_rate', default=5,
help=_("Rate in multiple of the rpc loop")),
]
cfg.CONF.register_opts(agent_additional_opts, "AGENT")
TUN_TRANSLATE_TABLE = 60
class L2OVSControllerAgent(ona.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,
prevent_arp_spoofing=False,
use_veth_interconnection=False,
quitting_rpc_timeout=None):
if prevent_arp_spoofing:
LOG.error(_LE("ARP Spoofing prevention is not"
" yet supported in Dragonflow feature disabled"))
prevent_arp_spoofing = False
'''
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
self.tunnel_map_check_rate = cfg.CONF.AGENT.tunnel_map_check_rate
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,
prevent_arp_spoofing,
use_veth_interconnection,
quitting_rpc_timeout)
self.df_available_local_vlans = set(moves.range(q_const.MIN_VLAN_TAG,
q_const.MAX_VLAN_TAG))
#mapping from vlan to destination tunnel ip
self.df_local_to_vlan_map = {}
self.controllers_ip_list = cfg.CONF.AGENT.L3controller_ip_list
# Initialize controller
self.set_controller_for_br(self.int_br, self.controllers_ip_list)
def set_controller_for_br(self, bridge, ip_address_list):
'''Set OpenFlow Controller on the Bridge .
:param bridge: the bridge object.
:param ip_address_list: tcp:ip_address:port;tcp:ip_address2:port
'''
if not self.enable_l3_controller:
LOG.info(_LI("Controller Base l3 is disabled on Agent"))
return
ip_address_ = ip_address_list.split(";")
LOG.debug("Set Controllers on br %s to %s", bridge.br_name,
ip_address_)
with self.set_controller_lock:
bridge.del_controller()
bridge.set_controller(ip_address_)
self.set_connection_mode(bridge, "out-of-band")
bridge.ovsdb.set_fail_mode(bridge.br_name,
"standalone").execute(check_error=True)
bridge.add_flow(priority=0, actions="normal")
bridge.add_flow(table=constants.CANARY_TABLE,
priority=0,
actions="drop")
if self.patch_tun_ofport > 0:
# Mark the tunnel ID so the data will be transferred to the
# br-tun virtual switch, tun id and metadata are local
self._add_tun_translate_pack_mark_flow(bridge)
# add the normal flow higher priority than the drop
for br in self.phys_brs.values():
br.add_flow(priority=3, actions="normal")
# add the vlan flows
cur_ports = self.int_br.get_vif_ports()
# use to initialize once each local vlan
l_vlan_map = set()
for port in cur_ports:
local_vlan_map = self.int_br.db_get_val("Port", port.port_name,
"other_config")
local_vlan = self.int_br.db_get_val("Port", port.port_name,
"tag")
net_uuid = local_vlan_map.get('net_uuid')
if (net_uuid and local_vlan != ona.DEAD_VLAN_TAG and
net_uuid not in l_vlan_map):
l_vlan_map.add(net_uuid)
self.provision_local_vlan2(
local_vlan_map['net_uuid'],
local_vlan_map['network_type'],
local_vlan_map['physical_network'],
local_vlan_map['segmentation_id'])
def _add_tun_translate_pack_mark_flow(self, bridge):
bridge.add_flow(table=TUN_TRANSLATE_TABLE, priority=1,
actions="move:NXM_NX_TUN_ID[0..31]"
"->NXM_NX_PKT_MARK[],"
"output:%s" %
(self.patch_tun_ofport))
def _check_tunnel_map_table(self):
"""Verify that the tunnel ip mapping is installed
on the integration bridge
"""
if p_const.TYPE_VLAN in self.tunnel_types:
# TODO(gampel) check for the vlan flows here
return
if not self.df_local_to_vlan_map:
return
tunnel_flows = self.int_br.dump_flows_for_table(
TUN_TRANSLATE_TABLE)
for tunnel_ip in self.df_local_to_vlan_map:
vlan_action = "mod_vlan_vid:%d" % (
self.df_local_to_vlan_map[tunnel_ip])
if vlan_action not in tunnel_flows:
self.tunnel_sync()
self._add_tun_translate_pack_mark_flow(self.int_br)
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
with self.set_controller_lock:
ret = super(L2OVSControllerAgent, self).check_ovs_status()
if not self.iter_num % self.tunnel_map_check_rate:
self._check_tunnel_map_table()
return ret
def _claim_df_tunnel_local_vlan(self, tunnel_ip_hex):
lvid = None
if tunnel_ip_hex in self.df_local_to_vlan_map:
lvid = self.df_local_to_vlan_map[tunnel_ip_hex]
else:
lvid = self.df_available_local_vlans.pop()
self.df_local_to_vlan_map[tunnel_ip_hex] = lvid
return lvid
def _release_df_tunnel_local_vlan(self, tunnel_ip_hex):
lvid = self.df_local_to_vlan_map.pop(tunnel_ip_hex, None)
self.df_available_local_vlans.add(lvid)
def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type):
items = list(self.tun_br_ofports[tunnel_type].items())
for remote_ip, ofport in items:
if ofport == tun_ofport:
tunnel_ip_hex = "0x%s" % self.get_ip_in_hex(remote_ip)
lvid = self.df_local_to_vlan_map[tunnel_ip_hex]
self.int_br.delete_flows(
table=TUN_TRANSLATE_TABLE,
reg7=tunnel_ip_hex)
br.delete_flows(
table=constants.UCAST_TO_TUN,
dl_vlan=lvid)
self._release_df_tunnel_local_vlan(tunnel_ip_hex)
return super(L2OVSControllerAgent, self).cleanup_tunnel_port(
br,
tun_ofport,
tunnel_type)
def _setup_tunnel_port(self, br, port_name, remote_ip, tunnel_type):
ofport = super(L2OVSControllerAgent, self) \
._setup_tunnel_port(
br,
port_name,
remote_ip,
tunnel_type)
if p_const.TYPE_VLAN not in self.tunnel_types:
tunnel_ip_hex = "0x%s" % self.get_ip_in_hex(remote_ip)
lvid = self._claim_df_tunnel_local_vlan(tunnel_ip_hex)
self.int_br.add_flow(
table=TUN_TRANSLATE_TABLE,
priority=2000,
reg7=tunnel_ip_hex,
actions="mod_vlan_vid:%s,"
"load:0->NXM_NX_REG7[0..31],"
"resubmit(,%s)" %
(lvid, TUN_TRANSLATE_TABLE))
br.add_flow(table=constants.UCAST_TO_TUN,
priority=100,
dl_vlan=lvid,
pkt_mark="0x80000000/0x80000000",
actions="strip_vlan,move:NXM_NX_PKT_MARK[0..30]"
"->NXM_NX_TUN_ID[0..30],"
"output:%s" %
(ofport))
if ofport > 0:
ofports = (ona._ofport_set_to_str
(self.tun_br_ofports[tunnel_type].values()))
if self.enable_l3_controller:
if ofports:
br.add_flow(table=constants.FLOOD_TO_TUN,
actions="move:NXM_NX_PKT_MARK[0..30]"
"->NXM_NX_TUN_ID[0..30],"
"output:%s" %
(ofports))
return ofport
def provision_local_vlan2(self, net_uuid, network_type, physical_network,
segmentation_id):
if network_type == p_const.TYPE_VLAN:
if physical_network in self.phys_brs:
#outbound
# The global vlan id is set in table 60
# from segmentation id/tun id
self.int_br.add_flow(table=TUN_TRANSLATE_TABLE,
priority=1,
actions="move:NXM_NX_TUN_ID[0..11]"
"->OXM_OF_VLAN_VID[],"
"output:%s" %
(self.int_ofports[physical_network]))
lvid = self.local_vlan_map.get(net_uuid).vlan
# inbound
self.int_br.add_flow(priority=1000,
in_port=self.
int_ofports[physical_network],
dl_vlan=segmentation_id,
actions="mod_vlan_vid:%s,normal" % lvid)
else:
LOG.error(_LE("Cannot provision VLAN network for "
"net-id=%(net_uuid)s - no bridge for "
"physical_network %(physical_network)s"),
{'net_uuid': net_uuid,
'physical_network': physical_network})
def set_connection_mode(self, bridge, mode):
ovsdb_api = ovsdb.API.get(bridge)
attrs = [('connection-mode', 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 = ona.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()