diff options
3 files changed, 106 insertions, 0 deletions
diff --git a/nova/db/sqlalchemy/migrate_repo/versions/330_enforce_mitaka_online_migrations.py b/nova/db/sqlalchemy/migrate_repo/versions/330_enforce_mitaka_online_migrations.py new file mode 100644 index 0000000000..652a0e919a --- /dev/null +++ b/nova/db/sqlalchemy/migrate_repo/versions/330_enforce_mitaka_online_migrations.py @@ -0,0 +1,47 @@ +# 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. + +from sqlalchemy import MetaData, Table, func, select + +from nova import exception +from nova.i18n import _ + + +WARNING_MSG = _('There are still %(count)i unmigrated records in ' + 'the %(table)s table. Migration cannot continue ' + 'until all records have been migrated.') + + +def upgrade(migrate_engine): + meta = MetaData(migrate_engine) + compute_nodes = Table('compute_nodes', meta, autoload=True) + aggregates = Table('aggregates', meta, autoload=True) + + for table in (compute_nodes, aggregates): + count = select([func.count()]).select_from(table).where( + table.c.uuid == None).execute().scalar() # NOQA + if count > 0: + msg = WARNING_MSG % { + 'count': count, + 'table': table.name, + } + raise exception.ValidationError(detail=msg) + + pci_devices = Table('pci_devices', meta, autoload=True) + count = select([func.count()]).select_from(pci_devices).where( + pci_devices.c.parent_addr == None).execute().scalar() # NOQA + if count > 0: + msg = WARNING_MSG % { + 'count': count, + 'table': pci_devices.name, + } + raise exception.ValidationError(detail=msg) diff --git a/nova/tests/unit/db/test_migrations.py b/nova/tests/unit/db/test_migrations.py index 01af1f82f2..782b4b1345 100644 --- a/nova/tests/unit/db/test_migrations.py +++ b/nova/tests/unit/db/test_migrations.py @@ -887,6 +887,10 @@ class NovaMigrationsCheckers(test_migrations.ModelsMigrationsSync, 'instances_deleted_created_at_idx', ['deleted', 'created_at']) + def _check_330(self, engine, data): + # Just a sanity-check migration + pass + class TestNovaMigrationsSQLite(NovaMigrationsCheckers, test_base.DbTestCase, diff --git a/nova/tests/unit/db/test_sqlalchemy_migration.py b/nova/tests/unit/db/test_sqlalchemy_migration.py index 1e3d761c9b..a4d2aff965 100644 --- a/nova/tests/unit/db/test_sqlalchemy_migration.py +++ b/nova/tests/unit/db/test_sqlalchemy_migration.py @@ -28,6 +28,7 @@ from nova.db.sqlalchemy import migration from nova import exception from nova import objects from nova import test +from nova.tests import fixtures as nova_fixtures class TestNullInstanceUuidScanDB(test.TestCase): @@ -224,3 +225,57 @@ class TestFlavorCheck(test.TestCase): inst.create() inst.destroy() self.migration.upgrade(self.engine) + + +class TestNewtonCheck(test.TestCase): + def setUp(self): + super(TestNewtonCheck, self).setUp() + self.useFixture(nova_fixtures.DatabaseAtVersion(329)) + self.context = context.get_admin_context() + self.migration = importlib.import_module( + 'nova.db.sqlalchemy.migrate_repo.versions.' + '330_enforce_mitaka_online_migrations') + self.engine = db_api.get_engine() + + def test_all_migrated(self): + cn = objects.ComputeNode(context=self.context, + vcpus=1, memory_mb=512, local_gb=10, + vcpus_used=0, memory_mb_used=256, + local_gb_used=5, hypervisor_type='HyperDanVM', + hypervisor_version='34', cpu_info='foo') + cn.create() + objects.Aggregate(context=self.context, + name='foo').create() + objects.PciDevice.create(self.context, {}) + self.migration.upgrade(self.engine) + + def test_cn_not_migrated(self): + cn = objects.ComputeNode(context=self.context, + vcpus=1, memory_mb=512, local_gb=10, + vcpus_used=0, memory_mb_used=256, + local_gb_used=5, hypervisor_type='HyperDanVM', + hypervisor_version='34', cpu_info='foo') + cn.create() + db_api.compute_node_update(self.context, cn.id, {'uuid': None}) + self.assertRaises(exception.ValidationError, + self.migration.upgrade, self.engine) + + def test_aggregate_not_migrated(self): + agg = objects.Aggregate(context=self.context, name='foo') + agg.create() + db_api.aggregate_update(self.context, agg.id, {'uuid': None}) + self.assertRaises(exception.ValidationError, + self.migration.upgrade, self.engine) + + def test_pci_device_not_migrated(self): + db_api.pci_device_update(self.context, 1, 'foo:bar', + {'parent_addr': None, + 'compute_node_id': 1, + 'address': 'foo:bar', + 'vendor_id': '123', + 'product_id': '456', + 'dev_type': 'foo', + 'label': 'foobar', + 'status': 'whatisthis?'}) + self.assertRaises(exception.ValidationError, + self.migration.upgrade, self.engine) |