2.43: Deprecate novaclient /os-hosts usage

os-hosts is deprecated in the compute REST API with
microversion 2.43 via change:

Ieb85653b85a1eff38a9fb0c9ff05e4cd39150ecc

So nova shell and API bindings should be deprecated as well.

Implements blueprint deprecate-os-hosts

Change-Id: I79091edf5a2569e49e79deba312456fdcdee09e1
This commit is contained in:
jichenjc 2017-03-29 05:28:17 +08:00 committed by Matt Riedemann
parent ff1eff18d9
commit ea3b9f7fef
6 changed files with 155 additions and 17 deletions

View File

@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
# when client supported the max version, and bumped sequentially, otherwise # when client supported the max version, and bumped sequentially, otherwise
# the client may break due to server side new version may include some # the client may break due to server side new version may include some
# backward incompatible change. # backward incompatible change.
API_MAX_VERSION = api_versions.APIVersion("2.42") API_MAX_VERSION = api_versions.APIVersion("2.43")

View File

@ -11,6 +11,10 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import mock
from novaclient import api_versions
from novaclient import exceptions
from novaclient.tests.unit.fixture_data import client from novaclient.tests.unit.fixture_data import client
from novaclient.tests.unit.fixture_data import hosts as data from novaclient.tests.unit.fixture_data import hosts as data
from novaclient.tests.unit import utils from novaclient.tests.unit import utils
@ -23,8 +27,14 @@ class HostsTest(utils.FixturedTestCase):
client_fixture_class = client.V1 client_fixture_class = client.V1
data_fixture_class = data.V1 data_fixture_class = data.V1
def setUp(self):
super(HostsTest, self).setUp()
self.warning_mock = mock.patch('warnings.warn').start()
self.addCleanup(self.warning_mock.stop)
def test_describe_resource(self): def test_describe_resource(self):
hs = self.cs.hosts.get('host') hs = self.cs.hosts.get('host')
self.warning_mock.assert_called_once()
self.assert_request_id(hs, fakes.FAKE_REQUEST_ID_LIST) self.assert_request_id(hs, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('GET', '/os-hosts/host') self.assert_called('GET', '/os-hosts/host')
for h in hs: for h in hs:
@ -32,6 +42,7 @@ class HostsTest(utils.FixturedTestCase):
def test_list_host(self): def test_list_host(self):
hs = self.cs.hosts.list() hs = self.cs.hosts.list()
self.warning_mock.assert_called_once()
self.assert_request_id(hs, fakes.FAKE_REQUEST_ID_LIST) self.assert_request_id(hs, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('GET', '/os-hosts') self.assert_called('GET', '/os-hosts')
for h in hs: for h in hs:
@ -50,6 +61,8 @@ class HostsTest(utils.FixturedTestCase):
host = self.cs.hosts.get('sample_host')[0] host = self.cs.hosts.get('sample_host')[0]
values = {"status": "enabled"} values = {"status": "enabled"}
result = host.update(values) result = host.update(values)
# one warning for the get, one warning for the update
self.assertEqual(2, self.warning_mock.call_count)
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('PUT', '/os-hosts/sample_host', values) self.assert_called('PUT', '/os-hosts/sample_host', values)
self.assertIsInstance(result, hosts.Host) self.assertIsInstance(result, hosts.Host)
@ -74,6 +87,8 @@ class HostsTest(utils.FixturedTestCase):
def test_host_startup(self): def test_host_startup(self):
host = self.cs.hosts.get('sample_host')[0] host = self.cs.hosts.get('sample_host')[0]
result = host.startup() result = host.startup()
# one warning for the get, one warning for the action
self.assertEqual(2, self.warning_mock.call_count)
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called( self.assert_called(
'GET', '/os-hosts/sample_host/startup') 'GET', '/os-hosts/sample_host/startup')
@ -81,6 +96,8 @@ class HostsTest(utils.FixturedTestCase):
def test_host_reboot(self): def test_host_reboot(self):
host = self.cs.hosts.get('sample_host')[0] host = self.cs.hosts.get('sample_host')[0]
result = host.reboot() result = host.reboot()
# one warning for the get, one warning for the action
self.assertEqual(2, self.warning_mock.call_count)
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called( self.assert_called(
'GET', '/os-hosts/sample_host/reboot') 'GET', '/os-hosts/sample_host/reboot')
@ -88,6 +105,8 @@ class HostsTest(utils.FixturedTestCase):
def test_host_shutdown(self): def test_host_shutdown(self):
host = self.cs.hosts.get('sample_host')[0] host = self.cs.hosts.get('sample_host')[0]
result = host.shutdown() result = host.shutdown()
# one warning for the get, one warning for the action
self.assertEqual(2, self.warning_mock.call_count)
self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST) self.assert_request_id(result, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called( self.assert_called(
'GET', '/os-hosts/sample_host/shutdown') 'GET', '/os-hosts/sample_host/shutdown')
@ -100,3 +119,30 @@ class HostsTest(utils.FixturedTestCase):
hs = self.cs.hosts.list() hs = self.cs.hosts.list()
for h in hs: for h in hs:
self.assertEqual('<Host: %s>' % h.host_name, repr(h)) self.assertEqual('<Host: %s>' % h.host_name, repr(h))
class DeprecatedHostsTestv2_43(utils.FixturedTestCase):
"""Tests the os-hosts API bindings at microversion 2.43 to ensure
they fail with a 404 error.
"""
client_fixture_class = client.V1
def setUp(self):
super(DeprecatedHostsTestv2_43, self).setUp()
self.cs.api_version = api_versions.APIVersion('2.43')
def test_get(self):
self.assertRaises(exceptions.VersionNotFoundForAPIMethod,
self.cs.hosts.get, 'host')
def test_list(self):
self.assertRaises(exceptions.VersionNotFoundForAPIMethod,
self.cs.hosts.list)
def test_update(self):
self.assertRaises(exceptions.VersionNotFoundForAPIMethod,
self.cs.hosts.update, 'host', {"status": "enabled"})
def test_host_action(self):
self.assertRaises(exceptions.VersionNotFoundForAPIMethod,
self.cs.hosts.host_action, 'host', 'reboot')

View File

@ -2171,7 +2171,11 @@ class ShellTest(utils.TestCase):
self.assert_called('DELETE', '/os-services/1') self.assert_called('DELETE', '/os-services/1')
def test_host_list(self): def test_host_list(self):
self.run_command('host-list') _, err = self.run_command('host-list')
# make sure we said it's deprecated
self.assertIn('WARNING: Command host-list is deprecated', err)
# and replaced with hypervisor-list
self.assertIn('hypervisor-list', err)
self.assert_called('GET', '/os-hosts') self.assert_called('GET', '/os-hosts')
def test_host_list_with_zone(self): def test_host_list_with_zone(self):
@ -2179,23 +2183,40 @@ class ShellTest(utils.TestCase):
self.assert_called('GET', '/os-hosts?zone=nova') self.assert_called('GET', '/os-hosts?zone=nova')
def test_host_update_status(self): def test_host_update_status(self):
self.run_command('host-update sample-host_1 --status enabled') _, err = self.run_command('host-update sample-host_1 --status enable')
body = {'status': 'enabled'} # make sure we said it's deprecated
self.assertIn('WARNING: Command host-update is deprecated', err)
# and replaced with service-enable
self.assertIn('service-enable', err)
body = {'status': 'enable'}
self.assert_called('PUT', '/os-hosts/sample-host_1', body) self.assert_called('PUT', '/os-hosts/sample-host_1', body)
def test_host_update_maintenance(self): def test_host_update_maintenance(self):
self.run_command('host-update sample-host_2 --maintenance enable') _, err = (
self.run_command('host-update sample-host_2 --maintenance enable'))
# make sure we said it's deprecated
self.assertIn('WARNING: Command host-update is deprecated', err)
# and there is no replacement
self.assertIn('There is no replacement', err)
body = {'maintenance_mode': 'enable'} body = {'maintenance_mode': 'enable'}
self.assert_called('PUT', '/os-hosts/sample-host_2', body) self.assert_called('PUT', '/os-hosts/sample-host_2', body)
def test_host_update_multiple_settings(self): def test_host_update_multiple_settings(self):
self.run_command('host-update sample-host_3 ' _, err = self.run_command('host-update sample-host_3 '
'--status disabled --maintenance enable') '--status disable --maintenance enable')
body = {'status': 'disabled', 'maintenance_mode': 'enable'} # make sure we said it's deprecated
self.assertIn('WARNING: Command host-update is deprecated', err)
# and replaced with service-disable
self.assertIn('service-disable', err)
body = {'status': 'disable', 'maintenance_mode': 'enable'}
self.assert_called('PUT', '/os-hosts/sample-host_3', body) self.assert_called('PUT', '/os-hosts/sample-host_3', body)
def test_host_startup(self): def test_host_startup(self):
self.run_command('host-action sample-host --action startup') _, err = self.run_command('host-action sample-host --action startup')
# make sure we said it's deprecated
self.assertIn('WARNING: Command host-action is deprecated', err)
# and there is no replacement
self.assertIn('There is no replacement', err)
self.assert_called( self.assert_called(
'GET', '/os-hosts/sample-host/startup') 'GET', '/os-hosts/sample-host/startup')
@ -2881,6 +2902,7 @@ class ShellTest(utils.TestCase):
39, # There are no versioned wrapped shell method changes for this 39, # There are no versioned wrapped shell method changes for this
41, # There are no version-wrapped shell method changes for this. 41, # There are no version-wrapped shell method changes for this.
42, # There are no version-wrapped shell method changes for this. 42, # There are no version-wrapped shell method changes for this.
43, # There are no version-wrapped shell method changes for this.
]) ])
versions_supported = set(range(0, versions_supported = set(range(0,
novaclient.API_MAX_VERSION.ver_minor + 1)) novaclient.API_MAX_VERSION.ver_minor + 1))

