Virtual device tagging client support
In order to support virtual device role tagging that was introduced in microversion 2.32, this patch adds: * The 'tag' key to the --nic flag when booting an instance. * The 'tag' key to the --block-device flag when booting an instance. Change-Id: I1866c670994254bc2849b494e318f4ff8cc44eb7 Implements: blueprint virt-device-role-tagging
This commit is contained in:
parent
423239c975
commit
7fb0bd4f35
@ -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.31")
|
||||
API_MAX_VERSION = api_versions.APIVersion("2.32")
|
||||
|
36
novaclient/tests/functional/v2/test_device_tagging.py
Normal file
36
novaclient/tests/functional/v2/test_device_tagging.py
Normal file
@ -0,0 +1,36 @@
|
||||
# Copyright (C) 2016, Red Hat, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import uuid
|
||||
|
||||
from novaclient.tests.functional import base
|
||||
|
||||
|
||||
class TestDeviceTaggingCLI(base.ClientTestBase):
|
||||
|
||||
COMPUTE_API_VERSION = "2.32"
|
||||
|
||||
def test_boot_server_with_tagged_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': str(uuid.uuid4()),
|
||||
'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)
|
@ -1217,3 +1217,53 @@ class ServersV230Test(ServersV229Test):
|
||||
{'os-migrateLive': {'host': 'hostname',
|
||||
'block_migration': 'auto',
|
||||
'force': True}})
|
||||
|
||||
|
||||
class ServersV232Test(ServersV226Test):
|
||||
def setUp(self):
|
||||
super(ServersV232Test, self).setUp()
|
||||
self.cs.api_version = api_versions.APIVersion("2.32")
|
||||
|
||||
def test_create_server_boot_with_tagged_nics(self):
|
||||
nics = [{'net-id': '11111111-1111-1111-1111-111111111111',
|
||||
'tag': 'one'},
|
||||
{'net-id': '22222222-2222-2222-2222-222222222222',
|
||||
'tag': 'two'}]
|
||||
self.cs.servers.create(name="Server with tagged nics",
|
||||
image=1,
|
||||
flavor=1,
|
||||
nics=nics)
|
||||
self.assert_called('POST', '/servers')
|
||||
|
||||
def test_create_server_boot_with_tagged_nics_pre232(self):
|
||||
self.cs.api_version = api_versions.APIVersion("2.31")
|
||||
nics = [{'net-id': '11111111-1111-1111-1111-111111111111',
|
||||
'tag': 'one'},
|
||||
{'net-id': '22222222-2222-2222-2222-222222222222',
|
||||
'tag': 'two'}]
|
||||
self.assertRaises(ValueError, self.cs.servers.create,
|
||||
name="Server with tagged nics", image=1, flavor=1,
|
||||
nics=nics)
|
||||
|
||||
def test_create_server_boot_from_volume_tagged_bdm_v2(self):
|
||||
bdm = [{"volume_size": "1",
|
||||
"volume_id": "11111111-1111-1111-1111-111111111111",
|
||||
"delete_on_termination": "0",
|
||||
"device_name": "vda", "tag": "foo"}]
|
||||
s = self.cs.servers.create(name="My server", image=1, flavor=1,
|
||||
meta={'foo': 'bar'}, userdata="hello moto",
|
||||
key_name="fakekey",
|
||||
block_device_mapping_v2=bdm)
|
||||
self.assert_request_id(s, fakes.FAKE_REQUEST_ID_LIST)
|
||||
self.assert_called('POST', '/os-volumes_boot')
|
||||
|
||||
def test_create_server_boot_from_volume_tagged_bdm_v2_pre232(self):
|
||||
self.cs.api_version = api_versions.APIVersion("2.31")
|
||||
bdm = [{"volume_size": "1",
|
||||
"volume_id": "11111111-1111-1111-1111-111111111111",
|
||||
"delete_on_termination": "0",
|
||||
"device_name": "vda", "tag": "foo"}]
|
||||
self.assertRaises(ValueError, self.cs.servers.create, name="My server",
|
||||
image=1, flavor=1, meta={'foo': 'bar'},
|
||||
userdata="hello moto", key_name="fakekey",
|
||||
block_device_mapping_v2=bdm)
|
||||
|
@ -343,6 +343,44 @@ class ShellTest(utils.TestCase):
|
||||
}},
|
||||
)
|
||||
|
||||
def test_boot_image_bdms_v2_with_tag(self):
|
||||
self.run_command(
|
||||
'boot --flavor 1 --image 1 --block-device id=fake-id,'
|
||||
'source=volume,dest=volume,device=vda,size=1,format=ext4,'
|
||||
'type=disk,shutdown=preserve,tag=foo some-server',
|
||||
api_version='2.32'
|
||||
)
|
||||
self.assert_called_anytime(
|
||||
'POST', '/os-volumes_boot',
|
||||
{'server': {
|
||||
'flavorRef': '1',
|
||||
'name': 'some-server',
|
||||
'block_device_mapping_v2': [
|
||||
{
|
||||
'uuid': 1,
|
||||
'source_type': 'image',
|
||||
'destination_type': 'local',
|
||||
'boot_index': 0,
|
||||
'delete_on_termination': True,
|
||||
},
|
||||
{
|
||||
'uuid': 'fake-id',
|
||||
'source_type': 'volume',
|
||||
'destination_type': 'volume',
|
||||
'device_name': 'vda',
|
||||
'volume_size': '1',
|
||||
'guest_format': 'ext4',
|
||||
'device_type': 'disk',
|
||||
'delete_on_termination': False,
|
||||
'tag': 'foo',
|
||||
},
|
||||
],
|
||||
'imageRef': '1',
|
||||
'min_count': 1,
|
||||
'max_count': 1,
|
||||
}},
|
||||
)
|
||||
|
||||
def test_boot_no_image_bdms_v2(self):
|
||||
self.run_command(
|
||||
'boot --flavor 1 --block-device id=fake-id,source=volume,'
|
||||
@ -523,6 +561,26 @@ class ShellTest(utils.TestCase):
|
||||
},
|
||||
)
|
||||
|
||||
def test_boot_nics_with_tag(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic net-id=a=c,v4-fixed-ip=10.0.0.1,tag=foo some-server')
|
||||
self.run_command(cmd, api_version='2.32')
|
||||
self.assert_called_anytime(
|
||||
'POST', '/servers',
|
||||
{
|
||||
'server': {
|
||||
'flavorRef': '1',
|
||||
'name': 'some-server',
|
||||
'imageRef': '1',
|
||||
'min_count': 1,
|
||||
'max_count': 1,
|
||||
'networks': [
|
||||
{'uuid': 'a=c', 'fixed_ip': '10.0.0.1', 'tag': 'foo'},
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
def test_boot_nics_ipv6(self):
|
||||
cmd = ('boot --image 1 --flavor 1 '
|
||||
'--nic net-id=a=c,v6-fixed-ip=2001:db9:0:1::10 some-server')
|
||||
@ -2864,6 +2922,8 @@ class ShellTest(utils.TestCase):
|
||||
# not explicitly tested via wraps and _SUBSTITUTIONS.
|
||||
28, # doesn't require any changes in novaclient
|
||||
31, # doesn't require any changes in novaclient
|
||||
32, # doesn't require separate version-wrapped methods in
|
||||
# novaclient
|
||||
])
|
||||
versions_supported = set(range(0,
|
||||
novaclient.API_MAX_VERSION.ver_minor + 1))
|
||||
|
@ -727,6 +727,8 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
net_data['fixed_ip'] = nic_info['v6-fixed-ip']
|
||||
if nic_info.get('port-id'):
|
||||
net_data['port'] = nic_info['port-id']
|
||||
if nic_info.get('tag'):
|
||||
net_data['tag'] = nic_info['tag']
|
||||
all_net_data.append(net_data)
|
||||
body['server']['networks'] = all_net_data
|
||||
|
||||
@ -1287,6 +1289,22 @@ class ServerManager(base.BootingManagerWithFind):
|
||||
if "description" in kwargs and self.api_version < descr_microversion:
|
||||
raise exceptions.UnsupportedAttribute("description", "2.19")
|
||||
|
||||
tags_microversion = api_versions.APIVersion("2.32")
|
||||
if self.api_version < tags_microversion:
|
||||
if nics:
|
||||
for nic_info in nics:
|
||||
if nic_info.get("tag"):
|
||||
raise ValueError("Setting interface tags is "
|
||||
"unsupported before microversion "
|
||||
"2.32")
|
||||
|
||||
if block_device_mapping_v2:
|
||||
for bdm in block_device_mapping_v2:
|
||||
if bdm.get("tag"):
|
||||
raise ValueError("Setting block device tags is "
|
||||
"unsupported before microversion "
|
||||
"2.32")
|
||||
|
||||
boot_kwargs = dict(
|
||||
meta=meta, files=files, userdata=userdata,
|
||||
reservation_id=reservation_id, min_count=min_count,
|
||||
|
@ -63,6 +63,7 @@ CLIENT_BDM2_KEYS = {
|
||||
'bootindex': 'boot_index',
|
||||
'type': 'device_type',
|
||||
'shutdown': 'delete_on_termination',
|
||||
'tag': 'tag',
|
||||
}
|
||||
|
||||
|
||||
@ -269,10 +270,10 @@ def _boot(cs, args):
|
||||
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,v6-fixed-ip=ip-addr,"
|
||||
"port-id=port-uuid>, with only one of net-id, net-name "
|
||||
"or port-id specified.") % nic_str)
|
||||
"port-id=port-uuid,tag=tag>, with only one of net-id, "
|
||||
"net-name or port-id specified.") % nic_str)
|
||||
nic_info = {"net-id": "", "v4-fixed-ip": "", "v6-fixed-ip": "",
|
||||
"port-id": "", "net-name": ""}
|
||||
"port-id": "", "net-name": "", "tag": ""}
|
||||
|
||||
for kv_str in nic_str.split(","):
|
||||
try:
|
||||
@ -438,6 +439,8 @@ def _boot(cs, args):
|
||||
metavar="key1=value1[,key2=value2...]",
|
||||
action='append',
|
||||
default=[],
|
||||
start_version='2.0',
|
||||
end_version='2.31',
|
||||
help=_("Block device mapping with the keys: "
|
||||
"id=UUID (image_id, snapshot_id or volume_id only if using source "
|
||||
"image, snapshot or volume) "
|
||||
@ -460,6 +463,35 @@ 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.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) "
|
||||
"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, ...; "
|
||||
"tag=device metadata tag (optional) "
|
||||
"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(
|
||||
'--swap',
|
||||
metavar="<swap_size>",
|
||||
@ -487,6 +519,8 @@ def _boot(cs, args):
|
||||
action='append',
|
||||
dest='nics',
|
||||
default=[],
|
||||
start_version='2.0',
|
||||
end_version='2.31',
|
||||
help=_("Create a NIC on the server. "
|
||||
"Specify option multiple times to create multiple NICs. "
|
||||
"net-id: attach NIC to network with this UUID "
|
||||
@ -496,6 +530,24 @@ def _boot(cs, args):
|
||||
"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="<net-id=net-uuid,net-name=network-name,v4-fixed-ip=ip-addr,"
|
||||
"v6-fixed-ip=ip-addr,port-id=port-uuid>",
|
||||
action='append',
|
||||
dest='nics',
|
||||
default=[],
|
||||
start_version='2.32',
|
||||
help=_("Create a NIC on the server. "
|
||||
"Specify option multiple times to create multiple nics. "
|
||||
"net-id: attach NIC to network with this 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 "
|
||||
"tag: interface metadata tag (optional) "
|
||||
"(either port-id or net-id must be provided)."))
|
||||
@utils.arg(
|
||||
'--config-drive',
|
||||
metavar="<value>",
|
||||
|
25
releasenotes/notes/microversion-v2_32-7947430cc2415597.yaml
Normal file
25
releasenotes/notes/microversion-v2_32-7947430cc2415597.yaml
Normal file
@ -0,0 +1,25 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
The 2.32 microverison adds support for virtual device
|
||||
role tagging. Device role tagging is an answer to the
|
||||
question 'Which device is which?' from inside the guest.
|
||||
When booting an instance, an optional arbitrary 'tag'
|
||||
parameter can be set on virtual network interfaces
|
||||
and/or block device mappings. This tag is exposed to the
|
||||
instance through the metadata API and on the config
|
||||
drive. Each tagged virtual network interface is listed
|
||||
along with information about the virtual hardware, such
|
||||
as bus type (ex: PCI), bus address (ex: 0000:00:02.0),
|
||||
and MAC address. For tagged block devices, the exposed
|
||||
hardware metadata includes the bus (ex: SCSI), bus
|
||||
address (ex: 1:0:2:0) and serial number.
|
||||
|
||||
In the client, device tagging is exposed with the 'tag'
|
||||
key in the --block-device and --nic boot arguments.
|
||||
issues:
|
||||
- |
|
||||
While not an issue with the client itself, it should be
|
||||
noted that if a tagged network interface or volume is
|
||||
detached from a guest, its metadata will continue to
|
||||
appear in the config drive, even after instance reboot.
|
Loading…
Reference in New Issue
Block a user