From 7e71a4e0a21ae2df92396edadd921ce1467a25c4 Mon Sep 17 00:00:00 2001 From: Dan Smith Date: Tue, 20 Sep 2016 07:21:14 -0700 Subject: [PATCH] Make nova-manage online migrations more verbose This makes the online_data_migrations command for nova-manage a little more verbose in what it is doing. Each time it is run, it will show you all the migrations that need running and how many records remain for each. Basically, you run this until you see all zeroes. Change-Id: I78c6312ec8e226a6ef9185382b6180f246f8fe84 --- nova/cmd/manage.py | 23 ++++++++++++++++--- nova/tests/unit/test_nova_manage.py | 19 ++++++++++++--- ...se-online-migrations-bd6f57e43328d554.yaml | 6 +++++ 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/verbose-online-migrations-bd6f57e43328d554.yaml diff --git a/nova/cmd/manage.py b/nova/cmd/manage.py index a9a04f1e8358..13e04fb601d3 100644 --- a/nova/cmd/manage.py +++ b/nova/cmd/manage.py @@ -66,6 +66,7 @@ from oslo_log import log as logging import oslo_messaging as messaging from oslo_utils import importutils from oslo_utils import uuidutils +import prettytable import six import six.moves.urllib.parse as urlparse @@ -881,6 +882,7 @@ class DbCommands(object): def _run_migration(self, ctxt, max_count): ran = 0 + migrations = {} for migration_meth in self.online_migrations: count = max_count - ran try: @@ -890,16 +892,20 @@ class DbCommands(object): method=migration_meth)) found = done = 0 + name = migration_meth.__name__ if found: print(_('%(total)i rows matched query %(meth)s, %(done)i ' 'migrated') % {'total': found, - 'meth': migration_meth.__name__, + 'meth': name, 'done': done}) + migrations.setdefault(name, (0, 0)) + migrations[name] = (migrations[name][0] + found, + migrations[name][1] + done) if max_count is not None: ran += done if ran >= max_count: break - return ran + return migrations @args('--max-count', metavar='', dest='max_count', help='Maximum number of objects to consider') @@ -920,11 +926,22 @@ class DbCommands(object): print(_('Running batches of %i until complete') % max_count) ran = None + migration_info = {} while ran is None or ran != 0: - ran = self._run_migration(ctxt, max_count) + migrations = self._run_migration(ctxt, max_count) + migration_info.update(migrations) + ran = sum([done for found, done in migrations.values()]) if not unlimited: break + t = prettytable.PrettyTable([_('Migration'), + _('Total Needed'), + _('Completed')]) + for name in sorted(migration_info.keys()): + info = migration_info[name] + t.add_row([name, info[0], info[1]]) + print(t) + return ran and 1 or 0 diff --git a/nova/tests/unit/test_nova_manage.py b/nova/tests/unit/test_nova_manage.py index be997d1547b6..27c9550fa4c4 100644 --- a/nova/tests/unit/test_nova_manage.py +++ b/nova/tests/unit/test_nova_manage.py @@ -592,12 +592,24 @@ class DBCommandsTestCase(test.NoDBTestCase): @mock.patch('nova.context.get_admin_context') def test_online_migrations(self, mock_get_context): + self.useFixture(fixtures.MonkeyPatch('sys.stdout', StringIO())) ctxt = mock_get_context.return_value command_cls = self._fake_db_command() command = command_cls() command.online_data_migrations(10) command_cls.online_migrations[0].assert_called_once_with(ctxt, 10) command_cls.online_migrations[1].assert_called_once_with(ctxt, 6) + expected = """\ +5 rows matched query mock_mig_1, 4 migrated +6 rows matched query mock_mig_2, 6 migrated ++------------+--------------+-----------+ +| Migration | Total Needed | Completed | ++------------+--------------+-----------+ +| mock_mig_1 | 5 | 4 | +| mock_mig_2 | 6 | 6 | ++------------+--------------+-----------+ +""" + self.assertEqual(expected, sys.stdout.getvalue()) @mock.patch('nova.context.get_admin_context') def test_online_migrations_no_max_count(self, mock_get_context): @@ -622,6 +634,7 @@ class DBCommandsTestCase(test.NoDBTestCase): def test_online_migrations_error(self): fake_migration = mock.MagicMock() fake_migration.side_effect = Exception + fake_migration.__name__ = 'fake' command_cls = self._fake_db_command((fake_migration,)) command = command_cls() command.online_data_migrations(None) @@ -636,19 +649,19 @@ class DBCommandsTestCase(test.NoDBTestCase): def test_online_migrations_no_max(self): with mock.patch.object(self.commands, '_run_migration') as rm: - rm.return_value = 0 + rm.return_value = {} self.assertEqual(0, self.commands.online_data_migrations()) def test_online_migrations_finished(self): with mock.patch.object(self.commands, '_run_migration') as rm: - rm.return_value = 0 + rm.return_value = {} self.assertEqual(0, self.commands.online_data_migrations(max_count=5)) def test_online_migrations_not_finished(self): with mock.patch.object(self.commands, '_run_migration') as rm: - rm.return_value = 5 + rm.return_value = {'mig': (10, 5)} self.assertEqual(1, self.commands.online_data_migrations(max_count=5)) diff --git a/releasenotes/notes/verbose-online-migrations-bd6f57e43328d554.yaml b/releasenotes/notes/verbose-online-migrations-bd6f57e43328d554.yaml new file mode 100644 index 000000000000..299459dbdd78 --- /dev/null +++ b/releasenotes/notes/verbose-online-migrations-bd6f57e43328d554.yaml @@ -0,0 +1,6 @@ +--- +features: + - The nova-manage online_data_migrations command now prints a + tabular summary of completed and remaining records. The goal + here is to get all your numbers to zero. The previous execution + return code behavior is retained for scripting.