View File

@ -14,12 +14,23 @@
# under the License. # under the License.
""" """
host interface (1.1 extension). DEPRECATED host interface (1.1 extension).
""" """
import warnings
from novaclient import api_versions
from novaclient import base from novaclient import base
from novaclient.i18n import _
HOSTS_DEPRECATION_WARNING = (
_('The os-hosts API is deprecated. This API binding will be removed '
'in the first major release after the Nova server 16.0.0 Pike release.')
)
class Host(base.Resource): class Host(base.Resource):
"""DEPRECATED"""
def __repr__(self): def __repr__(self):
return "<Host: %s>" % self.host return "<Host: %s>" % self.host
@ -28,15 +39,19 @@ class Host(base.Resource):
for (k, v) in dico.items(): for (k, v) in dico.items():
setattr(self, k, v) setattr(self, k, v)
@api_versions.wraps("2.0", "2.42")
def update(self, values): def update(self, values):
return self.manager.update(self.host, values) return self.manager.update(self.host, values)
@api_versions.wraps("2.0", "2.42")
def startup(self): def startup(self):
return self.manager.host_action(self.host, 'startup') return self.manager.host_action(self.host, 'startup')
@api_versions.wraps("2.0", "2.42")
def shutdown(self): def shutdown(self):
return self.manager.host_action(self.host, 'shutdown') return self.manager.host_action(self.host, 'shutdown')
@api_versions.wraps("2.0", "2.42")
def reboot(self): def reboot(self):
return self.manager.host_action(self.host, 'reboot') return self.manager.host_action(self.host, 'reboot')
@ -56,31 +71,39 @@ class Host(base.Resource):
class HostManager(base.ManagerWithFind): class HostManager(base.ManagerWithFind):
resource_class = Host resource_class = Host
@api_versions.wraps("2.0", "2.42")
def get(self, host): def get(self, host):
""" """
Describes cpu/memory/hdd info for host. DEPRECATED Describes cpu/memory/hdd info for host.
:param host: destination host name. :param host: destination host name.
""" """
warnings.warn(HOSTS_DEPRECATION_WARNING, DeprecationWarning)
return self._list("/os-hosts/%s" % host, "host") return self._list("/os-hosts/%s" % host, "host")
@api_versions.wraps("2.0", "2.42")
def update(self, host, values): def update(self, host, values):
"""Update status or maintenance mode for the host.""" """DEPRECATED Update status or maintenance mode for the host."""
warnings.warn(HOSTS_DEPRECATION_WARNING, DeprecationWarning)
return self._update("/os-hosts/%s" % host, values) return self._update("/os-hosts/%s" % host, values)
@api_versions.wraps("2.0", "2.42")
def host_action(self, host, action): def host_action(self, host, action):
""" """
Perform an action on a host. DEPRECATED Perform an action on a host.
:param host: The host to perform an action :param host: The host to perform an action
:param action: The action to perform :param action: The action to perform
:returns: An instance of novaclient.base.TupleWithMeta :returns: An instance of novaclient.base.TupleWithMeta
""" """
warnings.warn(HOSTS_DEPRECATION_WARNING, DeprecationWarning)
url = '/os-hosts/%s/%s' % (host, action) url = '/os-hosts/%s/%s' % (host, action)
resp, body = self.api.client.get(url) resp, body = self.api.client.get(url)
return base.TupleWithMeta((resp, body), resp) return base.TupleWithMeta((resp, body), resp)
@api_versions.wraps("2.0", "2.42")
def list(self, zone=None): def list(self, zone=None):
warnings.warn(HOSTS_DEPRECATION_WARNING, DeprecationWarning)
url = '/os-hosts' url = '/os-hosts'
if zone: if zone:
url = '/os-hosts?zone=%s' % zone url = '/os-hosts?zone=%s' % zone

