summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
authorJenkins <jenkins@review.openstack.org>2016-12-03 04:30:51 +0000
committerGerrit Code Review <review@openstack.org>2016-12-03 04:30:51 +0000
commite05c8d7bb04f477797e46e5728e93a35c104809a (patch)
treecc46ac44bae654c3a2a5b71839a87051a6ca7800 /openstackclient
parent2fd5ca22f642591e861d3615040feb534479b013 (diff)
parent23ee2fd8f060ed312a84eb03c99494e8af2ffb6f (diff)
downloadpython-openstackclient-e05c8d7bb04f477797e46e5728e93a35c104809a.tar.gz
Merge "Refactor "snapshot" commands"
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/tests/functional/volume/v1/test_snapshot.py32
-rw-r--r--openstackclient/tests/functional/volume/v2/test_snapshot.py32
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume.py10
-rw-r--r--openstackclient/tests/unit/volume/v1/test_snapshot.py50
-rw-r--r--openstackclient/tests/unit/volume/v2/test_snapshot.py51
-rw-r--r--openstackclient/volume/v1/snapshot.py18
-rw-r--r--openstackclient/volume/v1/volume_snapshot.py305
-rw-r--r--openstackclient/volume/v2/snapshot.py18
-rw-r--r--openstackclient/volume/v2/volume_snapshot.py338
9 files changed, 796 insertions, 58 deletions
diff --git a/openstackclient/tests/functional/volume/v1/test_snapshot.py b/openstackclient/tests/functional/volume/v1/test_snapshot.py
index c6d65ccc..1e1c6b21 100644
--- a/openstackclient/tests/functional/volume/v1/test_snapshot.py
+++ b/openstackclient/tests/functional/volume/v1/test_snapshot.py
@@ -16,8 +16,8 @@ import uuid
from openstackclient.tests.functional.volume.v1 import common
-class SnapshotTests(common.BaseVolumeTests):
- """Functional tests for snapshot. """
+class VolumeSnapshotTests(common.BaseVolumeTests):
+ """Functional tests for volume snapshot. """
VOLLY = uuid.uuid4().hex
NAME = uuid.uuid4().hex
@@ -36,24 +36,25 @@ class SnapshotTests(common.BaseVolumeTests):
@classmethod
def setUpClass(cls):
- super(SnapshotTests, cls).setUpClass()
+ super(VolumeSnapshotTests, cls).setUpClass()
cls.openstack('volume create --size 1 ' + cls.VOLLY)
cls.wait_for_status('volume show ' + cls.VOLLY, 'available\n', 3)
opts = cls.get_opts(['status'])
- raw_output = cls.openstack('snapshot create --name ' + cls.NAME +
- ' ' + cls.VOLLY + opts)
+ raw_output = cls.openstack('volume snapshot create --volume ' +
+ cls.VOLLY + ' ' + cls.NAME + opts)
cls.assertOutput('creating\n', raw_output)
- cls.wait_for_status('snapshot show ' + cls.NAME, 'available\n', 3)
+ cls.wait_for_status(
+ 'volume snapshot show ' + cls.NAME, 'available\n', 3)
@classmethod
def tearDownClass(cls):
# Rename test
raw_output = cls.openstack(
- 'snapshot set --name ' + cls.OTHER_NAME + ' ' + cls.NAME)
+ 'volume snapshot set --name ' + cls.OTHER_NAME + ' ' + cls.NAME)
cls.assertOutput('', raw_output)
# Delete test
raw_output_snapshot = cls.openstack(
- 'snapshot delete ' + cls.OTHER_NAME)
+ 'volume snapshot delete ' + cls.OTHER_NAME)
cls.wait_for_status('volume show ' + cls.VOLLY, 'available\n', 6)
raw_output_volume = cls.openstack('volume delete --force ' + cls.VOLLY)
cls.assertOutput('', raw_output_snapshot)
@@ -61,26 +62,27 @@ class SnapshotTests(common.BaseVolumeTests):
def test_snapshot_list(self):
opts = self.get_opts(self.HEADERS)
- raw_output = self.openstack('snapshot list' + opts)
+ raw_output = self.openstack('volume snapshot list' + opts)
self.assertIn(self.NAME, raw_output)
def test_snapshot_set_unset_properties(self):
raw_output = self.openstack(
- 'snapshot set --property a=b --property c=d ' + self.NAME)
+ 'volume snapshot set --property a=b --property c=d ' + self.NAME)
self.assertEqual("", raw_output)
opts = self.get_opts(["properties"])
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("a='b', c='d'\n", raw_output)
- raw_output = self.openstack('snapshot unset --property a ' + self.NAME)
+ raw_output = self.openstack(
+ 'volume snapshot unset --property a ' + self.NAME)
self.assertEqual("", raw_output)
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("c='d'\n", raw_output)
def test_snapshot_set_description(self):
raw_output = self.openstack(
- 'snapshot set --description backup ' + self.NAME)
+ 'volume snapshot set --description backup ' + self.NAME)
self.assertEqual("", raw_output)
opts = self.get_opts(["display_description", "display_name"])
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("backup\n" + self.NAME + "\n", raw_output)
diff --git a/openstackclient/tests/functional/volume/v2/test_snapshot.py b/openstackclient/tests/functional/volume/v2/test_snapshot.py
index fcbc31cb..4eb69e9d 100644
--- a/openstackclient/tests/functional/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/functional/volume/v2/test_snapshot.py
@@ -16,8 +16,8 @@ import uuid
from openstackclient.tests.functional.volume.v2 import common
-class SnapshotTests(common.BaseVolumeTests):
- """Functional tests for snapshot. """
+class VolumeSnapshotTests(common.BaseVolumeTests):
+ """Functional tests for volume snapshot. """
VOLLY = uuid.uuid4().hex
NAME = uuid.uuid4().hex
@@ -36,24 +36,25 @@ class SnapshotTests(common.BaseVolumeTests):
@classmethod
def setUpClass(cls):
- super(SnapshotTests, cls).setUpClass()
+ super(VolumeSnapshotTests, cls).setUpClass()
cls.openstack('volume create --size 1 ' + cls.VOLLY)
cls.wait_for_status('volume show ' + cls.VOLLY, 'available\n', 3)
opts = cls.get_opts(['status'])
- raw_output = cls.openstack('snapshot create --name ' + cls.NAME +
- ' ' + cls.VOLLY + opts)
+ raw_output = cls.openstack('volume snapshot create --volume ' +
+ cls.VOLLY + ' ' + cls.NAME + opts)
cls.assertOutput('creating\n', raw_output)
- cls.wait_for_status('snapshot show ' + cls.NAME, 'available\n', 3)
+ cls.wait_for_status(
+ 'volume snapshot show ' + cls.NAME, 'available\n', 3)
@classmethod
def tearDownClass(cls):
# Rename test
raw_output = cls.openstack(
- 'snapshot set --name ' + cls.OTHER_NAME + ' ' + cls.NAME)
+ 'volume snapshot set --name ' + cls.OTHER_NAME + ' ' + cls.NAME)
cls.assertOutput('', raw_output)
# Delete test
raw_output_snapshot = cls.openstack(
- 'snapshot delete ' + cls.OTHER_NAME)
+ 'volume snapshot delete ' + cls.OTHER_NAME)
cls.wait_for_status('volume show ' + cls.VOLLY, 'available\n', 6)
raw_output_volume = cls.openstack('volume delete --force ' + cls.VOLLY)
cls.assertOutput('', raw_output_snapshot)
@@ -61,26 +62,27 @@ class SnapshotTests(common.BaseVolumeTests):
def test_snapshot_list(self):
opts = self.get_opts(self.HEADERS)
- raw_output = self.openstack('snapshot list' + opts)
+ raw_output = self.openstack('volume snapshot list' + opts)
self.assertIn(self.NAME, raw_output)
def test_snapshot_properties(self):
raw_output = self.openstack(
- 'snapshot set --property a=b --property c=d ' + self.NAME)
+ 'volume snapshot set --property a=b --property c=d ' + self.NAME)
self.assertEqual("", raw_output)
opts = self.get_opts(["properties"])
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("a='b', c='d'\n", raw_output)
- raw_output = self.openstack('snapshot unset --property a ' + self.NAME)
+ raw_output = self.openstack(
+ 'volume snapshot unset --property a ' + self.NAME)
self.assertEqual("", raw_output)
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("c='d'\n", raw_output)
def test_snapshot_set(self):
raw_output = self.openstack(
- 'snapshot set --description backup ' + self.NAME)
+ 'volume snapshot set --description backup ' + self.NAME)
self.assertEqual("", raw_output)
opts = self.get_opts(["description", "name"])
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("backup\n" + self.NAME + "\n", raw_output)
diff --git a/openstackclient/tests/functional/volume/v2/test_volume.py b/openstackclient/tests/functional/volume/v2/test_volume.py
index fb880578..ea891cba 100644
--- a/openstackclient/tests/functional/volume/v2/test_volume.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume.py
@@ -106,11 +106,12 @@ class VolumeTests(common.BaseVolumeTests):
opts = self.get_opts(self.FIELDS)
# Create snapshot from test volume
- raw_output = self.openstack('snapshot create ' + self.NAME +
- ' --name ' + self.SNAPSHOT_NAME + opts)
+ raw_output = self.openstack('volume snapshot create ' +
+ self.SNAPSHOT_NAME +
+ ' --volume ' + self.NAME + opts)
expected = self.SNAPSHOT_NAME + '\n'
self.assertOutput(expected, raw_output)
- self.wait_for("snapshot", self.SNAPSHOT_NAME, "available")
+ self.wait_for("volume snapshot", self.SNAPSHOT_NAME, "available")
# Create volume from snapshot
raw_output = self.openstack('volume create --size 2 --snapshot ' +
@@ -126,7 +127,8 @@ class VolumeTests(common.BaseVolumeTests):
self.assertOutput('', raw_output)
# Delete test snapshot
- raw_output = self.openstack('snapshot delete ' + self.SNAPSHOT_NAME)
+ raw_output = self.openstack(
+ 'volume snapshot delete ' + self.SNAPSHOT_NAME)
self.assertOutput('', raw_output)
self.wait_for("volume", self.NAME, "available")
diff --git a/openstackclient/tests/unit/volume/v1/test_snapshot.py b/openstackclient/tests/unit/volume/v1/test_snapshot.py
index edfbdc19..8e30d6a9 100644
--- a/openstackclient/tests/unit/volume/v1/test_snapshot.py
+++ b/openstackclient/tests/unit/volume/v1/test_snapshot.py
@@ -19,7 +19,7 @@ from osc_lib import exceptions
from osc_lib import utils
from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes
-from openstackclient.volume.v1 import snapshot
+from openstackclient.volume.v1 import volume_snapshot
class TestSnapshot(volume_fakes.TestVolumev1):
@@ -67,20 +67,20 @@ class TestSnapshotCreate(TestSnapshot):
self.volumes_mock.get.return_value = self.volume
self.snapshots_mock.create.return_value = self.new_snapshot
# Get the command object to test
- self.cmd = snapshot.CreateSnapshot(self.app, None)
+ self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None)
def test_snapshot_create(self):
arglist = [
- "--name", self.new_snapshot.display_name,
+ "--volume", self.new_snapshot.volume_id,
"--description", self.new_snapshot.display_description,
"--force",
- self.new_snapshot.volume_id,
+ self.new_snapshot.display_name,
]
verifylist = [
- ("name", self.new_snapshot.display_name),
+ ("volume", self.new_snapshot.volume_id),
("description", self.new_snapshot.display_description),
("force", True),
- ("volume", self.new_snapshot.volume_id),
+ ("snapshot_name", self.new_snapshot.display_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -97,7 +97,7 @@ class TestSnapshotCreate(TestSnapshot):
def test_snapshot_create_without_name(self):
arglist = [
- self.new_snapshot.volume_id,
+ "--volume", self.new_snapshot.volume_id,
"--description", self.new_snapshot.display_description,
"--force"
]
@@ -119,6 +119,32 @@ class TestSnapshotCreate(TestSnapshot):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_snapshot_create_without_volume(self):
+ arglist = [
+ "--description", self.new_snapshot.display_description,
+ "--force",
+ self.new_snapshot.display_name
+ ]
+ verifylist = [
+ ("description", self.new_snapshot.display_description),
+ ("force", True),
+ ("snapshot_name", self.new_snapshot.display_name)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.get.assert_called_once_with(
+ self.new_snapshot.display_name)
+ self.snapshots_mock.create.assert_called_once_with(
+ self.new_snapshot.volume_id,
+ True,
+ self.new_snapshot.display_name,
+ self.new_snapshot.display_description,
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestSnapshotDelete(TestSnapshot):
@@ -132,7 +158,7 @@ class TestSnapshotDelete(TestSnapshot):
self.snapshots_mock.delete.return_value = None
# Get the command object to mock
- self.cmd = snapshot.DeleteSnapshot(self.app, None)
+ self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None)
def test_snapshot_delete(self):
arglist = [
@@ -244,7 +270,7 @@ class TestSnapshotList(TestSnapshot):
self.volumes_mock.list.return_value = [self.volume]
self.snapshots_mock.list.return_value = self.snapshots
# Get the command to test
- self.cmd = snapshot.ListSnapshot(self.app, None)
+ self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)
def test_snapshot_list_without_options(self):
arglist = []
@@ -307,7 +333,7 @@ class TestSnapshotSet(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
self.snapshots_mock.set_metadata.return_value = None
# Get the command object to mock
- self.cmd = snapshot.SetSnapshot(self.app, None)
+ self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None)
def test_snapshot_set_all(self):
arglist = [
@@ -404,7 +430,7 @@ class TestSnapshotShow(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
# Get the command object to test
- self.cmd = snapshot.ShowSnapshot(self.app, None)
+ self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None)
def test_snapshot_show(self):
arglist = [
@@ -432,7 +458,7 @@ class TestSnapshotUnset(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
self.snapshots_mock.delete_metadata.return_value = None
# Get the command object to mock
- self.cmd = snapshot.UnsetSnapshot(self.app, None)
+ self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None)
def test_snapshot_unset(self):
arglist = [
diff --git a/openstackclient/tests/unit/volume/v2/test_snapshot.py b/openstackclient/tests/unit/volume/v2/test_snapshot.py
index d355662d..b67dd6eb 100644
--- a/openstackclient/tests/unit/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/unit/volume/v2/test_snapshot.py
@@ -20,7 +20,7 @@ from osc_lib import exceptions
from osc_lib import utils
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
-from openstackclient.volume.v2 import snapshot
+from openstackclient.volume.v2 import volume_snapshot
class TestSnapshot(volume_fakes.TestVolume):
@@ -68,23 +68,23 @@ class TestSnapshotCreate(TestSnapshot):
self.volumes_mock.get.return_value = self.volume
self.snapshots_mock.create.return_value = self.new_snapshot
# Get the command object to test
- self.cmd = snapshot.CreateSnapshot(self.app, None)
+ self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None)
def test_snapshot_create(self):
arglist = [
- "--name", self.new_snapshot.name,
+ "--volume", self.new_snapshot.volume_id,
"--description", self.new_snapshot.description,
"--force",
'--property', 'Alpha=a',
'--property', 'Beta=b',
- self.new_snapshot.volume_id,
+ self.new_snapshot.name,
]
verifylist = [
- ("name", self.new_snapshot.name),
+ ("volume", self.new_snapshot.volume_id),
("description", self.new_snapshot.description),
("force", True),
('property', {'Alpha': 'a', 'Beta': 'b'}),
- ("volume", self.new_snapshot.volume_id),
+ ("snapshot_name", self.new_snapshot.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -102,7 +102,7 @@ class TestSnapshotCreate(TestSnapshot):
def test_snapshot_create_without_name(self):
arglist = [
- self.new_snapshot.volume_id,
+ "--volume", self.new_snapshot.volume_id,
"--description", self.new_snapshot.description,
"--force"
]
@@ -125,6 +125,33 @@ class TestSnapshotCreate(TestSnapshot):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_snapshot_create_without_volume(self):
+ arglist = [
+ "--description", self.new_snapshot.description,
+ "--force",
+ self.new_snapshot.name
+ ]
+ verifylist = [
+ ("description", self.new_snapshot.description),
+ ("force", True),
+ ("snapshot_name", self.new_snapshot.name)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.get.assert_called_once_with(
+ self.new_snapshot.name)
+ self.snapshots_mock.create.assert_called_once_with(
+ self.new_snapshot.volume_id,
+ force=True,
+ name=self.new_snapshot.name,
+ description=self.new_snapshot.description,
+ metadata=None,
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestSnapshotDelete(TestSnapshot):
@@ -138,7 +165,7 @@ class TestSnapshotDelete(TestSnapshot):
self.snapshots_mock.delete.return_value = None
# Get the command object to mock
- self.cmd = snapshot.DeleteSnapshot(self.app, None)
+ self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None)
def test_snapshot_delete(self):
arglist = [
@@ -250,7 +277,7 @@ class TestSnapshotList(TestSnapshot):
self.volumes_mock.list.return_value = [self.volume]
self.snapshots_mock.list.return_value = self.snapshots
# Get the command to test
- self.cmd = snapshot.ListSnapshot(self.app, None)
+ self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)
def test_snapshot_list_without_options(self):
arglist = []
@@ -330,7 +357,7 @@ class TestSnapshotSet(TestSnapshot):
self.snapshots_mock.set_metadata.return_value = None
self.snapshots_mock.update.return_value = None
# Get the command object to mock
- self.cmd = snapshot.SetSnapshot(self.app, None)
+ self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None)
def test_snapshot_set(self):
arglist = [
@@ -457,7 +484,7 @@ class TestSnapshotShow(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
# Get the command object to test
- self.cmd = snapshot.ShowSnapshot(self.app, None)
+ self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None)
def test_snapshot_show(self):
arglist = [
@@ -485,7 +512,7 @@ class TestSnapshotUnset(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
self.snapshots_mock.delete_metadata.return_value = None
# Get the command object to mock
- self.cmd = snapshot.UnsetSnapshot(self.app, None)
+ self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None)
def test_snapshot_unset(self):
arglist = [
diff --git a/openstackclient/volume/v1/snapshot.py b/openstackclient/volume/v1/snapshot.py
index 0f91ee72..e9e3894b 100644
--- a/openstackclient/volume/v1/snapshot.py
+++ b/openstackclient/volume/v1/snapshot.py
@@ -13,6 +13,10 @@
# under the License.
#
+# TODO(Huanxuan Ao): Remove this file and "snapshot create", "snapshot delete",
+# "snapshot set", "snapshot show" and "snapshot unset"
+# commands two cycles after Ocata.
+
"""Volume v1 Snapshot action implementations"""
import copy
@@ -27,6 +31,8 @@ import six
from openstackclient.i18n import _
+deprecated = True
+LOG_DEP = logging.getLogger('deprecated')
LOG = logging.getLogger(__name__)
@@ -61,6 +67,8 @@ class CreateSnapshot(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot create" instead.'))
volume_client = self.app.client_manager.volume
volume_id = utils.find_resource(volume_client.volumes,
parsed_args.volume).id
@@ -92,6 +100,8 @@ class DeleteSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot delete" instead.'))
volume_client = self.app.client_manager.volume
result = 0
@@ -133,6 +143,8 @@ class ListSnapshot(command.Lister):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot list" instead.'))
def _format_volume_id(volume_id):
"""Return a volume name if available
@@ -214,6 +226,8 @@ class SetSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot set" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(volume_client.volume_snapshots,
parsed_args.snapshot)
@@ -258,6 +272,8 @@ class ShowSnapshot(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot show" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(volume_client.volume_snapshots,
parsed_args.snapshot)
@@ -289,6 +305,8 @@ class UnsetSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot unset" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot)
diff --git a/openstackclient/volume/v1/volume_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py
new file mode 100644
index 00000000..c2ecf75b
--- /dev/null
+++ b/openstackclient/volume/v1/volume_snapshot.py
@@ -0,0 +1,305 @@
+# Copyright 2012-2013 OpenStack Foundation
+#
+# 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.
+#
+
+"""Volume v1 Snapshot action implementations"""
+
+import copy
+import logging
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+import six
+
+from openstackclient.i18n import _
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CreateVolumeSnapshot(command.ShowOne):
+ """Create new volume snapshot"""
+
+ def get_parser(self, prog_name):
+ parser = super(CreateVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot_name',
+ metavar='<snapshot-name>',
+ nargs="?",
+ help=_('Name of the snapshot (default to None)'),
+ )
+ parser.add_argument(
+ '--volume',
+ metavar='<volume>',
+ help=_('Volume to snapshot (name or ID) '
+ '(default is <snapshot-name>)'),
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('Description of the snapshot'),
+ )
+ parser.add_argument(
+ '--force',
+ dest='force',
+ action='store_true',
+ default=False,
+ help=_('Create a snapshot attached to an instance. '
+ 'Default is False'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ volume = parsed_args.volume
+ if not parsed_args.volume:
+ volume = parsed_args.snapshot_name
+ volume_id = utils.find_resource(volume_client.volumes,
+ volume).id
+ snapshot = volume_client.volume_snapshots.create(
+ volume_id,
+ parsed_args.force,
+ parsed_args.snapshot_name,
+ parsed_args.description
+ )
+
+ snapshot._info.update(
+ {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ )
+
+ return zip(*sorted(six.iteritems(snapshot._info)))
+
+
+class DeleteVolumeSnapshot(command.Command):
+ """Delete volume snapshot(s)"""
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshots',
+ metavar='<snapshot>',
+ nargs="+",
+ help=_('Snapshot(s) to delete (name or ID)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ result = 0
+
+ for i in parsed_args.snapshots:
+ try:
+ snapshot_id = utils.find_resource(
+ volume_client.volume_snapshots, i).id
+ volume_client.volume_snapshots.delete(snapshot_id)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete snapshot with "
+ "name or ID '%(snapshot)s': %(e)s"),
+ {'snapshot': i, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.snapshots)
+ msg = (_("%(result)s of %(total)s snapshots failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
+class ListVolumeSnapshot(command.Lister):
+ """List volume snapshots"""
+
+ def get_parser(self, prog_name):
+ parser = super(ListVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help=_('Include all projects (admin only)'),
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help=_('List additional fields in output'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+
+ def _format_volume_id(volume_id):
+ """Return a volume name if available
+
+ :param volume_id: a volume ID
+ :rtype: either the volume ID or name
+ """
+
+ volume = volume_id
+ if volume_id in volume_cache.keys():
+ volume = volume_cache[volume_id].display_name
+ return volume
+
+ if parsed_args.long:
+ columns = ['ID', 'Display Name', 'Display Description', 'Status',
+ 'Size', 'Created At', 'Volume ID', 'Metadata']
+ column_headers = copy.deepcopy(columns)
+ column_headers[6] = 'Volume'
+ column_headers[7] = 'Properties'
+ else:
+ columns = ['ID', 'Display Name', 'Display Description', 'Status',
+ 'Size']
+ column_headers = copy.deepcopy(columns)
+
+ # Always update Name and Description
+ column_headers[1] = 'Name'
+ column_headers[2] = 'Description'
+
+ # Cache the volume list
+ volume_cache = {}
+ try:
+ for s in self.app.client_manager.volume.volumes.list():
+ volume_cache[s.id] = s
+ except Exception:
+ # Just forget it if there's any trouble
+ pass
+
+ search_opts = {
+ 'all_tenants': parsed_args.all_projects,
+ }
+
+ data = self.app.client_manager.volume.volume_snapshots.list(
+ search_opts=search_opts)
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={'Metadata': utils.format_dict,
+ 'Volume ID': _format_volume_id},
+ ) for s in data))
+
+
+class SetVolumeSnapshot(command.Command):
+ """Set volume snapshot properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(SetVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to modify (name or ID)')
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help=_('New snapshot name')
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('New snapshot description')
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key=value>',
+ action=parseractions.KeyValueAction,
+ help=_('Property to add/change for this snapshot '
+ '(repeat option to set multiple properties)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(volume_client.volume_snapshots,
+ parsed_args.snapshot)
+
+ result = 0
+ if parsed_args.property:
+ try:
+ volume_client.volume_snapshots.set_metadata(
+ snapshot.id, parsed_args.property)
+ except Exception as e:
+ LOG.error(_("Failed to set snapshot property: %s"), e)
+ result += 1
+
+ kwargs = {}
+ if parsed_args.name:
+ kwargs['display_name'] = parsed_args.name
+ if parsed_args.description:
+ kwargs['display_description'] = parsed_args.description
+ if kwargs:
+ try:
+ snapshot.update(**kwargs)
+ except Exception as e:
+ LOG.error(_("Failed to update snapshot display name "
+ "or display description: %s"), e)
+ result += 1
+
+ if result > 0:
+ raise exceptions.CommandError(_("One or more of the "
+ "set operations failed"))
+
+
+class ShowVolumeSnapshot(command.ShowOne):
+ """Display volume snapshot details"""
+
+ def get_parser(self, prog_name):
+ parser = super(ShowVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to display (name or ID)')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(volume_client.volume_snapshots,
+ parsed_args.snapshot)
+
+ snapshot._info.update(
+ {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ )
+
+ return zip(*sorted(six.iteritems(snapshot._info)))
+
+
+class UnsetVolumeSnapshot(command.Command):
+ """Unset volume snapshot properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(UnsetVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to modify (name or ID)'),
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key>',
+ action='append',
+ help=_('Property to remove from snapshot '
+ '(repeat option to remove multiple properties)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(
+ volume_client.volume_snapshots, parsed_args.snapshot)
+
+ if parsed_args.property:
+ volume_client.volume_snapshots.delete_metadata(
+ snapshot.id,
+ parsed_args.property,
+ )
diff --git a/openstackclient/volume/v2/snapshot.py b/openstackclient/volume/v2/snapshot.py
index 8cda112a..a18887e3 100644
--- a/openstackclient/volume/v2/snapshot.py
+++ b/openstackclient/volume/v2/snapshot.py
@@ -12,6 +12,10 @@
# under the License.
#
+# TODO(Huanxuan Ao): Remove this file and "snapshot create", "snapshot delete",
+# "snapshot set", "snapshot show" and "snapshot unset"
+# commands two cycles after Ocata.
+
"""Volume v2 snapshot action implementations"""
import copy
@@ -26,6 +30,8 @@ import six
from openstackclient.i18n import _
+deprecated = True
+LOG_DEP = logging.getLogger('deprecated')
LOG = logging.getLogger(__name__)
@@ -66,6 +72,8 @@ class CreateSnapshot(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot create" instead.'))
volume_client = self.app.client_manager.volume
volume_id = utils.find_resource(
volume_client.volumes, parsed_args.volume).id
@@ -96,6 +104,8 @@ class DeleteSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot delete" instead.'))
volume_client = self.app.client_manager.volume
result = 0
@@ -149,6 +159,8 @@ class ListSnapshot(command.Lister):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot list" instead.'))
def _format_volume_id(volume_id):
"""Return a volume name if available
@@ -239,6 +251,8 @@ class SetSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot set" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(volume_client.volume_snapshots,
parsed_args.snapshot)
@@ -292,6 +306,8 @@ class ShowSnapshot(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot show" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot)
@@ -322,6 +338,8 @@ class UnsetSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot unset" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot)
diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py
new file mode 100644
index 00000000..43f30326
--- /dev/null
+++ b/openstackclient/volume/v2/volume_snapshot.py
@@ -0,0 +1,338 @@
+#
+# 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.
+#
+
+"""Volume v2 snapshot action implementations"""
+
+import copy
+import logging
+
+from osc_lib.cli import parseractions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+import six
+
+from openstackclient.i18n import _
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CreateVolumeSnapshot(command.ShowOne):
+ """Create new volume snapshot"""
+
+ def get_parser(self, prog_name):
+ parser = super(CreateVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ "snapshot_name",
+ metavar="<snapshot-name>",
+ nargs="?",
+ help=_("Name of the new snapshot (default to None)")
+ )
+ parser.add_argument(
+ "--volume",
+ metavar="<volume>",
+ help=_("Volume to snapshot (name or ID) "
+ "(default is <snapshot-name>)")
+ )
+ parser.add_argument(
+ "--description",
+ metavar="<description>",
+ help=_("Description of the snapshot")
+ )
+ parser.add_argument(
+ "--force",
+ action="store_true",
+ default=False,
+ help=_("Create a snapshot attached to an instance. "
+ "Default is False")
+ )
+ parser.add_argument(
+ "--property",
+ metavar="<key=value>",
+ action=parseractions.KeyValueAction,
+ help=_("Set a property to this snapshot "
+ "(repeat option to set multiple properties)"),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ volume = parsed_args.volume
+ if not parsed_args.volume:
+ volume = parsed_args.snapshot_name
+ volume_id = utils.find_resource(
+ volume_client.volumes, volume).id
+ snapshot = volume_client.volume_snapshots.create(
+ volume_id,
+ force=parsed_args.force,
+ name=parsed_args.snapshot_name,
+ description=parsed_args.description,
+ metadata=parsed_args.property,
+ )
+ snapshot._info.update(
+ {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ )
+ return zip(*sorted(six.iteritems(snapshot._info)))
+
+
+class DeleteVolumeSnapshot(command.Command):
+ """Delete volume snapshot(s)"""
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ "snapshots",
+ metavar="<snapshot>",
+ nargs="+",
+ help=_("Snapshot(s) to delete (name or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ result = 0
+
+ for i in parsed_args.snapshots:
+ try:
+ snapshot_id = utils.find_resource(
+ volume_client.volume_snapshots, i).id
+ volume_client.volume_snapshots.delete(snapshot_id)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete snapshot with "
+ "name or ID '%(snapshot)s': %(e)s")
+ % {'snapshot': i, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.snapshots)
+ msg = (_("%(result)s of %(total)s snapshots failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
+class ListVolumeSnapshot(command.Lister):
+ """List volume snapshots"""
+
+ def get_parser(self, prog_name):
+ parser = super(ListVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help=_('Include all projects (admin only)'),
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help=_('List additional fields in output'),
+ )
+ parser.add_argument(
+ '--marker',
+ metavar='<marker>',
+ help=_('The last snapshot ID of the previous page'),
+ )
+ parser.add_argument(
+ '--limit',
+ type=int,
+ action=parseractions.NonNegativeAction,
+ metavar='<limit>',
+ help=_('Maximum number of snapshots to display'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+
+ def _format_volume_id(volume_id):
+ """Return a volume name if available
+
+ :param volume_id: a volume ID
+ :rtype: either the volume ID or name
+ """
+
+ volume = volume_id
+ if volume_id in volume_cache.keys():
+ volume = volume_cache[volume_id].name
+ return volume
+
+ if parsed_args.long:
+ columns = ['ID', 'Name', 'Description', 'Status',
+ 'Size', 'Created At', 'Volume ID', 'Metadata']
+ column_headers = copy.deepcopy(columns)
+ column_headers[6] = 'Volume'
+ column_headers[7] = 'Properties'
+ else:
+ columns = ['ID', 'Name', 'Description', 'Status', 'Size']
+ column_headers = copy.deepcopy(columns)
+
+ # Cache the volume list
+ volume_cache = {}
+ try:
+ for s in self.app.client_manager.volume.volumes.list():
+ volume_cache[s.id] = s
+ except Exception:
+ # Just forget it if there's any trouble
+ pass
+
+ search_opts = {
+ 'all_tenants': parsed_args.all_projects,
+ }
+
+ data = self.app.client_manager.volume.volume_snapshots.list(
+ search_opts=search_opts,
+ marker=parsed_args.marker,
+ limit=parsed_args.limit,
+ )
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={'Metadata': utils.format_dict,
+ 'Volume ID': _format_volume_id},
+ ) for s in data))
+
+
+class SetVolumeSnapshot(command.Command):
+ """Set volume snapshot properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(SetVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to modify (name or ID)')
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help=_('New snapshot name')
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('New snapshot description')
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key=value>',
+ action=parseractions.KeyValueAction,
+ help=_('Property to add/change for this snapshot '
+ '(repeat option to set multiple properties)'),
+ )
+ parser.add_argument(
+ '--state',
+ metavar='<state>',
+ choices=['available', 'error', 'creating', 'deleting',
+ 'error-deleting'],
+ help=_('New snapshot state. ("available", "error", "creating", '
+ '"deleting", or "error_deleting") (admin only) '
+ '(This option simply changes the state of the snapshot '
+ 'in the database with no regard to actual status, '
+ 'exercise caution when using)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(volume_client.volume_snapshots,
+ parsed_args.snapshot)
+
+ result = 0
+ if parsed_args.property:
+ try:
+ volume_client.volume_snapshots.set_metadata(
+ snapshot.id, parsed_args.property)
+ except Exception as e:
+ LOG.error(_("Failed to set snapshot property: %s"), e)
+ result += 1
+
+ if parsed_args.state:
+ try:
+ volume_client.volume_snapshots.reset_state(
+ snapshot.id, parsed_args.state)
+ except Exception as e:
+ LOG.error(_("Failed to set snapshot state: %s"), e)
+ result += 1
+
+ kwargs = {}
+ if parsed_args.name:
+ kwargs['name'] = parsed_args.name
+ if parsed_args.description:
+ kwargs['description'] = parsed_args.description
+ if kwargs:
+ try:
+ volume_client.volume_snapshots.update(
+ snapshot.id, **kwargs)
+ except Exception as e:
+ LOG.error(_("Failed to update snapshot name "
+ "or description: %s"), e)
+ result += 1
+
+ if result > 0:
+ raise exceptions.CommandError(_("One or more of the "
+ "set operations failed"))
+
+
+class ShowVolumeSnapshot(command.ShowOne):
+ """Display volume snapshot details"""
+
+ def get_parser(self, prog_name):
+ parser = super(ShowVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ "snapshot",
+ metavar="<snapshot>",
+ help=_("Snapshot to display (name or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(
+ volume_client.volume_snapshots, parsed_args.snapshot)
+ snapshot._info.update(
+ {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ )
+ return zip(*sorted(six.iteritems(snapshot._info)))
+
+
+class UnsetVolumeSnapshot(command.Command):
+ """Unset volume snapshot properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(UnsetVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to modify (name or ID)'),
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key>',
+ action='append',
+ default=[],
+ help=_('Property to remove from snapshot '
+ '(repeat option to remove multiple properties)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(
+ volume_client.volume_snapshots, parsed_args.snapshot)
+
+ if parsed_args.property:
+ volume_client.volume_snapshots.delete_metadata(
+ snapshot.id,
+ parsed_args.property,
+ )