summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kurilin <akurilin@mirantis.com>2016-02-10 16:05:48 +0200
committerAndrey Kurilin <akurilin@mirantis.com>2016-02-16 16:20:41 +0200
commit1d1e43957dc785231c50a105229814b06eb74752 (patch)
tree038b07488e1cd800d50556efbe01e1eb3e243c31
parent99c588e28c2c4eb0b684cfd54d79d76fc30197fe (diff)
downloadpython-novaclient-1d1e43957dc785231c50a105229814b06eb74752.tar.gz
[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
-rw-r--r--novaclient/__init__.py2
-rw-r--r--novaclient/exceptions.py18
-rw-r--r--novaclient/tests/functional/v2/test_servers.py35
-rw-r--r--novaclient/tests/unit/v2/test_servers.py44
-rw-r--r--novaclient/v2/servers.py61
-rw-r--r--novaclient/v2/shell.py54
6 files changed, 206 insertions, 8 deletions
diff --git a/novaclient/__init__.py b/novaclient/__init__.py
index d0859b11..e9b2a802 100644
--- a/novaclient/__init__.py
+++ b/novaclient/__init__.py
@@ -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")
diff --git a/novaclient/exceptions.py b/novaclient/exceptions.py
index b9bdf114..c502e964 100644
--- a/novaclient/exceptions.py
+++ b/novaclient/exceptions.py
@@ -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
diff --git a/novaclient/tests/functional/v2/test_servers.py b/novaclient/tests/functional/v2/test_servers.py
index 590e543b..de5e57d9 100644
--- a/novaclient/tests/functional/v2/test_servers.py
+++ b/novaclient/tests/functional/v2/test_servers.py
@@ -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"))
diff --git a/novaclient/tests/unit/v2/test_servers.py b/novaclient/tests/unit/v2/test_servers.py
index 171a5dbc..b8a8bd16 100644
--- a/novaclient/tests/unit/v2/test_servers.py
+++ b/novaclient/tests/unit/v2/test_servers.py
@@ -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')
diff --git a/novaclient/v2/servers.py b/novaclient/v2/servers.py
index 7120df1a..961ae61d 100644
--- a/novaclient/v2/servers.py
+++ b/novaclient/v2/servers.py
@@ -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:
diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py
index f16d5903..95b892d8 100644
--- a/novaclient/v2/shell.py
+++ b/novaclient/v2/shell.py
@@ -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)
@@ -1625,6 +1635,13 @@ def do_reboot(cs, args):
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>",
action='append',
@@ -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.'))