]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
- added network and port models into the l2network plugin instead of using quantum...
authorrohitagarwalla <roagarwa@cisco.com>
Wed, 10 Aug 2011 20:01:35 +0000 (13:01 -0700)
committerrohitagarwalla <roagarwa@cisco.com>
Wed, 10 Aug 2011 20:01:35 +0000 (13:01 -0700)
- added api methods for network and ports
- restructured code to use the l2network network and port
- added l2network base class for other tables to inherit
- added support for l2network plugin model objects to behave like dictionary (gets rid of code to convert objects into dictionaries)
- added foreign key constraints to l2network plugin model attributes representing columns
- added attributes to represent relation between models in l2network plugin
- added joinedload only to network and port (need to to for others)
- added InnoDB as the storage medium in base table for imposing foreign keys
- updated l2network test cases to handle foreign key constraints

quantum/plugins/cisco/db/l2network_db.py
quantum/plugins/cisco/db/l2network_models.py

index 15861eee3015ac9de7b74de76d47ff247511810f..ce48b42a233bb3b9982a9eba321ac0fa934c8532 100644 (file)
 #    under the License.
 # @author: Rohit Agarwalla, Cisco Systems, Inc.
 
-from sqlalchemy.orm import exc
+from sqlalchemy import create_engine
+from sqlalchemy.orm import sessionmaker, exc, joinedload
 
 import l2network_models
