Merge "Microversion 2.63 - Add trusted_image_certificates"

This commit is contained in:
Zuul 2018-06-18 00:52:39 +00:00 committed by Gerrit Code Review
commit 910328c30d
8 changed files with 465 additions and 4 deletions

View File

@ -1011,6 +1011,7 @@ nova boot
[--config-drive <value>] [--poll] [--admin-pass <value>]
[--access-ip-v4 <value>] [--access-ip-v6 <value>]
[--description <description>]
[--trusted-image-certificate-id]
<name>
Boot a new server.
@ -1164,6 +1165,13 @@ Boot a new server.
Description for the server. (Supported by API
versions '2.19' - '2.latest')
``--trusted-image-certificate-id <trusted-image-certificate-id>``
Trusted image certificate IDs used to validate certificates
during the image signature verification process.
Defaults to env[OS_TRUSTED_IMAGE_CERTIFICATE_IDS].
May be specified multiple times to pass multiple trusted image
certificate IDs. (Supported by API versions '2.63' - '2.latest')
.. _nova_cell-capacities:
nova cell-capacities
@ -2683,6 +2691,8 @@ nova rebuild
[--minimal] [--preserve-ephemeral] [--name <name>]
[--description <description>] [--meta <key=value>]
[--file <dst-path=src-path>]
[--trusted-image-certificate-id <trusted-image-certificate-id>]
[--trusted-image-certificates-unset]
<server> <image>
Shutdown, re-image, and re-boot a server.
@ -2730,6 +2740,18 @@ Shutdown, re-image, and re-boot a server.
to <dst-path> on the new server. You may store
up to 5 files.
``--trusted-image-certificate-id <trusted-image-certificate-id>``
Trusted image certificate IDs used to validate certificates
during the image signature verification process.
Defaults to env[OS_TRUSTED_IMAGE_CERTIFICATE_IDS].
May be specified multiple times to pass multiple trusted image
certificate IDs. (Supported by API versions '2.63' - '2.latest')
``--trusted-image-certificates-unset``
Unset trusted_image_certificates in the server. Cannot be
specified with the ``--trusted-image-certificate-id`` option.
(Supported by API versions '2.63' - '2.latest')
.. _nova_refresh-network:
nova refresh-network

View File

@ -60,6 +60,16 @@ some environment variables:
The Keystone region name. Defaults to the first region if multiple regions
are available.
.. envvar:: OS_TRUSTED_IMAGE_CERTIFICATE_IDS
A comma-delimited list of trusted image certificate IDs. Only used
with the ``nova boot`` and ``nova rebuild`` commands starting with the
2.63 microversion.
For example::
export OS_TRUSTED_IMAGE_CERTIFICATE_IDS=trusted-cert-id1,trusted-cert-id2
For example, in Bash you'd use::
export OS_USERNAME=yourname

View File

@ -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.62")
API_MAX_VERSION = api_versions.APIVersion("2.63")

View File

@ -1542,3 +1542,70 @@ class ServersV257Test(ServersV256Test):
exceptions.UnsupportedAttribute, s.rebuild, image=1, name='new',
meta={'foo': 'bar'}, files=files)
self.assertIn('files', six.text_type(ex))
class ServersV263Test(ServersV257Test):
api_version = "2.63"
def test_create_server_with_trusted_image_certificates(self):
self.cs.servers.create(
name="My server",
image=1,
flavor=1,
meta={'foo': 'bar'},
userdata="hello moto",
key_name="fakekey",
nics=self._get_server_create_default_nics(),
trusted_image_certificates=['id1', 'id2'],
)
self.assert_called('POST', '/servers',
{'server': {
'flavorRef': '1',
'imageRef': '1',
'key_name': 'fakekey',
'max_count': 1,
'metadata': {'foo': 'bar'},
'min_count': 1,
'name': 'My server',
'networks': 'auto',
'trusted_image_certificates': ['id1', 'id2'],
'user_data': 'aGVsbG8gbW90bw=='
}}
)
def test_create_server_with_trusted_image_certificates_pre_263_fails(self):
self.cs.api_version = api_versions.APIVersion('2.62')
ex = self.assertRaises(
exceptions.UnsupportedAttribute, self.cs.servers.create,
name="My server", image=1, flavor=1, meta={'foo': 'bar'},
userdata="hello moto", key_name="fakekey",
nics=self._get_server_create_default_nics(),
trusted_image_certificates=['id1', 'id2'])
self.assertIn('trusted_image_certificates', six.text_type(ex))
def test_rebuild_server_with_trusted_image_certificates(self):
s = self.cs.servers.get(1234)
ret = s.rebuild(image="1", trusted_image_certificates=['id1', 'id2'])
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('POST', '/servers/1234/action',
{'rebuild': {
'imageRef': '1',
'trusted_image_certificates': ['id1', 'id2']}})
def test_rebuild_server_with_trusted_image_certificates_none(self):
s = self.cs.servers.get(1234)
ret = s.rebuild(image="1", trusted_image_certificates=None)
self.assert_request_id(ret, fakes.FAKE_REQUEST_ID_LIST)
self.assert_called('POST', '/servers/1234/action',
{'rebuild': {
'imageRef': '1',
'trusted_image_certificates': None}})
def test_rebuild_with_trusted_image_certificates_pre_263_fails(self):
self.cs.api_version = api_versions.APIVersion('2.62')
ex = self.assertRaises(exceptions.UnsupportedAttribute,
self.cs.servers.rebuild,
'1234', fakes.FAKE_IMAGE_UUID_1,
trusted_image_certificates=['id1', 'id2'])
self.assertIn('trusted_image_certificates', six.text_type(ex))

