Merge "Migrate upgrade checks to oslo.upgradecheck"
This commit is contained in:
commit
2e9356374e
@ -89,6 +89,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
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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' "
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user