-import quantum.db.api as db
-import quantum.db.models as models
+
+
+from quantum.common import exceptions as q_exc
+
+
+_ENGINE = None
+_MAKER = None
+BASE = l2network_models.BASE
+
+
+def configure_db(options):
+    """
+    Establish the database, create an engine if needed, and
+    register the models.
+
+    :param options: Mapping of configuration options
+    """
+    global _ENGINE
+    if not _ENGINE:
+        _ENGINE = create_engine(options['sql_connection'],
+                                echo=False,
+                                echo_pool=True,
+                                pool_recycle=3600)
+        register_models()
+
+
+def clear_db():
+    global _ENGINE
+    assert _ENGINE
+    for table in reversed(BASE.metadata.sorted_tables):
+        _ENGINE.execute(table.delete())
+
+
+def get_session(autocommit=True, expire_on_commit=False):
+    """Helper method to grab session"""
+    global _MAKER, _ENGINE
+    if not _MAKER:
+        assert _ENGINE
+        _MAKER = sessionmaker(bind=_ENGINE,
+                              autocommit=autocommit,
+                              expire_on_commit=expire_on_commit)
+    return _MAKER()
+
+
+def register_models():
+    """Register Models and create properties"""
+    global _ENGINE
+    assert _ENGINE
+    BASE.metadata.create_all(_ENGINE)
+
+
+def unregister_models():
+    """Unregister Models, useful clearing out data before testing"""
+    global _ENGINE
+    assert _ENGINE
+    BASE.metadata.drop_all(_ENGINE)
+
+
+def _check_duplicate_net_name(tenant_id, net_name):
+    session = get_session()
+    try:
+        net = session.query(l2network_models.Network).\
+          filter_by(tenant_id=tenant_id, name=net_name).\
+          one()
+        raise q_exc.NetworkNameExists(tenant_id=tenant_id,
+                        net_name=net_name, net_id=net.uuid)
+    except exc.NoResultFound:
+        # this is the "normal" path, as API spec specifies
+        # that net-names are unique within a tenant
+        pass
+
+
+def network_create(tenant_id, name):
+    session = get_session()
+
+    _check_duplicate_net_name(tenant_id, name)
+    with session.begin():
+        net = l2network_models.Network(tenant_id, name)
+        session.add(net)
+        session.flush()
+        return net
+
+
+def network_list(tenant_id):
+    session = get_session()
+    return session.query(l2network_models.Network).\
+      options(joinedload(l2network_models.Network.ports)). \
+      filter_by(tenant_id=tenant_id).\
+      all()
+
+
+def network_get(net_id):
+    session = get_session()
+    try:
+        return  session.query(l2network_models.Network).\
+            filter_by(uuid=net_id).\
+            one()
+    except exc.NoResultFound, e:
+        raise q_exc.NetworkNotFound(net_id=net_id)
+
+
+def network_rename(net_id, tenant_id, new_name):
+    session = get_session()
+    net = network_get(net_id)
+    _check_duplicate_net_name(tenant_id, new_name)
+    net.name = new_name
+    session.merge(net)
+    session.flush()
+    return net
+
+
+def network_destroy(net_id):
+    session = get_session()
+    try:
+        net = session.query(l2network_models.Network).\
+          filter_by(uuid=net_id).\
+          one()
+        session.delete(net)
+        session.flush()
+        return net
+    except exc.NoResultFound:
+        raise q_exc.NetworkNotFound(net_id=net_id)
+
+
+def port_create(net_id, state=None):
+    # confirm network exists
+    network_get(net_id)
+
+    session = get_session()
+    with session.begin():
+        port = l2network_models.Port(net_id)
+        port['state'] = state or 'DOWN'
+        session.add(port)
+        session.flush()
+        return port
+
+
+def port_list(net_id):
+    session = get_session()
+    return session.query(l2network_models.Port).\
+      options(joinedload(l2network_models.Port.network)). \
+      filter_by(network_id=net_id).\
+      all()
+
+
+def port_get(port_id, net_id):
+    # confirm network exists
+    network_get(net_id)
+    session = get_session()
+    try:
+        return  session.query(l2network_models.Port).\
+          filter_by(uuid=port_id).\
+          filter_by(network_id=net_id).\
+          one()
+    except exc.NoResultFound:
+        raise q_exc.PortNotFound(net_id=net_id, port_id=port_id)
+
+
+def port_set_state(port_id, net_id, new_state):
+    if new_state not in ('ACTIVE', 'DOWN'):
+        raise q_exc.StateInvalid(port_state=new_state)
+
+    # confirm network exists
+    network_get(net_id)
+
+    port = port_get(port_id, net_id)
+    session = get_session()
+    port.state = new_state
+    session.merge(port)
+    session.flush()
+    return port
+
+
+def port_set_attachment(port_id, net_id, new_interface_id):
+    # confirm network exists
+    network_get(net_id)
+
+    session = get_session()
+    port = port_get(port_id, net_id)
+
+    if new_interface_id != "":
+        # We are setting, not clearing, the attachment-id
+        if port['interface_id']:
+            raise q_exc.PortInUse(net_id=net_id, port_id=port_id,
+                                att_id=port['interface_id'])
+
+        try:
+            port = session.query(l2network_models.Port).\
+            filter_by(interface_id=new_interface_id).\
+            one()
+            raise q_exc.AlreadyAttached(net_id=net_id,
+                                    port_id=port_id,
+                                    att_id=new_interface_id,
+                                    att_port_id=port['uuid'])
+        except exc.NoResultFound:
+            # this is what should happen
+            pass
+    port.interface_id = new_interface_id
+    session.merge(port)
+    session.flush()
+    return port
+
+
+def port_unset_attachment(port_id, net_id):
+    # confirm network exists
+    network_get(net_id)
+
+    session = get_session()
+    port = port_get(port_id, net_id)
+    port.interface_id = None
+    session.merge(port)
+    session.flush()
+
+
+def port_destroy(port_id, net_id):
+    # confirm network exists
+    network_get(net_id)
+
+    session = get_session()
+    try:
+        port = session.query(l2network_models.Port).\
+          filter_by(uuid=port_id).\
+          filter_by(network_id=net_id).\
+          one()
+        if port['interface_id']:
+            raise q_exc.PortInUse(net_id=net_id, port_id=port_id,
+                                att_id=port['interface_id'])
+        session.delete(port)
+        session.flush()
+        return port
+    except exc.NoResultFound:
+        raise q_exc.PortNotFound(port_id=port_id)
 
 
 def get_all_vlan_bindings():
     """Lists all the vlan to network associations"""
-    session = db.get_session()
+    session = get_session()
     try:
         bindings = session.query(l2network_models.VlanBinding).\
           all()
@@ -35,7 +266,7 @@ def get_all_vlan_bindings():
 
 def get_vlan_binding(netid):
     """Lists the vlan given a network_id"""
-    session = db.get_session()
+    session = get_session()
     try:
         binding = session.query(l2network_models.VlanBinding).\
           filter_by(network_id=netid).\
@@ -47,7 +278,7 @@ def get_vlan_binding(netid):
 
 def add_vlan_binding(vlanid, vlanname, netid):
     """Adds a vlan to network association"""
-    session = db.get_session()
+    session = get_session()
     try:
         binding = session.query(l2network_models.VlanBinding).\
           filter_by(vlan_id=vlanid).\
@@ -62,7 +293,7 @@ def add_vlan_binding(vlanid, vlanname, netid):
 
 def remove_vlan_binding(netid):
     """Removes a vlan to network association"""
