Merge "Implementation of second phase of provider extension."
This commit is contained in:
commit
c2b890e74b
@ -43,7 +43,7 @@ api_paste_config = api-paste.ini
|
|||||||
# allow_bulk = True
|
# allow_bulk = True
|
||||||
# RPC configuration options. Defined in rpc __init__
|
# RPC configuration options. Defined in rpc __init__
|
||||||
# The messaging module to use, defaults to kombu.
|
# 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
|
# Size of RPC thread pool
|
||||||
# rpc_thread_pool_size = 64,
|
# rpc_thread_pool_size = 64,
|
||||||
# Size of RPC connection pool
|
# Size of RPC connection pool
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
[VLANS]
|
[VLANS]
|
||||||
vlan_start = 1000
|
# (ListOpt) Comma-separated list of
|
||||||
vlan_end = 3000
|
# <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]
|
[DATABASE]
|
||||||
# This line MUST be changed to actually run the plugin.
|
# This line MUST be changed to actually run the plugin.
|
||||||
@ -16,8 +19,12 @@ sql_connection = sqlite://
|
|||||||
reconnect_interval = 2
|
reconnect_interval = 2
|
||||||
|
|
||||||
[LINUX_BRIDGE]
|
[LINUX_BRIDGE]
|
||||||
# This is the interface connected to the switch on your Quantum network
|
# (ListOpt) Comma-separated list of
|
||||||
physical_interface = eth1
|
# <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]
|
||||||
# Agent's polling interval in seconds
|
# Agent's polling interval in seconds
|
||||||
@ -25,7 +32,5 @@ polling_interval = 2
|
|||||||
# Change to "sudo quantum-rootwrap" to limit commands that can be run
|
# Change to "sudo quantum-rootwrap" to limit commands that can be run
|
||||||
# as root.
|
# as root.
|
||||||
root_helper = sudo
|
root_helper = sudo
|
||||||
# Use Quantumv2 API
|
|
||||||
# target_v2_api = False
|
|
||||||
# Use RPC messaging to interface between agent and plugin
|
# Use RPC messaging to interface between agent and plugin
|
||||||
# rpc = True
|
# rpc = True
|
||||||
|
@ -27,6 +27,10 @@ from quantum.common import exceptions as q_exc
|
|||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def is_attr_set(attribute):
|
||||||
|
return attribute not in (None, ATTR_NOT_SPECIFIED)
|
||||||
|
|
||||||
|
|
||||||
def _validate_boolean(data, valid_values=None):
|
def _validate_boolean(data, valid_values=None):
|
||||||
if data in [True, False]:
|
if data in [True, False]:
|
||||||
return
|
return
|
||||||
@ -46,6 +50,19 @@ def _validate_values(data, valid_values=None):
|
|||||||
return msg
|
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):
|
def _validate_mac_address(data, valid_values=None):
|
||||||
try:
|
try:
|
||||||
netaddr.EUI(data)
|
netaddr.EUI(data)
|
||||||
@ -120,6 +137,7 @@ MAC_PATTERN = "^%s[aceACE02468](:%s{2}){5}$" % (HEX_ELEM, HEX_ELEM)
|
|||||||
# Dictionary that maintains a list of validation functions
|
# Dictionary that maintains a list of validation functions
|
||||||
validators = {'type:boolean': _validate_boolean,
|
validators = {'type:boolean': _validate_boolean,
|
||||||
'type:values': _validate_values,
|
'type:values': _validate_values,
|
||||||
|
'type:range': _validate_range,
|
||||||
'type:mac_address': _validate_mac_address,
|
'type:mac_address': _validate_mac_address,
|
||||||
'type:ip_address': _validate_ip_address,
|
'type:ip_address': _validate_ip_address,
|
||||||
'type:ip_address_or_none': _validate_ip_address_or_none,
|
'type:ip_address_or_none': _validate_ip_address_or_none,
|
||||||
|
@ -119,7 +119,8 @@ class IpAddressInUse(InUse):
|
|||||||
|
|
||||||
class VlanIdInUse(InUse):
|
class VlanIdInUse(InUse):
|
||||||
message = _("Unable to create the network. "
|
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):
|
class ResourceExhausted(QuantumException):
|
||||||
|
@ -755,7 +755,7 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
|
|
||||||
def update_network(self, context, id, network):
|
def update_network(self, context, id, network):
|
||||||
n = network['network']
|
n = network['network']
|
||||||
with context.session.begin():
|
with context.session.begin(subtransactions=True):
|
||||||
network = self._get_network(context, id)
|
network = self._get_network(context, id)
|
||||||
# validate 'shared' parameter
|
# validate 'shared' parameter
|
||||||
if 'shared' in n:
|
if 'shared' in n:
|
||||||
@ -764,7 +764,7 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
return self._make_network_dict(network)
|
return self._make_network_dict(network)
|
||||||
|
|
||||||
def delete_network(self, context, id):
|
def delete_network(self, context, id):
|
||||||
with context.session.begin():
|
with context.session.begin(subtransactions=True):
|
||||||
network = self._get_network(context, id)
|
network = self._get_network(context, id)
|
||||||
|
|
||||||
filter = {'network_id': [id]}
|
filter = {'network_id': [id]}
|
||||||
@ -874,7 +874,7 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
s = subnet['subnet']
|
s = subnet['subnet']
|
||||||
self._validate_subnet(s)
|
self._validate_subnet(s)
|
||||||
|
|
||||||
with context.session.begin():
|
with context.session.begin(subtransactions=True):
|
||||||
if "dns_nameservers" in s:
|
if "dns_nameservers" in s:
|
||||||
old_dns_list = self._get_dns_by_subnet(context, id)
|
old_dns_list = self._get_dns_by_subnet(context, id)
|
||||||
|
|
||||||
@ -922,7 +922,7 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
return self._make_subnet_dict(subnet)
|
return self._make_subnet_dict(subnet)
|
||||||
|
|
||||||
def delete_subnet(self, context, id):
|
def delete_subnet(self, context, id):
|
||||||
with context.session.begin():
|
with context.session.begin(subtransactions=True):
|
||||||
subnet = self._get_subnet(context, id)
|
subnet = self._get_subnet(context, id)
|
||||||
# Check if ports are using this subnet
|
# Check if ports are using this subnet
|
||||||
allocated_qry = context.session.query(models_v2.IPAllocation)
|
allocated_qry = context.session.query(models_v2.IPAllocation)
|
||||||
@ -999,7 +999,7 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
def update_port(self, context, id, port):
|
def update_port(self, context, id, port):
|
||||||
p = port['port']
|
p = port['port']
|
||||||
|
|
||||||
with context.session.begin():
|
with context.session.begin(subtransactions=True):
|
||||||
port = self._get_port(context, id)
|
port = self._get_port(context, id)
|
||||||
# Check if the IPs need to be updated
|
# Check if the IPs need to be updated
|
||||||
if 'fixed_ips' in p:
|
if 'fixed_ips' in p:
|
||||||
@ -1024,7 +1024,7 @@ class QuantumDbPluginV2(quantum_plugin_base_v2.QuantumPluginBaseV2):
|
|||||||
return self._make_port_dict(port)
|
return self._make_port_dict(port)
|
||||||
|
|
||||||
def delete_port(self, context, id):
|
def delete_port(self, context, id):
|
||||||
with context.session.begin():
|
with context.session.begin(subtransactions=True):
|
||||||
port = self._get_port(context, id)
|
port = self._get_port(context, id)
|
||||||
|
|
||||||
allocated_qry = context.session.query(models_v2.IPAllocation)
|
allocated_qry = context.session.query(models_v2.IPAllocation)
|
||||||
|
@ -17,9 +17,17 @@ from quantum.api.v2 import attributes
|
|||||||
|
|
||||||
EXTENDED_ATTRIBUTES_2_0 = {
|
EXTENDED_ATTRIBUTES_2_0 = {
|
||||||
'networks': {
|
'networks': {
|
||||||
# TODO(rkukura): specify validation
|
'provider:network_type': {'allow_post': True, 'allow_put': True,
|
||||||
'provider:vlan_id': {'allow_post': True, 'allow_put': False,
|
'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,
|
'convert_to': int,
|
||||||
|
'validate': {'type:range': (1, 4095)},
|
||||||
'default': attributes.ATTR_NOT_SPECIFIED,
|
'default': attributes.ATTR_NOT_SPECIFIED,
|
||||||
'is_visible': True},
|
'is_visible': True},
|
||||||
}
|
}
|
||||||
|
@ -58,9 +58,9 @@ quantum_use_dhcp=true
|
|||||||
|
|
||||||
Make the Linux Bridge plugin the current quantum plugin
|
Make the Linux Bridge plugin the current quantum plugin
|
||||||
|
|
||||||
- edit quantum.conf and change the core_plugin according to the API version
|
- edit quantum.conf and change the core_plugin
|
||||||
V1: "core_plugin = quantum.plugins.linuxbridge.LinuxBridgePlugin.LinuxBridgePlugin"
|
|
||||||
V2: "core_plugin = quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2"
|
core_plugin = quantum.plugins.linuxbridge.lb_quantum_plugin.LinuxBridgePluginV2
|
||||||
|
|
||||||
# -- Database config.
|
# -- Database config.
|
||||||
|
|
||||||
@ -104,10 +104,26 @@ mysql> FLUSH PRIVILEGES;
|
|||||||
actually running the server set it to mysql. At any given time, only one
|
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).
|
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
|
- On the quantum server, network_vlan_ranges must be configured in
|
||||||
ethernet interface on that particular host which is being used to participate
|
linuxbridge_conf.ini to specify the names of the physical networks
|
||||||
in the Quantum networks. This configuration has to be applied on each host
|
managed by the linuxbridge plugin, along with the ranges of VLAN IDs
|
||||||
on which the agent runs.
|
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
|
# -- Agent configuration
|
||||||
|
|
||||||
@ -122,6 +138,22 @@ mysql> FLUSH PRIVILEGES;
|
|||||||
|
|
||||||
Note: debug and logging information should be updated in etc/quantum.conf
|
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:
|
$ Run the following:
|
||||||
python linuxbridge_quantum_agent.py --config-file quantum.conf
|
python linuxbridge_quantum_agent.py --config-file quantum.conf
|
||||||
--config-file linuxbridge_conf.ini
|
--config-file linuxbridge_conf.ini
|
||||||
|
@ -34,6 +34,7 @@ import eventlet
|
|||||||
import pyudev
|
import pyudev
|
||||||
from sqlalchemy.ext.sqlsoup import SqlSoup
|
from sqlalchemy.ext.sqlsoup import SqlSoup
|
||||||
|
|
||||||
|
from quantum.agent.linux import utils
|
||||||
from quantum.agent import rpc as agent_rpc
|
from quantum.agent import rpc as agent_rpc
|
||||||
from quantum.common import config as logging_config
|
from quantum.common import config as logging_config
|
||||||
from quantum.common import topics
|
from quantum.common import topics
|
||||||
@ -42,8 +43,7 @@ from quantum.openstack.common import context
|
|||||||
from quantum.openstack.common import rpc
|
from quantum.openstack.common import rpc
|
||||||
from quantum.openstack.common.rpc import dispatcher
|
from quantum.openstack.common.rpc import dispatcher
|
||||||
from quantum.plugins.linuxbridge.common import config
|
from quantum.plugins.linuxbridge.common import config
|
||||||
|
from quantum.plugins.linuxbridge.common import constants
|
||||||
from quantum.agent.linux import utils
|
|
||||||
|
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -67,9 +67,8 @@ DEFAULT_RECONNECT_INTERVAL = 2
|
|||||||
|
|
||||||
|
|
||||||
class LinuxBridge:
|
class LinuxBridge:
|
||||||
def __init__(self, br_name_prefix, physical_interface, root_helper):
|
def __init__(self, interface_mappings, root_helper):
|
||||||
self.br_name_prefix = br_name_prefix
|
self.interface_mappings = interface_mappings
|
||||||
self.physical_interface = physical_interface
|
|
||||||
self.root_helper = root_helper
|
self.root_helper = root_helper
|
||||||
|
|
||||||
def device_exists(self, device):
|
def device_exists(self, device):
|
||||||
@ -92,14 +91,14 @@ class LinuxBridge:
|
|||||||
if not network_id:
|
if not network_id:
|
||||||
LOG.warning("Invalid Network ID, will lead to incorrect bridge"
|
LOG.warning("Invalid Network ID, will lead to incorrect bridge"
|
||||||
"name")
|
"name")
|
||||||
bridge_name = self.br_name_prefix + network_id[0:11]
|
bridge_name = BRIDGE_NAME_PREFIX + network_id[0:11]
|
||||||
return bridge_name
|
return bridge_name
|
||||||
|
|
||||||
def get_subinterface_name(self, vlan_id):
|
def get_subinterface_name(self, physical_interface, vlan_id):
|
||||||
if not vlan_id:
|
if not vlan_id:
|
||||||
LOG.warning("Invalid VLAN ID, will lead to incorrect "
|
LOG.warning("Invalid VLAN ID, will lead to incorrect "
|
||||||
"subinterface name")
|
"subinterface name")
|
||||||
subinterface_name = '%s.%s' % (self.physical_interface, vlan_id)
|
subinterface_name = '%s.%s' % (physical_interface, vlan_id)
|
||||||
return subinterface_name
|
return subinterface_name
|
||||||
|
|
||||||
def get_tap_device_name(self, interface_id):
|
def get_tap_device_name(self, interface_id):
|
||||||
@ -174,21 +173,27 @@ class LinuxBridge:
|
|||||||
DEVICE_NAME_PLACEHOLDER, device_name)
|
DEVICE_NAME_PLACEHOLDER, device_name)
|
||||||
return os.path.exists(bridge_port_path)
|
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."""
|
"""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)
|
bridge_name = self.get_bridge_name(network_id)
|
||||||
self.ensure_bridge(bridge_name, interface)
|
self.ensure_bridge(bridge_name, interface)
|
||||||
return 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."""
|
"""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):
|
if not self.device_exists(interface):
|
||||||
LOG.debug("Creating subinterface %s for VLAN %s on interface %s" %
|
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',
|
if utils.execute(['ip', 'link', 'add', 'link',
|
||||||
self.physical_interface,
|
physical_interface,
|
||||||
'name', interface, 'type', 'vlan', 'id',
|
'name', interface, 'type', 'vlan', 'id',
|
||||||
vlan_id], root_helper=self.root_helper):
|
vlan_id], root_helper=self.root_helper):
|
||||||
return
|
return
|
||||||
@ -225,7 +230,8 @@ class LinuxBridge:
|
|||||||
utils.execute(['brctl', 'addif', bridge_name, interface],
|
utils.execute(['brctl', 'addif', bridge_name, interface],
|
||||||
root_helper=self.root_helper)
|
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
|
If a VIF has been plugged into a network, this function will
|
||||||
add the corresponding tap device to the relevant bridge
|
add the corresponding tap device to the relevant bridge
|
||||||
@ -249,7 +255,10 @@ class LinuxBridge:
|
|||||||
tap_device_name], root_helper=self.root_helper):
|
tap_device_name], root_helper=self.root_helper):
|
||||||
return False
|
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],
|
if utils.execute(['brctl', 'addif', bridge_name, tap_device_name],
|
||||||
root_helper=self.root_helper):
|
root_helper=self.root_helper):
|
||||||
return False
|
return False
|
||||||
@ -257,26 +266,38 @@ class LinuxBridge:
|
|||||||
bridge_name))
|
bridge_name))
|
||||||
return True
|
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:
|
if not interface_id:
|
||||||
"""
|
"""
|
||||||
Since the VIF id is null, no VIF is plugged into this port
|
Since the VIF id is null, no VIF is plugged into this port
|
||||||
no more processing is required
|
no more processing is required
|
||||||
"""
|
"""
|
||||||
return False
|
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):
|
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:
|
else:
|
||||||
tap_device_name = self.get_tap_device_name(interface_id)
|
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):
|
def delete_vlan_bridge(self, bridge_name):
|
||||||
if self.device_exists(bridge_name):
|
if self.device_exists(bridge_name):
|
||||||
interfaces_on_bridge = self.get_interfaces_on_bridge(bridge_name)
|
interfaces_on_bridge = self.get_interfaces_on_bridge(bridge_name)
|
||||||
for interface in interfaces_on_bridge:
|
for interface in interfaces_on_bridge:
|
||||||
self.remove_interface(bridge_name, interface)
|
self.remove_interface(bridge_name, interface)
|
||||||
if interface.startswith(self.physical_interface):
|
for physical_interface in self.interface_mappings.itervalues():
|
||||||
self.delete_vlan(interface)
|
if interface.startswith(physical_interface):
|
||||||
|
self.delete_vlan(interface)
|
||||||
|
|
||||||
LOG.debug("Deleting bridge %s" % bridge_name)
|
LOG.debug("Deleting bridge %s" % bridge_name)
|
||||||
if utils.execute(['ip', 'link', 'set', bridge_name, 'down'],
|
if utils.execute(['ip', 'link', 'set', bridge_name, 'down'],
|
||||||
@ -360,23 +381,23 @@ class LinuxBridgeRpcCallbacks():
|
|||||||
|
|
||||||
class LinuxBridgeQuantumAgentDB:
|
class LinuxBridgeQuantumAgentDB:
|
||||||
|
|
||||||
def __init__(self, br_name_prefix, physical_interface, polling_interval,
|
def __init__(self, interface_mappings, polling_interval,
|
||||||
reconnect_interval, root_helper, target_v2_api,
|
reconnect_interval, root_helper, db_connection_url):
|
||||||
db_connection_url):
|
|
||||||
self.polling_interval = polling_interval
|
self.polling_interval = polling_interval
|
||||||
self.root_helper = root_helper
|
self.root_helper = root_helper
|
||||||
self.setup_linux_bridge(br_name_prefix, physical_interface)
|
self.setup_linux_bridge(interface_mappings)
|
||||||
self.target_v2_api = target_v2_api
|
|
||||||
self.reconnect_interval = reconnect_interval
|
self.reconnect_interval = reconnect_interval
|
||||||
self.db_connected = False
|
self.db_connected = False
|
||||||
self.db_connection_url = db_connection_url
|
self.db_connection_url = db_connection_url
|
||||||
|
|
||||||
def setup_linux_bridge(self, br_name_prefix, physical_interface):
|
def setup_linux_bridge(self, interface_mappings):
|
||||||
self.linux_br = LinuxBridge(br_name_prefix, physical_interface,
|
self.linux_br = LinuxBridge(interface_mappings, self.root_helper)
|
||||||
self.root_helper)
|
|
||||||
|
|
||||||
def process_port_binding(self, network_id, interface_id, vlan_id):
|
def process_port_binding(self, network_id, interface_id,
|
||||||
return self.linux_br.add_interface(network_id, vlan_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):
|
def remove_port_binding(self, network_id, interface_id):
|
||||||
bridge_name = self.linux_br.get_bridge_name(network_id)
|
bridge_name = self.linux_br.get_bridge_name(network_id)
|
||||||
@ -437,16 +458,18 @@ class LinuxBridgeQuantumAgentDB:
|
|||||||
old_port_bindings):
|
old_port_bindings):
|
||||||
vlan_bindings = {}
|
vlan_bindings = {}
|
||||||
try:
|
try:
|
||||||
vlan_binds = db.vlan_bindings.all()
|
network_binds = db.network_bindings.all()
|
||||||
except Exception as e:
|
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
|
self.db_connected = False
|
||||||
return {VLAN_BINDINGS: {},
|
return {VLAN_BINDINGS: {},
|
||||||
PORT_BINDINGS: []}
|
PORT_BINDINGS: []}
|
||||||
|
|
||||||
vlans_string = ""
|
vlans_string = ""
|
||||||
for bind in vlan_binds:
|
for bind in network_binds:
|
||||||
entry = {'network_id': bind.network_id, 'vlan_id': bind.vlan_id}
|
entry = {'network_id': bind.network_id,
|
||||||
|
'physical_network': bind.physical_network,
|
||||||
|
'vlan_id': bind.vlan_id}
|
||||||
vlan_bindings[bind.network_id] = entry
|
vlan_bindings[bind.network_id] = entry
|
||||||
vlans_string = "%s %s" % (vlans_string, entry)
|
vlans_string = "%s %s" % (vlans_string, entry)
|
||||||
|
|
||||||
@ -462,19 +485,12 @@ class LinuxBridgeQuantumAgentDB:
|
|||||||
all_bindings = {}
|
all_bindings = {}
|
||||||
for bind in port_binds:
|
for bind in port_binds:
|
||||||
append_entry = False
|
append_entry = False
|
||||||
if self.target_v2_api:
|
all_bindings[bind.id] = bind
|
||||||
all_bindings[bind.id] = bind
|
entry = {'network_id': bind.network_id,
|
||||||
entry = {'network_id': bind.network_id,
|
'uuid': bind.id,
|
||||||
'uuid': bind.id,
|
'status': bind.status,
|
||||||
'status': bind.status,
|
'interface_id': bind.id}
|
||||||
'interface_id': bind.id}
|
append_entry = bind.admin_state_up
|
||||||
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'
|
|
||||||
if append_entry:
|
if append_entry:
|
||||||
port_bindings.append(entry)
|
port_bindings.append(entry)
|
||||||
|
|
||||||
@ -484,15 +500,15 @@ class LinuxBridgeQuantumAgentDB:
|
|||||||
ports_string = "%s %s" % (ports_string, pb)
|
ports_string = "%s %s" % (ports_string, pb)
|
||||||
port_id = pb['uuid']
|
port_id = pb['uuid']
|
||||||
interface_id = pb['interface_id']
|
interface_id = pb['interface_id']
|
||||||
|
network_id = pb['network_id']
|
||||||
|
|
||||||
vlan_id = str(vlan_bindings[pb['network_id']]['vlan_id'])
|
physical_network = vlan_bindings[network_id]['physical_network']
|
||||||
if self.process_port_binding(pb['network_id'],
|
vlan_id = str(vlan_bindings[network_id]['vlan_id'])
|
||||||
|
if self.process_port_binding(network_id,
|
||||||
interface_id,
|
interface_id,
|
||||||
|
physical_network,
|
||||||
vlan_id):
|
vlan_id):
|
||||||
if self.target_v2_api:
|
all_bindings[port_id].status = OP_STATUS_UP
|
||||||
all_bindings[port_id].status = OP_STATUS_UP
|
|
||||||
else:
|
|
||||||
all_bindings[port_id].op_status = OP_STATUS_UP
|
|
||||||
|
|
||||||
plugged_interfaces.append(interface_id)
|
plugged_interfaces.append(interface_id)
|
||||||
|
|
||||||
@ -539,15 +555,16 @@ class LinuxBridgeQuantumAgentDB:
|
|||||||
|
|
||||||
class LinuxBridgeQuantumAgentRPC:
|
class LinuxBridgeQuantumAgentRPC:
|
||||||
|
|
||||||
def __init__(self, br_name_prefix, physical_interface, polling_interval,
|
def __init__(self, interface_mappings, polling_interval,
|
||||||
root_helper):
|
root_helper):
|
||||||
self.polling_interval = polling_interval
|
self.polling_interval = polling_interval
|
||||||
self.root_helper = root_helper
|
self.root_helper = root_helper
|
||||||
self.setup_linux_bridge(br_name_prefix, physical_interface)
|
self.setup_linux_bridge(interface_mappings)
|
||||||
self.setup_rpc(physical_interface)
|
self.setup_rpc(interface_mappings.values())
|
||||||
|
|
||||||
def setup_rpc(self, physical_interface):
|
def setup_rpc(self, physical_interfaces):
|
||||||
mac = utils.get_interface_mac(physical_interface)
|
# REVISIT try until one succeeds?
|
||||||
|
mac = utils.get_interface_mac(physical_interfaces[0])
|
||||||
self.agent_id = '%s%s' % ('lb', (mac.replace(":", "")))
|
self.agent_id = '%s%s' % ('lb', (mac.replace(":", "")))
|
||||||
self.topic = topics.AGENT
|
self.topic = topics.AGENT
|
||||||
self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
|
self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
|
||||||
@ -569,12 +586,14 @@ class LinuxBridgeQuantumAgentRPC:
|
|||||||
monitor = pyudev.Monitor.from_netlink(self.udev)
|
monitor = pyudev.Monitor.from_netlink(self.udev)
|
||||||
monitor.filter_by('net')
|
monitor.filter_by('net')
|
||||||
|
|
||||||
def setup_linux_bridge(self, br_name_prefix, physical_interface):
|
def setup_linux_bridge(self, interface_mappings):
|
||||||
self.linux_br = LinuxBridge(br_name_prefix, physical_interface,
|
self.linux_br = LinuxBridge(interface_mappings, self.root_helper)
|
||||||
self.root_helper)
|
|
||||||
|
|
||||||
def process_port_binding(self, network_id, interface_id, vlan_id):
|
def process_port_binding(self, network_id, interface_id,
|
||||||
return self.linux_br.add_interface(network_id, vlan_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):
|
def remove_port_binding(self, network_id, interface_id):
|
||||||
bridge_name = self.linux_br.get_bridge_name(network_id)
|
bridge_name = self.linux_br.get_bridge_name(network_id)
|
||||||
@ -633,6 +652,7 @@ class LinuxBridgeQuantumAgentRPC:
|
|||||||
# create the networking for the port
|
# create the networking for the port
|
||||||
self.process_port_binding(details['network_id'],
|
self.process_port_binding(details['network_id'],
|
||||||
details['port_id'],
|
details['port_id'],
|
||||||
|
details['physical_network'],
|
||||||
details['vlan_id'])
|
details['vlan_id'])
|
||||||
else:
|
else:
|
||||||
self.remove_port_binding(details['network_id'],
|
self.remove_port_binding(details['network_id'],
|
||||||
@ -696,29 +716,32 @@ def main():
|
|||||||
# (TODO) gary - swap with common logging
|
# (TODO) gary - swap with common logging
|
||||||
logging_config.setup_logging(cfg.CONF)
|
logging_config.setup_logging(cfg.CONF)
|
||||||
|
|
||||||
br_name_prefix = BRIDGE_NAME_PREFIX
|
interface_mappings = {}
|
||||||
physical_interface = cfg.CONF.LINUX_BRIDGE.physical_interface
|
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
|
polling_interval = cfg.CONF.AGENT.polling_interval
|
||||||
reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
|
reconnect_interval = cfg.CONF.DATABASE.reconnect_interval
|
||||||
root_helper = cfg.CONF.AGENT.root_helper
|
root_helper = cfg.CONF.AGENT.root_helper
|
||||||
rpc = cfg.CONF.AGENT.rpc
|
rpc = cfg.CONF.AGENT.rpc
|
||||||
if not cfg.CONF.AGENT.target_v2_api:
|
|
||||||
rpc = False
|
|
||||||
|
|
||||||
if rpc:
|
if rpc:
|
||||||
plugin = LinuxBridgeQuantumAgentRPC(br_name_prefix,
|
plugin = LinuxBridgeQuantumAgentRPC(interface_mappings,
|
||||||
physical_interface,
|
|
||||||
polling_interval,
|
polling_interval,
|
||||||
root_helper)
|
root_helper)
|
||||||
else:
|
else:
|
||||||
db_connection_url = cfg.CONF.DATABASE.sql_connection
|
db_connection_url = cfg.CONF.DATABASE.sql_connection
|
||||||
target_v2_api = cfg.CONF.AGENT.target_v2_api
|
plugin = LinuxBridgeQuantumAgentDB(interface_mappings,
|
||||||
plugin = LinuxBridgeQuantumAgentDB(br_name_prefix,
|
|
||||||
physical_interface,
|
|
||||||
polling_interval,
|
polling_interval,
|
||||||
reconnect_interval,
|
reconnect_interval,
|
||||||
root_helper,
|
root_helper,
|
||||||
target_v2_api,
|
|
||||||
db_connection_url)
|
db_connection_url)
|
||||||
LOG.info("Agent initialized successfully, now running... ")
|
LOG.info("Agent initialized successfully, now running... ")
|
||||||
plugin.daemon_loop()
|
plugin.daemon_loop()
|
||||||
|
@ -19,10 +19,15 @@
|
|||||||
|
|
||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
|
|
||||||
|
DEFAULT_VLAN_RANGES = ['default:1000:2999']
|
||||||
|
DEFAULT_INTERFACE_MAPPINGS = ['default:eth1']
|
||||||
|
|
||||||
|
|
||||||
vlan_opts = [
|
vlan_opts = [
|
||||||
cfg.IntOpt('vlan_start', default=1000),
|
cfg.ListOpt('network_vlan_ranges',
|
||||||
cfg.IntOpt('vlan_end', default=3000),
|
default=DEFAULT_VLAN_RANGES,
|
||||||
|
help="List of <physical_network>:<vlan_min>:<vlan_max> "
|
||||||
|
"or <physical_network>"),
|
||||||
]
|
]
|
||||||
|
|
||||||
database_opts = [
|
database_opts = [
|
||||||
@ -32,13 +37,14 @@ database_opts = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
bridge_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 = [
|
agent_opts = [
|
||||||
cfg.IntOpt('polling_interval', default=2),
|
cfg.IntOpt('polling_interval', default=2),
|
||||||
cfg.StrOpt('root_helper', default='sudo'),
|
cfg.StrOpt('root_helper', default='sudo'),
|
||||||
cfg.BoolOpt('target_v2_api', default=False),
|
|
||||||
cfg.BoolOpt('rpc', default=True),
|
cfg.BoolOpt('rpc', default=True),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -17,47 +17,11 @@
|
|||||||
# @author: Sumit Naiksatam, Cisco Systems, Inc.
|
# @author: Sumit Naiksatam, Cisco Systems, Inc.
|
||||||
|
|
||||||
|
|
||||||
PORT_STATE = 'port-state'
|
FLAT_VLAN_ID = -1
|
||||||
|
|
||||||
PORT_UP = "ACTIVE"
|
PORT_UP = "ACTIVE"
|
||||||
PORT_DOWN = "DOWN"
|
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'
|
VLANID = 'vlan_id'
|
||||||
VLANNAME = 'vlan_name'
|
|
||||||
|
|
||||||
ATTACHMENT = 'attachment'
|
|
||||||
PORT_ID = 'port-id'
|
PORT_ID = 'port-id'
|
||||||
PORT_OP_STATUS = 'port-op-status'
|
|
||||||
|
|
||||||
NET_ID = 'net-id'
|
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'
|
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
# 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.")
|
|
@ -1,311 +0,0 @@
|
|||||||
# 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)
|
|
196
quantum/plugins/linuxbridge/db/l2network_db_v2.py
Normal file
196
quantum/plugins/linuxbridge/db/l2network_db_v2.py
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
# 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)
|
@ -14,38 +14,46 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
from sqlalchemy import orm
|
|
||||||
|
|
||||||
from quantum.db import model_base
|
from quantum.db import model_base
|
||||||
|
|
||||||
|
|
||||||
class VlanID(model_base.BASEV2):
|
class NetworkState(model_base.BASEV2):
|
||||||
"""Represents a vlan_id usage"""
|
"""Represents state of vlan_id on physical network"""
|
||||||
__tablename__ = 'vlan_ids'
|
__tablename__ = 'network_states'
|
||||||
|
|
||||||
vlan_id = sa.Column(sa.Integer, nullable=False, primary_key=True)
|
physical_network = sa.Column(sa.String(64), nullable=False,
|
||||||
vlan_used = sa.Column(sa.Boolean, 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_id = vlan_id
|
||||||
self.vlan_used = False
|
self.allocated = False
|
||||||
|
|
||||||
def __repr__(self):
|
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):
|
class NetworkBinding(model_base.BASEV2):
|
||||||
"""Represents a binding of vlan_id to network_id"""
|
"""Represents binding of virtual network to physical_network and vlan_id"""
|
||||||
__tablename__ = 'vlan_bindings'
|
__tablename__ = 'network_bindings'
|
||||||
|
|
||||||
network_id = sa.Column(sa.String(36), sa.ForeignKey('networks.id',
|
network_id = sa.Column(sa.String(36),
|
||||||
ondelete="CASCADE"),
|
sa.ForeignKey('networks.id', ondelete="CASCADE"),
|
||||||
primary_key=True)
|
primary_key=True)
|
||||||
|
physical_network = sa.Column(sa.String(64), nullable=False)
|
||||||
vlan_id = sa.Column(sa.Integer, nullable=False)
|
vlan_id = sa.Column(sa.Integer, nullable=False)
|
||||||
|
|
||||||
def __init__(self, vlan_id, network_id):
|
def __init__(self, network_id, physical_network, vlan_id):
|
||||||
self.vlan_id = vlan_id
|
|
||||||
self.network_id = network_id
|
self.network_id = network_id
|
||||||
|
self.physical_network = physical_network
|
||||||
|
self.vlan_id = vlan_id
|
||||||
|
|
||||||
def __repr__(self):
|
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)
|
||||||
|
@ -14,10 +14,13 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import sys
|
||||||
|
|
||||||
from quantum.api import api_common
|
from quantum.api import api_common
|
||||||
from quantum.api.v2 import attributes
|
from quantum.api.v2 import attributes
|
||||||
|
from quantum.common import exceptions as q_exc
|
||||||
from quantum.common import topics
|
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 db_base_plugin_v2
|
||||||
from quantum.db import models_v2
|
from quantum.db import models_v2
|
||||||
from quantum.openstack.common import context
|
from quantum.openstack.common import context
|
||||||
@ -25,7 +28,8 @@ from quantum.openstack.common import cfg
|
|||||||
from quantum.openstack.common import rpc
|
from quantum.openstack.common import rpc
|
||||||
from quantum.openstack.common.rpc import dispatcher
|
from quantum.openstack.common.rpc import dispatcher
|
||||||
from quantum.openstack.common.rpc import proxy
|
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
|
from quantum import policy
|
||||||
|
|
||||||
|
|
||||||
@ -39,8 +43,8 @@ class LinuxBridgeRpcCallbacks():
|
|||||||
# Device names start with "tap"
|
# Device names start with "tap"
|
||||||
TAP_PREFIX_LEN = 3
|
TAP_PREFIX_LEN = 3
|
||||||
|
|
||||||
def __init__(self, context):
|
def __init__(self, rpc_context):
|
||||||
self.context = context
|
self.rpc_context = rpc_context
|
||||||
|
|
||||||
def create_rpc_dispatcher(self):
|
def create_rpc_dispatcher(self):
|
||||||
'''Get the rpc dispatcher for this manager.
|
'''Get the rpc dispatcher for this manager.
|
||||||
@ -50,38 +54,40 @@ class LinuxBridgeRpcCallbacks():
|
|||||||
'''
|
'''
|
||||||
return dispatcher.RpcDispatcher([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 requests device details"""
|
||||||
agent_id = kwargs.get('agent_id')
|
agent_id = kwargs.get('agent_id')
|
||||||
device = kwargs.get('device')
|
device = kwargs.get('device')
|
||||||
LOG.debug("Device %s details requested from %s", device, agent_id)
|
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:
|
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,
|
entry = {'device': device,
|
||||||
'vlan_id': vlan_binding['vlan_id'],
|
'physical_network': binding.physical_network,
|
||||||
|
'vlan_id': binding.vlan_id,
|
||||||
'network_id': port['network_id'],
|
'network_id': port['network_id'],
|
||||||
'port_id': port['id'],
|
'port_id': port['id'],
|
||||||
'admin_state_up': port['admin_state_up']}
|
'admin_state_up': port['admin_state_up']}
|
||||||
# Set the port status to 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:
|
else:
|
||||||
entry = {'device': device}
|
entry = {'device': device}
|
||||||
LOG.debug("%s can not be found in database", device)
|
LOG.debug("%s can not be found in database", device)
|
||||||
return entry
|
return entry
|
||||||
|
|
||||||
def update_device_down(self, context, **kwargs):
|
def update_device_down(self, rpc_context, **kwargs):
|
||||||
"""Device no longer exists on agent"""
|
"""Device no longer exists on agent"""
|
||||||
# (TODO) garyk - live migration and port status
|
# (TODO) garyk - live migration and port status
|
||||||
agent_id = kwargs.get('agent_id')
|
agent_id = kwargs.get('agent_id')
|
||||||
device = kwargs.get('device')
|
device = kwargs.get('device')
|
||||||
LOG.debug("Device %s no longer exists on %s", device, agent_id)
|
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:
|
if port:
|
||||||
entry = {'device': device,
|
entry = {'device': device,
|
||||||
'exists': True}
|
'exists': True}
|
||||||
# Set port status to DOWN
|
# 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:
|
else:
|
||||||
entry = {'device': device,
|
entry = {'device': device,
|
||||||
'exists': False}
|
'exists': False}
|
||||||
@ -115,10 +121,11 @@ class AgentNotifierApi(proxy.RpcProxy):
|
|||||||
network_id=network_id),
|
network_id=network_id),
|
||||||
topic=self.topic_network_delete)
|
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.fanout_cast(context,
|
||||||
self.make_msg('port_update',
|
self.make_msg('port_update',
|
||||||
port=port,
|
port=port,
|
||||||
|
physical_network=physical_network,
|
||||||
vlan_id=vlan_id),
|
vlan_id=vlan_id),
|
||||||
topic=self.topic_port_update)
|
topic=self.topic_port_update)
|
||||||
|
|
||||||
@ -137,24 +144,29 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
|||||||
be updated to take advantage of it.
|
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"]
|
supported_extension_aliases = ["provider"]
|
||||||
|
|
||||||
def __init__(self):
|
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
|
self.rpc = cfg.CONF.AGENT.rpc
|
||||||
if cfg.CONF.AGENT.rpc and cfg.CONF.AGENT.target_v2_api:
|
if self.rpc:
|
||||||
self.setup_rpc()
|
self._setup_rpc()
|
||||||
if not cfg.CONF.AGENT.target_v2_api:
|
|
||||||
self.rpc = False
|
|
||||||
LOG.debug("Linux Bridge Plugin initialization complete")
|
LOG.debug("Linux Bridge Plugin initialization complete")
|
||||||
|
|
||||||
def setup_rpc(self):
|
def _setup_rpc(self):
|
||||||
# RPC support
|
# RPC support
|
||||||
self.topic = topics.PLUGIN
|
self.topic = topics.PLUGIN
|
||||||
self.context = context.RequestContext('quantum', 'quantum',
|
self.rpc_context = context.RequestContext('quantum', 'quantum',
|
||||||
is_admin=False)
|
is_admin=False)
|
||||||
self.conn = rpc.create_connection(new=True)
|
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.dispatcher = self.callbacks.create_rpc_dispatcher()
|
||||||
self.conn.create_consumer(self.topic, self.dispatcher,
|
self.conn.create_consumer(self.topic, self.dispatcher,
|
||||||
fanout=False)
|
fanout=False)
|
||||||
@ -162,7 +174,32 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
|||||||
self.conn.consume_in_thread()
|
self.conn.consume_in_thread()
|
||||||
self.notifier = AgentNotifierApi(topics.AGENT)
|
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.
|
# when available.
|
||||||
|
|
||||||
def _check_provider_view_auth(self, context, network):
|
def _check_provider_view_auth(self, context, network):
|
||||||
@ -177,40 +214,119 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
|||||||
|
|
||||||
def _extend_network_dict(self, context, network):
|
def _extend_network_dict(self, context, network):
|
||||||
if self._check_provider_view_auth(context, network):
|
if self._check_provider_view_auth(context, network):
|
||||||
vlan_binding = cdb.get_vlan_binding(network['id'])
|
binding = db.get_network_binding(context.session, network['id'])
|
||||||
network['provider:vlan_id'] = vlan_binding['vlan_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):
|
def create_network(self, context, network):
|
||||||
net = super(LinuxBridgePluginV2, self).create_network(context,
|
(network_type, physical_network,
|
||||||
network)
|
vlan_id) = self._process_provider_create(context,
|
||||||
try:
|
network['network'])
|
||||||
vlan_id = network['network'].get('provider:vlan_id')
|
|
||||||
if vlan_id not in (None, attributes.ATTR_NOT_SPECIFIED):
|
session = context.session
|
||||||
self._enforce_provider_set_auth(context, net)
|
with session.begin(subtransactions=True):
|
||||||
cdb.reserve_specific_vlanid(int(vlan_id))
|
if not network_type:
|
||||||
|
physical_network, vlan_id = db.reserve_network(session)
|
||||||
else:
|
else:
|
||||||
vlan_id = cdb.reserve_vlanid()
|
db.reserve_specific_network(session, physical_network, vlan_id)
|
||||||
cdb.add_vlan_binding(vlan_id, net['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)
|
self._extend_network_dict(context, net)
|
||||||
except:
|
# note - exception will rollback entire transaction
|
||||||
super(LinuxBridgePluginV2, self).delete_network(context,
|
|
||||||
net['id'])
|
|
||||||
raise
|
|
||||||
return net
|
return net
|
||||||
|
|
||||||
def update_network(self, context, id, network):
|
def update_network(self, context, id, network):
|
||||||
net = super(LinuxBridgePluginV2, self).update_network(context, id,
|
self._check_provider_update(context, network['network'])
|
||||||
network)
|
|
||||||
self._extend_network_dict(context, net)
|
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
|
return net
|
||||||
|
|
||||||
def delete_network(self, context, id):
|
def delete_network(self, context, id):
|
||||||
vlan_binding = cdb.get_vlan_binding(id)
|
session = context.session
|
||||||
result = super(LinuxBridgePluginV2, self).delete_network(context, id)
|
with session.begin(subtransactions=True):
|
||||||
cdb.release_vlanid(vlan_binding['vlan_id'])
|
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:
|
if self.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, verbose=None):
|
def get_network(self, context, id, fields=None, verbose=None):
|
||||||
net = super(LinuxBridgePluginV2, self).get_network(context, id,
|
net = super(LinuxBridgePluginV2, self).get_network(context, id,
|
||||||
@ -233,7 +349,9 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
|||||||
port = super(LinuxBridgePluginV2, self).update_port(context, id, port)
|
port = super(LinuxBridgePluginV2, self).update_port(context, id, port)
|
||||||
if self.rpc:
|
if self.rpc:
|
||||||
if original_port['admin_state_up'] != port['admin_state_up']:
|
if original_port['admin_state_up'] != port['admin_state_up']:
|
||||||
vlan_binding = cdb.get_vlan_binding(port['network_id'])
|
binding = db.get_network_binding(context.session,
|
||||||
self.notifier.port_update(self.context, port,
|
port['network_id'])
|
||||||
vlan_binding['vlan_id'])
|
self.notifier.port_update(self.rpc_context, port,
|
||||||
|
binding.physical_network,
|
||||||
|
binding.vlan_id)
|
||||||
return port
|
return port
|
||||||
|
42
quantum/plugins/linuxbridge/tests/unit/test_defaults.py
Normal file
42
quantum/plugins/linuxbridge/tests/unit/test_defaults.py
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
# 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])
|
125
quantum/plugins/linuxbridge/tests/unit/test_lb_db.py
Normal file
125
quantum/plugins/linuxbridge/tests/unit/test_lb_db.py
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
# 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)
|
@ -74,7 +74,9 @@ class rpcApiTestCase(unittest2.TestCase):
|
|||||||
topics.PORT,
|
topics.PORT,
|
||||||
topics.UPDATE),
|
topics.UPDATE),
|
||||||
'port_update', rpc_method='fanout_cast',
|
'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):
|
def test_device_details(self):
|
||||||
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
|
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
|
||||||
|
@ -140,7 +140,9 @@ def reserve_specific_vlan_id(vlan_id, session):
|
|||||||
filter_by(vlan_id=vlan_id).
|
filter_by(vlan_id=vlan_id).
|
||||||
one())
|
one())
|
||||||
if record.vlan_used:
|
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)
|
LOG.debug("reserving specific vlan %s from pool" % vlan_id)
|
||||||
record.vlan_used = True
|
record.vlan_used = True
|
||||||
except exc.NoResultFound:
|
except exc.NoResultFound:
|
||||||
|
@ -234,15 +234,86 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
|||||||
network['provider:vlan_id'] = ovs_db_v2.get_vlan(
|
network['provider:vlan_id'] = ovs_db_v2.get_vlan(
|
||||||
network['id'], context.session)
|
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):
|
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)
|
net = super(OVSQuantumPluginV2, self).create_network(context, network)
|
||||||
try:
|
try:
|
||||||
vlan_id = network['network'].get('provider:vlan_id')
|
if not network_type:
|
||||||
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:
|
|
||||||
vlan_id = ovs_db_v2.reserve_vlan_id(context.session)
|
vlan_id = ovs_db_v2.reserve_vlan_id(context.session)
|
||||||
|
else:
|
||||||
|
ovs_db_v2.reserve_specific_vlan_id(vlan_id, context.session)
|
||||||
except Exception:
|
except Exception:
|
||||||
super(OVSQuantumPluginV2, self).delete_network(context, net['id'])
|
super(OVSQuantumPluginV2, self).delete_network(context, net['id'])
|
||||||
raise
|
raise
|
||||||
@ -253,6 +324,8 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2):
|
|||||||
return net
|
return net
|
||||||
|
|
||||||
def update_network(self, context, id, network):
|
def update_network(self, context, id, network):
|
||||||
|
self._check_provider_update(context, network['network'])
|
||||||
|
|
||||||
net = super(OVSQuantumPluginV2, self).update_network(context, id,
|
net = super(OVSQuantumPluginV2, self).update_network(context, id,
|
||||||
network)
|
network)
|
||||||
self._extend_network_dict(context, net)
|
self._extend_network_dict(context, net)
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from quantum.openstack.common import cfg
|
from quantum.openstack.common import cfg
|
||||||
|
from quantum.plugins.openvswitch.common import config
|
||||||
|
|
||||||
|
|
||||||
class ConfigurationTest(unittest.TestCase):
|
class ConfigurationTest(unittest.TestCase):
|
||||||
|
@ -16,3 +16,6 @@ api_extensions_path = unit/extensions
|
|||||||
|
|
||||||
# Paste configuration file
|
# Paste configuration file
|
||||||
api_paste_config = api-paste.ini.test
|
api_paste_config = api-paste.ini.test
|
||||||
|
|
||||||
|
# The messaging module to use, defaults to kombu.
|
||||||
|
rpc_backend = quantum.openstack.common.rpc.impl_fake
|
||||||
|
Loading…
x
Reference in New Issue
Block a user