Make cinder-manage online migrations more verbose
This makes the online_data_migrations command for cinder-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. Sample output of $cinder-manage db online_data_migrations +------------+-------+------+-----------+ | Migration | Found | Done | Remaining | +------------+-------+------+-----------+ | mock_mig_1 | 5 | 4 | 1 | | mock_mig_2 | 6 | 6 | 0 | +------------+-------+------+-----------+ Change-Id: I572bf2eb560698766d741bd2fd78c8b1067335d0
This commit is contained in:
parent
669fd027bf
commit
939fa2c0ff
@ -57,6 +57,7 @@ from __future__ import print_function
|
||||
|
||||
import logging as python_logging
|
||||
import os
|
||||
import prettytable
|
||||
import sys
|
||||
import time
|
||||
|
||||
@ -67,9 +68,6 @@ from oslo_log import log as logging
|
||||
import oslo_messaging as messaging
|
||||
from oslo_utils import timeutils
|
||||
|
||||
from cinder import i18n
|
||||
i18n.enable_lazy()
|
||||
|
||||
# Need to register global_opts
|
||||
from cinder.common import config # noqa
|
||||
from cinder.common import constants
|
||||
@ -246,6 +244,7 @@ class DbCommands(object):
|
||||
|
||||
def _run_migration(self, ctxt, max_count, ignore_state):
|
||||
ran = 0
|
||||
migrations = {}
|
||||
for migration_meth in self.online_migrations:
|
||||
count = max_count - ran
|
||||
try:
|
||||
@ -255,16 +254,24 @@ class DbCommands(object):
|
||||
{'method': migration_meth.__name__})
|
||||
found = done = 0
|
||||
|
||||
name = migration_meth.__name__
|
||||
remaining = found - done
|
||||
if found:
|
||||
print(_('%(total)i rows matched query %(meth)s, %(done)i '
|
||||
'migrated') % {'total': found,
|
||||
'meth': migration_meth.__name__,
|
||||
'done': done})
|
||||
print(_('%(found)i rows matched query %(meth)s, %(done)i '
|
||||
'migrated, %(remaining)i remaining') % {'found': found,
|
||||
'meth': name,
|
||||
'done': done,
|
||||
'remaining':
|
||||
remaining})
|
||||
migrations.setdefault(name, (0, 0, 0))
|
||||
migrations[name] = (migrations[name][0] + found,
|
||||
migrations[name][1] + done,
|
||||
migrations[name][2] + remaining)
|
||||
if max_count is not None:
|
||||
ran += done
|
||||
if ran >= max_count:
|
||||
break
|
||||
return ran
|
||||
return migrations
|
||||
|
||||
@args('--max_count', metavar='<number>', dest='max_count', type=int,
|
||||
help='Maximum number of objects to consider.')
|
||||
@ -286,11 +293,23 @@ 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, ignore_state)
|
||||
migrations = self._run_migration(ctxt, max_count, ignore_state)
|
||||
migration_info.update(migrations)
|
||||
ran = sum([done for found, done, remaining in migrations.values()])
|
||||
if not unlimited:
|
||||
break
|
||||
|
||||
t = prettytable.PrettyTable([_('Migration'),
|
||||
_('Found'),
|
||||
_('Done'),
|
||||
_('Remaining')])
|
||||
for name in sorted(migration_info.keys()):
|
||||
info = migration_info[name]
|
||||
t.add_row([name, info[0], info[1], info[2]])
|
||||
print(t)
|
||||
|
||||
sys.exit(1 if ran else 0)
|
||||
|
||||
|
||||
|
@ -16,10 +16,12 @@ import sys
|
||||
import time
|
||||
|
||||
import ddt
|
||||
import fixtures
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
from six.moves import StringIO
|
||||
|
||||
try:
|
||||
import rtslib_fb
|
||||
@ -224,6 +226,45 @@ class TestCinderManageCmd(test.TestCase):
|
||||
cinder_manage.DbCommands.online_migrations[0].assert_has_calls(
|
||||
(mock.call(mock.ANY, 50, False),) * 2)
|
||||
|
||||
def _fake_db_command(self, migrations=None):
|
||||
if migrations is None:
|
||||
mock_mig_1 = mock.MagicMock(__name__="mock_mig_1")
|
||||
mock_mig_2 = mock.MagicMock(__name__="mock_mig_2")
|
||||
mock_mig_1.return_value = (5, 4)
|
||||
mock_mig_2.return_value = (6, 6)
|
||||
migrations = (mock_mig_1, mock_mig_2)
|
||||
|
||||
class _CommandSub(cinder_manage.DbCommands):
|
||||
online_migrations = migrations
|
||||
|
||||
return _CommandSub
|
||||
|
||||
@mock.patch('cinder.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
|
||||
db_cmds = self._fake_db_command()
|
||||
command = db_cmds()
|
||||
exit = self.assertRaises(SystemExit,
|
||||
command.online_data_migrations, 10)
|
||||
self.assertEqual(1, exit.code)
|
||||
expected = """\
|
||||
5 rows matched query mock_mig_1, 4 migrated, 1 remaining
|
||||
6 rows matched query mock_mig_2, 6 migrated, 0 remaining
|
||||
+------------+-------+------+-----------+
|
||||
| Migration | Found | Done | Remaining |
|
||||
+------------+-------+------+-----------+
|
||||
| mock_mig_1 | 5 | 4 | 1 |
|
||||
| mock_mig_2 | 6 | 6 | 0 |
|
||||
+------------+-------+------+-----------+
|
||||
"""
|
||||
command.online_migrations[0].assert_has_calls([mock.call(ctxt,
|
||||
10, False)])
|
||||
command.online_migrations[1].assert_has_calls([mock.call(ctxt,
|
||||
6, False)])
|
||||
|
||||
self.assertEqual(expected, sys.stdout.getvalue())
|
||||
|
||||
@mock.patch('cinder.cmd.manage.DbCommands.online_migrations',
|
||||
(mock.Mock(side_effect=((2, 2), (0, 0)), __name__='foo'),))
|
||||
def test_db_commands_online_data_migrations_ignore_state_and_max(self):
|
||||
|
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- The cinder-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.
|
Loading…
Reference in New Issue
Block a user