Manage db sync command for cell0
Add the ability to sync the cell0 database using nova-manage. The `db sync` command will be used to sync the cell0 database. This ensures that operators will only have two db sync commands to perform in the single cell case. blueprint cells-cell0 Change-Id: I21ae13a6c029e8ac89484faa212434911160fd51
This commit is contained in:
parent
9a1f48403d
commit
24f0c5b9d6
@ -797,8 +797,26 @@ class DbCommands(object):
|
||||
pass
|
||||
|
||||
@args('--version', metavar='<version>', help='Database version')
|
||||
def sync(self, version=None):
|
||||
@args('--local_cell', action='store_true',
|
||||
help='Only sync db in the local cell: do not attempt to fan-out'
|
||||
'to all cells')
|
||||
def sync(self, version=None, local_cell=False):
|
||||
"""Sync the database up to the most recent version."""
|
||||
if not local_cell:
|
||||
ctxt = context.RequestContext()
|
||||
# NOTE(mdoff): Multiple cells not yet implemented. Currently
|
||||
# fanout only looks for cell0.
|
||||
try:
|
||||
cell_mapping = objects.CellMapping.get_by_uuid(ctxt,
|
||||
objects.CellMapping.CELL0_UUID)
|
||||
with context.target_cell(ctxt, cell_mapping):
|
||||
migration.db_sync(version, context=ctxt)
|
||||
except exception.CellMappingNotFound:
|
||||
print(_('WARNING: cell0 mapping not found - not'
|
||||
' syncing cell0.'))
|
||||
except Exception:
|
||||
print(_('ERROR: could not access cell mapping database - has'
|
||||
' api db been created?'))
|
||||
return migration.db_sync(version)
|
||||
|
||||
def version(self):
|
||||
|
@ -21,14 +21,14 @@ from nova.db.sqlalchemy import migration
|
||||
IMPL = migration
|
||||
|
||||
|
||||
def db_sync(version=None, database='main'):
|
||||
def db_sync(version=None, database='main', context=None):
|
||||
"""Migrate the database to `version` or the most recent version."""
|
||||
return IMPL.db_sync(version=version, database=database)
|
||||
return IMPL.db_sync(version=version, database=database, context=context)
|
||||
|
||||
|
||||
def db_version(database='main'):
|
||||
def db_version(database='main', context=None):
|
||||
"""Display the current database version."""
|
||||
return IMPL.db_version(database=database)
|
||||
return IMPL.db_version(database=database, context=context)
|
||||
|
||||
|
||||
def db_initial_version(database='main'):
|
||||
|
@ -37,44 +37,48 @@ _REPOSITORY = {}
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_engine(database='main'):
|
||||
def get_engine(database='main', context=None):
|
||||
if database == 'main':
|
||||
return db_session.get_engine()
|
||||
return db_session.get_engine(context=context)
|
||||
if database == 'api':
|
||||
return db_session.get_api_engine()
|
||||
if database == 'placement':
|
||||
return db_session.get_placement_engine()
|
||||
|
||||
|
||||
def db_sync(version=None, database='main'):
|
||||
def db_sync(version=None, database='main', context=None):
|
||||
if version is not None:
|
||||
try:
|
||||
version = int(version)
|
||||
except ValueError:
|
||||
raise exception.NovaException(_("version should be an integer"))
|
||||
|
||||
current_version = db_version(database)
|
||||
current_version = db_version(database, context=context)
|
||||
repository = _find_migrate_repo(database)
|
||||
if version is None or version > current_version:
|
||||
return versioning_api.upgrade(get_engine(database), repository,
|
||||
version)
|
||||
return versioning_api.upgrade(get_engine(database, context=context),
|
||||
repository, version)
|
||||
else:
|
||||
return versioning_api.downgrade(get_engine(database), repository,
|
||||
version)
|
||||
return versioning_api.downgrade(get_engine(database, context=context),
|
||||
repository, version)
|
||||
|
||||
|
||||
def db_version(database='main'):
|
||||
def db_version(database='main', context=None):
|
||||
repository = _find_migrate_repo(database)
|
||||
try:
|
||||
return versioning_api.db_version(get_engine(database), repository)
|
||||
return versioning_api.db_version(get_engine(database, context=context),
|
||||
repository)
|
||||
except versioning_exceptions.DatabaseNotControlledError as exc:
|
||||
meta = sqlalchemy.MetaData()
|
||||
engine = get_engine(database)
|
||||
engine = get_engine(database, context=context)
|
||||
meta.reflect(bind=engine)
|
||||
tables = meta.tables
|
||||
if len(tables) == 0:
|
||||
db_version_control(INIT_VERSION[database], database)
|
||||
return versioning_api.db_version(get_engine(database), repository)
|
||||
db_version_control(INIT_VERSION[database],
|
||||
database,
|
||||
context=context)
|
||||
return versioning_api.db_version(
|
||||
get_engine(database, context=context), repository)
|
||||
else:
|
||||
LOG.exception(exc)
|
||||
# Some pre-Essex DB's may not be version controlled.
|
||||
@ -156,9 +160,11 @@ def db_null_instance_uuid_scan(delete=False):
|
||||
return processed
|
||||
|
||||
|
||||
def db_version_control(version=None, database='main'):
|
||||
def db_version_control(version=None, database='main', context=None):
|
||||
repository = _find_migrate_repo(database)
|
||||
versioning_api.version_control(get_engine(database), repository, version)
|
||||
versioning_api.version_control(get_engine(database, context=context),
|
||||
repository,
|
||||
version)
|
||||
return version
|
||||
|
||||
|
||||
|
@ -58,7 +58,7 @@ class NovaAPIModelsSync(test_migrations.ModelsMigrationsSync):
|
||||
def migrate_engine(self):
|
||||
return self.engine
|
||||
|
||||
def get_engine(self):
|
||||
def get_engine(self, context=None):
|
||||
return self.migrate_engine
|
||||
|
||||
def get_metadata(self):
|
||||
|
@ -143,7 +143,7 @@ class NovaMigrationsCheckers(test_migrations.ModelsMigrationsSync,
|
||||
return_value=engine):
|
||||
sa_migration.db_sync()
|
||||
|
||||
def get_engine(self):
|
||||
def get_engine(self, context=None):
|
||||
return self.migrate_engine
|
||||
|
||||
def get_metadata(self):
|
||||
|
@ -107,9 +107,9 @@ class TestDbSync(test.NoDBTestCase):
|
||||
mock_find_repo, mock_version):
|
||||
database = 'fake'
|
||||
migration.db_sync(database=database)
|
||||
mock_version.assert_called_once_with(database)
|
||||
mock_version.assert_called_once_with(database, context=None)
|
||||
mock_find_repo.assert_called_once_with(database)
|
||||
mock_get_engine.assert_called_once_with(database)
|
||||
mock_get_engine.assert_called_once_with(database, context=None)
|
||||
mock_upgrade.assert_called_once_with('engine', 'repo', None)
|
||||
self.assertFalse(mock_downgrade.called)
|
||||
|
||||
@ -117,9 +117,9 @@ class TestDbSync(test.NoDBTestCase):
|
||||
mock_find_repo, mock_version):
|
||||
database = 'fake'
|
||||
migration.db_sync(1, database=database)
|
||||
mock_version.assert_called_once_with(database)
|
||||
mock_version.assert_called_once_with(database, context=None)
|
||||
mock_find_repo.assert_called_once_with(database)
|
||||
mock_get_engine.assert_called_once_with(database)
|
||||
mock_get_engine.assert_called_once_with(database, context=None)
|
||||
mock_downgrade.assert_called_once_with('engine', 'repo', 1)
|
||||
self.assertFalse(mock_upgrade.called)
|
||||
|
||||
@ -149,10 +149,12 @@ class TestDbVersion(test.NoDBTestCase):
|
||||
metadata), mock.patch.object(migration,
|
||||
'db_version_control') as mock_version_control:
|
||||
migration.db_version(database)
|
||||
mock_version_control.assert_called_once_with(0, database)
|
||||
mock_version_control.assert_called_once_with(0,
|
||||
database,
|
||||
context=None)
|
||||
db_version_calls = [mock.call('engine', 'repo')] * 2
|
||||
self.assertEqual(db_version_calls, mock_db_version.call_args_list)
|
||||
engine_calls = [mock.call(database)] * 3
|
||||
engine_calls = [mock.call(database, context=None)] * 3
|
||||
self.assertEqual(engine_calls, mock_get_engine.call_args_list)
|
||||
|
||||
|
||||
@ -176,7 +178,7 @@ class TestGetEngine(test.NoDBTestCase):
|
||||
return_value='engine') as mock_get_engine:
|
||||
engine = migration.get_engine()
|
||||
self.assertEqual('engine', engine)
|
||||
mock_get_engine.assert_called_once_with()
|
||||
mock_get_engine.assert_called_once_with(context=None)
|
||||
|
||||
def test_get_api_engine(self):
|
||||
with mock.patch.object(db_api, 'get_api_engine',
|
||||
|
@ -544,12 +544,38 @@ class DBCommandsTestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(sqla_migration, 'db_version', return_value=2)
|
||||
def test_version(self, sqla_migrate):
|
||||
self.commands.version()
|
||||
sqla_migrate.assert_called_once_with(database='main')
|
||||
sqla_migrate.assert_called_once_with(context=None, database='main')
|
||||
|
||||
@mock.patch.object(sqla_migration, 'db_sync')
|
||||
def test_sync(self, sqla_sync):
|
||||
self.commands.sync(version=4)
|
||||
sqla_sync.assert_called_once_with(version=4, database='main')
|
||||
self.commands.sync(version=4, local_cell=True)
|
||||
sqla_sync.assert_called_once_with(context=None,
|
||||
version=4, database='main')
|
||||
|
||||
@mock.patch('nova.db.migration.db_sync')
|
||||
@mock.patch.object(objects.CellMapping, 'get_by_uuid', return_value='map')
|
||||
def test_sync_cell0(self, mock_get_by_uuid, mock_db_sync):
|
||||
ctxt = context.get_admin_context()
|
||||
cell_ctxt = context.get_admin_context()
|
||||
with test.nested(
|
||||
mock.patch('nova.context.RequestContext',
|
||||
return_value=ctxt),
|
||||
mock.patch('nova.context.target_cell')) \
|
||||
as (mock_get_context,
|
||||
mock_target_cell):
|
||||
fake_target_cell_mock = mock.MagicMock()
|
||||
fake_target_cell_mock.__enter__.return_value = cell_ctxt
|
||||
mock_target_cell.return_value = fake_target_cell_mock
|
||||
self.commands.sync(version=4)
|
||||
mock_get_by_uuid.assert_called_once_with(ctxt,
|
||||
objects.CellMapping.CELL0_UUID)
|
||||
mock_target_cell.assert_called_once_with(ctxt, 'map')
|
||||
|
||||
db_sync_calls = [
|
||||
mock.call(4, context=ctxt),
|
||||
mock.call(4)
|
||||
]
|
||||
mock_db_sync.assert_has_calls(db_sync_calls)
|
||||
|
||||
def _fake_db_command(self, migrations=None):
|
||||
if migrations is None:
|
||||
@ -635,12 +661,14 @@ class ApiDbCommandsTestCase(test.NoDBTestCase):
|
||||
@mock.patch.object(sqla_migration, 'db_version', return_value=2)
|
||||
def test_version(self, sqla_migrate):
|
||||
self.commands.version()
|
||||
sqla_migrate.assert_called_once_with(database='api')
|
||||
sqla_migrate.assert_called_once_with(context=None,
|
||||
database='api')
|
||||
|
||||
@mock.patch.object(sqla_migration, 'db_sync')
|
||||
def test_sync(self, sqla_sync):
|
||||
self.commands.sync(version=4)
|
||||
sqla_sync.assert_called_once_with(version=4, database='api')
|
||||
sqla_sync.assert_called_once_with(context=None,
|
||||
version=4, database='api')
|
||||
|
||||
|
||||
class CellCommandsTestCase(test.NoDBTestCase):
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
upgrade:
|
||||
- |
|
||||
'nova-manage db sync' can now sync the cell0 database.
|
||||
The cell0 db is required to store instances that cannot be scheduled to
|
||||
any cell. Before the 'db sync' command is called a cell mapping
|
||||
for cell0 must have been created using 'nova-manage cell_v2 map_cell0'.
|
||||
This command only needs to be called when upgrading to CellsV2.
|
Loading…
Reference in New Issue
Block a user