-    session = db.get_session()
+    session = get_session()
     try:
         binding = session.query(l2network_models.VlanBinding).\
           filter_by(network_id=netid).\
@@ -76,15 +307,15 @@ def remove_vlan_binding(netid):
 
 def update_vlan_binding(netid, newvlanid=None, newvlanname=None):
     """Updates a vlan to network association"""
-    session = db.get_session()
+    session = get_session()
     try:
         binding = session.query(l2network_models.VlanBinding).\
           filter_by(network_id=netid).\
           one()
         if newvlanid:
-            binding.vlan_id = newvlanid
+            binding["vlan_id"] = newvlanid
         if newvlanname:
-            binding.vlan_name = newvlanname
+            binding["vlan_name"] = newvlanname
         session.merge(binding)
         session.flush()
         return binding
@@ -94,7 +325,7 @@ def update_vlan_binding(netid, newvlanid=None, newvlanname=None):
 
 def get_all_portprofiles():
     """Lists all the port profiles"""
-    session = db.get_session()
+    session = get_session()
     try:
         pps = session.query(l2network_models.PortProfile).\
           all()
@@ -105,7 +336,7 @@ def get_all_portprofiles():
 
 def get_portprofile(ppid):
     """Lists a port profile"""
-    session = db.get_session()
+    session = get_session()
     try:
         pp = session.query(l2network_models.PortProfile).\
           filter_by(uuid=ppid).\
@@ -117,7 +348,7 @@ def get_portprofile(ppid):
 
 def add_portprofile(ppname, vlanid, qos):
     """Adds a port profile"""
-    session = db.get_session()
+    session = get_session()
     try:
         pp = session.query(l2network_models.PortProfile).\
           filter_by(name=ppname).\
@@ -132,7 +363,7 @@ def add_portprofile(ppname, vlanid, qos):
 
 def remove_portprofile(ppid):
     """Removes a port profile"""
-    session = db.get_session()
+    session = get_session()
     try:
         pp = session.query(l2network_models.PortProfile).\
           filter_by(uuid=ppid).\
@@ -146,17 +377,17 @@ def remove_portprofile(ppid):
 
 def update_portprofile(ppid, newppname=None, newvlanid=None, newqos=None):
     """Updates port profile"""
-    session = db.get_session()
+    session = get_session()
     try:
         pp = session.query(l2network_models.PortProfile).\
           filter_by(uuid=ppid).\
           one()
         if newppname:
-            pp.name = newppname
+            pp["name"] = newppname
         if newvlanid:
-            pp.vlan_id = newvlanid
+            pp["vlan_id"] = newvlanid
         if newqos:
-            pp.qos = newqos
+            pp["qos"] = newqos
         session.merge(pp)
         session.flush()
         return pp
@@ -166,7 +397,7 @@ def update_portprofile(ppid, newppname=None, newvlanid=None, newqos=None):
 
 def get_all_pp_bindings():
     """Lists all the port profiles"""
-    session = db.get_session()
+    session = get_session()
     try:
         bindings = session.query(l2network_models.PortProfileBinding).\
           all()
@@ -177,7 +408,7 @@ def get_all_pp_bindings():
 
 def get_pp_binding(ppid):
     """Lists a port profile binding"""
-    session = db.get_session()
+    session = get_session()
     try:
         binding = session.query(l2network_models.PortProfileBinding).\
           filter_by(portprofile_id=ppid).\
@@ -189,7 +420,7 @@ def get_pp_binding(ppid):
 
 def add_pp_binding(tenantid, networkid, ppid, default):
     """Adds a port profile binding"""
-    session = db.get_session()
+    session = get_session()
     try:
         binding = session.query(l2network_models.PortProfileBinding).\
           filter_by(portprofile_id=ppid).\
@@ -206,7 +437,7 @@ def add_pp_binding(tenantid, networkid, ppid, default):
 
 def remove_pp_binding(ppid):
     """Removes a port profile binding"""
-    session = db.get_session()
+    session = get_session()
     try:
         binding = session.query(l2network_models.PortProfileBinding).\
           filter_by(portprofile_id=ppid).\
@@ -221,17 +452,17 @@ def remove_pp_binding(ppid):
 def update_pp_binding(ppid, newtenantid=None, newnetworkid=None, \
                                                     newdefault=None):
     """Updates port profile binding"""
-    session = db.get_session()
+    session = get_session()
     try:
         binding = session.query(l2network_models.PortProfileBinding).\
           filter_by(portprofile_id=ppid).\
           one()
         if newtenantid:
