Make nova-manage db purge take --all-cells
This makes purge iterate over all cells if requested. This also makes our post_test_hook.sh use the --all-cells variant with just the base config file. Related to blueprint purge-db Change-Id: I7eb5ed05224838cdba18e96724162cc930f4422e
This commit is contained in:
parent
ff47787e11
commit
fd59fbd4d1
@ -73,15 +73,17 @@ Nova Database
|
|||||||
is desired for the purge, then run ``nova-manage db purge --before
|
is desired for the purge, then run ``nova-manage db purge --before
|
||||||
<date>`` manually after archiving is complete.
|
<date>`` manually after archiving is complete.
|
||||||
|
|
||||||
``nova-manage db purge [--all] [--before <date>] [--verbose]``
|
``nova-manage db purge [--all] [--before <date>] [--verbose] [--all-cells]``
|
||||||
|
|
||||||
Delete rows from shadow tables. Specifying --all will delete all data from
|
Delete rows from shadow tables. Specifying --all will delete all data from
|
||||||
all shadow tables. Specifying --before will delete data from all shadow tables
|
all shadow tables. Specifying --before will delete data from all shadow tables
|
||||||
that is older than the date provided. Date strings may be fuzzy, such as
|
that is older than the date provided. Date strings may be fuzzy, such as
|
||||||
``Oct 21 2015``. Returns exit code 0 if rows were deleted, 1 if required
|
``Oct 21 2015``. Specifying --verbose will cause information to be printed about
|
||||||
arguments are not provided, 2 if an invalid date is provided, 3 if no data
|
purged records. Specifying --all-cells will cause the purge to be applied against
|
||||||
was deleted. Specifying --verbose will cause information to be printed about
|
all cell databases. For --all-cells to work, the api database connection
|
||||||
purged records.
|
information must be configured. Returns exit code 0 if rows were deleted, 1 if
|
||||||
|
required arguments are not provided, 2 if an invalid date is provided, 3 if no
|
||||||
|
data was deleted, 4 if the list of cells cannot be obtained.
|
||||||
|
|
||||||
``nova-manage db null_instance_uuid_scan [--delete]``
|
``nova-manage db null_instance_uuid_scan [--delete]``
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ function archive_deleted_rows {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function purge_db {
|
function purge_db {
|
||||||
$MANAGE $* db purge --all --verbose
|
$MANAGE db purge --all --verbose --all-cells
|
||||||
RET=$?
|
RET=$?
|
||||||
if [[ $RET -eq 0 ]]; then
|
if [[ $RET -eq 0 ]]; then
|
||||||
echo Purge successful
|
echo Purge successful
|
||||||
@ -40,7 +40,7 @@ cell_conf=$(conductor_conf 1)
|
|||||||
conf="--config-file $NOVA_CONF --config-file $cell_conf"
|
conf="--config-file $NOVA_CONF --config-file $cell_conf"
|
||||||
|
|
||||||
archive_deleted_rows $conf
|
archive_deleted_rows $conf
|
||||||
purge_db $conf
|
purge_db
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
# We need to get the admin credentials to run the OSC CLIs for Placement.
|
# We need to get the admin credentials to run the OSC CLIs for Placement.
|
||||||
|
@ -562,7 +562,10 @@ Error: %s""") % six.text_type(e))
|
|||||||
help='Purge all rows in the shadow tables')
|
help='Purge all rows in the shadow tables')
|
||||||
@args('--verbose', dest='verbose', action='store_true', default=False,
|
@args('--verbose', dest='verbose', action='store_true', default=False,
|
||||||
help='Print information about purged records')
|
help='Print information about purged records')
|
||||||
def purge(self, before=None, purge_all=False, verbose=False):
|
@args('--all-cells', dest='all_cells', action='store_true', default=False,
|
||||||
|
help='Run against all cell databases')
|
||||||
|
def purge(self, before=None, purge_all=False, verbose=False,
|
||||||
|
all_cells=False):
|
||||||
if before is None and purge_all is False:
|
if before is None and purge_all is False:
|
||||||
print(_('Either --before or --all is required'))
|
print(_('Either --before or --all is required'))
|
||||||
return 1
|
return 1
|
||||||
@ -577,9 +580,28 @@ Error: %s""") % six.text_type(e))
|
|||||||
|
|
||||||
def status(msg):
|
def status(msg):
|
||||||
if verbose:
|
if verbose:
|
||||||
print(msg)
|
print('%s: %s' % (identity, msg))
|
||||||
|
|
||||||
deleted = sa_db.purge_shadow_tables(before_date, status_fn=status)
|
deleted = 0
|
||||||
|
admin_ctxt = context.get_admin_context()
|
||||||
|
|
||||||
|
if all_cells:
|
||||||
|
try:
|
||||||
|
cells = objects.CellMappingList.get_all(admin_ctxt)
|
||||||
|
except db_exc.DBError:
|
||||||
|
print(_('Unable to get cell list from API DB. '
|
||||||
|
'Is it configured?'))
|
||||||
|
return 4
|
||||||
|
for cell in cells:
|
||||||
|
identity = _('Cell %s') % cell.identity
|
||||||
|
with context.target_cell(admin_ctxt, cell) as cctxt:
|
||||||
|
deleted += sa_db.purge_shadow_tables(cctxt,
|
||||||
|
before_date,
|
||||||
|
status_fn=status)
|
||||||
|
else:
|
||||||
|
identity = _('DB')
|
||||||
|
deleted = sa_db.purge_shadow_tables(admin_ctxt,
|
||||||
|
before_date, status_fn=status)
|
||||||
if deleted:
|
if deleted:
|
||||||
return 0
|
return 0
|
||||||
else:
|
else:
|
||||||
|
@ -5926,8 +5926,8 @@ def _purgeable_tables(metadata):
|
|||||||
t.name.endswith('migrate_version'))]
|
t.name.endswith('migrate_version'))]
|
||||||
|
|
||||||
|
|
||||||
def purge_shadow_tables(before_date, status_fn=None):
|
def purge_shadow_tables(context, before_date, status_fn=None):
|
||||||
engine = get_engine()
|
engine = get_engine(context=context)
|
||||||
conn = engine.connect()
|
conn = engine.connect()
|
||||||
metadata = MetaData()
|
metadata = MetaData()
|
||||||
metadata.bind = engine
|
metadata.bind = engine
|
||||||
|
@ -178,7 +178,9 @@ class TestDatabaseArchive(test_servers.ServersTestBase):
|
|||||||
def status(msg):
|
def status(msg):
|
||||||
lines.append(msg)
|
lines.append(msg)
|
||||||
|
|
||||||
deleted = sqlalchemy_api.purge_shadow_tables(None, status_fn=status)
|
admin_context = context.get_admin_context()
|
||||||
|
deleted = sqlalchemy_api.purge_shadow_tables(admin_context,
|
||||||
|
None, status_fn=status)
|
||||||
self.assertNotEqual(0, deleted)
|
self.assertNotEqual(0, deleted)
|
||||||
self.assertNotEqual(0, len(lines))
|
self.assertNotEqual(0, len(lines))
|
||||||
for line in lines:
|
for line in lines:
|
||||||
@ -199,7 +201,9 @@ class TestDatabaseArchive(test_servers.ServersTestBase):
|
|||||||
pre_purge_results = self._get_table_counts()
|
pre_purge_results = self._get_table_counts()
|
||||||
|
|
||||||
past = timeutils.utcnow() - datetime.timedelta(hours=1)
|
past = timeutils.utcnow() - datetime.timedelta(hours=1)
|
||||||
deleted = sqlalchemy_api.purge_shadow_tables(past)
|
admin_context = context.get_admin_context()
|
||||||
|
deleted = sqlalchemy_api.purge_shadow_tables(admin_context,
|
||||||
|
past)
|
||||||
# Make sure we didn't delete anything if the marker is before
|
# Make sure we didn't delete anything if the marker is before
|
||||||
# we started
|
# we started
|
||||||
self.assertEqual(0, deleted)
|
self.assertEqual(0, deleted)
|
||||||
@ -209,7 +213,7 @@ class TestDatabaseArchive(test_servers.ServersTestBase):
|
|||||||
self.assertEqual(pre_purge_results, results)
|
self.assertEqual(pre_purge_results, results)
|
||||||
|
|
||||||
future = timeutils.utcnow() + datetime.timedelta(hours=1)
|
future = timeutils.utcnow() + datetime.timedelta(hours=1)
|
||||||
deleted = sqlalchemy_api.purge_shadow_tables(future)
|
deleted = sqlalchemy_api.purge_shadow_tables(admin_context, future)
|
||||||
# Make sure we deleted things when the marker is after
|
# Make sure we deleted things when the marker is after
|
||||||
# we started
|
# we started
|
||||||
self.assertNotEqual(0, deleted)
|
self.assertNotEqual(0, deleted)
|
||||||
@ -228,5 +232,6 @@ class TestDatabaseArchive(test_servers.ServersTestBase):
|
|||||||
results, deleted_ids = db.archive_deleted_rows(max_rows=1000)
|
results, deleted_ids = db.archive_deleted_rows(max_rows=1000)
|
||||||
self.assertEqual([server_id], deleted_ids)
|
self.assertEqual([server_id], deleted_ids)
|
||||||
date = dateutil_parser.parse('oct 21 2015', fuzzy=True)
|
date = dateutil_parser.parse('oct 21 2015', fuzzy=True)
|
||||||
deleted = sqlalchemy_api.purge_shadow_tables(date)
|
admin_context = context.get_admin_context()
|
||||||
|
deleted = sqlalchemy_api.purge_shadow_tables(admin_context, date)
|
||||||
self.assertEqual(0, deleted)
|
self.assertEqual(0, deleted)
|
||||||
|
@ -479,7 +479,8 @@ Rows were archived, running purge...
|
|||||||
mock_db_archive.assert_has_calls([mock.call(20),
|
mock_db_archive.assert_has_calls([mock.call(20),
|
||||||
mock.call(20),
|
mock.call(20),
|
||||||
mock.call(20)])
|
mock.call(20)])
|
||||||
mock_db_purge.assert_called_once_with(None, status_fn=mock.ANY)
|
mock_db_purge.assert_called_once_with(mock.ANY, None,
|
||||||
|
status_fn=mock.ANY)
|
||||||
|
|
||||||
def test_archive_deleted_rows_until_stopped_quiet(self):
|
def test_archive_deleted_rows_until_stopped_quiet(self):
|
||||||
self.test_archive_deleted_rows_until_stopped(verbose=False)
|
self.test_archive_deleted_rows_until_stopped(verbose=False)
|
||||||
@ -547,14 +548,15 @@ Rows were archived, running purge...
|
|||||||
mock_purge.return_value = 1
|
mock_purge.return_value = 1
|
||||||
ret = self.commands.purge(purge_all=True)
|
ret = self.commands.purge(purge_all=True)
|
||||||
self.assertEqual(0, ret)
|
self.assertEqual(0, ret)
|
||||||
mock_purge.assert_called_once_with(None, status_fn=mock.ANY)
|
mock_purge.assert_called_once_with(mock.ANY, None, status_fn=mock.ANY)
|
||||||
|
|
||||||
@mock.patch('nova.db.sqlalchemy.api.purge_shadow_tables')
|
@mock.patch('nova.db.sqlalchemy.api.purge_shadow_tables')
|
||||||
def test_purge_date(self, mock_purge):
|
def test_purge_date(self, mock_purge):
|
||||||
mock_purge.return_value = 1
|
mock_purge.return_value = 1
|
||||||
ret = self.commands.purge(before='oct 21 2015')
|
ret = self.commands.purge(before='oct 21 2015')
|
||||||
self.assertEqual(0, ret)
|
self.assertEqual(0, ret)
|
||||||
mock_purge.assert_called_once_with(datetime.datetime(2015, 10, 21),
|
mock_purge.assert_called_once_with(mock.ANY,
|
||||||
|
datetime.datetime(2015, 10, 21),
|
||||||
status_fn=mock.ANY)
|
status_fn=mock.ANY)
|
||||||
|
|
||||||
@mock.patch('nova.db.sqlalchemy.api.purge_shadow_tables')
|
@mock.patch('nova.db.sqlalchemy.api.purge_shadow_tables')
|
||||||
@ -575,6 +577,44 @@ Rows were archived, running purge...
|
|||||||
ret = self.commands.purge(purge_all=True)
|
ret = self.commands.purge(purge_all=True)
|
||||||
self.assertEqual(3, ret)
|
self.assertEqual(3, ret)
|
||||||
|
|
||||||
|
@mock.patch('nova.db.sqlalchemy.api.purge_shadow_tables')
|
||||||
|
@mock.patch('nova.objects.CellMappingList.get_all')
|
||||||
|
def test_purge_all_cells(self, mock_get_cells, mock_purge):
|
||||||
|
cell1 = objects.CellMapping(uuid=uuidsentinel.cell1, name='cell1',
|
||||||
|
database_connection='foo1',
|
||||||
|
transport_url='bar1')
|
||||||
|
cell2 = objects.CellMapping(uuid=uuidsentinel.cell2, name='cell2',
|
||||||
|
database_connection='foo2',
|
||||||
|
transport_url='bar2')
|
||||||
|
|
||||||
|
mock_get_cells.return_value = [cell1, cell2]
|
||||||
|
|
||||||
|
values = [123, 456]
|
||||||
|
|
||||||
|
def fake_purge(*args, **kwargs):
|
||||||
|
val = values.pop(0)
|
||||||
|
kwargs['status_fn'](val)
|
||||||
|
return val
|
||||||
|
mock_purge.side_effect = fake_purge
|
||||||
|
|
||||||
|
ret = self.commands.purge(purge_all=True, all_cells=True, verbose=True)
|
||||||
|
self.assertEqual(0, ret)
|
||||||
|
mock_get_cells.assert_called_once_with(mock.ANY)
|
||||||
|
output = self.output.getvalue()
|
||||||
|
expected = """\
|
||||||
|
Cell %s: 123
|
||||||
|
Cell %s: 456
|
||||||
|
""" % (cell1.identity, cell2.identity)
|
||||||
|
|
||||||
|
self.assertEqual(expected, output)
|
||||||
|
|
||||||
|
@mock.patch('nova.objects.CellMappingList.get_all')
|
||||||
|
def test_purge_all_cells_no_api_config(self, mock_get_cells):
|
||||||
|
mock_get_cells.side_effect = db_exc.DBError
|
||||||
|
ret = self.commands.purge(purge_all=True, all_cells=True)
|
||||||
|
self.assertEqual(4, ret)
|
||||||
|
self.assertIn('Unable to get cell list', self.output.getvalue())
|
||||||
|
|
||||||
@mock.patch.object(migration, 'db_null_instance_uuid_scan',
|
@mock.patch.object(migration, 'db_null_instance_uuid_scan',
|
||||||
return_value={'foo': 0})
|
return_value={'foo': 0})
|
||||||
def test_null_instance_uuid_scan_no_records_found(self, mock_scan):
|
def test_null_instance_uuid_scan_no_records_found(self, mock_scan):
|
||||||
|
Loading…
Reference in New Issue
Block a user