Skip to content

Commit

Permalink
compute: Allow users to manually specify bootable volumes
Browse files Browse the repository at this point in the history
When creating a server with an attached volume, you can specify a block
device with a 'boot_index' of '0' and this will become the bootable
device. OSC allows users to do this by using either the '--volume'
option or a combination of the '--image' and '--boot-from-volume'
options, but we should also allow them to do it the "hard way" via the
'--block-device' option. For example:

  openstack server create \
    --block-device uuid=0a89ecd8-1fe2-45f0-94da-7789067911c9,boot_index=0 \
    --block-device uuid=589266ef-fd88-46e9-b7b2-94503ce8f88f,boot_index=1 \
    ... \
    my-server

Make this possible.

Change-Id: Ia48449fecbc590346630807b1c7da40102d53b33
Signed-off-by: Stephen Finucane <sfinucan@redhat.com>
Story: 2010376
Task: 46617
  • Loading branch information
stephenfin committed Dec 1, 2022
1 parent e7bc687 commit 91277e7
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 12 deletions.
19 changes: 13 additions & 6 deletions openstackclient/compute/v2/server.py
Expand Up @@ -891,9 +891,7 @@ def get_parser(self, prog_name):
required=True,
help=_('Create server with this flavor (name or ID)'),
)
disk_group = parser.add_mutually_exclusive_group(
required=True,
)
disk_group = parser.add_mutually_exclusive_group()
disk_group.add_argument(
'--image',
metavar='<image>',
Expand Down Expand Up @@ -1451,14 +1449,14 @@ def _match_image(image_api, wanted_properties):
if volume:
block_device_mapping_v2 = [{
'uuid': volume,
'boot_index': '0',
'boot_index': 0,
'source_type': 'volume',
'destination_type': 'volume'
}]
elif snapshot:
block_device_mapping_v2 = [{
'uuid': snapshot,
'boot_index': '0',
'boot_index': 0,
'source_type': 'snapshot',
'destination_type': 'volume',
'delete_on_termination': False
Expand All @@ -1467,7 +1465,7 @@ def _match_image(image_api, wanted_properties):
# Tell nova to create a root volume from the image provided.
block_device_mapping_v2 = [{
'uuid': image.id,
'boot_index': '0',
'boot_index': 0,
'source_type': 'image',
'destination_type': 'volume',
'volume_size': parsed_args.boot_from_volume
Expand Down Expand Up @@ -1604,6 +1602,15 @@ def _match_image(image_api, wanted_properties):

block_device_mapping_v2.append(mapping)

if not image and not any(
[bdm.get('boot_index') == 0 for bdm in block_device_mapping_v2]
):
msg = _(
'An image (--image, --image-property) or bootable volume '
'(--volume, --snapshot, --block-device) is required'
)
raise exceptions.CommandError(msg)

nics = parsed_args.nics

if 'auto' in nics or 'none' in nics:
Expand Down
44 changes: 38 additions & 6 deletions openstackclient/tests/unit/compute/v2/test_server.py
Expand Up @@ -2455,7 +2455,7 @@ def test_server_create_with_volume(self):
'admin_pass': None,
'block_device_mapping_v2': [{
'uuid': self.volume.id,
'boot_index': '0',
'boot_index': 0,
'source_type': 'volume',
'destination_type': 'volume',
}],
Expand Down Expand Up @@ -2506,7 +2506,7 @@ def test_server_create_with_snapshot(self):
'admin_pass': None,
'block_device_mapping_v2': [{
'uuid': self.snapshot.id,
'boot_index': '0',
'boot_index': 0,
'source_type': 'snapshot',
'destination_type': 'volume',
'delete_on_termination': False,
Expand All @@ -2529,20 +2529,20 @@ def test_server_create_with_snapshot(self):
self.assertEqual(self.datalist(), data)

def test_server_create_with_block_device(self):
block_device = f'uuid={self.volume.id},source_type=volume'
block_device = f'uuid={self.volume.id},source_type=volume,boot_index=0'
arglist = [
'--image', 'image1',
'--flavor', self.flavor.id,
'--block-device', block_device,
self.new_server.name,
]
verifylist = [
('image', 'image1'),
('image', None),
('flavor', self.flavor.id),
('block_devices', [
{
'uuid': self.volume.id,
'source_type': 'volume',
'boot_index': '0',
},
]),
('server_name', self.new_server.name),
Expand All @@ -2569,6 +2569,7 @@ def test_server_create_with_block_device(self):
'uuid': self.volume.id,
'source_type': 'volume',
'destination_type': 'volume',
'boot_index': 0,
},
],
'nics': [],
Expand All @@ -2578,7 +2579,7 @@ def test_server_create_with_block_device(self):
# ServerManager.create(name, image, flavor, **kwargs)
self.servers_mock.create.assert_called_with(
self.new_server.name,
self.image,
None,
self.flavor,
**kwargs
)
Expand Down Expand Up @@ -3506,6 +3507,37 @@ def test_server_create_image_property_with_image_list(self):
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist(), data)

def test_server_create_no_boot_device(self):
block_device = f'uuid={self.volume.id},source_type=volume,boot_index=1'
arglist = [
'--block-device', block_device,
'--flavor', self.flavor.id,
self.new_server.name,
]
verifylist = [
('image', None),
('flavor', self.flavor.id),
('block_devices', [
{
'uuid': self.volume.id,
'source_type': 'volume',
'boot_index': '1',
},
]),
('server_name', self.new_server.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
exc = self.assertRaises(
exceptions.CommandError,
self.cmd.take_action,
parsed_args,
)
self.assertIn(
'An image (--image, --image-property) or bootable volume '
'(--volume, --snapshot, --block-device) is required',
str(exc),
)

def test_server_create_with_swap(self):
arglist = [
'--image', 'image1',
Expand Down
7 changes: 7 additions & 0 deletions releasenotes/notes/bug-2010376-e15362bdd6c8d6ec.yaml
@@ -0,0 +1,7 @@
---
fixes:
- |
The ``server create`` command will no longer insist on an ``--image``,
``--image-property``, ``--volume`` or ``--snapshot`` argument when a
volume is provided with a boot index of ``0`` via the ``--block-device``
option.

0 comments on commit 91277e7

Please sign in to comment.