-            binding.tenant_id = newtenantid
+            binding["tenant_id"] = newtenantid
         if newnetworkid:
-            binding.network_id = newnetworkid
+            binding["network_id"] = newnetworkid
         if newdefault:
-            binding.default = newdefault
+            binding["default"] = newdefault
         session.merge(binding)
         session.flush()
         return binding
index 76321c74a91e4c1e26f3dc755ee2ad4435c51b4b..38d93295279ca7315626b078ebf542577112b8a8 100644 (file)
 import uuid
 
 from sqlalchemy import Column, Integer, String, ForeignKey, Boolean
-from sqlalchemy.orm import relation
+from sqlalchemy.ext.declarative import declarative_base
+from sqlalchemy.orm import relation, object_mapper
 
-from quantum.db.models import BASE
+BASE = declarative_base()
 
 
-class VlanBinding(BASE):
+class L2NetworkBase(object):
+    """Base class for L2Network Models."""
+    __table_args__ = {'mysql_engine': 'InnoDB'}
+
+    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, L2NetworkBase):
+    """Represents a port on a l2network plugin"""
+    __tablename__ = 'ports'
+
+    uuid = Column(String(255), primary_key=True)
+    network_id = Column(String(255), ForeignKey("networks.uuid"),
+                        nullable=False)
+    interface_id = Column(String(255))
+    state = Column(String(8))
+
+    def __init__(self, network_id):
+        self.uuid = str(uuid.uuid4())
+        self.network_id = network_id
+        self.state = "DOWN"
+
+    def __repr__(self):
+        return "<Port(%s,%s,%s,%s)>" % (self.uuid, self.network_id,
+                                     self.state, self.interface_id)
+
+
+class Network(BASE, L2NetworkBase):
+    """Represents a networ on l2network plugin"""
+    __tablename__ = 'networks'
+
+    uuid = Column(String(255), primary_key=True)
+    tenant_id = Column(String(255), nullable=False)
+    name = Column(String(255))
+    ports = relation(Port, order_by=Port.uuid, backref="network")
+
+    def __init__(self, tenant_id, name):
+        self.uuid = str(uuid.uuid4())
+        self.tenant_id = tenant_id
+        self.name = name
+
+    def __repr__(self):
+        return "<Network(%s,%s,%s)>" % \
+          (self.uuid, self.name, self.tenant_id)
+
+
+class VlanBinding(BASE, L2NetworkBase):
     """Represents a binding of vlan_id to network_id"""
     __tablename__ = 'vlan_bindings'
 
     vlan_id = Column(Integer, primary_key=True)
     vlan_name = Column(String(255))
-    network_id = Column(String(255), nullable=False)
-    #foreign key to networks.uuid
+    network_id = Column(String(255), ForeignKey("networks.uuid"), \
+                        nullable=False)
+    network = relation(Network, uselist=False)
 
     def __init__(self, vlan_id, vlan_name, network_id):
         self.vlan_id = vlan_id
@@ -42,7 +119,7 @@ class VlanBinding(BASE):
           (self.vlan_id, self.vlan_name, self.network_id)
 
 
-class PortProfile(BASE):
+class PortProfile(BASE, L2NetworkBase):
     """Represents L2 network plugin level PortProfile for a network"""
     __tablename__ = 'portprofiles'
 
@@ -62,18 +139,20 @@ class PortProfile(BASE):
           (self.uuid, self.name, self.vlan_id, self.qos)
 
 
-class PortProfileBinding(BASE):
+class PortProfileBinding(BASE, L2NetworkBase):
     """Represents PortProfile binding to tenant and network"""
     __tablename__ = 'portprofile_bindings'
 
     id = Column(Integer, primary_key=True, autoincrement=True)
     tenant_id = Column(String(255))
 
-    network_id = Column(String(255), nullable=False)
-    #foreign key to networks.uuid
-    portprofile_id = Column(String(255), nullable=False)
-    #foreign key to portprofiles.uuid
+    network_id = Column(String(255), ForeignKey("networks.uuid"), \
+                        nullable=False)
+    portprofile_id = Column(String(255), ForeignKey("portprofiles.uuid"), \
+                            nullable=False)
     default = Column(Boolean)
+    network = relation(Network, uselist=False)
+    portprofile = relation(PortProfile, uselist=False)
 
     def __init__(self, tenant_id, network_id, portprofile_id, default):
         self.tenant_id = tenant_id