View File

@ -55,6 +55,26 @@ CERT_DEPRECATION_WARNING = (
) )
# NOTE(mriedem): Remove this along with the deprecated commands in the first
# major python-novaclient release AFTER the nova server 16.0.0 Pike release.
def emit_hosts_deprecation_warning(command_name, replacement=None):
if replacement is None:
print(_('WARNING: Command %s is deprecated and will be removed '
'in the first major release after the Nova server 16.0.0 '
'Pike release. There is no replacement or alternative for '
'this command. Specify --os-compute-api-version less than '
'2.43 to continue using this command until it is removed.') %
command_name, file=sys.stderr)
else:
print(_('WARNING: Command %(command)s is deprecated and will be '
'removed in the first major release after the Nova server '
'16.0.0 Pike release. Use %(replacement)s instead. Specify '
'--os-compute-api-version less than 2.43 to continue using '
'this command until it is removed.') %
{'command': command_name, 'replacement': replacement},
file=sys.stderr)
CLIENT_BDM2_KEYS = { CLIENT_BDM2_KEYS = {
'id': 'uuid', 'id': 'uuid',
'source': 'source_type', 'source': 'source_type',
@ -3386,7 +3406,9 @@ def do_service_delete(cs, args):
@utils.arg('host', metavar='<hostname>', help=_('Name of host.')) @utils.arg('host', metavar='<hostname>', help=_('Name of host.'))
def do_host_describe(cs, args): def do_host_describe(cs, args):
"""Describe a specific host.""" """DEPRECATED Describe a specific host."""
emit_hosts_deprecation_warning('host-describe', 'hypervisor-show')
result = cs.hosts.get(args.host) result = cs.hosts.get(args.host)
columns = ["HOST", "PROJECT", "cpu", "memory_mb", "disk_gb"] columns = ["HOST", "PROJECT", "cpu", "memory_mb", "disk_gb"]
utils.print_list(result, columns) utils.print_list(result, columns)
@ -3399,7 +3421,9 @@ def do_host_describe(cs, args):
help=_('Filters the list, returning only those hosts in the availability ' help=_('Filters the list, returning only those hosts in the availability '
'zone <zone>.')) 'zone <zone>.'))
def do_host_list(cs, args): def do_host_list(cs, args):
"""List all hosts by service.""" """DEPRECATED List all hosts by service."""
emit_hosts_deprecation_warning('host-list', 'hypervisor-list')
columns = ["host_name", "service", "zone"] columns = ["host_name", "service", "zone"]
result = cs.hosts.list(args.zone) result = cs.hosts.list(args.zone)
utils.print_list(result, columns) utils.print_list(result, columns)
@ -3416,7 +3440,14 @@ def do_host_list(cs, args):
dest='maintenance', dest='maintenance',
help=_('Either put or resume host to/from maintenance.')) help=_('Either put or resume host to/from maintenance.'))
def do_host_update(cs, args): def do_host_update(cs, args):
"""Update host settings.""" """DEPRECATED Update host settings."""
if args.status == 'enable':
emit_hosts_deprecation_warning('host-update', 'service-enable')
elif args.status == 'disable':
emit_hosts_deprecation_warning('host-update', 'service-disable')
else:
emit_hosts_deprecation_warning('host-update')
updates = {} updates = {}
columns = ["HOST"] columns = ["HOST"]
if args.status: if args.status:
@ -3435,7 +3466,9 @@ def do_host_update(cs, args):
choices=['startup', 'shutdown', 'reboot'], choices=['startup', 'shutdown', 'reboot'],
help=_('A power action: startup, reboot, or shutdown.')) help=_('A power action: startup, reboot, or shutdown.'))
def do_host_action(cs, args): def do_host_action(cs, args):
"""Perform a power action on a host.""" """DEPRECATED Perform a power action on a host."""
emit_hosts_deprecation_warning('host-action')
result = cs.hosts.host_action(args.host, args.action) result = cs.hosts.host_action(args.host, args.action)
utils.print_list([result], ['HOST', 'power_action']) utils.print_list([result], ['HOST', 'power_action'])

View File

@ -0,0 +1,14 @@
---
deprecations:
- |
The following CLIs and their backing API bindings are deprecated and capped
at microversion 2.43:
* ``nova host-describe`` - superseded by ``nova hypervisor-show``
* ``nova host-list`` - superseded by ``nova hypervisor-list``
* ``nova host-update`` - superseded by ``nova service-enable`` and
``nova service-disable``
* ``nova host-action`` - no alternative by design
The CLIs and API bindings will be removed in the first major release after
Nova 16.0.0 Pike is released.