Require nova_client.api_version >= 2.56

The [nova_client]/api_version defaults to 2.56 since
change Idd6ebc94f81ad5d65256c80885f2addc1aaeaae1. There
is compatibility code for that change but if 2.56 is
not available watcher_non_live_migrate_instance will
still fail if a destination host is used.

Since 2.56 has been available since the Queens version of
nova it should be reasonable to require at least that
version of nova is running for using Watcher.

This adds code which enforces the minimum version along
with a release note and "watcher-status upgrade check"
check method.

Note that it's kind of weird for watcher to have a config
option like nova_client.api_version since compute API
microversions are per API request even though novaclient
is constructed with the single configured version. It should
really be something the client (watcher in this case) determines
using version discovery and gracefully enables features if
the required nova API version is available, but that's a bigger
change.

Change-Id: Id34938c7bb8a5ca934d997e52cac3b365414c006
This commit is contained in:
Matt Riedemann 2019-05-14 20:42:45 -04:00
parent 1e6ce53273
commit 7489126d83
7 changed files with 88 additions and 16 deletions

View File

@ -81,3 +81,7 @@ Upgrade
**2.0.0 (Stein)**
* Sample check to be filled in with checks as they are added in Stein.
**3.0.0 (Train)**
* A check was added to enforce the minimum required version of nova API used.

View File

@ -0,0 +1,8 @@
---
upgrade:
- |
The minimum required version of the ``[nova_client]/api_version`` value
is now enforced to be ``2.56`` which is available since the Queens version
of the nova compute service.
A ``watcher-status upgrade check`` has been added for this.

View File

@ -15,8 +15,10 @@
import sys
from oslo_upgradecheck import upgradecheck
import six
from watcher._i18n import _
from watcher.common import clients
from watcher import conf
CONF = conf.CONF
@ -30,17 +32,18 @@ class Checks(upgradecheck.UpgradeCommands):
and added to _upgrade_checks tuple.
"""
def _sample_check(self):
"""This is sample check added to test the upgrade check framework
It needs to be removed after adding any real upgrade check
"""
return upgradecheck.Result(upgradecheck.Code.SUCCESS, 'Sample detail')
def _minimum_nova_api_version(self):
"""Checks the minimum required version of nova_client.api_version"""
try:
clients.check_min_nova_api_version(CONF.nova_client.api_version)
except ValueError as e:
return upgradecheck.Result(
upgradecheck.Code.FAILURE, six.text_type(e))
return upgradecheck.Result(upgradecheck.Code.SUCCESS)
_upgrade_checks = (
# Sample check added for now.
# Whereas in future real checks must be added here in tuple
(_('Sample Check'), _sample_check),
# Added in Train.
(_('Minimum Nova API Version'), _minimum_nova_api_version),
)

View File

@ -20,6 +20,7 @@ from keystoneauth1 import loading as ka_loading
from keystoneclient import client as keyclient
from monascaclient import client as monclient
from neutronclient.neutron import client as netclient
from novaclient import api_versions as nova_api_versions
from novaclient import client as nvclient
from watcher.common import exception
@ -34,6 +35,26 @@ CONF = cfg.CONF
_CLIENTS_AUTH_GROUP = 'watcher_clients_auth'
# NOTE(mriedem): This is the minimum required version of the nova API for
# watcher features to work. If new features are added which require new
# versions, they should perform version discovery and be backward compatible
# for at least one release before raising the minimum required version.
MIN_NOVA_API_VERSION = '2.56'
def check_min_nova_api_version(config_version):
"""Validates the minimum required nova API version.
:param config_version: The configured [nova_client]/api_version value
:raises: ValueError if the configured version is less than the required
minimum
"""
min_required = nova_api_versions.APIVersion(MIN_NOVA_API_VERSION)
if nova_api_versions.APIVersion(config_version) < min_required:
raise ValueError('Invalid nova_client.api_version %s. %s or '
'greater is required.' % (config_version,
MIN_NOVA_API_VERSION))
class OpenStackClients(object):
"""Convenience class to create and cache client instances."""
@ -87,6 +108,9 @@ class OpenStackClients(object):
return self._nova
novaclient_version = self._get_client_option('nova', 'api_version')
check_min_nova_api_version(novaclient_version)
nova_endpoint_type = self._get_client_option('nova', 'endpoint_type')
nova_region_name = self._get_client_option('nova', 'region_name')
self._nova = nvclient.Client(novaclient_version,

View File

@ -18,13 +18,24 @@
from oslo_config import cfg
from watcher.common import clients
nova_client = cfg.OptGroup(name='nova_client',
title='Configuration Options for Nova')
NOVA_CLIENT_OPTS = [
cfg.StrOpt('api_version',
default='2.56',
help='Version of Nova API to use in novaclient.'),
help="""
Version of Nova API to use in novaclient.
Minimum required version: %s
Certain Watcher features depend on a minimum version of the compute
API being available which is enforced with this option. See
https://docs.openstack.org/nova/latest/reference/api-microversion-history.html
for the compute API microversion history.
""" % clients.MIN_NOVA_API_VERSION),
cfg.StrOpt('endpoint_type',
default='publicURL',
help='Type of endpoint to use in novaclient. '

View File

@ -15,8 +15,11 @@
from oslo_upgradecheck.upgradecheck import Code
from watcher.cmd import status
from watcher import conf
from watcher.tests import base
CONF = conf.CONF
class TestUpgradeChecks(base.TestCase):
@ -24,7 +27,16 @@ class TestUpgradeChecks(base.TestCase):
super(TestUpgradeChecks, self).setUp()
self.cmd = status.Checks()
def test__sample_check(self):
check_result = self.cmd._sample_check()
self.assertEqual(
Code.SUCCESS, check_result.code)
def test_minimum_nova_api_version_ok(self):
# Tests that the default [nova_client]/api_version meets the minimum
# required version.
result = self.cmd._minimum_nova_api_version()
self.assertEqual(Code.SUCCESS, result.code)
def test_minimum_nova_api_version_fail(self):
# Tests the scenario that [nova_client]/api_version is less than the
# minimum required version.
CONF.set_override('api_version', '2.47', group='nova_client')
result = self.cmd._minimum_nova_api_version()
self.assertEqual(Code.FAILURE, result.code)
self.assertIn('Invalid nova_client.api_version 2.47.', result.details)

View File

@ -26,6 +26,7 @@ from monascaclient.v2_0 import client as monclient_v2
from neutronclient.neutron import client as netclient
from neutronclient.v2_0 import client as netclient_v2
from novaclient import client as nvclient
import six
from watcher.common import clients
from watcher import conf
@ -125,11 +126,20 @@ class TestClients(base.TestCase):
@mock.patch.object(clients.OpenStackClients, 'session')
def test_clients_nova_diff_vers(self, mock_session):
CONF.set_override('api_version', '2.3', group='nova_client')
CONF.set_override('api_version', '2.60', group='nova_client')
osc = clients.OpenStackClients()
osc._nova = None
osc.nova()
self.assertEqual('2.3', osc.nova().api_version.get_string())
self.assertEqual('2.60', osc.nova().api_version.get_string())
@mock.patch.object(clients.OpenStackClients, 'session')
def test_clients_nova_bad_min_version(self, mock_session):
CONF.set_override('api_version', '2.47', group='nova_client')
osc = clients.OpenStackClients()
osc._nova = None
ex = self.assertRaises(ValueError, osc.nova)
self.assertIn('Invalid nova_client.api_version 2.47',
six.text_type(ex))
@mock.patch.object(clients.OpenStackClients, 'session')
def test_clients_nova_diff_endpoint(self, mock_session):