Microversion 2.63 - Add trusted_image_certificates
This change adds a `--trusted-image-certificate-id` option to the `nova boot` and `nova rebuild` commands. This option takes in a a single trusted certificate ID. The option may be used multiple times to specify multiple trusted certificate IDs, which will be used to validate certificates in the image signature verification process. If ID values are not specified using this option, the value of the newly added OS_TRUSTED_IMAGE_CERTIFICATE_IDS environment variable will be used instead. This value will be converted into a list before being passed on. The ``nova rebuild`` command also gets a new ``--trusted-image-certificates-unset`` option to unset/reset the trusted image certificates in a server during rebuild. This is similar to unsetting key_name and user_data during rebuild. Corresponding `trusted_image_certificates` kwarg has been added to the server create and rebuild Python API bindings. Co-Authored-By: Brianna Poulos <Brianna.Poulos@jhuapl.edu> Co-Authored-By: Matt Riedemann <mriedem.os@gmail.com> Change-Id: I235541a689732826950c7b2a510d5835211120c3 Implements: blueprint nova-validate-certificates
This commit is contained in:
parent
7907528256
commit
7f10707e5d
@ -1011,6 +1011,7 @@ nova boot
|
|||||||
[--config-drive <value>] [--poll] [--admin-pass <value>]
|
[--config-drive <value>] [--poll] [--admin-pass <value>]
|
||||||
[--access-ip-v4 <value>] [--access-ip-v6 <value>]
|
[--access-ip-v4 <value>] [--access-ip-v6 <value>]
|
||||||
[--description <description>]
|
[--description <description>]
|
||||||
|
[--trusted-image-certificate-id]
|
||||||
<name>
|
<name>
|
||||||
|
|
||||||
Boot a new server.
|
Boot a new server.
|
||||||
@ -1164,6 +1165,13 @@ Boot a new server.
|
|||||||
Description for the server. (Supported by API
|
Description for the server. (Supported by API
|
||||||
versions '2.19' - '2.latest')
|
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:
|
||||||
|
|
||||||
nova cell-capacities
|
nova cell-capacities
|
||||||
@ -2660,6 +2668,8 @@ nova rebuild
|
|||||||
[--minimal] [--preserve-ephemeral] [--name <name>]
|
[--minimal] [--preserve-ephemeral] [--name <name>]
|
||||||
[--description <description>] [--meta <key=value>]
|
[--description <description>] [--meta <key=value>]
|
||||||
[--file <dst-path=src-path>]
|
[--file <dst-path=src-path>]
|
||||||
|
[--trusted-image-certificate-id <trusted-image-certificate-id>]
|
||||||
|
[--trusted-image-certificates-unset]
|
||||||
<server> <image>
|
<server> <image>
|
||||||
|
|
||||||
Shutdown, re-image, and re-boot a server.
|
Shutdown, re-image, and re-boot a server.
|
||||||
@ -2707,6 +2717,18 @@ Shutdown, re-image, and re-boot a server.
|
|||||||
to <dst-path> on the new server. You may store
|
to <dst-path> on the new server. You may store
|
||||||
up to 5 files.
|
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:
|
||||||
|
|
||||||
nova refresh-network
|
nova refresh-network
|
||||||
|
@ -60,6 +60,16 @@ some environment variables:
|
|||||||
The Keystone region name. Defaults to the first region if multiple regions
|
The Keystone region name. Defaults to the first region if multiple regions
|
||||||
are available.
|
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::
|
For example, in Bash you'd use::
|
||||||
|
|
||||||
export OS_USERNAME=yourname
|
export OS_USERNAME=yourname
|
||||||
|
@ -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.62")
|
API_MAX_VERSION = api_versions.APIVersion("2.63")
|
||||||
|
@ -1542,3 +1542,70 @@ class ServersV257Test(ServersV256Test):
|
|||||||
exceptions.UnsupportedAttribute, s.rebuild, image=1, name='new',
|
exceptions.UnsupportedAttribute, s.rebuild, image=1, name='new',
|
||||||
meta={'foo': 'bar'}, files=files)
|
meta={'foo': 'bar'}, files=files)
|
||||||
self.assertIn('files', six.text_type(ex))
|
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))
|
||||||
|
@ -1155,6 +1155,113 @@ class ShellTest(utils.TestCase):
|
|||||||
self.assertRaises(SystemExit, self.run_command,
|
self.assertRaises(SystemExit, self.run_command,
|
||||||
cmd, api_version='2.51')
|
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):
|
def test_flavor_list(self):
|
||||||
out, _ = self.run_command('flavor-list')
|
out, _ = self.run_command('flavor-list')
|
||||||
self.assert_called_anytime('GET', '/flavors/detail')
|
self.assert_called_anytime('GET', '/flavors/detail')
|
||||||
@ -1664,6 +1771,148 @@ class ShellTest(utils.TestCase):
|
|||||||
self.assertIn("Cannot specify '--user-data-unset' with "
|
self.assertIn("Cannot specify '--user-data-unset' with "
|
||||||
"'--user-data'.", six.text_type(ex))
|
"'--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):
|
def test_start(self):
|
||||||
self.run_command('start sample-server')
|
self.run_command('start sample-server')
|
||||||
self.assert_called('POST', '/servers/1234/action', {'os-start': None})
|
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.
|
60, # There are no client-side changes for volume multiattach.
|
||||||
61, # There are no version-wrapped shell method changes for this.
|
61, # There are no version-wrapped shell method changes for this.
|
||||||
62, # 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,
|
versions_supported = set(range(0,
|
||||||
novaclient.API_MAX_VERSION.ver_minor + 1))
|
novaclient.API_MAX_VERSION.ver_minor + 1))
|
||||||
|
@ -650,7 +650,7 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
block_device_mapping_v2=None, nics=None, scheduler_hints=None,
|
block_device_mapping_v2=None, nics=None, scheduler_hints=None,
|
||||||
config_drive=None, admin_pass=None, disk_config=None,
|
config_drive=None, admin_pass=None, disk_config=None,
|
||||||
access_ip_v4=None, access_ip_v6=None, description=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.
|
Create (boot) a new server.
|
||||||
"""
|
"""
|
||||||
@ -768,6 +768,10 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
if tags:
|
if tags:
|
||||||
body['server']['tags'] = 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 self._create('/servers', body, response_key,
|
||||||
return_raw=return_raw, **kwargs)
|
return_raw=return_raw, **kwargs)
|
||||||
|
|
||||||
@ -1191,7 +1195,8 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
block_device_mapping=None, block_device_mapping_v2=None,
|
block_device_mapping=None, block_device_mapping_v2=None,
|
||||||
nics=None, scheduler_hints=None,
|
nics=None, scheduler_hints=None,
|
||||||
config_drive=None, disk_config=None, admin_pass=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
|
# TODO(anthony): indicate in doc string if param is an extension
|
||||||
# and/or optional
|
# and/or optional
|
||||||
"""
|
"""
|
||||||
@ -1252,6 +1257,8 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
microversion 2.19)
|
microversion 2.19)
|
||||||
:param tags: A list of arbitrary strings to be added to the
|
:param tags: A list of arbitrary strings to be added to the
|
||||||
server as tags (allowed since microversion 2.52)
|
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:
|
if not min_count:
|
||||||
min_count = 1
|
min_count = 1
|
||||||
@ -1292,6 +1299,12 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
if files and self.api_version >= personality_files_deprecation:
|
if files and self.api_version >= personality_files_deprecation:
|
||||||
raise exceptions.UnsupportedAttribute('files', '2.0', '2.56')
|
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(
|
boot_kwargs = dict(
|
||||||
meta=meta, files=files, userdata=userdata,
|
meta=meta, files=files, userdata=userdata,
|
||||||
reservation_id=reservation_id, min_count=min_count,
|
reservation_id=reservation_id, min_count=min_count,
|
||||||
@ -1299,7 +1312,8 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
key_name=key_name, availability_zone=availability_zone,
|
key_name=key_name, availability_zone=availability_zone,
|
||||||
scheduler_hints=scheduler_hints, config_drive=config_drive,
|
scheduler_hints=scheduler_hints, config_drive=config_drive,
|
||||||
disk_config=disk_config, admin_pass=admin_pass,
|
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:
|
if block_device_mapping:
|
||||||
boot_kwargs['block_device_mapping'] = 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
|
well or a string. If None is specified, the existing
|
||||||
user_data is unset.
|
user_data is unset.
|
||||||
(starting from microversion 2.57)
|
(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`
|
:returns: :class:`Server`
|
||||||
"""
|
"""
|
||||||
descr_microversion = api_versions.APIVersion("2.19")
|
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:
|
if 'userdata' in kwargs and self.api_version < files_and_userdata:
|
||||||
raise exceptions.UnsupportedAttribute('userdata', '2.57')
|
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)}
|
body = {'imageRef': base.getid(image)}
|
||||||
if password is not None:
|
if password is not None:
|
||||||
body['adminPass'] = password
|
body['adminPass'] = password
|
||||||
@ -1449,6 +1475,9 @@ class ServerManager(base.BootingManagerWithFind):
|
|||||||
body["description"] = kwargs["description"]
|
body["description"] = kwargs["description"]
|
||||||
if 'key_name' in kwargs:
|
if 'key_name' in kwargs:
|
||||||
body['key_name'] = kwargs['key_name']
|
body['key_name'] = kwargs['key_name']
|
||||||
|
if "trusted_image_certificates" in kwargs:
|
||||||
|
body["trusted_image_certificates"] = kwargs[
|
||||||
|
"trusted_image_certificates"]
|
||||||
if meta:
|
if meta:
|
||||||
body['metadata'] = meta
|
body['metadata'] = meta
|
||||||
if files:
|
if files:
|
||||||
|
@ -510,6 +510,14 @@ def _boot(cs, args):
|
|||||||
if include_files:
|
if include_files:
|
||||||
boot_kwargs['files'] = 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
|
return boot_args, boot_kwargs
|
||||||
|
|
||||||
|
|
||||||
@ -874,6 +882,18 @@ def _boot(cs, args):
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
default=False,
|
default=False,
|
||||||
help=_("Return a reservation id bound to created servers."))
|
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):
|
def do_boot(cs, args):
|
||||||
"""Boot a new server."""
|
"""Boot a new server."""
|
||||||
boot_args, boot_kwargs = _boot(cs, args)
|
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 "
|
help=_("Unset user_data in the server. Cannot be specified with the "
|
||||||
"'--user-data' option."),
|
"'--user-data' option."),
|
||||||
start_version='2.57')
|
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):
|
def do_rebuild(cs, args):
|
||||||
"""Shutdown, re-image, and re-boot a server."""
|
"""Shutdown, re-image, and re-boot a server."""
|
||||||
server = _find_server(cs, args.server)
|
server = _find_server(cs, args.server)
|
||||||
@ -1861,6 +1900,33 @@ def do_rebuild(cs, args):
|
|||||||
elif args.key_name:
|
elif args.key_name:
|
||||||
kwargs['key_name'] = 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)
|
server = server.rebuild(image, _password, **kwargs)
|
||||||
_print_server(cs, args, server)
|
_print_server(cs, args, server)
|
||||||
|
|
||||||
|
17
releasenotes/notes/microversion-v2_63-cd058a9145550cae.yaml
Normal file
17
releasenotes/notes/microversion-v2_63-cd058a9145550cae.yaml
Normal 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
|
Loading…
Reference in New Issue
Block a user