From 33b4bd2e4270c92bf3ed80c4af7f674bf8611e32 Mon Sep 17 00:00:00 2001 From: Eric Windisch Date: Thu, 17 Jan 2013 15:38:02 -0500 Subject: [PATCH] Use oslo database code Bring in the new database code from oslo. Uses get_session() from oslo as well as changing NovaBase to derive from a common class. Remove test_sqlalchemy.py now that this code is test in oslo. Implements blueprint db-common. Change-Id: I090754981c871250dd981cbbe1a08e7181440120 --- bin/nova-manage | 9 +- nova/common/sqlalchemyutils.py | 128 ------------------ nova/tests/baremetal/db/test_bm_interface.py | 3 +- nova/tests/baremetal/db/test_bm_pxe_ip.py | 5 +- nova/tests/baremetal/test_pxe.py | 3 +- nova/tests/test_instance_types.py | 2 +- nova/tests/test_sqlalchemy.py | 129 ------------------- 7 files changed, 13 insertions(+), 266 deletions(-) delete mode 100644 nova/common/sqlalchemyutils.py delete mode 100644 nova/tests/test_sqlalchemy.py diff --git a/bin/nova-manage b/bin/nova-manage index 82edf738..c793fed1 100755 --- a/bin/nova-manage +++ b/bin/nova-manage @@ -80,6 +80,7 @@ from nova.db import migration from nova import exception from nova.openstack.common import cfg from nova.openstack.common import cliutils +from nova.openstack.common.db.sqlalchemy import session as db_session from nova.openstack.common import importutils from nova.openstack.common import log as logging from nova.openstack.common import rpc @@ -831,7 +832,7 @@ class InstanceTypeCommands(object): except exception.InstanceTypeNotFound: print _("Valid instance type name is required") sys.exit(1) - except exception.DBError, e: + except db_session.DBError, e: print _("DB Error: %s") % e sys.exit(2) except Exception: @@ -848,7 +849,7 @@ class InstanceTypeCommands(object): inst_types = instance_types.get_all_types() else: inst_types = instance_types.get_instance_type_by_name(name) - except exception.DBError, e: + except db_session.DBError, e: _db_error(e) if isinstance(inst_types.values()[0], dict): for k, v in inst_types.iteritems(): @@ -879,7 +880,7 @@ class InstanceTypeCommands(object): ext_spec) print _("Key %(key)s set to %(value)s on instance" " type %(name)s") % locals() - except exception.DBError, e: + except db_session.DBError, e: _db_error(e) @args('--name', dest='name', metavar='', @@ -902,7 +903,7 @@ class InstanceTypeCommands(object): key) print _("Key %(key)s on instance type %(name)s unset") % locals() - except exception.DBError, e: + except db_session.DBError, e: _db_error(e) diff --git a/nova/common/sqlalchemyutils.py b/nova/common/sqlalchemyutils.py deleted file mode 100644 index a186948a..00000000 --- a/nova/common/sqlalchemyutils.py +++ /dev/null @@ -1,128 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# Copyright 2010-2011 OpenStack LLC. -# Copyright 2012 Justin Santa Barbara -# 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. - -"""Implementation of paginate query.""" - -import sqlalchemy - -from nova import exception -from nova.openstack.common import log as logging - - -LOG = logging.getLogger(__name__) - - -# copy from glance/db/sqlalchemy/api.py -def paginate_query(query, model, limit, sort_keys, marker=None, - sort_dir=None, sort_dirs=None): - """Returns a query with sorting / pagination criteria added. - - Pagination works by requiring a unique sort_key, specified by sort_keys. - (If sort_keys is not unique, then we risk looping through values.) - We use the last row in the previous page as the 'marker' for pagination. - So we must return values that follow the passed marker in the order. - With a single-valued sort_key, this would be easy: sort_key > X. - With a compound-values sort_key, (k1, k2, k3) we must do this to repeat - the lexicographical ordering: - (k1 > X1) or (k1 == X1 && k2 > X2) or (k1 == X1 && k2 == X2 && k3 > X3) - - We also have to cope with different sort_directions. - - Typically, the id of the last row is used as the client-facing pagination - marker, then the actual marker object must be fetched from the db and - passed in to us as marker. - - :param query: the query object to which we should add paging/sorting - :param model: the ORM model class - :param limit: maximum number of items to return - :param sort_keys: array of attributes by which results should be sorted - :param marker: the last item of the previous page; we returns the next - results after this value. - :param sort_dir: direction in which results should be sorted (asc, desc) - :param sort_dirs: per-column array of sort_dirs, corresponding to sort_keys - - :rtype: sqlalchemy.orm.query.Query - :return: The query with sorting/pagination added. - """ - - if 'id' not in sort_keys: - # TODO(justinsb): If this ever gives a false-positive, check - # the actual primary key, rather than assuming its id - LOG.warn(_('Id not in sort_keys; is sort_keys unique?')) - - assert(not (sort_dir and sort_dirs)) - - # Default the sort direction to ascending - if sort_dirs is None and sort_dir is None: - sort_dir = 'asc' - - # Ensure a per-column sort direction - if sort_dirs is None: - sort_dirs = [sort_dir for _sort_key in sort_keys] - - assert(len(sort_dirs) == len(sort_keys)) - - # Add sorting - for current_sort_key, current_sort_dir in zip(sort_keys, sort_dirs): - sort_dir_func = { - 'asc': sqlalchemy.asc, - 'desc': sqlalchemy.desc, - }[current_sort_dir] - - try: - sort_key_attr = getattr(model, current_sort_key) - except AttributeError: - raise exception.InvalidSortKey() - query = query.order_by(sort_dir_func(sort_key_attr)) - - # Add pagination - if marker is not None: - marker_values = [] - for sort_key in sort_keys: - v = getattr(marker, sort_key) - marker_values.append(v) - - # Build up an array of sort criteria as in the docstring - criteria_list = [] - for i in xrange(0, len(sort_keys)): - crit_attrs = [] - for j in xrange(0, i): - model_attr = getattr(model, sort_keys[j]) - crit_attrs.append((model_attr == marker_values[j])) - - model_attr = getattr(model, sort_keys[i]) - if sort_dirs[i] == 'desc': - crit_attrs.append((model_attr < marker_values[i])) - elif sort_dirs[i] == 'asc': - crit_attrs.append((model_attr > marker_values[i])) - else: - raise ValueError(_("Unknown sort direction, " - "must be 'desc' or 'asc'")) - - criteria = sqlalchemy.sql.and_(*crit_attrs) - criteria_list.append(criteria) - - f = sqlalchemy.sql.or_(*criteria_list) - query = query.filter(f) - - if limit is not None: - query = query.limit(limit) - - return query diff --git a/nova/tests/baremetal/db/test_bm_interface.py b/nova/tests/baremetal/db/test_bm_interface.py index 9f051ac9..32beb1ce 100644 --- a/nova/tests/baremetal/db/test_bm_interface.py +++ b/nova/tests/baremetal/db/test_bm_interface.py @@ -18,6 +18,7 @@ Bare-metal DB testcase for BareMetalInterface """ from nova import exception +from nova.openstack.common.db.sqlalchemy import session as db_session from nova.tests.baremetal.db import base from nova.virt.baremetal import db @@ -27,7 +28,7 @@ class BareMetalInterfaceTestCase(base.BMDBTestCase): def test_unique_address(self): pif1_id = db.bm_interface_create(self.context, 1, '11:11:11:11:11:11', '0x1', 1) - self.assertRaises(exception.DBError, + self.assertRaises(db_session.DBError, db.bm_interface_create, self.context, 2, '11:11:11:11:11:11', '0x2', 2) # succeed after delete pif1 diff --git a/nova/tests/baremetal/db/test_bm_pxe_ip.py b/nova/tests/baremetal/db/test_bm_pxe_ip.py index 9a93b46a..9820f3af 100644 --- a/nova/tests/baremetal/db/test_bm_pxe_ip.py +++ b/nova/tests/baremetal/db/test_bm_pxe_ip.py @@ -18,6 +18,7 @@ Bare-metal DB testcase for BareMetalPxeIp """ from nova import exception +from nova.openstack.common.db.sqlalchemy import session as db_session from nova.tests.baremetal.db import base from nova.tests.baremetal.db import utils from nova.virt.baremetal import db @@ -50,14 +51,14 @@ class BareMetalPxeIpTestCase(base.BMDBTestCase): # address duplicates i = utils.new_bm_pxe_ip(address='10.1.1.1', server_address='10.1.1.201') - self.assertRaises(exception.DBError, + self.assertRaises(db_session.DBError, db.bm_pxe_ip_create_direct, self.context, i) # server_address duplicates i = utils.new_bm_pxe_ip(address='10.1.1.3', server_address='10.1.1.101') - self.assertRaises(exception.DBError, + self.assertRaises(db_session.DBError, db.bm_pxe_ip_create_direct, self.context, i) diff --git a/nova/tests/baremetal/test_pxe.py b/nova/tests/baremetal/test_pxe.py index 09f1079b..e50462b0 100644 --- a/nova/tests/baremetal/test_pxe.py +++ b/nova/tests/baremetal/test_pxe.py @@ -25,6 +25,7 @@ from testtools import matchers from nova import exception from nova.openstack.common import cfg +from nova.openstack.common.db.sqlalchemy import session as db_session from nova.tests.baremetal.db import base as bm_db_base from nova.tests.baremetal.db import utils as bm_db_utils from nova.tests.image import fake as fake_image @@ -521,7 +522,7 @@ class PXEPublicMethodsTestCase(BareMetalPXETestCase): AndRaise(exception.NovaException) bm_utils.unlink_without_raise(pxe_path) self.driver._collect_mac_addresses(self.context, self.node).\ - AndRaise(exception.DBError) + AndRaise(db_session.DBError) bm_utils.rmtree_without_raise( os.path.join(CONF.baremetal.tftp_root, 'fake-uuid')) self.mox.ReplayAll() diff --git a/nova/tests/test_instance_types.py b/nova/tests/test_instance_types.py index b70b96b7..5abf62c3 100644 --- a/nova/tests/test_instance_types.py +++ b/nova/tests/test_instance_types.py @@ -21,8 +21,8 @@ from nova.compute import instance_types from nova import context from nova import db from nova.db.sqlalchemy import models -from nova.db.sqlalchemy import session as sql_session from nova import exception +from nova.openstack.common.db.sqlalchemy import session as sql_session from nova.openstack.common import log as logging from nova import test diff --git a/nova/tests/test_sqlalchemy.py b/nova/tests/test_sqlalchemy.py deleted file mode 100644 index 5c7f4450..00000000 --- a/nova/tests/test_sqlalchemy.py +++ /dev/null @@ -1,129 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright (c) 2012 Rackspace Hosting -# 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. - -"""Unit tests for SQLAlchemy specific code.""" - -from eventlet import db_pool -try: - import MySQLdb -except ImportError: - MySQLdb = None - -from sqlalchemy import Column, MetaData, Table, UniqueConstraint -from sqlalchemy.ext.declarative import declarative_base -from sqlalchemy import DateTime, Integer - -from nova import context -from nova.db.sqlalchemy import models -from nova.db.sqlalchemy import session -from nova import exception -from nova import test - - -class DbPoolTestCase(test.TestCase): - def setUp(self): - super(DbPoolTestCase, self).setUp() - self.flags(sql_dbpool_enable=True) - self.user_id = 'fake' - self.project_id = 'fake' - self.context = context.RequestContext(self.user_id, self.project_id) - if not MySQLdb: - self.skipTest("Unable to test due to lack of MySQLdb") - - def test_db_pool_option(self): - self.flags(sql_idle_timeout=11, sql_min_pool_size=21, - sql_max_pool_size=42) - - info = {} - - class FakeConnectionPool(db_pool.ConnectionPool): - def __init__(self, mod_name, **kwargs): - info['module'] = mod_name - info['kwargs'] = kwargs - super(FakeConnectionPool, self).__init__(mod_name, - **kwargs) - - def connect(self, *args, **kwargs): - raise test.TestingException() - - self.stubs.Set(db_pool, 'ConnectionPool', - FakeConnectionPool) - - sql_connection = 'mysql://user:pass@127.0.0.1/nova' - self.assertRaises(test.TestingException, session.create_engine, - sql_connection) - - self.assertEqual(info['module'], MySQLdb) - self.assertEqual(info['kwargs']['max_idle'], 11) - self.assertEqual(info['kwargs']['min_size'], 21) - self.assertEqual(info['kwargs']['max_size'], 42) - - -BASE = declarative_base() -_TABLE_NAME = '__tmp__test__tmp__' - - -class TmpTable(BASE, models.NovaBase): - __tablename__ = _TABLE_NAME - id = Column(Integer, primary_key=True) - foo = Column(Integer) - - -class SessionErrorWrapperTestCase(test.TestCase): - def setUp(self): - super(SessionErrorWrapperTestCase, self).setUp() - meta = MetaData() - meta.bind = session.get_engine() - test_table = Table(_TABLE_NAME, meta, - Column('id', Integer, primary_key=True, - nullable=False), - Column('deleted', Integer, default=0), - Column('deleted_at', DateTime), - Column('updated_at', DateTime), - Column('created_at', DateTime), - Column('foo', Integer), - UniqueConstraint('foo', name='uniq_foo')) - test_table.create() - - def tearDown(self): - super(SessionErrorWrapperTestCase, self).tearDown() - meta = MetaData() - meta.bind = session.get_engine() - test_table = Table(_TABLE_NAME, meta, autoload=True) - test_table.drop() - - def test_flush_wrapper(self): - tbl = TmpTable() - tbl.update({'foo': 10}) - tbl.save() - - tbl2 = TmpTable() - tbl2.update({'foo': 10}) - self.assertRaises(exception.DBDuplicateEntry, tbl2.save) - - def test_execute_wrapper(self): - _session = session.get_session() - with _session.begin(): - for i in [10, 20]: - tbl = TmpTable() - tbl.update({'foo': i}) - tbl.save(session=_session) - - method = _session.query(TmpTable).\ - filter_by(foo=10).\ - update - self.assertRaises(exception.DBDuplicateEntry, - method, {'foo': 20})