reconnect_interval = 2
[OVS]
-# This enables the new OVSQuantumTunnelAgent which enables tunneling
-# between hybervisors. Leave it set to False or omit for legacy behavior.
-enable_tunneling = False
+# (ListOpt) Comma-separated list of
+# <physical_network>:<vlan_min>:<vlan_max> tuples enumerating ranges
+# of VLAN IDs on named physical networks that are available for
+# allocation.
+# network_vlan_ranges = default:1000:2999
+
+# (ListOpt) Comma-separated list of <tun_min>:<tun_max> tuples
+# enumerating ranges of GRE tunnel IDs that are available for
+# allocation.
+# tunnel_id_ranges =
# Do not change this parameter unless you have a good reason to.
# This is the name of the OVS integration bridge. There is one per hypervisor.
# The integration bridge acts as a virtual "patch port". All VM VIFs are
# attached to this bridge and then "patched" according to their network
# connectivity.
-integration_bridge = br-int
+# integration_bridge = br-int
-# Only used if enable-tunneling (above) is True.
+# Only used if tunnel_id_ranges (above) is not empty.
# In most cases, the default value should be fine.
-tunnel_bridge = br-tun
+# tunnel_bridge = br-tun
-# Uncomment this line if enable-tunneling is True above.
+# (ListOpt) Comma-separated list of <physical_network>:<bridge> tuples
+# mapping physical network names to agent's node-specific OVS bridge
+# names. Each bridge must exist, and should have physical network
+# interface configured as a port.
+# bridge_mappings = default:br-eth1
+
+# Uncomment this line if tunnel_id_ranges (above) is not empty.
# Set local-ip to be the local IP address of this hypervisor.
# local_ip = 10.0.0.3
-# Uncomment if you want to use custom VLAN range.
-# vlan_min = 1
-# vlan_max = 4094
-
[AGENT]
# Agent's polling interval in seconds
polling_interval = 2
# Sample Configurations.
#-----------------------------------------------------------------------------
#
-# 1. Without tunneling.
+# 1. With VLANs on eth1.
# [DATABASE]
# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum
# [OVS]
-# enable_tunneling = False
+# network_vlan_ranges = default:2000:3999
+# tunnel_id_ranges =
# integration_bridge = br-int
+# bridge_mappings = default:br-eth1
# [AGENT]
# root_helper = sudo
# Add the following setting, if you want to log to a file
-# log_file = /var/log/quantum/ovs_quantum_agent.log
#
# 2. With tunneling.
# [DATABASE]
# sql_connection = mysql://root:nova@127.0.0.1:3306/ovs_quantum
# [OVS]
-# enable_tunneling = True
+# network_vlan_ranges =
+# tunnel_id_ranges = 1:1000
# integration_bridge = br-int
# tunnel_bridge = br-tun
-# remote-ip-file = /opt/stack/remote-ips.txt
# local_ip = 10.0.0.3
# [AGENT]
# root_helper = sudo
self.run_vsctl(["--", "--if-exists", "del-br", self.br_name])
self.run_vsctl(["add-br", self.br_name])
+ def add_port(self, port_name):
+ self.run_vsctl(["--", "--may-exist", "add-port", self.br_name,
+ port_name])
+ return self.get_port_ofport(port_name)
+
def delete_port(self, port_name):
self.run_vsctl(["--", "--if-exists", "del-port", self.br_name,
port_name])
"%(physical_network)s is in use.")
+class TunnelIdInUse(InUse):
+ message = _("Unable to create the network. "
+ "The tunnel ID %(tunnel_id)s is in use.")
+
+
class ResourceExhausted(QuantumException):
pass
from sqlalchemy.ext import sqlsoup
from quantum.agent import rpc as agent_rpc
+from quantum.agent.linux import ip_lib
from quantum.agent.linux import ovs_lib
from quantum.agent.linux import utils
-from quantum.common import constants
+from quantum.common import constants as q_const
from quantum.common import config as logging_config
from quantum.common import topics
from quantum.openstack.common import cfg
from quantum.openstack.common import rpc
from quantum.openstack.common.rpc import dispatcher
from quantum.plugins.openvswitch.common import config
+from quantum.plugins.openvswitch.common import constants
logging.basicConfig()
LOG = logging.getLogger(__name__)
# A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'
# attributes set).
class LocalVLANMapping:
- def __init__(self, vlan, lsw_id, vif_ids=None):
+ def __init__(self, vlan, network_type, physical_network, physical_id,
+ vif_ids=None):
if vif_ids is None:
vif_ids = []
self.vlan = vlan
- self.lsw_id = lsw_id
+ self.network_type = network_type
+ self.physical_network = physical_network
+ self.physical_id = physical_id
self.vif_ids = vif_ids
def __str__(self):
- return "lv-id = %s ls-id = %s" % (self.vlan, self.lsw_id)
+ return ("lv-id = %s type = %s phys-net = %s phys-id = %s" %
+ (self.vlan, self.network_type, self.physical_network,
+ self.physical_id))
class Port(object):
class OVSQuantumAgent(object):
-
- def __init__(self, integ_br, root_helper, polling_interval,
- reconnect_interval, rpc):
- self.root_helper = root_helper
- self.setup_integration_br(integ_br)
- self.polling_interval = polling_interval
- self.reconnect_interval = reconnect_interval
- self.rpc = rpc
- if rpc:
- self.setup_rpc(integ_br)
-
- def setup_rpc(self, integ_br):
- mac = utils.get_interface_mac(integ_br)
- self.agent_id = '%s' % (mac.replace(":", ""))
- self.topic = topics.AGENT
- self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
-
- # RPC network init
- self.context = context.RequestContext('quantum', 'quantum',
- is_admin=False)
- # Handle updates from service
- self.callbacks = OVSRpcCallbacks(self.context, self.int_br)
- self.dispatcher = self.callbacks.create_rpc_dispatcher()
- # Define the listening consumers for the agent
- consumers = [[topics.PORT, topics.UPDATE],
- [topics.NETWORK, topics.DELETE]]
- self.connection = agent_rpc.create_consumers(self.dispatcher,
- self.topic,
- consumers)
-
- def port_bound(self, port, vlan_id):
- self.int_br.set_db_attribute("Port", port.port_name,
- "tag", str(vlan_id))
- self.int_br.delete_flows(in_port=port.ofport)
-
- def port_unbound(self, port, still_exists):
- if still_exists:
- self.int_br.clear_db_attribute("Port", port.port_name, "tag")
-
- def setup_integration_br(self, integ_br):
- self.int_br = ovs_lib.OVSBridge(integ_br, self.root_helper)
- self.int_br.remove_all_flows()
- # switch all traffic using L2 learning
- self.int_br.add_flow(priority=1, actions="normal")
-
- def db_loop(self, db_connection_url):
- '''Main processing loop for Non-Tunneling Agent.
-
- :param options: database information - in the event need to reconnect
- '''
- self.local_vlan_map = {}
- old_local_bindings = {}
- old_vif_ports = {}
- db_connected = False
-
- while True:
- if not db_connected:
- time.sleep(self.reconnect_interval)
- db = sqlsoup.SqlSoup(db_connection_url)
- db_connected = True
- LOG.info("Connecting to database \"%s\" on %s" %
- (db.engine.url.database, db.engine.url.host))
-
- all_bindings = {}
- try:
- ports = db.ports.all()
- except Exception, e:
- LOG.info("Unable to get port bindings! Exception: %s" % e)
- db_connected = False
- continue
-
- for port in ports:
- all_bindings[port.id] = port
-
- vlan_bindings = {}
- try:
- vlan_binds = db.vlan_bindings.all()
- except Exception, e:
- LOG.info("Unable to get vlan bindings! Exception: %s" % e)
- db_connected = False
- continue
-
- for bind in vlan_binds:
- vlan_bindings[bind.network_id] = bind.vlan_id
-
- new_vif_ports = {}
- new_local_bindings = {}
- vif_ports = self.int_br.get_vif_ports()
- for p in vif_ports:
- new_vif_ports[p.vif_id] = p
- if p.vif_id in all_bindings:
- net_id = all_bindings[p.vif_id].network_id
- new_local_bindings[p.vif_id] = net_id
- else:
- # no binding, put him on the 'dead vlan'
- self.int_br.set_db_attribute("Port", p.port_name, "tag",
- DEAD_VLAN_TAG)
- self.int_br.add_flow(priority=2,
- in_port=p.ofport,
- actions="drop")
-
- old_b = old_local_bindings.get(p.vif_id, None)
- new_b = new_local_bindings.get(p.vif_id, None)
-
- if old_b != new_b:
- if old_b is not None:
- LOG.info("Removing binding to net-id = %s for %s"
- % (old_b, str(p)))
- self.port_unbound(p, True)
- if p.vif_id in all_bindings:
- all_bindings[p.vif_id].status = (
- constants.PORT_STATUS_DOWN)
- if new_b is not None:
- # If we don't have a binding we have to stick it on
- # the dead vlan
- net_id = all_bindings[p.vif_id].network_id
- vlan_id = vlan_bindings.get(net_id, DEAD_VLAN_TAG)
- self.port_bound(p, vlan_id)
- if p.vif_id in all_bindings:
- all_bindings[p.vif_id].status = (
- constants.PORT_STATUS_ACTIVE)
- LOG.info(("Adding binding to net-id = %s "
- "for %s on vlan %s") %
- (new_b, str(p), vlan_id))
-
- for vif_id in old_vif_ports:
- if vif_id not in new_vif_ports:
- LOG.info("Port Disappeared: %s" % vif_id)
- if vif_id in old_local_bindings:
- old_b = old_local_bindings[vif_id]
- self.port_unbound(old_vif_ports[vif_id], False)
- if vif_id in all_bindings:
- all_bindings[vif_id].status = (
- constants.PORT_STATUS_DOWN)
-
- old_vif_ports = new_vif_ports
- old_local_bindings = new_local_bindings
- try:
- db.commit()
- except Exception, e:
- LOG.info("Unable to commit to database! Exception: %s" % e)
- db.rollback()
- old_local_bindings = {}
- old_vif_ports = {}
-
- time.sleep(self.polling_interval)
-
- def update_ports(self, registered_ports):
- ports = self.int_br.get_vif_port_set()
- if ports == registered_ports:
- return
- added = ports - registered_ports
- removed = registered_ports - ports
- return {'current': ports,
- 'added': added,
- 'removed': removed}
-
- def treat_devices_added(self, devices):
- resync = False
- for device in devices:
- LOG.info("Port %s added", device)
- try:
- details = self.plugin_rpc.get_device_details(self.context,
- device,
- self.agent_id)
- except Exception as e:
- LOG.debug("Unable to get port details for %s: %s", device, e)
- resync = True
- continue
- if 'port_id' in details:
- LOG.info("Port %s updated. Details: %s", device, details)
- port = self.int_br.get_vif_port_by_id(details['port_id'])
- if port:
- if details['admin_state_up']:
- self.port_bound(port, details['vlan_id'])
- else:
- self.port_unbound(port, True)
- else:
- LOG.debug("Device %s not defined on plugin", device)
- return resync
-
- def treat_devices_removed(self, devices):
- resync = False
- for device in devices:
- LOG.info("Attachment %s removed", device)
- try:
- details = self.plugin_rpc.update_device_down(self.context,
- device,
- self.agent_id)
- except Exception as e:
- LOG.debug("port_removed failed for %s: %s", device, e)
- resync = True
- if details['exists']:
- LOG.info("Port %s updated.", device)
- # Nothing to do regarding local networking
- else:
- LOG.debug("Device %s not defined on plugin", device)
- return resync
-
- def process_network_ports(self, port_info):
- resync_a = False
- resync_b = False
- if 'added' in port_info:
- resync_a = self.treat_devices_added(port_info['added'])
- if 'removed' in port_info:
- resync_b = self.treat_devices_removed(port_info['removed'])
- # If one of the above opertaions fails => resync with plugin
- return (resync_a | resync_b)
-
- def rpc_loop(self):
- sync = True
- ports = set()
-
- while True:
- start = time.time()
- if sync:
- LOG.info("Agent out of sync with plugin!")
- ports.clear()
- sync = False
-
- port_info = self.update_ports(ports)
-
- # notify plugin about port deltas
- if port_info:
- LOG.debug("Agent loop has new devices!")
- # If treat devices fails - indicates must resync with plugin
- sync = self.process_network_ports(port_info)
- ports = port_info['current']
-
- # sleep till end of polling interval
- elapsed = (time.time() - start)
- if (elapsed < self.polling_interval):
- time.sleep(self.polling_interval - elapsed)
- else:
- LOG.debug("Loop iteration exceeded interval (%s vs. %s)!",
- self.polling_interval, elapsed)
-
- def daemon_loop(self, db_connection_url):
- if self.rpc:
- self.rpc_loop()
- else:
- self.db_loop(db_connection_url)
-
-
-class OVSQuantumTunnelAgent(object):
- '''Implements OVS-based tunneling.
-
- Two local bridges are created: an integration bridge (defaults to 'br-int')
- and a tunneling bridge (defaults to 'br-tun').
-
- All VM VIFs are plugged into the integration bridge. VMs for a given tenant
- share a common "local" VLAN (i.e. not propagated externally). The VLAN id
- of this local VLAN is mapped to a Logical Switch (LS) identifier and is
- used to differentiate tenant traffic on inter-HV tunnels.
-
- A mesh of tunnels is created to other Hypervisors in the cloud. These
- tunnels originate and terminate on the tunneling bridge of each hypervisor.
-
- Port patching is done to connect local VLANs on the integration bridge
- to inter-hypervisor tunnels on the tunnel bridge.
+ '''Implements OVS-based tunneling, VLANs and flat networks.
+
+ Two local bridges are created: an integration bridge (defaults to
+ 'br-int') and a tunneling bridge (defaults to 'br-tun'). An
+ additional bridge is created for each physical network interface
+ used for VLANs and/or flat networks.
+
+ All VM VIFs are plugged into the integration bridge. VM VIFs on a
+ given virtual network share a common "local" VLAN (i.e. not
+ propagated externally). The VLAN id of this local VLAN is mapped
+ to the physical networking details realizing that virtual network.
+
+ For virtual networks realized as GRE tunnels, a Logical Switch
+ (LS) identifier and is used to differentiate tenant traffic on
+ inter-HV tunnels. A mesh of tunnels is created to other
+ Hypervisors in the cloud. These tunnels originate and terminate on
+ the tunneling bridge of each hypervisor. Port patching is done to
+ connect local VLANs on the integration bridge to inter-hypervisor
+ tunnels on the tunnel bridge.
+
+ For each virtual networks realized as a VLANs or flat network, a
+ veth is used to connect the local VLAN on the integration bridge
+ with the physical network bridge, with flow rules adding,
+ modifying, or stripping VLAN tags as necessary.
'''
# Lower bound on available vlans.
# Upper bound on available vlans.
MAX_VLAN_TAG = 4094
- def __init__(self, integ_br, tun_br, local_ip, root_helper,
+ def __init__(self, integ_br, tun_br, local_ip,
+ bridge_mappings, root_helper,
polling_interval, reconnect_interval, rpc):
'''Constructor.
:param integ_br: name of the integration bridge.
:param tun_br: name of the tunnel bridge.
:param local_ip: local IP address of this hypervisor.
+ :param bridge_mappings: mappings from phyiscal interface to bridge.
:param root_helper: utility to use when running shell cmds.
:param polling_interval: interval (secs) to poll DB.
:param reconnect_internal: retry interval (secs) on DB error.
'''
self.root_helper = root_helper
self.available_local_vlans = set(
- xrange(OVSQuantumTunnelAgent.MIN_VLAN_TAG,
- OVSQuantumTunnelAgent.MAX_VLAN_TAG))
+ xrange(OVSQuantumAgent.MIN_VLAN_TAG,
+ OVSQuantumAgent.MAX_VLAN_TAG))
self.setup_integration_br(integ_br)
+ self.setup_physical_bridges(bridge_mappings)
self.local_vlan_map = {}
self.polling_interval = polling_interval
self.local_ip = local_ip
self.tunnel_count = 0
self.setup_tunnel_br(tun_br)
+
self.rpc = rpc
if rpc:
self.setup_rpc(integ_br)
# Define the listening consumers for the agent
consumers = [[topics.PORT, topics.UPDATE],
[topics.NETWORK, topics.DELETE],
- [config.TUNNEL, topics.UPDATE]]
+ [constants.TUNNEL, topics.UPDATE]]
self.connection = agent_rpc.create_consumers(self.dispatcher,
self.topic,
consumers)
- def provision_local_vlan(self, net_uuid, lsw_id):
+ def provision_local_vlan(self, net_uuid, network_type, physical_network,
+ physical_id):
'''Provisions a local VLAN.
:param net_uuid: the uuid of the network associated with this vlan.
- :param lsw_id: the logical switch id of this vlan.'''
+ :param network_type: the type of the network ('gre', 'vlan', 'flat')
+ :param physical_network: the physical network for 'vlan' or 'flat'
+ :param physical_id: the VLAN ID for 'vlan' or tunnel ID for 'tunnel'
+ '''
+
if not self.available_local_vlans:
- raise Exception("No local VLANs available for ls-id = %s" % lsw_id)
+ raise Exception("No local VLAN available for net-id=%s" % net_uuid)
lvid = self.available_local_vlans.pop()
LOG.info("Assigning %s as local vlan for net-id=%s" % (lvid, net_uuid))
- self.local_vlan_map[net_uuid] = LocalVLANMapping(lvid, lsw_id)
-
- # outbound
- self.tun_br.add_flow(priority=4, in_port=self.patch_int_ofport,
- dl_vlan=lvid,
- actions="set_tunnel:%s,normal" % lsw_id)
- # inbound bcast/mcast
- self.tun_br.add_flow(priority=3, tun_id=lsw_id,
- dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
- actions="mod_vlan_vid:%s,output:%s" %
- (lvid, self.patch_int_ofport))
+ self.local_vlan_map[net_uuid] = LocalVLANMapping(lvid, network_type,
+ physical_network,
+ physical_id)
+
+ if network_type == constants.TYPE_GRE:
+ # outbound
+ self.tun_br.add_flow(priority=4, in_port=self.patch_int_ofport,
+ dl_vlan=lvid,
+ actions="set_tunnel:%s,normal" % physical_id)
+ # inbound bcast/mcast
+ self.tun_br.add_flow(priority=3, tun_id=physical_id,
+ dl_dst="01:00:00:00:00:00/01:00:00:00:00:00",
+ actions="mod_vlan_vid:%s,output:%s" %
+ (lvid, self.patch_int_ofport))
+ elif network_type == constants.TYPE_FLAT:
+ # outbound
+ br = self.phys_brs[physical_network]
+ br.add_flow(priority=4,
+ in_port=self.phys_ofports[physical_network],
+ dl_vlan=lvid,
+ actions="strip_vlan,normal")
+ # inbound
+ self.int_br.add_flow(priority=3,
+ in_port=self.int_ofports[physical_network],
+ dl_vlan=0xffff,
+ actions="mod_vlan_vid:%s,normal" % lvid)
+ elif network_type == constants.TYPE_VLAN:
+ # outbound
+ br = self.phys_brs[physical_network]
+ br.add_flow(priority=4,
+ in_port=self.phys_ofports[physical_network],
+ dl_vlan=lvid,
+ actions="mod_vlan_vid:%s,normal" % physical_id)
+ # inbound
+ self.int_br.add_flow(priority=3,
+ in_port=self.int_ofports[physical_network],
+ dl_vlan=physical_id,
+ actions="mod_vlan_vid:%s,normal" % lvid)
+ else:
+ LOG.error("provisioning unknown network type %s for net-id=%s" %
+ (network_type, net_uuid))
def reclaim_local_vlan(self, net_uuid, lvm):
'''Reclaim a local VLAN.
:param lvm: a LocalVLANMapping object that tracks (vlan, lsw_id,
vif_ids) mapping.'''
LOG.info("reclaming vlan = %s from net-id = %s" % (lvm.vlan, net_uuid))
- self.tun_br.delete_flows(tun_id=lvm.lsw_id)
- self.tun_br.delete_flows(dl_vlan=lvm.vlan)
+
+ if lvm.network_type == constants.TYPE_GRE:
+ self.tun_br.delete_flows(tun_id=lvm.physical_id)
+ self.tun_br.delete_flows(dl_vlan=lvm.vlan)
+ elif network_type == constants.TYPE_FLAT:
+ # outbound
+ br = self.phys_brs[lvm.physical_network]
+ br.delete_flows(in_port=self.phys_ofports[lvm.physical_network],
+ dl_vlan=lvm.vlan)
+ # inbound
+ br = self.int_br
+ br.delete_flows(in_port=self.int_ofports[lvm.physical_network],
+ dl_vlan=0xffff)
+ elif network_type == constants.TYPE_VLAN:
+ # outbound
+ br = self.phys_brs[lvm.physical_network]
+ br.delete_flows(in_port=self.phys_ofports[lvm.physical_network],
+ dl_vlan=lvm.vlan)
+ # inbound
+ br = self.int_br
+ br.delete_flows(in_port=self.int_ofports[lvm.physical_network],
+ dl_vlan=lvm.physical_id)
+ else:
+ LOG.error("reclaiming unknown network type %s for net-id=%s" %
+ (lvm.network_type, net_uuid))
+
del self.local_vlan_map[net_uuid]
self.available_local_vlans.add(lvm.vlan)
- def port_bound(self, port, net_uuid, lsw_id):
+ def port_bound(self, port, net_uuid,
+ network_type, physical_network, physical_id):
'''Bind port to net_uuid/lsw_id and install flow for inbound traffic
to vm.
:param port: a ovslib.VifPort object.
:param net_uuid: the net_uuid this port is to be associated with.
- :param lsw_id: the logical switch this port is to be associated with.
+ :param network_type: the type of the network ('gre', 'vlan', 'flat')
+ :param physical_network: the physical network for 'vlan' or 'flat'
+ :param physical_id: the VLAN ID for 'vlan' or tunnel ID for 'tunnel'
'''
if net_uuid not in self.local_vlan_map:
- self.provision_local_vlan(net_uuid, lsw_id)
+ self.provision_local_vlan(net_uuid, network_type,
+ physical_network, physical_id)
lvm = self.local_vlan_map[net_uuid]
lvm.vif_ids.append(port.vif_id)
- # inbound unicast
- self.tun_br.add_flow(priority=3, tun_id=lsw_id, dl_dst=port.vif_mac,
- actions="mod_vlan_vid:%s,normal" % lvm.vlan)
+ if network_type == constants.TYPE_GRE:
+ # inbound unicast
+ self.tun_br.add_flow(priority=3, tun_id=physical_id,
+ dl_dst=port.vif_mac,
+ actions="mod_vlan_vid:%s,normal" % lvm.vlan)
self.int_br.set_db_attribute("Port", port.port_name, "tag",
str(lvm.vlan))
return
lvm = self.local_vlan_map[net_uuid]
+ # REVISIT(rkukura): Does inbound unicast flow need to be removed here?
+
if port.vif_id in lvm.vif_ids:
lvm.vif_ids.remove(port.vif_id)
else:
self.tun_br.remove_all_flows()
self.tun_br.add_flow(priority=1, actions="drop")
+ def setup_physical_bridges(self, bridge_mappings):
+ '''Setup the physical network bridges.
+
+ Creates phyiscal network bridges and links them to the
+ integration bridge using veths.
+
+ :param bridge_mappings: map physical network names to bridge names.'''
+ self.phys_brs = {}
+ self.int_ofports = {}
+ self.phys_ofports = {}
+ ip_wrapper = ip_lib.IPWrapper(self.root_helper)
+ for physical_network, bridge in bridge_mappings.iteritems():
+ # setup physical bridge
+ if not ip_lib.device_exists(bridge, self.root_helper):
+ LOG.error("Bridge %s for physical network %s does not exist" %
+ (bridge, physical_network))
+ sys.exit(1)
+ br = ovs_lib.OVSBridge(bridge, self.root_helper)
+ br.remove_all_flows()
+ br.add_flow(priority=1, actions="normal")
+ self.phys_brs[physical_network] = br
+
+ # create veth to patch physical bridge with integration bridge
+ int_veth_name = constants.VETH_INTEGRATION_PREFIX + bridge
+ self.int_br.delete_port(int_veth_name)
+ phys_veth_name = constants.VETH_PHYSICAL_PREFIX + bridge
+ br.delete_port(phys_veth_name)
+ if ip_lib.device_exists(int_veth_name, self.root_helper):
+ ip_lib.IPDevice(int_veth_name, self.root_helper).link.delete()
+ int_veth, phys_veth = ip_wrapper.add_veth(int_veth_name,
+ phys_veth_name)
+ self.int_ofports[physical_network] = self.int_br.add_port(int_veth)
+ self.phys_ofports[physical_network] = br.add_port(phys_veth)
+
+ # block all untranslated traffic over veth between bridges
+ self.int_br.add_flow(priority=2,
+ in_port=self.int_ofports[physical_network],
+ actions="drop")
+ br.add_flow(priority=2,
+ in_port=self.phys_ofports[physical_network],
+ actions="drop")
+
+ # enable veth to pass traffic
+ int_veth.link.set_up()
+ phys_veth.link.set_up()
+
def manage_tunnels(self, tunnel_ips, old_tunnel_ips, db):
if self.local_ip in tunnel_ips:
tunnel_ips.remove(self.local_ip)
else:
- db.tunnel_ips.insert(ip_address=self.local_ip)
+ db.ovs_tunnel_ips.insert(ip_address=self.local_ip)
new_tunnel_ips = tunnel_ips - old_tunnel_ips
if new_tunnel_ips:
all_bindings = dict((p.id, Port(p))
for p in db.ports.all())
all_bindings_vif_port_ids = set(all_bindings)
- lsw_id_bindings = dict((bind.network_id, bind.vlan_id)
- for bind in db.vlan_bindings.all())
+ net_bindings = dict((bind.network_id, bind)
+ for bind in
+ db.ovs_network_bindings.all())
- tunnel_ips = set(x.ip_address for x in db.tunnel_ips.all())
+ tunnel_ips = set(x.ip_address for x in db.ovs_tunnel_ips.all())
self.manage_tunnels(tunnel_ips, old_tunnel_ips, db)
# Get bindings from OVS bridge.
if b[2] != b[1]])
LOG.debug('all_bindings: %s', all_bindings)
- LOG.debug('lsw_id_bindings: %s', lsw_id_bindings)
+ LOG.debug('net_bindings: %s', net_bindings)
LOG.debug('new_vif_ports_ids: %s', new_vif_ports_ids)
LOG.debug('dead_vif_ports_ids: %s', dead_vif_ports_ids)
LOG.debug('old_vif_ports_ids: %s', old_vif_ports_ids)
self.port_unbound(p, old_net_uuid)
if p.vif_id in all_bindings:
all_bindings[p.vif_id].status = (
- constants.PORT_STATUS_DOWN)
+ q_const.PORT_STATUS_DOWN)
if not new_port:
self.port_dead(p)
if new_port:
new_net_uuid = new_port.network_id
- if new_net_uuid not in lsw_id_bindings:
- LOG.warn("No ls-id binding found for net-id '%s'" %
- new_net_uuid)
+ if new_net_uuid not in net_bindings:
+ LOG.warn("No network binding found for net-id"
+ " '%s'" % new_net_uuid)
continue
- lsw_id = lsw_id_bindings[new_net_uuid]
- self.port_bound(p, new_net_uuid, lsw_id)
+ bind = net_bindings[new_net_uuid]
+ self.port_bound(p, new_net_uuid,
+ bind.network_type,
+ bind.physical_network,
+ bind.physical_id)
all_bindings[p.vif_id].status = (
- constants.PORT_STATUS_ACTIVE)
+ q_const.PORT_STATUS_ACTIVE)
LOG.info("Port %s on net-id = %s bound to %s " % (
str(p), new_net_uuid,
str(self.local_vlan_map[new_net_uuid])))
LOG.info("Port Disappeared: " + vif_id)
if vif_id in all_bindings:
all_bindings[vif_id].status = (
- constants.PORT_STATUS_DOWN)
+ q_const.PORT_STATUS_DOWN)
old_port = old_local_bindings.get(vif_id)
if old_port:
self.port_unbound(old_vif_ports[vif_id],
if port:
if details['admin_state_up']:
self.port_bound(port, details['network_id'],
- details['vlan_id'])
+ details['network_type'],
+ details['physical_network'],
+ details['physical_id'])
else:
self.port_unbound(port, details['network_id'])
else:
# (TODO) gary - swap with common logging
logging_config.setup_logging(cfg.CONF)
- # Determine which agent type to use.
- enable_tunneling = cfg.CONF.OVS.enable_tunneling
integ_br = cfg.CONF.OVS.integration_bridge
db_connection_url = cfg.CONF.DATABASE.sql_connection
polling_interval = cfg.CONF.AGENT.polling_interval
reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
root_helper = cfg.CONF.AGENT.root_helper
rpc = cfg.CONF.AGENT.rpc
+ tun_br = cfg.CONF.OVS.tunnel_bridge
+ local_ip = cfg.CONF.OVS.local_ip
- if enable_tunneling:
- # Get parameters for OVSQuantumTunnelAgent
- tun_br = cfg.CONF.OVS.tunnel_bridge
- # Mandatory parameter.
- local_ip = cfg.CONF.OVS.local_ip
- plugin = OVSQuantumTunnelAgent(integ_br, tun_br, local_ip, root_helper,
- polling_interval, reconnect_interval,
- rpc)
- else:
- # Get parameters for OVSQuantumAgent.
- plugin = OVSQuantumAgent(integ_br, root_helper, polling_interval,
- reconnect_interval, rpc)
+ bridge_mappings = {}
+ for mapping in cfg.CONF.OVS.bridge_mappings:
+ mapping = mapping.strip()
+ if mapping != '':
+ try:
+ physical_network, bridge = mapping.split(':')
+ bridge_mappings[physical_network] = bridge
+ LOG.debug("physical network %s mapped to bridge %s" %
+ (physical_network, bridge))
+ except ValueError as ex:
+ LOG.error("Invalid bridge mapping: \'%s\' - %s" %
+ (mapping, ex))
+ sys.exit(1)
+
+ plugin = OVSQuantumAgent(integ_br, tun_br, local_ip, bridge_mappings,
+ root_helper, polling_interval,
+ reconnect_interval, rpc)
# Start everything.
plugin.daemon_loop(db_connection_url)
from quantum.openstack.common import cfg
-# Topic for tunnel notifications between the plugin and agent
-TUNNEL = 'tunnel'
+DEFAULT_BRIDGE_MAPPINGS = ['default:br-eth1']
+DEFAULT_VLAN_RANGES = ['default:1000:2999']
+DEFAULT_TUNNEL_RANGES = []
database_opts = [
cfg.StrOpt('sql_connection', default='sqlite://'),
]
ovs_opts = [
- cfg.BoolOpt('enable_tunneling', default=False),
cfg.StrOpt('integration_bridge', default='br-int'),
cfg.StrOpt('tunnel_bridge', default='br-tun'),
cfg.StrOpt('local_ip', default='10.0.0.3'),
- cfg.IntOpt('vlan_min', default=1),
- cfg.IntOpt('vlan_max', default=4094),
+ cfg.ListOpt('bridge_mappings',
+ default=DEFAULT_BRIDGE_MAPPINGS,
+ help="List of <physical_network>:<bridge>"),
+ cfg.ListOpt('network_vlan_ranges',
+ default=DEFAULT_VLAN_RANGES,
+ help="List of <physical_network>:<vlan_min>:<vlan_max> "
+ "or <physical_network>"),
+ cfg.ListOpt('tunnel_id_ranges',
+ default=DEFAULT_TUNNEL_RANGES,
+ help="List of <tun_min>:<tun_max>"),
]
agent_opts = [
cfg.IntOpt('polling_interval', default=2),
cfg.StrOpt('root_helper', default='sudo'),
- cfg.StrOpt('log_file', default=None),
cfg.BoolOpt('rpc', default=True),
]
--- /dev/null
+# Copyright (c) 2012 OpenStack, LLC.
+#
+# 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.
+
+# Special vlan_id value in ovs_vlan_allocations table indicating flat network
+FLAT_VLAN_ID = -1
+
+# Topic for tunnel notifications between the plugin and agent
+TUNNEL = 'tunnel'
+
+# Values for network_type
+TYPE_FLAT = 'flat'
+TYPE_VLAN = 'vlan'
+TYPE_GRE = 'gre'
+
+# Name prefixes for veth device pair linking the integration bridge
+# with the physical bridge for a physical network
+VETH_INTEGRATION_PREFIX = 'int-'
+VETH_PHYSICAL_PREFIX = 'phy-'
LOG = logging.getLogger(__name__)
-def get_vlans():
- session = db.get_session()
- try:
- bindings = (session.query(ovs_models_v2.VlanBinding).
- all())
- except exc.NoResultFound:
- return []
- return [(binding.vlan_id, binding.network_id) for binding in bindings]
+def initialize():
+ options = {"sql_connection": "%s" % cfg.CONF.DATABASE.sql_connection}
+ options.update({"sql_max_retries": cfg.CONF.DATABASE.sql_max_retries})
+ options.update({"reconnect_interval":
+ cfg.CONF.DATABASE.reconnect_interval})
+ options.update({"base": models_v2.model_base.BASEV2})
+ db.configure_db(options)
-def get_vlan(net_id, session=None):
+def get_network_binding(session, network_id):
session = session or db.get_session()
try:
- binding = (session.query(ovs_models_v2.VlanBinding).
- filter_by(network_id=net_id).
+ binding = (session.query(ovs_models_v2.NetworkBinding).
+ filter_by(network_id=network_id).
one())
+ return binding
except exc.NoResultFound:
return
- return binding.vlan_id
-def add_vlan_binding(vlan_id, net_id, session):
+def add_network_binding(session, network_id, network_type,
+ physical_network, physical_id):
with session.begin(subtransactions=True):
- binding = ovs_models_v2.VlanBinding(vlan_id, net_id)
+ binding = ovs_models_v2.NetworkBinding(network_id, network_type,
+ physical_network,
+ physical_id)
session.add(binding)
- return binding
-def remove_vlan_binding(net_id):
+def sync_vlan_allocations(network_vlan_ranges):
+ """Synchronize vlan_allocations table with configured VLAN ranges"""
+
+ session = db.get_session()
+ with session.begin():
+ # process vlan ranges for each physical network separately
+ for physical_network, vlan_ranges in network_vlan_ranges.iteritems():
+
+ # determine current configured allocatable vlans for this
+ # physical network
+ vlan_ids = set()
+ for vlan_range in vlan_ranges:
+ vlan_ids |= set(xrange(vlan_range[0], vlan_range[1] + 1))
+
+ # remove from table unallocated vlans not currently allocatable
+ try:
+ allocs = (session.query(ovs_models_v2.VlanAllocation).
+ filter_by(physical_network=physical_network).
+ all())
+ for alloc in allocs:
+ try:
+ # see if vlan is allocatable
+ vlan_ids.remove(alloc.vlan_id)
+ except KeyError:
+ # it's not allocatable, so check if its allocated
+ if not alloc.allocated:
+ # it's not, so remove it from table
+ LOG.debug("removing vlan %s on physical network "
+ "%s from pool" %
+ (alloc.vlan_id, physical_network))
+ session.delete(alloc)
+ except exc.NoResultFound:
+ pass
+
+ # add missing allocatable vlans to table
+ for vlan_id in sorted(vlan_ids):
+ alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
+ session.add(alloc)
+
+
+def get_vlan_allocation(physical_network, vlan_id):
session = db.get_session()
try:
- binding = (session.query(ovs_models_v2.VlanBinding).
- filter_by(network_id=net_id).
- one())
- session.delete(binding)
+ alloc = (session.query(ovs_models_v2.VlanAllocation).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ one())
+ return alloc
except exc.NoResultFound:
- pass
- session.flush()
+ return
-def update_vlan_id_pool():
- """Update vlan_ids based on current configuration."""
+def reserve_vlan(session):
+ with session.begin(subtransactions=True):
+ alloc = (session.query(ovs_models_v2.VlanAllocation).
+ filter_by(allocated=False).
+ first())
+ if alloc:
+ LOG.debug("reserving vlan %s on physical network %s from pool" %
+ (alloc.vlan_id, alloc.physical_network))
+ alloc.allocated = True
+ return (alloc.physical_network, alloc.vlan_id)
+ raise q_exc.NoNetworkAvailable()
+
+
+def reserve_specific_vlan(session, physical_network, vlan_id):
+ with session.begin(subtransactions=True):
+ try:
+ alloc = (session.query(ovs_models_v2.VlanAllocation).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ one())
+ if alloc.allocated:
+ raise q_exc.VlanIdInUse(vlan_id=vlan_id,
+ physical_network=physical_network)
+ LOG.debug("reserving specific vlan %s on physical network %s "
+ "from pool" % (vlan_id, physical_network))
+ alloc.allocated = True
+ except exc.NoResultFound:
+ LOG.debug("reserving specific vlan %s on physical network %s "
+ "outside pool" % (vlan_id, physical_network))
+ alloc = ovs_models_v2.VlanAllocation(physical_network, vlan_id)
+ alloc.allocated = True
+ session.add(alloc)
- # determine current dynamically-allocated range
- vlans = set(xrange(cfg.CONF.OVS.vlan_min,
- cfg.CONF.OVS.vlan_max + 1))
- session = db.get_session()
+def release_vlan(session, physical_network, vlan_id, network_vlan_ranges):
with session.begin(subtransactions=True):
- # remove unused vlan_ids outside current range
try:
- records = (session.query(ovs_models_v2.VlanID).
- all())
- for record in records:
+ alloc = (session.query(ovs_models_v2.VlanAllocation).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ one())
+ alloc.allocated = False
+ inside = False
+ for vlan_range in network_vlan_ranges.get(physical_network, []):
+ if vlan_id >= vlan_range[0] and vlan_id <= vlan_range[1]:
+ inside = True
+ break
+ if not inside:
+ session.delete(alloc)
+ LOG.debug("releasing vlan %s on physical network %s %s pool" %
+ (vlan_id, physical_network,
+ inside and "to" or "outside"))
+ except exc.NoResultFound:
+ LOG.warning("vlan_id %s on physical network %s not found" %
+ (vlan_id, physical_network))
+
+
+def sync_tunnel_allocations(tunnel_id_ranges):
+ """Synchronize tunnel_allocations table with configured tunnel ranges"""
+
+ # determine current configured allocatable tunnels
+ tunnel_ids = set()
+ for tunnel_id_range in tunnel_id_ranges:
+ tun_min, tun_max = tunnel_id_range
+ if tun_max + 1 - tun_min > 1000000:
+ LOG.error("Skipping unreasonable tunnel ID range %s:%s" %
+ tunnel_id_range)
+ else:
+ tunnel_ids |= set(xrange(tun_min, tun_max + 1))
+
+ session = db.get_session()
+ with session.begin():
+ # remove from table unallocated tunnels not currently allocatable
+ try:
+ allocs = (session.query(ovs_models_v2.TunnelAllocation).
+ all())
+ for alloc in allocs:
try:
- vlans.remove(record.vlan_id)
+ # see if tunnel is allocatable
+ tunnel_ids.remove(alloc.tunnel_id)
except KeyError:
- if not record.vlan_used:
- LOG.debug("removing vlan %s from pool"
- % record.vlan_id)
- session.delete(record)
+ # it's not allocatable, so check if its allocated
+ if not alloc.allocated:
+ # it's not, so remove it from table
+ LOG.debug("removing tunnel %s from pool" %
+ alloc.tunnel_id)
+ session.delete(alloc)
except exc.NoResultFound:
pass
- # add missing vlan_ids
- for vlan in vlans:
- record = ovs_models_v2.VlanID(vlan)
- session.add(record)
-
+ # add missing allocatable tunnels to table
+ for tunnel_id in sorted(tunnel_ids):
+ alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
+ session.add(alloc)
-def get_vlan_id(vlan_id):
- """Get state of specified vlan"""
+def get_tunnel_allocation(tunnel_id):
session = db.get_session()
try:
- record = (session.query(ovs_models_v2.VlanID).
- filter_by(vlan_id=vlan_id).
- one())
- return record
+ alloc = (session.query(ovs_models_v2.TunnelAllocation).
+ filter_by(tunnel_id=tunnel_id).
+ one())
+ return alloc
except exc.NoResultFound:
- return None
-
+ return
-def reserve_vlan_id(session):
- """Reserve an unused vlan_id"""
+def reserve_tunnel(session):
with session.begin(subtransactions=True):
- record = (session.query(ovs_models_v2.VlanID).
- filter_by(vlan_used=False).
- first())
- if not record:
- raise q_exc.NoNetworkAvailable()
- LOG.debug("reserving vlan %s from pool" % record.vlan_id)
- record.vlan_used = True
- return record.vlan_id
-
-
-def reserve_specific_vlan_id(vlan_id, session):
- """Reserve a specific vlan_id"""
+ alloc = (session.query(ovs_models_v2.TunnelAllocation).
+ filter_by(allocated=False).
+ first())
+ if alloc:
+ LOG.debug("reserving tunnel %s from pool" % alloc.tunnel_id)
+ alloc.allocated = True
+ return alloc.tunnel_id
+ raise q_exc.NoNetworkAvailable()
- if vlan_id < 1 or vlan_id > 4094:
- msg = _("Specified VLAN %s outside legal range (1-4094)") % vlan_id
- raise q_exc.InvalidInput(error_message=msg)
+def reserve_specific_tunnel(session, tunnel_id):
with session.begin(subtransactions=True):
try:
- record = (session.query(ovs_models_v2.VlanID).
- filter_by(vlan_id=vlan_id).
- one())
- if record.vlan_used:
- # REVISIT(rkukura) pass phyiscal_network
- raise q_exc.VlanIdInUse(vlan_id=vlan_id,
- physical_network='default')
- LOG.debug("reserving specific vlan %s from pool" % vlan_id)
- record.vlan_used = True
+ alloc = (session.query(ovs_models_v2.TunnelAllocation).
+ filter_by(tunnel_id=tunnel_id).
+ one())
+ if alloc.allocated:
+ raise q_exc.TunnelIdInUse(tunnel_id=tunnel_id)
+ LOG.debug("reserving specific tunnel %s from pool" % tunnel_id)
+ alloc.allocated = True
except exc.NoResultFound:
- LOG.debug("reserving specific vlan %s outside pool" % vlan_id)
- record = ovs_models_v2.VlanID(vlan_id)
- record.vlan_used = True
- session.add(record)
-
+ LOG.debug("reserving specific tunnel %s outside pool" % tunnel_id)
+ alloc = ovs_models_v2.TunnelAllocation(tunnel_id)
+ alloc.allocated = True
+ session.add(alloc)
-def release_vlan_id(vlan_id):
- """Set the vlan state to be unused, and delete if not in range"""
- session = db.get_session()
+def release_tunnel(session, tunnel_id, tunnel_id_ranges):
with session.begin(subtransactions=True):
try:
- record = (session.query(ovs_models_v2.VlanID).
- filter_by(vlan_id=vlan_id).
- one())
- record.vlan_used = False
- if (vlan_id >= cfg.CONF.OVS.vlan_min and
- vlan_id <= cfg.CONF.OVS.vlan_max):
- LOG.debug("releasing vlan %s to pool" % vlan_id)
- else:
- LOG.debug("removing vlan %s outside pool" % vlan_id)
- session.delete(record)
+ alloc = (session.query(ovs_models_v2.TunnelAllocation).
+ filter_by(tunnel_id=tunnel_id).
+ one())
+ alloc.allocated = False
+ inside = False
+ for tunnel_id_range in tunnel_id_ranges:
+ if (tunnel_id >= tunnel_id_range[0]
+ and tunnel_id <= tunnel_id_range[1]):
+ inside = True
+ break
+ if not inside:
+ session.delete(alloc)
+ LOG.debug("releasing tunnel %s %s pool" %
+ (tunnel_id, inside and "to" or "outside"))
except exc.NoResultFound:
- LOG.error("vlan id %s not found in release_vlan_id" % vlan_id)
+ LOG.warning("tunnel_id %s not found" % tunnel_id)
def get_port(port_id):
raise q_exc.PortNotFound(port_id=port_id)
-def get_tunnels():
+def get_tunnel_endpoints():
session = db.get_session()
try:
- tunnels = session.query(ovs_models_v2.TunnelInfo).all()
+ tunnels = session.query(ovs_models_v2.TunnelEndpoint).all()
except exc.NoResultFound:
return []
return [{'id': tunnel.id,
'ip_address': tunnel.ip_address} for tunnel in tunnels]
-def generate_tunnel_id(session):
+def _generate_tunnel_id(session):
try:
- tunnels = session.query(ovs_models_v2.TunnelInfo).all()
+ tunnels = session.query(ovs_models_v2.TunnelEndpoint).all()
except exc.NoResultFound:
return 0
tunnel_ids = ([tunnel['id'] for tunnel in tunnels])
return id + 1
-def add_tunnel(ip):
+def add_tunnel_endpoint(ip):
session = db.get_session()
try:
- tunnel = (session.query(ovs_models_v2.TunnelInfo).
+ tunnel = (session.query(ovs_models_v2.TunnelEndpoint).
filter_by(ip_address=ip).one())
except exc.NoResultFound:
- # Generate an id for the tunnel
- id = generate_tunnel_id(session)
- tunnel = ovs_models_v2.TunnelInfo(ip, id)
+ id = _generate_tunnel_id(session)
+ tunnel = ovs_models_v2.TunnelEndpoint(ip, id)
session.add(tunnel)
session.flush()
return tunnel
from quantum.db.models_v2 import model_base
-class VlanID(model_base.BASEV2):
- """Represents a vlan_id usage"""
- __tablename__ = 'vlan_ids'
+class VlanAllocation(model_base.BASEV2):
+ """Represents allocation state of vlan_id on physical network"""
+ __tablename__ = 'ovs_vlan_allocations'
- vlan_id = Column(Integer, nullable=False, primary_key=True)
- vlan_used = Column(Boolean, nullable=False)
+ physical_network = Column(String(64), nullable=False, primary_key=True)
+ vlan_id = Column(Integer, nullable=False, primary_key=True,
+ autoincrement=False)
+ allocated = Column(Boolean, nullable=False)
- def __init__(self, vlan_id):
+ def __init__(self, physical_network, vlan_id):
+ self.physical_network = physical_network
self.vlan_id = vlan_id
- self.vlan_used = False
+ self.allocated = False
def __repr__(self):
- return "<VlanID(%d,%s)>" % (self.vlan_id, self.vlan_used)
+ return "<VlanAllocation(%s,%d,%s)>" % (self.physical_network,
+ self.vlan_id, self.allocated)
-class VlanBinding(model_base.BASEV2):
- """Represents a binding of network_id to vlan_id."""
- __tablename__ = 'vlan_bindings'
+class TunnelAllocation(model_base.BASEV2):
+ """Represents allocation state of tunnel_id"""
+ __tablename__ = 'ovs_tunnel_allocations'
- network_id = Column(String(36), ForeignKey('networks.id',
- ondelete="CASCADE"),
+ tunnel_id = Column(Integer, nullable=False, primary_key=True,
+ autoincrement=False)
+ allocated = Column(Boolean, nullable=False)
+
+ def __init__(self, tunnel_id):
+ self.tunnel_id = tunnel_id
+ self.allocated = False
+
+ def __repr__(self):
+ return "<TunnelAllocation(%d,%s)>" % (self.tunnel_id, self.allocated)
+
+
+class NetworkBinding(model_base.BASEV2):
+ """Represents binding of virtual network to physical realization"""
+ __tablename__ = 'ovs_network_bindings'
+
+ network_id = Column(String(36),
+ ForeignKey('networks.id', ondelete="CASCADE"),
primary_key=True)
- vlan_id = Column(Integer, nullable=False)
+ network_type = Column(String(32), nullable=False) # 'gre', 'vlan', 'flat'
+ physical_network = Column(String(64))
+ physical_id = Column(Integer) # tunnel_id or vlan_id
- def __init__(self, vlan_id, network_id):
+ def __init__(self, network_id, network_type, physical_network,
+ physical_id):
self.network_id = network_id
- self.vlan_id = vlan_id
+ self.network_type = network_type
+ self.physical_network = physical_network
+ self.physical_id = physical_id
def __repr__(self):
- return "<VlanBinding(%s,%s)>" % (self.vlan_id, self.network_id)
+ return "<NetworkBinding(%s,%s,%s,%d)>" % (self.network_id,
+ self.network_type,
+ self.physical_network,
+ self.physical_id)
class TunnelIP(model_base.BASEV2):
- """Represents a remote IP in tunnel mode."""
- __tablename__ = 'tunnel_ips'
+ """Represents tunnel endpoint in DB mode"""
+ __tablename__ = 'ovs_tunnel_ips'
ip_address = Column(String(255), primary_key=True)
return "<TunnelIP(%s)>" % (self.ip_address)
-class TunnelInfo(model_base.BASEV2):
- """Represents remote tunnel information in tunnel mode."""
- __tablename__ = 'tunnel_info'
+class TunnelEndpoint(model_base.BASEV2):
+ """Represents tunnel endpoint in RPC mode"""
+ __tablename__ = 'ovs_tunnel_endpoints'
ip_address = Column(String(64), primary_key=True)
id = Column(Integer, nullable=False)
self.id = id
def __repr__(self):
- return "<TunnelInfo(%s,%s)>" % (self.ip_address, self.id)
+ return "<TunnelEndpoint(%s,%s)>" % (self.ip_address, self.id)
import logging
import os
+import sys
from quantum.api.v2 import attributes
-from quantum.common import constants
+from quantum.common import constants as q_const
from quantum.common import exceptions as q_exc
from quantum.common import topics
-from quantum.db import api as db
from quantum.db import db_base_plugin_v2
from quantum.db import dhcp_rpc_base
from quantum.db import l3_db
-from quantum.db import models_v2
from quantum.openstack.common import context
from quantum.openstack.common import cfg
from quantum.openstack.common import rpc
from quantum.openstack.common.rpc import dispatcher
from quantum.openstack.common.rpc import proxy
from quantum.plugins.openvswitch.common import config
+from quantum.plugins.openvswitch.common import constants
from quantum.plugins.openvswitch import ovs_db_v2
from quantum import policy
# Set RPC API version to 1.0 by default.
RPC_API_VERSION = '1.0'
- def __init__(self, context, notifier):
- self.context = context
+ def __init__(self, rpc_context, notifier):
+ self.rpc_context = rpc_context
self.notifier = notifier
def create_rpc_dispatcher(self):
'''
return dispatcher.RpcDispatcher([self])
- def get_device_details(self, context, **kwargs):
+ def get_device_details(self, rpc_context, **kwargs):
"""Agent requests device details"""
agent_id = kwargs.get('agent_id')
device = kwargs.get('device')
LOG.debug("Device %s details requested from %s", device, agent_id)
port = ovs_db_v2.get_port(device)
if port:
- vlan_id = ovs_db_v2.get_vlan(port['network_id'])
+ binding = ovs_db_v2.get_network_binding(None, port['network_id'])
entry = {'device': device,
- 'vlan_id': vlan_id,
'network_id': port['network_id'],
'port_id': port['id'],
- 'admin_state_up': port['admin_state_up']}
+ 'admin_state_up': port['admin_state_up'],
+ 'network_type': binding.network_type,
+ 'physical_id': binding.physical_id,
+ 'physical_network': binding.physical_network}
# Set the port status to UP
- ovs_db_v2.set_port_status(port['id'], constants.PORT_STATUS_ACTIVE)
+ ovs_db_v2.set_port_status(port['id'], q_const.PORT_STATUS_ACTIVE)
else:
entry = {'device': device}
LOG.debug("%s can not be found in database", device)
return entry
- def update_device_down(self, context, **kwargs):
+ def update_device_down(self, rpc_context, **kwargs):
"""Device no longer exists on agent"""
# (TODO) garyk - live migration and port status
agent_id = kwargs.get('agent_id')
entry = {'device': device,
'exists': True}
# Set port status to DOWN
- ovs_db_v2.set_port_status(port['id'], constants.PORT_STATUS_DOWN)
+ ovs_db_v2.set_port_status(port['id'], q_const.PORT_STATUS_DOWN)
else:
entry = {'device': device,
'exists': False}
LOG.debug("%s can not be found in database", device)
return entry
- def tunnel_sync(self, context, **kwargs):
+ def tunnel_sync(self, rpc_context, **kwargs):
"""Update new tunnel.
Updates the datbase with the tunnel IP. All listening agents will also
"""
tunnel_ip = kwargs.get('tunnel_ip')
# Update the database with the IP
- tunnel = ovs_db_v2.add_tunnel(tunnel_ip)
- tunnels = ovs_db_v2.get_tunnels()
+ tunnel = ovs_db_v2.add_tunnel_endpoint(tunnel_ip)
+ tunnels = ovs_db_v2.get_tunnel_endpoints()
entry = dict()
entry['tunnels'] = tunnels
# Notify all other listening agents
- self.notifier.tunnel_update(self.context, tunnel.ip_address,
+ self.notifier.tunnel_update(self.rpc_context, tunnel.ip_address,
tunnel.id)
# Return the list of tunnels IP's to the agent
return entry
topics.PORT,
topics.UPDATE)
self.topic_tunnel_update = topics.get_topic_name(topic,
- config.TUNNEL,
+ constants.TUNNEL,
topics.UPDATE)
def network_delete(self, context, network_id):
supported_extension_aliases = ["provider", "os-quantum-router"]
def __init__(self, configfile=None):
- self.enable_tunneling = cfg.CONF.OVS.enable_tunneling
- options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
- options.update({'base': models_v2.model_base.BASEV2})
- sql_max_retries = cfg.CONF.DATABASE.sql_max_retries
- options.update({"sql_max_retries": sql_max_retries})
- reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
- options.update({"reconnect_interval": reconnect_interval})
- db.configure_db(options)
-
- # update the vlan_id table based on current configuration
- ovs_db_v2.update_vlan_id_pool()
+ ovs_db_v2.initialize()
+ self._parse_network_vlan_ranges()
+ ovs_db_v2.sync_vlan_allocations(self.network_vlan_ranges)
+ self._parse_tunnel_id_ranges()
+ ovs_db_v2.sync_tunnel_allocations(self.tunnel_id_ranges)
self.agent_rpc = cfg.CONF.AGENT.rpc
self.setup_rpc()
def setup_rpc(self):
# RPC support
self.topic = topics.PLUGIN
- self.context = context.RequestContext('quantum', 'quantum',
- is_admin=False)
+ self.rpc_context = context.RequestContext('quantum', 'quantum',
+ is_admin=False)
self.conn = rpc.create_connection(new=True)
self.notifier = AgentNotifierApi(topics.AGENT)
- self.callbacks = OVSRpcCallbacks(self.context, self.notifier)
+ self.callbacks = OVSRpcCallbacks(self.rpc_context, self.notifier)
self.dispatcher = self.callbacks.create_rpc_dispatcher()
self.conn.create_consumer(self.topic, self.dispatcher,
fanout=False)
# Consume from all consumers in a thread
self.conn.consume_in_thread()
+ def _parse_network_vlan_ranges(self):
+ self.network_vlan_ranges = {}
+ for entry in cfg.CONF.OVS.network_vlan_ranges:
+ entry = entry.strip()
+ if ':' in entry:
+ try:
+ physical_network, vlan_min, vlan_max = entry.split(':')
+ self._add_network_vlan_range(physical_network.strip(),
+ int(vlan_min),
+ int(vlan_max))
+ except ValueError as ex:
+ LOG.error("Invalid network VLAN range: \'%s\' - %s" %
+ (entry, ex))
+ sys.exit(1)
+ else:
+ self._add_network(entry)
+ LOG.debug("network VLAN ranges: %s" % self.network_vlan_ranges)
+
+ def _add_network_vlan_range(self, physical_network, vlan_min, vlan_max):
+ self._add_network(physical_network)
+ self.network_vlan_ranges[physical_network].append((vlan_min, vlan_max))
+
+ def _add_network(self, physical_network):
+ if physical_network not in self.network_vlan_ranges:
+ self.network_vlan_ranges[physical_network] = []
+
+ def _parse_tunnel_id_ranges(self):
+ self.tunnel_id_ranges = []
+ for entry in cfg.CONF.OVS.tunnel_id_ranges:
+ entry = entry.strip()
+ try:
+ tun_min, tun_max = entry.split(':')
+ self.tunnel_id_ranges.append((int(tun_min), int(tun_max)))
+ except ValueError as ex:
+ LOG.error("Invalid tunnel ID range: \'%s\' - %s" %
+ (entry, ex))
+ sys.exit(1)
+ LOG.debug("tunnel ID ranges: %s" % self.tunnel_id_ranges)
+
# TODO(rkukura) Use core mechanism for attribute authorization
# when available.
def _extend_network_dict(self, context, network):
if self._check_provider_view_auth(context, network):
- if not self.enable_tunneling:
- network['provider:vlan_id'] = ovs_db_v2.get_vlan(
- network['id'], context.session)
+ binding = ovs_db_v2.get_network_binding(context.session,
+ network['id'])
+ network['provider:network_type'] = binding.network_type
+ if binding.network_type == constants.TYPE_GRE:
+ network['provider:physical_network'] = None
+ network['provider:vlan_id'] = None
+ elif binding.network_type == constants.TYPE_FLAT:
+ network['provider:physical_network'] = binding.physical_network
+ network['provider:vlan_id'] = None
+ elif binding.network_type == constants.TYPE_VLAN:
+ network['provider:physical_network'] = binding.physical_network
+ network['provider:vlan_id'] = binding.physical_id
def _process_provider_create(self, context, attrs):
network_type = attrs.get('provider:network_type')
if not network_type_set:
msg = _("provider:network_type required")
raise q_exc.InvalidInput(error_message=msg)
- elif network_type == 'flat':
- msg = _("plugin does not support flat networks")
- raise q_exc.InvalidInput(error_message=msg)
- # REVISIT(rkukura) to be enabled in phase 3
- # if vlan_id_set:
- # msg = _("provider:vlan_id specified for flat network")
- # raise q_exc.InvalidInput(error_message=msg)
- # else:
- # vlan_id = db.FLAT_VLAN_ID
- elif network_type == 'vlan':
+ elif network_type == constants.TYPE_FLAT:
+ if vlan_id_set:
+ msg = _("provider:vlan_id specified for flat network")
+ raise q_exc.InvalidInput(error_message=msg)
+ else:
+ vlan_id = constants.FLAT_VLAN_ID
+ elif network_type == constants.TYPE_VLAN:
if not vlan_id_set:
msg = _("provider:vlan_id required")
raise q_exc.InvalidInput(error_message=msg)
raise q_exc.InvalidInput(error_message=msg)
if physical_network_set:
- msg = _("plugin does not support specifying physical_network")
+ if physical_network not in self.network_vlan_ranges:
+ msg = _("unknown provider:physical_network %s" %
+ physical_network)
+ raise q_exc.InvalidInput(error_message=msg)
+ elif 'default' in self.network_vlan_ranges:
+ physical_network = 'default'
+ else:
+ msg = _("provider:physical_network required")
raise q_exc.InvalidInput(error_message=msg)
- # REVISIT(rkukura) to be enabled in phase 3
- # if physical_network not in self.physical_networks:
- # msg = _("unknown provider:physical_network %s" %
- # physical_network)
- # raise q_exc.InvalidInput(error_message=msg)
- #elif 'default' in self.physical_networks:
- # physical_network = 'default'
- #else:
- # msg = _("provider:physical_network required")
- # raise q_exc.InvalidInput(error_message=msg)
return (network_type, physical_network, vlan_id)
def create_network(self, context, network):
(network_type, physical_network,
- vlan_id) = self._process_provider_create(context,
- network['network'])
+ physical_id) = self._process_provider_create(context,
+ network['network'])
- net = super(OVSQuantumPluginV2, self).create_network(context, network)
- try:
+ session = context.session
+ with session.begin(subtransactions=True):
if not network_type:
- vlan_id = ovs_db_v2.reserve_vlan_id(context.session)
+ try:
+ (physical_network,
+ physical_id) = ovs_db_v2.reserve_vlan(session)
+ network_type = constants.TYPE_VLAN
+ except q_exc.NoNetworkAvailable:
+ physical_id = ovs_db_v2.reserve_tunnel(session)
+ network_type = constants.TYPE_GRE
else:
- ovs_db_v2.reserve_specific_vlan_id(vlan_id, context.session)
- except Exception:
- super(OVSQuantumPluginV2, self).delete_network(context, net['id'])
- raise
-
+ ovs_db_v2.reserve_specific_vlan(session, physical_network,
+ physical_id)
+ net = super(OVSQuantumPluginV2, self).create_network(context,
+ network)
+ ovs_db_v2.add_network_binding(session, net['id'], network_type,
+ physical_network, physical_id)
+ self._extend_network_dict(context, net)
+ # note - exception will rollback entire transaction
LOG.debug("Created network: %s" % net['id'])
- ovs_db_v2.add_vlan_binding(vlan_id, str(net['id']), context.session)
- self._extend_network_dict(context, net)
return net
def update_network(self, context, id, network):
self._check_provider_update(context, network['network'])
- net = super(OVSQuantumPluginV2, self).update_network(context, id,
- network)
- self._extend_network_dict(context, net)
+ session = context.session
+ with session.begin(subtransactions=True):
+ net = super(OVSQuantumPluginV2, self).update_network(context, id,
+ network)
+ self._extend_network_dict(context, net)
return net
def delete_network(self, context, id):
- vlan_id = ovs_db_v2.get_vlan(id)
- result = super(OVSQuantumPluginV2, self).delete_network(context, id)
- ovs_db_v2.release_vlan_id(vlan_id)
+ session = context.session
+ with session.begin(subtransactions=True):
+ binding = ovs_db_v2.get_network_binding(session, id)
+ result = super(OVSQuantumPluginV2, self).delete_network(context,
+ id)
+ if binding.network_type == constants.TYPE_GRE:
+ ovs_db_v2.release_tunnel(session, binding.physical_id,
+ self.tunnel_id_ranges)
+ else:
+ ovs_db_v2.release_vlan(session, binding.physical_network,
+ binding.physical_id,
+ self.network_vlan_ranges)
+ # the network_binding record is deleted via cascade from
+ # the network record, so explicit removal is not necessary
if self.agent_rpc:
- self.notifier.network_delete(self.context, id)
+ self.notifier.network_delete(self.rpc_context, id)
return result
def get_network(self, context, id, fields=None):
port = super(OVSQuantumPluginV2, self).update_port(context, id, port)
if self.agent_rpc:
if original_port['admin_state_up'] != port['admin_state_up']:
- vlan_id = ovs_db_v2.get_vlan(port['network_id'])
- self.notifier.port_update(self.context, port, vlan_id)
+ binding = ovs_db_v2.get_network_binding(None,
+ port['network_id'])
+ # REVISIT(rkukura): needs other binding data as well
+ self.notifier.port_update(self.rpc_context, port,
+ binding.physical_id)
return port
def delete_port(self, context, id):
from quantum.common import exceptions as q_exc
from quantum.db import api as db
-from quantum.db import models_v2
-from quantum.plugins.openvswitch.common import config
-from quantum.openstack.common import cfg
from quantum.plugins.openvswitch import ovs_db_v2
+PHYS_NET = 'physnet1'
VLAN_MIN = 10
VLAN_MAX = 19
+VLAN_RANGES = {PHYS_NET: [(VLAN_MIN, VLAN_MAX)]}
+UPDATED_VLAN_RANGES = {PHYS_NET: [(VLAN_MIN + 5, VLAN_MAX + 5)]}
+TUN_MIN = 100
+TUN_MAX = 109
+TUNNEL_RANGES = [(TUN_MIN, TUN_MAX)]
+UPDATED_TUNNEL_RANGES = [(TUN_MIN + 5, TUN_MAX + 5)]
+TEST_NETWORK_ID = 'abcdefghijklmnopqrstuvwxyz'
-class OVSVlanIdsTest(unittest2.TestCase):
+class VlanAllocationsTest(unittest2.TestCase):
def setUp(self):
- cfg.CONF.set_override('vlan_min', VLAN_MIN, group='OVS')
- cfg.CONF.set_override('vlan_max', VLAN_MAX, group='OVS')
-
- options = {"sql_connection": cfg.CONF.DATABASE.sql_connection}
- options.update({'base': models_v2.model_base.BASEV2})
- sql_max_retries = cfg.CONF.DATABASE.sql_max_retries
- options.update({"sql_max_retries": sql_max_retries})
- reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
- options.update({"reconnect_interval": reconnect_interval})
- db.configure_db(options)
-
- ovs_db_v2.update_vlan_id_pool()
+ ovs_db_v2.initialize()
+ ovs_db_v2.sync_vlan_allocations(VLAN_RANGES)
+ self.session = db.get_session()
def tearDown(self):
db.clear_db()
- cfg.CONF.reset()
-
- def test_update_vlan_id_pool(self):
- self.assertIsNone(ovs_db_v2.get_vlan_id(VLAN_MIN - 1))
- self.assertFalse(ovs_db_v2.get_vlan_id(VLAN_MIN).vlan_used)
- self.assertFalse(ovs_db_v2.get_vlan_id(VLAN_MIN + 1).vlan_used)
- self.assertFalse(ovs_db_v2.get_vlan_id(VLAN_MAX).vlan_used)
- self.assertIsNone(ovs_db_v2.get_vlan_id(VLAN_MAX + 1))
-
- cfg.CONF.set_override('vlan_min', VLAN_MIN + 5, group='OVS')
- cfg.CONF.set_override('vlan_max', VLAN_MAX + 5, group='OVS')
- ovs_db_v2.update_vlan_id_pool()
-
- self.assertIsNone(ovs_db_v2.get_vlan_id(VLAN_MIN + 5 - 1))
- self.assertFalse(ovs_db_v2.get_vlan_id(VLAN_MIN + 5).vlan_used)
- self.assertFalse(ovs_db_v2.get_vlan_id(VLAN_MIN + 5 + 1).vlan_used)
- self.assertFalse(ovs_db_v2.get_vlan_id(VLAN_MAX + 5).vlan_used)
- self.assertIsNone(ovs_db_v2.get_vlan_id(VLAN_MAX + 5 + 1))
-
- def test_vlan_id_pool(self):
- session = db.get_session()
+
+ def test_sync_vlan_allocations(self):
+ self.assertIsNone(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MIN - 1))
+ self.assertFalse(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MIN).allocated)
+ self.assertFalse(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MIN + 1).allocated)
+ self.assertFalse(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MAX).allocated)
+ self.assertIsNone(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MAX + 1))
+
+ ovs_db_v2.sync_vlan_allocations(UPDATED_VLAN_RANGES)
+
+ self.assertIsNone(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MIN + 5 - 1))
+ self.assertFalse(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MIN + 5).
+ allocated)
+ self.assertFalse(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MIN + 5 + 1).
+ allocated)
+ self.assertFalse(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MAX + 5 - 1).
+ allocated)
+ self.assertFalse(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MAX + 5).
+ allocated)
+ self.assertIsNone(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ VLAN_MAX + 5 + 1))
+
+ def test_vlan_pool(self):
vlan_ids = set()
for x in xrange(VLAN_MIN, VLAN_MAX + 1):
- vlan_id = ovs_db_v2.reserve_vlan_id(db.get_session())
+ physical_network, vlan_id = ovs_db_v2.reserve_vlan(self.session)
+ self.assertEqual(physical_network, PHYS_NET)
self.assertGreaterEqual(vlan_id, VLAN_MIN)
self.assertLessEqual(vlan_id, VLAN_MAX)
vlan_ids.add(vlan_id)
with self.assertRaises(q_exc.NoNetworkAvailable):
- vlan_id = ovs_db_v2.reserve_vlan_id(session)
-
- for vlan_id in vlan_ids:
- ovs_db_v2.release_vlan_id(vlan_id)
+ physical_network, vlan_id = ovs_db_v2.reserve_vlan(self.session)
- def test_invalid_specific_vlan_id(self):
- session = db.get_session()
- with self.assertRaises(q_exc.InvalidInput):
- vlan_id = ovs_db_v2.reserve_specific_vlan_id(0, session)
+ ovs_db_v2.release_vlan(self.session, PHYS_NET, vlan_ids.pop(),
+ VLAN_RANGES)
+ physical_network, vlan_id = ovs_db_v2.reserve_vlan(self.session)
+ self.assertEqual(physical_network, PHYS_NET)
+ self.assertGreaterEqual(vlan_id, VLAN_MIN)
+ self.assertLessEqual(vlan_id, VLAN_MAX)
+ vlan_ids.add(vlan_id)
- with self.assertRaises(q_exc.InvalidInput):
- vlan_id = ovs_db_v2.reserve_specific_vlan_id(4095, session)
+ for vlan_id in vlan_ids:
+ ovs_db_v2.release_vlan(self.session, PHYS_NET, vlan_id,
+ VLAN_RANGES)
- def test_specific_vlan_id_inside_pool(self):
- session = db.get_session()
+ def test_specific_vlan_inside_pool(self):
vlan_id = VLAN_MIN + 5
- self.assertFalse(ovs_db_v2.get_vlan_id(vlan_id).vlan_used)
- ovs_db_v2.reserve_specific_vlan_id(vlan_id, session)
- self.assertTrue(ovs_db_v2.get_vlan_id(vlan_id).vlan_used)
+ self.assertFalse(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ vlan_id).allocated)
+ ovs_db_v2.reserve_specific_vlan(self.session, PHYS_NET, vlan_id)
+ self.assertTrue(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ vlan_id).allocated)
with self.assertRaises(q_exc.VlanIdInUse):
- ovs_db_v2.reserve_specific_vlan_id(vlan_id, session)
+ ovs_db_v2.reserve_specific_vlan(self.session, PHYS_NET, vlan_id)
- ovs_db_v2.release_vlan_id(vlan_id)
- self.assertFalse(ovs_db_v2.get_vlan_id(vlan_id).vlan_used)
+ ovs_db_v2.release_vlan(self.session, PHYS_NET, vlan_id, VLAN_RANGES)
+ self.assertFalse(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ vlan_id).allocated)
- def test_specific_vlan_id_outside_pool(self):
- session = db.get_session()
+ def test_specific_vlan_outside_pool(self):
vlan_id = VLAN_MAX + 5
- self.assertIsNone(ovs_db_v2.get_vlan_id(vlan_id))
- ovs_db_v2.reserve_specific_vlan_id(vlan_id, session)
- self.assertTrue(ovs_db_v2.get_vlan_id(vlan_id).vlan_used)
+ self.assertIsNone(ovs_db_v2.get_vlan_allocation(PHYS_NET, vlan_id))
+ ovs_db_v2.reserve_specific_vlan(self.session, PHYS_NET, vlan_id)
+ self.assertTrue(ovs_db_v2.get_vlan_allocation(PHYS_NET,
+ vlan_id).allocated)
with self.assertRaises(q_exc.VlanIdInUse):
- ovs_db_v2.reserve_specific_vlan_id(vlan_id, session)
+ ovs_db_v2.reserve_specific_vlan(self.session, PHYS_NET, vlan_id)
+
+ ovs_db_v2.release_vlan(self.session, PHYS_NET, vlan_id, VLAN_RANGES)
+ self.assertIsNone(ovs_db_v2.get_vlan_allocation(PHYS_NET, vlan_id))
+
+
+class TunnelAllocationsTest(unittest2.TestCase):
+ def setUp(self):
+ ovs_db_v2.initialize()
+ ovs_db_v2.sync_tunnel_allocations(TUNNEL_RANGES)
+ self.session = db.get_session()
+
+ def tearDown(self):
+ db.clear_db()
+
+ def test_sync_tunnel_allocations(self):
+ self.assertIsNone(ovs_db_v2.get_tunnel_allocation(TUN_MIN - 1))
+ self.assertFalse(ovs_db_v2.get_tunnel_allocation(TUN_MIN).allocated)
+ self.assertFalse(ovs_db_v2.get_tunnel_allocation(TUN_MIN + 1).
+ allocated)
+ self.assertFalse(ovs_db_v2.get_tunnel_allocation(TUN_MAX).allocated)
+ self.assertIsNone(ovs_db_v2.get_tunnel_allocation(TUN_MAX + 1))
+
+ ovs_db_v2.sync_tunnel_allocations(UPDATED_TUNNEL_RANGES)
+
+ self.assertIsNone(ovs_db_v2.get_tunnel_allocation(TUN_MIN + 5 - 1))
+ self.assertFalse(ovs_db_v2.get_tunnel_allocation(TUN_MIN + 5).
+ allocated)
+ self.assertFalse(ovs_db_v2.get_tunnel_allocation(TUN_MIN + 5 + 1).
+ allocated)
+ self.assertFalse(ovs_db_v2.get_tunnel_allocation(TUN_MAX + 5 - 1).
+ allocated)
+ self.assertFalse(ovs_db_v2.get_tunnel_allocation(TUN_MAX + 5).
+ allocated)
+ self.assertIsNone(ovs_db_v2.get_tunnel_allocation(TUN_MAX + 5 + 1))
+
+ def test_tunnel_pool(self):
+ tunnel_ids = set()
+ for x in xrange(TUN_MIN, TUN_MAX + 1):
+ tunnel_id = ovs_db_v2.reserve_tunnel(self.session)
+ self.assertGreaterEqual(tunnel_id, TUN_MIN)
+ self.assertLessEqual(tunnel_id, TUN_MAX)
+ tunnel_ids.add(tunnel_id)
+
+ with self.assertRaises(q_exc.NoNetworkAvailable):
+ tunnel_id = ovs_db_v2.reserve_tunnel(self.session)
+
+ ovs_db_v2.release_tunnel(self.session, tunnel_ids.pop(), TUNNEL_RANGES)
+ tunnel_id = ovs_db_v2.reserve_tunnel(self.session)
+ self.assertGreaterEqual(tunnel_id, TUN_MIN)
+ self.assertLessEqual(tunnel_id, TUN_MAX)
+ tunnel_ids.add(tunnel_id)
+
+ for tunnel_id in tunnel_ids:
+ ovs_db_v2.release_tunnel(self.session, tunnel_id, TUNNEL_RANGES)
+
+ def test_specific_tunnel_inside_pool(self):
+ tunnel_id = TUN_MIN + 5
+ self.assertFalse(ovs_db_v2.get_tunnel_allocation(tunnel_id).allocated)
+ ovs_db_v2.reserve_specific_tunnel(self.session, tunnel_id)
+ self.assertTrue(ovs_db_v2.get_tunnel_allocation(tunnel_id).allocated)
+
+ with self.assertRaises(q_exc.TunnelIdInUse):
+ ovs_db_v2.reserve_specific_tunnel(self.session, tunnel_id)
+
+ ovs_db_v2.release_tunnel(self.session, tunnel_id, TUNNEL_RANGES)
+ self.assertFalse(ovs_db_v2.get_tunnel_allocation(tunnel_id).allocated)
+
+ def test_specific_tunnel_outside_pool(self):
+ tunnel_id = TUN_MAX + 5
+ self.assertIsNone(ovs_db_v2.get_tunnel_allocation(tunnel_id))
+ ovs_db_v2.reserve_specific_tunnel(self.session, tunnel_id)
+ self.assertTrue(ovs_db_v2.get_tunnel_allocation(tunnel_id).allocated)
+
+ with self.assertRaises(q_exc.TunnelIdInUse):
+ ovs_db_v2.reserve_specific_tunnel(self.session, tunnel_id)
+
+ ovs_db_v2.release_tunnel(self.session, tunnel_id, TUNNEL_RANGES)
+ self.assertIsNone(ovs_db_v2.get_tunnel_allocation(tunnel_id))
+
+
+class NetworkBindingsTest(unittest2.TestCase):
+ def setUp(self):
+ ovs_db_v2.initialize()
+ self.session = db.get_session()
+
+ def tearDown(self):
+ db.clear_db()
- ovs_db_v2.release_vlan_id(vlan_id)
- self.assertIsNone(ovs_db_v2.get_vlan_id(vlan_id))
+ def test_add_network_binding(self):
+ self.assertIsNone(ovs_db_v2.get_network_binding(self.session,
+ TEST_NETWORK_ID))
+ ovs_db_v2.add_network_binding(self.session, TEST_NETWORK_ID, 'vlan',
+ PHYS_NET, 1234)
+ binding = ovs_db_v2.get_network_binding(self.session, TEST_NETWORK_ID)
+ self.assertIsNotNone(binding)
+ self.assertEqual(binding.network_id, TEST_NETWORK_ID)
+ self.assertEqual(binding.network_type, 'vlan')
+ self.assertEqual(binding.physical_network, PHYS_NET)
+ self.assertEqual(binding.physical_id, 1234)
class ConfigurationTest(unittest.TestCase):
def test_defaults(self):
- self.assertFalse(cfg.CONF.OVS.enable_tunneling)
self.assertEqual('br-int', cfg.CONF.OVS.integration_bridge)
self.assertEqual('br-tun', cfg.CONF.OVS.tunnel_bridge)
self.assertEqual('sqlite://', cfg.CONF.DATABASE.sql_connection)
self.assertEqual(2, cfg.CONF.DATABASE.reconnect_interval)
self.assertEqual(2, cfg.CONF.AGENT.polling_interval)
self.assertEqual('sudo', cfg.CONF.AGENT.root_helper)
+
+ mappings = cfg.CONF.OVS.bridge_mappings
+ self.assertEqual(1, len(mappings))
+ self.assertEqual('default:br-eth1', mappings[0])
+
+ ranges = cfg.CONF.OVS.network_vlan_ranges
+ self.assertEqual(1, len(ranges))
+ self.assertEqual('default:1000:2999', ranges[0])
+
+ ranges = cfg.CONF.OVS.tunnel_id_ranges
+ self.assertEqual(0, len(ranges))
from quantum.openstack.common import context
from quantum.openstack.common import rpc
from quantum.plugins.openvswitch import ovs_quantum_plugin as povs
-from quantum.plugins.openvswitch.common import config
+from quantum.plugins.openvswitch.common import constants
class rpcApiTestCase(unittest2.TestCase):
rpcapi = povs.AgentNotifierApi(topics.AGENT)
self._test_ovs_api(rpcapi,
topics.get_topic_name(topics.AGENT,
- config.TUNNEL,
+ constants.TUNNEL,
topics.UPDATE),
'tunnel_update', rpc_method='fanout_cast',
tunnel_ip='fake_ip', tunnel_id='fake_id')
LS_ID = '42'
LV_ID = 42
LV_IDS = [42, 43]
-LVM = ovs_quantum_agent.LocalVLANMapping(LV_ID, LS_ID, LV_IDS)
+LVM = ovs_quantum_agent.LocalVLANMapping(LV_ID, 'gre', None, LS_ID, LV_IDS)
VIF_ID = '404deaec-5d37-11e1-a64b-000c29d5f0a8'
VIF_MAC = '3c:09:24:1e:78:23'
OFPORT_NUM = 1
def testConstruct(self):
self.mox.ReplayAll()
- b = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
- self.TUN_BRIDGE,
- '10.0.0.1',
- 'sudo', 2, 2, False)
+ b = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ '10.0.0.1', {},
+ 'sudo', 2, 2, False)
self.mox.VerifyAll()
def testProvisionLocalVlan(self):
self.mox.ReplayAll()
- a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
- self.TUN_BRIDGE,
- '10.0.0.1',
- 'sudo', 2, 2, False)
+ a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ '10.0.0.1', {},
+ 'sudo', 2, 2, False)
a.available_local_vlans = set([LV_ID])
- a.provision_local_vlan(NET_UUID, LS_ID)
+ a.provision_local_vlan(NET_UUID, 'gre', None, LS_ID)
self.mox.VerifyAll()
def testReclaimLocalVlan(self):
- self.mock_tun_bridge.delete_flows(tun_id=LVM.lsw_id)
+ self.mock_tun_bridge.delete_flows(tun_id=LVM.physical_id)
self.mock_tun_bridge.delete_flows(dl_vlan=LVM.vlan)
self.mox.ReplayAll()
- a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
- self.TUN_BRIDGE,
- '10.0.0.1',
- 'sudo', 2, 2, False)
+ a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ '10.0.0.1', {},
+ 'sudo', 2, 2, False)
a.available_local_vlans = set()
a.local_vlan_map[NET_UUID] = LVM
a.reclaim_local_vlan(NET_UUID, LVM)
actions=action_string)
self.mox.ReplayAll()
- a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
- self.TUN_BRIDGE,
- '10.0.0.1',
- 'sudo', 2, 2, False)
+ a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ '10.0.0.1', {},
+ 'sudo', 2, 2, False)
a.local_vlan_map[NET_UUID] = LVM
- a.port_bound(VIF_PORT, NET_UUID, LS_ID)
+ a.port_bound(VIF_PORT, NET_UUID, 'gre', None, LS_ID)
self.mox.VerifyAll()
def testPortUnbound(self):
self.mox.ReplayAll()
- a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
- self.TUN_BRIDGE,
- '10.0.0.1',
- 'sudo', 2, 2, False)
+ a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ '10.0.0.1', {},
+ 'sudo', 2, 2, False)
a.available_local_vlans = set([LV_ID])
a.local_vlan_map[NET_UUID] = LVM
a.port_unbound(VIF_PORT, NET_UUID)
actions='drop')
self.mox.ReplayAll()
- a = ovs_quantum_agent.OVSQuantumTunnelAgent(self.INT_BRIDGE,
- self.TUN_BRIDGE,
- '10.0.0.1',
- 'sudo', 2, 2, False)
+ a = ovs_quantum_agent.OVSQuantumAgent(self.INT_BRIDGE,
+ self.TUN_BRIDGE,
+ '10.0.0.1', {},
+ 'sudo', 2, 2, False)
a.available_local_vlans = set([LV_ID])
a.local_vlan_map[NET_UUID] = LVM
a.port_dead(VIF_PORT)