Add some options to "volume create" command
Add "--bootable", "--non-bootable", "--read-only" and "--read-write" options to "volume create" command for setting some attributes at the time of crration. Change-Id: I71b4e9fccb4ee0ab1a90e7179d6d2d34dbbae909 Implements: bp cinder-command-support
This commit is contained in:
parent
29587eaa66
commit
7e5a98bca9
@ -24,6 +24,8 @@ Create new volume
|
||||
[--property <key=value> [...] ]
|
||||
[--hint <key=value> [...] ]
|
||||
[--multi-attach]
|
||||
[--bootable | --non-bootable]
|
||||
[--read-only | --read-write]
|
||||
<name>
|
||||
|
||||
.. option:: --size <size>
|
||||
@ -89,6 +91,22 @@ Create new volume
|
||||
|
||||
Allow volume to be attached more than once (default to False)
|
||||
|
||||
.. option:: --bootable
|
||||
|
||||
Mark volume as bootable
|
||||
|
||||
.. option:: --non-bootable
|
||||
|
||||
Mark volume as non-bootable (default)
|
||||
|
||||
.. option:: --read-only
|
||||
|
||||
Set volume to read-only access mode
|
||||
|
||||
.. option:: --read-write
|
||||
|
||||
Set volume to read-write access mode (default)
|
||||
|
||||
.. _volume_create-name:
|
||||
.. describe:: <name>
|
||||
|
||||
|
@ -431,6 +431,142 @@ class TestVolumeCreate(TestVolume):
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.datalist, data)
|
||||
|
||||
def test_volume_create_with_bootable_and_readonly(self):
|
||||
arglist = [
|
||||
'--bootable',
|
||||
'--read-only',
|
||||
'--size', str(self.new_volume.size),
|
||||
self.new_volume.display_name,
|
||||
]
|
||||
verifylist = [
|
||||
('bootable', True),
|
||||
('non_bootable', False),
|
||||
('read_only', True),
|
||||
('read_write', False),
|
||||
('size', self.new_volume.size),
|
||||
('name', self.new_volume.display_name),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.volumes_mock.create.assert_called_with(
|
||||
self.new_volume.size,
|
||||
None,
|
||||
None,
|
||||
self.new_volume.display_name,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.datalist, data)
|
||||
self.volumes_mock.set_bootable.assert_called_with(
|
||||
self.new_volume.id, True)
|
||||
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||
self.new_volume.id, True)
|
||||
|
||||
def test_volume_create_with_nonbootable_and_readwrite(self):
|
||||
arglist = [
|
||||
'--non-bootable',
|
||||
'--read-write',
|
||||
'--size', str(self.new_volume.size),
|
||||
self.new_volume.display_name,
|
||||
]
|
||||
verifylist = [
|
||||
('bootable', False),
|
||||
('non_bootable', True),
|
||||
('read_only', False),
|
||||
('read_write', True),
|
||||
('size', self.new_volume.size),
|
||||
('name', self.new_volume.display_name),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.volumes_mock.create.assert_called_with(
|
||||
self.new_volume.size,
|
||||
None,
|
||||
None,
|
||||
self.new_volume.display_name,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.datalist, data)
|
||||
self.volumes_mock.set_bootable.assert_called_with(
|
||||
self.new_volume.id, False)
|
||||
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||
self.new_volume.id, False)
|
||||
|
||||
@mock.patch.object(volume.LOG, 'error')
|
||||
def test_volume_create_with_bootable_and_readonly_fail(
|
||||
self, mock_error):
|
||||
|
||||
self.volumes_mock.set_bootable.side_effect = (
|
||||
exceptions.CommandError())
|
||||
|
||||
self.volumes_mock.update_readonly_flag.side_effect = (
|
||||
exceptions.CommandError())
|
||||
|
||||
arglist = [
|
||||
'--bootable',
|
||||
'--read-only',
|
||||
'--size', str(self.new_volume.size),
|
||||
self.new_volume.display_name,
|
||||
]
|
||||
verifylist = [
|
||||
('bootable', True),
|
||||
('non_bootable', False),
|
||||
('read_only', True),
|
||||
('read_write', False),
|
||||
('size', self.new_volume.size),
|
||||
('name', self.new_volume.display_name),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.volumes_mock.create.assert_called_with(
|
||||
self.new_volume.size,
|
||||
None,
|
||||
None,
|
||||
self.new_volume.display_name,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
)
|
||||
|
||||
self.assertEqual(2, mock_error.call_count)
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.datalist, data)
|
||||
self.volumes_mock.set_bootable.assert_called_with(
|
||||
self.new_volume.id, True)
|
||||
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||
self.new_volume.id, True)
|
||||
|
||||
def test_volume_create_without_size(self):
|
||||
arglist = [
|
||||
self.new_volume.display_name,
|
||||
|
@ -450,6 +450,154 @@ class TestVolumeCreate(TestVolume):
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.datalist, data)
|
||||
|
||||
def test_volume_create_with_bootable_and_readonly(self):
|
||||
arglist = [
|
||||
'--bootable',
|
||||
'--read-only',
|
||||
'--size', str(self.new_volume.size),
|
||||
self.new_volume.name,
|
||||
]
|
||||
verifylist = [
|
||||
('bootable', True),
|
||||
('non_bootable', False),
|
||||
('read_only', True),
|
||||
('read_write', False),
|
||||
('size', self.new_volume.size),
|
||||
('name', self.new_volume.name),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.volumes_mock.create.assert_called_with(
|
||||
size=self.new_volume.size,
|
||||
snapshot_id=None,
|
||||
name=self.new_volume.name,
|
||||
description=None,
|
||||
volume_type=None,
|
||||
user_id=None,
|
||||
project_id=None,
|
||||
availability_zone=None,
|
||||
metadata=None,
|
||||
imageRef=None,
|
||||
source_volid=None,
|
||||
consistencygroup_id=None,
|
||||
source_replica=None,
|
||||
multiattach=False,
|
||||
scheduler_hints=None,
|
||||
)
|
||||
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.datalist, data)
|
||||
self.volumes_mock.set_bootable.assert_called_with(
|
||||
self.new_volume.id, True)
|
||||
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||
self.new_volume.id, True)
|
||||
|
||||
def test_volume_create_with_nonbootable_and_readwrite(self):
|
||||
arglist = [
|
||||
'--non-bootable',
|
||||
'--read-write',
|
||||
'--size', str(self.new_volume.size),
|
||||
self.new_volume.name,
|
||||
]
|
||||
verifylist = [
|
||||
('bootable', False),
|
||||
('non_bootable', True),
|
||||
('read_only', False),
|
||||
('read_write', True),
|
||||
('size', self.new_volume.size),
|
||||
('name', self.new_volume.name),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.volumes_mock.create.assert_called_with(
|
||||
size=self.new_volume.size,
|
||||
snapshot_id=None,
|
||||
name=self.new_volume.name,
|
||||
description=None,
|
||||
volume_type=None,
|
||||
user_id=None,
|
||||
project_id=None,
|
||||
availability_zone=None,
|
||||
metadata=None,
|
||||
imageRef=None,
|
||||
source_volid=None,
|
||||
consistencygroup_id=None,
|
||||
source_replica=None,
|
||||
multiattach=False,
|
||||
scheduler_hints=None,
|
||||
)
|
||||
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.datalist, data)
|
||||
self.volumes_mock.set_bootable.assert_called_with(
|
||||
self.new_volume.id, False)
|
||||
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||
self.new_volume.id, False)
|
||||
|
||||
@mock.patch.object(volume.LOG, 'error')
|
||||
def test_volume_create_with_bootable_and_readonly_fail(
|
||||
self, mock_error):
|
||||
|
||||
self.volumes_mock.set_bootable.side_effect = (
|
||||
exceptions.CommandError())
|
||||
|
||||
self.volumes_mock.update_readonly_flag.side_effect = (
|
||||
exceptions.CommandError())
|
||||
|
||||
arglist = [
|
||||
'--bootable',
|
||||
'--read-only',
|
||||
'--size', str(self.new_volume.size),
|
||||
self.new_volume.name,
|
||||
]
|
||||
verifylist = [
|
||||
('bootable', True),
|
||||
('non_bootable', False),
|
||||
('read_only', True),
|
||||
('read_write', False),
|
||||
('size', self.new_volume.size),
|
||||
('name', self.new_volume.name),
|
||||
]
|
||||
|
||||
parsed_args = self.check_parser(
|
||||
self.cmd, arglist, verifylist)
|
||||
|
||||
columns, data = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.volumes_mock.create.assert_called_with(
|
||||
size=self.new_volume.size,
|
||||
snapshot_id=None,
|
||||
name=self.new_volume.name,
|
||||
description=None,
|
||||
volume_type=None,
|
||||
user_id=None,
|
||||
project_id=None,
|
||||
availability_zone=None,
|
||||
metadata=None,
|
||||
imageRef=None,
|
||||
source_volid=None,
|
||||
consistencygroup_id=None,
|
||||
source_replica=None,
|
||||
multiattach=False,
|
||||
scheduler_hints=None,
|
||||
)
|
||||
|
||||
self.assertEqual(2, mock_error.call_count)
|
||||
self.assertEqual(self.columns, columns)
|
||||
self.assertEqual(self.datalist, data)
|
||||
self.volumes_mock.set_bootable.assert_called_with(
|
||||
self.new_volume.id, True)
|
||||
self.volumes_mock.update_readonly_flag.assert_called_with(
|
||||
self.new_volume.id, True)
|
||||
|
||||
def test_volume_create_with_source_replicated(self):
|
||||
self.volumes_mock.get.return_value = self.new_volume
|
||||
arglist = [
|
||||
|
@ -114,6 +114,28 @@ class CreateVolume(command.ShowOne):
|
||||
help=_('Set a property on this volume '
|
||||
'(repeat option to set multiple properties)'),
|
||||
)
|
||||
bootable_group = parser.add_mutually_exclusive_group()
|
||||
bootable_group.add_argument(
|
||||
"--bootable",
|
||||
action="store_true",
|
||||
help=_("Mark volume as bootable")
|
||||
)
|
||||
bootable_group.add_argument(
|
||||
"--non-bootable",
|
||||
action="store_true",
|
||||
help=_("Mark volume as non-bootable (default)")
|
||||
)
|
||||
readonly_group = parser.add_mutually_exclusive_group()
|
||||
readonly_group.add_argument(
|
||||
"--read-only",
|
||||
action="store_true",
|
||||
help=_("Set volume to read-only access mode")
|
||||
)
|
||||
readonly_group.add_argument(
|
||||
"--read-write",
|
||||
action="store_true",
|
||||
help=_("Set volume to read-write access mode (default)")
|
||||
)
|
||||
|
||||
return parser
|
||||
|
||||
@ -166,6 +188,22 @@ class CreateVolume(command.ShowOne):
|
||||
parsed_args.property,
|
||||
image,
|
||||
)
|
||||
|
||||
if parsed_args.bootable or parsed_args.non_bootable:
|
||||
try:
|
||||
volume_client.volumes.set_bootable(
|
||||
volume.id, parsed_args.bootable)
|
||||
except Exception as e:
|
||||
LOG.error(_("Failed to set volume bootable property: %s"), e)
|
||||
if parsed_args.read_only or parsed_args.read_write:
|
||||
try:
|
||||
volume_client.volumes.update_readonly_flag(
|
||||
volume.id,
|
||||
parsed_args.read_only)
|
||||
except Exception as e:
|
||||
LOG.error(_("Failed to set volume read-only access "
|
||||
"mode flag: %s"), e)
|
||||
|
||||
# Map 'metadata' column to 'properties'
|
||||
volume._info.update(
|
||||
{
|
||||
|
@ -132,6 +132,28 @@ class CreateVolume(command.ShowOne):
|
||||
help=_("Allow volume to be attached more than once "
|
||||
"(default to False)")
|
||||
)
|
||||
bootable_group = parser.add_mutually_exclusive_group()
|
||||
bootable_group.add_argument(
|
||||
"--bootable",
|
||||
action="store_true",
|
||||
help=_("Mark volume as bootable")
|
||||
)
|
||||
bootable_group.add_argument(
|
||||
"--non-bootable",
|
||||
action="store_true",
|
||||
help=_("Mark volume as non-bootable (default)")
|
||||
)
|
||||
readonly_group = parser.add_mutually_exclusive_group()
|
||||
readonly_group.add_argument(
|
||||
"--read-only",
|
||||
action="store_true",
|
||||
help=_("Set volume to read-only access mode")
|
||||
)
|
||||
readonly_group.add_argument(
|
||||
"--read-write",
|
||||
action="store_true",
|
||||
help=_("Set volume to read-write access mode (default)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
@ -199,6 +221,22 @@ class CreateVolume(command.ShowOne):
|
||||
multiattach=parsed_args.multi_attach,
|
||||
scheduler_hints=parsed_args.hint,
|
||||
)
|
||||
|
||||
if parsed_args.bootable or parsed_args.non_bootable:
|
||||
try:
|
||||
volume_client.volumes.set_bootable(
|
||||
volume.id, parsed_args.bootable)
|
||||
except Exception as e:
|
||||
LOG.error(_("Failed to set volume bootable property: %s"), e)
|
||||
if parsed_args.read_only or parsed_args.read_write:
|
||||
try:
|
||||
volume_client.volumes.update_readonly_flag(
|
||||
volume.id,
|
||||
parsed_args.read_only)
|
||||
except Exception as e:
|
||||
LOG.error(_("Failed to set volume read-only access "
|
||||
"mode flag: %s"), e)
|
||||
|
||||
# Remove key links from being displayed
|
||||
volume._info.update(
|
||||
{
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- Add ``--bootable``, ``--non-bootable``, ``--read-only`` and ``--read-write``
|
||||
options to ``volume create`` command.
|
||||
[Blueprint `cinder-command-support <https://blueprints.launchpad.net/python-openstackclient/+spec/cinder-command-support>`_]
|
Loading…
Reference in New Issue
Block a user