diff options
author | Jenkins <jenkins@review.openstack.org> | 2013-06-17 23:16:55 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2013-06-17 23:16:55 +0000 |
commit | f27a4f306183017da5dfceeb7d1dfa8cf4cbb433 (patch) | |
tree | 8d5d2cbe7472694c902b3cb6d9c9e9725e37197d | |
parent | 5d26786ff3c37991bf79a2d91e709277c194bee6 (diff) | |
parent | 7fdba82bad3c4e550bda4db03ed9d1ab7ab62934 (diff) | |
download | nova-f27a4f306183017da5dfceeb7d1dfa8cf4cbb433.tar.gz nova-f27a4f306183017da5dfceeb7d1dfa8cf4cbb433.tar.xz nova-f27a4f306183017da5dfceeb7d1dfa8cf4cbb433.zip |
Merge "Add unique constraints to Cell."
-rw-r--r-- | nova/db/sqlalchemy/api.py | 5 | ||||
-rw-r--r-- | nova/db/sqlalchemy/migrate_repo/versions/189_add_cells_uc.py | 40 | ||||
-rw-r--r-- | nova/db/sqlalchemy/models.py | 4 | ||||
-rw-r--r-- | nova/exception.py | 4 | ||||
-rw-r--r-- | nova/tests/db/test_db_api.py | 5 | ||||
-rw-r--r-- | nova/tests/db/test_migrations.py | 29 |
6 files changed, 86 insertions, 1 deletions
diff --git a/nova/db/sqlalchemy/api.py b/nova/db/sqlalchemy/api.py index 664425441..55ef03006 100644 --- a/nova/db/sqlalchemy/api.py +++ b/nova/db/sqlalchemy/api.py @@ -4048,7 +4048,10 @@ def instance_type_extra_specs_update_or_create(context, flavor_id, specs): def cell_create(context, values): cell = models.Cell() cell.update(values) - cell.save() + try: + cell.save() + except db_exc.DBDuplicateEntry: + raise exception.CellExists(name=values['name']) return cell diff --git a/nova/db/sqlalchemy/migrate_repo/versions/189_add_cells_uc.py b/nova/db/sqlalchemy/migrate_repo/versions/189_add_cells_uc.py new file mode 100644 index 000000000..d0606e9f9 --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/189_add_cells_uc.py @@ -0,0 +1,40 @@ +# Copyright 2013 Mirantis 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. +# +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +from migrate.changeset import UniqueConstraint +from sqlalchemy import MetaData, Table + +from nova.db.sqlalchemy import utils + + +UC_NAME = 'uniq_cell_name0deleted' +COLUMNS = ('name', 'deleted') +TABLE_NAME = 'cells' + + +def upgrade(migrate_engine): + meta = MetaData(bind=migrate_engine) + t = Table(TABLE_NAME, meta, autoload=True) + + utils.drop_old_duplicate_entries_from_table(migrate_engine, TABLE_NAME, + True, *COLUMNS) + uc = UniqueConstraint(*COLUMNS, table=t, name=UC_NAME) + uc.create() + + +def downgrade(migrate_engine): + utils.drop_unique_constraint(migrate_engine, TABLE_NAME, UC_NAME, *COLUMNS) diff --git a/nova/db/sqlalchemy/models.py b/nova/db/sqlalchemy/models.py index 815041638..9b6d849d3 100644 --- a/nova/db/sqlalchemy/models.py +++ b/nova/db/sqlalchemy/models.py @@ -859,6 +859,10 @@ class Cell(BASE, NovaBase): of entries with is_parent=True or False """ __tablename__ = 'cells' + __table_args__ = (schema.UniqueConstraint( + "name", "deleted", name="uniq_cell_name0deleted" + ), + ) id = Column(Integer, primary_key=True) # Name here is the 'short name' of a cell. For instance: 'child1' name = Column(String(255)) diff --git a/nova/exception.py b/nova/exception.py index 68cf1f991..893c0df75 100644 --- a/nova/exception.py +++ b/nova/exception.py @@ -825,6 +825,10 @@ class CellNotFound(NotFound): message = _("Cell %(cell_name)s doesn't exist.") +class CellExists(Duplicate): + message = _("Cell with name %(name)s already exists.") + + class CellRoutingInconsistency(NovaException): message = _("Inconsistency in cell routing: %(reason)s") diff --git a/nova/tests/db/test_db_api.py b/nova/tests/db/test_db_api.py index 7f68dfbc4..0a5b75bf4 100644 --- a/nova/tests/db/test_db_api.py +++ b/nova/tests/db/test_db_api.py @@ -4967,6 +4967,11 @@ class CellTestCase(test.TestCase, ModelsObjectComparatorMixin): self.assertRaises(exception.CellNotFound, db.cell_update, self.ctxt, 'cellnotinbase', self._get_cell_base_values()) + def test_cell_create_exists(self): + db.cell_create(self.ctxt, self._get_cell_base_values()) + self.assertRaises(exception.CellExists, db.cell_create, + self.ctxt, self._get_cell_base_values()) + class ArchiveTestCase(test.TestCase): diff --git a/nova/tests/db/test_migrations.py b/nova/tests/db/test_migrations.py index dc77a27d7..812f0d8ae 100644 --- a/nova/tests/db/test_migrations.py +++ b/nova/tests/db/test_migrations.py @@ -1643,6 +1643,35 @@ class TestNovaMigrations(BaseMigrationTestCase, CommonTestsMixIn): rows = services.select().execute().fetchall() self.assertFalse('disabled_reason' in rows[0]) + def _pre_upgrade_189(self, engine): + cells = db_utils.get_table(engine, 'cells') + data = [ + {'name': 'name_123', 'deleted': 0}, + {'name': 'name_123', 'deleted': 0}, + {'name': 'name_345', 'deleted': 0}, + ] + for item in data: + cells.insert().values(item).execute() + return data + + def _check_189(self, engine, data): + cells = db_utils.get_table(engine, 'cells') + + def get_(name, deleted): + deleted_value = 0 if not deleted else cells.c.id + return cells.select().\ + where(cells.c.name == name).\ + where(cells.c.deleted == deleted_value).\ + execute().\ + fetchall() + + self.assertEqual(1, len(get_('name_123', False))) + self.assertEqual(1, len(get_('name_123', True))) + self.assertEqual(1, len(get_('name_345', False))) + self.assertRaises(sqlalchemy.exc.IntegrityError, + cells.insert().execute, + {'name': 'name_123', 'deleted': 0}) + class TestBaremetalMigrations(BaseMigrationTestCase, CommonTestsMixIn): """Test sqlalchemy-migrate migrations.""" |