Require cellsv2 setup before migrating to Ocata

We have code going into Ocata that needs to be sure that cell and
host mappings are in place. Since this was required homework in
Newton, we can land a migration to intentionally fail if this was
not completed.

This is, however, a little difficult to require because a first-time
deployment will be initialized schema-wise with none of these records,
which is also sane. So, we look to see if any flavors are defined as
a sentinel to indicate that this is an upgrade of an existing
deployment instead of a first-time event. Not perfect, but since this
is really just a helper for the user, it seems like a reasonable
risk.

Depends-On: If1af9c478e8ea2420f2523a9bb8b70fafddc86b7
Change-Id: I72fb724dc13e1a5f4e97c58915b538ba761c582d
This commit is contained in:
Dan Smith 2016-11-01 08:54:59 -07:00 committed by Matt Riedemann
parent 6d53334946
commit ff6b9998bb
4 changed files with 168 additions and 1 deletions

View File

@ -0,0 +1,57 @@
# 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 _
from nova import objects
def upgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
flavors = Table('flavors', meta, autoload=True)
count = select([func.count()]).select_from(flavors).scalar()
if count == 0:
# NOTE(danms): We need to be careful here if this is a new
# installation, which can't possibly have any mappings. Check
# to see if any flavors are defined to determine if we are
# upgrading an existing system. If not, then don't obsess over
# the lack of mappings
return
cell_mappings = Table('cell_mappings', meta, autoload=True)
count = select([func.count()]).select_from(cell_mappings).scalar()
# Two mappings are required at a minimum, cell0 and your first cell
if count < 2:
msg = _('Cell mappings are not created, but required for Ocata. '
'Please run nova-manage db simple_cell_setup before '
'continuing.')
raise exception.ValidationError(detail=msg)
count = select([func.count()]).select_from(cell_mappings).where(
cell_mappings.c.uuid == objects.CellMapping.CELL0_UUID).scalar()
if count != 1:
msg = _('A mapping for Cell0 was not found, but is required for '
'Ocata. Please run nova-manage db simple_cell_setup before '
'continuing.')
raise exception.ValidationError(detail=msg)
host_mappings = Table('host_mappings', meta, autoload=True)
count = select([func.count()]).select_from(host_mappings).scalar()
if count == 0:
msg = _('No host mappings were found, but are required for Ocata. '
'Please run nova-manage db simple_cell_setup before '
'continuing.')
raise exception.ValidationError(detail=msg)

View File

@ -164,7 +164,10 @@ class NovaAPIMigrationsWalk(test_migrations.WalkVersionsMixin):
def _skippable_migrations(self):
mitaka_placeholders = range(8, 13)
newton_placeholders = range(21, 26)
return mitaka_placeholders + newton_placeholders
special_cases = [
30, # Enforcement migration, no changes to test
]
return mitaka_placeholders + newton_placeholders + special_cases
def migrate_up(self, version, with_data=False):
if with_data:

View File

@ -365,3 +365,103 @@ class TestOcataCheck(test.TestCase):
group = db_api.instance_group_create(self.context, self.ig_values)
db_api.instance_group_delete(self.context, group['uuid'])
self.migration.upgrade(self.engine)
class TestNewtonCellsCheck(test.NoDBTestCase):
USES_DB_SELF = True
def setUp(self):
super(TestNewtonCellsCheck, self).setUp()
self.useFixture(nova_fixtures.DatabaseAtVersion(28, 'api'))
self.context = context.get_admin_context()
self.migration = importlib.import_module(
'nova.db.sqlalchemy.api_migrations.migrate_repo.versions.'
'030_require_cell_setup')
self.engine = db_api.get_api_engine()
@mock.patch('nova.objects.Flavor._ensure_migrated')
def _flavor_me(self, _):
flavor = objects.Flavor(context=self.context,
name='foo', memory_mb=123,
vcpus=1, root_gb=1,
flavorid='m1.foo')
flavor.create()
def test_upgrade_with_no_cell_mappings(self):
self._flavor_me()
self.assertRaisesRegex(exception.ValidationError,
'Cell mappings',
self.migration.upgrade, self.engine)
def test_upgrade_with_only_cell0(self):
self._flavor_me()
cell0 = objects.CellMapping(context=self.context,
uuid=objects.CellMapping.CELL0_UUID,
name='cell0',
transport_url='fake',
database_connection='fake')
cell0.create()
self.assertRaisesRegex(exception.ValidationError,
'Cell mappings',
self.migration.upgrade, self.engine)
def test_upgrade_without_cell0(self):
self._flavor_me()
cell1 = objects.CellMapping(context=self.context,
uuid=uuidsentinel.cell1,
name='cell1',
transport_url='fake',
database_connection='fake')
cell1.create()
cell2 = objects.CellMapping(context=self.context,
uuid=uuidsentinel.cell2,
name='cell2',
transport_url='fake',
database_connection='fake')
cell2.create()
self.assertRaisesRegex(exception.ValidationError,
'Cell0',
self.migration.upgrade, self.engine)
def test_upgrade_with_no_host_mappings(self):
self._flavor_me()
cell0 = objects.CellMapping(context=self.context,
uuid=objects.CellMapping.CELL0_UUID,
name='cell0',
transport_url='fake',
database_connection='fake')
cell0.create()
cell1 = objects.CellMapping(context=self.context,
uuid=uuidsentinel.cell1,
name='cell1',
transport_url='fake',
database_connection='fake')
cell1.create()
self.assertRaisesRegex(exception.ValidationError,
'host mappings',
self.migration.upgrade, self.engine)
def test_upgrade_with_required_mappings(self):
self._flavor_me()
cell0 = objects.CellMapping(context=self.context,
uuid=objects.CellMapping.CELL0_UUID,
name='cell0',
transport_url='fake',
database_connection='fake')
cell0.create()
cell1 = objects.CellMapping(context=self.context,
uuid=uuidsentinel.cell1,
name='cell1',
transport_url='fake',
database_connection='fake')
cell1.create()
hostmapping = objects.HostMapping(context=self.context,
cell_mapping=cell1,
host='foo')
hostmapping.create()
self.migration.upgrade(self.engine)
def test_upgrade_new_deploy(self):
self.migration.upgrade(self.engine)

View File

@ -0,0 +1,7 @@
---
upgrade:
- Ocata requires that your deployment have created the
cell and host mappings in Newton. If you have not done
this, Ocata's `db sync` command will fail. Small deployments
will want to run `nova-manage db simple_cell_setup`
on Newton before upgrading.