Merge "compute: Allow users to manually specify bootable volumes"
This commit is contained in:
commit
8248efa8d9
@ -913,9 +913,7 @@ class CreateServer(command.ShowOne):
|
|||||||
required=True,
|
required=True,
|
||||||
help=_('Create server with this flavor (name or ID)'),
|
help=_('Create server with this flavor (name or ID)'),
|
||||||
)
|
)
|
||||||
disk_group = parser.add_mutually_exclusive_group(
|
disk_group = parser.add_mutually_exclusive_group()
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
disk_group.add_argument(
|
disk_group.add_argument(
|
||||||
'--image',
|
'--image',
|
||||||
metavar='<image>',
|
metavar='<image>',
|
||||||
@ -1473,14 +1471,14 @@ class CreateServer(command.ShowOne):
|
|||||||
if volume:
|
if volume:
|
||||||
block_device_mapping_v2 = [{
|
block_device_mapping_v2 = [{
|
||||||
'uuid': volume,
|
'uuid': volume,
|
||||||
'boot_index': '0',
|
'boot_index': 0,
|
||||||
'source_type': 'volume',
|
'source_type': 'volume',
|
||||||
'destination_type': 'volume'
|
'destination_type': 'volume'
|
||||||
}]
|
}]
|
||||||
elif snapshot:
|
elif snapshot:
|
||||||
block_device_mapping_v2 = [{
|
block_device_mapping_v2 = [{
|
||||||
'uuid': snapshot,
|
'uuid': snapshot,
|
||||||
'boot_index': '0',
|
'boot_index': 0,
|
||||||
'source_type': 'snapshot',
|
'source_type': 'snapshot',
|
||||||
'destination_type': 'volume',
|
'destination_type': 'volume',
|
||||||
'delete_on_termination': False
|
'delete_on_termination': False
|
||||||
@ -1489,7 +1487,7 @@ class CreateServer(command.ShowOne):
|
|||||||
# Tell nova to create a root volume from the image provided.
|
# Tell nova to create a root volume from the image provided.
|
||||||
block_device_mapping_v2 = [{
|
block_device_mapping_v2 = [{
|
||||||
'uuid': image.id,
|
'uuid': image.id,
|
||||||
'boot_index': '0',
|
'boot_index': 0,
|
||||||
'source_type': 'image',
|
'source_type': 'image',
|
||||||
'destination_type': 'volume',
|
'destination_type': 'volume',
|
||||||
'volume_size': parsed_args.boot_from_volume
|
'volume_size': parsed_args.boot_from_volume
|
||||||
@ -1626,6 +1624,15 @@ class CreateServer(command.ShowOne):
|
|||||||
|
|
||||||
block_device_mapping_v2.append(mapping)
|
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
|
nics = parsed_args.nics
|
||||||
|
|
||||||
if 'auto' in nics or 'none' in nics:
|
if 'auto' in nics or 'none' in nics:
|
||||||
|
@ -2470,7 +2470,7 @@ class TestServerCreate(TestServer):
|
|||||||
'admin_pass': None,
|
'admin_pass': None,
|
||||||
'block_device_mapping_v2': [{
|
'block_device_mapping_v2': [{
|
||||||
'uuid': self.volume.id,
|
'uuid': self.volume.id,
|
||||||
'boot_index': '0',
|
'boot_index': 0,
|
||||||
'source_type': 'volume',
|
'source_type': 'volume',
|
||||||
'destination_type': 'volume',
|
'destination_type': 'volume',
|
||||||
}],
|
}],
|
||||||
@ -2521,7 +2521,7 @@ class TestServerCreate(TestServer):
|
|||||||
'admin_pass': None,
|
'admin_pass': None,
|
||||||
'block_device_mapping_v2': [{
|
'block_device_mapping_v2': [{
|
||||||
'uuid': self.snapshot.id,
|
'uuid': self.snapshot.id,
|
||||||
'boot_index': '0',
|
'boot_index': 0,
|
||||||
'source_type': 'snapshot',
|
'source_type': 'snapshot',
|
||||||
'destination_type': 'volume',
|
'destination_type': 'volume',
|
||||||
'delete_on_termination': False,
|
'delete_on_termination': False,
|
||||||
@ -2544,20 +2544,20 @@ class TestServerCreate(TestServer):
|
|||||||
self.assertEqual(self.datalist(), data)
|
self.assertEqual(self.datalist(), data)
|
||||||
|
|
||||||
def test_server_create_with_block_device(self):
|
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 = [
|
arglist = [
|
||||||
'--image', 'image1',
|
|
||||||
'--flavor', self.flavor.id,
|
'--flavor', self.flavor.id,
|
||||||
'--block-device', block_device,
|
'--block-device', block_device,
|
||||||
self.new_server.name,
|
self.new_server.name,
|
||||||
]
|
]
|
||||||
verifylist = [
|
verifylist = [
|
||||||
('image', 'image1'),
|
('image', None),
|
||||||
('flavor', self.flavor.id),
|
('flavor', self.flavor.id),
|
||||||
('block_devices', [
|
('block_devices', [
|
||||||
{
|
{
|
||||||
'uuid': self.volume.id,
|
'uuid': self.volume.id,
|
||||||
'source_type': 'volume',
|
'source_type': 'volume',
|
||||||
|
'boot_index': '0',
|
||||||
},
|
},
|
||||||
]),
|
]),
|
||||||
('server_name', self.new_server.name),
|
('server_name', self.new_server.name),
|
||||||
@ -2584,6 +2584,7 @@ class TestServerCreate(TestServer):
|
|||||||
'uuid': self.volume.id,
|
'uuid': self.volume.id,
|
||||||
'source_type': 'volume',
|
'source_type': 'volume',
|
||||||
'destination_type': 'volume',
|
'destination_type': 'volume',
|
||||||
|
'boot_index': 0,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'nics': [],
|
'nics': [],
|
||||||
@ -2593,7 +2594,7 @@ class TestServerCreate(TestServer):
|
|||||||
# ServerManager.create(name, image, flavor, **kwargs)
|
# ServerManager.create(name, image, flavor, **kwargs)
|
||||||
self.servers_mock.create.assert_called_with(
|
self.servers_mock.create.assert_called_with(
|
||||||
self.new_server.name,
|
self.new_server.name,
|
||||||
self.image,
|
None,
|
||||||
self.flavor,
|
self.flavor,
|
||||||
**kwargs
|
**kwargs
|
||||||
)
|
)
|
||||||
@ -3521,6 +3522,37 @@ class TestServerCreate(TestServer):
|
|||||||
self.assertEqual(self.columns, columns)
|
self.assertEqual(self.columns, columns)
|
||||||
self.assertEqual(self.datalist(), data)
|
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):
|
def test_server_create_with_swap(self):
|
||||||
arglist = [
|
arglist = [
|
||||||
'--image', 'image1',
|
'--image', 'image1',
|
||||||
|
7
releasenotes/notes/bug-2010376-e15362bdd6c8d6ec.yaml
Normal file
7
releasenotes/notes/bug-2010376-e15362bdd6c8d6ec.yaml
Normal file
@ -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.
|
Loading…
x
Reference in New Issue
Block a user