From: rohitagarwalla Date: Thu, 11 Aug 2011 17:39:43 +0000 (-0700) Subject: added network and port models similar to quantum with following changes - X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=7b4e389c1b0096616840529e49f3bffe45ee1d18;p=openstack-build%2Fneutron-build.git added network and port models similar to quantum with following changes - - InnoDB as storage engine to allow foreign key constraints - joinedLoad operation on the queries to make use of relation between Network and Port Moved out the network and port code to make l2network contain vlanbinding, portprofile and portprofile bindings --- diff --git a/quantum/plugins/cisco/db/api.py b/quantum/plugins/cisco/db/api.py new file mode 100644 index 000000000..509583a1d --- /dev/null +++ b/quantum/plugins/cisco/db/api.py @@ -0,0 +1,254 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2011 Nicira Networks, 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: Somik Behera, Nicira Networks, Inc. +# @author: Brad Hall, Nicira Networks, Inc. +# @author: Dan Wendlandt, Nicira Networks, Inc. + +from sqlalchemy import create_engine +from sqlalchemy.orm import sessionmaker, exc, joinedload + +from quantum.common import exceptions as q_exc +from quantum.plugins.cisco.db import models + +_ENGINE = None +_MAKER = None +BASE = 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(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 = models.Network(tenant_id, name) + session.add(net) + session.flush() + return net + + +def network_list(tenant_id): + session = get_session() + return session.query(models.Network).\ + options(joinedload(models.Network.ports)). \ + filter_by(tenant_id=tenant_id).\ + all() + + +def network_get(net_id): + session = get_session() + try: + return session.query(models.Network).\ + options(joinedload(models.Network.ports)). \ + filter_by(uuid=net_id).\ + one() + except exc.NoResultFound, e: + raise q_exc.NetworkNotFound(net_id=net_id) + + +def network_rename(tenant_id, net_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(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 = 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(models.Port).\ + options(joinedload(models.Port.network)). \ + filter_by(network_id=net_id).\ + all() + + +def port_get(net_id, port_id): + # confirm network exists + network_get(net_id) + session = get_session() + try: + return session.query(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(net_id, port_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(net_id, port_id) + session = get_session() + port.state = new_state + session.merge(port) + session.flush() + return port + + +def port_set_attachment(net_id, port_id, new_interface_id): + # confirm network exists + network_get(net_id) + + session = get_session() + port = port_get(net_id, port_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(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(net_id, port_id): + # confirm network exists + network_get(net_id) + + session = get_session() + port = port_get(net_id, port_id) + port.interface_id = None + session.merge(port) + session.flush() + + +def port_destroy(net_id, port_id): + # confirm network exists + network_get(net_id) + + session = get_session() + try: + port = session.query(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) + diff --git a/quantum/plugins/cisco/db/l2network_db.py b/quantum/plugins/cisco/db/l2network_db.py index ce48b42a2..bc3d7574c 100644 --- a/quantum/plugins/cisco/db/l2network_db.py +++ b/quantum/plugins/cisco/db/l2network_db.py @@ -15,247 +15,72 @@ # under the License. # @author: Rohit Agarwalla, Cisco Systems, Inc. +import ConfigParser +from optparse import OptionParser +import os +import logging as LOG + from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker, exc, joinedload -import l2network_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 +from quantum.plugins.cisco import l2network_plugin_configuration as conf +import quantum.plugins.cisco.db.api as db -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() +import l2network_models -def port_get(port_id, net_id): - # confirm network exists - network_get(net_id) - session = get_session() +CONF_FILE = "db_conn.ini" + + +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 + + +def initialize(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.debug("Using configuration file: %s" % configfile) + config.read(configfile) + + 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) + +def prepopulate_vlan_bindings(): + """Prepopulates the vlan_bindings table""" + session = db.get_session() try: - return session.query(l2network_models.Port).\ - filter_by(uuid=port_id).\ - filter_by(network_id=net_id).\ - one() + binding = session.query(l2network_models.VlanBinding).\ + all() + raise Exception("Vlan table not empty id for prepopulation") 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) + start = conf.VLAN_START + end = conf.VLAN_END + while start < end: + binding = l2network_models.VlanBinding(vlanid, "", 0) + session.add(binding) session.flush() - return port - except exc.NoResultFound: - raise q_exc.PortNotFound(port_id=port_id) - + return def get_all_vlan_bindings(): """Lists all the vlan to network associations""" - session = get_session() + session = db.get_session() try: bindings = session.query(l2network_models.VlanBinding).\ all() @@ -266,7 +91,7 @@ def get_all_vlan_bindings(): def get_vlan_binding(netid): """Lists the vlan given a network_id""" - session = get_session() + session = db.get_session() try: binding = session.query(l2network_models.VlanBinding).\ filter_by(network_id=netid).\ @@ -278,7 +103,7 @@ def get_vlan_binding(netid): def add_vlan_binding(vlanid, vlanname, netid): """Adds a vlan to network association""" - session = get_session() + session = db.get_session() try: binding = session.query(l2network_models.VlanBinding).\ filter_by(vlan_id=vlanid).\ @@ -293,7 +118,7 @@ def add_vlan_binding(vlanid, vlanname, netid): def remove_vlan_binding(netid): """Removes a vlan to network association""" - session = get_session() + session = db.get_session() try: binding = session.query(l2network_models.VlanBinding).\ filter_by(network_id=netid).\ @@ -307,7 +132,7 @@ def remove_vlan_binding(netid): def update_vlan_binding(netid, newvlanid=None, newvlanname=None): """Updates a vlan to network association""" - session = get_session() + session = db.get_session() try: binding = session.query(l2network_models.VlanBinding).\ filter_by(network_id=netid).\ @@ -325,7 +150,7 @@ def update_vlan_binding(netid, newvlanid=None, newvlanname=None): def get_all_portprofiles(): """Lists all the port profiles""" - session = get_session() + session = db.get_session() try: pps = session.query(l2network_models.PortProfile).\ all() @@ -336,7 +161,7 @@ def get_all_portprofiles(): def get_portprofile(ppid): """Lists a port profile""" - session = get_session() + session = db.get_session() try: pp = session.query(l2network_models.PortProfile).\ filter_by(uuid=ppid).\ @@ -348,7 +173,7 @@ def get_portprofile(ppid): def add_portprofile(ppname, vlanid, qos): """Adds a port profile""" - session = get_session() + session = db.get_session() try: pp = session.query(l2network_models.PortProfile).\ filter_by(name=ppname).\ @@ -363,7 +188,7 @@ def add_portprofile(ppname, vlanid, qos): def remove_portprofile(ppid): """Removes a port profile""" - session = get_session() + session = db.get_session() try: pp = session.query(l2network_models.PortProfile).\ filter_by(uuid=ppid).\ @@ -377,7 +202,7 @@ def remove_portprofile(ppid): def update_portprofile(ppid, newppname=None, newvlanid=None, newqos=None): """Updates port profile""" - session = get_session() + session = db.get_session() try: pp = session.query(l2network_models.PortProfile).\ filter_by(uuid=ppid).\ @@ -397,7 +222,7 @@ def update_portprofile(ppid, newppname=None, newvlanid=None, newqos=None): def get_all_pp_bindings(): """Lists all the port profiles""" - session = get_session() + session = db.get_session() try: bindings = session.query(l2network_models.PortProfileBinding).\ all() @@ -408,7 +233,7 @@ def get_all_pp_bindings(): def get_pp_binding(ppid): """Lists a port profile binding""" - session = get_session() + session = db.get_session() try: binding = session.query(l2network_models.PortProfileBinding).\ filter_by(portprofile_id=ppid).\ @@ -420,7 +245,7 @@ def get_pp_binding(ppid): def add_pp_binding(tenantid, networkid, ppid, default): """Adds a port profile binding""" - session = get_session() + session = db.get_session() try: binding = session.query(l2network_models.PortProfileBinding).\ filter_by(portprofile_id=ppid).\ @@ -437,7 +262,7 @@ def add_pp_binding(tenantid, networkid, ppid, default): def remove_pp_binding(ppid): """Removes a port profile binding""" - session = get_session() + session = db.get_session() try: binding = session.query(l2network_models.PortProfileBinding).\ filter_by(portprofile_id=ppid).\ @@ -452,7 +277,7 @@ def remove_pp_binding(ppid): def update_pp_binding(ppid, newtenantid=None, newnetworkid=None, \ newdefault=None): """Updates port profile binding""" - session = get_session() + session = db.get_session() try: binding = session.query(l2network_models.PortProfileBinding).\ filter_by(portprofile_id=ppid).\ diff --git a/quantum/plugins/cisco/db/l2network_models.py b/quantum/plugins/cisco/db/l2network_models.py index 38d932952..fe76c7d15 100644 --- a/quantum/plugins/cisco/db/l2network_models.py +++ b/quantum/plugins/cisco/db/l2network_models.py @@ -21,7 +21,8 @@ from sqlalchemy import Column, Integer, String, ForeignKey, Boolean from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import relation, object_mapper -BASE = declarative_base() +from quantum.plugins.cisco.db.models import BASE +from quantum.plugins.cisco.db import models class L2NetworkBase(object): @@ -60,45 +61,6 @@ class L2NetworkBase(object): 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' @@ -107,7 +69,7 @@ class VlanBinding(BASE, L2NetworkBase): vlan_name = Column(String(255)) network_id = Column(String(255), ForeignKey("networks.uuid"), \ nullable=False) - network = relation(Network, uselist=False) + network = relation(models.Network, uselist=False) def __init__(self, vlan_id, vlan_name, network_id): self.vlan_id = vlan_id @@ -151,7 +113,7 @@ class PortProfileBinding(BASE, L2NetworkBase): portprofile_id = Column(String(255), ForeignKey("portprofiles.uuid"), \ nullable=False) default = Column(Boolean) - network = relation(Network, uselist=False) + network = relation(models.Network, uselist=False) portprofile = relation(PortProfile, uselist=False) def __init__(self, tenant_id, network_id, portprofile_id, default): diff --git a/quantum/plugins/cisco/db/models.py b/quantum/plugins/cisco/db/models.py new file mode 100644 index 000000000..81ac773a6 --- /dev/null +++ b/quantum/plugins/cisco/db/models.py @@ -0,0 +1,102 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright 2011 Nicira Networks, 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: Somik Behera, Nicira Networks, Inc. +# @author: Brad Hall, Nicira Networks, Inc. +# @author: Dan Wendlandt, Nicira Networks, Inc. + +import uuid + +from sqlalchemy import Column, String, ForeignKey +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy.orm import relation, object_mapper + +BASE = declarative_base() + + +class QuantumBase(object): + """Base class for Quantum 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, QuantumBase): + """Represents a port on a quantum network""" + __tablename__ = 'ports' + + uuid = Column(String(255), primary_key=True) + network_id = Column(String(255), ForeignKey("networks.uuid"), + nullable=False) + interface_id = Column(String(255)) + # Port state - Hardcoding string value at the moment + 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, QuantumBase): + """Represents a quantum network""" + __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)