Migrate upgrade checks to oslo.upgradecheck

The common upgrade check code has been moved to oslo.upgradecheck.
This change switches the Nova upgrade checks to use the common code
and removes the tests for functionality that is now the responsibility
of the library.

Change-Id: I0dc2044286dbe78314c650a92c4654f7f50642d2
This commit is contained in:
Ben Nemec 2018-09-18 21:28:22 +00:00
parent f6996903d2
commit c9436c3846
5 changed files with 75 additions and 271 deletions

View File

@ -90,6 +90,7 @@ oslo.reports==1.18.0
oslo.rootwrap==5.8.0
oslo.serialization==2.18.0
oslo.service==1.33.0
oslo.upgradecheck==0.1.1
oslo.utils==3.37.0
oslo.versionedobjects==1.33.3
oslo.vmware==2.17.0

View File

@ -19,18 +19,15 @@ CLI interface for nova status commands.
from __future__ import print_function
import collections
# enum comes from the enum34 package if python < 3.4, else it's stdlib
import enum
import functools
import sys
import textwrap
import traceback
from keystoneauth1 import exceptions as ks_exc
from oslo_config import cfg
from oslo_serialization import jsonutils
from oslo_upgradecheck import upgradecheck
import pkg_resources
import prettytable
from sqlalchemy import func as sqlfunc
from sqlalchemy import MetaData, Table, and_, select
from sqlalchemy.sql import false
@ -59,47 +56,7 @@ PLACEMENT_DOCS_LINK = 'https://docs.openstack.org/nova/latest' \
MIN_PLACEMENT_MICROVERSION = "1.30"
class UpgradeCheckCode(enum.IntEnum):
"""These are the status codes for the nova-status upgrade check command
and internal check commands.
"""
# All upgrade readiness checks passed successfully and there is
# nothing to do.
SUCCESS = 0
# At least one check encountered an issue and requires further
# investigation. This is considered a warning but the upgrade may be OK.
WARNING = 1
# There was an upgrade status check failure that needs to be
# investigated. This should be considered something that stops an upgrade.
FAILURE = 2
UPGRADE_CHECK_MSG_MAP = {
UpgradeCheckCode.SUCCESS: _('Success'),
UpgradeCheckCode.WARNING: _('Warning'),
UpgradeCheckCode.FAILURE: _('Failure'),
}
class UpgradeCheckResult(object):
"""Class used for 'nova-status upgrade check' results.
The 'code' attribute is an UpgradeCheckCode enum.
The 'details' attribute is a translated message generally only used for
checks that result in a warning or failure code. The details should provide
information on what issue was discovered along with any remediation.
"""
def __init__(self, code, details=None):
super(UpgradeCheckResult, self).__init__()
self.code = code
self.details = details
class UpgradeCommands(object):
class UpgradeCommands(upgradecheck.UpgradeCommands):
"""Commands related to upgrades.
The subcommands here must not rely on the nova object model since they
@ -152,7 +109,7 @@ class UpgradeCommands(object):
'cell0 and one for your first cell. Run command '
'\'nova-manage cell_v2 simple_cell_setup\' and then '
'retry.')
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
count = select([sqlfunc.count()]).select_from(cell_mappings).where(
cell_mappings.c.uuid ==
@ -161,7 +118,7 @@ class UpgradeCommands(object):
msg = _('No cell0 mapping found. Run command '
'\'nova-manage cell_v2 simple_cell_setup\' and then '
'retry.')
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
host_mappings = Table('host_mappings', meta, autoload=True)
count = select([sqlfunc.count()]).select_from(host_mappings).scalar()
@ -176,14 +133,14 @@ class UpgradeCommands(object):
msg = _('No host mappings found but there are compute nodes. '
'Run command \'nova-manage cell_v2 '
'simple_cell_setup\' and then retry.')
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
msg = _('No host mappings or compute nodes were found. Remember '
'to run command \'nova-manage cell_v2 discover_hosts\' '
'when new compute hosts are deployed.')
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS, msg)
return upgradecheck.Result(upgradecheck.Code.SUCCESS, msg)
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS)
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
@staticmethod
def _placement_get(path):
@ -213,24 +170,24 @@ class UpgradeCommands(object):
msg = (_('Placement API version %(needed)s needed, '
'you have %(current)s.') %
{'needed': needs_version, 'current': max_version})
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
except ks_exc.MissingAuthPlugin:
msg = _('No credentials specified for placement API in nova.conf.')
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
except ks_exc.Unauthorized:
msg = _('Placement service credentials do not work.')
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
except ks_exc.EndpointNotFound:
msg = _('Placement API endpoint not found.')
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
except ks_exc.DiscoveryFailure:
msg = _('Discovery for placement API URI failed.')
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
except ks_exc.NotFound:
msg = _('Placement API does not seem to be running.')
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS)
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
@staticmethod
def _count_compute_resource_providers():
@ -319,7 +276,7 @@ class UpgradeCommands(object):
'%(placement_docs_link)s for more details.') %
{'num_computes': num_computes,
'placement_docs_link': PLACEMENT_DOCS_LINK})
return UpgradeCheckResult(UpgradeCheckCode.WARNING, msg)
return upgradecheck.Result(upgradecheck.Code.WARNING, msg)
# There are no resource providers and no compute nodes so we
# assume this is a fresh install and move on. We should return a
@ -330,7 +287,7 @@ class UpgradeCommands(object):
'report into the Placement service. See '
'%(placement_docs_link)s for more details.') %
{'placement_docs_link': PLACEMENT_DOCS_LINK})
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS, msg)
return upgradecheck.Result(upgradecheck.Code.SUCCESS, msg)
elif num_rps < num_computes:
# There are fewer resource providers than compute nodes, so return
@ -349,10 +306,10 @@ class UpgradeCommands(object):
{'num_resource_providers': num_rps,
'num_compute_nodes': num_computes,
'placement_docs_link': PLACEMENT_DOCS_LINK})
return UpgradeCheckResult(UpgradeCheckCode.WARNING, msg)
return upgradecheck.Result(upgradecheck.Code.WARNING, msg)
else:
# We have RPs >= CNs which is what we want to see.
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS)
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
@staticmethod
def _is_ironic_instance_migrated(extras, inst):
@ -431,7 +388,7 @@ class UpgradeCommands(object):
# on this.
msg = (_('Unable to determine ironic flavor migration without '
'cell mappings.'))
return UpgradeCheckResult(UpgradeCheckCode.WARNING, msg)
return upgradecheck.Result(upgradecheck.Code.WARNING, msg)
if unmigrated_instance_count_by_cell:
# There are unmigrated ironic instances, so we need to fail.
@ -442,11 +399,11 @@ class UpgradeCommands(object):
cell_id, unmigrated_instance_count_by_cell[cell_id])
for cell_id in
sorted(unmigrated_instance_count_by_cell.keys())))
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
# Either there were no ironic compute nodes or all instances for
# those nodes are already migrated, so there is nothing to do.
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS)
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
def _get_min_service_version(self, context, binary):
meta = MetaData(bind=db_session.get_engine(context=context))
@ -475,7 +432,7 @@ class UpgradeCommands(object):
"""
# If we're using cells v1 then we don't care about this.
if CONF.cells.enable:
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS)
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
meta = MetaData(bind=db_session.get_api_engine())
cell_mappings = Table('cell_mappings', meta, autoload=True)
@ -487,7 +444,7 @@ class UpgradeCommands(object):
# on this.
msg = (_('Unable to determine API service versions without '
'cell mappings.'))
return UpgradeCheckResult(UpgradeCheckCode.WARNING, msg)
return upgradecheck.Result(upgradecheck.Code.WARNING, msg)
ctxt = nova_context.get_admin_context()
cells_with_old_api_services = []
@ -513,8 +470,8 @@ class UpgradeCommands(object):
"records. See "
"https://bugs.launchpad.net/nova/+bug/1759316 for "
"details.") % ', '.join(cells_with_old_api_services))
return UpgradeCheckResult(UpgradeCheckCode.WARNING, msg)
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS)
return upgradecheck.Result(upgradecheck.Code.WARNING, msg)
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
def _check_request_spec_migration(self):
"""Checks to make sure request spec migrations are complete.
@ -535,7 +492,7 @@ class UpgradeCommands(object):
# on this.
msg = (_('Unable to determine request spec migrations without '
'cell mappings.'))
return UpgradeCheckResult(UpgradeCheckCode.WARNING, msg)
return upgradecheck.Result(upgradecheck.Code.WARNING, msg)
request_specs = Table('request_specs', meta, autoload=True)
ctxt = nova_context.get_admin_context()
@ -573,8 +530,8 @@ class UpgradeCommands(object):
"'nova-manage db online_data_migrations' on each cell "
"to create the missing request specs.") %
', '.join(incomplete_cells))
return UpgradeCheckResult(UpgradeCheckCode.FAILURE, msg)
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS)
return upgradecheck.Result(upgradecheck.Code.FAILURE, msg)
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
def _check_console_auths(self):
"""Checks for console usage and warns with info for rolling upgrade.
@ -591,7 +548,7 @@ class UpgradeCommands(object):
# If the operator has already enabled the workaround, we don't need
# to check anything.
if CONF.cells.enable or CONF.workarounds.enable_consoleauth:
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS)
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
# We need to check cell0 for nova-consoleauth service records because
# it's possible a deployment could have services stored in the cell0
@ -606,7 +563,7 @@ class UpgradeCommands(object):
# return a warning. The cellsv2 check would have already failed
# on this.
msg = (_('Unable to check consoles without cell mappings.'))
return UpgradeCheckResult(UpgradeCheckCode.WARNING, msg)
return upgradecheck.Result(upgradecheck.Code.WARNING, msg)
ctxt = nova_context.get_admin_context()
# If we find a non-deleted, non-disabled nova-consoleauth service in
@ -652,15 +609,15 @@ class UpgradeCommands(object):
"console proxy host if you are performing a "
"rolling upgrade to enable consoles to "
"function during a partial upgrade.")
return UpgradeCheckResult(UpgradeCheckCode.WARNING,
return upgradecheck.Result(upgradecheck.Code.WARNING,
msg)
return UpgradeCheckResult(UpgradeCheckCode.SUCCESS)
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
# The format of the check functions is to return an UpgradeCheckResult
# object with the appropriate UpgradeCheckCode and details set. If the
# The format of the check functions is to return an upgradecheck.Result
# object with the appropriate upgradecheck.Code and details set. If the
# check hits warnings or failures then those should be stored in the
# returned UpgradeCheckResult's "details" attribute. The summary will
# returned upgradecheck.Result's "details" attribute. The summary will
# be rolled up at the end of the check() function. These functions are
# intended to be run in order and build on top of each other so order
# matters.
@ -681,65 +638,6 @@ class UpgradeCommands(object):
(_('Console Auths'), _check_console_auths),
)
def _get_details(self, upgrade_check_result):
if upgrade_check_result.details is not None:
# wrap the text on the details to 60 characters
return '\n'.join(textwrap.wrap(upgrade_check_result.details, 60,
subsequent_indent=' '))
def check(self):
"""Performs checks to see if the deployment is ready for upgrade.
These checks are expected to be run BEFORE services are restarted with
new code. These checks also require access to potentially all of the
Nova databases (nova, nova_api, nova_api_cell0) and external services
such as the placement API service.
:returns: UpgradeCheckCode
"""
return_code = UpgradeCheckCode.SUCCESS
# This is a list if 2-item tuples for the check name and it's results.
check_results = []
for name, func in self._upgrade_checks:
result = func(self)
# store the result of the check for the summary table
check_results.append((name, result))
# we want to end up with the highest level code of all checks
if result.code > return_code:
return_code = result.code
# We're going to build a summary table that looks like:
# +----------------------------------------------------+
# | Upgrade Check Results |
# +----------------------------------------------------+
# | Check: Cells v2 |
# | Result: Success |
# | Details: None |
# +----------------------------------------------------+
# | Check: Placement API |
# | Result: Failure |
# | Details: There is no placement-api endpoint in the |
# | service catalog. |
# +----------------------------------------------------+
t = prettytable.PrettyTable([_('Upgrade Check Results')],
hrules=prettytable.ALL)
t.align = 'l'
for name, result in check_results:
cell = (
_('Check: %(name)s\n'
'Result: %(result)s\n'
'Details: %(details)s') %
{
'name': name,
'result': UPGRADE_CHECK_MSG_MAP[result.code],
'details': self._get_details(result),
}
)
t.add_row([cell])
print(t)
return return_code
CATEGORIES = {
'upgrade': UpgradeCommands,

View File

@ -17,6 +17,7 @@ from six.moves import StringIO
from oslo_config import cfg
from oslo_config import fixture as config_fixture
from oslo_upgradecheck import upgradecheck
from oslo_utils.fixture import uuidsentinel as uuids
from oslo_utils import uuidutils
import placement.db_api
@ -97,7 +98,7 @@ class TestUpgradeCheckResourceProviders(test.NoDBTestCase):
self.useFixture(nova_fixtures.Database())
result = self.cmd._check_resource_providers()
# this is assumed to be base install so it's OK but with details
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
self.assertIn('There are no compute resource providers in the '
'Placement service nor are there compute nodes in the '
'database',
@ -114,7 +115,7 @@ class TestUpgradeCheckResourceProviders(test.NoDBTestCase):
# install and not a failure.
result = self.cmd._check_resource_providers()
# this is assumed to be base install so it's OK but with details
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
self.assertIn('There are no compute resource providers in the '
'Placement service nor are there compute nodes in the '
'database',
@ -141,7 +142,7 @@ class TestUpgradeCheckResourceProviders(test.NoDBTestCase):
cpu_info='{"arch": "x86_64"}')
cn.create()
result = self.cmd._check_resource_providers()
self.assertEqual(status.UpgradeCheckCode.WARNING, result.code)
self.assertEqual(upgradecheck.Code.WARNING, result.code)
self.assertIn('There are no compute resource providers in the '
'Placement service but there are 1 compute nodes in the '
'deployment.', result.details)
@ -192,7 +193,7 @@ class TestUpgradeCheckResourceProviders(test.NoDBTestCase):
self._create_resource_provider(FAKE_IP_POOL_INVENTORY)
result = self.cmd._check_resource_providers()
self.assertEqual(status.UpgradeCheckCode.WARNING, result.code)
self.assertEqual(upgradecheck.Code.WARNING, result.code)
self.assertIn('There are no compute resource providers in the '
'Placement service but there are 1 compute nodes in the '
'deployment.', result.details)
@ -225,7 +226,7 @@ class TestUpgradeCheckResourceProviders(test.NoDBTestCase):
self._create_resource_provider(FAKE_VCPU_INVENTORY)
result = self.cmd._check_resource_providers()
self.assertEqual(status.UpgradeCheckCode.WARNING, result.code)
self.assertEqual(upgradecheck.Code.WARNING, result.code)
self.assertIn('There are 1 compute resource providers and 2 compute '
'nodes in the deployment.', result.details)
@ -285,5 +286,5 @@ class TestUpgradeCheckResourceProviders(test.NoDBTestCase):
stub_count_compute_nodes)
result = self.cmd._check_resource_providers()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
self.assertIsNone(result.details)

View File

@ -27,6 +27,7 @@ from six.moves import StringIO
from keystoneauth1 import exceptions as ks_exc
from keystoneauth1 import loading as keystone
from keystoneauth1 import session
from oslo_upgradecheck import upgradecheck
from oslo_utils.fixture import uuidsentinel as uuids
from oslo_utils import uuidutils
from requests import models
@ -124,7 +125,7 @@ class TestPlacementCheck(test.NoDBTestCase):
"""
auth.side_effect = ks_exc.MissingAuthPlugin()
res = self.cmd._check_placement()
self.assertEqual(status.UpgradeCheckCode.FAILURE, res.code)
self.assertEqual(upgradecheck.Code.FAILURE, res.code)
self.assertIn('No credentials specified', res.details)
@mock.patch.object(keystone, "load_auth_from_conf_options")
@ -166,7 +167,7 @@ class TestPlacementCheck(test.NoDBTestCase):
"""
get.side_effect = ks_exc.Unauthorized()
res = self.cmd._check_placement()
self.assertEqual(status.UpgradeCheckCode.FAILURE, res.code)
self.assertEqual(upgradecheck.Code.FAILURE, res.code)
self.assertIn('Placement service credentials do not work', res.details)
@mock.patch.object(status.UpgradeCommands, "_placement_get")
@ -179,7 +180,7 @@ class TestPlacementCheck(test.NoDBTestCase):
"""
get.side_effect = ks_exc.EndpointNotFound()
res = self.cmd._check_placement()
self.assertEqual(status.UpgradeCheckCode.FAILURE, res.code)
self.assertEqual(upgradecheck.Code.FAILURE, res.code)
self.assertIn('Placement API endpoint not found', res.details)
@mock.patch.object(status.UpgradeCommands, "_placement_get")
@ -192,7 +193,7 @@ class TestPlacementCheck(test.NoDBTestCase):
"""
get.side_effect = ks_exc.DiscoveryFailure()
res = self.cmd._check_placement()
self.assertEqual(status.UpgradeCheckCode.FAILURE, res.code)
self.assertEqual(upgradecheck.Code.FAILURE, res.code)
self.assertIn('Discovery for placement API URI failed.', res.details)
@mock.patch.object(status.UpgradeCommands, "_placement_get")
@ -204,7 +205,7 @@ class TestPlacementCheck(test.NoDBTestCase):
"""
get.side_effect = ks_exc.NotFound()
res = self.cmd._check_placement()
self.assertEqual(status.UpgradeCheckCode.FAILURE, res.code)
self.assertEqual(upgradecheck.Code.FAILURE, res.code)
self.assertIn('Placement API does not seem to be running', res.details)
@mock.patch.object(status.UpgradeCommands, "_placement_get")
@ -219,7 +220,7 @@ class TestPlacementCheck(test.NoDBTestCase):
]
}
res = self.cmd._check_placement()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, res.code)
self.assertEqual(upgradecheck.Code.SUCCESS, res.code)
@mock.patch.object(status.UpgradeCommands, "_placement_get")
def test_version_comparison_does_not_use_floats(self, get):
@ -239,7 +240,7 @@ class TestPlacementCheck(test.NoDBTestCase):
]
}
res = self.cmd._check_placement()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, res.code)
self.assertEqual(upgradecheck.Code.SUCCESS, res.code)
@mock.patch.object(status.UpgradeCommands, "_placement_get")
def test_invalid_version(self, get):
@ -253,109 +254,11 @@ class TestPlacementCheck(test.NoDBTestCase):
]
}
res = self.cmd._check_placement()
self.assertEqual(status.UpgradeCheckCode.FAILURE, res.code)
self.assertEqual(upgradecheck.Code.FAILURE, res.code)
self.assertIn('Placement API version %s needed, you have 0.9' %
status.MIN_PLACEMENT_MICROVERSION, res.details)
class TestUpgradeCheckBasic(test.NoDBTestCase):
"""Tests for the nova-status upgrade check command.
The tests in this class should just test basic logic and use mock. Real
checks which require more elaborate fixtures or the database should be done
in separate test classes as they are more or less specific to a particular
release and may be removed in a later release after they are no longer
needed.
"""
def setUp(self):
super(TestUpgradeCheckBasic, self).setUp()
self.output = StringIO()
self.useFixture(fixtures.MonkeyPatch('sys.stdout', self.output))
self.cmd = status.UpgradeCommands()
def test_check_success(self):
fake_checks = (
('good', mock.Mock(return_value=status.UpgradeCheckResult(
status.UpgradeCheckCode.SUCCESS
))),
)
with mock.patch.object(self.cmd, '_upgrade_checks', fake_checks):
self.assertEqual(status.UpgradeCheckCode.SUCCESS, self.cmd.check())
expected = """\
+-----------------------+
| Upgrade Check Results |
+-----------------------+
| Check: good |
| Result: Success |
| Details: None |
+-----------------------+
"""
self.assertEqual(expected, self.output.getvalue())
def test_check_warning(self):
fake_checks = (
('good', mock.Mock(return_value=status.UpgradeCheckResult(
status.UpgradeCheckCode.SUCCESS
))),
('warn', mock.Mock(return_value=status.UpgradeCheckResult(
status.UpgradeCheckCode.WARNING, 'there might be a problem'
))),
)
with mock.patch.object(self.cmd, '_upgrade_checks', fake_checks):
self.assertEqual(status.UpgradeCheckCode.WARNING, self.cmd.check())
expected = """\
+-----------------------------------+
| Upgrade Check Results |
+-----------------------------------+
| Check: good |
| Result: Success |
| Details: None |
+-----------------------------------+
| Check: warn |
| Result: Warning |
| Details: there might be a problem |
+-----------------------------------+
"""
self.assertEqual(expected, self.output.getvalue())
def test_check_failure(self):
# make the error details over 60 characters so we test the wrapping
error_details = 'go back to bed' + '!' * 60
fake_checks = (
('good', mock.Mock(return_value=status.UpgradeCheckResult(
status.UpgradeCheckCode.SUCCESS
))),
('warn', mock.Mock(return_value=status.UpgradeCheckResult(
status.UpgradeCheckCode.WARNING, 'there might be a problem'
))),
('fail', mock.Mock(return_value=status.UpgradeCheckResult(
status.UpgradeCheckCode.FAILURE, error_details
))),
)
with mock.patch.object(self.cmd, '_upgrade_checks', fake_checks):
self.assertEqual(status.UpgradeCheckCode.FAILURE, self.cmd.check())
expected = """\
+-----------------------------------------------------------------------+
| Upgrade Check Results |
+-----------------------------------------------------------------------+
| Check: good |
| Result: Success |
| Details: None |
+-----------------------------------------------------------------------+
| Check: warn |
| Result: Warning |
| Details: there might be a problem |
+-----------------------------------------------------------------------+
| Check: fail |
| Result: Failure |
| Details: go back to bed!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! |
| !!!!!!!!!!!!!! |
+-----------------------------------------------------------------------+
"""
self.assertEqual(expected, self.output.getvalue())
class TestUpgradeCheckCellsV2(test.NoDBTestCase):
"""Tests for the nova-status upgrade cells v2 specific check."""
@ -374,7 +277,7 @@ class TestUpgradeCheckCellsV2(test.NoDBTestCase):
"""The cells v2 check should fail because there are no cell mappings.
"""
result = self.cmd._check_cellsv2()
self.assertEqual(status.UpgradeCheckCode.FAILURE, result.code)
self.assertEqual(upgradecheck.Code.FAILURE, result.code)
self.assertIn('There needs to be at least two cell mappings',
result.details)
@ -395,7 +298,7 @@ class TestUpgradeCheckCellsV2(test.NoDBTestCase):
self._create_cell_mapping(uuid)
result = self.cmd._check_cellsv2()
self.assertEqual(status.UpgradeCheckCode.FAILURE, result.code)
self.assertEqual(upgradecheck.Code.FAILURE, result.code)
self.assertIn('No cell0 mapping found', result.details)
def test_check_no_host_mappings_with_computes(self):
@ -418,7 +321,7 @@ class TestUpgradeCheckCellsV2(test.NoDBTestCase):
cn.create()
result = self.cmd._check_cellsv2()
self.assertEqual(status.UpgradeCheckCode.FAILURE, result.code)
self.assertEqual(upgradecheck.Code.FAILURE, result.code)
self.assertIn('No host mappings found but there are compute nodes',
result.details)
@ -429,7 +332,7 @@ class TestUpgradeCheckCellsV2(test.NoDBTestCase):
self._setup_cells()
result = self.cmd._check_cellsv2()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
self.assertIn('No host mappings or compute nodes were found',
result.details)
@ -446,7 +349,7 @@ class TestUpgradeCheckCellsV2(test.NoDBTestCase):
hm.create()
result = self.cmd._check_cellsv2()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
self.assertIsNone(result.details)
@ -522,7 +425,7 @@ class TestUpgradeCheckIronicFlavorMigration(test.NoDBTestCase):
warning.
"""
result = self.cmd._check_ironic_flavor_migration()
self.assertEqual(status.UpgradeCheckCode.WARNING, result.code)
self.assertEqual(upgradecheck.Code.WARNING, result.code)
self.assertIn('Unable to determine ironic flavor migration without '
'cell mappings', result.details)
@ -533,7 +436,7 @@ class TestUpgradeCheckIronicFlavorMigration(test.NoDBTestCase):
"""
self._setup_cells()
result = self.cmd._check_ironic_flavor_migration()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
def test_mixed_computes_deleted_ironic_instance(self):
"""Tests the scenario where we have a kvm compute node in one cell
@ -557,7 +460,7 @@ class TestUpgradeCheckIronicFlavorMigration(test.NoDBTestCase):
ctxt, self.cell_mappings['cell1'], ironic_node, is_deleted=True)
result = self.cmd._check_ironic_flavor_migration()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
def test_unmigrated_ironic_instances(self):
"""Tests a scenario where we have two cells with only ironic compute
@ -590,7 +493,7 @@ class TestUpgradeCheckIronicFlavorMigration(test.NoDBTestCase):
ctxt, cell, ironic_node, flavor_migrated=False)
result = self.cmd._check_ironic_flavor_migration()
self.assertEqual(status.UpgradeCheckCode.FAILURE, result.code)
self.assertEqual(upgradecheck.Code.FAILURE, result.code)
# Check the message - it should point out cell1 has one unmigrated
# instance and cell2 has two unmigrated instances.
unmigrated_instance_count_by_cell = {
@ -629,12 +532,12 @@ class TestUpgradeCheckAPIServiceVersion(test.NoDBTestCase):
"""
self.flags(enable=True, group='cells')
result = self.cmd._check_api_service_version()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
def test_check_no_cell_mappings_warning(self):
"""Warn when there are no cell mappings."""
result = self.cmd._check_api_service_version()
self.assertEqual(status.UpgradeCheckCode.WARNING, result.code)
self.assertEqual(upgradecheck.Code.WARNING, result.code)
self.assertEqual('Unable to determine API service versions without '
'cell mappings.', result.details)
@ -684,7 +587,7 @@ class TestUpgradeCheckAPIServiceVersion(test.NoDBTestCase):
binary='nova-compute', version=14)
result = self.cmd._check_api_service_version()
self.assertEqual(status.UpgradeCheckCode.WARNING, result.code)
self.assertEqual(upgradecheck.Code.WARNING, result.code)
# The only cell in the message should be cell0.
self.assertIn(cell0.uuid, result.details)
self.assertNotIn(cell1.uuid, result.details)
@ -707,7 +610,7 @@ class TestUpgradeCheckAPIServiceVersion(test.NoDBTestCase):
binary='nova-compute', version=15)
result = self.cmd._check_api_service_version()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
class TestUpgradeCheckRequestSpecMigration(test.NoDBTestCase):
@ -758,7 +661,7 @@ class TestUpgradeCheckRequestSpecMigration(test.NoDBTestCase):
warning.
"""
result = self.cmd._check_request_spec_migration()
self.assertEqual(status.UpgradeCheckCode.WARNING, result.code)
self.assertEqual(upgradecheck.Code.WARNING, result.code)
self.assertIn('Unable to determine request spec migrations without '
'cell mappings.', result.details)
@ -779,7 +682,7 @@ class TestUpgradeCheckRequestSpecMigration(test.NoDBTestCase):
ctxt, self.cell_mappings['cell2'], create_request_spec=True)
result = self.cmd._check_request_spec_migration()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
def test_unmigrated_request_spec_instances(self):
"""Tests the scenario that we have a migrated instance in cell1 and
@ -796,7 +699,7 @@ class TestUpgradeCheckRequestSpecMigration(test.NoDBTestCase):
self._create_instance_in_cell(ctxt, self.cell_mappings['cell2'])
result = self.cmd._check_request_spec_migration()
self.assertEqual(status.UpgradeCheckCode.FAILURE, result.code)
self.assertEqual(upgradecheck.Code.FAILURE, result.code)
self.assertIn("The following cells have instances which do not have "
"matching request_specs in the API database: %s Run "
"'nova-manage db online_data_migrations' on each cell "
@ -856,7 +759,7 @@ class TestUpgradeCheckConsoles(test.NoDBTestCase):
"""
self.flags(enable=True, group='cells')
result = self.cmd._check_console_auths()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
def test_check_workaround_enabled(self):
"""This is a 'success' case since the console auths check is
@ -864,7 +767,7 @@ class TestUpgradeCheckConsoles(test.NoDBTestCase):
"""
self.flags(enable_consoleauth=True, group='workarounds')
result = self.cmd._check_console_auths()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
def test_deleted_disabled_consoleauth(self):
"""Tests that services other than nova-consoleauth and deleted/disabled
@ -887,7 +790,7 @@ class TestUpgradeCheckConsoles(test.NoDBTestCase):
'nova-consoleauth', disabled=True)
result = self.cmd._check_console_auths()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
def test_consoleauth_with_upgrade_not_started(self):
"""Tests the scenario where the deployment is using consoles but has no
@ -908,7 +811,7 @@ class TestUpgradeCheckConsoles(test.NoDBTestCase):
'nova-compute', version=30)
result = self.cmd._check_console_auths()
self.assertEqual(status.UpgradeCheckCode.WARNING, result.code)
self.assertEqual(upgradecheck.Code.WARNING, result.code)
def test_consoleauth_with_upgrade_complete(self):
"""Tests the scenario where the deployment is using consoles and has
@ -939,7 +842,7 @@ class TestUpgradeCheckConsoles(test.NoDBTestCase):
'nova-compute', version=30)
result = self.cmd._check_console_auths()
self.assertEqual(status.UpgradeCheckCode.SUCCESS, result.code)
self.assertEqual(upgradecheck.Code.SUCCESS, result.code)
def test_consoleauth_with_upgrade_partial(self):
"""Tests the scenario where the deployment is using consoles and has
@ -968,7 +871,7 @@ class TestUpgradeCheckConsoles(test.NoDBTestCase):
result = self.cmd._check_console_auths()
self.assertEqual(status.UpgradeCheckCode.WARNING, result.code)
self.assertEqual(upgradecheck.Code.WARNING, result.code)
self.assertIn("One or more cells were found which have nova-compute "
"services older than Rocky. "
"Please set the '[workarounds]enable_consoleauth' "

View File

@ -42,6 +42,7 @@ oslo.context>=2.19.2 # Apache-2.0
oslo.log>=3.36.0 # Apache-2.0
oslo.reports>=1.18.0 # Apache-2.0
oslo.serialization!=2.19.1,>=2.18.0 # Apache-2.0
oslo.upgradecheck>=0.1.1
oslo.utils>=3.37.0 # Apache-2.0
oslo.db>=4.40.0 # Apache-2.0
oslo.rootwrap>=5.8.0 # Apache-2.0