View File

@ -1155,6 +1155,113 @@ class ShellTest(utils.TestCase):
self.assertRaises(SystemExit, self.run_command,
cmd, api_version='2.51')
def test_boot_with_single_trusted_image_certificates(self):
self.run_command('boot --flavor 1 --image %s --nic auto some-server '
'--trusted-image-certificate-id id1'
% FAKE_UUID_1, api_version='2.63')
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'flavorRef': '1',
'name': 'some-server',
'imageRef': FAKE_UUID_1,
'min_count': 1,
'max_count': 1,
'networks': 'auto',
'trusted_image_certificates': ['id1']
}},
)
def test_boot_with_multiple_trusted_image_certificates(self):
self.run_command('boot --flavor 1 --image %s --nic auto some-server '
'--trusted-image-certificate-id id1 '
'--trusted-image-certificate-id id2'
% FAKE_UUID_1, api_version='2.63')
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'flavorRef': '1',
'name': 'some-server',
'imageRef': FAKE_UUID_1,
'min_count': 1,
'max_count': 1,
'networks': 'auto',
'trusted_image_certificates': ['id1', 'id2']
}},
)
def test_boot_with_trusted_image_certificates_envar(self):
self.useFixture(fixtures.EnvironmentVariable(
'OS_TRUSTED_IMAGE_CERTIFICATE_IDS', 'var_id1,var_id2'))
self.run_command('boot --flavor 1 --image %s --nic auto some-server'
% FAKE_UUID_1, api_version='2.63')
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'flavorRef': '1',
'name': 'some-server',
'imageRef': FAKE_UUID_1,
'min_count': 1,
'max_count': 1,
'networks': 'auto',
'trusted_image_certificates': ['var_id1', 'var_id2']
}},
)
def test_boot_without_trusted_image_certificates_v263(self):
self.run_command('boot --flavor 1 --image %s --nic auto some-server'
% FAKE_UUID_1, api_version='2.63')
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'flavorRef': '1',
'name': 'some-server',
'imageRef': FAKE_UUID_1,
'min_count': 1,
'max_count': 1,
'networks': 'auto',
}},
)
def test_boot_with_trusted_image_certificates_pre_v263(self):
cmd = ('boot --flavor 1 --image %s some-server '
'--trusted-image-certificate-id id1 '
'--trusted-image-certificate-id id2' % FAKE_UUID_1)
self.assertRaises(SystemExit, self.run_command,
cmd, api_version='2.62')
# OS_TRUSTED_IMAGE_CERTIFICATE_IDS environment variable is not supported in
# microversions < 2.63 (should result in an UnsupportedAttribute exception)
def test_boot_with_trusted_image_certificates_envar_pre_v263(self):
self.useFixture(fixtures.EnvironmentVariable(
'OS_TRUSTED_IMAGE_CERTIFICATE_IDS', 'var_id1,var_id2'))
cmd = ('boot --flavor 1 --image %s --nic auto some-server '
% FAKE_UUID_1)
self.assertRaises(exceptions.UnsupportedAttribute, self.run_command,
cmd, api_version='2.62')
def test_boot_with_trusted_image_certificates_arg_and_envvar(self):
"""Tests that if both the environment variable and argument are
specified, the argument takes precedence.
"""
self.useFixture(fixtures.EnvironmentVariable(
'OS_TRUSTED_IMAGE_CERTIFICATE_IDS', 'cert1'))
self.run_command('boot --flavor 1 --image %s --nic auto '
'--trusted-image-certificate-id cert2 some-server'
% FAKE_UUID_1, api_version='2.63')
self.assert_called_anytime(
'POST', '/servers',
{'server': {
'flavorRef': '1',
'name': 'some-server',
'imageRef': FAKE_UUID_1,
'min_count': 1,
'max_count': 1,
'networks': 'auto',
'trusted_image_certificates': ['cert2']
}},
)
def test_flavor_list(self):
out, _ = self.run_command('flavor-list')
self.assert_called_anytime('GET', '/flavors/detail')
@ -1664,6 +1771,148 @@ class ShellTest(utils.TestCase):
self.assertIn("Cannot specify '--user-data-unset' with "
"'--user-data'.", six.text_type(ex))
def test_rebuild_with_single_trusted_image_certificates(self):
self.run_command('rebuild sample-server %s '
'--trusted-image-certificate-id id1'
% FAKE_UUID_1, api_version='2.63')
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,
'trusted_image_certificates': ['id1']
}
}, pos=3)
self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=4)
def test_rebuild_with_multiple_trusted_image_certificate_ids(self):
self.run_command('rebuild sample-server %s '
'--trusted-image-certificate-id id1 '
'--trusted-image-certificate-id id2'
% FAKE_UUID_1, api_version='2.63')
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,
'trusted_image_certificates': ['id1',
'id2']
}
}, pos=3)
self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=4)
def test_rebuild_with_trusted_image_certificates_envar(self):
self.useFixture(fixtures.EnvironmentVariable(
'OS_TRUSTED_IMAGE_CERTIFICATE_IDS', 'var_id1,var_id2'))
self.run_command('rebuild sample-server %s'
% FAKE_UUID_1, api_version='2.63')
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,
'trusted_image_certificates':
['var_id1', 'var_id2']}
}, pos=3)
self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=4)
def test_rebuild_without_trusted_image_certificates_v263(self):
self.run_command('rebuild sample-server %s' % FAKE_UUID_1,
api_version='2.63')
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,
}
}, pos=3)
self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=4)
def test_rebuild_with_trusted_image_certificates_pre_v263(self):
cmd = ('rebuild sample-server %s'
'--trusted-image-certificate-id id1 '
'--trusted-image-certificate-id id2' % FAKE_UUID_1)
self.assertRaises(SystemExit, self.run_command,
cmd, api_version='2.62')
# OS_TRUSTED_IMAGE_CERTIFICATE_IDS environment variable is not supported in
# microversions < 2.63 (should result in an UnsupportedAttribute exception)
def test_rebuild_with_trusted_image_certificates_envar_pre_v263(self):
self.useFixture(fixtures.EnvironmentVariable(
'OS_TRUSTED_IMAGE_CERTIFICATE_IDS', 'var_id1,var_id2'))
cmd = ('rebuild sample-server %s' % FAKE_UUID_1)
self.assertRaises(exceptions.UnsupportedAttribute, self.run_command,
cmd, api_version='2.62')
def test_rebuild_with_trusted_image_certificates_unset(self):
"""Tests explicitly unsetting the existing server trusted image
certificate IDs.
"""
self.run_command('rebuild sample-server %s '
'--trusted-image-certificates-unset'
% FAKE_UUID_1, api_version='2.63')
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,
'trusted_image_certificates': None
}
}, pos=3)
self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=4)
def test_rebuild_with_trusted_image_certificates_unset_arg_conflict(self):
"""Tests the error condition that trusted image certs are both unset
and set via argument during rebuild.
"""
ex = self.assertRaises(
exceptions.CommandError, self.run_command,
'rebuild sample-server %s --trusted-image-certificate-id id1 '
'--trusted-image-certificates-unset' % FAKE_UUID_1,
api_version='2.63')
self.assertIn("Cannot specify '--trusted-image-certificates-unset' "
"with '--trusted-image-certificate-id'",
six.text_type(ex))
def test_rebuild_with_trusted_image_certificates_unset_env_conflict(self):
"""Tests the error condition that trusted image certs are both unset
and set via environment variable during rebuild.
"""
self.useFixture(fixtures.EnvironmentVariable(
'OS_TRUSTED_IMAGE_CERTIFICATE_IDS', 'var_id1'))
ex = self.assertRaises(
exceptions.CommandError, self.run_command,
'rebuild sample-server %s --trusted-image-certificates-unset' %
FAKE_UUID_1, api_version='2.63')
self.assertIn("Cannot specify '--trusted-image-certificates-unset' "
"with '--trusted-image-certificate-id'",
six.text_type(ex))
def test_rebuild_with_trusted_image_certificates_arg_and_envar(self):
"""Tests that if both the environment variable and argument are
specified, the argument takes precedence.
"""
self.useFixture(fixtures.EnvironmentVariable(
'OS_TRUSTED_IMAGE_CERTIFICATE_IDS', 'cert1'))
self.run_command('rebuild sample-server '
'--trusted-image-certificate-id cert2 %s'
% FAKE_UUID_1, api_version='2.63')
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,
'trusted_image_certificates':
['cert2']}
}, pos=3)
self.assert_called('GET', '/v2/images/%s' % FAKE_UUID_2, pos=4)
def test_start(self):
self.run_command('start sample-server')
self.assert_called('POST', '/servers/1234/action', {'os-start': None})
@ -3547,6 +3796,7 @@ class ShellTest(utils.TestCase):
60, # There are no client-side changes for volume multiattach.
61, # There are no version-wrapped shell method changes for this.
62, # There are no version-wrapped shell method changes for this.
63, # There are no version-wrapped shell method changes for this.
])
versions_supported = set(range(0,
novaclient.API_MAX_VERSION.ver_minor + 1))

