From: rohitagarwalla Date: Wed, 10 Aug 2011 20:01:35 +0000 (-0700) Subject: - added network and port models into the l2network plugin instead of using quantum... X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=0d404e9eb8effd1e5e562a91de33916620af7e75;p=openstack-build%2Fneutron-build.git - added network and port models into the l2network plugin instead of using quantum models - 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 --- diff --git a/quantum/plugins/cisco/db/l2network_db.py b/quantum/plugins/cisco/db/l2network_db.py index 15861eee3..ce48b42a2 100644 --- a/quantum/plugins/cisco/db/l2network_db.py +++ b/quantum/plugins/cisco/db/l2network_db.py @@ -15,16 +15,247 @@ # 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 diff --git a/quantum/plugins/cisco/db/l2network_models.py b/quantum/plugins/cisco/db/l2network_models.py index 76321c74a..38d932952 100644 --- a/quantum/plugins/cisco/db/l2network_models.py +++ b/quantum/plugins/cisco/db/l2network_models.py @@ -18,19 +18,96 @@ 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 "" % (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 "" % \ + (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