Initial cut of openvswitch plugin

This commit is contained in:
Brad Hall 2011-06-03 20:56:32 -07:00
parent 84bed05e11
commit 46a57d2c11
10 changed files with 787 additions and 0 deletions

View File

@ -0,0 +1,30 @@
QUANTUM_PATH=../../../
# TODO(bgh): DIST_DIR and target for plugin
AGENT_DIST_DIR=ovs_quantum_agent
AGENT_DIST_TARBALL=ovs_quantum_agent.tgz
agent-dist: distclean
mkdir $(AGENT_DIST_DIR)
cp agent/*.py $(AGENT_DIST_DIR)
cp agent/*.sh $(AGENT_DIST_DIR)
cp README $(AGENT_DIST_DIR)
cp ovs_quantum_plugin.ini $(AGENT_DIST_DIR)
tar -zcvf $(AGENT_DIST_TARBALL) $(AGENT_DIST_DIR)/
@echo "Agent tarball created: $(AGENT_DIST_TARBALL)"
@echo "See README for installation details"
all:
clean:
$(find . -name *.pyc | xargs rm)
distclean:
-rm -rf $(AGENT_DIST_DIR)
-rm -f $(AGENT_DIST_TARBALL)
check:
PYTHONPATH=$(QUANTUM_PATH):. python ovs_quantum_plugin.py
PHONY: agent-dist check clean distclean

View File

@ -0,0 +1,37 @@
To Run:
1) On the "Openstack Controller" host:
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 ovs_naas"
2) Edit the configuration file (src/ovs/plugins/ovs_quantum_plugin.ini)
- Make sure it matches your mysql configuration. This file must be updated
with the addresses and credentials to access the database.
3) Create the agent distribution tarball
$ make agent-dist
4) Copy the resulting tarball to your xenserver
5) Unpack the tarball and run install.sh. This will install all of the
necessary pieces into /etc/xapi.d/plugins.
6) Run the agent (example below):
# /etc/xapi.d/plugins/ovs_quantum_agent.py /etc/xapi.d/plugins/ovs_quantum_plugin.ini
7) Run ovs_quantum_plugin.py via the quantum plugin framework cli.
- Edit quantum/plugins.ini to point to where the plugin and configuration
files live
$ PYTHONPATH=$HOME/src/quantum-framework/quantum:$PYTHONPATH python quantum/cli.py
This will show all of the available commands.

View File

View File

@ -0,0 +1,38 @@
#!/bin/bash
CONF_FILE=/etc/xapi.d/plugins/ovs_quantum_plugin.ini
if [ ! -d /etc/xapi.d/plugins ]; then
echo "Am I on a xenserver? I can't find the plugins directory!"
exit 1
fi
# Make sure we have mysql-python
rpm -qa | grep MYyQL-python >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "MySQL-python not found; installing."
yum -y install MySQL-python
if [ $? -ne 0 ]; then
echo "Failed to install MYSQL-python; agent will not work."
exit 1
fi
fi
cp ovs_quantum_agent.py /etc/xapi.d/plugins
cp ovs_quantum_plugin.ini /etc/xapi.d/plugins
cp set_external_ids.sh /etc/xapi.d/plugins
xe network-list name-label="integration-bridge" | grep xapi >/dev/null 2>&1
if [ $? -ne 0 ]; then
echo "No integration bridge found. Creating."
xe network-create name-label="integration-bridge"
fi
BR=$(xe network-list name-label="integration-bridge" | grep "bridge.*:" | awk '{print $4}')
CONF_BR=$(grep integration-bridge ${CONF_FILE} | cut -d= -f2)
if [ "X$BR" != "X$CONF_BR" ]; then
echo "Integration bridge doesn't match configuration file; fixing."
sed -i -e "s/^integration-bridge =.*$/integration-bridge = ${BR}/g" $CONF_FILE
fi
echo "Make sure to edit: $CONF_FILE"

View File

@ -0,0 +1,272 @@
#!/usr/bin/env python
import ConfigParser
import logging as LOG
import MySQLdb
import os
import sys
import time
from optparse import OptionParser
from subprocess import *
# A class to represent a VIF (i.e., a port that has 'iface-id' and 'vif-mac'
# attributes set).
class VifPort:
def __init__(self, port_name, ofport, vif_id, vif_mac, switch):
self.port_name = port_name
self.ofport = ofport
self.vif_id = vif_id
self.vif_mac = vif_mac
self.switch = switch
def __str__(self):
return "iface-id=" + self.vif_id + ", vif_mac=" + \
self.vif_mac + ", port_name=" + self.port_name + \
", ofport=" + self.ofport + ", bridge name = " + self.switch.br_name
class OVSBridge:
def __init__(self, br_name):
self.br_name = br_name
def run_cmd(self, args):
# LOG.debug("## running command: " + " ".join(args))
return Popen(args, stdout=PIPE).communicate()[0]
def run_vsctl(self, args):
full_args = ["ovs-vsctl" ] + args
return self.run_cmd(full_args)
def reset_bridge(self):
self.run_vsctl([ "--" , "--if-exists", "del-br", self.br_name])
self.run_vsctl(["add-br", self.br_name])
def delete_port(self, port_name):
self.run_vsctl([ "--" , "--if-exists", "del-port", self.br_name,
port_name])
def set_db_attribute(self, table_name, record, column, value):
args = [ "set", table_name, record, "%s=%s" % (column,value) ]
self.run_vsctl(args)
def clear_db_attribute(self, table_name,record, column):
args = [ "clear", table_name, record, column ]
self.run_vsctl(args)
def run_ofctl(self, cmd, args):
full_args = ["ovs-ofctl", cmd, self.br_name ] + args
return self.run_cmd(full_args)
def remove_all_flows(self):
self.run_ofctl("del-flows", [])
def get_port_ofport(self, port_name):
return self.db_get_val("Interface", port_name, "ofport")
def add_flow(self,**dict):
if "actions" not in dict:
raise Exception("must specify one or more actions")
if "priority" not in dict:
dict["priority"] = "0"
flow_str = "priority=%s" % dict["priority"]
if "match" in dict:
flow_str += "," + dict["match"]
flow_str += ",actions=%s" % (dict["actions"])
self.run_ofctl("add-flow", [ flow_str ] )
def delete_flows(self,**dict):
all_args = []
if "priority" in dict:
all_args.append("priority=%s" % dict["priority"])
if "match" in dict:
all_args.append(dict["match"])
if "actions" in dict:
all_args.append("actions=%s" % (dict["actions"]))
flow_str = ",".join(all_args)
self.run_ofctl("del-flows", [ flow_str ] )
def db_get_map(self, table, record, column):
str = self.run_vsctl([ "get" , table, record, column ]).rstrip("\n\r")
return self.db_str_to_map(str)
def db_get_val(self, table, record, column):
return self.run_vsctl([ "get" , table, record, column ]).rstrip("\n\r")
def db_str_to_map(self, full_str):
list = full_str.strip("{}").split(", ")
ret = {}
for e in list:
if e.find("=") == -1:
continue
arr = e.split("=")
ret[arr[0]] = arr[1].strip("\"")
return ret
def get_port_name_list(self):
res = self.run_vsctl([ "list-ports", self.br_name])
return res.split("\n")[0:-1]
def get_port_stats(self, port_name):
return self.db_get_map("Interface", port_name, "statistics")
# returns a VIF object for each VIF port
def get_vif_ports(self):
edge_ports = []
port_names = self.get_port_name_list()
for name in port_names:
external_ids = self.db_get_map("Interface",name,"external_ids")
if "iface-id" in external_ids and "attached-mac" in external_ids:
ofport = self.db_get_val("Interface",name,"ofport")
p = VifPort(name, ofport, external_ids["iface-id"],
external_ids["attached-mac"], self)
edge_ports.append(p)
else:
# iface-id might not be set. See if we can figure it out and
# set it here.
external_ids = self.db_get_map("Interface",name,"external_ids")
if "attached-mac" not in external_ids:
continue
vif_uuid = external_ids.get("xs-vif-uuid", "")
if len(vif_uuid) == 0:
continue
LOG.debug("iface-id not set, got vif-uuid: %s" % vif_uuid)
res = os.popen("xe vif-param-get param-name=other-config uuid=%s | grep nicira-iface-id | awk '{print $2}'" % vif_uuid).readline()
res = res.strip()
if len(res) == 0:
continue
external_ids["iface-id"] = res
LOG.info("Setting interface \"%s\" iface-id to \"%s\"" % (name, res))
self.set_db_attribute("Interface", name,
"external-ids:iface-id", res)
ofport = self.db_get_val("Interface",name,"ofport")
p = VifPort(name, ofport, external_ids["iface-id"],
external_ids["attached-mac"], self)
edge_ports.append(p)
return edge_ports
class OVSNaaSPlugin:
def __init__(self, integ_br):
self.setup_integration_br(integ_br)
def port_bound(self, port, vlan_id):
self.int_br.set_db_attribute("Port", port.port_name,"tag",
str(vlan_id))
def port_unbound(self, port, still_exists):
if still_exists:
self.int_br.clear_db_attribute("Port", port.port_name,"tag")
def setup_integration_br(self, integ_br):
self.int_br = OVSBridge(integ_br)
self.int_br.remove_all_flows()
# drop all traffic on the 'dead vlan'
self.int_br.add_flow(priority=2, match="dl_vlan=4095", actions="drop")
# switch all other traffic using L2 learning
self.int_br.add_flow(priority=1, actions="normal")
# FIXME send broadcast everywhere, regardless of tenant
#int_br.add_flow(priority=3, match="dl_dst=ff:ff:ff:ff:ff:ff", actions="normal")
def daemon_loop(self, conn):
self.local_vlan_map = {}
old_local_bindings = {}
old_vif_ports = {}
while True:
cursor = conn.cursor()
cursor.execute("SELECT * FROM network_bindings")
rows = cursor.fetchall()
cursor.close()
all_bindings = {}
for r in rows:
all_bindings[r[2]] = r[1]
cursor = conn.cursor()
cursor.execute("SELECT * FROM vlan_bindings")
rows = cursor.fetchall()
cursor.close()
vlan_bindings = {}
for r in rows:
vlan_bindings[r[1]] = r[0]
new_vif_ports = {}
new_local_bindings = {}
vif_ports = self.int_br.get_vif_ports()
for p in vif_ports:
new_vif_ports[p.vif_id] = p
if p.vif_id in all_bindings:
new_local_bindings[p.vif_id] = all_bindings[p.vif_id]
else:
# no binding, put him on the 'dead vlan'
self.int_br.set_db_attribute("Port", p.port_name, "tag",
"4095")
old_b = old_local_bindings.get(p.vif_id,None)
new_b = new_local_bindings.get(p.vif_id,None)
if old_b != new_b:
if old_b is not None:
LOG.info("Removing binding to net-id = %s for %s"
% (old_b, str(p)))
self.port_unbound(p, True)
if new_b is not None:
LOG.info("Adding binding to net-id = %s for %s" \
% (new_b, str(p)))
# If we don't have a binding we have to stick it on
# the dead vlan
vlan_id = vlan_bindings.get(all_bindings[p.vif_id],
"4095")
self.port_bound(p, vlan_id)
for vif_id in old_vif_ports.keys():
if vif_id not in new_vif_ports:
LOG.info("Port Disappeared: %s" % vif_id)
if vif_id in old_local_bindings:
old_b = old_local_bindings[vif_id]
self.port_unbound(old_vif_ports[vif_id], False)
old_vif_ports = new_vif_ports
old_local_bindings = new_local_bindings
self.int_br.run_cmd(["bash",
"/etc/xapi.d/plugins/set_external_ids.sh"])
time.sleep(2)
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()
try:
config.read(config_file)
except Exception, e:
LOG.error("Unable to parse config file \"%s\": %s" % (config_file,
str(e)))
integ_br = config.get("OVS", "integration-bridge")
db_name = config.get("DATABASE", "name")
db_user = config.get("DATABASE", "user")
db_pass = config.get("DATABASE", "pass")
db_host = config.get("DATABASE", "host")
conn = None
try:
LOG.info("Connecting to database \"%s\" on %s" % (db_name, db_host))
conn = MySQLdb.connect(host=db_host, user=db_user,
passwd=db_pass, db=db_name)
plugin = OVSNaaSPlugin(integ_br)
plugin.daemon_loop(conn)
finally:
if conn:
conn.close()
sys.exit(0)

View File

@ -0,0 +1,15 @@
VIFLIST=`xe vif-list params=uuid --minimal | sed s/,/" "/g`
for VIF_UUID in $VIFLIST; do
DEVICE_NUM=`xe vif-list params=device uuid=$VIF_UUID --minimal`
VM_NAME=`xe vif-list params=vm-name-label uuid=$VIF_UUID --minimal`
NAME="$VM_NAME-eth$DEVICE_NUM"
echo "Vif: $VIF_UUID is '$NAME'"
xe vif-param-set uuid=$VIF_UUID other-config:nicira-iface-id="$NAME"
done
ps auxw | grep -v grep | grep ovs-xapi-sync > /dev/null 2>&1
if [ $? -eq 0 ]; then
killall -HUP ovs-xapi-sync
fi

View File

@ -0,0 +1,52 @@
from sqlalchemy.orm import exc
import quantum.db.api as db
import quantum.db.models as models
import ovs_models
def get_vlans():
session = db.get_session()
try:
bindings = session.query(ovs_models.VlanBinding).\
all()
except exc.NoResultFound:
return []
res = []
for x in bindings:
res.append((x.vlan_id, x.network_id))
return res
def add_vlan_binding(vlanid, netid):
session = db.get_session()
binding = ovs_models.VlanBinding(vlanid, netid)
session.add(binding)
session.flush()
return binding.vlan_id
def remove_vlan_binding(netid):
session = db.get_session()
try:
binding = session.query(ovs_models.VlanBinding).\
filter_by(network_id=netid).\
one()
session.delete(binding)
except exc.NoResultFound:
pass
session.flush()
def update_network_binding(netid, ifaceid):
session = db.get_session()
# Add to or delete from the bindings table
if ifaceid == None:
try:
binding = session.query(ovs_models.NetworkBinding).\
filter_by(network_id=netid).\
one()
session.delete(binding)
except exc.NoResultFound:
raise Exception("No binding found with network_id = %s" % netid)
else:
binding = ovs_models.NetworkBinding(netid, ifaceid)
session.add(binding)
session.flush()

View File

@ -0,0 +1,38 @@
import uuid
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relation
from quantum.db.models import BASE
class NetworkBinding(BASE):
"""Represents a binding of network_id, vif_id"""
__tablename__ = 'network_bindings'
id = Column(Integer, primary_key=True, autoincrement=True)
network_id = Column(String(255))
vif_id = Column(String(255))
def __init__(self, network_id, vif_id):
self.network_id = network_id
self.vif_id = vif_id
def __repr__(self):
return "<NetworkBinding(%s,%s)>" % \
(self.network_id, self.vif_id)
class VlanBinding(BASE):
"""Represents a binding of network_id, vlan_id"""
__tablename__ = 'vlan_bindings'
vlan_id = Column(Integer, primary_key=True)
network_id = Column(String(255))
def __init__(self, vlan_id, network_id):
self.network_id = network_id
self.vlan_id = vlan_id
def __repr__(self):
return "<VlanBinding(%s,%s)>" % \
(self.vlan_id, self.network_id)

View File

@ -0,0 +1,9 @@
[DATABASE]
name = ovs_naas
user = root
pass = foobar
host = 127.0.0.1
port = 3306
[OVS]
integration-bridge = xapi1

View File

@ -0,0 +1,296 @@
import ConfigParser
import logging as LOG
import os
import sys
import unittest
from quantum.quantum_plugin_base import QuantumPluginBase
import quantum.db.api as db
import ovs_db
# TODO(bgh): Make sure we delete from network bindings when deleting a port,
# network, etc.
CONF_FILE="ovs_quantum_plugin.ini"
LOG.basicConfig(level=LOG.DEBUG)
LOG.getLogger("ovs_quantum_plugin")
def find_config(basepath):
for root, dirs, files in os.walk(basepath):
if CONF_FILE in files:
return os.path.join(root, CONF_FILE)
return None
class VlanMap(object):
vlans = {}
def __init__(self):
for x in xrange(2, 4094):
self.vlans[x] = None
def set(self, vlan_id, network_id):
self.vlans[vlan_id] = network_id
def acquire(self, network_id):
for x in xrange(2, 4094):
if self.vlans[x] == None:
self.vlans[x] = network_id
# LOG.debug("VlanMap::acquire %s -> %s" % (x, network_id))
return x
raise Exception("No free vlans..")
def get(self, vlan_id):
return self.vlans[vlan_id]
def release(self, network_id):
for x in self.vlans.keys():
if self.vlans[x] == network_id:
self.vlans[x] = None
# LOG.debug("VlanMap::release %s" % (x))
return
raise Exception("No vlan found with network \"%s\"" % network_id)
class OVSQuantumPlugin(QuantumPluginBase):
def __init__(self, configfile=None):
config = ConfigParser.ConfigParser()
if configfile == None:
if os.path.exists(CONF_FILE):
configfile = CONF_FILE
else:
configfile = find_config(os.path.abspath(os.path.dirname(__file__)))
if configfile == None:
raise Exception("Configuration file \"%s\" doesn't exist" %
(configfile))
LOG.info("Using configuration file: %s" % configfile)
config.read(configfile)
LOG.debug("Config: %s" % config)
DB_NAME = config.get("DATABASE", "name")
DB_USER = config.get("DATABASE", "user")
DB_PASS = config.get("DATABASE", "pass")
DB_HOST = config.get("DATABASE", "host")
options = {"sql_connection": "mysql://%s:%s@%s/%s" % (DB_USER,
DB_PASS, DB_HOST, DB_NAME)}
db.configure_db(options)
self.vmap = VlanMap()
# Populate the map with anything that is already present in the
# database
vlans = ovs_db.get_vlans()
for x in vlans:
vlan_id, network_id = x
# LOG.debug("Adding already populated vlan %s -> %s" % (vlan_id, network_id))
self.vmap.set(vlan_id, network_id)
def get_all_networks(self, tenant_id):
nets = []
for x in db.network_list(tenant_id):
LOG.debug("Adding network: %s" % x.uuid)
d = {}
d["net-id"] = str(x.uuid)
d["net-name"] = x.name
nets.append(d)
return nets
def create_network(self, tenant_id, net_name):
d = {}
try:
res = db.network_create(tenant_id, net_name)
LOG.debug("Created newtork: %s" % res)
except Exception, e:
LOG.error("Error: %s" % str(e))
return d
d["net-id"] = str(res.uuid)
d["net-name"] = res.name
vlan_id = self.vmap.acquire(str(res.uuid))
ovs_db.add_vlan_binding(vlan_id, str(res.uuid))
return d
def delete_network(self, tenant_id, net_id):
net = db.network_destroy(net_id)
d = {}
d["net-id"] = net.uuid
ovs_db.remove_vlan_binding(net_id)
self.vmap.release(net_id)
return d
def get_network_details(self, tenant_id, net_id):
network = db.network_get(net_id)
d = {}
d["net-id"] = str(network.uuid)
d["net-name"] = network.name
d["net-ports"] = self.get_all_ports(tenant_id, net_id)
return d
def rename_network(self, tenant_id, net_id, new_name):
try:
net = db.network_rename(net_id, tenant_id, new_name)
except Exception, e:
raise Exception("Failed to rename network: %s" % str(e))
d = {}
d["net-id"] = str(net.uuid)
d["net-name"] = net.name
return d
def get_all_ports(self, tenant_id, net_id):
ids = []
ports = db.port_list(net_id)
for x in ports:
LOG.debug("Appending port: %s" % x.uuid)
d = {}
d["port-id"] = str(x.uuid)
ids.append(d)
return ids
def create_port(self, tenant_id, net_id, port_state=None):
LOG.debug("Creating port with network_id: %s" % net_id)
port = db.port_create(net_id)
d = {}
d["port-id"] = str(port.uuid)
LOG.debug("-> %s" % (port.uuid))
return d
def delete_port(self, tenant_id, net_id, port_id):
try:
port = db.port_destroy(port_id)
except Exception, e:
raise Exception("Failed to delete port: %s" % str(e))
d = {}
d["port-id"] = str(port.uuid)
return d
def update_port(self, tenant_id, net_id, port_id, port_state):
"""
Updates the state of a port on the specified Virtual Network.
"""
LOG.debug("update_port() called\n")
port = db.port_get(port_id)
port['port-state'] = port_state
return port
def get_port_details(self, tenant_id, net_id, port_id):
port = db.port_get(port_id)
rv = {"port-id": port.uuid, "attachment": port.interface_id,
"net-id": port.network_id, "port-state": "UP"}
return rv
def get_all_attached_interfaces(self, tenant_id, net_id):
ports = db.port_list(net_id)
ifaces = []
for p in ports:
ifaces.append(p.interface_id)
return ifaces
def plug_interface(self, tenant_id, net_id, port_id, remote_iface_id):
db.port_set_attachment(port_id, remote_iface_id)
ovs_db.update_network_binding(net_id, remote_iface_id)
def unplug_interface(self, tenant_id, net_id, port_id):
db.port_set_attachment(port_id, "None")
ovs_db.update_network_binding(net_id, remote_iface_id)
def get_interface_details(self, tenant_id, net_id, port_id):
res = db.port_get(port_id)
return res.interface_id
class VlanMapTest(unittest.TestCase):
def setUp(self):
self.vmap = VlanMap()
def tearDown(self):
pass
def testAddVlan(self):
vlan_id = self.vmap.acquire("foobar")
self.assertTrue(vlan_id == 2)
def testReleaseVlan(self):
vlan_id = self.vmap.acquire("foobar")
self.vmap.release("foobar")
self.assertTrue(self.vmap.get(vlan_id) == None)
# TODO(bgh): Make the tests use a sqlite database instead of mysql
class OVSPluginTest(unittest.TestCase):
def setUp(self):
self.quantum = OVSQuantumPlugin()
self.tenant_id = "testtenant"
def testCreateNetwork(self):
net1 = self.quantum.create_network(self.tenant_id, "plugin_test1")
self.assertTrue(net1["net-name"] == "plugin_test1")
def testGetNetworks(self):
net1 = self.quantum.create_network(self.tenant_id, "plugin_test1")
net2 = self.quantum.create_network(self.tenant_id, "plugin_test2")
nets = self.quantum.get_all_networks(self.tenant_id)
count = 0
for x in nets:
print x
if "plugin_test" in x["net-name"]:
count += 1
self.assertTrue(count == 2)
def testDeleteNetwork(self):
net = self.quantum.create_network(self.tenant_id, "plugin_test1")
self.quantum.delete_network(self.tenant_id, net["net-id"])
nets = self.quantum.get_all_networks(self.tenant_id)
count = 0
for x in nets:
print x
if "plugin_test" in x["net-name"]:
count += 1
self.assertTrue(count == 0)
def testRenameNetwork(self):
net = self.quantum.create_network(self.tenant_id, "plugin_test1")
net = self.quantum.rename_network(self.tenant_id, net["net-id"],
"plugin_test_renamed")
self.assertTrue(net["net-name"] == "plugin_test_renamed")
def testCreatePort(self):
net1 = self.quantum.create_network(self.tenant_id, "plugin_test1")
port = self.quantum.create_port(self.tenant_id, net1["net-id"])
ports = self.quantum.get_all_ports(self.tenant_id, net1["net-id"])
count = 0
for p in ports:
count += 1
self.assertTrue(count == 1)
def testDeletePort(self):
pass
def testGetPorts(self):
pass
def testPlugInterface(self):
pass
def testUnPlugInterface(self):
pass
def tearDown(self):
networks = self.quantum.get_all_networks(self.tenant_id)
print networks
# Clean up any test networks lying around
for net in networks:
id = net["net-id"]
name = net["net-name"]
if "plugin_test" in name:
# Clean up any test ports lying around
ports = self.quantum.get_all_ports(self.tenant_id, id)
print ports
for p in ports:
self.quantum.delete_port(self.tenant_id, id, p["port-id"])
self.quantum.delete_network(self.tenant_id, id)
if __name__ == "__main__":
suite = unittest.TestLoader().loadTestsFromTestCase(OVSPluginTest)
unittest.TextTestRunner(verbosity=2).run(suite)
suite = unittest.TestLoader().loadTestsFromTestCase(VlanMapTest)
unittest.TextTestRunner(verbosity=2).run(suite)
# TODO(bgh) move to unit tets
if False:
quantum.plug_interface(tenant_id, net1, port, "vif1.1")
portdetails = quantum.get_port_details(tenant_id, net1, port)
LOG.DEBUG(portdetails)
LOG.info("=== PORT: %s" % quantum.get_port_details(tenant_id, net1, port))
assert(portdetails["interface_id"] == "vif1.1")
networks = quantum.get_all_networks(tenant_id)
LOG.debug(networks)
for nid, name in networks.iteritems():
ports = quantum.get_all_ports(tenant_id, nid)
LOG.debug(ports)