summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStephen Finucane <stephenfin@redhat.com>2021-08-31 17:44:20 +0100
committerStephen Finucane <stephenfin@redhat.com>2021-09-02 11:01:07 +0100
commit8066f8c745054caeb7bd66d4c2be15ab5fac1d1f (patch)
tree81e6a7df9e656084305a4de9259262966f5d480b
parent01c7a3aa10f13eb889221f609dc4fb1463904fe3 (diff)
downloadpython-novaclient-8066f8c745054caeb7bd66d4c2be15ab5fac1d1f.tar.gz
Microversion 2.90 - Configurable hostnamesxena-em17.6.0
Change-Id: Icd4362a07196e59bafcdfaff44323ce1386d4f55 Signed-off-by: Stephen Finucane <stephenfin@redhat.com> Depends-On: https://review.opendev.org/c/openstack/nova/+/778550/
-rw-r--r--doc/source/cli/nova.rst26
-rw-r--r--novaclient/__init__.py2
-rw-r--r--novaclient/tests/unit/v2/test_servers.py87
-rw-r--r--novaclient/tests/unit/v2/test_shell.py72
-rw-r--r--novaclient/v2/servers.py133
-rw-r--r--novaclient/v2/shell.py38
-rw-r--r--releasenotes/notes/microversion-v2_90-259779668e67dfb5.yaml13
7 files changed, 333 insertions, 38 deletions
diff --git a/doc/source/cli/nova.rst b/doc/source/cli/nova.rst
index 4b501949..54ec9991 100644
--- a/doc/source/cli/nova.rst
+++ b/doc/source/cli/nova.rst
@@ -975,6 +975,7 @@ nova boot
[--trusted-image-certificate-id <trusted-image-certificate-id>]
[--host <host>]
[--hypervisor-hostname <hypervisor-hostname>]
+ [--hostname <hostname>]
<name>
Boot a new server.
@@ -1149,6 +1150,12 @@ quality of service support, microversion ``2.72`` is required.
Requested hypervisor hostname to create servers. Admin only by default.
(Supported by API versions '2.74' - '2.latest')
+``--hostname <hostname>``
+ Hostname for the instance. This sets the hostname stored in the
+ metadata server: a utility such as cloud-init running on the guest
+ is required to propagate these changes to the guest.
+ (Supported by API versions '2.90' - '2.latest')
+
.. _nova_clear-password:
nova clear-password
@@ -2885,6 +2892,7 @@ nova rebuild
[--user-data <user-data>] [--user-data-unset]
[--trusted-image-certificate-id <trusted-image-certificate-id>]
[--trusted-image-certificates-unset]
+ [--hostname <hostname>]
<server> <image>
Shutdown, re-image, and re-boot a server.
@@ -2958,6 +2966,12 @@ Shutdown, re-image, and re-boot a server.
specified with the ``--trusted-image-certificate-id`` option.
(Supported by API versions '2.63' - '2.latest')
+``--hostname <hostname>``
+ New hostname for the instance. This only updates the hostname
+ stored in the metadata server: a utility running on the guest
+ is required to propagate these changes to the guest.
+ (Supported by API versions '2.90' - '2.latest')
+
.. _nova_refresh-network:
nova refresh-network
@@ -3795,9 +3809,11 @@ nova update
.. code-block:: console
- usage: nova update [--name <name>] [--description <description>] <server>
+ usage: nova update [--name <name>] [--description <description>]
+ [--hostname <hostname>]
+ <server>
-Update the name or the description for a server.
+Update attributes of a server.
**Positional arguments:**
@@ -3815,6 +3831,12 @@ Update the name or the description for a server.
will be removed. (Supported by API versions
'2.19' - '2.latest')
+``--hostname <hostname>``
+ New hostname for the instance. This only updates the hostname
+ stored in the metadata server: a utility running on the guest
+ is required to propagate these changes to the guest.
+ (Supported by API versions '2.90' - '2.latest')
+
.. _nova_usage:
nova usage
diff --git a/novaclient/__init__.py b/novaclient/__init__.py
index 5f3b6953..d49e8841 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.89")
+API_MAX_VERSION = api_versions.APIVersion("2.90")
diff --git a/novaclient/tests/unit/v2/test_servers.py b/novaclient/tests/unit/v2/test_servers.py
index 40d88a19..071ae5c1 100644
--- a/novaclient/tests/unit/v2/test_servers.py
+++ b/novaclient/tests/unit/v2/test_servers.py
@@ -1905,3 +1905,90 @@ class ServersV278Test(ServersV273Test):
self.cs.api_version = api_versions.APIVersion('2.77')
s = self.cs.servers.get(1234)
self.assertRaises(exceptions.VersionNotFoundForAPIMethod, s.topology)
+
+
+class ServersV290Test(ServersV278Test):
+
+ api_version = '2.90'
+
+ def test_create_server_with_hostname(self):
+ self.cs.servers.create(
+ name='My server',
+ image=1,
+ flavor=1,
+ nics='auto',
+ hostname='new-hostname',
+ )
+ self.assert_called(
+ 'POST', '/servers',
+ {
+ 'server': {
+ 'flavorRef': '1',
+ 'imageRef': '1',
+ 'max_count': 1,
+ 'min_count': 1,
+ 'name': 'My server',
+ 'networks': 'auto',
+ 'hostname': 'new-hostname'
+ },
+ }
+ )
+
+ def test_create_server_with_hostname_pre_290_fails(self):
+ self.cs.api_version = api_versions.APIVersion('2.89')
+ ex = self.assertRaises(
+ exceptions.UnsupportedAttribute,
+ self.cs.servers.create,
+ name='My server',
+ image=1,
+ flavor=1,
+ nics='auto',
+ hostname='new-hostname')
+ self.assertIn(
+ "'hostname' argument is only allowed since microversion 2.90",
+ str(ex))
+
+ def test_rebuild_server_with_hostname(self):
+ s = self.cs.servers.get(1234)
+ ret = s.rebuild(image="1", hostname='new-hostname')
+ self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
+ self.assert_called(
+ 'POST', '/servers/1234/action',
+ {
+ 'rebuild': {
+ 'imageRef': '1',
+ 'hostname': 'new-hostname',
+ },
+ },
+ )
+
+ def test_rebuild_server_with_hostname_pre_290_fails(self):
+ self.cs.api_version = api_versions.APIVersion('2.89')
+ ex = self.assertRaises(
+ exceptions.UnsupportedAttribute,
+ self.cs.servers.rebuild,
+ '1234', fakes.FAKE_IMAGE_UUID_1,
+ hostname='new-hostname')
+ self.assertIn('hostname', str(ex))
+
+ def test_update_server_with_hostname(self):
+ s = self.cs.servers.get(1234)
+
+ s.update(hostname='new-hostname')
+ self.assert_called(
+ 'PUT', '/servers/1234',
+ {
+ 'server': {
+ 'hostname': 'new-hostname',
+ },
+ },
+ )
+
+ def test_update_with_hostname_pre_290_fails(self):
+ self.cs.api_version = api_versions.APIVersion('2.89')
+ s = self.cs.servers.get(1234)
+ ex = self.assertRaises(
+ TypeError,
+ s.update,
+ hostname='new-hostname')
+ self.assertIn('hostname', str(ex))
diff --git a/novaclient/tests/unit/v2/test_shell.py b/novaclient/tests/unit/v2/test_shell.py
index ed36685f..e6c0fda7 100644
--- a/novaclient/tests/unit/v2/test_shell.py
+++ b/novaclient/tests/unit/v2/test_shell.py
@@ -1502,6 +1502,34 @@ class ShellTest(utils.TestCase):
self.assertRaises(SystemExit, self.run_command,
cmd, api_version='2.73')
+ def test_boot_with_hostname(self):
+ self.run_command(
+ 'boot --flavor 1 --image %s '
+ '--hostname my-hostname --nic auto '
+ 'some-server' % FAKE_UUID_1,
+ api_version='2.90')
+ self.assert_called_anytime(
+ 'POST', '/servers',
+ {'server': {
+ 'flavorRef': '1',
+ 'name': 'some-server',
+ 'imageRef': FAKE_UUID_1,
+ 'min_count': 1,
+ 'max_count': 1,
+ 'networks': 'auto',
+ 'hostname': 'my-hostname',
+ }},
+ )
+
+ def test_boot_with_hostname_pre_v290(self):
+ cmd = (
+ 'boot --flavor 1 --image %s --nic auto '
+ '--hostname my-hostname some-server' % FAKE_UUID_1
+ )
+ self.assertRaises(
+ SystemExit, self.run_command,
+ cmd, api_version='2.89')
+
def test_flavor_list(self):
out, _ = self.run_command('flavor-list')
self.assert_called_anytime('GET', '/flavors/detail')
@@ -2258,6 +2286,31 @@ class ShellTest(utils.TestCase):
self.assertNotIn('server_groups', out)
self.assertNotIn('a67359fb-d397-4697-88f1-f55e3ee7c499', out)
+ def test_rebuild_with_hostname(self):
+ self.run_command(
+ 'rebuild sample-server %s --hostname new-hostname' % FAKE_UUID_1,
+ api_version='2.90')
+ self.assert_called('GET', '/servers?name=sample-server', pos=0)
+ self.assert_called('GET', '/servers/1234', pos=1)
+ self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_1, pos=2)
+ self.assert_called(
+ 'POST', '/servers/1234/action',
+ {
+ 'rebuild': {
+ 'imageRef': FAKE_UUID_1,
+ 'description': None,
+ 'hostname': 'new-hostname',
+ },
+ },
+ pos=3)
+ self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=4)
+
+ def test_rebuild_with_hostname_pre_v290(self):
+ self.assertRaises(
+ SystemExit, self.run_command,
+ 'rebuild sample-server %s --hostname hostname' % FAKE_UUID_1,
+ api_version='2.89')
+
def test_start(self):
self.run_command('start sample-server')
self.assert_called('POST', '/servers/1234/action', {'os-start': None})
@@ -2424,6 +2477,25 @@ class ShellTest(utils.TestCase):
'update --description new-description sample-server',
api_version='2.18')
+ def test_update_with_hostname(self):
+ self.run_command(
+ 'update --hostname new-hostname sample-server',
+ api_version='2.90')
+ expected_put_body = {
+ "server": {
+ "hostname": "new-hostname"
+ }
+ }
+ self.assert_called('GET', '/servers/1234', pos=-2)
+ self.assert_called('PUT', '/servers/1234', expected_put_body, pos=-1)
+
+ def test_update_with_hostname_pre_v290(self):
+ self.assertRaises(
+ SystemExit,
+ self.run_command,
+ 'update --hostname new-hostname sample-server',
+ api_version='2.89')
+
def test_resize(self):
self.run_command('resize sample-server 1')
self.assert_called('POST', '/servers/1234/action',
diff --git a/novaclient/v2/servers.py b/novaclient/v2/servers.py
index 7768ef7a..76709ec5 100644
--- a/novaclient/v2/servers.py
+++ b/novaclient/v2/servers.py
@@ -67,17 +67,17 @@ class Server(base.Resource):
@api_versions.wraps("2.0", "2.18")
def update(self, name=None):
"""
- Update the name for this server.
+ Update attributes of this server.
:param name: Update the server's name.
:returns: :class:`Server`
"""
return self.manager.update(self, name=name)
- @api_versions.wraps("2.19")
+ @api_versions.wraps("2.19", "2.89")
def update(self, name=None, description=None):
"""
- Update the name and the description for this server.
+ Update attributes of this server.
:param name: Update the server's name.
:param description: Update the server's description.
@@ -88,6 +88,23 @@ class Server(base.Resource):
update_kwargs["description"] = description
return self.manager.update(self, **update_kwargs)
+ @api_versions.wraps("2.90")
+ def update(self, name=None, description=None, hostname=None):
+ """
+ Update attributes of this server.
+
+ :param name: Update the server's name.
+ :param description: Update the server's description.
+ :param hostname: Update the server's hostname.
+ :returns: :class:`Server`
+ """
+ update_kwargs = {"name": name}
+ if description is not None:
+ update_kwargs["description"] = description
+ if hostname is not None:
+ update_kwargs["hostname"] = hostname
+ return self.manager.update(self, **update_kwargs)
+
def get_console_output(self, length=None):
"""
Get text console log output from Server.
@@ -704,7 +721,7 @@ class ServerManager(base.BootingManagerWithFind):
config_drive=None, admin_pass=None, disk_config=None,
access_ip_v4=None, access_ip_v6=None, description=None,
tags=None, trusted_image_certificates=None,
- host=None, hypervisor_hostname=None, **kwargs):
+ host=None, hypervisor_hostname=None, hostname=None, **kwargs):
"""
Create (boot) a new server.
"""
@@ -833,6 +850,9 @@ class ServerManager(base.BootingManagerWithFind):
if hypervisor_hostname:
body['server']['hypervisor_hostname'] = hypervisor_hostname
+ if hostname:
+ body['server']['hostname'] = hostname
+
return self._create('/servers', body, response_key,
return_raw=return_raw, **kwargs)
@@ -1318,10 +1338,8 @@ class ServerManager(base.BootingManagerWithFind):
config_drive=None, disk_config=None, admin_pass=None,
access_ip_v4=None, access_ip_v6=None,
trusted_image_certificates=None,
- host=None, hypervisor_hostname=None,
+ host=None, hypervisor_hostname=None, hostname=None,
**kwargs):
- # TODO(anthony): indicate in doc string if param is an extension
- # and/or optional
"""
Create (boot) a new server.
@@ -1390,6 +1408,8 @@ class ServerManager(base.BootingManagerWithFind):
(allowed since microversion 2.74)
:param hypervisor_hostname: requested hypervisor hostname to create
servers (allowed since microversion 2.74)
+ :param hostname: requested hostname of server (allowed since
+ microversion 2.90)
"""
if not min_count:
min_count = 1
@@ -1453,6 +1473,10 @@ class ServerManager(base.BootingManagerWithFind):
raise exceptions.UnsupportedAttribute(
"hypervisor_hostname", "2.74")
+ hostname_microversion = api_versions.APIVersion("2.90")
+ if hostname and self.api_version < hostname_microversion:
+ raise exceptions.UnsupportedAttribute("hostname", "2.90")
+
boot_kwargs = dict(
meta=meta, files=files, userdata=userdata,
reservation_id=reservation_id, min_count=min_count,
@@ -1463,7 +1487,7 @@ class ServerManager(base.BootingManagerWithFind):
access_ip_v4=access_ip_v4, access_ip_v6=access_ip_v6,
trusted_image_certificates=trusted_image_certificates,
host=host, hypervisor_hostname=hypervisor_hostname,
- **kwargs)
+ hostname=hostname, **kwargs)
if block_device_mapping:
boot_kwargs['block_device_mapping'] = block_device_mapping
@@ -1479,10 +1503,11 @@ class ServerManager(base.BootingManagerWithFind):
@api_versions.wraps("2.0", "2.18")
def update(self, server, name=None):
"""
- Update the name for a server.
+ Update attributes of a server.
:param server: The :class:`Server` (or its ID) to update.
:param name: Update the server's name.
+ :returns: :class:`Server`
"""
if name is None:
return
@@ -1495,15 +1520,16 @@ class ServerManager(base.BootingManagerWithFind):
return self._update("/servers/%s" % base.getid(server), body, "server")
- @api_versions.wraps("2.19")
+ @api_versions.wraps("2.19", "2.89")
def update(self, server, name=None, description=None):
"""
- Update the name or the description for a server.
+ Update attributes of 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.
+ :returns: :class:`Server`
"""
if name is None and description is None:
return
@@ -1518,6 +1544,36 @@ class ServerManager(base.BootingManagerWithFind):
return self._update("/servers/%s" % base.getid(server), body, "server")
+ @api_versions.wraps("2.90")
+ def update(self, server, name=None, description=None, hostname=None):
+ """
+ Update attributes of 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.
+ :param hostname: Update the server's hostname as recorded by the
+ metadata service. Note that a separate utility running on the
+ guest will be necessary to reflect these changes in the guest
+ itself.
+ :returns: :class:`Server`
+ """
+ if name is None and description is None and hostname is None:
+ return
+
+ body = {"server": {}}
+ if name:
+ body["server"]["name"] = name
+ if description == "":
+ body["server"]["description"] = None
+ elif description:
+ body["server"]["description"] = description
+ if hostname:
+ body["server"]["hostname"] = hostname
+
+ return self._update("/servers/%s" % base.getid(server), body, "server")
+
def change_password(self, server, password):
"""
Update the password for a server.
@@ -1548,6 +1604,7 @@ class ServerManager(base.BootingManagerWithFind):
"""
return self._action('reboot', server, {'type': reboot_type})
+ # TODO(stephenfin): Expand out kwargs
def rebuild(self, server, image, password=None, disk_config=None,
preserve_ephemeral=False, name=None, meta=None, files=None,
**kwargs):
@@ -1555,34 +1612,36 @@ class ServerManager(base.BootingManagerWithFind):
Rebuild -- shut down and then re-image -- a server.
:param server: The :class:`Server` (or its ID) to share onto.
- :param image: the :class:`Image` (or its ID) to re-image with.
- :param password: string to set as password on the rebuilt server.
- :param disk_config: partitioning mode to use on the rebuilt server.
- Valid values are 'AUTO' or 'MANUAL'
+ :param image: The :class:`Image` (or its ID) to re-image with.
+ :param password: String to set as password on the rebuilt server.
+ :param disk_config: Partitioning mode to use on the rebuilt server.
+ Valid values are 'AUTO' or 'MANUAL'
:param preserve_ephemeral: If True, request that any ephemeral device
be preserved when rebuilding the instance. Defaults to False.
:param name: Something to name the server.
:param meta: A dict of arbitrary key/value metadata to store for this
- server. Both keys and values must be <=255 characters.
+ server. Both keys and values must be <=255 characters.
:param files: A dict of files to overwrite on the server upon boot.
- Keys are file names (i.e. ``/etc/passwd``) and values
- 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.
- (deprecated starting with microversion 2.57)
- :param description: optional description of the server (allowed since
- microversion 2.19)
- :param key_name: optional key pair name for rebuild operation; passing
- None will unset the key for the server instance
- (starting from microversion 2.54)
- :param userdata: optional user data to pass to be exposed by the
- metadata server; this can be a file type object as
- well or a string. If None is specified, the existing
- user_data is unset.
- (starting from microversion 2.57)
+ Keys are file names (i.e. ``/etc/passwd``) and values 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.
+ (deprecated starting with microversion 2.57)
+ :param description: Optional description of the server. If None is
+ specified, the existing description will be unset.
+ (starting from microversion 2.19)
+ :param key_name: Optional key pair name for rebuild operation. If None
+ is specified, the existing key will be unset.
+ (starting from microversion 2.54)
+ :param userdata: Optional user data to pass to be exposed by the
+ metadata server; this can be a file type object as well or a
+ string. If None is specified, the existing user_data is unset.
+ (starting from microversion 2.57)
:param trusted_image_certificates: A list of trusted certificate IDs
- or None to unset/reset the servers trusted image
- certificates (allowed since microversion 2.63)
+ or None to unset/reset the servers trusted image certificates
+ (starting from microversion 2.63)
+ :param hostname: Optional hostname to configure for the instance. If
+ None is specified, the existing hostname will be unset.
+ (starting from microversion 2.90)
:returns: :class:`Server`
"""
descr_microversion = api_versions.APIVersion("2.19")
@@ -1612,6 +1671,12 @@ class ServerManager(base.BootingManagerWithFind):
raise exceptions.UnsupportedAttribute("trusted_image_certificates",
"2.63")
+ if (
+ 'hostname' in kwargs and
+ self.api_version < api_versions.APIVersion("2.90")
+ ):
+ raise exceptions.UnsupportedAttribute('hostname', '2.90')
+
body = {'imageRef': base.getid(image)}
if password is not None:
body['adminPass'] = password
@@ -1628,6 +1693,8 @@ class ServerManager(base.BootingManagerWithFind):
if "trusted_image_certificates" in kwargs:
body["trusted_image_certificates"] = kwargs[
"trusted_image_certificates"]
+ if "hostname" in kwargs:
+ body["hostname"] = kwargs["hostname"]
if meta:
body['metadata'] = meta
if files:
diff --git a/novaclient/v2/shell.py b/novaclient/v2/shell.py
index e47fcbc0..dfe93c01 100644
--- a/novaclient/v2/shell.py
+++ b/novaclient/v2/shell.py
@@ -532,8 +532,10 @@ def _boot(cs, args):
if include_files:
boot_kwargs['files'] = files
- if ('trusted_image_certificates' in args and
- args.trusted_image_certificates):
+ if (
+ 'trusted_image_certificates' in args and
+ args.trusted_image_certificates
+ ):
boot_kwargs['trusted_image_certificates'] = (
args.trusted_image_certificates)
elif utils.env('OS_TRUSTED_IMAGE_CERTIFICATE_IDS'):
@@ -545,6 +547,9 @@ def _boot(cs, args):
"OS_TRUSTED_IMAGE_CERTIFICATE_IDS",
"2.63")
+ if 'hostname' in args and args.hostname:
+ boot_kwargs['hostname'] = args.hostname
+
return boot_args, boot_kwargs
@@ -970,6 +975,14 @@ def _boot(cs, args):
help=_('Requested hypervisor hostname to create servers. Admin only by '
'default.'),
start_version="2.74")
+@utils.arg(
+ '--hostname',
+ help=_(
+ 'Hostname for the instance. This sets the hostname stored in the '
+ 'metadata server: a utility such as cloud-init running on the guest '
+ 'is required to propagate these changes to the guest.'
+ ),
+ start_version='2.90')
def do_boot(cs, args):
"""Boot a new server."""
boot_args, boot_kwargs = _boot(cs, args)
@@ -2031,6 +2044,14 @@ def do_reboot(cs, args):
help=_("Unset trusted_image_certificates in the server. Cannot be "
"specified with the '--trusted-image-certificate-id' option."),
start_version="2.63")
+@utils.arg(
+ '--hostname',
+ help=_(
+ 'New hostname for the instance. This only updates the hostname '
+ 'stored in the metadata server: a utility running on the guest '
+ 'is required to propagate these changes to the guest.'
+ ),
+ start_version='2.90')
def do_rebuild(cs, args):
"""Shutdown, re-image, and re-boot a server."""
server = _find_server(cs, args.server)
@@ -2121,6 +2142,9 @@ def do_rebuild(cs, args):
"OS_TRUSTED_IMAGE_CERTIFICATE_IDS",
"2.63")
+ if 'hostname' in args and args.hostname is not None:
+ kwargs['hostname'] = args.hostname
+
server = server.rebuild(image, _password, **kwargs)
_print_server(cs, args, server)
@@ -2145,6 +2169,14 @@ def do_rebuild(cs, args):
help=_('New description for the server. If it equals to empty string '
'(i.g. ""), the server description will be removed.'),
start_version="2.19")
+@utils.arg(
+ '--hostname',
+ help=_(
+ 'New hostname for the instance. This only updates the hostname '
+ 'stored in the metadata server: a utility running on the guest '
+ 'is required to propagate these changes to the guest.'
+ ),
+ start_version='2.90')
def do_update(cs, args):
"""Update the name or the description for a server."""
update_kwargs = {}
@@ -2152,6 +2184,8 @@ def do_update(cs, args):
update_kwargs["name"] = args.name
if "description" in args and args.description is not None:
update_kwargs["description"] = args.description
+ if "hostname" in args and args.hostname is not None:
+ update_kwargs["hostname"] = args.hostname
_find_server(cs, args.server).update(**update_kwargs)
diff --git a/releasenotes/notes/microversion-v2_90-259779668e67dfb5.yaml b/releasenotes/notes/microversion-v2_90-259779668e67dfb5.yaml
new file mode 100644
index 00000000..8ab21c91
--- /dev/null
+++ b/releasenotes/notes/microversion-v2_90-259779668e67dfb5.yaml
@@ -0,0 +1,13 @@
+---
+features:
+ - |
+ Added support for `microversion 2.90`_. This microversion provides the
+ ability to manually configure the instance ``hostname`` attribute when
+ creating a new instance (``nova boot --hostname HOSTNAME ...``), updating
+ an existing instance (``nova update --hostname HOSTNAME ...``), or
+ rebuilding an existing instance (``nova rebuild --hostname HOSTNAME``).
+ This attribute is published via the metadata service and config drive and
+ can be used by init scripts such as ``cloud-init`` to configure the guest's
+ hostname.
+
+ .. _microversion 2.90: https://docs.openstack.org/nova/latest/reference/api-microversion-history.html#microversion-2-90