Implementation of db check command
This patch adds a new "glance-manage db check" command which will check the current state of the users upgrade repos and relay info back to the user if the user has any outstanding db upgrades left to run with appropriate exit code. Co-Authored-By: Bhagyashri Shewale <bhagyashri.shewale@nttdata.com> Implements: Ie1e2fec2361765ddf23da897abcf0e12e682612e Change-Id: I1e0b02d615690f65a17b4ccfe4e4a72cc9e15ada
This commit is contained in:
parent
55fa35963a
commit
08d1c7f930
@ -89,6 +89,39 @@ class DbCommands(object):
|
||||
'"glance-manage db sync" to place the database under '
|
||||
'alembic migration control.'))
|
||||
|
||||
def check(self):
|
||||
"""Report any pending database upgrades.
|
||||
|
||||
An exit code of 3 indicates db expand is needed, see stdout output.
|
||||
An exit code of 4 indicates db migrate is needed, see stdout output.
|
||||
An exit code of 5 indicates db contract is needed, see stdout output.
|
||||
"""
|
||||
engine = db_api.get_engine()
|
||||
self._validate_engine(engine)
|
||||
|
||||
curr_heads = alembic_migrations.get_current_alembic_heads()
|
||||
|
||||
expand_heads = alembic_migrations.get_alembic_branch_head(
|
||||
db_migration.EXPAND_BRANCH)
|
||||
contract_heads = alembic_migrations.get_alembic_branch_head(
|
||||
db_migration.CONTRACT_BRANCH)
|
||||
|
||||
if (contract_heads in curr_heads):
|
||||
print(_('Database is up to date. No upgrades needed.'))
|
||||
sys.exit()
|
||||
elif ((not expand_heads) or (expand_heads not in curr_heads)):
|
||||
print(_('Your database is not up to date. '
|
||||
'Your first step is to run `glance-manage db expand`.'))
|
||||
sys.exit(3)
|
||||
elif data_migrations.has_pending_migrations(db_api.get_engine()):
|
||||
print(_('Your database is not up to date. '
|
||||
'Your next step is to run `glance-manage db migrate`.'))
|
||||
sys.exit(4)
|
||||
elif ((not contract_heads) or (contract_heads not in curr_heads)):
|
||||
print(_('Your database is not up to date. '
|
||||
'Your next step is to run `glance-manage db contract`.'))
|
||||
sys.exit(5)
|
||||
|
||||
@args('--version', metavar='<version>', help='Database version')
|
||||
def upgrade(self, version='heads'):
|
||||
"""Upgrade the database's migration level"""
|
||||
@ -335,6 +368,9 @@ class DbLegacyCommands(object):
|
||||
def migrate(self):
|
||||
self.command_object.migrate()
|
||||
|
||||
def check(self):
|
||||
self.command_object.check()
|
||||
|
||||
def load_metadefs(self, path=None, merge=False,
|
||||
prefer_new=False, overwrite=False):
|
||||
self.command_object.load_metadefs(CONF.command.path,
|
||||
@ -384,6 +420,10 @@ def add_legacy_command_parsers(command_object, subparsers):
|
||||
parser.set_defaults(action_fn=legacy_command_object.migrate)
|
||||
parser.set_defaults(action='db_migrate')
|
||||
|
||||
parser = subparsers.add_parser('db_check')
|
||||
parser.set_defaults(action_fn=legacy_command_object.check)
|
||||
parser.set_defaults(action='db_check')
|
||||
|
||||
parser = subparsers.add_parser('db_load_metadefs')
|
||||
parser.set_defaults(action_fn=legacy_command_object.load_metadefs)
|
||||
parser.add_argument('path', nargs='?')
|
||||
|
@ -24,6 +24,8 @@ from oslo_db import options as db_options
|
||||
from glance.common import utils
|
||||
from glance.db import migration as db_migration
|
||||
from glance.db.sqlalchemy import alembic_migrations
|
||||
from glance.db.sqlalchemy.alembic_migrations import data_migrations
|
||||
from glance.db.sqlalchemy import api as db_api
|
||||
from glance.tests import functional
|
||||
from glance.tests.utils import depends_on_exe
|
||||
from glance.tests.utils import execute
|
||||
@ -46,16 +48,28 @@ class TestGlanceManage(functional.FunctionalTest):
|
||||
db_options.set_defaults(CONF, connection='sqlite:///%s' %
|
||||
self.db_filepath)
|
||||
|
||||
def _sync_db(self):
|
||||
def _db_command(self, db_method):
|
||||
with open(self.conf_filepath, 'w') as conf_file:
|
||||
conf_file.write('[DEFAULT]\n')
|
||||
conf_file.write(self.connection)
|
||||
conf_file.flush()
|
||||
|
||||
cmd = ('%s -m glance.cmd.manage --config-file %s db sync' %
|
||||
(sys.executable, self.conf_filepath))
|
||||
cmd = ('%s -m glance.cmd.manage --config-file %s db %s' %
|
||||
(sys.executable, self.conf_filepath, db_method))
|
||||
execute(cmd, raise_error=True)
|
||||
|
||||
def _check_db(self, expected_exitcode):
|
||||
with open(self.conf_filepath, 'w') as conf_file:
|
||||
conf_file.write('[DEFAULT]\n')
|
||||
conf_file.write(self.connection)
|
||||
conf_file.flush()
|
||||
|
||||
cmd = ('%s -m glance.cmd.manage --config-file %s db check' %
|
||||
(sys.executable, self.conf_filepath))
|
||||
exitcode, out, err = execute(cmd, raise_error=True,
|
||||
expected_exitcode=expected_exitcode)
|
||||
return exitcode, out
|
||||
|
||||
def _assert_table_exists(self, db_table):
|
||||
cmd = ("sqlite3 {0} \"SELECT name FROM sqlite_master WHERE "
|
||||
"type='table' AND name='{1}'\"").format(self.db_filepath,
|
||||
@ -68,7 +82,7 @@ class TestGlanceManage(functional.FunctionalTest):
|
||||
@skip_if_disabled
|
||||
def test_db_creation(self):
|
||||
"""Test schema creation by db_sync on a fresh DB"""
|
||||
self._sync_db()
|
||||
self._db_command(db_method='sync')
|
||||
|
||||
for table in ['images', 'image_tags', 'image_locations',
|
||||
'image_members', 'image_properties']:
|
||||
@ -78,7 +92,7 @@ class TestGlanceManage(functional.FunctionalTest):
|
||||
@skip_if_disabled
|
||||
def test_sync(self):
|
||||
"""Test DB sync which internally calls EMC"""
|
||||
self._sync_db()
|
||||
self._db_command(db_method='sync')
|
||||
contract_head = alembic_migrations.get_alembic_branch_head(
|
||||
db_migration.CONTRACT_BRANCH)
|
||||
|
||||
@ -86,3 +100,22 @@ class TestGlanceManage(functional.FunctionalTest):
|
||||
).format(self.db_filepath)
|
||||
exitcode, out, err = execute(cmd, raise_error=True)
|
||||
self.assertEqual(contract_head, out.rstrip().decode("utf-8"))
|
||||
|
||||
@depends_on_exe('sqlite3')
|
||||
@skip_if_disabled
|
||||
def test_check(self):
|
||||
exitcode, out = self._check_db(3)
|
||||
self.assertEqual(3, exitcode)
|
||||
|
||||
self._db_command(db_method='expand')
|
||||
if data_migrations.has_pending_migrations(db_api.get_engine()):
|
||||
exitcode, out = self._check_db(4)
|
||||
self.assertEqual(4, exitcode)
|
||||
|
||||
self._db_command(db_method='migrate')
|
||||
exitcode, out = self._check_db(5)
|
||||
self.assertEqual(5, exitcode)
|
||||
|
||||
self._db_command(db_method='contract')
|
||||
exitcode, out = self._check_db(0)
|
||||
self.assertEqual(0, exitcode)
|
||||
|
@ -17,6 +17,7 @@ from __future__ import absolute_import
|
||||
|
||||
import fixtures
|
||||
import mock
|
||||
from six.moves import StringIO
|
||||
|
||||
from glance.cmd import manage
|
||||
from glance.db.sqlalchemy import api as db_api
|
||||
@ -158,11 +159,80 @@ class TestLegacyManage(TestManageBase):
|
||||
|
||||
class TestManage(TestManageBase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestManage, self).setUp()
|
||||
self.db = manage.DbCommands()
|
||||
self.output = StringIO()
|
||||
self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.output))
|
||||
|
||||
@mock.patch('glance.db.sqlalchemy.api.get_engine')
|
||||
@mock.patch(
|
||||
'glance.db.sqlalchemy.alembic_migrations.data_migrations.'
|
||||
'has_pending_migrations')
|
||||
@mock.patch(
|
||||
'glance.db.sqlalchemy.alembic_migrations.get_current_alembic_heads')
|
||||
@mock.patch(
|
||||
'glance.db.sqlalchemy.alembic_migrations.get_alembic_branch_head')
|
||||
def test_db_check_result(self, mock_get_alembic_branch_head,
|
||||
mock_get_current_alembic_heads,
|
||||
mock_has_pending_migrations,
|
||||
get_mock_engine):
|
||||
|
||||
get_mock_engine.return_value = mock.Mock()
|
||||
engine = get_mock_engine.return_value
|
||||
engine.engine.name = 'postgresql'
|
||||
exit = self.assertRaises(SystemExit, self.db.check)
|
||||
self.assertIn('Rolling upgrades are currently supported only for '
|
||||
'MySQL and Sqlite', exit.code)
|
||||
|
||||
engine = get_mock_engine.return_value
|
||||
engine.engine.name = 'mysql'
|
||||
|
||||
mock_get_current_alembic_heads.return_value = ['ocata_contract01']
|
||||
mock_get_alembic_branch_head.return_value = 'pike_expand01'
|
||||
exit = self.assertRaises(SystemExit, self.db.check)
|
||||
self.assertEqual(3, exit.code)
|
||||
self.assertIn('Your database is not up to date. '
|
||||
'Your first step is to run `glance-manage db expand`.',
|
||||
self.output.getvalue())
|
||||
|
||||
mock_get_current_alembic_heads.return_value = ['pike_expand01']
|
||||
mock_get_alembic_branch_head.side_effect = ['pike_expand01', None]
|
||||
mock_has_pending_migrations.return_value = [mock.Mock()]
|
||||
exit = self.assertRaises(SystemExit, self.db.check)
|
||||
self.assertEqual(4, exit.code)
|
||||
self.assertIn('Your database is not up to date. '
|
||||
'Your next step is to run `glance-manage db migrate`.',
|
||||
self.output.getvalue())
|
||||
|
||||
mock_get_current_alembic_heads.return_value = ['pike_expand01']
|
||||
mock_get_alembic_branch_head.side_effect = ['pike_expand01',
|
||||
'pike_contract01']
|
||||
mock_has_pending_migrations.return_value = None
|
||||
exit = self.assertRaises(SystemExit, self.db.check)
|
||||
self.assertEqual(5, exit.code)
|
||||
self.assertIn('Your database is not up to date. '
|
||||
'Your next step is to run `glance-manage db contract`.',
|
||||
self.output.getvalue())
|
||||
|
||||
mock_get_current_alembic_heads.return_value = ['pike_contract01']
|
||||
mock_get_alembic_branch_head.side_effect = ['pike_expand01',
|
||||
'pike_contract01']
|
||||
mock_has_pending_migrations.return_value = None
|
||||
self.assertRaises(SystemExit, self.db.check)
|
||||
self.assertIn('Database is up to date. No upgrades needed.',
|
||||
self.output.getvalue())
|
||||
|
||||
@mock.patch.object(manage.DbCommands, 'version')
|
||||
def test_db_version(self, version):
|
||||
self._main_test_helper(['glance.cmd.manage', 'db', 'version'],
|
||||
manage.DbCommands.version)
|
||||
|
||||
@mock.patch.object(manage.DbCommands, 'check')
|
||||
def test_db_check(self, check):
|
||||
self._main_test_helper(['glance.cmd.manage', 'db', 'check'],
|
||||
manage.DbCommands.check)
|
||||
|
||||
@mock.patch.object(manage.DbCommands, 'sync')
|
||||
def test_db_sync(self, sync):
|
||||
self._main_test_helper(['glance.cmd.manage', 'db', 'sync'],
|
||||
|
@ -0,0 +1,27 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Added a new command ``glance-manage db check``, the command will
|
||||
allow a user to check the status of upgrades in the database.
|
||||
|
||||
upgrade:
|
||||
- |
|
||||
Using db check
|
||||
|
||||
In order to check the current state of your database upgrades, you may run the
|
||||
command ``glance-manage db check``. This will inform you of any
|
||||
outstanding actions you have left to take.
|
||||
|
||||
Here is a list of possible return codes:
|
||||
|
||||
- A return code of ``0`` means you are currently up to date with the latest
|
||||
migration script version and all ``db`` upgrades are complete.
|
||||
|
||||
- A return code of ``3`` means that an upgrade from your current database
|
||||
version is available and your first step is to run ``glance-manage db expand``.
|
||||
|
||||
- A return code of ``4`` means that the expansion stage is complete, and the
|
||||
next step is to run ``glance-manage db migrate``.
|
||||
|
||||
- A return code of ``5`` means that the expansion and data migration stages are
|
||||
complete, and the next step is to run ``glance-manage db contract``.
|
Loading…
Reference in New Issue
Block a user