# allow_bulk = True
# RPC configuration options. Defined in rpc __init__
# The messaging module to use, defaults to kombu.
-# rpc_backend = quantum.openstack.common.notifier.rpc.impl_kombu
+# rpc_backend = quantum.openstack.common.rpc.impl_kombu
# Size of RPC thread pool
# rpc_thread_pool_size = 64,
# Size of RPC connection pool
[VLANS]
-vlan_start = 1000
-vlan_end = 3000
+# (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
[DATABASE]
# This line MUST be changed to actually run the plugin.
reconnect_interval = 2
[LINUX_BRIDGE]
-# This is the interface connected to the switch on your Quantum network
-physical_interface = eth1
+# (ListOpt) Comma-separated list of
+# <physical_network>:<physical_interface> tuples mapping physical
+# network names to agent's node-specific physical network
+# interfaces. Server uses physical network names for validation but
+# ignores interfaces.
+# physical_interface_mappings = default:eth1
[AGENT]
# Agent's polling interval in seconds
# Change to "sudo quantum-rootwrap" to limit commands that can be run
# as root.
root_helper = sudo
-# Use Quantumv2 API
-# target_v2_api = False
# Use RPC messaging to interface between agent and plugin
# rpc = True
LOG = logging.getLogger(__name__)
+def is_attr_set(attribute):
+ return attribute not in (None, ATTR_NOT_SPECIFIED)
+
+
def _validate_boolean(data, valid_values=None):
if data in [True, False]:
return
return msg
+def _validate_range(data, valid_values=None):
+ min_value = valid_values[0]
+ max_value = valid_values[1]
+ if data >= min_value and data <= max_value:
+ return
+ else:
+ msg_dict = dict(data=data, min_value=min_value, max_value=max_value)
+ msg = _("%(data)s is not in range %(min_value)s through "
+ "%(max_value)s") % msg_dict
+ LOG.debug("validate_range: %s", msg)
+ return msg
+
+
def _validate_mac_address(data, valid_values=None):
try:
netaddr.EUI(data)
# Dictionary that maintains a list of validation functions
validators = {'type:boolean': _validate_boolean,
'type:values': _validate_values,
+ 'type:range': _validate_range,
'type:mac_address': _validate_mac_address,
'type:ip_address': _validate_ip_address,
'type:ip_address_or_none': _validate_ip_address_or_none,
class VlanIdInUse(InUse):
message = _("Unable to create the network. "
- "The VLAN %(vlan_id)s is in use.")
+ "The VLAN %(vlan_id)s on physical network "
+ "%(physical_network)s is in use.")
class ResourceExhausted(QuantumException):
def update_network(self, context, id, network):
n = network['network']
- with context.session.begin():
+ with context.session.begin(subtransactions=True):
network = self._get_network(context, id)
# validate 'shared' parameter
if 'shared' in n:
return self._make_network_dict(network)
def delete_network(self, context, id):
- with context.session.begin():
+ with context.session.begin(subtransactions=True):
network = self._get_network(context, id)
filter = {'network_id': [id]}
s = subnet['subnet']
self._validate_subnet(s)
- with context.session.begin():
+ with context.session.begin(subtransactions=True):
if "dns_nameservers" in s:
old_dns_list = self._get_dns_by_subnet(context, id)
return self._make_subnet_dict(subnet)
def delete_subnet(self, context, id):
- with context.session.begin():
+ with context.session.begin(subtransactions=True):
subnet = self._get_subnet(context, id)
# Check if ports are using this subnet
allocated_qry = context.session.query(models_v2.IPAllocation)
def update_port(self, context, id, port):
p = port['port']
- with context.session.begin():
+ with context.session.begin(subtransactions=True):
port = self._get_port(context, id)
# Check if the IPs need to be updated
if 'fixed_ips' in p:
return self._make_port_dict(port)
def delete_port(self, context, id):
- with context.session.begin():
+ with context.session.begin(subtransactions=True):
port = self._get_port(context, id)
allocated_qry = context.session.query(models_v2.IPAllocation)
EXTENDED_ATTRIBUTES_2_0 = {
'networks': {
- # TODO(rkukura): specify validation
- 'provider:vlan_id': {'allow_post': True, 'allow_put': False,
+ 'provider:network_type': {'allow_post': True, 'allow_put': True,
+ 'validate': {'type:values': ['flat',
+ 'vlan']},
+ 'default': attributes.ATTR_NOT_SPECIFIED,
+ 'is_visible': True},
+ 'provider:physical_network': {'allow_post': True, 'allow_put': True,
+ 'default': attributes.ATTR_NOT_SPECIFIED,
+ 'is_visible': True},
+ 'provider:vlan_id': {'allow_post': True, 'allow_put': True,
'convert_to': int,
+ 'validate': {'type:range': (1, 4095)},
'default': attributes.ATTR_NOT_SPECIFIED,
'is_visible': True},
}
Make the Linux Bridge plugin the current quantum plugin
-- edit quantum.conf and change the core_plugin according to the API version
-V1: "core_plugin = quantum.plugins.linuxbridge.LinuxBridgePlugin.LinuxBridgePlugin"
-V2: "core_plugin = quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2"
+- edit quantum.conf and change the core_plugin
+
+core_plugin = quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2
# -- Database config.
actually running the server set it to mysql. At any given time, only one
of these should be active in the conf file (you can comment out the other).
-- Remember to change the interface configuration to indicate the correct
- ethernet interface on that particular host which is being used to participate
- in the Quantum networks. This configuration has to be applied on each host
- on which the agent runs.
+- On the quantum server, network_vlan_ranges must be configured in
+ linuxbridge_conf.ini to specify the names of the physical networks
+ managed by the linuxbridge plugin, along with the ranges of VLAN IDs
+ available on each physical network for allocation to virtual
+ networks. An entry of the form
+ "<physical_network>:<vlan_min>:<vlan_max>" specifies a VLAN range on
+ the named physical network. An entry of the form
+ "<physical_network>" specifies a named network without making a
+ range of VLANs available for allocation. Networks specified using
+ either form are available for adminstrators to create provider flat
+ networks and provider VLANs. Multiple VLAN ranges can be specified
+ for the same physical network.
+
+ The following example linuxbridge_conf.ini entry shows three
+ physical networks that can be used to create provider networks, with
+ ranges of VLANs available for allocation on two of them:
+
+ [VLANS]
+ network_vlan_ranges = physnet1:1000:2999,physnet1:3000:3999,physnet2,physnet3:1:4094
+
# -- Agent configuration
Note: debug and logging information should be updated in etc/quantum.conf
+- On each compute node, the network_interface_mappings must be
+ configured in linuxbridge_conf.ini to map each physical network name
+ to the physical interface connecting the node to that physical
+ network. Entries are of the form
+ "<physical_network>:<physical_interface>". For example, one compute
+ node may use the following physical_inteface_mappings entries:
+
+ [LINUX_BRIDGE]
+ physical_interface_mappings = physnet1:eth1,physnet2:eth2,physnet3:eth3
+
+ while another might use:
+
+ [LINUX_BRIDGE]
+ physical_interface_mappings = physnet1:em3,physnet2:em2,physnet3:em1
+
+
$ Run the following:
python linuxbridge_quantum_agent.py --config-file quantum.conf
--config-file linuxbridge_conf.ini
import pyudev
from sqlalchemy.ext.sqlsoup import SqlSoup
+from quantum.agent.linux import utils
from quantum.agent import rpc as agent_rpc
from quantum.common import config as logging_config
from quantum.common import topics
from quantum.openstack.common import rpc
from quantum.openstack.common.rpc import dispatcher
from quantum.plugins.linuxbridge.common import config
-
-from quantum.agent.linux import utils
+from quantum.plugins.linuxbridge.common import constants
logging.basicConfig()
LOG = logging.getLogger(__name__)
class LinuxBridge:
- def __init__(self, br_name_prefix, physical_interface, root_helper):
- self.br_name_prefix = br_name_prefix
- self.physical_interface = physical_interface
+ def __init__(self, interface_mappings, root_helper):
+ self.interface_mappings = interface_mappings
self.root_helper = root_helper
def device_exists(self, device):
if not network_id:
LOG.warning("Invalid Network ID, will lead to incorrect bridge"
"name")
- bridge_name = self.br_name_prefix + network_id[0:11]
+ bridge_name = BRIDGE_NAME_PREFIX + network_id[0:11]
return bridge_name
- def get_subinterface_name(self, vlan_id):
+ def get_subinterface_name(self, physical_interface, vlan_id):
if not vlan_id:
LOG.warning("Invalid VLAN ID, will lead to incorrect "
"subinterface name")
- subinterface_name = '%s.%s' % (self.physical_interface, vlan_id)
+ subinterface_name = '%s.%s' % (physical_interface, vlan_id)
return subinterface_name
def get_tap_device_name(self, interface_id):
DEVICE_NAME_PLACEHOLDER, device_name)
return os.path.exists(bridge_port_path)
- def ensure_vlan_bridge(self, network_id, vlan_id):
+ def ensure_vlan_bridge(self, network_id, physical_interface, vlan_id):
"""Create a vlan and bridge unless they already exist."""
- interface = self.ensure_vlan(vlan_id)
+ interface = self.ensure_vlan(physical_interface, vlan_id)
bridge_name = self.get_bridge_name(network_id)
self.ensure_bridge(bridge_name, interface)
return interface
- def ensure_vlan(self, vlan_id):
+ def ensure_flat_bridge(self, network_id, physical_interface):
+ """Create a non-vlan bridge unless it already exists."""
+ bridge_name = self.get_bridge_name(network_id)
+ self.ensure_bridge(bridge_name, physical_interface)
+ return physical_interface
+
+ def ensure_vlan(self, physical_interface, vlan_id):
"""Create a vlan unless it already exists."""
- interface = self.get_subinterface_name(vlan_id)
+ interface = self.get_subinterface_name(physical_interface, vlan_id)
if not self.device_exists(interface):
LOG.debug("Creating subinterface %s for VLAN %s on interface %s" %
- (interface, vlan_id, self.physical_interface))
+ (interface, vlan_id, physical_interface))
if utils.execute(['ip', 'link', 'add', 'link',
- self.physical_interface,
+ physical_interface,
'name', interface, 'type', 'vlan', 'id',
vlan_id], root_helper=self.root_helper):
return
utils.execute(['brctl', 'addif', bridge_name, interface],
root_helper=self.root_helper)
- def add_tap_interface(self, network_id, vlan_id, tap_device_name):
+ def add_tap_interface(self, network_id, physical_interface, vlan_id,
+ tap_device_name):
"""
If a VIF has been plugged into a network, this function will
add the corresponding tap device to the relevant bridge
tap_device_name], root_helper=self.root_helper):
return False
- self.ensure_vlan_bridge(network_id, vlan_id)
+ if int(vlan_id) == constants.FLAT_VLAN_ID:
+ self.ensure_flat_bridge(network_id, physical_interface)
+ else:
+ self.ensure_vlan_bridge(network_id, physical_interface, vlan_id)
if utils.execute(['brctl', 'addif', bridge_name, tap_device_name],
root_helper=self.root_helper):
return False
bridge_name))
return True
- def add_interface(self, network_id, vlan_id, interface_id):
+ def add_interface(self, network_id, physical_network, vlan_id,
+ interface_id):
if not interface_id:
"""
Since the VIF id is null, no VIF is plugged into this port
no more processing is required
"""
return False
+
+ physical_interface = self.interface_mappings.get(physical_network)
+ if not physical_interface:
+ LOG.error("No mapping for physical network %s" % physical_network)
+ return False
+
if interface_id.startswith(GATEWAY_INTERFACE_PREFIX):
- return self.add_tap_interface(network_id, vlan_id, interface_id)
+ return self.add_tap_interface(network_id,
+ physical_interface, vlan_id,
+ interface_id)
else:
tap_device_name = self.get_tap_device_name(interface_id)
- return self.add_tap_interface(network_id, vlan_id, tap_device_name)
+ return self.add_tap_interface(network_id,
+ physical_interface, vlan_id,
+ tap_device_name)
def delete_vlan_bridge(self, bridge_name):
if self.device_exists(bridge_name):
interfaces_on_bridge = self.get_interfaces_on_bridge(bridge_name)
for interface in interfaces_on_bridge:
self.remove_interface(bridge_name, interface)
- if interface.startswith(self.physical_interface):
- self.delete_vlan(interface)
+ for physical_interface in self.interface_mappings.itervalues():
+ if interface.startswith(physical_interface):
+ self.delete_vlan(interface)
LOG.debug("Deleting bridge %s" % bridge_name)
if utils.execute(['ip', 'link', 'set', bridge_name, 'down'],
class LinuxBridgeQuantumAgentDB:
- def __init__(self, br_name_prefix, physical_interface, polling_interval,
- reconnect_interval, root_helper, target_v2_api,
- db_connection_url):
+ def __init__(self, interface_mappings, polling_interval,
+ reconnect_interval, root_helper, db_connection_url):
self.polling_interval = polling_interval
self.root_helper = root_helper
- self.setup_linux_bridge(br_name_prefix, physical_interface)
- self.target_v2_api = target_v2_api
+ self.setup_linux_bridge(interface_mappings)
self.reconnect_interval = reconnect_interval
self.db_connected = False
self.db_connection_url = db_connection_url
- def setup_linux_bridge(self, br_name_prefix, physical_interface):
- self.linux_br = LinuxBridge(br_name_prefix, physical_interface,
- self.root_helper)
+ def setup_linux_bridge(self, interface_mappings):
+ self.linux_br = LinuxBridge(interface_mappings, self.root_helper)
- def process_port_binding(self, network_id, interface_id, vlan_id):
- return self.linux_br.add_interface(network_id, vlan_id, interface_id)
+ def process_port_binding(self, network_id, interface_id,
+ physical_network, vlan_id):
+ return self.linux_br.add_interface(network_id,
+ physical_network, vlan_id,
+ interface_id)
def remove_port_binding(self, network_id, interface_id):
bridge_name = self.linux_br.get_bridge_name(network_id)
old_port_bindings):
vlan_bindings = {}
try:
- vlan_binds = db.vlan_bindings.all()
+ network_binds = db.network_bindings.all()
except Exception as e:
- LOG.info("Unable to get vlan bindings! Exception: %s" % e)
+ LOG.info("Unable to get network bindings! Exception: %s" % e)
self.db_connected = False
return {VLAN_BINDINGS: {},
PORT_BINDINGS: []}
vlans_string = ""
- for bind in vlan_binds:
- entry = {'network_id': bind.network_id, 'vlan_id': bind.vlan_id}
+ for bind in network_binds:
+ entry = {'network_id': bind.network_id,
+ 'physical_network': bind.physical_network,
+ 'vlan_id': bind.vlan_id}
vlan_bindings[bind.network_id] = entry
vlans_string = "%s %s" % (vlans_string, entry)
all_bindings = {}
for bind in port_binds:
append_entry = False
- if self.target_v2_api:
- all_bindings[bind.id] = bind
- entry = {'network_id': bind.network_id,
- 'uuid': bind.id,
- 'status': bind.status,
- 'interface_id': bind.id}
- append_entry = bind.admin_state_up
- else:
- all_bindings[bind.uuid] = bind
- entry = {'network_id': bind.network_id, 'state': bind.state,
- 'op_status': bind.op_status, 'uuid': bind.uuid,
- 'interface_id': bind.interface_id}
- append_entry = bind.state == 'ACTIVE'
+ all_bindings[bind.id] = bind
+ entry = {'network_id': bind.network_id,
+ 'uuid': bind.id,
+ 'status': bind.status,
+ 'interface_id': bind.id}
+ append_entry = bind.admin_state_up
if append_entry:
port_bindings.append(entry)
ports_string = "%s %s" % (ports_string, pb)
port_id = pb['uuid']
interface_id = pb['interface_id']
+ network_id = pb['network_id']
- vlan_id = str(vlan_bindings[pb['network_id']]['vlan_id'])
- if self.process_port_binding(pb['network_id'],
+ physical_network = vlan_bindings[network_id]['physical_network']
+ vlan_id = str(vlan_bindings[network_id]['vlan_id'])
+ if self.process_port_binding(network_id,
interface_id,
+ physical_network,
vlan_id):
- if self.target_v2_api:
- all_bindings[port_id].status = OP_STATUS_UP
- else:
- all_bindings[port_id].op_status = OP_STATUS_UP
+ all_bindings[port_id].status = OP_STATUS_UP
plugged_interfaces.append(interface_id)
class LinuxBridgeQuantumAgentRPC:
- def __init__(self, br_name_prefix, physical_interface, polling_interval,
+ def __init__(self, interface_mappings, polling_interval,
root_helper):
self.polling_interval = polling_interval
self.root_helper = root_helper
- self.setup_linux_bridge(br_name_prefix, physical_interface)
- self.setup_rpc(physical_interface)
+ self.setup_linux_bridge(interface_mappings)
+ self.setup_rpc(interface_mappings.values())
- def setup_rpc(self, physical_interface):
- mac = utils.get_interface_mac(physical_interface)
+ def setup_rpc(self, physical_interfaces):
+ # REVISIT try until one succeeds?
+ mac = utils.get_interface_mac(physical_interfaces[0])
self.agent_id = '%s%s' % ('lb', (mac.replace(":", "")))
self.topic = topics.AGENT
self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
monitor = pyudev.Monitor.from_netlink(self.udev)
monitor.filter_by('net')
- def setup_linux_bridge(self, br_name_prefix, physical_interface):
- self.linux_br = LinuxBridge(br_name_prefix, physical_interface,
- self.root_helper)
+ def setup_linux_bridge(self, interface_mappings):
+ self.linux_br = LinuxBridge(interface_mappings, self.root_helper)
- def process_port_binding(self, network_id, interface_id, vlan_id):
- return self.linux_br.add_interface(network_id, vlan_id, interface_id)
+ def process_port_binding(self, network_id, interface_id,
+ physical_network, vlan_id):
+ return self.linux_br.add_interface(network_id,
+ physical_network, vlan_id,
+ interface_id)
def remove_port_binding(self, network_id, interface_id):
bridge_name = self.linux_br.get_bridge_name(network_id)
# create the networking for the port
self.process_port_binding(details['network_id'],
details['port_id'],
+ details['physical_network'],
details['vlan_id'])
else:
self.remove_port_binding(details['network_id'],
# (TODO) gary - swap with common logging
logging_config.setup_logging(cfg.CONF)
- br_name_prefix = BRIDGE_NAME_PREFIX
- physical_interface = cfg.CONF.LINUX_BRIDGE.physical_interface
+ interface_mappings = {}
+ for mapping in cfg.CONF.LINUX_BRIDGE.physical_interface_mappings:
+ try:
+ physical_network, physical_interface = mapping.split(':')
+ interface_mappings[physical_network] = physical_interface
+ LOG.debug("physical network %s mapped to physical interface %s" %
+ (physical_network, physical_interface))
+ except ValueError as ex:
+ LOG.error("Invalid physical interface mapping: \'%s\' - %s" %
+ (mapping, ex))
+ sys.exit(1)
+
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
- if not cfg.CONF.AGENT.target_v2_api:
- rpc = False
-
if rpc:
- plugin = LinuxBridgeQuantumAgentRPC(br_name_prefix,
- physical_interface,
+ plugin = LinuxBridgeQuantumAgentRPC(interface_mappings,
polling_interval,
root_helper)
else:
db_connection_url = cfg.CONF.DATABASE.sql_connection
- target_v2_api = cfg.CONF.AGENT.target_v2_api
- plugin = LinuxBridgeQuantumAgentDB(br_name_prefix,
- physical_interface,
+ plugin = LinuxBridgeQuantumAgentDB(interface_mappings,
polling_interval,
reconnect_interval,
root_helper,
- target_v2_api,
db_connection_url)
LOG.info("Agent initialized successfully, now running... ")
plugin.daemon_loop()
from quantum.openstack.common import cfg
+DEFAULT_VLAN_RANGES = ['default:1000:2999']
+DEFAULT_INTERFACE_MAPPINGS = ['default:eth1']
+
vlan_opts = [
- cfg.IntOpt('vlan_start', default=1000),
- cfg.IntOpt('vlan_end', default=3000),
+ cfg.ListOpt('network_vlan_ranges',
+ default=DEFAULT_VLAN_RANGES,
+ help="List of <physical_network>:<vlan_min>:<vlan_max> "
+ "or <physical_network>"),
]
database_opts = [
]
bridge_opts = [
- cfg.StrOpt('physical_interface', default='eth1'),
+ cfg.ListOpt('physical_interface_mappings',
+ default=DEFAULT_INTERFACE_MAPPINGS,
+ help="List of <physical_network>:<physical_interface>"),
]
agent_opts = [
cfg.IntOpt('polling_interval', default=2),
cfg.StrOpt('root_helper', default='sudo'),
- cfg.BoolOpt('target_v2_api', default=False),
cfg.BoolOpt('rpc', default=True),
]
# @author: Sumit Naiksatam, Cisco Systems, Inc.
-PORT_STATE = 'port-state'
+FLAT_VLAN_ID = -1
+
PORT_UP = "ACTIVE"
PORT_DOWN = "DOWN"
-UUID = 'uuid'
-TENANTID = 'tenant_id'
-NETWORKID = 'network_id'
-NETWORKNAME = 'name'
-NETWORKPORTS = 'ports'
-OPSTATUS = 'op_status'
-INTERFACEID = 'interface_id'
-PORTSTATE = 'state'
-PORTID = 'port_id'
-PPNAME = 'name'
-PPVLANID = 'vlan_id'
VLANID = 'vlan_id'
-VLANNAME = 'vlan_name'
-
-ATTACHMENT = 'attachment'
PORT_ID = 'port-id'
-PORT_OP_STATUS = 'port-op-status'
-
NET_ID = 'net-id'
-NET_NAME = 'net-name'
-NET_PORTS = 'net-ports'
-NET_OP_STATUS = 'net-op-status'
-NET_VLAN_NAME = 'net-vlan-name'
-NET_VLAN_ID = 'net-vlan-id'
-NET_TENANTS = 'net-tenants'
-
-USERNAME = 'username'
-PASSWORD = 'password'
-
-DELIMITERS = "[,;:\b\s]"
-
-UUID_LENGTH = 36
-
-UNPLUGGED = '(detached)'
-
-ASSOCIATION_STATUS = 'association_status'
-
-ATTACHED = 'attached'
-
-DETACHED = 'detached'
+++ /dev/null
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-#
-# Copyright 2012 Cisco Systems, 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.
-#
-# @author: Sumit Naiksatam, Cisco Systems, Inc.
-# @author: Rohit Agarwalla, Cisco Systems, Inc.
-
-"""
-Exceptions used by the LinuxBridge plugin
-"""
-
-from quantum.common import exceptions
-
-
-class NetworksLimit(exceptions.QuantumException):
- """Total number of network objects limit has been hit"""
- message = _("Unable to create new network. Number of networks"
- "for the system has exceeded the limit")
-
-
-class NetworkVlanBindingAlreadyExists(exceptions.QuantumException):
- """Binding cannot be created, since it already exists"""
- message = _("NetworkVlanBinding for %(vlan_id)s and network "
- "%(network_id)s already exists")
-
-
-class NetworkVlanBindingNotFound(exceptions.QuantumException):
- """Binding could not be found"""
- message = _("NetworkVlanBinding for network "
- "%(network_id)s does not exist")
-
-
-class VlanIDNotFound(exceptions.QuantumException):
- """VLAN ID cannot be found"""
- message = _("Vlan ID %(vlan_id)s not found")
-
-
-class VlanIDNotAvailable(exceptions.QuantumException):
- """No VLAN ID available"""
- message = _("No Vlan ID available")
-
-
-class UnableToChangeVlanRange(exceptions.QuantumException):
- """No VLAN ID available"""
- message = _("Current VLAN ID range %(range_start)s to %(range_end)s "
- "cannot be changed. Please check plugin conf file.")
+++ /dev/null
-# vim: tabstop=4 shiftwidth=4 softtabstop=4
-
-# Copyright 2012, Cisco Systems, Inc.
-#
-# 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.
-# @author: Rohit Agarwalla, Cisco Systems, Inc.
-
-import logging
-
-from sqlalchemy import func
-from sqlalchemy.orm import exc
-
-from quantum.api import api_common
-from quantum.common import exceptions as q_exc
-import quantum.db.api as db
-from quantum.db import models_v2
-from quantum.openstack.common import cfg
-from quantum.plugins.linuxbridge.common import config
-from quantum.plugins.linuxbridge.common import exceptions as c_exc
-from quantum.plugins.linuxbridge.db import l2network_models_v2
-
-LOG = logging.getLogger(__name__)
-
-# The global variable for the database version model
-L2_MODEL = l2network_models_v2
-
-
-def initialize(base=None):
- global L2_MODEL
- 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})
- if base:
- options.update({"base": base})
- db.configure_db(options)
- create_vlanids()
-
-
-def create_vlanids():
- """Prepopulate the vlan_bindings table"""
- LOG.debug("create_vlanids() called")
- session = db.get_session()
- start = cfg.CONF.VLANS.vlan_start
- end = cfg.CONF.VLANS.vlan_end
- try:
- vlanid = session.query(L2_MODEL.VlanID).one()
- except exc.MultipleResultsFound:
- """
- TODO (Sumit): Salvatore rightly points out that this will not handle
- change in VLAN ID range across server reboots. This is currently not
- a supported feature. This logic will need to change if this feature
- has to be supported.
- Per Dan's suggestion we just throw a server exception for now.
- """
- current_start = (
- int(session.query(func.min(L2_MODEL.VlanID.vlan_id)).
- one()[0]))
- current_end = (
- int(session.query(func.max(L2_MODEL.VlanID.vlan_id)).
- one()[0]))
- if current_start != start or current_end != end:
- LOG.debug("Old VLAN range %s-%s" % (current_start, current_end))
- LOG.debug("New VLAN range %s-%s" % (start, end))
- raise c_exc.UnableToChangeVlanRange(range_start=current_start,
- range_end=current_end)
- except exc.NoResultFound:
- LOG.debug("Setting VLAN range to %s-%s" % (start, end))
- while start <= end:
- vlanid = L2_MODEL.VlanID(start)
- session.add(vlanid)
- start += 1
- session.flush()
- return
-
-
-def get_all_vlanids():
- """Get all the vlanids"""
- LOG.debug("get_all_vlanids() called")
- session = db.get_session()
- try:
- vlanids = (session.query(L2_MODEL.VlanID).
- all())
- return vlanids
- except exc.NoResultFound:
- return []
-
-
-def is_vlanid_used(vlan_id):
- """Check if a vlanid is in use"""
- LOG.debug("is_vlanid_used() called")
- session = db.get_session()
- try:
- vlanid = (session.query(L2_MODEL.VlanID).
- filter_by(vlan_id=vlan_id).
- one())
- return vlanid["vlan_used"]
- except exc.NoResultFound:
- raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
-
-
-def release_vlanid(vlan_id):
- """Set the vlanid state to be unused, and delete if not in range"""
- LOG.debug("release_vlanid() called")
- session = db.get_session()
- try:
- vlanid = (session.query(L2_MODEL.VlanID).
- filter_by(vlan_id=vlan_id).
- one())
- vlanid["vlan_used"] = False
- if (vlan_id >= cfg.CONF.VLANS.vlan_start and
- vlan_id <= cfg.CONF.VLANS.vlan_end):
- session.merge(vlanid)
- else:
- session.delete(vlanid)
- session.flush()
- return vlanid["vlan_used"]
- except exc.NoResultFound:
- raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
- return
-
-
-def delete_vlanid(vlan_id):
- """Delete a vlanid entry from db"""
- LOG.debug("delete_vlanid() called")
- session = db.get_session()
- try:
- vlanid = (session.query(L2_MODEL.VlanID).
- filter_by(vlan_id=vlan_id).
- one())
- session.delete(vlanid)
- session.flush()
- return vlanid
- except exc.NoResultFound:
- raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
-
-
-def reserve_vlanid():
- """Reserve the first unused vlanid"""
- LOG.debug("reserve_vlanid() called")
- session = db.get_session()
- try:
- rvlan = (session.query(L2_MODEL.VlanID).
- first())
- if not rvlan:
- create_vlanids()
-
- rvlan = (session.query(L2_MODEL.VlanID).
- filter_by(vlan_used=False).
- first())
- if not rvlan:
- raise c_exc.VlanIDNotAvailable()
-
- rvlanid = (session.query(L2_MODEL.VlanID).
- filter_by(vlan_id=rvlan["vlan_id"]).
- one())
- rvlanid["vlan_used"] = True
- session.merge(rvlanid)
- session.flush()
- return rvlan["vlan_id"]
- except exc.NoResultFound:
- raise c_exc.VlanIDNotAvailable()
-
-
-def reserve_specific_vlanid(vlan_id):
- """Reserve a specific vlanid"""
- LOG.debug("reserve_specific_vlanid() called")
- 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)
- session = db.get_session()
- try:
- rvlanid = (session.query(l2network_models_v2.VlanID).
- filter_by(vlan_id=vlan_id).
- one())
- if rvlanid["vlan_used"]:
- raise q_exc.VlanIdInUse(vlan_id=vlan_id)
- LOG.debug("reserving dynamic vlanid %s" % vlan_id)
- rvlanid["vlan_used"] = True
- session.merge(rvlanid)
- except exc.NoResultFound:
- rvlanid = l2network_models_v2.VlanID(vlan_id)
- LOG.debug("reserving non-dynamic vlanid %s" % vlan_id)
- rvlanid["vlan_used"] = True
- session.add(rvlanid)
- session.flush()
-
-
-def get_all_vlanids_used():
- """Get all the vlanids used"""
- LOG.debug("get_all_vlanids() called")
- session = db.get_session()
- try:
- vlanids = (session.query(L2_MODEL.VlanID).
- filter_by(vlan_used=True).
- all())
- return vlanids
- except exc.NoResultFound:
- return []
-
-
-def get_all_vlan_bindings():
- """List all the vlan to network associations"""
- LOG.debug("get_all_vlan_bindings() called")
- session = db.get_session()
- try:
- bindings = (session.query(L2_MODEL.VlanBinding).
- all())
- return bindings
- except exc.NoResultFound:
- return []
-
-
-def get_vlan_binding(netid):
- """List the vlan given a network_id"""
- LOG.debug("get_vlan_binding() called")
- session = db.get_session()
- try:
- binding = (session.query(L2_MODEL.VlanBinding).
- filter_by(network_id=netid).
- one())
- return binding
- except exc.NoResultFound:
- raise c_exc.NetworkVlanBindingNotFound(network_id=netid)
-
-
-def add_vlan_binding(vlanid, netid):
- """Add a vlan to network association"""
- LOG.debug("add_vlan_binding() called")
- session = db.get_session()
- try:
- binding = (session.query(L2_MODEL.VlanBinding).
- filter_by(vlan_id=vlanid).
- one())
- raise c_exc.NetworkVlanBindingAlreadyExists(vlan_id=vlanid,
- network_id=netid)
- except exc.NoResultFound:
- binding = L2_MODEL.VlanBinding(vlanid, netid)
- session.add(binding)
- session.flush()
- return binding
-
-
-def remove_vlan_binding(netid):
- """Remove a vlan to network association"""
- LOG.debug("remove_vlan_binding() called")
- session = db.get_session()
- try:
- binding = (session.query(L2_MODEL.VlanBinding).
- filter_by(network_id=netid).
- one())
- session.delete(binding)
- session.flush()
- return binding
- except exc.NoResultFound:
- pass
-
-
-def update_vlan_binding(netid, newvlanid=None):
- """Update a vlan to network association"""
- LOG.debug("update_vlan_binding() called")
- session = db.get_session()
- try:
- binding = (session.query(L2_MODEL.VlanBinding).
- filter_by(network_id=netid).
- one())
- if newvlanid:
- binding["vlan_id"] = newvlanid
- session.merge(binding)
- session.flush()
- return binding
- except exc.NoResultFound:
- raise q_exc.NetworkNotFound(net_id=netid)
-
-
-def get_port_from_device(device):
- """Get port from database"""
- LOG.debug("get_port_from_device() called")
- session = db.get_session()
- ports = session.query(models_v2.Port).all()
- if not ports:
- return
- for port in ports:
- if port['id'].startswith(device):
- return port
- return
-
-
-def set_port_status(port_id, status):
- """Set the port status"""
- LOG.debug("set_port_status as %s called", status)
- session = db.get_session()
- try:
- port = session.query(models_v2.Port).filter_by(id=port_id).one()
- port['status'] = status
- if status == api_common.PORT_STATUS_DOWN:
- port['device_id'] = ''
- session.merge(port)
- session.flush()
- except exc.NoResultFound:
- raise q_exc.PortNotFound(port_id=port_id)
--- /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.
+
+import logging
+
+from sqlalchemy.orm import exc
+
+from quantum.api import api_common
+from quantum.common import exceptions as q_exc
+import quantum.db.api as db
+from quantum.db import models_v2
+from quantum.openstack.common import cfg
+from quantum.plugins.linuxbridge.common import config
+from quantum.plugins.linuxbridge.db import l2network_models_v2
+
+LOG = logging.getLogger(__name__)
+
+
+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 sync_network_states(network_vlan_ranges):
+ """Synchronize network_states table with current configured VLAN ranges."""
+
+ # 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))
+
+ session = db.get_session()
+ with session.begin():
+ # remove from table unallocated vlans not currently allocatable
+ try:
+ states = (session.query(l2network_models_v2.NetworkState).
+ filter_by(physical_network=physical_network).
+ all())
+ for state in states:
+ try:
+ # see if vlan is allocatable
+ vlan_ids.remove(state.vlan_id)
+ except KeyError:
+ # it's not allocatable, so check if its allocated
+ if not state.allocated:
+ # it's not, so remove it from table
+ LOG.debug("removing vlan %s on physical network "
+ "%s from pool" %
+ (state.vlan_id, physical_network))
+ session.delete(state)
+ except exc.NoResultFound:
+ pass
+
+ # add missing allocatable vlans to table
+ for vlan_id in sorted(vlan_ids):
+ state = l2network_models_v2.NetworkState(physical_network,
+ vlan_id)
+ session.add(state)
+
+
+def get_network_state(physical_network, vlan_id):
+ """Get state of specified network"""
+
+ session = db.get_session()
+ try:
+ state = (session.query(l2network_models_v2.NetworkState).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ one())
+ return state
+ except exc.NoResultFound:
+ return None
+
+
+def reserve_network(session):
+ with session.begin(subtransactions=True):
+ state = (session.query(l2network_models_v2.NetworkState).
+ filter_by(allocated=False).
+ first())
+ if not state:
+ raise q_exc.NoNetworkAvailable()
+ LOG.debug("reserving vlan %s on physical network %s from pool" %
+ (state.vlan_id, state.physical_network))
+ state.allocated = True
+ return (state.physical_network, state.vlan_id)
+
+
+def reserve_specific_network(session, physical_network, vlan_id):
+ with session.begin(subtransactions=True):
+ try:
+ state = (session.query(l2network_models_v2.NetworkState).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ one())
+ if state.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))
+ state.allocated = True
+ except exc.NoResultFound:
+ LOG.debug("reserving specific vlan %s on physical network %s "
+ "outside pool" % (vlan_id, physical_network))
+ state = l2network_models_v2.NetworkState(physical_network, vlan_id)
+ state.allocated = True
+ session.add(state)
+
+
+def release_network(session, physical_network, vlan_id, network_vlan_ranges):
+ with session.begin(subtransactions=True):
+ try:
+ state = (session.query(l2network_models_v2.NetworkState).
+ filter_by(physical_network=physical_network,
+ vlan_id=vlan_id).
+ one())
+ state.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 inside:
+ LOG.debug("releasing vlan %s on physical network %s to pool" %
+ (vlan_id, physical_network))
+ else:
+ LOG.debug("releasing vlan %s on physical network %s outside "
+ "pool" % (vlan_id, physical_network))
+ session.delete(state)
+ except exc.NoResultFound:
+ LOG.warning("vlan_id %s on physical network %s not found" %
+ (vlan_id, physical_network))
+
+
+def add_network_binding(session, network_id, physical_network, vlan_id):
+ with session.begin(subtransactions=True):
+ binding = l2network_models_v2.NetworkBinding(network_id,
+ physical_network, vlan_id)
+ session.add(binding)
+
+
+def get_network_binding(session, network_id):
+ try:
+ binding = (session.query(l2network_models_v2.NetworkBinding).
+ filter_by(network_id=network_id).
+ one())
+ return binding
+ except exc.NoResultFound:
+ return
+
+
+def get_port_from_device(device):
+ """Get port from database"""
+ LOG.debug("get_port_from_device() called")
+ session = db.get_session()
+ ports = session.query(models_v2.Port).all()
+ if not ports:
+ return
+ for port in ports:
+ if port['id'].startswith(device):
+ return port
+ return
+
+
+def set_port_status(port_id, status):
+ """Set the port status"""
+ LOG.debug("set_port_status as %s called", status)
+ session = db.get_session()
+ try:
+ port = session.query(models_v2.Port).filter_by(id=port_id).one()
+ port['status'] = status
+ if status == api_common.PORT_STATUS_DOWN:
+ port['device_id'] = ''
+ session.merge(port)
+ session.flush()
+ except exc.NoResultFound:
+ raise q_exc.PortNotFound(port_id=port_id)
# limitations under the License.
import sqlalchemy as sa
-from sqlalchemy import orm
from quantum.db import model_base
-class VlanID(model_base.BASEV2):
- """Represents a vlan_id usage"""
- __tablename__ = 'vlan_ids'
+class NetworkState(model_base.BASEV2):
+ """Represents state of vlan_id on physical network"""
+ __tablename__ = 'network_states'
- vlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True)
- vlan_used = sa.Column(sa.Boolean, nullable=False)
+ physical_network = sa.Column(sa.String(64), nullable=False,
+ primary_key=True)
+ vlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True,
+ autoincrement=False)
+ allocated = sa.Column(sa.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 "<NetworkState(%s,%d,%s)>" % (self.physical_network,
+ self.vlan_id, self.allocated)
-class VlanBinding(model_base.BASEV2):
- """Represents a binding of vlan_id to network_id"""
- __tablename__ = 'vlan_bindings'
+class NetworkBinding(model_base.BASEV2):
+ """Represents binding of virtual network to physical_network and vlan_id"""
+ __tablename__ = 'network_bindings'
- network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id',
- ondelete="CASCADE"),
+ network_id = sa.Column(sa.String(36),
+ sa.ForeignKey('networks.id', ondelete="CASCADE"),
primary_key=True)
+ physical_network = sa.Column(sa.String(64), nullable=False)
vlan_id = sa.Column(sa.Integer, nullable=False)
- def __init__(self, vlan_id, network_id):
- self.vlan_id = vlan_id
+ def __init__(self, network_id, physical_network, vlan_id):
self.network_id = network_id
+ self.physical_network = physical_network
+ self.vlan_id = vlan_id
def __repr__(self):
- return "<VlanBinding(%d,%s)>" % (self.vlan_id, self.network_id)
+ return "<NetworkBinding(%s,%s,%d)>" % (self.network_id,
+ self.physical_network,
+ self.vlan_id)
# limitations under the License.
import logging
+import sys
from quantum.api import api_common
from quantum.api.v2 import attributes
+from quantum.common import exceptions as q_exc
from quantum.common import topics
+from quantum.db import api as db_api
from quantum.db import db_base_plugin_v2
from quantum.db import models_v2
from quantum.openstack.common import context
from quantum.openstack.common import rpc
from quantum.openstack.common.rpc import dispatcher
from quantum.openstack.common.rpc import proxy
-from quantum.plugins.linuxbridge.db import l2network_db as cdb
+from quantum.plugins.linuxbridge.common import constants
+from quantum.plugins.linuxbridge.db import l2network_db_v2 as db
from quantum import policy
# Device names start with "tap"
TAP_PREFIX_LEN = 3
- def __init__(self, context):
- self.context = context
+ def __init__(self, rpc_context):
+ self.rpc_context = rpc_context
def create_rpc_dispatcher(self):
'''Get the rpc dispatcher for this manager.
'''
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 = cdb.get_port_from_device(device[self.TAP_PREFIX_LEN:])
+ port = db.get_port_from_device(device[self.TAP_PREFIX_LEN:])
if port:
- vlan_binding = cdb.get_vlan_binding(port['network_id'])
+ binding = db.get_network_binding(db_api.get_session(),
+ port['network_id'])
entry = {'device': device,
- 'vlan_id': vlan_binding['vlan_id'],
+ 'physical_network': binding.physical_network,
+ 'vlan_id': binding.vlan_id,
'network_id': port['network_id'],
'port_id': port['id'],
'admin_state_up': port['admin_state_up']}
# Set the port status to UP
- cdb.set_port_status(port['id'], api_common.PORT_STATUS_UP)
+ db.set_port_status(port['id'], api_common.PORT_STATUS_UP)
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')
device = kwargs.get('device')
LOG.debug("Device %s no longer exists on %s", device, agent_id)
- port = cdb.get_port_from_device(device[self.TAP_PREFIX_LEN:])
+ port = db.get_port_from_device(device[self.TAP_PREFIX_LEN:])
if port:
entry = {'device': device,
'exists': True}
# Set port status to DOWN
- cdb.set_port_status(port['id'], api_common.PORT_STATUS_UP)
+ db.set_port_status(port['id'], api_common.PORT_STATUS_DOWN)
else:
entry = {'device': device,
'exists': False}
network_id=network_id),
topic=self.topic_network_delete)
- def port_update(self, context, port, vlan_id):
+ def port_update(self, context, port, physical_network, vlan_id):
self.fanout_cast(context,
self.make_msg('port_update',
port=port,
+ physical_network=physical_network,
vlan_id=vlan_id),
topic=self.topic_port_update)
be updated to take advantage of it.
"""
+ # This attribute specifies whether the plugin supports or not
+ # bulk operations. Name mangling is used in order to ensure it
+ # is qualified by class
+ __native_bulk_support = True
+
supported_extension_aliases = ["provider"]
def __init__(self):
- cdb.initialize(base=models_v2.model_base.BASEV2)
+ db.initialize()
+ self._parse_network_vlan_ranges()
+ db.sync_network_states(self.network_vlan_ranges)
self.rpc = cfg.CONF.AGENT.rpc
- if cfg.CONF.AGENT.rpc and cfg.CONF.AGENT.target_v2_api:
- self.setup_rpc()
- if not cfg.CONF.AGENT.target_v2_api:
- self.rpc = False
+ if self.rpc:
+ self._setup_rpc()
LOG.debug("Linux Bridge Plugin initialization complete")
- def setup_rpc(self):
+ 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.callbacks = LinuxBridgeRpcCallbacks(self.context)
+ self.callbacks = LinuxBridgeRpcCallbacks(self.rpc_context)
self.dispatcher = self.callbacks.create_rpc_dispatcher()
self.conn.create_consumer(self.topic, self.dispatcher,
fanout=False)
self.conn.consume_in_thread()
self.notifier = AgentNotifierApi(topics.AGENT)
- # TODO(rkukura) Use core mechanism for attribute authorization
+ def _parse_network_vlan_ranges(self):
+ self.network_vlan_ranges = {}
+ for entry in cfg.CONF.VLANS.network_vlan_ranges:
+ if ':' in entry:
+ try:
+ physical_network, vlan_min, vlan_max = entry.split(':')
+ self._add_network_vlan_range(physical_network,
+ 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] = []
+
+ # REVISIT(rkukura) Use core mechanism for attribute authorization
# when available.
def _check_provider_view_auth(self, context, network):
def _extend_network_dict(self, context, network):
if self._check_provider_view_auth(context, network):
- vlan_binding = cdb.get_vlan_binding(network['id'])
- network['provider:vlan_id'] = vlan_binding['vlan_id']
+ binding = db.get_network_binding(context.session, network['id'])
+ network['provider:physical_network'] = binding.physical_network
+ if binding.vlan_id == constants.FLAT_VLAN_ID:
+ network['provider:network_type'] = 'flat'
+ network['provider:vlan_id'] = None
+ else:
+ network['provider:network_type'] = 'vlan'
+ network['provider:vlan_id'] = binding.vlan_id
+
+ def _process_provider_create(self, context, attrs):
+ network_type = attrs.get('provider:network_type')
+ physical_network = attrs.get('provider:physical_network')
+ vlan_id = attrs.get('provider:vlan_id')
+
+ network_type_set = attributes.is_attr_set(network_type)
+ physical_network_set = attributes.is_attr_set(physical_network)
+ vlan_id_set = attributes.is_attr_set(vlan_id)
+
+ if not (network_type_set or physical_network_set or vlan_id_set):
+ return (None, None, None)
+
+ # Authorize before exposing plugin details to client
+ self._enforce_provider_set_auth(context, attrs)
+
+ if not network_type_set:
+ msg = _("provider:network_type required")
+ raise q_exc.InvalidInput(error_message=msg)
+ elif network_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 == 'vlan':
+ if not vlan_id_set:
+ msg = _("provider:vlan_id required")
+ raise q_exc.InvalidInput(error_message=msg)
+ else:
+ msg = _("invalid provider:network_type %s" % network_type)
+ raise q_exc.InvalidInput(error_message=msg)
+
+ if physical_network_set:
+ 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)
+
+ return (network_type, physical_network, vlan_id)
+
+ def _check_provider_update(self, context, attrs):
+ network_type = attrs.get('provider:network_type')
+ physical_network = attrs.get('provider:physical_network')
+ vlan_id = attrs.get('provider:vlan_id')
+
+ network_type_set = attributes.is_attr_set(network_type)
+ physical_network_set = attributes.is_attr_set(physical_network)
+ vlan_id_set = attributes.is_attr_set(vlan_id)
+
+ if not (network_type_set or physical_network_set or vlan_id_set):
+ return
+
+ # Authorize before exposing plugin details to client
+ self._enforce_provider_set_auth(context, attrs)
+
+ msg = _("plugin does not support updating provider attributes")
+ raise q_exc.InvalidInput(error_message=msg)
def create_network(self, context, network):
- net = super(LinuxBridgePluginV2, self).create_network(context,
- network)
- try:
- vlan_id = network['network'].get('provider:vlan_id')
- if vlan_id not in (None, attributes.ATTR_NOT_SPECIFIED):
- self._enforce_provider_set_auth(context, net)
- cdb.reserve_specific_vlanid(int(vlan_id))
+ (network_type, physical_network,
+ vlan_id) = self._process_provider_create(context,
+ network['network'])
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ if not network_type:
+ physical_network, vlan_id = db.reserve_network(session)
else:
- vlan_id = cdb.reserve_vlanid()
- cdb.add_vlan_binding(vlan_id, net['id'])
+ db.reserve_specific_network(session, physical_network, vlan_id)
+ net = super(LinuxBridgePluginV2, self).create_network(context,
+ network)
+ db.add_network_binding(session, net['id'],
+ physical_network, vlan_id)
self._extend_network_dict(context, net)
- except:
- super(LinuxBridgePluginV2, self).delete_network(context,
- net['id'])
- raise
+ # note - exception will rollback entire transaction
return net
def update_network(self, context, id, network):
- net = super(LinuxBridgePluginV2, self).update_network(context, id,
- network)
- self._extend_network_dict(context, net)
+ self._check_provider_update(context, network['network'])
+
+ session = context.session
+ with session.begin(subtransactions=True):
+ net = super(LinuxBridgePluginV2, self).update_network(context, id,
+ network)
+ self._extend_network_dict(context, net)
return net
def delete_network(self, context, id):
- vlan_binding = cdb.get_vlan_binding(id)
- result = super(LinuxBridgePluginV2, self).delete_network(context, id)
- cdb.release_vlanid(vlan_binding['vlan_id'])
+ session = context.session
+ with session.begin(subtransactions=True):
+ binding = db.get_network_binding(session, id)
+ result = super(LinuxBridgePluginV2, self).delete_network(context,
+ id)
+ db.release_network(session, binding.physical_network,
+ binding.vlan_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.rpc:
- self.notifier.network_delete(self.context, id)
- return result
+ self.notifier.network_delete(self.rpc_context, id)
def get_network(self, context, id, fields=None, verbose=None):
net = super(LinuxBridgePluginV2, self).get_network(context, id,
port = super(LinuxBridgePluginV2, self).update_port(context, id, port)
if self.rpc:
if original_port['admin_state_up'] != port['admin_state_up']:
- vlan_binding = cdb.get_vlan_binding(port['network_id'])
- self.notifier.port_update(self.context, port,
- vlan_binding['vlan_id'])
+ binding = db.get_network_binding(context.session,
+ port['network_id'])
+ self.notifier.port_update(self.rpc_context, port,
+ binding.physical_network,
+ binding.vlan_id)
return port
--- /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.
+
+import unittest2 as unittest
+
+from quantum.openstack.common import cfg
+from quantum.plugins.linuxbridge.common import config
+
+
+class ConfigurationTest(unittest.TestCase):
+
+ def test_defaults(self):
+ self.assertEqual('sqlite://',
+ cfg.CONF.DATABASE.sql_connection)
+ self.assertEqual(-1,
+ cfg.CONF.DATABASE.sql_max_retries)
+ self.assertEqual(2,
+ cfg.CONF.DATABASE.reconnect_interval)
+ self.assertEqual(2,
+ cfg.CONF.AGENT.polling_interval)
+ self.assertEqual('sudo',
+ cfg.CONF.AGENT.root_helper)
+
+ ranges = cfg.CONF.VLANS.network_vlan_ranges
+ self.assertEqual(1, len(ranges))
+ self.assertEqual('default:1000:2999', ranges[0])
+
+ mappings = cfg.CONF.LINUX_BRIDGE.physical_interface_mappings
+ self.assertEqual(1, len(mappings))
+ self.assertEqual('default:eth1', mappings[0])
--- /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.
+
+import unittest2
+
+from quantum.common import exceptions as q_exc
+from quantum.db import api as db
+from quantum.plugins.linuxbridge.db import l2network_db_v2 as lb_db
+
+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)]}
+TEST_NETWORK_ID = 'abcdefghijklmnopqrstuvwxyz'
+
+
+class NetworkStatesTest(unittest2.TestCase):
+ def setUp(self):
+ lb_db.initialize()
+ lb_db.sync_network_states(VLAN_RANGES)
+ self.session = db.get_session()
+
+ def tearDown(self):
+ db.clear_db()
+
+ def test_sync_network_states(self):
+ self.assertIsNone(lb_db.get_network_state(PHYS_NET,
+ VLAN_MIN - 1))
+ self.assertFalse(lb_db.get_network_state(PHYS_NET,
+ VLAN_MIN).allocated)
+ self.assertFalse(lb_db.get_network_state(PHYS_NET,
+ VLAN_MIN + 1).allocated)
+ self.assertFalse(lb_db.get_network_state(PHYS_NET,
+ VLAN_MAX).allocated)
+ self.assertIsNone(lb_db.get_network_state(PHYS_NET,
+ VLAN_MAX + 1))
+
+ lb_db.sync_network_states(UPDATED_VLAN_RANGES)
+
+ self.assertIsNone(lb_db.get_network_state(PHYS_NET,
+ VLAN_MIN + 5 - 1))
+ self.assertFalse(lb_db.get_network_state(PHYS_NET,
+ VLAN_MIN + 5).allocated)
+ self.assertFalse(lb_db.get_network_state(PHYS_NET,
+ VLAN_MIN + 5 + 1).allocated)
+ self.assertFalse(lb_db.get_network_state(PHYS_NET,
+ VLAN_MAX + 5).allocated)
+ self.assertIsNone(lb_db.get_network_state(PHYS_NET,
+ VLAN_MAX + 5 + 1))
+
+ def test_network_pool(self):
+ vlan_ids = set()
+ for x in xrange(VLAN_MIN, VLAN_MAX + 1):
+ physical_network, vlan_id = lb_db.reserve_network(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):
+ physical_network, vlan_id = lb_db.reserve_network(self.session)
+
+ for vlan_id in vlan_ids:
+ lb_db.release_network(self.session, PHYS_NET, vlan_id, VLAN_RANGES)
+
+ def test_specific_network_inside_pool(self):
+ vlan_id = VLAN_MIN + 5
+ self.assertFalse(lb_db.get_network_state(PHYS_NET,
+ vlan_id).allocated)
+ lb_db.reserve_specific_network(self.session, PHYS_NET, vlan_id)
+ self.assertTrue(lb_db.get_network_state(PHYS_NET,
+ vlan_id).allocated)
+
+ with self.assertRaises(q_exc.VlanIdInUse):
+ lb_db.reserve_specific_network(self.session, PHYS_NET, vlan_id)
+
+ lb_db.release_network(self.session, PHYS_NET, vlan_id, VLAN_RANGES)
+ self.assertFalse(lb_db.get_network_state(PHYS_NET,
+ vlan_id).allocated)
+
+ def test_specific_network_outside_pool(self):
+ vlan_id = VLAN_MAX + 5
+ self.assertIsNone(lb_db.get_network_state(PHYS_NET, vlan_id))
+ lb_db.reserve_specific_network(self.session, PHYS_NET, vlan_id)
+ self.assertTrue(lb_db.get_network_state(PHYS_NET,
+ vlan_id).allocated)
+
+ with self.assertRaises(q_exc.VlanIdInUse):
+ lb_db.reserve_specific_network(self.session, PHYS_NET, vlan_id)
+
+ lb_db.release_network(self.session, PHYS_NET, vlan_id, VLAN_RANGES)
+ self.assertIsNone(lb_db.get_network_state(PHYS_NET, vlan_id))
+
+
+class NetworkBindingsTest(unittest2.TestCase):
+ def setUp(self):
+ lb_db.initialize()
+ self.session = db.get_session()
+
+ def tearDown(self):
+ db.clear_db()
+
+ def test_add_network_binding(self):
+ self.assertIsNone(lb_db.get_network_binding(self.session,
+ TEST_NETWORK_ID))
+ lb_db.add_network_binding(self.session, TEST_NETWORK_ID, PHYS_NET,
+ 1234)
+ binding = lb_db.get_network_binding(self.session, TEST_NETWORK_ID)
+ self.assertIsNotNone(binding)
+ self.assertEqual(binding.network_id, TEST_NETWORK_ID)
+ self.assertEqual(binding.physical_network, PHYS_NET)
+ self.assertEqual(binding.vlan_id, 1234)
topics.PORT,
topics.UPDATE),
'port_update', rpc_method='fanout_cast',
- port='fake_port', vlan_id='fake_vlan_id')
+ port='fake_port',
+ physical_network='fake_net',
+ vlan_id='fake_vlan_id')
def test_device_details(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
filter_by(vlan_id=vlan_id).
one())
if record.vlan_used:
- raise q_exc.VlanIdInUse(vlan_id=vlan_id)
+ # 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
except exc.NoResultFound:
network['provider:vlan_id'] = ovs_db_v2.get_vlan(
network['id'], context.session)
+ def _process_provider_create(self, context, attrs):
+ network_type = attrs.get('provider:network_type')
+ physical_network = attrs.get('provider:physical_network')
+ vlan_id = attrs.get('provider:vlan_id')
+
+ network_type_set = attributes.is_attr_set(network_type)
+ physical_network_set = attributes.is_attr_set(physical_network)
+ vlan_id_set = attributes.is_attr_set(vlan_id)
+
+ if not (network_type_set or physical_network_set or vlan_id_set):
+ return (None, None, None)
+
+ # Authorize before exposing plugin details to client
+ self._enforce_provider_set_auth(context, attrs)
+
+ 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':
+ if not vlan_id_set:
+ msg = _("provider:vlan_id required")
+ raise q_exc.InvalidInput(error_message=msg)
+ else:
+ msg = _("invalid provider:network_type %s" % network_type)
+ raise q_exc.InvalidInput(error_message=msg)
+
+ if physical_network_set:
+ msg = _("plugin does not support specifying physical_network")
+ 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 _check_provider_update(self, context, attrs):
+ network_type = attrs.get('provider:network_type')
+ physical_network = attrs.get('provider:physical_network')
+ vlan_id = attrs.get('provider:vlan_id')
+
+ network_type_set = attributes.is_attr_set(network_type)
+ physical_network_set = attributes.is_attr_set(physical_network)
+ vlan_id_set = attributes.is_attr_set(vlan_id)
+
+ if not (network_type_set or physical_network_set or vlan_id_set):
+ return
+
+ # Authorize before exposing plugin details to client
+ self._enforce_provider_set_auth(context, attrs)
+
+ msg = _("plugin does not support updating provider attributes")
+ raise q_exc.InvalidInput(error_message=msg)
+
def create_network(self, context, network):
+ (network_type, physical_network,
+ vlan_id) = self._process_provider_create(context,
+ network['network'])
+
net = super(OVSQuantumPluginV2, self).create_network(context, network)
try:
- vlan_id = network['network'].get('provider:vlan_id')
- if vlan_id not in (None, attributes.ATTR_NOT_SPECIFIED):
- self._enforce_provider_set_auth(context, net)
- ovs_db_v2.reserve_specific_vlan_id(vlan_id, context.session)
- else:
+ if not network_type:
vlan_id = ovs_db_v2.reserve_vlan_id(context.session)
+ else:
+ ovs_db_v2.reserve_specific_vlan_id(vlan_id, context.session)
except Exception:
super(OVSQuantumPluginV2, self).delete_network(context, net['id'])
raise
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)
import unittest
from quantum.openstack.common import cfg
+from quantum.plugins.openvswitch.common import config
class ConfigurationTest(unittest.TestCase):
# Paste configuration file
api_paste_config = api-paste.ini.test
+
+# The messaging module to use, defaults to kombu.
+rpc_backend = quantum.openstack.common.rpc.impl_fake