- Unit tests will use FakePlugin

- FakePlugin adapted to db API with sqlite
- db Models updated to inherit from generic Quantum Base model (provides utility functions and capabilities for treating db objects as dicts - see nova.db.models.NovaBase)
- functional tests commented out temporarily. Will un-comment when code for starting actual service is in place
This commit is contained in:
Salvatore Orlando 2011-06-29 16:40:15 +01:00
parent d0cb0eea78
commit a99caf43c1
4 changed files with 127 additions and 62 deletions

View File

@ -29,7 +29,7 @@ class Controller(common.QuantumController):
""" Network API controller for Quantum API """ """ Network API controller for Quantum API """
_network_ops_param_list = [{ _network_ops_param_list = [{
'param-name': 'network-name', 'param-name': 'net-name',
'required': True}, ] 'required': True}, ]
_serialization_metadata = { _serialization_metadata = {

View File

@ -72,9 +72,10 @@ def network_create(tenant_id, name):
net = None net = None
try: try:
net = session.query(models.Network).\ net = session.query(models.Network).\
filter_by(name=name).\ filter_by(name=name, tenant_id=tenant_id).\
one() one()
raise Exception("Network with name \"%s\" already exists" % name) raise Exception("Network with name %(name)s already " \
"exists for tenant %(tenant_id)s" % locals())
except exc.NoResultFound: except exc.NoResultFound:
with session.begin(): with session.begin():
net = models.Network(tenant_id, name) net = models.Network(tenant_id, name)
@ -154,6 +155,16 @@ def port_get(port_id):
raise Exception("No port found with id = %s " % port_id) raise Exception("No port found with id = %s " % port_id)
def port_set_state(port_id, new_state):
port = port_get(port_id)
if port:
session = get_session()
port.state = new_state
session.merge(port)
session.flush()
return port
def port_set_attachment(port_id, new_interface_id): def port_set_attachment(port_id, new_interface_id):
session = get_session() session = get_session()
ports = None ports = None

View File

@ -26,7 +26,61 @@ from sqlalchemy.orm import relation
BASE = declarative_base() BASE = declarative_base()
class Port(BASE): class QuantumBase(object):
"""Base class for Quantum Models."""
def save(self, session=None):
"""Save this object."""
if not session:
session = get_session()
session.add(self)
try:
session.flush()
except IntegrityError, e:
if str(e).endswith('is not unique'):
raise exception.Duplicate(str(e))
else:
raise
def delete(self, session=None):
"""Delete this object."""
self.deleted = True
self.deleted_at = utils.utcnow()
self.save(session=session)
def __setitem__(self, key, value):
setattr(self, key, value)
def __getitem__(self, key):
return getattr(self, key)
def get(self, key, default=None):
return getattr(self, key, default)
def __iter__(self):
self._i = iter(object_mapper(self).columns)
return self
def next(self):
n = self._i.next().name
return n, getattr(self, n)
def update(self, values):
"""Make the model object behave like a dict"""
for k, v in values.iteritems():
setattr(self, k, v)
def iteritems(self):
"""Make the model object behave like a dict.
Includes attributes from joins."""
local = dict(self)
joined = dict([(k, v) for k, v in self.__dict__.iteritems()
if not k[0] == '_'])
local.update(joined)
return local.iteritems()
class Port(BASE, QuantumBase):
"""Represents a port on a quantum network""" """Represents a port on a quantum network"""
__tablename__ = 'ports' __tablename__ = 'ports'
@ -34,17 +88,21 @@ class Port(BASE):
network_id = Column(String(255), ForeignKey("networks.uuid"), network_id = Column(String(255), ForeignKey("networks.uuid"),
nullable=False) nullable=False)
interface_id = Column(String(255)) interface_id = Column(String(255))
# Port state - Hardcoding string value at the moment
state = Column(String(8))
def __init__(self, network_id): def __init__(self, network_id):
self.uuid = uuid.uuid4() self.uuid = str(uuid.uuid4())
self.network_id = network_id self.network_id = network_id
self.state = "DOWN"
def __repr__(self): def __repr__(self):
return "<Port(%s,%s,%s)>" % (self.uuid, self.network_id, return "<Port(%s,%s,%s,%s)>" % (self.uuid, self.network_id,
self.interface_id) self.state,self.interface_id)
class Network(BASE): class Network(BASE, QuantumBase):
"""Represents a quantum network""" """Represents a quantum network"""
__tablename__ = 'networks' __tablename__ = 'networks'
@ -54,7 +112,7 @@ class Network(BASE):
ports = relation(Port, order_by=Port.uuid, backref="network") ports = relation(Port, order_by=Port.uuid, backref="network")
def __init__(self, tenant_id, name): def __init__(self, tenant_id, name):
self.uuid = uuid.uuid4() self.uuid = str(uuid.uuid4())
self.tenant_id = tenant_id self.tenant_id = tenant_id
self.name = name self.name = name

View File

@ -19,6 +19,7 @@
import logging import logging
from quantum.common import exceptions as exc from quantum.common import exceptions as exc
from quantum.db import api as db
LOG = logging.getLogger('quantum.plugins.SamplePlugin') LOG = logging.getLogger('quantum.plugins.SamplePlugin')
@ -258,35 +259,37 @@ class FakePlugin(object):
'net-ports': _port_dict_2}} 'net-ports': _port_dict_2}}
def __init__(self): def __init__(self):
FakePlugin._net_counter = len(FakePlugin._networks) db_options = {"sql_connection": "sqlite:///fake_plugin.sqllite"}
db.configure_db(db_options)
FakePlugin._net_counter = 0
def _get_network(self, tenant_id, network_id): def _get_network(self, tenant_id, network_id):
network = FakePlugin._networks.get(network_id) network = db.network_get(network_id)
if not network: if not network:
raise exc.NetworkNotFound(net_id=network_id) raise exc.NetworkNotFound(net_id=network_id)
return network return network
def _get_port(self, tenant_id, network_id, port_id): def _get_port(self, tenant_id, network_id, port_id):
net = self._get_network(tenant_id, network_id) net = self._get_network(tenant_id, network_id)
port = net['net-ports'].get(int(port_id)) port = db.port_get(port_id)
if not port: # Port must exist and belong to the appropriate network.
if not port or port['network_id']!=net['uuid']:
raise exc.PortNotFound(net_id=network_id, port_id=port_id) raise exc.PortNotFound(net_id=network_id, port_id=port_id)
return port return port
def _validate_port_state(self, port_state): def _validate_port_state(self, port_state):
if port_state.upper() not in ('UP', 'DOWN'): if port_state.upper() not in ('ACTIVE', 'DOWN'):
raise exc.StateInvalid(port_state=port_state) raise exc.StateInvalid(port_state=port_state)
return True return True
def _validate_attachment(self, tenant_id, network_id, port_id, def _validate_attachment(self, tenant_id, network_id, port_id,
remote_interface_id): remote_interface_id):
network = self._get_network(tenant_id, network_id) for port in db.port_list(network_id):
for port in network['net-ports'].values(): if port['interface_id'] == remote_interface_id:
if port['attachment'] == remote_interface_id:
raise exc.AlreadyAttached(net_id=network_id, raise exc.AlreadyAttached(net_id=network_id,
port_id=port_id, port_id=port_id,
att_id=port['attachment'], att_id=port['interface_id'],
att_port_id=port['port-id']) att_port_id=port['uuid'])
def get_all_networks(self, tenant_id): def get_all_networks(self, tenant_id):
""" """
@ -295,14 +298,19 @@ class FakePlugin(object):
the specified tenant. the specified tenant.
""" """
LOG.debug("FakePlugin.get_all_networks() called") LOG.debug("FakePlugin.get_all_networks() called")
return FakePlugin._networks.values() nets = []
for net in db.network_list(tenant_id):
net_item = {'net-id':str(net.uuid),
'net-name':net.name}
nets.append(net_item)
return nets
def get_network_details(self, tenant_id, net_id): def get_network_details(self, tenant_id, net_id):
""" """
retrieved a list of all the remote vifs that retrieved a list of all the remote vifs that
are attached to the network are attached to the network
""" """
LOG.debug("get_network_details() called") LOG.debug("FakePlugin.get_network_details() called")
return self._get_network(tenant_id, net_id) return self._get_network(tenant_id, net_id)
def create_network(self, tenant_id, net_name): def create_network(self, tenant_id, net_name):
@ -311,15 +319,9 @@ class FakePlugin(object):
a symbolic name. a symbolic name.
""" """
LOG.debug("FakePlugin.create_network() called") LOG.debug("FakePlugin.create_network() called")
FakePlugin._net_counter += 1 new_net = db.network_create(tenant_id, net_name)
new_net_id = ("0" * (3 - len(str(FakePlugin._net_counter)))) + \ # Return uuid for newly created network as net-id.
str(FakePlugin._net_counter) return {'net-id': new_net['uuid']}
new_net_dict = {'net-id': new_net_id,
'net-name': net_name,
'net-ports': {}}
FakePlugin._networks[new_net_id] = new_net_dict
# return network_id of the created network
return new_net_dict
def delete_network(self, tenant_id, net_id): def delete_network(self, tenant_id, net_id):
""" """
@ -327,14 +329,14 @@ class FakePlugin(object):
belonging to the specified tenant. belonging to the specified tenant.
""" """
LOG.debug("FakePlugin.delete_network() called") LOG.debug("FakePlugin.delete_network() called")
net = FakePlugin._networks.get(net_id) net = self._get_network(tenant_id, net_id)
# Verify that no attachments are plugged into the network # Verify that no attachments are plugged into the network
if net: if net:
if net['net-ports']: if net['net-ports']:
for port in net['net-ports'].values(): for port in db.port_list(net_id):
if port['attachment']: if port['interface-id']:
raise exc.NetworkInUse(net_id=net_id) raise exc.NetworkInUse(net_id=net_id)
FakePlugin._networks.pop(net_id) db.network_destroy(net_id)
return net return net
# Network not found # Network not found
raise exc.NetworkNotFound(net_id=net_id) raise exc.NetworkNotFound(net_id=net_id)
@ -345,8 +347,8 @@ class FakePlugin(object):
Virtual Network. Virtual Network.
""" """
LOG.debug("FakePlugin.rename_network() called") LOG.debug("FakePlugin.rename_network() called")
db.network_rename(net_id, tenant_id, new_name)
net = self._get_network(tenant_id, net_id) net = self._get_network(tenant_id, net_id)
net['net-name'] = new_name
return net return net
def get_all_ports(self, tenant_id, net_id): def get_all_ports(self, tenant_id, net_id):
@ -355,9 +357,12 @@ class FakePlugin(object):
specified Virtual Network. specified Virtual Network.
""" """
LOG.debug("FakePlugin.get_all_ports() called") LOG.debug("FakePlugin.get_all_ports() called")
network = self._get_network(tenant_id, net_id) port_ids = []
ports_on_net = network['net-ports'].values() ports = db.port_list(net_id)
return ports_on_net for x in ports:
d = {'port-id':str(x.uuid)}
port_ids.append(d)
return port_ids
def get_port_details(self, tenant_id, net_id, port_id): def get_port_details(self, tenant_id, net_id, port_id):
""" """
@ -372,30 +377,19 @@ class FakePlugin(object):
Creates a port on the specified Virtual Network. Creates a port on the specified Virtual Network.
""" """
LOG.debug("FakePlugin.create_port() called") LOG.debug("FakePlugin.create_port() called")
net = self._get_network(tenant_id, net_id) port = db.port_create(net_id)
# check port state port_item = {'port-id':str(port.uuid)}
# TODO(salvatore-orlando): Validate port state in API? return port_item
self._validate_port_state(port_state)
ports = net['net-ports']
if len(ports.keys()) == 0:
new_port_id = 1
else:
new_port_id = max(ports.keys()) + 1
new_port_dict = {'port-id': new_port_id,
'port-state': port_state,
'attachment': None}
ports[new_port_id] = new_port_dict
return new_port_dict
def update_port(self, tenant_id, net_id, port_id, port_state): def update_port(self, tenant_id, net_id, port_id, new_state):
""" """
Updates the state of a port on the specified Virtual Network. Updates the state of a port on the specified Virtual Network.
""" """
port=self._get_port(tenant_id, net_id, port_id)
LOG.debug("FakePlugin.update_port() called") LOG.debug("FakePlugin.update_port() called")
port = self._get_port(tenant_id, net_id, port_id)
self._validate_port_state(port_state) self._validate_port_state(port_state)
port['port-state'] = port_state db.port_set_state(new_state)
return port return
def delete_port(self, tenant_id, net_id, port_id): def delete_port(self, tenant_id, net_id, port_id):
""" """
@ -411,9 +405,12 @@ class FakePlugin(object):
raise exc.PortInUse(net_id=net_id, port_id=port_id, raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port['attachment']) att_id=port['attachment'])
try: try:
net['net-ports'].pop(int(port_id)) port = db.port_destroy(port_id)
except KeyError: except Exception, e:
raise exc.PortNotFound(net_id=net_id, port_id=port_id) raise Exception("Failed to delete port: %s" % str(e))
d = {}
d["port-id"] = str(port.uuid)
return d
def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id): def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
""" """
@ -428,7 +425,7 @@ class FakePlugin(object):
if port['attachment']: if port['attachment']:
raise exc.PortInUse(net_id=net_id, port_id=port_id, raise exc.PortInUse(net_id=net_id, port_id=port_id,
att_id=port['attachment']) att_id=port['attachment'])
port['attachment'] = remote_interface_id db.port_set_attachment(port_id, remote_interface_id)
def unplug_interface(self, tenant_id, net_id, port_id): def unplug_interface(self, tenant_id, net_id, port_id):
""" """
@ -436,7 +433,6 @@ class FakePlugin(object):
specified Virtual Network. specified Virtual Network.
""" """
LOG.debug("FakePlugin.unplug_interface() called") LOG.debug("FakePlugin.unplug_interface() called")
port = self._get_port(tenant_id, net_id, port_id)
# TODO(salvatore-orlando): # TODO(salvatore-orlando):
# Should unplug on port without attachment raise an Error? # Should unplug on port without attachment raise an Error?
port['attachment'] = None db.port_set_attachment(port_id, None)