Microversion 2.42 - Fix tag attribute disappearing

The 2.32 microversion added the support for creating a
server with a tagged block device mapping and/or tagged
nic.

Due to a bug, block device tags would only work in the
2.32 microversion but nic tags would still work up until
the 2.37 microversion, which regressed that functionality
as well.

Both block device and nic tags were fixed again in the 2.42
microversion.

This change updates the help text for the 'nova boot' CLI
and client-side validation in the shell for the various
microversion ranges of support for both block device and
nic tags.

Co-Authored-By: Andrey Kurilin <akurilin@mirantis.com>
Change-Id: I7492b20b5d2daa0f8391abed07ef861043c3e51e
Implements: blueprint fix-tag-attribute-disappearing
Closes-Bug: #1658571
This commit is contained in:
Takashi NATSUME 2017-02-06 14:46:54 +09:00 committed by Matt Riedemann
parent 080a8e4d48
commit 4b8863341a
5 changed files with 289 additions and 17 deletions

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.41")
API_MAX_VERSION = api_versions.APIVersion("2.42")

View File

@ -13,24 +13,159 @@
# under the License.
from oslo_utils import uuidutils
import six
from tempest.lib import exceptions
from novaclient.tests.functional import base
class TestDeviceTaggingCLI(base.ClientTestBase):
class TestBlockDeviceTaggingCLIError(base.ClientTestBase):
"""Negative test that asserts that creating a server with a tagged
block device with a specific microversion will fail.
"""
COMPUTE_API_VERSION = "2.31"
def test_boot_server_with_tagged_block_devices_with_error(self):
try:
output = self.nova('boot', params=(
'%(name)s --flavor %(flavor)s --poll '
'--nic net-id=%(net-uuid)s '
'--block-device '
'source=image,dest=volume,id=%(image)s,size=1,bootindex=0,'
'shutdown=remove,tag=bar' % {'name': uuidutils.generate_uuid(),
'flavor': self.flavor.id,
'net-uuid': self.network.id,
'image': self.image.id}))
except exceptions.CommandFailed as e:
self.assertIn("ERROR (CommandError): "
"'tag' in block device mapping is not supported "
"in API version %s." % self.COMPUTE_API_VERSION,
six.text_type(e))
else:
server_id = self._get_value_from_the_table(output, 'id')
self.client.servers.delete(server_id)
self.wait_for_resource_delete(server_id, self.client.servers)
self.fail("Booting a server with block device tag is not failed.")
class TestNICDeviceTaggingCLIError(base.ClientTestBase):
"""Negative test that asserts that creating a server with a tagged
nic with a specific microversion will fail.
"""
COMPUTE_API_VERSION = "2.31"
def test_boot_server_with_tagged_nic_devices_with_error(self):
try:
output = self.nova('boot', params=(
'%(name)s --flavor %(flavor)s --poll '
'--nic net-id=%(net-uuid)s,tag=foo '
'--block-device '
'source=image,dest=volume,id=%(image)s,size=1,bootindex=0,'
'shutdown=remove' % {'name': uuidutils.generate_uuid(),
'flavor': self.flavor.id,
'net-uuid': self.network.id,
'image': self.image.id}))
except exceptions.CommandFailed as e:
self.assertIn("Invalid nic argument", six.text_type(e))
else:
server_id = self._get_value_from_the_table(output, 'id')
self.client.servers.delete(server_id)
self.wait_for_resource_delete(server_id, self.client.servers)
self.fail("Booting a server with network interface tag "
"is not failed.")
class TestBlockDeviceTaggingCLI(base.ClientTestBase):
"""Tests that creating a server with a tagged block device will work
with the 2.32 microversion, where the feature was originally added.
"""
COMPUTE_API_VERSION = "2.32"
def test_boot_server_with_tagged_devices(self):
def test_boot_server_with_tagged_block_devices(self):
server_info = self.nova('boot', params=(
'%(name)s --flavor %(flavor)s --poll '
'--nic net-id=%(net-uuid)s '
'--block-device '
'source=image,dest=volume,id=%(image)s,size=1,bootindex=0,'
'shutdown=remove,tag=bar' % {'name': uuidutils.generate_uuid(),
'flavor': self.flavor.id,
'net-uuid': self.network.id,
'image': self.image.id}))
server_id = self._get_value_from_the_table(server_info, 'id')
self.client.servers.delete(server_id)
self.wait_for_resource_delete(server_id, self.client.servers)
class TestNICDeviceTaggingCLI(base.ClientTestBase):
"""Tests that creating a server with a tagged nic will work
with the 2.32 microversion, where the feature was originally added.
"""
COMPUTE_API_VERSION = "2.32"
def test_boot_server_with_tagged_nic_devices(self):
server_info = self.nova('boot', params=(
'%(name)s --flavor %(flavor)s --poll '
'--nic net-id=%(net-uuid)s,tag=foo '
'--block-device '
'source=image,dest=volume,id=%(image)s,size=1,'
'bootindex=0,tag=bar' % {'name': uuidutils.generate_uuid(),
'flavor': self.flavor.id,
'net-uuid': self.network.id,
'image': self.image.id}))
'source=image,dest=volume,id=%(image)s,size=1,bootindex=0,'
'shutdown=remove' % {'name': uuidutils.generate_uuid(),
'flavor': self.flavor.id,
'net-uuid': self.network.id,
'image': self.image.id}))
server_id = self._get_value_from_the_table(server_info, 'id')
self.client.servers.delete(server_id)
self.wait_for_resource_delete(server_id, self.client.servers)
class TestDeviceTaggingCLIV233(TestBlockDeviceTaggingCLIError,
TestNICDeviceTaggingCLI):
"""Tests that in microversion 2.33, creating a server with a tagged
block device will fail, but creating a server with a tagged nic will
succeed.
"""
COMPUTE_API_VERSION = "2.33"
class TestDeviceTaggingCLIV236(TestBlockDeviceTaggingCLIError,
TestNICDeviceTaggingCLI):
"""Tests that in microversion 2.36, creating a server with a tagged
block device will fail, but creating a server with a tagged nic will
succeed. This is testing the boundary before 2.37 where nic tagging
was broken.
"""
COMPUTE_API_VERSION = "2.36"
class TestDeviceTaggingCLIV237(TestBlockDeviceTaggingCLIError,
TestNICDeviceTaggingCLIError):
"""Tests that in microversion 2.37, creating a server with either a
tagged block device or tagged nic would fail.
"""
COMPUTE_API_VERSION = "2.37"
class TestDeviceTaggingCLIV241(TestBlockDeviceTaggingCLIError,
TestNICDeviceTaggingCLIError):
"""Tests that in microversion 2.41, creating a server with either a
tagged block device or tagged nic would fail. This is testing the
boundary before 2.42 where block device tags and nic tags were fixed
for server create requests.
"""
COMPUTE_API_VERSION = "2.41"
class TestDeviceTaggingCLIV242(TestBlockDeviceTaggingCLI,
TestNICDeviceTaggingCLI):
"""Tests that in microversion 2.42 you could once again create a server
with a tagged block device or a tagged nic.
"""
COMPUTE_API_VERSION = "2.42"