View File

@ -650,7 +650,7 @@ class ServerManager(base.BootingManagerWithFind):
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, description=None,
tags=None, **kwargs):
tags=None, trusted_image_certificates=None, **kwargs):
"""
Create (boot) a new server.
"""
@ -768,6 +768,10 @@ class ServerManager(base.BootingManagerWithFind):
if tags:
body['server']['tags'] = tags
if trusted_image_certificates:
body['server']['trusted_image_certificates'] = (
trusted_image_certificates)
return self._create('/servers', body, response_key,
return_raw=return_raw, **kwargs)
@ -1191,7 +1195,8 @@ class ServerManager(base.BootingManagerWithFind):
block_device_mapping=None, block_device_mapping_v2=None,
nics=None, scheduler_hints=None,
config_drive=None, disk_config=None, admin_pass=None,
access_ip_v4=None, access_ip_v6=None, **kwargs):
access_ip_v4=None, access_ip_v6=None,
trusted_image_certificates=None, **kwargs):
# TODO(anthony): indicate in doc string if param is an extension
# and/or optional
"""
@ -1252,6 +1257,8 @@ class ServerManager(base.BootingManagerWithFind):
microversion 2.19)
:param tags: A list of arbitrary strings to be added to the
server as tags (allowed since microversion 2.52)
:param trusted_image_certificates: A list of trusted certificate IDs
(allowed since microversion 2.63)
"""
if not min_count:
min_count = 1
@ -1292,6 +1299,12 @@ class ServerManager(base.BootingManagerWithFind):
if files and self.api_version >= personality_files_deprecation:
raise exceptions.UnsupportedAttribute('files', '2.0', '2.56')
trusted_certs_microversion = api_versions.APIVersion("2.63")
if (trusted_image_certificates and
self.api_version < trusted_certs_microversion):
raise exceptions.UnsupportedAttribute("trusted_image_certificates",
"2.63")
boot_kwargs = dict(
meta=meta, files=files, userdata=userdata,
reservation_id=reservation_id, min_count=min_count,
@ -1299,7 +1312,8 @@ class ServerManager(base.BootingManagerWithFind):
key_name=key_name, availability_zone=availability_zone,
scheduler_hints=scheduler_hints, config_drive=config_drive,
disk_config=disk_config, admin_pass=admin_pass,
access_ip_v4=access_ip_v4, access_ip_v6=access_ip_v6, **kwargs)
access_ip_v4=access_ip_v4, access_ip_v6=access_ip_v6,
trusted_image_certificates=trusted_image_certificates, **kwargs)
if block_device_mapping:
boot_kwargs['block_device_mapping'] = block_device_mapping
@ -1416,6 +1430,9 @@ class ServerManager(base.BootingManagerWithFind):
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)
:returns: :class:`Server`
"""
descr_microversion = api_versions.APIVersion("2.19")
@ -1436,6 +1453,15 @@ class ServerManager(base.BootingManagerWithFind):
if 'userdata' in kwargs and self.api_version < files_and_userdata:
raise exceptions.UnsupportedAttribute('userdata', '2.57')
trusted_certs_microversion = api_versions.APIVersion("2.63")
# trusted_image_certificates is intentionally *not* a named kwarg
# so that trusted_image_certificates=None is not confused with an
# intentional unset/reset request.
if ("trusted_image_certificates" in kwargs and
self.api_version < trusted_certs_microversion):
raise exceptions.UnsupportedAttribute("trusted_image_certificates",
"2.63")
body = {'imageRef': base.getid(image)}
if password is not None:
body['adminPass'] = password
@ -1449,6 +1475,9 @@ class ServerManager(base.BootingManagerWithFind):
body["description"] = kwargs["description"]
if 'key_name' in kwargs:
body['key_name'] = kwargs['key_name']
if "trusted_image_certificates" in kwargs:
body["trusted_image_certificates"] = kwargs[
"trusted_image_certificates"]
if meta:
body['metadata'] = meta
if files:

View File

@ -510,6 +510,14 @@ def _boot(cs, args):
if include_files:
boot_kwargs['files'] = files
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'):
boot_kwargs["trusted_image_certificates"] = utils.env(
'OS_TRUSTED_IMAGE_CERTIFICATE_IDS').split(',')
return boot_args, boot_kwargs
@ -874,6 +882,18 @@ def _boot(cs, args):
action="store_true",
default=False,
help=_("Return a reservation id bound to created servers."))
@utils.arg(
'--trusted-image-certificate-id',
metavar='<trusted-image-certificate-id>',
action='append',
dest='trusted_image_certificates',
default=[],
help=_('Trusted image certificate IDs used to validate certificates '
'during the image signature verification process. '
'Defaults to env[OS_TRUSTED_IMAGE_CERTIFICATE_IDS]. '
'May be specified multiple times to pass multiple trusted image '
'certificate IDs.'),
start_version="2.63")
def do_boot(cs, args):
"""Boot a new server."""
boot_args, boot_kwargs = _boot(cs, args)
@ -1807,6 +1827,25 @@ def do_reboot(cs, args):
help=_("Unset user_data in the server. Cannot be specified with the "
"'--user-data' option."),
start_version='2.57')
@utils.arg(
'--trusted-image-certificate-id',
metavar='<trusted-image-certificate-id>',
action='append',
dest='trusted_image_certificates',
default=[],
help=_('Trusted image certificate IDs used to validate certificates '
'during the image signature verification process. '
'Defaults to env[OS_TRUSTED_IMAGE_CERTIFICATE_IDS]. '
'May be specified multiple times to pass multiple trusted image '
'certificate IDs.'),
start_version="2.63")
@utils.arg(
'--trusted-image-certificates-unset',
action='store_true',
default=False,
help=_("Unset trusted_image_certificates in the server. Cannot be "
"specified with the '--trusted-image-certificate-id' option."),
start_version="2.63")
def do_rebuild(cs, args):
"""Shutdown, re-image, and re-boot a server."""
server = _find_server(cs, args.server)
@ -1861,6 +1900,33 @@ def do_rebuild(cs, args):
elif args.key_name:
kwargs['key_name'] = args.key_name
if cs.api_version >= api_versions.APIVersion('2.63'):
# First determine if the user specified anything via the command line
# or the environment variable.
trusted_image_certificates = None
if ('trusted_image_certificates' in args and
args.trusted_image_certificates):
trusted_image_certificates = args.trusted_image_certificates
elif utils.env('OS_TRUSTED_IMAGE_CERTIFICATE_IDS'):
trusted_image_certificates = utils.env(
'OS_TRUSTED_IMAGE_CERTIFICATE_IDS').split(',')
if args.trusted_image_certificates_unset:
kwargs['trusted_image_certificates'] = None
# Check for conflicts in option usage.
if trusted_image_certificates:
raise exceptions.CommandError(
_("Cannot specify '--trusted-image-certificates-unset' "
"with '--trusted-image-certificate-id' or with "
"OS_TRUSTED_IMAGE_CERTIFICATE_IDS env variable set."))
elif trusted_image_certificates:
# Only specify the kwarg if there is a value specified to avoid
# confusion with unsetting the value.
kwargs['trusted_image_certificates'] = trusted_image_certificates
elif utils.env('OS_TRUSTED_IMAGE_CERTIFICATE_IDS'):
raise exceptions.UnsupportedAttribute("trusted_image_certificates",
"2.63")
server = server.rebuild(image, _password, **kwargs)
_print_server(cs, args, server)

View File

@ -0,0 +1,17 @@
---
features:
- |
Added support for `microversion 2.63`_, which includes the following
changes:
- New environment variable called ``OS_TRUSTED_IMAGE_CERTIFICATE_IDS``
- New ``nova boot`` option called ``--trusted-image-certificate-id``
- New ``nova rebuild`` options called ``--trusted-image-certificate-id``
and ``--trusted-image-certificates-unset``
- New kwarg called ``trusted_image_certificates`` added to python API
bindings:
- ``novaclient.v2.servers.ServerManager.create()``
- ``novaclient.v2.servers.ServerManager.rebuild()``
.. _microversion 2.63: https://docs.openstack.org/nova/latest/api_microversion_history.html#id57