summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjiahui.qiang <jiahui.qiang@easystack.cn>2016-12-04 00:29:03 +0800
committerjiahui.qiang <jiahui.qiang@easystack.cn>2016-12-06 00:17:36 +0800
commit7e5a98bca91e0734f3440fdbeb12cd59273cd6a4 (patch)
treec18ed8bd939d1edf1d1a05ec1c08f35a4f34faa8
parent29587eaa6661493b7df9357ad818bf058e820730 (diff)
downloadpython-openstackclient-7e5a98bca91e0734f3440fdbeb12cd59273cd6a4.tar.gz
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
-rw-r--r--doc/source/command-objects/volume.rst18
-rw-r--r--openstackclient/tests/unit/volume/v1/test_volume.py136
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume.py148
-rw-r--r--openstackclient/volume/v1/volume.py38
-rw-r--r--openstackclient/volume/v2/volume.py38
-rw-r--r--releasenotes/notes/bp-cinder-command-support-ff7acc531baae8c3.yaml5
6 files changed, 383 insertions, 0 deletions
diff --git a/doc/source/command-objects/volume.rst b/doc/source/command-objects/volume.rst
index df4d6880..5383386f 100644
--- a/doc/source/command-objects/volume.rst
+++ b/doc/source/command-objects/volume.rst
@@ -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>
diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py
index 7a44dea8..6c6d9a1d 100644
--- a/openstackclient/tests/unit/volume/v1/test_volume.py
+++ b/openstackclient/tests/unit/volume/v1/test_volume.py
@@ -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,
diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py
index 41728342..1529c2e2 100644
--- a/openstackclient/tests/unit/volume/v2/test_volume.py
+++ b/openstackclient/tests/unit/volume/v2/test_volume.py
@@ -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 = [
diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py
index 0087bad4..739484df 100644
--- a/openstackclient/volume/v1/volume.py
+++ b/openstackclient/volume/v1/volume.py
@@ -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(
{
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index 80abfb55..301bf5e4 100644
--- a/openstackclient/volume/v2/volume.py
+++ b/openstackclient/volume/v2/volume.py
@@ -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(
{
diff --git a/releasenotes/notes/bp-cinder-command-support-ff7acc531baae8c3.yaml b/releasenotes/notes/bp-cinder-command-support-ff7acc531baae8c3.yaml
new file mode 100644
index 00000000..d8126b1e
--- /dev/null
+++ b/releasenotes/notes/bp-cinder-command-support-ff7acc531baae8c3.yaml
@@ -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>`_]