View File

@ -2880,6 +2880,7 @@ class ShellTest(utils.TestCase):
38, # doesn't require any changes in novaclient
39, # There are no versioned wrapped shell method changes for this
41, # There are no version-wrapped shell method changes for this.
42, # There are no version-wrapped shell method changes for this.
])
versions_supported = set(range(0,
novaclient.API_MAX_VERSION.ver_minor + 1))

View File

@ -106,7 +106,15 @@ def _match_image(cs, wanted_properties):
return images_matched
def _parse_block_device_mapping_v2(args, image):
def _supports_block_device_tags(cs):
if (cs.api_version == api_versions.APIVersion('2.32') or
cs.api_version >= api_versions.APIVersion('2.42')):
return True
else:
return False
def _parse_block_device_mapping_v2(cs, args, image):
bdm = []
if args.boot_volume:
@ -125,6 +133,12 @@ def _parse_block_device_mapping_v2(args, image):
spec_dict = dict(v.split('=') for v in device_spec.split(','))
bdm_dict = {}
if ('tag' in spec_dict and not _supports_block_device_tags(cs)):
raise exceptions.CommandError(
_("'tag' in block device mapping is not supported "
"in API version %(version)s.")
% {'version': cs.api_version.get_string()})
for key, value in spec_dict.items():
bdm_dict[CLIENT_BDM2_KEYS[key]] = value
@ -193,9 +207,25 @@ def _parse_block_device_mapping_v2(args, image):
return bdm
def _supports_nic_tags(cs):
if ((cs.api_version >= api_versions.APIVersion('2.32') and
cs.api_version <= api_versions.APIVersion('2.36')) or
cs.api_version >= api_versions.APIVersion('2.42')):
return True
else:
return False
def _parse_nics(cs, args):
supports_auto_alloc = cs.api_version >= api_versions.APIVersion('2.37')
if supports_auto_alloc:
supports_nic_tags = _supports_nic_tags(cs)
nic_info = {"net-id": "", "v4-fixed-ip": "", "v6-fixed-ip": "",
"port-id": "", "net-name": ""}
if supports_auto_alloc and supports_nic_tags:
# API version >= 2.42
nic_info.update({"tag": ""})
err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
"the form --nic <auto,none,net-id=net-uuid,"
"net-name=network-name,v4-fixed-ip=ip-addr,"
@ -203,7 +233,18 @@ def _parse_nics(cs, args):
"with only one of net-id, net-name or port-id "
"specified. Specifying a --nic of auto or none cannot "
"be used with any other --nic value."))
elif cs.api_version >= api_versions.APIVersion('2.32'):
elif supports_auto_alloc and not supports_nic_tags:
# 2.41 >= API version >= 2.37
err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
"the form --nic <auto,none,net-id=net-uuid,"
"net-name=network-name,v4-fixed-ip=ip-addr,"
"v6-fixed-ip=ip-addr,port-id=port-uuid>, "
"with only one of net-id, net-name or port-id "
"specified. Specifying a --nic of auto or none cannot "
"be used with any other --nic value."))
elif not supports_auto_alloc and supports_nic_tags:
# 2.36 >= API version >= 2.32
nic_info.update({"tag": ""})
err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
"the form --nic <net-id=net-uuid,"
"net-name=network-name,v4-fixed-ip=ip-addr,"
@ -211,6 +252,7 @@ def _parse_nics(cs, args):
"with only one of net-id, net-name or port-id "
"specified."))
else:
# API version <= 2.31
err_msg = (_("Invalid nic argument '%s'. Nic arguments must be of "
"the form --nic <net-id=net-uuid,"
"net-name=network-name,v4-fixed-ip=ip-addr,"
@ -220,9 +262,6 @@ def _parse_nics(cs, args):
auto_or_none = False
nics = []
for nic_str in args.nics:
nic_info = {"net-id": "", "v4-fixed-ip": "", "v6-fixed-ip": "",
"port-id": "", "net-name": "", "tag": ""}
for kv_str in nic_str.split(","):
try:
# handle the special auto/none cases
@ -360,7 +399,7 @@ def _boot(cs, args):
device_name, mapping = bdm.split('=', 1)
block_device_mapping[device_name] = mapping
block_device_mapping_v2 = _parse_block_device_mapping_v2(args, image)
block_device_mapping_v2 = _parse_block_device_mapping_v2(cs, args, image)
n_boot_args = len(list(filter(
bool, (image, args.boot_volume, args.snapshot))))
@ -545,6 +584,7 @@ def _boot(cs, args):
action='append',
default=[],
start_version='2.32',
end_version='2.32',
help=_("Block device mapping with the keys: "
"id=UUID (image_id, snapshot_id or volume_id only if using source "
"image, snapshot or volume) "
@ -568,6 +608,64 @@ def _boot(cs, args):
"for others need to be specified) and "
"shutdown=shutdown behaviour (either preserve or remove, "
"for local destination set to remove)."))
@utils.arg(
'--block-device',
metavar="key1=value1[,key2=value2...]",
action='append',
default=[],
start_version='2.33',
end_version='2.41',
help=_("Block device mapping with the keys: "
"id=UUID (image_id, snapshot_id or volume_id only if using source "
"image, snapshot or volume) "
"source=source type (image, snapshot, volume or blank), "
"dest=destination type of the block device (volume or local), "
"bus=device's bus (e.g. uml, lxc, virtio, ...; if omitted, "
"hypervisor driver chooses a suitable default, "
"honoured only if device type is supplied) "
"type=device type (e.g. disk, cdrom, ...; defaults to 'disk') "
"device=name of the device (e.g. vda, xda, ...; "
"if omitted, hypervisor driver chooses suitable device "
"depending on selected bus; note the libvirt driver always "
"uses default device names), "
"size=size of the block device in MB(for swap) and in "
"GB(for other formats) "
"(if omitted, hypervisor driver calculates size), "
"format=device will be formatted (e.g. swap, ntfs, ...; optional), "
"bootindex=integer used for ordering the boot disks "
"(for image backed instances it is equal to 0, "
"for others need to be specified) and "
"shutdown=shutdown behaviour (either preserve or remove, "
"for local destination set to remove)."))
@utils.arg(
'--block-device',
metavar="key1=value1[,key2=value2...]",
action='append',
default=[],
start_version='2.42',
help=_("Block device mapping with the keys: "
"id=UUID (image_id, snapshot_id or volume_id only if using source "
"image, snapshot or volume) "
"source=source type (image, snapshot, volume or blank), "
"dest=destination type of the block device (volume or local), "
"bus=device's bus (e.g. uml, lxc, virtio, ...; if omitted, "
"hypervisor driver chooses a suitable default, "
"honoured only if device type is supplied) "
"type=device type (e.g. disk, cdrom, ...; defaults to 'disk') "
"device=name of the device (e.g. vda, xda, ...; "
"if omitted, hypervisor driver chooses suitable device "
"depending on selected bus; note the libvirt driver always "
"uses default device names), "
"size=size of the block device in MB(for swap) and in "
"GB(for other formats) "
"(if omitted, hypervisor driver calculates size), "
"format=device will be formatted (e.g. swap, ntfs, ...; optional), "
"bootindex=integer used for ordering the boot disks "
"(for image backed instances it is equal to 0, "
"for others need to be specified), "
"shutdown=shutdown behaviour (either preserve or remove, "
"for local destination set to remove) and "
"tag=device metadata tag (optional)."))
@utils.arg(
'--swap',
metavar="<swap_size>",
@ -609,7 +707,7 @@ def _boot(cs, args):
@utils.arg(
'--nic',
metavar="<net-id=net-uuid,net-name=network-name,v4-fixed-ip=ip-addr,"
"v6-fixed-ip=ip-addr,port-id=port-uuid>",
"v6-fixed-ip=ip-addr,port-id=port-uuid,tag=tag>",
action='append',
dest='nics',
default=[],
@ -629,11 +727,36 @@ def _boot(cs, args):
'--nic',
metavar="<auto,none,"
"net-id=net-uuid,net-name=network-name,port-id=port-uuid,"
"v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,tag=tag>",
"v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr>",
action='append',
dest='nics',
default=[],
start_version='2.37',
end_version='2.41',
help=_("Create a NIC on the server. "
"Specify option multiple times to create multiple nics unless "
"using the special 'auto' or 'none' values. "
"auto: automatically allocate network resources if none are "
"available. This cannot be specified with any other nic value and "
"cannot be specified multiple times. "
"none: do not attach a NIC at all. This cannot be specified "
"with any other nic value and cannot be specified multiple times. "
"net-id: attach NIC to network with a specific UUID. "
"net-name: attach NIC to network with this name "
"(either port-id or net-id or net-name must be provided), "
"v4-fixed-ip: IPv4 fixed address for NIC (optional), "
"v6-fixed-ip: IPv6 fixed address for NIC (optional), "
"port-id: attach NIC to port with this UUID "
"(either port-id or net-id must be provided)."))
@utils.arg(
'--nic',
metavar="<auto,none,"
"net-id=net-uuid,net-name=network-name,port-id=port-uuid,"
"v4-fixed-ip=ip-addr,v6-fixed-ip=ip-addr,tag=tag>",
action='append',
dest='nics',
default=[],
start_version='2.42',
help=_("Create a NIC on the server. "
"Specify option multiple times to create multiple nics unless "
"using the special 'auto' or 'none' values. "

View File

@ -0,0 +1,13 @@
---
fixes:
- |
Microversion 2.42 is related to the following bug.
* https://bugs.launchpad.net/nova/+bug/1658571
The following options have been changed as of Microversion 2.42.
* Remove ``tag`` attribute in ``--block-device`` option
on the server boot (nova boot) between microversion 2.33 and 2.41.
* Remove ``tag`` attribute in ``--nic`` option
on the server boot (nova boot) between microversion 2.37 and 2.41.