include etc/init.d/*
include etc/quantum/plugins/openvswitch/*
include etc/quantum/plugins/cisco/*
+include etc/quantum/plugins/linuxbridge/*
--- /dev/null
+[VLANS]
+vlan_start=1000
+vlan_end=3000
+
+[DATABASE]
+# Use the following when running the tests for the in-memory DB
+connection = sqlite
+# Uncomment the following for using the MySQL DB when actually running the plugin,
+# also remove the earlier sqlite connection configuration
+#connection = mysql
+name = quantum_linux_bridge
+user = <mysql_user_name_here>
+pass = <mysql_password_here>
+host = <hostname_or_IP_address_of_Quantum_server>
+# If you use a non-default port for the DB, change the following
+port = 3306
+
+[LINUX_BRIDGE]
+#this is the interface connected to the switch on your Quantum network
+physical_interface = eth1
+
+[AGENT]
+#agent's polling interval in seconds
+polling_interval = 2
--- /dev/null
+"""
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+"""
+
+import logging
+
+from quantum.api.api_common import OperationalStatus
+from quantum.common import exceptions as exc
+from quantum.db import api as db
+from quantum.plugins.linuxbridge import plugin_configuration as conf
+from quantum.plugins.linuxbridge.common import constants as const
+from quantum.plugins.linuxbridge.common import utils as cutil
+from quantum.plugins.linuxbridge.db import l2network_db as cdb
+from quantum.quantum_plugin_base import QuantumPluginBase
+
+
+LOG = logging.getLogger(__name__)
+
+
+class LinuxBridgePlugin(QuantumPluginBase):
+ """
+ LinuxBridgePlugin provides support for Quantum abstractions
+ using LinuxBridge. A new VLAN is created for each network.
+ It relies on an agent to perform the actual bridge configuration
+ on each host.
+ """
+
+ def __init__(self, configfile=None):
+ cdb.initialize()
+ LOG.debug("Linux Bridge Plugin initialization done successfully")
+
+ def _get_vlan_for_tenant(self, tenant_id, **kwargs):
+ """Get an available VLAN ID"""
+ try:
+ return cdb.reserve_vlanid()
+ except:
+ raise Exception("Failed to reserve VLAN ID for network")
+
+ def _release_vlan_for_tenant(self, tenant_id, net_id, **kwargs):
+ """Release the ID"""
+ vlan_binding = cdb.get_vlan_binding(net_id)
+ return cdb.release_vlanid(vlan_binding[const.VLANID])
+
+ def _validate_port_state(self, port_state):
+ if port_state.upper() not in ('ACTIVE', 'DOWN'):
+ raise exc.StateInvalid(port_state=port_state)
+ return True
+
+ def get_all_networks(self, tenant_id, **kwargs):
+ """
+ Returns a dictionary containing all
+ <network_uuid, network_name> for
+ the specified tenant.
+ """
+ LOG.debug("LinuxBridgePlugin.get_all_networks() called")
+ networks_list = db.network_list(tenant_id)
+ new_networks_list = []
+ for network in networks_list:
+ new_network_dict = cutil.make_net_dict(network[const.UUID],
+ network[const.NETWORKNAME],
+ [], network[const.OPSTATUS])
+ new_networks_list.append(new_network_dict)
+
+ # This plugin does not perform filtering at the moment
+ return new_networks_list
+
+ def get_network_details(self, tenant_id, net_id):
+ """
+ retrieved a list of all the remote vifs that
+ are attached to the network
+ """
+ LOG.debug("LinuxBridgePlugin.get_network_details() called")
+ network = db.network_get(net_id)
+ ports_list = db.port_list(net_id)
+ ports_on_net = []
+ for port in ports_list:
+ new_port = cutil.make_port_dict(port)
+ ports_on_net.append(new_port)
+
+ new_network = cutil.make_net_dict(network[const.UUID],
+ network[const.NETWORKNAME],
+ ports_on_net,
+ network[const.OPSTATUS])
+
+ return new_network
+
+ def create_network(self, tenant_id, net_name, **kwargs):
+ """
+ Creates a new Virtual Network, and assigns it
+ a symbolic name.
+ """
+ LOG.debug("LinuxBridgePlugin.create_network() called")
+ new_network = db.network_create(tenant_id, net_name,
+ op_status=OperationalStatus.UP)
+ new_net_id = new_network[const.UUID]
+ vlan_id = self._get_vlan_for_tenant(tenant_id)
+ cdb.add_vlan_binding(vlan_id, new_net_id)
+ new_net_dict = {const.NET_ID: new_net_id,
+ const.NET_NAME: net_name,
+ const.NET_PORTS: [],
+ const.NET_OP_STATUS: new_network[const.OPSTATUS]}
+ return new_net_dict
+
+ def delete_network(self, tenant_id, net_id):
+ """
+ Deletes the network with the specified network identifier
+ belonging to the specified tenant.
+ """
+ LOG.debug("LinuxBridgePlugin.delete_network() called")
+ net = db.network_get(net_id)
+ if net:
+ ports_on_net = db.port_list(net_id)
+ if len(ports_on_net) > 0:
+ for port in ports_on_net:
+ if port[const.INTERFACEID]:
+ raise exc.NetworkInUse(net_id=net_id)
+ for port in ports_on_net:
+ self.delete_port(tenant_id, net_id, port[const.UUID])
+
+ net_dict = cutil.make_net_dict(net[const.UUID],
+ net[const.NETWORKNAME],
+ [], net[const.OPSTATUS])
+ try:
+ self._release_vlan_for_tenant(tenant_id, net_id)
+ cdb.remove_vlan_binding(net_id)
+ except Exception as excp:
+ LOG.warning("Exception: %s" % excp)
+ db.network_update(net_id, tenant_id, {const.OPSTATUS:
+ OperationalStatus.DOWN})
+ db.network_destroy(net_id)
+ return net_dict
+ # Network not found
+ raise exc.NetworkNotFound(net_id=net_id)
+
+ def update_network(self, tenant_id, net_id, **kwargs):
+ """
+ Updates the attributes of a particular Virtual Network.
+ """
+ LOG.debug("LinuxBridgePlugin.update_network() called")
+ network = db.network_update(net_id, tenant_id, **kwargs)
+ net_dict = cutil.make_net_dict(network[const.UUID],
+ network[const.NETWORKNAME],
+ [], network[const.OPSTATUS])
+ return net_dict
+
+ def get_all_ports(self, tenant_id, net_id, **kwargs):
+ """
+ Retrieves all port identifiers belonging to the
+ specified Virtual Network.
+ """
+ LOG.debug("LinuxBridgePlugin.get_all_ports() called")
+ network = db.network_get(net_id)
+ ports_list = db.port_list(net_id)
+ ports_on_net = []
+ for port in ports_list:
+ new_port = cutil.make_port_dict(port)
+ ports_on_net.append(new_port)
+
+ # This plugin does not perform filtering at the moment
+ return ports_on_net
+
+ def get_port_details(self, tenant_id, net_id, port_id):
+ """
+ This method allows the user to retrieve a remote interface
+ that is attached to this particular port.
+ """
+ LOG.debug("LinuxBridgePlugin.get_port_details() called")
+ network = db.network_get(net_id)
+ port = db.port_get(port_id, net_id)
+ new_port_dict = cutil.make_port_dict(port)
+ return new_port_dict
+
+ def create_port(self, tenant_id, net_id, port_state=None, **kwargs):
+ """
+ Creates a port on the specified Virtual Network.
+ """
+ LOG.debug("LinuxBridgePlugin.create_port() called")
+ port = db.port_create(net_id, port_state,
+ op_status=OperationalStatus.DOWN)
+ unique_port_id_string = port[const.UUID]
+ new_port_dict = cutil.make_port_dict(port)
+ return new_port_dict
+
+ def update_port(self, tenant_id, net_id, port_id, **kwargs):
+ """
+ Updates the attributes of a port on the specified Virtual Network.
+ """
+ LOG.debug("LinuxBridgePlugin.update_port() called")
+ network = db.network_get(net_id)
+ self._validate_port_state(kwargs["state"])
+ port = db.port_update(port_id, net_id, **kwargs)
+
+ new_port_dict = cutil.make_port_dict(port)
+ return new_port_dict
+
+ def delete_port(self, tenant_id, net_id, port_id):
+ """
+ Deletes a port on a specified Virtual Network,
+ if the port contains a remote interface attachment,
+ the remote interface is first un-plugged and then the port
+ is deleted.
+ """
+ LOG.debug("LinuxBridgePlugin.delete_port() called")
+ network = db.network_get(net_id)
+ port = db.port_get(port_id, net_id)
+ attachment_id = port[const.INTERFACEID]
+ if not attachment_id:
+ db.port_destroy(port_id, net_id)
+ new_port_dict = cutil.make_port_dict(port)
+ return new_port_dict
+ else:
+ raise exc.PortInUse(port_id=port_id, net_id=net_id,
+ att_id=attachment_id)
+
+ def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
+ """
+ Attaches a remote interface to the specified port on the
+ specified Virtual Network.
+ """
+ LOG.debug("LinuxBridgePlugin.plug_interface() called")
+ network = db.network_get(net_id)
+ port = db.port_get(port_id, net_id)
+ attachment_id = port[const.INTERFACEID]
+ if attachment_id:
+ raise exc.PortInUse(port_id=port_id, net_id=net_id,
+ att_id=attachment_id)
+ db.port_set_attachment(port_id, net_id, remote_interface_id)
+
+ def unplug_interface(self, tenant_id, net_id, port_id):
+ """
+ Detaches a remote interface from the specified port on the
+ specified Virtual Network.
+ """
+ LOG.debug("LinuxBridgePlugin.unplug_interface() called")
+ network = db.network_get(net_id)
+ port = db.port_get(port_id, net_id)
+ attachment_id = port[const.INTERFACEID]
+ if attachment_id == None:
+ raise exc.InvalidDetach(port_id=port_id, net_id=net_id,
+ att_id=remote_interface_id)
+ db.port_unset_attachment(port_id, net_id)
+ db.port_update(port_id, net_id, op_status=OperationalStatus.DOWN)
--- /dev/null
+# -- Background
+
+The Quantum Linux Bridge plugin is a plugin that allows you to manage
+connectivity between VMs on hosts that are capable of running a Linux Bridge.
+
+The Quantum Linux Bridge plugin consists of three components:
+
+1) The plugin itself: The plugin uses a database backend (mysql for
+ now) to store configuration and mappings that are used by the
+ agent. The mysql server runs on a central server (often the same
+ host as nova itself).
+
+2) The quantum service host which will be running quantum. This can
+ be run on the server running nova.
+
+3) An agent which runs on the host and communicates with the host operating
+ system. The agent gathers the configuration and mappings from
+ the mysql database running on the quantum host.
+
+The sections below describe how to configure and run the quantum
+service with the Linux Bridge plugin.
+
+# -- Python library dependencies
+
+ Make sure you have the following package(s) installedi on quantum server
+ host as well as any hosts which run the agent:
+ python-configobj
+ bridge-utils
+ python-mysqldb
+ sqlite3
+
+# -- Nova configuration (controller node)
+
+1) Make sure to set up nova using the quantum network manager in the
+ nova.conf on the node that will be running nova-network.
+
+--network_manager=nova.network.quantum.manager.QuantumManager
+
+# -- Nova configuration (compute node(s))
+
+1) Configure the vif driver, and libvirt/vif type
+
+--connection_type=libvirt
+--libvirt_type=qemu
+--libvirt_vif_type=ethernet
+--libvirt_vif_driver=nova.virt.libvirt.vif_linuxbridge_quantum.QuantumLibvirtLinuxBridgeDriver
+--linuxnet_interface_driver=nova.network.quantum.linux_net_linux_bridge.QuantumLibvirtLinuxBridgeDriver
+
+The above two drivers are packaged with Quantum in the location:
+quantum/plugins/linuxbridge/nova
+
+These need to copied to the compute node in the appropriate locations.
+
+a) Copy:
+ quantum/plugins/linuxbridge/nova/vif_linuxbridge_quantum.py
+ to:
+ nova/virt/libvirt/vif_linuxbridge_quantum.py
+
+b) Copy:
+ quantum/plugins/linuxbridge/nova/linux_net_linux_bridge.py
+ to:
+ nova/network/quantum/linux_net_linux_bridge.py
+
+2) If you want a DHCP server to be run for the VMs to acquire IPs,
+ add the following flag to your nova.conf file:
+
+--quantum_use_dhcp
+
+(Note: For more details on how to work with Quantum using Nova, i.e. how to create networks and such,
+ please refer to the top level Quantum README which points to the relevant documentation.)
+
+# -- Quantum configuration
+
+Make the Linux Bridge plugin the current quantum plugin
+
+- edit etc/plugins.ini and change the provider line to be:
+provider = quantum.plugins.linuxbridge.LinuxBridgePlugin.LinuxBridgePlugin
+
+# -- Database config.
+
+(Note: The plugin ships with a default SQLite in-memory database configuration,
+ and can be used to run tests without performing the suggested DB config below.)
+
+The Linux Bridge quantum plugin requires access to a mysql database in order
+to store configuration and mappings that will be used by the agent. Here is
+how to set up the database on the host that you will be running the quantum
+service on.
+
+MySQL should be installed on the host, and all plugins and clients
+must be configured with access to the database.
+
+To prep mysql, run:
+
+$ mysql -u root -p -e "create database quantum_linux_bridge"
+
+# log in to mysql service
+$ mysql -u root -p
+# The Linux Bridge Quantum agent running on each compute node must be able to
+# make a mysql connection back to the main database server.
+mysql> GRANT USAGE ON *.* to root@'yourremotehost' IDENTIFIED BY 'newpassword';
+# force update of authorization changes
+mysql> FLUSH PRIVILEGES;
+
+(Note: If the remote connection fails to MySQL, you might need to add the IP address,
+ and/or fully-qualified hostname, and/or unqualified hostname in the above GRANT sql
+ command. Also, you might need to specify "ALL" instead of "USAGE".)
+
+# -- Plugin configuration
+
+- Edit the configuration file:
+ etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini
+ Make sure it matches your mysql configuration. This file must be updated
+ with the addresses and credentials to access the database.
+
+ Note: When running the tests, set the connection type to sqlite, and when
+ actually running the server set it to mysql. At any given time, only one
+ of these should be active in the conf file (you can comment out the other).
+
+- Remember to change the interface configuration to indicate the correct
+ ethernet interface on that particular host which is being used to participate
+ in the Quantum networks. This configuration has to be applied on each host
+ on which the agent runs.
+
+# -- Agent configuration
+
+- Edit the configuration file:
+ etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini
+
+- Copy quantum/plugins/linuxbridge/agent/linuxbridge_quantum_agent.py
+ and etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini
+ to the compute node.
+
+$ Run the following:
+ sudo python linuxbridge_quantum_agent.py linuxbridge_conf.ini
+ (Use --verbose option to see the logs)
+
+# -- Running Tests
+
+(Note: The plugin ships with a default SQLite in-memory database configuration,
+ and can be used to run tests out of the box. Alternatively you can perform the
+ DB configuration for a persistent database as mentioned in the Database
+ Configuration section.)
+
+- To run tests related to the Plugin and the VLAN management (run the
+ following from the top level Quantum directory):
+ PLUGIN_DIR=quantum/plugins/linuxbridge ./run_tests.sh -N
+
+- The above will not however run the tests for the agent (which deals
+ with creating the bridge and interfaces). To run the agent tests, run the
+ following from the top level Quantum directory:
+ sudo PLUGIN_DIR=quantum/plugins/linuxbridge ./run_tests.sh -N tests.unit._test_linuxbridgeAgent
+
+ (Note: To run the agent tests you should have the environment setup as
+ indicated in the Agent Configuration, and also have the necessary dependencies
+ insalled.)
--- /dev/null
+#!/usr/bin/env python
+# 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.
+#
+#
+# Performs per host Linux Bridge configuration for Quantum.
+# Based on the structure of the OpenVSwitch agent in the
+# Quantum OpenVSwitch Plugin.
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+#
+
+from optparse import OptionParser
+from subprocess import *
+
+import ConfigParser
+import logging as LOG
+import MySQLdb
+import os
+import signal
+import sqlite3
+import sys
+import time
+
+
+BRIDGE_NAME_PREFIX = "brq"
+GATEWAY_INTERFACE_PREFIX = "gw-"
+TAP_INTERFACE_PREFIX = "tap"
+BRIDGE_FS = "/sys/devices/virtual/net/"
+BRIDGE_NAME_PLACEHOLDER = "bridge_name"
+BRIDGE_INTERFACES_FS = BRIDGE_FS + BRIDGE_NAME_PLACEHOLDER + "/brif/"
+PORT_OPSTATUS_UPDATESQL = "UPDATE ports SET op_status = '%s' WHERE uuid = '%s'"
+DEVICE_NAME_PLACEHOLDER = "device_name"
+BRIDGE_PORT_FS_FOR_DEVICE = BRIDGE_FS + DEVICE_NAME_PLACEHOLDER + "/brport"
+VLAN_BINDINGS = "vlan_bindings"
+PORT_BINDINGS = "port_bindings"
+OP_STATUS_UP = "UP"
+OP_STATUS_DOWN = "DOWN"
+DB_CONNECTION = None
+
+
+class LinuxBridge:
+ def __init__(self, br_name_prefix, physical_interface):
+ self.br_name_prefix = br_name_prefix
+ self.physical_interface = physical_interface
+
+ def run_cmd(self, args):
+ LOG.debug("Running command: " + " ".join(args))
+ p = Popen(args, stdout=PIPE)
+ retval = p.communicate()[0]
+ if p.returncode == -(signal.SIGALRM):
+ LOG.debug("Timeout running command: " + " ".join(args))
+ if retval:
+ LOG.debug("Command returned: %s" % retval)
+ return retval
+
+ def device_exists(self, device):
+ """Check if ethernet device exists."""
+ retval = self.run_cmd(['ip', 'link', 'show', 'dev', device])
+ if retval:
+ return True
+ else:
+ return False
+
+ def get_bridge_name(self, network_id):
+ if not network_id:
+ LOG.warning("Invalid Network ID, will lead to incorrect bridge" \
+ "name")
+ bridge_name = self.br_name_prefix + network_id[0:11]
+ return bridge_name
+
+ def get_subinterface_name(self, vlan_id):
+ if not vlan_id:
+ LOG.warning("Invalid VLAN ID, will lead to incorrect " \
+ "subinterface name")
+ subinterface_name = '%s.%s' % (self.physical_interface, vlan_id)
+ return subinterface_name
+
+ def get_tap_device_name(self, interface_id):
+ if not interface_id:
+ LOG.warning("Invalid Interface ID, will lead to incorrect " \
+ "tap device name")
+ tap_device_name = TAP_INTERFACE_PREFIX + interface_id[0:11]
+ return tap_device_name
+
+ def get_all_quantum_bridges(self):
+ quantum_bridge_list = []
+ bridge_list = os.listdir(BRIDGE_FS)
+ for bridge in bridge_list:
+ if bridge.startswith(BRIDGE_NAME_PREFIX):
+ quantum_bridge_list.append(bridge)
+ return quantum_bridge_list
+
+ def get_interfaces_on_bridge(self, bridge_name):
+ if self.device_exists(bridge_name):
+ bridge_interface_path = \
+ BRIDGE_INTERFACES_FS.replace(BRIDGE_NAME_PLACEHOLDER,
+ bridge_name)
+ return os.listdir(bridge_interface_path)
+
+ def get_all_tap_devices(self):
+ tap_devices = []
+ retval = self.run_cmd(['ip', 'tuntap'])
+ rows = retval.split('\n')
+ for row in rows:
+ split_row = row.split(':')
+ if split_row[0].startswith(TAP_INTERFACE_PREFIX):
+ tap_devices.append(split_row[0])
+
+ return tap_devices
+
+ def get_all_gateway_devices(self):
+ gw_devices = []
+ retval = self.run_cmd(['ip', 'tuntap'])
+ rows = retval.split('\n')
+ for row in rows:
+ split_row = row.split(':')
+ if split_row[0].startswith(GATEWAY_INTERFACE_PREFIX):
+ gw_devices.append(split_row[0])
+
+ return gw_devices
+
+ def get_bridge_for_tap_device(self, tap_device_name):
+ bridges = self.get_all_quantum_bridges()
+ for bridge in bridges:
+ interfaces = self.get_interfaces_on_bridge(bridge)
+ if tap_device_name in interfaces:
+ return bridge
+
+ return None
+
+ def is_device_on_bridge(self, device_name):
+ if not device_name:
+ return False
+ else:
+ bridge_port_path = \
+ BRIDGE_PORT_FS_FOR_DEVICE.replace(DEVICE_NAME_PLACEHOLDER,
+ device_name)
+ return os.path.exists(bridge_port_path)
+
+ def ensure_vlan_bridge(self, network_id, vlan_id):
+ """Create a vlan and bridge unless they already exist."""
+ interface = self.ensure_vlan(vlan_id)
+ bridge_name = self.get_bridge_name(network_id)
+ self.ensure_bridge(bridge_name, interface)
+ return interface
+
+ def ensure_vlan(self, vlan_id):
+ """Create a vlan unless it already exists."""
+ interface = self.get_subinterface_name(vlan_id)
+ if not self.device_exists(interface):
+ LOG.debug("Creating subinterface %s for VLAN %s on interface %s" %
+ (interface, vlan_id, self.physical_interface))
+ if self.run_cmd(['ip', 'link', 'add', 'link',
+ self.physical_interface,
+ 'name', interface, 'type', 'vlan', 'id',
+ vlan_id]):
+ return
+ if self.run_cmd(['ip', 'link', 'set', interface, 'up']):
+ return
+ LOG.debug("Done creating subinterface %s" % interface)
+ return interface
+
+ def ensure_bridge(self, bridge_name, interface):
+ """
+ Create a bridge unless it already exists.
+ """
+ if not self.device_exists(bridge_name):
+ LOG.debug("Starting bridge %s for subinterface %s" % (bridge_name,
+ interface))
+ if self.run_cmd(['brctl', 'addbr', bridge_name]):
+ return
+ if self.run_cmd(['brctl', 'setfd', bridge_name, str(0)]):
+ return
+ if self.run_cmd(['brctl', 'stp', bridge_name, 'off']):
+ return
+ if self.run_cmd(['ip', 'link', 'set', bridge_name, 'up']):
+ return
+ LOG.debug("Done starting bridge %s for subinterface %s" %
+ (bridge_name, interface))
+
+ self.run_cmd(['brctl', 'addif', bridge_name, interface])
+
+ def add_tap_interface(self, network_id, vlan_id, tap_device_name):
+ """
+ If a VIF has been plugged into a network, this function will
+ add the corresponding tap device to the relevant bridge
+ """
+ if not tap_device_name:
+ return False
+
+ if not self.device_exists(tap_device_name):
+ LOG.debug("Tap device: %s does not exist on this host, skipped" %
+ tap_device_name)
+ return False
+
+ current_bridge_name = \
+ self.get_bridge_for_tap_device(tap_device_name)
+ bridge_name = self.get_bridge_name(network_id)
+ if bridge_name == current_bridge_name:
+ return False
+ LOG.debug("Adding device %s to bridge %s" % (tap_device_name,
+ bridge_name))
+ if current_bridge_name:
+ if self.run_cmd(['brctl', 'delif', current_bridge_name,
+ tap_device_name]):
+ return False
+
+ self.ensure_vlan_bridge(network_id, vlan_id)
+ if self.run_cmd(['brctl', 'addif', bridge_name, tap_device_name]):
+ return False
+ LOG.debug("Done adding device %s to bridge %s" % (tap_device_name,
+ bridge_name))
+ return True
+
+ def add_interface(self, network_id, vlan_id, interface_id):
+ if not interface_id:
+ """
+ Since the VIF id is null, no VIF is plugged into this port
+ no more processing is required
+ """
+ return False
+ if interface_id.startswith(GATEWAY_INTERFACE_PREFIX):
+ return self.add_tap_interface(network_id, vlan_id,
+ interface_id)
+ else:
+ tap_device_name = self.get_tap_device_name(interface_id)
+ return self.add_tap_interface(network_id, vlan_id,
+ tap_device_name)
+
+ def delete_vlan_bridge(self, bridge_name):
+ if self.device_exists(bridge_name):
+ interfaces_on_bridge = self.get_interfaces_on_bridge(bridge_name)
+ for interface in interfaces_on_bridge:
+ self.remove_interface(bridge_name, interface)
+ if interface.startswith(self.physical_interface):
+ self.delete_vlan(interface)
+
+ LOG.debug("Deleting bridge %s" % bridge_name)
+ if self.run_cmd(['ip', 'link', 'set', bridge_name, 'down']):
+ return
+ if self.run_cmd(['brctl', 'delbr', bridge_name]):
+ return
+ LOG.debug("Done deleting bridge %s" % bridge_name)
+
+ else:
+ LOG.error("Cannot delete bridge %s, does not exist" % bridge_name)
+
+ def remove_interface(self, bridge_name, interface_name):
+ if self.device_exists(bridge_name):
+ if not self.is_device_on_bridge(interface_name):
+ return True
+ LOG.debug("Removing device %s from bridge %s" % \
+ (interface_name, bridge_name))
+ if self.run_cmd(['brctl', 'delif', bridge_name, interface_name]):
+ return False
+ LOG.debug("Done removing device %s from bridge %s" % \
+ (interface_name, bridge_name))
+ return True
+ else:
+ LOG.debug("Cannot remove device %s, bridge %s does not exist" % \
+ (interface_name, bridge_name))
+ return False
+
+ def delete_vlan(self, interface):
+ if self.device_exists(interface):
+ LOG.debug("Deleting subinterface %s for vlan" % interface)
+ if self.run_cmd(['ip', 'link', 'set', interface, 'down']):
+ return
+ if self.run_cmd(['ip', 'link', 'delete', interface]):
+ return
+ LOG.debug("Done deleting subinterface %s" % interface)
+
+
+class LinuxBridgeQuantumAgent:
+
+ def __init__(self, br_name_prefix, physical_interface, polling_interval):
+ self.polling_interval = int(polling_interval)
+ self.setup_linux_bridge(br_name_prefix, physical_interface)
+
+ def setup_linux_bridge(self, br_name_prefix, physical_interface):
+ self.linux_br = LinuxBridge(br_name_prefix, physical_interface)
+
+ def process_port_binding(self, port_id, network_id, interface_id,
+ vlan_id):
+ return self.linux_br.add_interface(network_id, vlan_id, interface_id)
+
+ def process_unplugged_interfaces(self, plugged_interfaces):
+ """
+ If there are any tap devices that are not corresponding to the
+ list of attached VIFs, then those are corresponding to recently
+ unplugged VIFs, so we need to remove those tap devices from their
+ current bridge association
+ """
+ plugged_tap_device_names = []
+ plugged_gateway_device_names = []
+ for interface in plugged_interfaces:
+ if interface.startswith(GATEWAY_INTERFACE_PREFIX):
+ """
+ The name for the gateway devices is set by the linux net
+ driver, hence we use the name as is
+ """
+ plugged_gateway_device_names.append(interface)
+ else:
+ tap_device_name = self.linux_br.get_tap_device_name(interface)
+ plugged_tap_device_names.append(tap_device_name)
+
+ LOG.debug("plugged tap device names %s" % plugged_tap_device_names)
+ for tap_device in self.linux_br.get_all_tap_devices():
+ if tap_device not in plugged_tap_device_names:
+ current_bridge_name = \
+ self.linux_br.get_bridge_for_tap_device(tap_device)
+ if current_bridge_name:
+ self.linux_br.remove_interface(current_bridge_name,
+ tap_device)
+
+ for gw_device in self.linux_br.get_all_gateway_devices():
+ if gw_device not in plugged_gateway_device_names:
+ current_bridge_name = \
+ self.linux_br.get_bridge_for_tap_device(gw_device)
+ if current_bridge_name:
+ self.linux_br.remove_interface(current_bridge_name,
+ gw_device)
+
+ def process_deleted_networks(self, vlan_bindings):
+ current_quantum_networks = vlan_bindings.keys()
+ current_quantum_bridge_names = []
+ for network in current_quantum_networks:
+ bridge_name = self.linux_br.get_bridge_name(network)
+ current_quantum_bridge_names.append(bridge_name)
+
+ quantum_bridges_on_this_host = self.linux_br.get_all_quantum_bridges()
+ for bridge in quantum_bridges_on_this_host:
+ if bridge not in current_quantum_bridge_names:
+ self.linux_br.delete_vlan_bridge(bridge)
+
+ def manage_networks_on_host(self, conn, old_vlan_bindings,
+ old_port_bindings):
+ if DB_CONNECTION != 'sqlite':
+ cursor = MySQLdb.cursors.DictCursor(conn)
+ else:
+ cursor = conn.cursor()
+ cursor.execute("SELECT * FROM vlan_bindings")
+ rows = cursor.fetchall()
+ cursor.close()
+ vlan_bindings = {}
+ vlans_string = ""
+ for row in rows:
+ vlan_bindings[row['network_id']] = row
+ vlans_string = "%s %s" % (vlans_string, row)
+
+ plugged_interfaces = []
+ cursor = MySQLdb.cursors.DictCursor(conn)
+ cursor.execute("SELECT * FROM ports where state = 'ACTIVE'")
+ port_bindings = cursor.fetchall()
+ cursor.close()
+
+ ports_string = ""
+ for pb in port_bindings:
+ ports_string = "%s %s" % (ports_string, pb)
+ if pb['interface_id']:
+ vlan_id = \
+ str(vlan_bindings[pb['network_id']]['vlan_id'])
+ if self.process_port_binding(pb['uuid'],
+ pb['network_id'],
+ pb['interface_id'],
+ vlan_id):
+ cursor = MySQLdb.cursors.DictCursor(conn)
+ sql = PORT_OPSTATUS_UPDATESQL % (pb['uuid'],
+ OP_STATUS_UP)
+ cursor.execute(sql)
+ cursor.close()
+ plugged_interfaces.append(pb['interface_id'])
+
+ if old_port_bindings != port_bindings:
+ LOG.debug("Port-bindings: %s" % ports_string)
+
+ self.process_unplugged_interfaces(plugged_interfaces)
+
+ if old_vlan_bindings != vlan_bindings:
+ LOG.debug("VLAN-bindings: %s" % vlans_string)
+
+ self.process_deleted_networks(vlan_bindings)
+
+ conn.commit()
+ return {VLAN_BINDINGS: vlan_bindings,
+ PORT_BINDINGS: port_bindings}
+
+ def daemon_loop(self, conn):
+ old_vlan_bindings = {}
+ old_port_bindings = {}
+
+ while True:
+ bindings = self.manage_networks_on_host(conn,
+ old_vlan_bindings,
+ old_port_bindings)
+ old_vlan_bindings = bindings[VLAN_BINDINGS]
+ old_port_bindings = bindings[PORT_BINDINGS]
+ time.sleep(self.polling_interval)
+
+if __name__ == "__main__":
+ usagestr = "%prog [OPTIONS] <config file>"
+ parser = OptionParser(usage=usagestr)
+ parser.add_option("-v", "--verbose", dest="verbose",
+ action="store_true", default=False, help="turn on verbose logging")
+
+ options, args = parser.parse_args()
+
+ if options.verbose:
+ LOG.basicConfig(level=LOG.DEBUG)
+ else:
+ LOG.basicConfig(level=LOG.WARN)
+
+ if len(args) != 1:
+ parser.print_help()
+ sys.exit(1)
+
+ config_file = args[0]
+ config = ConfigParser.ConfigParser()
+ conn = None
+ try:
+ fh = open(config_file)
+ fh.close()
+ config.read(config_file)
+ br_name_prefix = BRIDGE_NAME_PREFIX
+ physical_interface = config.get("LINUX_BRIDGE", "physical_interface")
+ polling_interval = config.get("AGENT", "polling_interval")
+ 'Establish database connection and load models'
+ DB_CONNECTION = config.get("DATABASE", "connection")
+ if DB_CONNECTION == 'sqlite':
+ LOG.info("Connecting to sqlite DB")
+ conn = sqlite3.connect(":memory:")
+ conn.row_factory = sqlite3.Row
+ else:
+ db_name = config.get("DATABASE", "name")
+ db_user = config.get("DATABASE", "user")
+ db_pass = config.get("DATABASE", "pass")
+ db_host = config.get("DATABASE", "host")
+ db_port = int(config.get("DATABASE", "port"))
+ LOG.info("Connecting to database %s on %s" % (db_name, db_host))
+ conn = MySQLdb.connect(host=db_host, user=db_user, port=db_port,
+ passwd=db_pass, db=db_name)
+ except Exception, e:
+ LOG.error("Unable to parse config file \"%s\": \nException%s"
+ % (config_file, str(e)))
+ sys.exit(1)
+
+ try:
+ plugin = LinuxBridgeQuantumAgent(br_name_prefix, physical_interface,
+ polling_interval)
+ LOG.info("Agent initialized successfully, now running...")
+ plugin.daemon_loop(conn)
+ finally:
+ if conn:
+ conn.close()
+
+ sys.exit(0)
--- /dev/null
+"""
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+#
+"""
--- /dev/null
+"""
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+#
+"""
+
+from configobj import ConfigObj
+
+
+class ConfigParser(ConfigObj):
+ """Config Parser based on the ConfigObj module"""
+
+ def __init__(self, filename):
+ super(ConfigParser, self).__init__(filename, raise_errors=True,
+ file_error=True)
+
+ def dummy(self, section, key):
+ """Dummy function to return the same key, used in walk"""
+ return section[key]
--- /dev/null
+"""
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+#
+"""
+
+PORT_STATE = 'port-state'
+PORT_UP = "ACTIVE"
+PORT_DOWN = "DOWN"
+
+UUID = 'uuid'
+TENANTID = 'tenant_id'
+NETWORKID = 'network_id'
+NETWORKNAME = 'name'
+NETWORKPORTS = 'ports'
+OPSTATUS = 'op_status'
+INTERFACEID = 'interface_id'
+PORTSTATE = 'state'
+PORTID = 'port_id'
+PPNAME = 'name'
+PPVLANID = 'vlan_id'
+VLANID = 'vlan_id'
+VLANNAME = 'vlan_name'
+
+ATTACHMENT = 'attachment'
+PORT_ID = 'port-id'
+PORT_OP_STATUS = 'port-op-status'
+
+NET_ID = 'net-id'
+NET_NAME = 'net-name'
+NET_PORTS = 'net-ports'
+NET_OP_STATUS = 'net-op-status'
+NET_VLAN_NAME = 'net-vlan-name'
+NET_VLAN_ID = 'net-vlan-id'
+NET_TENANTS = 'net-tenants'
+
+USERNAME = 'username'
+PASSWORD = 'password'
+
+DELIMITERS = "[,;:\b\s]"
+
+UUID_LENGTH = 36
+
+UNPLUGGED = '(detached)'
+
+ASSOCIATION_STATUS = 'association_status'
+
+ATTACHED = 'attached'
+
+DETACHED = 'detached'
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+"""
+Exceptions used by the LinuxBridge plugin
+"""
+from quantum.common import exceptions
+
+
+class NetworksLimit(exceptions.QuantumException):
+ """Total number of network objects limit has been hit"""
+ message = _("Unable to create new network. Number of networks" \
+ "for the system has exceeded the limit")
+
+
+class NetworkVlanBindingAlreadyExists(exceptions.QuantumException):
+ """Binding cannot be created, since it already exists"""
+ message = _("NetworkVlanBinding for %(vlan_id)s and network " \
+ "%(network_id)s already exists")
+
+
+class NetworkVlanBindingNotFound(exceptions.QuantumException):
+ """Binding could not be found"""
+ message = _("NetworkVlanBinding for network " \
+ "%(network_id)s does not exist")
+
+
+class VlanIDNotFound(exceptions.QuantumException):
+ """VLAN ID cannot be found"""
+ message = _("Vlan ID %(vlan_id)s not found")
+
+
+class VlanIDNotAvailable(exceptions.QuantumException):
+ """No VLAN ID available"""
+ message = _("No Vlan ID available")
+
+
+class UnableToChangeVlanRange(exceptions.QuantumException):
+ """No VLAN ID available"""
+ message = _("Current VLAN ID range %(range_start)s to %(range_end)s " \
+ "cannot be changed. Please check plugin conf file.")
+
+
+try:
+ _("test")
+except NameError:
+
+ def _(a_string):
+ """
+ Default implementation of the gettext string
+ translation function: no translation
+ """
+ return a_string
+except TypeError:
+ # during doctesting, _ might mean something else
+ pass
--- /dev/null
+"""
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+#
+"""
+
+import logging
+
+from quantum.api.api_common import OperationalStatus
+from quantum.plugins.linuxbridge.common import constants as const
+
+LOG = logging.getLogger(__name__)
+
+
+def make_net_dict(net_id, net_name, ports, op_status):
+ """Helper funciton"""
+ res = {const.NET_ID: net_id, const.NET_NAME: net_name, const.NET_OP_STATUS:
+ op_status}
+ if ports:
+ res[const.NET_PORTS] = ports
+ return res
+
+
+def make_port_dict(port):
+ """Helper funciton"""
+ if port[const.PORTSTATE] == const.PORT_UP:
+ op_status = port[const.OPSTATUS]
+ else:
+ op_status = OperationalStatus.DOWN
+
+ return {const.PORT_ID: str(port[const.UUID]),
+ const.PORT_STATE: port[const.PORTSTATE],
+ const.PORT_OP_STATUS: op_status,
+ const.NET_ID: port[const.NETWORKID],
+ const.ATTACHMENT: port[const.INTERFACEID]}
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+#
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+from sqlalchemy import func
+from sqlalchemy.orm import exc
+
+from quantum.common import exceptions as q_exc
+from quantum.plugins.linuxbridge import plugin_configuration as conf
+from quantum.plugins.linuxbridge.common import exceptions as c_exc
+from quantum.plugins.linuxbridge.db import l2network_models
+
+import logging
+
+import quantum.db.api as db
+
+
+LOG = logging.getLogger(__name__)
+
+
+def initialize():
+ 'Establish database connection and load models'
+ if conf.DB_CONNECTION == 'sqlite':
+ options = {"sql_connection": "sqlite://"}
+ else:
+ options = {"sql_connection": "mysql://%s:%s@%s:%s/%s" % (conf.DB_USER,
+ conf.DB_PASS,
+ conf.DB_HOST,
+ conf.DB_PORT,
+ conf.DB_NAME)}
+
+ db.configure_db(options)
+ create_vlanids()
+
+
+def create_vlanids():
+ """Prepopulates the vlan_bindings table"""
+ LOG.debug("create_vlanids() called")
+ session = db.get_session()
+ start = int(conf.VLAN_START)
+ end = int(conf.VLAN_END)
+ try:
+ vlanid = session.query(l2network_models.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(l2network_models.VlanID.vlan_id)).
+ one()[0])
+ current_end = \
+ int(session.query(func.max(l2network_models.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 = l2network_models.VlanID(start)
+ session.add(vlanid)
+ start += 1
+ session.flush()
+ return
+
+
+def get_all_vlanids():
+ """Gets all the vlanids"""
+ LOG.debug("get_all_vlanids() called")
+ session = db.get_session()
+ try:
+ vlanids = session.query(l2network_models.VlanID).\
+ all()
+ return vlanids
+ except exc.NoResultFound:
+ return []
+
+
+def is_vlanid_used(vlan_id):
+ """Checks if a vlanid is in use"""
+ LOG.debug("is_vlanid_used() called")
+ session = db.get_session()
+ try:
+ vlanid = session.query(l2network_models.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):
+ """Sets the vlanid state to be unused"""
+ LOG.debug("release_vlanid() called")
+ session = db.get_session()
+ try:
+ vlanid = session.query(l2network_models.VlanID).\
+ filter_by(vlan_id=vlan_id).\
+ one()
+ vlanid["vlan_used"] = False
+ session.merge(vlanid)
+ session.flush()
+ return vlanid["vlan_used"]
+ except exc.NoResultFound:
+ raise c_exc.VlanIDNotFound(vlan_id=vlan_id)
+ return
+
+
+def delete_vlanid(vlan_id):
+ """Deletes a vlanid entry from db"""
+ LOG.debug("delete_vlanid() called")
+ session = db.get_session()
+ try:
+ vlanid = session.query(l2network_models.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():
+ """Reserves the first unused vlanid"""
+ LOG.debug("reserve_vlanid() called")
+ session = db.get_session()
+ try:
+ rvlan = session.query(l2network_models.VlanID).\
+ first()
+ if not rvlan:
+ create_vlanids()
+
+ rvlan = session.query(l2network_models.VlanID).\
+ filter_by(vlan_used=False).\
+ first()
+ if not rvlan:
+ raise c_exc.VlanIDNotAvailable()
+
+ rvlanid = session.query(l2network_models.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 get_all_vlanids_used():
+ """Gets all the vlanids used"""
+ LOG.debug("get_all_vlanids() called")
+ session = db.get_session()
+ try:
+ vlanids = session.query(l2network_models.VlanID).\
+ filter_by(vlan_used=True).\
+ all()
+ return vlanids
+ except exc.NoResultFound:
+ return []
+
+
+def get_all_vlan_bindings():
+ """Lists all the vlan to network associations"""
+ LOG.debug("get_all_vlan_bindings() called")
+ session = db.get_session()
+ try:
+ bindings = session.query(l2network_models.VlanBinding).\
+ all()
+ return bindings
+ except exc.NoResultFound:
+ return []
+
+
+def get_vlan_binding(netid):
+ """Lists the vlan given a network_id"""
+ LOG.debug("get_vlan_binding() called")
+ session = db.get_session()
+ try:
+ binding = session.query(l2network_models.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):
+ """Adds a vlan to network association"""
+ LOG.debug("add_vlan_binding() called")
+ session = db.get_session()
+ try:
+ binding = session.query(l2network_models.VlanBinding).\
+ filter_by(vlan_id=vlanid).\
+ one()
+ raise c_exc.NetworkVlanBindingAlreadyExists(vlan_id=vlanid,
+ network_id=netid)
+ except exc.NoResultFound:
+ binding = l2network_models.VlanBinding(vlanid, netid)
+ session.add(binding)
+ session.flush()
+ return binding
+
+
+def remove_vlan_binding(netid):
+ """Removes a vlan to network association"""
+ LOG.debug("remove_vlan_binding() called")
+ session = db.get_session()
+ try:
+ binding = session.query(l2network_models.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):
+ """Updates a vlan to network association"""
+ LOG.debug("update_vlan_binding() called")
+ session = db.get_session()
+ try:
+ binding = session.query(l2network_models.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)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+
+import uuid
+
+from sqlalchemy import Column, Integer, String, Boolean
+from sqlalchemy.orm import relation, object_mapper
+
+from quantum.db.models import BASE
+from quantum.db.models import QuantumBase
+from quantum.db import models
+
+
+class VlanID(BASE, QuantumBase):
+ """Represents a vlan_id usage"""
+ __tablename__ = 'vlan_ids'
+
+ vlan_id = Column(Integer, primary_key=True)
+ vlan_used = Column(Boolean)
+
+ def __init__(self, vlan_id):
+ self.vlan_id = vlan_id
+ self.vlan_used = False
+
+ def __repr__(self):
+ return "<VlanID(%d,%s)>" % \
+ (self.vlan_id, self.vlan_used)
+
+
+class VlanBinding(BASE, QuantumBase):
+ """Represents a binding of vlan_id to network_id"""
+ __tablename__ = 'vlan_bindings'
+
+ vlan_id = Column(Integer, primary_key=True)
+ network_id = Column(String(255), nullable=False)
+
+ def __init__(self, vlan_id, network_id):
+ self.vlan_id = vlan_id
+ self.network_id = network_id
+
+ def __repr__(self):
+ return "<VlanBinding(%d,%s,%s)>" % \
+ (self.vlan_id, self.network_id)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc.
+# All Rights Reserved.
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# Extends the linux_net.py kvm/linux network driver in Nova,
+# borrows structure and code
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+#
+
+
+"""Extends the linux_net driver when using the Linux Bridge plugin with
+QuantumManager"""
+
+
+from nova import exception
+from nova import log as logging
+from nova import utils
+
+from nova.network.linux_net import *
+
+
+LOG = logging.getLogger(__name__)
+
+
+BRDIGE_NAME_PREFIX = "brq"
+GATEWAY_INTERFACE_PREFIX = "gw-"
+
+
+def _device_exists(device):
+ """Check if ethernet device exists."""
+ (_out, err) = utils.execute('ip', 'link', 'show', 'dev', device,
+ check_exit_code=False)
+ return not err
+
+
+# plugs interfaces using Linux Bridge when using QuantumManager
+class QuantumLibvirtLinuxBridgeDriver(LinuxNetInterfaceDriver):
+
+ def plug(self, network, mac_address, gateway=True):
+ LOG.debug(_("inside plug()"))
+ dev = self.get_dev(network)
+ bridge = self.get_bridge(network)
+ if not gateway:
+ # If we weren't instructed to act as a gateway then add the
+ # appropriate flows to block all non-dhcp traffic.
+ # .. and make sure iptbles won't forward it as well.
+ iptables_manager.ipv4['filter'].add_rule('FORWARD',
+ '--in-interface %s -j DROP' % bridge)
+ iptables_manager.ipv4['filter'].add_rule('FORWARD',
+ '--out-interface %s -j DROP' % bridge)
+ return bridge
+ else:
+ iptables_manager.ipv4['filter'].add_rule('FORWARD',
+ '--in-interface %s -j ACCEPT' % bridge)
+ iptables_manager.ipv4['filter'].add_rule('FORWARD',
+ '--out-interface %s -j ACCEPT' % bridge)
+
+ if not _device_exists(dev):
+ try:
+ # First, try with 'ip'
+ utils.execute('ip', 'tuntap', 'add', dev, 'mode', 'tap',
+ run_as_root=True)
+ except exception.ProcessExecutionError:
+ # Second option: tunctl
+ utils.execute('tunctl', '-b', '-t', dev, run_as_root=True)
+ utils.execute('ip', 'link', 'set', dev, "address", mac_address,
+ run_as_root=True)
+ utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True)
+
+ if not _device_exists(bridge):
+ LOG.debug(_("Starting bridge %s "), bridge)
+ utils.execute('brctl', 'addbr', bridge, run_as_root=True)
+ utils.execute('brctl', 'setfd', bridge, str(0), run_as_root=True)
+ utils.execute('brctl', 'stp', bridge, 'off', run_as_root=True)
+ utils.execute('ip', 'link', 'set', bridge, "address", mac_address,
+ run_as_root=True)
+ utils.execute('ip', 'link', 'set', bridge, 'up', run_as_root=True)
+ LOG.debug(_("Done starting bridge %s"), bridge)
+
+ full_ip = '%s/%s' % (network['dhcp_server'],
+ network['cidr'].rpartition('/')[2])
+ utils.execute('ip', 'address', 'add', full_ip, 'dev', bridge,
+ run_as_root=True)
+
+ return dev
+
+ def unplug(self, network):
+ LOG.debug(_("inside unplug()"))
+ dev = self.get_dev(network)
+ try:
+ utils.execute('ip', 'link', 'delete', dev, run_as_root=True)
+ except exception.ProcessExecutionError:
+ LOG.warning(_("Failed while unplugging gateway interface '%s'"),
+ dev)
+ raise
+ LOG.debug(_("Unplugged gateway interface '%s'"), dev)
+ return dev
+
+ def get_dev(self, network):
+ dev = GATEWAY_INTERFACE_PREFIX + str(network['uuid'][0:11])
+ return dev
+
+ def get_bridge(self, network):
+ bridge = BRDIGE_NAME_PREFIX + str(network['uuid'][0:11])
+ return bridge
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright (C) 2012 Midokura KK
+# Copyright (C) 2012 Nicira, Inc
+# Copyright (C) 2012 Cisco Systems, Inc
+# Copyright 2012 OpenStack LLC.
+# 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.
+
+"""
+VIF driver for libvirt when QuantumManager is configured with Linux Bridge
+plugin
+"""
+
+from nova import flags
+from nova import log as logging
+from nova.network import linux_net
+from nova.virt import netutils
+from nova import utils
+from nova.virt.vif import VIFDriver
+from nova import exception
+
+LOG = logging.getLogger('nova.virt.libvirt.vif_linuxbridge_quantum')
+
+FLAGS = flags.FLAGS
+
+
+class QuantumLibvirtLinuxBridgeDriver(VIFDriver):
+ """VIF driver for Linux Bridge."""
+
+ def get_dev_name(_self, iface_id):
+ return "tap" + iface_id[0:11]
+
+ def plug(self, instance, network, mapping):
+ iface_id = mapping['vif_uuid']
+ dev = self.get_dev_name(iface_id)
+ if not linux_net._device_exists(dev):
+ try:
+ # First, try with 'ip'
+ utils.execute('ip', 'tuntap', 'add', dev, 'mode', 'tap',
+ run_as_root=True)
+ except exception.ProcessExecutionError:
+ # Second option: tunctl
+ utils.execute('tunctl', '-b', '-t', dev, run_as_root=True)
+ utils.execute('ip', 'link', 'set', dev, 'up', run_as_root=True)
+
+ result = {
+ 'script': '',
+ 'name': dev,
+ 'mac_address': mapping['mac']}
+ return result
+
+ def unplug(self, instance, network, mapping):
+ """Unplug the VIF from the network by deleting the port from
+ the bridge."""
+ dev = self.get_dev_name(mapping['vif_uuid'])
+ try:
+ utils.execute('ip', 'link', 'delete', dev, run_as_root=True)
+ except exception.ProcessExecutionError:
+ LOG.warning(_("Failed while unplugging vif of instance '%s'"),
+ instance['name'])
+ raise
--- /dev/null
+"""
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Sumit Naiksatam, Cisco Systems, Inc.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+"""
+
+import os
+
+from quantum.common.config import find_config_file
+from quantum.plugins.linuxbridge.common import configparser as confp
+
+
+CONF_FILE = find_config_file({'plugin': 'linuxbridge'}, None,
+ "linuxbridge_conf.ini")
+CONF_PARSER_OBJ = confp.ConfigParser(CONF_FILE)
+
+
+"""
+Reading the conf for the linuxbridge_plugin
+"""
+SECTION_CONF = CONF_PARSER_OBJ['VLANS']
+VLAN_START = SECTION_CONF['vlan_start']
+VLAN_END = SECTION_CONF['vlan_end']
+
+
+SECTION_CONF = CONF_PARSER_OBJ['DATABASE']
+DB_CONNECTION = SECTION_CONF['connection']
+if DB_CONNECTION != 'sqlite':
+ DB_NAME = SECTION_CONF['name']
+ DB_USER = SECTION_CONF['user']
+ DB_PASS = SECTION_CONF['pass']
+ DB_HOST = SECTION_CONF['host']
+ DB_PORT = SECTION_CONF['port']
--- /dev/null
+#!/usr/bin/env python
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2010 OpenStack, LLC
+# 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.
+
+
+"""Unittest runner for quantum Linux Bridge plugin
+
+This file should be run from the top dir in the quantum directory
+
+To run all tests::
+ PLUGIN_DIR=quantum/plugins/linuxbridge ./run_tests.sh
+"""
+
+import gettext
+import logging
+import os
+import unittest
+import sys
+
+from nose import config
+from nose import core
+
+sys.path.append(os.getcwd())
+sys.path.append(os.path.dirname(__file__))
+
+
+from quantum.api.api_common import OperationalStatus
+from quantum.common.test_lib import run_tests, test_config
+from quantum.plugins.linuxbridge.LinuxBridgePlugin import LinuxBridgePlugin
+import quantum.tests.unit
+from tests.unit.test_database import L2networkDBTest
+
+
+if __name__ == '__main__':
+ exit_status = False
+
+ # if a single test case was specified,
+ # we should only invoked the tests once
+ invoke_once = len(sys.argv) > 1
+
+ test_config['plugin_name'] = "LinuxBridgePlugin.LinuxBridgePlugin"
+ test_config['default_net_op_status'] = OperationalStatus.UP
+ test_config['default_port_op_status'] = OperationalStatus.DOWN
+
+ cwd = os.getcwd()
+ c = config.Config(stream=sys.stdout,
+ env=os.environ,
+ verbosity=3,
+ includeExe=True,
+ traverseNamespace=True,
+ plugins=core.DefaultPluginManager())
+ c.configureWhere(quantum.tests.unit.__path__)
+ exit_status = run_tests(c)
+
+ if invoke_once:
+ sys.exit(0)
+
+ os.chdir(cwd)
+
+ working_dir = os.path.abspath("quantum/plugins/linuxbridge")
+ c = config.Config(stream=sys.stdout,
+ env=os.environ,
+ verbosity=3,
+ workingDir=working_dir)
+ exit_status = exit_status or run_tests(c)
+
+ sys.exit(exit_status)
--- /dev/null
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012 Cisco Systems, Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# @author: Shweta Padubidri, Cisco Systems, Inc.
+#
+
+import ConfigParser
+import logging as LOG
+import unittest
+import sys
+import os
+import signal
+from subprocess import *
+
+import quantum.plugins.linuxbridge.agent.linuxbridge_quantum_agent\
+ as linux_agent
+from quantum.plugins.linuxbridge.common import constants as lconst
+from quantum.plugins.linuxbridge import LinuxBridgePlugin
+from quantum.plugins.linuxbridge.db import l2network_db as cdb
+import quantum.db.api as db
+
+
+LOG.getLogger(__name__)
+
+
+class LinuxBridgeAgentTest(unittest.TestCase):
+
+ def test_add_gateway_interface(
+ self, tenant_id="test_tenant", network_name="test_network",
+ interface_id='fe701ddf-26a2-42ea-b9e6-7313d1c522cc',
+ mac_address='fe:16:3e:51:60:dd'):
+
+ LOG.debug("test_tap_gateway_interface - START")
+ new_network =\
+ self._linuxbridge_plugin.create_network(tenant_id, network_name)
+ new_port = self._linuxbridge_plugin.create_port(
+ tenant_id, new_network[lconst.NET_ID], lconst.PORT_UP)
+ self._linuxbridge_plugin.plug_interface(
+ tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID], interface_id)
+ bridge_name = self.br_name_prefix + new_network[lconst.NET_ID][0:11]
+ self.create_bridge(bridge_name)
+ device_name = self.gw_name_prefix + new_network[lconst.NET_ID][0:11]
+ self.create_device(device_name, mac_address)
+
+ vlan_bind = cdb.get_vlan_binding(new_network[lconst.NET_ID])
+ vlan_id = vlan_bind[lconst.VLANID]
+
+ self._linuxbridge_quantum_agent.process_port_binding(
+ new_port[lconst.PORT_ID], new_network[lconst.NET_ID],
+ device_name, str(vlan_id))
+ list_interface = self._linuxbridge_quantum_agent.linux_br.\
+ get_interfaces_on_bridge(bridge_name)
+
+ self.assertTrue(device_name in list_interface)
+ for interface in list_interface:
+ self._linuxbridge_quantum_agent.linux_br.remove_interface(
+ bridge_name, interface)
+ self.delete_device(interface)
+ self.delete_bridge(bridge_name)
+ self.tearDownUnplugInterface(tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+
+ LOG.debug("test_add_gateway_interface - END")
+
+ def test_add_tap_interface(
+ self, tenant_id="test_tenant", network_name="test_network",
+ interface_id='fe701ddf-26a2-42ea-b9e6-7313d1c522cc',
+ mac_address='fe:16:3e:51:60:dd'):
+
+ LOG.debug("test_add_tap_interface - START")
+ new_network =\
+ self._linuxbridge_plugin.create_network(tenant_id, network_name)
+ new_port = self._linuxbridge_plugin.create_port(
+ tenant_id, new_network[lconst.NET_ID], lconst.PORT_UP)
+ self._linuxbridge_plugin.plug_interface(
+ tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID], interface_id)
+ bridge_name = self.br_name_prefix + new_network[lconst.NET_ID][0:11]
+ self.create_bridge(bridge_name)
+ device_name = self.tap_name_prefix + interface_id[0:11]
+ self.create_device(device_name, mac_address)
+
+ vlan_bind = cdb.get_vlan_binding(new_network[lconst.NET_ID])
+ vlan_id = vlan_bind[lconst.VLANID]
+
+ self._linuxbridge_quantum_agent.process_port_binding(
+ new_port[lconst.PORT_ID], new_network[lconst.NET_ID],
+ interface_id, str(vlan_id))
+ list_interface = self._linuxbridge_quantum_agent.linux_br.\
+ get_interfaces_on_bridge(bridge_name)
+
+ self.assertTrue(device_name in list_interface)
+ for interface in list_interface:
+ self._linuxbridge_quantum_agent.linux_br.remove_interface(
+ bridge_name, interface)
+ self.delete_device(interface)
+ self.delete_bridge(bridge_name)
+ self.tearDownUnplugInterface(tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+
+ LOG.debug("test_add_tap_interface -END")
+
+ def test_remove_interface(
+ self, tenant_id="test_tenant", network_name="test_network",
+ interface_id='fe701ddf-26a2-42ea-b9e6-7313d1c522cc',
+ mac_address='fe:16:3e:51:60:dd'):
+
+ LOG.debug("test_remove_interface - START")
+ new_network =\
+ self._linuxbridge_plugin.create_network(tenant_id, network_name)
+ new_port = self._linuxbridge_plugin.create_port(
+ tenant_id, new_network[lconst.NET_ID], lconst.PORT_UP)
+ self._linuxbridge_plugin.plug_interface(
+ tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID], interface_id)
+ bridge_name = self.br_name_prefix + new_network[lconst.NET_ID][0:11]
+ self.create_bridge(bridge_name)
+ device_name = self.tap_name_prefix + interface_id[0:11]
+ self.create_device(device_name, mac_address)
+
+ vlan_bind = cdb.get_vlan_binding(new_network[lconst.NET_ID])
+ vlan_id = vlan_bind[lconst.VLANID]
+
+ self._linuxbridge_quantum_agent.process_port_binding(
+ new_port[lconst.PORT_ID], new_network[lconst.NET_ID],
+ interface_id, str(vlan_id))
+ list_interface = self._linuxbridge_quantum_agent.linux_br.\
+ get_interfaces_on_bridge(bridge_name)
+
+ self._linuxbridge_quantum_agent.linux_br.remove_interface(bridge_name,
+ device_name)
+ list_interface = self._linuxbridge_quantum_agent.linux_br.\
+ get_interfaces_on_bridge(bridge_name)
+ self.assertFalse(device_name in list_interface)
+ for interface in list_interface:
+ self._linuxbridge_quantum_agent.linux_br.remove_interface(
+ bridge_name, interface)
+ self.delete_device(interface)
+ self.delete_device(device_name)
+ self.delete_bridge(bridge_name)
+ self.tearDownUnplugInterface(tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+
+ LOG.debug("test_remove_interface -END")
+
+ def test_ensure_vlan_bridge(
+ self, tenant_id="test_tenant", network_name="test_network",
+ interface_id='fe701ddf-26a2-42ea-b9e6-7313d1c522cc'):
+
+ LOG.debug("test_ensure_vlan_bridge - START")
+ new_network =\
+ self._linuxbridge_plugin.create_network(tenant_id, network_name)
+ new_port = self._linuxbridge_plugin.create_port(
+ tenant_id, new_network[lconst.NET_ID], lconst.PORT_UP)
+ self._linuxbridge_plugin.plug_interface(
+ tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID], interface_id)
+ bridge_name = self.br_name_prefix + new_network[lconst.NET_ID][0:11]
+ vlan_bind = cdb.get_vlan_binding(new_network[lconst.NET_ID])
+ vlan_id = vlan_bind[lconst.VLANID]
+ vlan_subinterface = self.physical_interface + '.' + str(vlan_id)
+
+ self._linuxbridge_quantum_agent.linux_br.ensure_vlan_bridge(
+ new_network[lconst.NET_ID], str(vlan_id))
+ list_quantum_bridges = self._linuxbridge_quantum_agent.linux_br.\
+ get_all_quantum_bridges()
+ self.assertTrue(bridge_name in list_quantum_bridges)
+ list_interface = self._linuxbridge_quantum_agent.linux_br.\
+ get_interfaces_on_bridge(bridge_name)
+ self.assertTrue(vlan_subinterface in list_interface)
+
+ for interface in list_interface:
+ self._linuxbridge_quantum_agent.linux_br.remove_interface(
+ bridge_name, interface)
+ self.delete_device(interface)
+ self.delete_bridge(bridge_name)
+ self.tearDownUnplugInterface(tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+
+ LOG.debug("test_ensure_vlan_bridge -END")
+
+ def test_delete_vlan_bridge(
+ self, tenant_id="test_tenant", network_name="test_network",
+ interface_id='fe701ddf-26a2-42ea-b9e6-7313d1c522cc'):
+
+ LOG.debug("test_delete_vlan_bridge - START")
+ new_network =\
+ self._linuxbridge_plugin.create_network(tenant_id, network_name)
+ new_port = self._linuxbridge_plugin.create_port(
+ tenant_id, new_network[lconst.NET_ID], lconst.PORT_UP)
+ self._linuxbridge_plugin.plug_interface(
+ tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID], interface_id)
+ bridge_name = self.br_name_prefix + new_network[lconst.NET_ID][0:11]
+ vlan_bind = cdb.get_vlan_binding(new_network[lconst.NET_ID])
+ vlan_id = vlan_bind[lconst.VLANID]
+ vlan_subinterface = self.physical_interface + '.' + str(vlan_id)
+
+ self._linuxbridge_quantum_agent.linux_br.ensure_vlan_bridge(
+ new_network[lconst.NET_ID], str(vlan_id))
+ self._linuxbridge_quantum_agent.linux_br.delete_vlan_bridge(
+ bridge_name)
+
+ self.assertEquals(self.device_exists(vlan_subinterface), False)
+ self.assertEquals(self.device_exists(bridge_name), False)
+ self.tearDownUnplugInterface(tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+
+ LOG.debug("test_delete_vlan_bridge - END")
+
+ def test_process_deleted_networks(
+ self, tenant_id="test_tenant", network_name="test_network",
+ interface_id='fe701ddf-26a2-42ea-b9e6-7313d1c522cc'):
+
+ LOG.debug("test_delete_vlan_bridge - START")
+ new_network =\
+ self._linuxbridge_plugin.create_network(tenant_id, network_name)
+ new_port = self._linuxbridge_plugin.create_port(
+ tenant_id, new_network[lconst.NET_ID], lconst.PORT_UP)
+ self._linuxbridge_plugin.plug_interface(
+ tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID], interface_id)
+ bridge_name = self.br_name_prefix + new_network[lconst.NET_ID][0:11]
+ vlan_bindings = {}
+ vlan_bindings[new_network[lconst.NET_ID]] =\
+ cdb.get_vlan_binding(new_network[lconst.NET_ID])
+ vlan_id = vlan_bindings[new_network[lconst.NET_ID]][lconst.VLANID]
+ vlan_subinterface = self.physical_interface + '.' + str(vlan_id)
+
+ self._linuxbridge_quantum_agent.linux_br.ensure_vlan_bridge(
+ new_network[lconst.NET_ID], str(vlan_id))
+ self.tearDownUnplugInterface(tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+ vlan_bindings = {}
+ self._linuxbridge_quantum_agent.process_deleted_networks(vlan_bindings)
+
+ self.assertEquals(self.device_exists(vlan_subinterface), False)
+ self.assertEquals(self.device_exists(bridge_name), False)
+ LOG.debug("test_delete_vlan_bridge - END")
+
+ def test_process_unplugged_tap_interface(
+ self, tenant_id="test_tenant", network_name="test_network",
+ interface_id='fe701ddf-26a2-42ea-b9e6-7313d1c522cc',
+ mac_address='fe:16:3e:51:60:dd'):
+
+ LOG.debug("test_process_unplugged_tap_interface - START")
+ new_network =\
+ self._linuxbridge_plugin.create_network(tenant_id, network_name)
+ new_port = self._linuxbridge_plugin.create_port(
+ tenant_id, new_network[lconst.NET_ID], lconst.PORT_UP)
+ self._linuxbridge_plugin.plug_interface(
+ tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID], interface_id)
+ bridge_name = self.br_name_prefix + new_network[lconst.NET_ID][0:11]
+ self.create_bridge(bridge_name)
+ device_name = self.tap_name_prefix + interface_id[0:11]
+ self.create_device(device_name, mac_address)
+
+ vlan_bind = cdb.get_vlan_binding(new_network[lconst.NET_ID])
+ vlan_id = vlan_bind[lconst.VLANID]
+
+ self._linuxbridge_quantum_agent.process_port_binding(
+ new_port[lconst.PORT_ID], new_network[lconst.NET_ID],
+ interface_id, str(vlan_id))
+ list_interface = self._linuxbridge_quantum_agent.linux_br.\
+ get_interfaces_on_bridge(bridge_name)
+ self._linuxbridge_plugin.unplug_interface(tenant_id,
+ new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+ plugged_interface = []
+ self._linuxbridge_quantum_agent.process_unplugged_interfaces(
+ plugged_interface)
+ list_interface = self._linuxbridge_quantum_agent.linux_br.\
+ get_interfaces_on_bridge(bridge_name)
+ self.assertFalse(device_name in list_interface)
+ for interface in list_interface:
+ self._linuxbridge_quantum_agent.linux_br.remove_interface(
+ bridge_name, interface)
+ self.delete_device(interface)
+ self.delete_device(device_name)
+ self.delete_bridge(bridge_name)
+ self.tearDownNetworkPort(tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+
+ LOG.debug("test_test_process_unplugged_tap_interface -END")
+
+ def test_process_unplugged_gw_interface(
+ self, tenant_id="test_tenant", network_name="test_network",
+ interface_id='fe701ddf-26a2-42ea-b9e6-7313d1c522cc',
+ mac_address='fe:16:3e:51:60:dd'):
+
+ LOG.debug("test_process_unplugged_gw_interface - START")
+ new_network =\
+ self._linuxbridge_plugin.create_network(tenant_id, network_name)
+ new_port = self._linuxbridge_plugin.create_port(
+ tenant_id, new_network[lconst.NET_ID], lconst.PORT_UP)
+ self._linuxbridge_plugin.plug_interface(
+ tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID], interface_id)
+ bridge_name = self.br_name_prefix + new_network[lconst.NET_ID][0:11]
+ self.create_bridge(bridge_name)
+ device_name = self.gw_name_prefix + new_network[lconst.NET_ID][0:11]
+ self.create_device(device_name, mac_address)
+
+ vlan_bind = cdb.get_vlan_binding(new_network[lconst.NET_ID])
+ vlan_id = vlan_bind[lconst.VLANID]
+
+ self._linuxbridge_quantum_agent.process_port_binding(
+ new_port[lconst.PORT_ID], new_network[lconst.NET_ID],
+ interface_id, str(vlan_id))
+ list_interface = self._linuxbridge_quantum_agent.linux_br.\
+ get_interfaces_on_bridge(bridge_name)
+ self._linuxbridge_plugin.unplug_interface(tenant_id,
+ new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+ plugged_interface = []
+ self._linuxbridge_quantum_agent.process_unplugged_interfaces(
+ plugged_interface)
+ list_interface = self._linuxbridge_quantum_agent.linux_br.\
+ get_interfaces_on_bridge(bridge_name)
+ self.assertFalse(device_name in list_interface)
+ for interface in list_interface:
+ self._linuxbridge_quantum_agent.linux_br.remove_interface(
+ bridge_name, interface)
+ self.delete_device(interface)
+ self.delete_device(device_name)
+ self.delete_bridge(bridge_name)
+ self.tearDownNetworkPort(tenant_id, new_network[lconst.NET_ID],
+ new_port[lconst.PORT_ID])
+
+ LOG.debug("test_test_process_unplugged_gw_interface -END")
+
+ def create_bridge(self, bridge_name):
+ """
+ Create a bridge
+ """
+ self.run_cmd(['brctl', 'addbr', bridge_name])
+ self.run_cmd(['brctl', 'setfd', bridge_name, str(0)])
+ self.run_cmd(['brctl', 'stp', bridge_name, 'off'])
+ self.run_cmd(['ip', 'link', 'set', bridge_name, 'up'])
+
+ def delete_bridge(self, bridge_name):
+ """
+ Delete a bridge
+ """
+ self.run_cmd(['ip', 'link', 'set', bridge_name, 'down'])
+ self.run_cmd(['brctl', 'delbr', bridge_name])
+
+ def create_device(self, dev, mac_address):
+ self.run_cmd(['ip', 'tuntap', 'add', dev, 'mode', 'tap'])
+ self.run_cmd(['ip', 'link', 'set', dev, "address", mac_address])
+ self.run_cmd(['ip', 'link', 'set', dev, 'up'])
+
+ def delete_device(self, dev):
+ self.run_cmd(['ip', 'link', 'delete', dev])
+
+ def setUp(self):
+ """
+ Set up function
+ """
+ self.tenant_id = "test_tenant"
+ self.network_name = "test_network"
+ self.config_file = os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir, os.pardir, os.pardir,
+ os.pardir, "etc", "quantum",
+ "plugins", "linuxbridge",
+ "linuxbridge_conf.ini")
+
+ config = ConfigParser.ConfigParser()
+ self.br_name_prefix = "brq"
+ self.gw_name_prefix = "gw-"
+ self.tap_name_prefix = "tap"
+ self._linuxbridge_plugin = LinuxBridgePlugin.LinuxBridgePlugin()
+ try:
+ fh = open(self.config_file)
+ fh.close()
+ config.read(self.config_file)
+ self.physical_interface = config.get("LINUX_BRIDGE",
+ "physical_interface")
+ self.polling_interval = config.get("AGENT", "polling_interval")
+ except Exception, e:
+ LOG.error("Unable to parse config file \"%s\": \nException%s"
+ % (self.config_file, str(e)))
+ sys.exit(1)
+ self._linuxbridge = linux_agent.LinuxBridge(self.br_name_prefix,
+ self.physical_interface)
+ self._linuxbridge_quantum_agent = linux_agent.LinuxBridgeQuantumAgent(
+ self.br_name_prefix,
+ self.physical_interface,
+ self.polling_interval)
+
+ def run_cmd(self, args):
+ LOG.debug("Running command: " + " ".join(args))
+ p = Popen(args, stdout=PIPE)
+ retval = p.communicate()[0]
+ if p.returncode == -(signal.SIGALRM):
+ LOG.debug("Timeout running command: " + " ".join(args))
+ if retval:
+ LOG.debug("Command returned: %s" % retval)
+ return retval
+
+ def device_exists(self, device):
+ """Check if ethernet device exists."""
+ retval = self.run_cmd(['ip', 'link', 'show', 'dev', device])
+ if retval:
+ return True
+ else:
+ return False
+
+ """
+ Clean up functions after the tests
+ """
+ def tearDown(self):
+ """Clear the test environment(Clean the Database)"""
+ db.clear_db()
+
+ def tearDownNetwork(self, tenant_id, network_dict_id):
+ """
+ Tear down the Network
+ """
+ self._linuxbridge_plugin.delete_network(tenant_id, network_dict_id)
+
+ def tearDownUnplugInterface(self, tenant_id, network_dict_id, port_id):
+ """
+ Tear down the port
+ """
+ self._linuxbridge_plugin.unplug_interface(tenant_id, network_dict_id,
+ port_id)
+ self.tearDownNetworkPort(tenant_id, network_dict_id, port_id)
+
+ def tearDownNetworkPort(self, tenant_id, network_dict_id, port_id):
+ """
+ Tear down Network Port
+ """
+ self._linuxbridge_plugin.delete_port(tenant_id, network_dict_id,
+ port_id)
+ self.tearDownNetwork(tenant_id, network_dict_id)
--- /dev/null
+"""
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+#
+# Copyright 2012, Cisco Systems, Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+# @author: Rohit Agarwalla, Cisco Systems, Inc.
+"""
+
+"""
+test_database.py is an independent test suite
+that tests the database api method calls
+"""
+import logging as LOG
+import unittest
+
+from common import constants as const
+
+import quantum.db.api as db
+import db.l2network_db as l2network_db
+
+
+LOG.getLogger(__name__)
+
+
+class L2networkDB(object):
+
+ """Class conisting of methods to call L2network db methods"""
+ def get_all_vlan_bindings(self):
+ """Get all vlan binding into a list of dict"""
+ vlans = []
+ try:
+ for vlan_bind in l2network_db.get_all_vlan_bindings():
+ LOG.debug("Getting vlan bindings for vlan: %s" %
+ vlan_bind.vlan_id)
+ vlan_dict = {}
+ vlan_dict["vlan-id"] = str(vlan_bind.vlan_id)
+ vlan_dict["net-id"] = str(vlan_bind.network_id)
+ vlans.append(vlan_dict)
+ except Exception, exc:
+ LOG.error("Failed to get all vlan bindings: %s" % str(exc))
+ return vlans
+
+ def get_vlan_binding(self, network_id):
+ """Get a vlan binding"""
+ vlan = []
+ try:
+ for vlan_bind in l2network_db.get_vlan_binding(network_id):
+ LOG.debug("Getting vlan binding for vlan: %s"
+ % vlan_bind.vlan_id)
+ vlan_dict = {}
+ vlan_dict["vlan-id"] = str(vlan_bind.vlan_id)
+ vlan_dict["net-id"] = str(vlan_bind.network_id)
+ vlan.append(vlan_dict)
+ except Exception, exc:
+ LOG.error("Failed to get vlan binding: %s" % str(exc))
+ return vlan
+
+ def create_vlan_binding(self, vlan_id, network_id):
+ """Create a vlan binding"""
+ vlan_dict = {}
+ try:
+ res = l2network_db.add_vlan_binding(vlan_id, network_id)
+ LOG.debug("Created vlan binding for vlan: %s" % res.vlan_id)
+ vlan_dict["vlan-id"] = str(res.vlan_id)
+ vlan_dict["net-id"] = str(res.network_id)
+ return vlan_dict
+ except Exception, exc:
+ LOG.error("Failed to create vlan binding: %s" % str(exc))
+
+ def delete_vlan_binding(self, network_id):
+ """Delete a vlan binding"""
+ try:
+ res = l2network_db.remove_vlan_binding(network_id)
+ LOG.debug("Deleted vlan binding for vlan: %s" % res.vlan_id)
+ vlan_dict = {}
+ vlan_dict["vlan-id"] = str(res.vlan_id)
+ return vlan_dict
+ except Exception, exc:
+ raise Exception("Failed to delete vlan binding: %s" % str(exc))
+
+ def update_vlan_binding(self, network_id, vlan_id):
+ """Update a vlan binding"""
+ try:
+ res = l2network_db.update_vlan_binding(network_id, vlan_id)
+ LOG.debug("Updating vlan binding for vlan: %s" % res.vlan_id)
+ vlan_dict = {}
+ vlan_dict["vlan-id"] = str(res.vlan_id)
+ vlan_dict["net-id"] = str(res.network_id)
+ return vlan_dict
+ except Exception, exc:
+ raise Exception("Failed to update vlan binding: %s" % str(exc))
+
+
+class QuantumDB(object):
+ """Class conisting of methods to call Quantum db methods"""
+ def get_all_networks(self, tenant_id):
+ """Get all networks"""
+ nets = []
+ try:
+ for net in db.network_list(tenant_id):
+ LOG.debug("Getting network: %s" % net.uuid)
+ net_dict = {}
+ net_dict["tenant-id"] = net.tenant_id
+ net_dict["net-id"] = str(net.uuid)
+ net_dict["net-name"] = net.name
+ nets.append(net_dict)
+ except Exception, exc:
+ LOG.error("Failed to get all networks: %s" % str(exc))
+ return nets
+
+ def get_network(self, network_id):
+ """Get a network"""
+ net = []
+ try:
+ for net in db.network_get(network_id):
+ LOG.debug("Getting network: %s" % net.uuid)
+ net_dict = {}
+ net_dict["tenant-id"] = net.tenant_id
+ net_dict["net-id"] = str(net.uuid)
+ net_dict["net-name"] = net.name
+ net.append(net_dict)
+ except Exception, exc:
+ LOG.error("Failed to get network: %s" % str(exc))
+ return net
+
+ def create_network(self, tenant_id, net_name):
+ """Create a network"""
+ net_dict = {}
+ try:
+ res = db.network_create(tenant_id,
+ net_name,
+ op_status="UP")
+ LOG.debug("Created network: %s" % res.uuid)
+ net_dict["tenant-id"] = res.tenant_id
+ net_dict["net-id"] = str(res.uuid)
+ net_dict["net-name"] = res.name
+ return net_dict
+ except Exception, exc:
+ LOG.error("Failed to create network: %s" % str(exc))
+
+ def delete_network(self, net_id):
+ """Delete a network"""
+ try:
+ net = db.network_destroy(net_id)
+ LOG.debug("Deleted network: %s" % net.uuid)
+ net_dict = {}
+ net_dict["net-id"] = str(net.uuid)
+ return net_dict
+ except Exception, exc:
+ raise Exception("Failed to delete port: %s" % str(exc))
+
+ def update_network(self, tenant_id, net_id, **kwargs):
+ """Update a network"""
+ try:
+ net = db.network_update(net_id, tenant_id, **kwargs)
+ LOG.debug("Updated network: %s" % net.uuid)
+ net_dict = {}
+ net_dict["net-id"] = str(net.uuid)
+ net_dict["net-name"] = net.name
+ return net_dict
+ except Exception, exc:
+ raise Exception("Failed to update network: %s" % str(exc))
+
+ def get_all_ports(self, net_id):
+ """Get all ports"""
+ ports = []
+ try:
+ for port in db.port_list(net_id):
+ LOG.debug("Getting port: %s" % port.uuid)
+ port_dict = {}
+ port_dict["port-id"] = str(port.uuid)
+ port_dict["net-id"] = str(port.network_id)
+ port_dict["int-id"] = port.interface_id
+ port_dict["state"] = port.state
+ port_dict["net"] = port.network
+ ports.append(port_dict)
+ return ports
+ except Exception, exc:
+ LOG.error("Failed to get all ports: %s" % str(exc))
+
+ def get_port(self, net_id, port_id):
+ """Get a port"""
+ port_list = []
+ port = db.port_get(net_id, port_id)
+ try:
+ LOG.debug("Getting port: %s" % port.uuid)
+ port_dict = {}
+ port_dict["port-id"] = str(port.uuid)
+ port_dict["net-id"] = str(port.network_id)
+ port_dict["int-id"] = port.interface_id
+ port_dict["state"] = port.state
+ port_list.append(port_dict)
+ return port_list
+ except Exception, exc:
+ LOG.error("Failed to get port: %s" % str(exc))
+
+ def create_port(self, net_id):
+ """Add a port"""
+ port_dict = {}
+ try:
+ port = db.port_create(net_id)
+ LOG.debug("Creating port %s" % port.uuid)
+ port_dict["port-id"] = str(port.uuid)
+ port_dict["net-id"] = str(port.network_id)
+ port_dict["int-id"] = port.interface_id
+ port_dict["state"] = port.state
+ return port_dict
+ except Exception, exc:
+ LOG.error("Failed to create port: %s" % str(exc))
+
+ def delete_port(self, net_id, port_id):
+ """Delete a port"""
+ try:
+ port = db.port_destroy(net_id, port_id)
+ LOG.debug("Deleted port %s" % port.uuid)
+ port_dict = {}
+ port_dict["port-id"] = str(port.uuid)
+ return port_dict
+ except Exception, exc:
+ raise Exception("Failed to delete port: %s" % str(exc))
+
+ def update_port(self, net_id, port_id, port_state):
+ """Update a port"""
+ try:
+ port = db.port_set_state(net_id, port_id, port_state)
+ LOG.debug("Updated port %s" % port.uuid)
+ port_dict = {}
+ port_dict["port-id"] = str(port.uuid)
+ port_dict["net-id"] = str(port.network_id)
+ port_dict["int-id"] = port.interface_id
+ port_dict["state"] = port.state
+ return port_dict
+ except Exception, exc:
+ raise Exception("Failed to update port state: %s" % str(exc))
+
+ def plug_interface(self, net_id, port_id, int_id):
+ """Plug interface to a port"""
+ try:
+ port = db.port_set_attachment(net_id, port_id, int_id)
+ LOG.debug("Attached interface to port %s" % port.uuid)
+ port_dict = {}
+ port_dict["port-id"] = str(port.uuid)
+ port_dict["net-id"] = str(port.network_id)
+ port_dict["int-id"] = port.interface_id
+ port_dict["state"] = port.state
+ return port_dict
+ except Exception, exc:
+ raise Exception("Failed to plug interface: %s" % str(exc))
+
+ def unplug_interface(self, net_id, port_id):
+ """Unplug interface to a port"""
+ try:
+ port = db.port_unset_attachment(net_id, port_id)
+ LOG.debug("Detached interface from port %s" % port.uuid)
+ port_dict = {}
+ port_dict["port-id"] = str(port.uuid)
+ port_dict["net-id"] = str(port.network_id)
+ port_dict["int-id"] = port.interface_id
+ port_dict["state"] = port.state
+ return port_dict
+ except Exception, exc:
+ raise Exception("Failed to unplug interface: %s" % str(exc))
+
+
+class L2networkDBTest(unittest.TestCase):
+ """Class conisting of L2network DB unit tests"""
+ def setUp(self):
+ """Setup for tests"""
+ l2network_db.initialize()
+ l2network_db.create_vlanids()
+ self.dbtest = L2networkDB()
+ self.quantum = QuantumDB()
+ LOG.debug("Setup")
+
+ def tearDown(self):
+ """Tear Down"""
+ db.clear_db()
+
+ def testa_create_vlanbinding(self):
+ """test add vlan binding"""
+ net1 = self.quantum.create_network("t1", "netid1")
+ vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"])
+ self.assertTrue(vlan1["vlan-id"] == "10")
+ self.teardown_vlanbinding()
+ self.teardown_network()
+
+ def testb_getall_vlanbindings(self):
+ """test get all vlan binding"""
+ net1 = self.quantum.create_network("t1", "netid1")
+ net2 = self.quantum.create_network("t1", "netid2")
+ vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"])
+ self.assertTrue(vlan1["vlan-id"] == "10")
+ vlan2 = self.dbtest.create_vlan_binding(20, net2["net-id"])
+ self.assertTrue(vlan2["vlan-id"] == "20")
+ vlans = self.dbtest.get_all_vlan_bindings()
+ self.assertTrue(len(vlans) == 2)
+ self.teardown_vlanbinding()
+ self.teardown_network()
+
+ def testc_delete_vlanbinding(self):
+ """test delete vlan binding"""
+ net1 = self.quantum.create_network("t1", "netid1")
+ vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"])
+ self.assertTrue(vlan1["vlan-id"] == "10")
+ self.dbtest.delete_vlan_binding(net1["net-id"])
+ vlans = self.dbtest.get_all_vlan_bindings()
+ count = 0
+ for vlan in vlans:
+ if vlan["vlan-id"] is "10":
+ count += 1
+ self.assertTrue(count == 0)
+ self.teardown_vlanbinding()
+ self.teardown_network()
+
+ def testd_update_vlanbinding(self):
+ """test update vlan binding"""
+ net1 = self.quantum.create_network("t1", "netid1")
+ vlan1 = self.dbtest.create_vlan_binding(10, net1["net-id"])
+ self.assertTrue(vlan1["vlan-id"] == "10")
+ vlan1 = self.dbtest.update_vlan_binding(net1["net-id"], 11)
+ self.assertTrue(vlan1["vlan-id"] == "11")
+ self.teardown_vlanbinding()
+ self.teardown_network()
+
+ def teste_test_vlanids(self):
+ """test vlanid methods"""
+ l2network_db.create_vlanids()
+ vlanids = l2network_db.get_all_vlanids()
+ self.assertTrue(len(vlanids) > 0)
+ vlanid = l2network_db.reserve_vlanid()
+ used = l2network_db.is_vlanid_used(vlanid)
+ self.assertTrue(used)
+ used = l2network_db.release_vlanid(vlanid)
+ self.assertFalse(used)
+ #counting on default teardown here to clear db
+
+ def teardown_network(self):
+ """tearDown Network table"""
+ LOG.debug("Tearing Down Network")
+ nets = self.quantum.get_all_networks("t1")
+ for net in nets:
+ netid = net["net-id"]
+ self.quantum.delete_network(netid)
+
+ def teardown_vlanbinding(self):
+ """tearDown VlanBinding table"""
+ LOG.debug("Tearing Down Vlan Binding")
+ vlans = self.dbtest.get_all_vlan_bindings()
+ for vlan in vlans:
+ netid = vlan["net-id"]
+ self.dbtest.delete_vlan_binding(netid)
init_path = 'etc/init.d'
ovs_plugin_config_path = 'etc/quantum/plugins/openvswitch'
cisco_plugin_config_path = 'etc/quantum/plugins/cisco'
+linuxbridge_plugin_config_path = 'etc/quantum/plugins/linuxbridge'
DataFiles = [
(config_path,
'etc/quantum/plugins/cisco/ucs.ini',
'etc/quantum/plugins/cisco/cisco_plugins.ini',
'etc/quantum/plugins/cisco/db_conn.ini']),
+ (linuxbridge_plugin_config_path,
+ ['etc/quantum/plugins/linuxbridge/linuxbridge_conf.ini']),
]
setup(