[microversions] Add support for 2.19
2.19 - Allow the user to set and get the server description. The user will be able to set the description when creating, rebuilding, or updating a server, and get the description as part of the server details. Methods `rebuild` and `create` of novaclient.v2.servers.ServerManager were not wrapped with `api_versions.wraps` decorator to reduce code and docsting duplication. Version checks added inside these methods. Change-Id: I75b804c6edd0cdf02c2cd002d0b5968fec8da545
This commit is contained in:
parent
99c588e28c
commit
1d1e43957d
@ -25,4 +25,4 @@ API_MIN_VERSION = api_versions.APIVersion("2.1")
|
||||
# when client supported the max version, and bumped sequentially, otherwise
|
||||
# the client may break due to server side new version may include some
|
||||
# backward incompatible change.
|
||||
API_MAX_VERSION = api_versions.APIVersion("2.18")
|
||||
API_MAX_VERSION = api_versions.APIVersion("2.19")
|
||||
|
@ -24,6 +24,24 @@ class UnsupportedVersion(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class UnsupportedAttribute(AttributeError):
|
||||
"""Indicates that the user is trying to transmit the argument to a method,
|
||||
which is not supported by selected version.
|
||||
"""
|
||||
|
||||
def __init__(self, argument_name, start_version, end_version=None):
|
||||
if end_version:
|
||||
self.message = (
|
||||
"'%(name)s' argument is only allowed for microversions "
|
||||
"%(start)s - %(end)s." % {"name": argument_name,
|
||||
"start": start_version,
|
||||
"end": end_version})
|
||||
else:
|
||||
self.message = (
|
||||
"'%(name)s' argument is only allowed since microversion "
|
||||
"%(start)s." % {"name": argument_name, "start": start_version})
|
||||
|
||||
|
||||
class CommandError(Exception):
|
||||
pass
|
||||
|
||||
|
@ -53,3 +53,38 @@ class TestServerLockV29(base.ClientTestBase):
|
||||
|
||||
self.nova("unlock %s" % server.id)
|
||||
self._show_server_and_check_lock_attr(server, False)
|
||||
|
||||
|
||||
class TestServersDescription(base.ClientTestBase):
|
||||
|
||||
COMPUTE_API_VERSION = "2.19"
|
||||
|
||||
def _boot_server_with_description(self):
|
||||
name = str(uuid.uuid4())
|
||||
network = self.client.networks.list()[0]
|
||||
descr = "Some words about this test VM."
|
||||
server = self.client.servers.create(
|
||||
name, self.image, self.flavor, nics=[{"net-id": network.id}],
|
||||
description=descr)
|
||||
self.addCleanup(server.delete)
|
||||
|
||||
self.assertEqual(descr, server.description)
|
||||
|
||||
return server, descr
|
||||
|
||||
def test_create(self):
|
||||
server, descr = self._boot_server_with_description()
|
||||
|
||||
output = self.nova("show %s" % server.id)
|
||||
self.assertEqual(descr, self._get_value_from_the_table(output,
|
||||
"description"))
|
||||
|
||||
def test_update(self):
|
||||
server, descr = self._boot_server_with_description()
|
||||
|
||||
# remove description
|
||||
self.nova("update %s --description ''" % server.id)
|
||||
|
||||
output = self.nova("show %s" % server.id)
|
||||
self.assertEqual("-", self._get_value_from_the_table(output,
|
||||
"description"))
|
||||
|
@ -945,6 +945,18 @@ class ServersTest(utils.FixturedTestCase):
|
||||
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
|
||||
self.assert_called('DELETE', '/servers/1234/os-interface/port-id')
|
||||
|
||||
def test_create_server_with_description(self):
|
||||
self.assertRaises(exceptions.UnsupportedAttribute,
|
||||
self.cs.servers.create,
|
||||
name="My server",
|
||||
description="descr",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata="hello moto",
|
||||
key_name="fakekey"
|
||||
)
|
||||
|
||||
|
||||
class ServersV26Test(ServersTest):
|
||||
def setUp(self):
|
||||
@ -1033,3 +1045,35 @@ class ServersV217Test(ServersV214Test):
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
self.cs.servers.trigger_crash_dump(s)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
||||
|
||||
class ServersV219Test(ServersV217Test):
|
||||
def setUp(self):
|
||||
super(ServersV219Test, self).setUp()
|
||||
self.cs.api_version = api_versions.APIVersion("2.19")
|
||||
|
||||
def test_create_server_with_description(self):
|
||||
self.cs.servers.create(
|
||||
name="My server",
|
||||
description="descr",
|
||||
image=1,
|
||||
flavor=1,
|
||||
meta={'foo': 'bar'},
|
||||
userdata="hello moto",
|
||||
key_name="fakekey"
|
||||
)
|
||||
self.assert_called('POST', '/servers')
|
||||
|
||||
def test_update_server_with_description(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
|
||||
s.update(description='hi')
|
||||
s.update(name='hi', description='hi')
|
||||
self.assert_called('PUT', '/servers/1234')
|
||||
|
||||
def test_rebuild_with_description(self):
|
||||
s = self.cs.servers.get(1234)
|
||||
|
||||
ret = s.rebuild(image="1", description="descr")
|
||||
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
|
||||
self.assert_called('POST', '/servers/1234/action')
|
||||
|
@ -28,6 +28,7 @@ from six.moves.urllib import parse
|
||||
from novaclient import api_versions
|
||||
from novaclient import base
|
||||
from novaclient import crypto
|
||||
from novaclient import exceptions
|
||||
from novaclient.i18n import _
|
||||
from novaclient.v2 import security_groups
|
||||
|
||||
@ -49,14 +50,22 @@ class Server(base.Resource):
|
||||
"""
|
||||
return self.manager.delete(self)
|
||||
|
||||
def update(self, name=None):
|
||||
def update(self, name=None, description=None):
|
||||
"""
|
||||
Update the name for this server.
|
||||
Update the name and the description for this server.
|
||||
|
||||
:param name: Update the server's name.
|
||||
:param description: Update the server's description(
|
||||
allowed for 2.19-latest).
|
||||
:returns: :class:`Server`
|
||||
"""
|
||||
return self.manager.update(self, name=name)
|
||||
if (description is not None and
|
||||
self.manager.api_version < api_versions.APIVersion("2.19")):
|
||||
raise exceptions.UnsupportedAttribute("description", "2.19")
|
||||
update_kwargs = {"name": name}
|
||||
if description is not None:
|
||||
update_kwargs["description"] = description
|
||||
return self.manager.update(self, **update_kwargs)
|
||||
|
||||
def get_console_output(self, length=None):
|
||||
"""
|
||||
@ -510,7 +519,8 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
availability_zone=None, block_device_mapping=None,
|
||||
block_device_mapping_v2=None, nics=None, scheduler_hints=None,
|
||||
config_drive=None, admin_pass=None, disk_config=None,
|
||||
access_ip_v4=None, access_ip_v6=None, **kwargs):
|
||||
access_ip_v4=None, access_ip_v6=None, description=None,
|
||||
**kwargs):
|
||||
"""
|
||||
Create (boot) a new server.
|
||||
"""
|
||||
@ -632,6 +642,9 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
if access_ip_v6 is not None:
|
||||
body['server']['accessIPv6'] = access_ip_v6
|
||||
|
||||
if description:
|
||||
body['server']['description'] = description
|
||||
|
||||
return self._create(resource_url, body, response_key,
|
||||
return_raw=return_raw, **kwargs)
|
||||
|
||||
@ -1168,6 +1181,8 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
password.
|
||||
:param access_ip_v4: (optional extension) add alternative access ip v4
|
||||
:param access_ip_v6: (optional extension) add alternative access ip v6
|
||||
:param description: optional description of the server (allowed since
|
||||
microversion 2.19)
|
||||
"""
|
||||
if not min_count:
|
||||
min_count = 1
|
||||
@ -1178,6 +1193,10 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
|
||||
boot_args = [name, image, flavor]
|
||||
|
||||
descr_microversion = api_versions.APIVersion("2.19")
|
||||
if "description" in kwargs and self.api_version < descr_microversion:
|
||||
raise exceptions.UnsupportedAttribute("description", "2.19")
|
||||
|
||||
boot_kwargs = dict(
|
||||
meta=meta, files=files, userdata=userdata,
|
||||
reservation_id=reservation_id, min_count=min_count,
|
||||
@ -1202,9 +1221,10 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
return self._boot(resource_url, response_key, *boot_args,
|
||||
**boot_kwargs)
|
||||
|
||||
@api_versions.wraps("2.0", "2.18")
|
||||
def update(self, server, name=None):
|
||||
"""
|
||||
Update the name or the password for a server.
|
||||
Update the name for a server.
|
||||
|
||||
:param server: The :class:`Server` (or its ID) to update.
|
||||
:param name: Update the server's name.
|
||||
@ -1220,6 +1240,29 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
|
||||
return self._update("/servers/%s" % base.getid(server), body, "server")
|
||||
|
||||
@api_versions.wraps("2.19")
|
||||
def update(self, server, name=None, description=None):
|
||||
"""
|
||||
Update the name or the description for a server.
|
||||
|
||||
:param server: The :class:`Server` (or its ID) to update.
|
||||
:param name: Update the server's name.
|
||||
:param description: Update the server's description. If it equals to
|
||||
empty string(i.g. ""), the server description will be removed.
|
||||
"""
|
||||
if name is None and description is None:
|
||||
return
|
||||
|
||||
body = {"server": {}}
|
||||
if name:
|
||||
body["server"]["name"] = name
|
||||
if description == "":
|
||||
body["server"]["description"] = None
|
||||
elif description:
|
||||
body["server"]["description"] = description
|
||||
|
||||
return self._update("/servers/%s" % base.getid(server), body, "server")
|
||||
|
||||
def change_password(self, server, password):
|
||||
"""
|
||||
Update the password for a server.
|
||||
@ -1271,8 +1314,14 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
are the file contents (either as a string or as a
|
||||
file-like object). A maximum of five entries is allowed,
|
||||
and each file must be 10k or less.
|
||||
:param description: optional description of the server (allowed since
|
||||
microversion 2.19)
|
||||
:returns: :class:`Server`
|
||||
"""
|
||||
descr_microversion = api_versions.APIVersion("2.19")
|
||||
if "description" in kwargs and self.api_version < descr_microversion:
|
||||
raise exceptions.UnsupportedAttribute("description", "2.19")
|
||||
|
||||
body = {'imageRef': base.getid(image)}
|
||||
if password is not None:
|
||||
body['adminPass'] = password
|
||||
@ -1282,6 +1331,8 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
body['preserve_ephemeral'] = True
|
||||
if name is not None:
|
||||
body['name'] = name
|
||||
if "description" in kwargs:
|
||||
body["description"] = kwargs["description"]
|
||||
if meta:
|
||||
body['metadata'] = meta
|
||||
if files:
|
||||
|
@ -350,6 +350,9 @@ def _boot(cs, args):
|
||||
access_ip_v4=args.access_ip_v4,
|
||||
access_ip_v6=args.access_ip_v6)
|
||||
|
||||
if 'description' in args:
|
||||
boot_kwargs["description"] = args.description
|
||||
|
||||
return boot_args, boot_kwargs
|
||||
|
||||
|
||||
@ -569,6 +572,13 @@ def _boot(cs, args):
|
||||
metavar='<value>',
|
||||
default=None,
|
||||
help=_('Alternative access IPv6 of the instance.'))
|
||||
@cliutils.arg(
|
||||
'--description',
|
||||
metavar='<description>',
|
||||
dest='description',
|
||||
default=None,
|
||||
help=_('Description for the server.'),
|
||||
start_version="2.19")
|
||||
def do_boot(cs, args):
|
||||
"""Boot a new server."""
|
||||
boot_args, boot_kwargs = _boot(cs, args)
|
||||
@ -1624,6 +1634,13 @@ def do_reboot(cs, args):
|
||||
metavar='<name>',
|
||||
default=None,
|
||||
help=_('Name for the new server.'))
|
||||
@cliutils.arg(
|
||||
'--description',
|
||||
metavar='<description>',
|
||||
dest='description',
|
||||
default=None,
|
||||
help=_('New description for the server.'),
|
||||
start_version="2.19")
|
||||
@cliutils.arg(
|
||||
'--meta',
|
||||
metavar="<key=value>",
|
||||
@ -1652,6 +1669,8 @@ def do_rebuild(cs, args):
|
||||
kwargs = utils.get_resource_manager_extra_kwargs(do_rebuild, args)
|
||||
kwargs['preserve_ephemeral'] = args.preserve_ephemeral
|
||||
kwargs['name'] = args.name
|
||||
if 'description' in args:
|
||||
kwargs['description'] = args.description
|
||||
meta = _meta_parsing(args.meta)
|
||||
kwargs['meta'] = meta
|
||||
|
||||
@ -1682,8 +1701,39 @@ def do_rebuild(cs, args):
|
||||
help=_('Name (old name) or ID of server.'))
|
||||
@cliutils.arg('name', metavar='<name>', help=_('New name for the server.'))
|
||||
def do_rename(cs, args):
|
||||
"""Rename a server."""
|
||||
_find_server(cs, args.server).update(name=args.name)
|
||||
"""DEPRECATED, use update instead."""
|
||||
do_update(cs, args)
|
||||
|
||||
|
||||
@cliutils.arg(
|
||||
'server', metavar='<server>',
|
||||
help=_('Name (old name) or ID of server.'))
|
||||
@cliutils.arg(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
dest='name',
|
||||
default=None,
|
||||
help=_('New name for the server.'))
|
||||
@cliutils.arg(
|
||||
'--description',
|
||||
metavar='<description>',
|
||||
dest='description',
|
||||
default=None,
|
||||
help=_('New description for the server. If it equals to empty string '
|
||||
'(i.g. ""), the server description will be removed.'),
|
||||
start_version="2.19")
|
||||
def do_update(cs, args):
|
||||
"""Update the name or the description for a server."""
|
||||
update_kwargs = {}
|
||||
if args.name:
|
||||
update_kwargs["name"] = args.name
|
||||
# NOTE(andreykurilin): `do_update` method is used by `do_rename` method,
|
||||
# which do not have description argument at all. When `do_rename` will be
|
||||
# removed after deprecation period, feel free to change the check below to:
|
||||
# `if args.description:`
|
||||
if "description" in args and args.description is not None:
|
||||
update_kwargs["description"] = args.description
|
||||
_find_server(cs, args.server).update(**update_kwargs)
|
||||
|
||||
|
||||
@cliutils.arg('server', metavar='<server>', help=_('Name or ID of server.'))
|
||||
|
Loading…
Reference in New Issue
Block a user