LogoopenSUSE Build Service > Projects
Sign Up | Log In

View File openstack-volume-backport.diff of Package python-openstackclient (Project home:garloff:OTC:python-save)

diff -uNrp openstackclient/volume.orig/client.py openstackclient/volume/client.py
--- openstackclient/volume.orig/client.py	2016-03-22 22:40:39.000000000 +0100
+++ openstackclient/volume/client.py	2015-12-18 16:14:46.000000000 +0100
@@ -15,29 +15,32 @@
 
 import logging
 
-from cinderclient import extension
-from cinderclient.v1.contrib import list_extensions
-from cinderclient.v1 import volume_snapshots
-from cinderclient.v1 import volumes
-
 from openstackclient.common import utils
 
-# Monkey patch for v1 cinderclient
-volumes.Volume.NAME_ATTR = 'display_name'
-volume_snapshots.Snapshot.NAME_ATTR = 'display_name'
-
 LOG = logging.getLogger(__name__)
 
-DEFAULT_VOLUME_API_VERSION = '1'
+DEFAULT_API_VERSION = '2'
 API_VERSION_OPTION = 'os_volume_api_version'
 API_NAME = "volume"
 API_VERSIONS = {
-    "1": "cinderclient.v1.client.Client"
+    "1": "cinderclient.v1.client.Client",
+    "2": "cinderclient.v2.client.Client"
 }
 
 
 def make_client(instance):
     """Returns a volume service client."""
+
+    # Defer client imports until we actually need them
+    from cinderclient import extension
+    from cinderclient.v1.contrib import list_extensions
+    from cinderclient.v1 import volume_snapshots
+    from cinderclient.v1 import volumes
+
+    # Monkey patch for v1 cinderclient
+    volumes.Volume.NAME_ATTR = 'display_name'
+    volume_snapshots.Snapshot.NAME_ATTR = 'display_name'
+
     volume_client = utils.get_client_class(
         API_NAME,
         instance._api_version[API_NAME],
@@ -50,11 +53,15 @@ def make_client(instance):
 
     extensions = [extension.Extension('list_extensions', list_extensions)]
 
+    # Remember interface only if it is set
+    kwargs = utils.build_kwargs_dict('endpoint_type', instance._interface)
+
     client = volume_client(
         session=instance.session,
         extensions=extensions,
         http_log_debug=http_log_debug,
         region_name=instance._region_name,
+        **kwargs
     )
 
     return client
@@ -65,10 +72,8 @@ def build_option_parser(parser):
     parser.add_argument(
         '--os-volume-api-version',
         metavar='<volume-api-version>',
-        default=utils.env(
-            'OS_VOLUME_API_VERSION',
-            default=DEFAULT_VOLUME_API_VERSION),
+        default=utils.env('OS_VOLUME_API_VERSION'),
         help='Volume API version, default=' +
-             DEFAULT_VOLUME_API_VERSION +
+             DEFAULT_API_VERSION +
              ' (Env: OS_VOLUME_API_VERSION)')
     return parser
diff -uNrp openstackclient/volume.orig/v1/backup.py openstackclient/volume/v1/backup.py
--- openstackclient/volume.orig/v1/backup.py	2016-03-22 22:40:39.000000000 +0100
+++ openstackclient/volume/v1/backup.py	2015-12-18 16:14:46.000000000 +0100
@@ -57,14 +57,14 @@ class CreateBackup(show.ShowOne):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         volume_id = utils.find_resource(volume_client.volumes,
                                         parsed_args.volume).id
         backup = volume_client.backups.create(
             volume_id,
-            parsed_args.volume,
+            parsed_args.container,
             parsed_args.name,
             parsed_args.description
         )
@@ -88,8 +88,8 @@ class DeleteBackup(command.Command):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         for backup in parsed_args.backups:
             backup_id = utils.find_resource(volume_client.backups,
@@ -113,8 +113,8 @@ class ListBackup(lister.Lister):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
 
         def _format_volume_id(volume_id):
             """Return a volume name if available
@@ -172,8 +172,8 @@ class RestoreBackup(command.Command):
             help='Volume to restore to (name or ID)')
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         backup = utils.find_resource(volume_client.backups,
                                      parsed_args.backup)
@@ -196,8 +196,8 @@ class ShowBackup(show.ShowOne):
             help='Backup to display (ID only)')
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         backup = utils.find_resource(volume_client.backups,
                                      parsed_args.backup)
diff -uNrp openstackclient/volume.orig/v1/qos_specs.py openstackclient/volume/v1/qos_specs.py
--- openstackclient/volume.orig/v1/qos_specs.py	1970-01-01 01:00:00.000000000 +0100
+++ openstackclient/volume/v1/qos_specs.py	2015-12-18 16:14:46.000000000 +0100
@@ -0,0 +1,302 @@
+#   Copyright 2015 iWeb Technologies Inc.
+#
+#   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 QoS action implementations"""
+
+import logging
+import six
+
+from cliff import command
+from cliff import lister
+from cliff import show
+
+from openstackclient.common import parseractions
+from openstackclient.common import utils
+
+
+class AssociateQos(command.Command):
+    """Associate a QoS specification to a volume type"""
+
+    log = logging.getLogger(__name__ + '.AssociateQos')
+
+    def get_parser(self, prog_name):
+        parser = super(AssociateQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to modify (name or ID)',
+        )
+        parser.add_argument(
+            'volume_type',
+            metavar='<volume-type>',
+            help='Volume type to associate the QoS (name or ID)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+        volume_type = utils.find_resource(volume_client.volume_types,
+                                          parsed_args.volume_type)
+
+        volume_client.qos_specs.associate(qos_spec.id, volume_type.id)
+
+        return
+
+
+class CreateQos(show.ShowOne):
+    """Create new QoS specification"""
+
+    log = logging.getLogger(__name__ + '.CreateQos')
+
+    def get_parser(self, prog_name):
+        parser = super(CreateQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'name',
+            metavar='<name>',
+            help='New QoS specification name',
+        )
+        consumer_choices = ['front-end', 'back-end', 'both']
+        parser.add_argument(
+            '--consumer',
+            metavar='<consumer>',
+            choices=consumer_choices,
+            default='both',
+            help='Consumer of the QoS. Valid consumers: %s '
+                 "(defaults to 'both')" % utils.format_list(consumer_choices)
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Set a QoS specification property '
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        specs = {}
+        specs.update({'consumer': parsed_args.consumer})
+
+        if parsed_args.property:
+            specs.update(parsed_args.property)
+
+        qos_spec = volume_client.qos_specs.create(parsed_args.name, specs)
+
+        return zip(*sorted(six.iteritems(qos_spec._info)))
+
+
+class DeleteQos(command.Command):
+    """Delete QoS specification"""
+
+    log = logging.getLogger(__name__ + '.DeleteQos')
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_specs',
+            metavar='<qos-spec>',
+            nargs="+",
+            help='QoS specification(s) to delete (name or ID)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        for qos in parsed_args.qos_specs:
+            qos_spec = utils.find_resource(volume_client.qos_specs, qos)
+            volume_client.qos_specs.delete(qos_spec.id)
+        return
+
+
+class DisassociateQos(command.Command):
+    """Disassociate a QoS specification from a volume type"""
+
+    log = logging.getLogger(__name__ + '.DisassociateQos')
+
+    def get_parser(self, prog_name):
+        parser = super(DisassociateQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to modify (name or ID)',
+        )
+        volume_type_group = parser.add_mutually_exclusive_group()
+        volume_type_group.add_argument(
+            '--volume-type',
+            metavar='<volume-type>',
+            help='Volume type to disassociate the QoS from (name or ID)',
+        )
+        volume_type_group.add_argument(
+            '--all',
+            action='store_true',
+            default=False,
+            help='Disassociate the QoS from every volume type',
+        )
+
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+
+        if parsed_args.volume_type:
+            volume_type = utils.find_resource(volume_client.volume_types,
+                                              parsed_args.volume_type)
+            volume_client.qos_specs.disassociate(qos_spec.id, volume_type.id)
+        elif parsed_args.all:
+            volume_client.qos_specs.disassociate_all(qos_spec.id)
+
+        return
+
+
+class ListQos(lister.Lister):
+    """List QoS specifications"""
+
+    log = logging.getLogger(__name__ + '.ListQos')
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_specs_list = volume_client.qos_specs.list()
+
+        for qos in qos_specs_list:
+            qos_associations = volume_client.qos_specs.get_associations(qos)
+            if qos_associations:
+                associations = [association.name
+                                for association in qos_associations]
+                qos._info.update({'associations': associations})
+
+        columns = ('ID', 'Name', 'Consumer', 'Associations', 'Specs')
+        return (columns,
+                (utils.get_dict_properties(
+                    s._info, columns,
+                    formatters={
+                        'Specs': utils.format_dict,
+                        'Associations': utils.format_list
+                    },
+                ) for s in qos_specs_list))
+
+
+class SetQos(command.Command):
+    """Set QoS specification properties"""
+
+    log = logging.getLogger(__name__ + '.SetQos')
+
+    def get_parser(self, prog_name):
+        parser = super(SetQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to modify (name or ID)',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add or modify for this QoS specification '
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+
+        if parsed_args.property:
+            volume_client.qos_specs.set_keys(qos_spec.id,
+                                             parsed_args.property)
+        else:
+            self.app.log.error("No changes requested\n")
+
+        return
+
+
+class ShowQos(show.ShowOne):
+    """Display QoS specification details"""
+
+    log = logging.getLogger(__name__ + '.ShowQos')
+
+    def get_parser(self, prog_name):
+        parser = super(ShowQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to display (name or ID)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+
+        qos_associations = volume_client.qos_specs.get_associations(qos_spec)
+        if qos_associations:
+            associations = [association.name
+                            for association in qos_associations]
+            qos_spec._info.update({
+                'associations': utils.format_list(associations)
+            })
+        qos_spec._info.update({'specs': utils.format_dict(qos_spec.specs)})
+
+        return zip(*sorted(six.iteritems(qos_spec._info)))
+
+
+class UnsetQos(command.Command):
+    """Unset QoS specification properties"""
+
+    log = logging.getLogger(__name__ + '.SetQos')
+
+    def get_parser(self, prog_name):
+        parser = super(UnsetQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to modify (name or ID)',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key>',
+            action='append',
+            default=[],
+            help='Property to remove from the QoS specification. '
+                 '(repeat option to unset multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+
+        if parsed_args.property:
+            volume_client.qos_specs.unset_keys(qos_spec.id,
+                                               parsed_args.property)
+        else:
+            self.app.log.error("No changes requested\n")
+
+        return
diff -uNrp openstackclient/volume.orig/v1/snapshot.py openstackclient/volume/v1/snapshot.py
--- openstackclient/volume.orig/v1/snapshot.py	2016-03-22 22:40:39.000000000 +0100
+++ openstackclient/volume/v1/snapshot.py	2015-12-18 16:14:48.000000000 +0100
@@ -59,8 +59,8 @@ class CreateSnapshot(show.ShowOne):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         volume_id = utils.find_resource(volume_client.volumes,
                                         parsed_args.volume).id
@@ -93,8 +93,8 @@ class DeleteSnapshot(command.Command):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         for snapshot in parsed_args.snapshots:
             snapshot_id = utils.find_resource(volume_client.volume_snapshots,
@@ -118,8 +118,8 @@ class ListSnapshot(lister.Lister):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
 
         def _format_volume_id(volume_id):
             """Return a volume name if available
@@ -194,8 +194,8 @@ class SetSnapshot(command.Command):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         snapshot = utils.find_resource(volume_client.volume_snapshots,
                                        parsed_args.snapshot)
@@ -231,8 +231,8 @@ class ShowSnapshot(show.ShowOne):
             help='Snapshot to display (name or ID)')
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         snapshot = utils.find_resource(volume_client.volume_snapshots,
                                        parsed_args.snapshot)
@@ -263,11 +263,12 @@ class UnsetSnapshot(command.Command):
             default=[],
             help='Property to remove from snapshot '
                  '(repeat to remove multiple values)',
+            required=True,
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         snapshot = utils.find_resource(
             volume_client.volume_snapshots, parsed_args.snapshot)
diff -uNrp openstackclient/volume.orig/v1/type.py openstackclient/volume/v1/type.py
--- openstackclient/volume.orig/v1/type.py	2016-03-22 22:40:39.000000000 +0100
+++ openstackclient/volume/v1/type.py	1970-01-01 01:00:00.000000000 +0100
@@ -1,184 +0,0 @@
-#   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 Type action implementations"""
-
-import logging
-import six
-
-from cliff import command
-from cliff import lister
-from cliff import show
-
-from openstackclient.common import parseractions
-from openstackclient.common import utils
-
-
-class CreateVolumeType(show.ShowOne):
-    """Create new volume type"""
-
-    log = logging.getLogger(__name__ + '.CreateVolumeType')
-
-    def get_parser(self, prog_name):
-        parser = super(CreateVolumeType, self).get_parser(prog_name)
-        parser.add_argument(
-            'name',
-            metavar='<name>',
-            help='New volume type name',
-        )
-        parser.add_argument(
-            '--property',
-            metavar='<key=value>',
-            action=parseractions.KeyValueAction,
-            help='Property to add for this volume type '
-                 '(repeat option to set multiple properties)',
-        )
-        return parser
-
-    def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
-        volume_client = self.app.client_manager.volume
-        volume_type = volume_client.volume_types.create(parsed_args.name)
-        volume_type._info.pop('extra_specs')
-        if parsed_args.property:
-            result = volume_type.set_keys(parsed_args.property)
-            volume_type._info.update({'properties': utils.format_dict(result)})
-
-        info = {}
-        info.update(volume_type._info)
-        return zip(*sorted(six.iteritems(info)))
-
-
-class DeleteVolumeType(command.Command):
-    """Delete volume type"""
-
-    log = logging.getLogger(__name__ + '.DeleteVolumeType')
-
-    def get_parser(self, prog_name):
-        parser = super(DeleteVolumeType, self).get_parser(prog_name)
-        parser.add_argument(
-            'volume_type',
-            metavar='<volume-type>',
-            help='Volume type to delete (name or ID)',
-        )
-        return parser
-
-    def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
-        volume_client = self.app.client_manager.volume
-        volume_type_id = utils.find_resource(
-            volume_client.volume_types, parsed_args.volume_type).id
-        volume_client.volume_types.delete(volume_type_id)
-        return
-
-
-class ListVolumeType(lister.Lister):
-    """List volume types"""
-
-    log = logging.getLogger(__name__ + '.ListVolumeType')
-
-    def get_parser(self, prog_name):
-        parser = super(ListVolumeType, self).get_parser(prog_name)
-        parser.add_argument(
-            '--long',
-            action='store_true',
-            default=False,
-            help='List additional fields in output')
-        return parser
-
-    def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
-        if parsed_args.long:
-            columns = ('ID', 'Name', 'Extra Specs')
-            column_headers = ('ID', 'Name', 'Properties')
-        else:
-            columns = ('ID', 'Name')
-            column_headers = columns
-        data = self.app.client_manager.volume.volume_types.list()
-        return (column_headers,
-                (utils.get_item_properties(
-                    s, columns,
-                    formatters={'Extra Specs': utils.format_dict},
-                ) for s in data))
-
-
-class SetVolumeType(command.Command):
-    """Set volume type properties"""
-
-    log = logging.getLogger(__name__ + '.SetVolumeType')
-
-    def get_parser(self, prog_name):
-        parser = super(SetVolumeType, self).get_parser(prog_name)
-        parser.add_argument(
-            'volume_type',
-            metavar='<volume-type>',
-            help='Volume type to modify (name or ID)',
-        )
-        parser.add_argument(
-            '--property',
-            metavar='<key=value>',
-            action=parseractions.KeyValueAction,
-            help='Property to add or modify for this volume type '
-                 '(repeat option to set multiple properties)',
-        )
-        return parser
-
-    def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
-        volume_client = self.app.client_manager.volume
-        volume_type = utils.find_resource(
-            volume_client.volume_types, parsed_args.volume_type)
-
-        if parsed_args.property:
-            volume_type.set_keys(parsed_args.property)
-
-        return
-
-
-class UnsetVolumeType(command.Command):
-    """Unset volume type properties"""
-
-    log = logging.getLogger(__name__ + '.UnsetVolumeType')
-
-    def get_parser(self, prog_name):
-        parser = super(UnsetVolumeType, self).get_parser(prog_name)
-        parser.add_argument(
-            'volume_type',
-            metavar='<volume-type>',
-            help='Volume type to modify (name or ID)',
-        )
-        parser.add_argument(
-            '--property',
-            metavar='<key>',
-            action='append',
-            default=[],
-            help='Property to remove from volume type '
-                 '(repeat option to remove multiple properties)',
-        )
-        return parser
-
-    def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
-        volume_client = self.app.client_manager.volume
-        volume_type = utils.find_resource(
-            volume_client.volume_types,
-            parsed_args.volume_type,
-        )
-
-        if parsed_args.property:
-            volume_type.unset_keys(parsed_args.property)
-        else:
-            self.app.log.error("No changes requested\n")
-        return
diff -uNrp openstackclient/volume.orig/v1/volume.py openstackclient/volume/v1/volume.py
--- openstackclient/volume.orig/v1/volume.py	2016-03-22 22:40:39.000000000 +0100
+++ openstackclient/volume/v1/volume.py	2015-12-18 16:14:48.000000000 +0100
@@ -102,8 +102,8 @@ class CreateVolume(show.ShowOne):
 
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
 
         identity_client = self.app.client_manager.identity
         image_client = self.app.client_manager.image
@@ -186,8 +186,8 @@ class DeleteVolume(command.Command):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         for volume in parsed_args.volumes:
             volume_obj = utils.find_resource(
@@ -230,8 +230,8 @@ class ListVolume(lister.Lister):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
 
         volume_client = self.app.client_manager.volume
         compute_client = self.app.client_manager.compute
@@ -351,8 +351,8 @@ class SetVolume(command.Command):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
 
@@ -399,8 +399,8 @@ class ShowVolume(show.ShowOne):
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
         # Map 'metadata' column to 'properties'
@@ -437,11 +437,12 @@ class UnsetVolume(command.Command):
             default=[],
             help='Property to remove from volume '
                  '(repeat option to remove multiple properties)',
+            required=True,
         )
         return parser
 
+    @utils.log_method(log)
     def take_action(self, parsed_args):
-        self.log.debug('take_action(%s)', parsed_args)
         volume_client = self.app.client_manager.volume
         volume = utils.find_resource(
             volume_client.volumes, parsed_args.volume)
diff -uNrp openstackclient/volume.orig/v1/volume_type.py openstackclient/volume/v1/volume_type.py
--- openstackclient/volume.orig/v1/volume_type.py	1970-01-01 01:00:00.000000000 +0100
+++ openstackclient/volume/v1/volume_type.py	2015-12-18 16:14:46.000000000 +0100
@@ -0,0 +1,209 @@
+#   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 Type action implementations"""
+
+import logging
+import six
+
+from cliff import command
+from cliff import lister
+from cliff import show
+
+from openstackclient.common import parseractions
+from openstackclient.common import utils
+
+
+class CreateVolumeType(show.ShowOne):
+    """Create new volume type"""
+
+    log = logging.getLogger(__name__ + '.CreateVolumeType')
+
+    def get_parser(self, prog_name):
+        parser = super(CreateVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            'name',
+            metavar='<name>',
+            help='New volume type name',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add for this volume type '
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        volume_type = volume_client.volume_types.create(parsed_args.name)
+        volume_type._info.pop('extra_specs')
+        if parsed_args.property:
+            result = volume_type.set_keys(parsed_args.property)
+            volume_type._info.update({'properties': utils.format_dict(result)})
+
+        info = {}
+        info.update(volume_type._info)
+        return zip(*sorted(six.iteritems(info)))
+
+
+class DeleteVolumeType(command.Command):
+    """Delete volume type"""
+
+    log = logging.getLogger(__name__ + '.DeleteVolumeType')
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            'volume_type',
+            metavar='<volume-type>',
+            help='Volume type to delete (name or ID)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        volume_type_id = utils.find_resource(
+            volume_client.volume_types, parsed_args.volume_type).id
+        volume_client.volume_types.delete(volume_type_id)
+        return
+
+
+class ListVolumeType(lister.Lister):
+    """List volume types"""
+
+    log = logging.getLogger(__name__ + '.ListVolumeType')
+
+    def get_parser(self, prog_name):
+        parser = super(ListVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            '--long',
+            action='store_true',
+            default=False,
+            help='List additional fields in output')
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        if parsed_args.long:
+            columns = ('ID', 'Name', 'Extra Specs')
+            column_headers = ('ID', 'Name', 'Properties')
+        else:
+            columns = ('ID', 'Name')
+            column_headers = columns
+        data = self.app.client_manager.volume.volume_types.list()
+        return (column_headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters={'Extra Specs': utils.format_dict},
+                ) for s in data))
+
+
+class SetVolumeType(command.Command):
+    """Set volume type properties"""
+
+    log = logging.getLogger(__name__ + '.SetVolumeType')
+
+    def get_parser(self, prog_name):
+        parser = super(SetVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            'volume_type',
+            metavar='<volume-type>',
+            help='Volume type to modify (name or ID)',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add or modify for this volume type '
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        volume_type = utils.find_resource(
+            volume_client.volume_types, parsed_args.volume_type)
+
+        if parsed_args.property:
+            volume_type.set_keys(parsed_args.property)
+
+        return
+
+
+class UnsetVolumeType(command.Command):
+    """Unset volume type properties"""
+
+    log = logging.getLogger(__name__ + '.UnsetVolumeType')
+
+    def get_parser(self, prog_name):
+        parser = super(UnsetVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            'volume_type',
+            metavar='<volume-type>',
+            help='Volume type to modify (name or ID)',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key>',
+            action='append',
+            default=[],
+            help='Property to remove from volume type '
+                 '(repeat option to remove multiple properties)',
+            required=True,
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        volume_type = utils.find_resource(
+            volume_client.volume_types,
+            parsed_args.volume_type,
+        )
+
+        if parsed_args.property:
+            volume_type.unset_keys(parsed_args.property)
+        else:
+            self.app.log.error("No changes requested\n")
+        return
+
+
+class ShowVolumeType(show.ShowOne):
+    """Display volume type details"""
+
+    log = logging.getLogger(__name__ + ".ShowVolumeType")
+
+    def get_parser(self, prog_name):
+        parser = super(ShowVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            "volume_type",
+            metavar="<volume-type>",
+            help="Volume type to display (name or ID)"
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        volume_type = utils.find_resource(
+            volume_client.volume_types, parsed_args.volume_type)
+        properties = utils.format_dict(volume_type._info.pop('extra_specs'))
+        volume_type._info.update({'properties': properties})
+        return zip(*sorted(six.iteritems(volume_type._info)))
diff -uNrp openstackclient/volume.orig/v2/backup.py openstackclient/volume/v2/backup.py
--- openstackclient/volume.orig/v2/backup.py	1970-01-01 01:00:00.000000000 +0100
+++ openstackclient/volume/v2/backup.py	2015-12-18 16:14:46.000000000 +0100
@@ -0,0 +1,202 @@
+#
+#   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 Backup action implementations"""
+
+import copy
+import logging
+
+from cliff import command
+from cliff import lister
+from cliff import show
+import six
+
+from openstackclient.common import utils
+
+
+class CreateBackup(show.ShowOne):
+    """Create new backup"""
+
+    log = logging.getLogger(__name__ + ".CreateBackup")
+
+    def get_parser(self, prog_name):
+        parser = super(CreateBackup, self).get_parser(prog_name)
+        parser.add_argument(
+            "volume",
+            metavar="<volume>",
+            help="Volume to backup (name or ID)"
+        )
+        parser.add_argument(
+            "--name",
+            metavar="<name>",
+            required=True,
+            help="Name of the backup"
+        )
+        parser.add_argument(
+            "--description",
+            metavar="<description>",
+            help="Description of the backup"
+        )
+        parser.add_argument(
+            "--container",
+            metavar="<container>",
+            help="Optional backup container name"
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        volume_id = utils.find_resource(
+            volume_client.volumes, parsed_args.volume).id
+        backup = volume_client.backups.create(
+            volume_id,
+            container=parsed_args.container,
+            name=parsed_args.name,
+            description=parsed_args.description
+        )
+        backup._info.pop("links", None)
+        return zip(*sorted(six.iteritems(backup._info)))
+
+
+class DeleteBackup(command.Command):
+    """Delete backup(s)"""
+
+    log = logging.getLogger(__name__ + ".DeleteBackup")
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteBackup, self).get_parser(prog_name)
+        parser.add_argument(
+            "backups",
+            metavar="<backup>",
+            nargs="+",
+            help="Backup(s) to delete (name or ID)"
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        for backup in parsed_args.backups:
+            backup_id = utils.find_resource(
+                volume_client.backups, backup).id
+            volume_client.backups.delete(backup_id)
+        return
+
+
+class ListBackup(lister.Lister):
+    """List backups"""
+
+    log = logging.getLogger(__name__ + ".ListBackup")
+
+    def get_parser(self, prog_name):
+        parser = super(ListBackup, self).get_parser(prog_name)
+        parser.add_argument(
+            "--long",
+            action="store_true",
+            default=False,
+            help="List additional fields in output"
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", 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',
+                       'Availability Zone', 'Volume ID', 'Container']
+            column_headers = copy.deepcopy(columns)
+            column_headers[6] = 'Volume'
+        else:
+            columns = ['ID', 'Name', 'Description', 'Status', 'Size']
+            column_headers = 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
+
+        data = self.app.client_manager.volume.backups.list()
+
+        return (column_headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters={'Volume ID': _format_volume_id},
+                ) for s in data))
+
+
+class RestoreBackup(show.ShowOne):
+    """Restore backup"""
+
+    log = logging.getLogger(__name__ + ".RestoreBackup")
+
+    def get_parser(self, prog_name):
+        parser = super(RestoreBackup, self).get_parser(prog_name)
+        parser.add_argument(
+            "backup",
+            metavar="<backup>",
+            help="Backup to restore (ID only)"
+        )
+        parser.add_argument(
+            "volume",
+            metavar="<volume>",
+            help="Volume to restore to (name or ID)"
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        backup = utils.find_resource(volume_client.backups, parsed_args.backup)
+        destination_volume = utils.find_resource(volume_client.volumes,
+                                                 parsed_args.volume)
+        return volume_client.restores.restore(backup.id, destination_volume.id)
+
+
+class ShowBackup(show.ShowOne):
+    """Display backup details"""
+
+    log = logging.getLogger(__name__ + ".ShowBackup")
+
+    def get_parser(self, prog_name):
+        parser = super(ShowBackup, self).get_parser(prog_name)
+        parser.add_argument(
+            "backup",
+            metavar="<backup>",
+            help="Backup to display (name or ID)")
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        backup = utils.find_resource(volume_client.backups,
+                                     parsed_args.backup)
+        backup._info.pop("links", None)
+        return zip(*sorted(six.iteritems(backup._info)))
diff -uNrp openstackclient/volume.orig/v2/qos_specs.py openstackclient/volume/v2/qos_specs.py
--- openstackclient/volume.orig/v2/qos_specs.py	1970-01-01 01:00:00.000000000 +0100
+++ openstackclient/volume/v2/qos_specs.py	2015-12-18 16:14:46.000000000 +0100
@@ -0,0 +1,302 @@
+#   Copyright 2015 iWeb Technologies Inc.
+#
+#   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 QoS action implementations"""
+
+import logging
+import six
+
+from cliff import command
+from cliff import lister
+from cliff import show
+
+from openstackclient.common import parseractions
+from openstackclient.common import utils
+
+
+class AssociateQos(command.Command):
+    """Associate a QoS specification to a volume type"""
+
+    log = logging.getLogger(__name__ + '.AssociateQos')
+
+    def get_parser(self, prog_name):
+        parser = super(AssociateQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to modify (name or ID)',
+        )
+        parser.add_argument(
+            'volume_type',
+            metavar='<volume-type>',
+            help='Volume type to associate the QoS (name or ID)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+        volume_type = utils.find_resource(volume_client.volume_types,
+                                          parsed_args.volume_type)
+
+        volume_client.qos_specs.associate(qos_spec.id, volume_type.id)
+
+        return
+
+
+class CreateQos(show.ShowOne):
+    """Create new QoS specification"""
+
+    log = logging.getLogger(__name__ + '.CreateQos')
+
+    def get_parser(self, prog_name):
+        parser = super(CreateQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'name',
+            metavar='<name>',
+            help='New QoS specification name',
+        )
+        consumer_choices = ['front-end', 'back-end', 'both']
+        parser.add_argument(
+            '--consumer',
+            metavar='<consumer>',
+            choices=consumer_choices,
+            default='both',
+            help='Consumer of the QoS. Valid consumers: %s '
+                 "(defaults to 'both')" % utils.format_list(consumer_choices)
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Set a QoS specification property '
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        specs = {}
+        specs.update({'consumer': parsed_args.consumer})
+
+        if parsed_args.property:
+            specs.update(parsed_args.property)
+
+        qos_spec = volume_client.qos_specs.create(parsed_args.name, specs)
+
+        return zip(*sorted(six.iteritems(qos_spec._info)))
+
+
+class DeleteQos(command.Command):
+    """Delete QoS specification"""
+
+    log = logging.getLogger(__name__ + '.DeleteQos')
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_specs',
+            metavar='<qos-spec>',
+            nargs="+",
+            help='QoS specification(s) to delete (name or ID)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        for qos in parsed_args.qos_specs:
+            qos_spec = utils.find_resource(volume_client.qos_specs, qos)
+            volume_client.qos_specs.delete(qos_spec.id)
+        return
+
+
+class DisassociateQos(command.Command):
+    """Disassociate a QoS specification from a volume type"""
+
+    log = logging.getLogger(__name__ + '.DisassociateQos')
+
+    def get_parser(self, prog_name):
+        parser = super(DisassociateQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to modify (name or ID)',
+        )
+        volume_type_group = parser.add_mutually_exclusive_group()
+        volume_type_group.add_argument(
+            '--volume-type',
+            metavar='<volume-type>',
+            help='Volume type to disassociate the QoS from (name or ID)',
+        )
+        volume_type_group.add_argument(
+            '--all',
+            action='store_true',
+            default=False,
+            help='Disassociate the QoS from every volume type',
+        )
+
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+
+        if parsed_args.volume_type:
+            volume_type = utils.find_resource(volume_client.volume_types,
+                                              parsed_args.volume_type)
+            volume_client.qos_specs.disassociate(qos_spec.id, volume_type.id)
+        elif parsed_args.all:
+            volume_client.qos_specs.disassociate_all(qos_spec.id)
+
+        return
+
+
+class ListQos(lister.Lister):
+    """List QoS specifications"""
+
+    log = logging.getLogger(__name__ + '.ListQos')
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_specs_list = volume_client.qos_specs.list()
+
+        for qos in qos_specs_list:
+            qos_associations = volume_client.qos_specs.get_associations(qos)
+            if qos_associations:
+                associations = [association.name
+                                for association in qos_associations]
+                qos._info.update({'associations': associations})
+
+        columns = ('ID', 'Name', 'Consumer', 'Associations', 'Specs')
+        return (columns,
+                (utils.get_dict_properties(
+                    s._info, columns,
+                    formatters={
+                        'Specs': utils.format_dict,
+                        'Associations': utils.format_list
+                    },
+                ) for s in qos_specs_list))
+
+
+class SetQos(command.Command):
+    """Set QoS specification properties"""
+
+    log = logging.getLogger(__name__ + '.SetQos')
+
+    def get_parser(self, prog_name):
+        parser = super(SetQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to modify (name or ID)',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add or modify for this QoS specification '
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+
+        if parsed_args.property:
+            volume_client.qos_specs.set_keys(qos_spec.id,
+                                             parsed_args.property)
+        else:
+            self.app.log.error("No changes requested\n")
+
+        return
+
+
+class ShowQos(show.ShowOne):
+    """Display QoS specification details"""
+
+    log = logging.getLogger(__name__ + '.ShowQos')
+
+    def get_parser(self, prog_name):
+        parser = super(ShowQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to display (name or ID)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+
+        qos_associations = volume_client.qos_specs.get_associations(qos_spec)
+        if qos_associations:
+            associations = [association.name
+                            for association in qos_associations]
+            qos_spec._info.update({
+                'associations': utils.format_list(associations)
+            })
+        qos_spec._info.update({'specs': utils.format_dict(qos_spec.specs)})
+
+        return zip(*sorted(six.iteritems(qos_spec._info)))
+
+
+class UnsetQos(command.Command):
+    """Unset QoS specification properties"""
+
+    log = logging.getLogger(__name__ + '.SetQos')
+
+    def get_parser(self, prog_name):
+        parser = super(UnsetQos, self).get_parser(prog_name)
+        parser.add_argument(
+            'qos_spec',
+            metavar='<qos-spec>',
+            help='QoS specification to modify (name or ID)',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key>',
+            action='append',
+            default=[],
+            help='Property to remove from the QoS specification. '
+                 '(repeat option to unset multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        qos_spec = utils.find_resource(volume_client.qos_specs,
+                                       parsed_args.qos_spec)
+
+        if parsed_args.property:
+            volume_client.qos_specs.unset_keys(qos_spec.id,
+                                               parsed_args.property)
+        else:
+            self.app.log.error("No changes requested\n")
+
+        return
diff -uNrp openstackclient/volume.orig/v2/snapshot.py openstackclient/volume/v2/snapshot.py
--- openstackclient/volume.orig/v2/snapshot.py	1970-01-01 01:00:00.000000000 +0100
+++ openstackclient/volume/v2/snapshot.py	2015-12-18 16:14:48.000000000 +0100
@@ -0,0 +1,272 @@
+#
+#   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 cliff import command
+from cliff import lister
+from cliff import show
+import six
+
+from openstackclient.common import parseractions
+from openstackclient.common import utils
+
+
+class CreateSnapshot(show.ShowOne):
+    """Create new snapshot"""
+
+    log = logging.getLogger(__name__ + ".CreateSnapshot")
+
+    def get_parser(self, prog_name):
+        parser = super(CreateSnapshot, self).get_parser(prog_name)
+        parser.add_argument(
+            "volume",
+            metavar="<volume>",
+            help="Volume to snapshot (name or ID)"
+        )
+        parser.add_argument(
+            "--name",
+            metavar="<name>",
+            required=True,
+            help="Name of the snapshot"
+        )
+        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):
+        self.log.debug("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        volume_id = utils.find_resource(
+            volume_client.volumes, parsed_args.volume).id
+        snapshot = volume_client.volume_snapshots.create(
+            volume_id,
+            force=parsed_args.force,
+            name=parsed_args.name,
+            description=parsed_args.description
+        )
+        snapshot._info.update(
+            {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+        )
+        return zip(*sorted(six.iteritems(snapshot._info)))
+
+
+class DeleteSnapshot(command.Command):
+    """Delete volume snapshot(s)"""
+
+    log = logging.getLogger(__name__ + ".DeleteSnapshot")
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteSnapshot, 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):
+        self.log.debug("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        for snapshot in parsed_args.snapshots:
+            snapshot_id = utils.find_resource(
+                volume_client.volume_snapshots, snapshot).id
+            volume_client.volume_snapshots.delete(snapshot_id)
+        return
+
+
+class ListSnapshot(lister.Lister):
+    """List snapshots"""
+
+    log = logging.getLogger(__name__ + ".ListSnapshot")
+
+    def get_parser(self, prog_name):
+        parser = super(ListSnapshot, self).get_parser(prog_name)
+        parser.add_argument(
+            '--long',
+            action='store_true',
+            default=False,
+            help='List additional fields in output',
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", 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
+
+        data = self.app.client_manager.volume.volume_snapshots.list()
+        return (column_headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters={'Metadata': utils.format_dict,
+                                'Volume ID': _format_volume_id},
+                ) for s in data))
+
+
+class SetSnapshot(command.Command):
+    """Set snapshot properties"""
+
+    log = logging.getLogger(__name__ + '.SetSnapshot')
+
+    def get_parser(self, prog_name):
+        parser = super(SetSnapshot, 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
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        snapshot = utils.find_resource(volume_client.volume_snapshots,
+                                       parsed_args.snapshot)
+
+        kwargs = {}
+        if parsed_args.name:
+            kwargs['name'] = parsed_args.name
+        if parsed_args.description:
+            kwargs['description'] = parsed_args.description
+
+        if not kwargs and not parsed_args.property:
+            self.app.log.error("No changes requested\n")
+            return
+
+        if parsed_args.property:
+            volume_client.volume_snapshots.set_metadata(snapshot.id,
+                                                        parsed_args.property)
+        volume_client.volume_snapshots.update(snapshot.id, **kwargs)
+        return
+
+
+class ShowSnapshot(show.ShowOne):
+    """Display snapshot details"""
+
+    log = logging.getLogger(__name__ + ".ShowSnapshot")
+
+    def get_parser(self, prog_name):
+        parser = super(ShowSnapshot, 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):
+        self.log.debug("take_action: (%s)", 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 UnsetSnapshot(command.Command):
+    """Unset snapshot properties"""
+
+    log = logging.getLogger(__name__ + '.UnsetSnapshot')
+
+    def get_parser(self, prog_name):
+        parser = super(UnsetSnapshot, 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 to remove multiple values)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    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,
+            )
+        else:
+            self.app.log.error("No changes requested\n")
+        return
diff -uNrp openstackclient/volume.orig/v2/volume.py openstackclient/volume/v2/volume.py
--- openstackclient/volume.orig/v2/volume.py	1970-01-01 01:00:00.000000000 +0100
+++ openstackclient/volume/v2/volume.py	2015-12-18 16:14:48.000000000 +0100
@@ -0,0 +1,427 @@
+#
+#   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 Volume action implementations"""
+
+import copy
+import logging
+import os
+
+from cliff import command
+from cliff import lister
+from cliff import show
+import six
+
+from openstackclient.common import parseractions
+from openstackclient.common import utils
+
+
+class CreateVolume(show.ShowOne):
+    """Create new volume"""
+
+    log = logging.getLogger(__name__ + ".CreateVolume")
+
+    def get_parser(self, prog_name):
+        parser = super(CreateVolume, self).get_parser(prog_name)
+        parser.add_argument(
+            "name",
+            metavar="<name>",
+            help="New volume name"
+        )
+        parser.add_argument(
+            "--size",
+            metavar="<size>",
+            type=int,
+            required=True,
+            help="New volume size in GB"
+        )
+        parser.add_argument(
+            "--snapshot",
+            metavar="<snapshot>",
+            help="Use <snapshot> as source of new volume (name or ID)"
+        )
+        parser.add_argument(
+            "--description",
+            metavar="<description>",
+            help="New volume description"
+        )
+        parser.add_argument(
+            "--type",
+            metavar="<volume-type>",
+            help="Use <volume-type> as the new volume type",
+        )
+        parser.add_argument(
+            '--user',
+            metavar='<user>',
+            help='Specify an alternate user (name or ID)',
+        )
+        parser.add_argument(
+            '--project',
+            metavar='<project>',
+            help='Specify an alternate project (name or ID)',
+        )
+        parser.add_argument(
+            "--availability-zone",
+            metavar="<availability-zone>",
+            help="Create new volume in <availability_zone>"
+        )
+        parser.add_argument(
+            "--image",
+            metavar="<image>",
+            help="Use <image> as source of new volume (name or ID)"
+        )
+        parser.add_argument(
+            "--source",
+            metavar="<volume>",
+            help="Volume to clone (name or ID)"
+        )
+        parser.add_argument(
+            "--property",
+            metavar="<key=value>",
+            action=parseractions.KeyValueAction,
+            help="Set a property to this volume "
+                 "(repeat option to set multiple properties)"
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", parsed_args)
+
+        identity_client = self.app.client_manager.identity
+        volume_client = self.app.client_manager.volume
+        image_client = self.app.client_manager.image
+
+        source_volume = None
+        if parsed_args.source:
+            source_volume = utils.find_resource(
+                volume_client.volumes,
+                parsed_args.source).id
+
+        image = None
+        if parsed_args.image:
+            image = utils.find_resource(
+                image_client.images,
+                parsed_args.image).id
+
+        snapshot = None
+        if parsed_args.snapshot:
+            snapshot = utils.find_resource(
+                volume_client.snapshots,
+                parsed_args.snapshot).id
+
+        project = None
+        if parsed_args.project:
+            project = utils.find_resource(
+                identity_client.projects,
+                parsed_args.project).id
+
+        user = None
+        if parsed_args.user:
+            user = utils.find_resource(
+                identity_client.users,
+                parsed_args.user).id
+
+        volume = volume_client.volumes.create(
+            size=parsed_args.size,
+            snapshot_id=snapshot,
+            name=parsed_args.name,
+            description=parsed_args.description,
+            volume_type=parsed_args.type,
+            user_id=user,
+            project_id=project,
+            availability_zone=parsed_args.availability_zone,
+            metadata=parsed_args.property,
+            imageRef=image,
+            source_volid=source_volume
+        )
+        # Remove key links from being displayed
+        volume._info.update(
+            {
+                'properties': utils.format_dict(volume._info.pop('metadata')),
+                'type': volume._info.pop('volume_type')
+            }
+        )
+        volume._info.pop("links", None)
+        return zip(*sorted(six.iteritems(volume._info)))
+
+
+class DeleteVolume(command.Command):
+    """Delete volume(s)"""
+
+    log = logging.getLogger(__name__ + ".DeleteVolume")
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteVolume, self).get_parser(prog_name)
+        parser.add_argument(
+            "volumes",
+            metavar="<volume>",
+            nargs="+",
+            help="Volume(s) to delete (name or ID)"
+        )
+        parser.add_argument(
+            "--force",
+            dest="force",
+            action="store_true",
+            default=False,
+            help="Attempt forced removal of volume(s), regardless of state "
+                 "(defaults to False"
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        for volume in parsed_args.volumes:
+            volume_obj = utils.find_resource(
+                volume_client.volumes, volume)
+        if parsed_args.force:
+            volume_client.volumes.force_delete(volume_obj.id)
+        else:
+            volume_client.volumes.delete(volume_obj.id)
+        return
+
+
+class ListVolume(lister.Lister):
+    """List volumes"""
+
+    log = logging.getLogger(__name__ + '.ListVolume')
+
+    def get_parser(self, prog_name):
+        parser = super(ListVolume, self).get_parser(prog_name)
+        parser.add_argument(
+            '--all-projects',
+            action='store_true',
+            default=bool(int(os.environ.get("ALL_PROJECTS", 0))),
+            help='Include all projects (admin only)',
+        )
+        parser.add_argument(
+            '--long',
+            action='store_true',
+            default=False,
+            help='List additional fields in output',
+        )
+        parser.add_argument(
+            '--name',
+            metavar='<name>',
+            help='Filter results by name',
+        )
+        parser.add_argument(
+            '--status',
+            metavar='<status>',
+            help='Filter results by status',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+
+        volume_client = self.app.client_manager.volume
+        compute_client = self.app.client_manager.compute
+
+        def _format_attach(attachments):
+            """Return a formatted string of a volume's attached instances
+
+            :param volume: a volume.attachments field
+            :rtype: a string of formatted instances
+            """
+
+            msg = ''
+            for attachment in attachments:
+                server = attachment['server_id']
+                if server in server_cache:
+                    server = server_cache[server].name
+                device = attachment['device']
+                msg += 'Attached to %s on %s ' % (server, device)
+            return msg
+
+        if parsed_args.long:
+            columns = [
+                'ID',
+                'Name',
+                'Status',
+                'Size',
+                'Volume Type',
+                'Bootable',
+                'Attachments',
+                'Metadata',
+            ]
+            column_headers = copy.deepcopy(columns)
+            column_headers[1] = 'Display Name'
+            column_headers[4] = 'Type'
+            column_headers[6] = 'Attached to'
+            column_headers[7] = 'Properties'
+        else:
+            columns = [
+                'ID',
+                'Name',
+                'Status',
+                'Size',
+                'Attachments',
+                ]
+            column_headers = copy.deepcopy(columns)
+            column_headers[1] = 'Display Name'
+            column_headers[4] = 'Attached to'
+
+        # Cache the server list
+        server_cache = {}
+        try:
+            for s in compute_client.servers.list():
+                server_cache[s.id] = s
+        except Exception:
+            # Just forget it if there's any trouble
+            pass
+
+        search_opts = {
+            'all_projects': parsed_args.all_projects,
+            'display_name': parsed_args.name,
+            'status': parsed_args.status,
+        }
+
+        data = volume_client.volumes.list(search_opts=search_opts)
+
+        return (column_headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters={'Metadata': utils.format_dict,
+                                'Attachments': _format_attach},
+                ) for s in data))
+
+
+class SetVolume(show.ShowOne):
+    """Set volume properties"""
+
+    log = logging.getLogger(__name__ + '.SetVolume')
+
+    def get_parser(self, prog_name):
+        parser = super(SetVolume, self).get_parser(prog_name)
+        parser.add_argument(
+            'volume',
+            metavar='<volume>',
+            help='Volume to change (name or ID)',
+        )
+        parser.add_argument(
+            '--name',
+            metavar='<name>',
+            help='New volume name',
+        )
+        parser.add_argument(
+            '--description',
+            metavar='<description>',
+            help='New volume description',
+        )
+        parser.add_argument(
+            '--size',
+            metavar='<size>',
+            type=int,
+            help='Extend volume size in GB',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add or modify for this volume '
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
+
+        if parsed_args.size:
+            if volume.status != 'available':
+                self.app.log.error("Volume is in %s state, it must be "
+                                   "available before size can be extended" %
+                                   volume.status)
+                return
+            if parsed_args.size <= volume.size:
+                self.app.log.error("New size must be greater than %s GB" %
+                                   volume.size)
+                return
+            volume_client.volumes.extend(volume.id, parsed_args.size)
+
+        if parsed_args.property:
+            volume_client.volumes.set_metadata(volume.id, parsed_args.property)
+
+        kwargs = {}
+        if parsed_args.name:
+            kwargs['display_name'] = parsed_args.name
+        if parsed_args.description:
+            kwargs['display_description'] = parsed_args.description
+        if kwargs:
+            volume_client.volumes.update(volume.id, **kwargs)
+
+        if not kwargs and not parsed_args.property and not parsed_args.size:
+            self.app.log.error("No changes requested\n")
+
+        return
+
+
+class ShowVolume(show.ShowOne):
+    """Display volume details"""
+
+    log = logging.getLogger(__name__ + '.ShowVolume')
+
+    def get_parser(self, prog_name):
+        parser = super(ShowVolume, self).get_parser(prog_name)
+        parser.add_argument(
+            'volume',
+            metavar="<volume-id>",
+            help="Volume to display (name or ID)"
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
+
+        # Remove key links from being displayed
+        volume._info.pop("links", None)
+        return zip(*sorted(six.iteritems(volume._info)))
+
+
+class UnsetVolume(command.Command):
+    """Unset volume properties"""
+
+    log = logging.getLogger(__name__ + '.UnsetVolume')
+
+    def get_parser(self, prog_name):
+        parser = super(UnsetVolume, self).get_parser(prog_name)
+        parser.add_argument(
+            'volume',
+            metavar='<volume>',
+            help='Volume to modify (name or ID)',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key>',
+            required=True,
+            action='append',
+            default=[],
+            help='Property to remove from volume '
+                 '(repeat option to remove multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        volume = utils.find_resource(
+            volume_client.volumes, parsed_args.volume)
+
+        volume_client.volumes.delete_metadata(
+            volume.id, parsed_args.property)
+        return
diff -uNrp openstackclient/volume.orig/v2/volume_type.py openstackclient/volume/v2/volume_type.py
--- openstackclient/volume.orig/v2/volume_type.py	1970-01-01 01:00:00.000000000 +0100
+++ openstackclient/volume/v2/volume_type.py	2015-12-18 16:14:48.000000000 +0100
@@ -0,0 +1,261 @@
+#
+#   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 Type action implementations"""
+
+import logging
+
+from cliff import command
+from cliff import lister
+from cliff import show
+import six
+
+from openstackclient.common import parseractions
+from openstackclient.common import utils
+
+
+class CreateVolumeType(show.ShowOne):
+    """Create new volume type"""
+
+    log = logging.getLogger(__name__ + ".CreateVolumeType")
+
+    def get_parser(self, prog_name):
+        parser = super(CreateVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            "name",
+            metavar="<name>",
+            help="New volume type name"
+        )
+        parser.add_argument(
+            "--description",
+            metavar="<description>",
+            help="New volume type description",
+        )
+        public_group = parser.add_mutually_exclusive_group()
+        public_group.add_argument(
+            "--public",
+            dest="public",
+            action="store_true",
+            default=False,
+            help="Volume type is accessible to the public",
+        )
+        public_group.add_argument(
+            "--private",
+            dest="private",
+            action="store_true",
+            default=False,
+            help="Volume type is not accessible to the public",
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add for this volume type'
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+
+        volume_client = self.app.client_manager.volume
+
+        kwargs = {}
+        if parsed_args.public:
+            kwargs['public'] = True
+        if parsed_args.private:
+            kwargs['private'] = True
+
+        volume_type = volume_client.volume_types.create(
+            parsed_args.name,
+            description=parsed_args.description,
+            **kwargs
+        )
+        volume_type._info.pop('extra_specs')
+        if parsed_args.property:
+            result = volume_type.set_keys(parsed_args.property)
+            volume_type._info.update({'properties': utils.format_dict(result)})
+
+        return zip(*sorted(six.iteritems(volume_type._info)))
+
+
+class DeleteVolumeType(command.Command):
+    """Delete volume type"""
+
+    log = logging.getLogger(__name__ + ".DeleteVolumeType")
+
+    def get_parser(self, prog_name):
+        parser = super(DeleteVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            "volume_type",
+            metavar="<volume-type>",
+            help="Volume type to delete (name or ID)"
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.info("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        volume_type = utils.find_resource(
+            volume_client.volume_types, parsed_args.volume_type)
+        volume_client.volume_types.delete(volume_type.id)
+        return
+
+
+class ListVolumeType(lister.Lister):
+    """List volume types"""
+
+    log = logging.getLogger(__name__ + '.ListVolumeType')
+
+    def get_parser(self, prog_name):
+        parser = super(ListVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            '--long',
+            action='store_true',
+            default=False,
+            help='List additional fields in output')
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        if parsed_args.long:
+            columns = ['ID', 'Name', 'Description', 'Extra Specs']
+            column_headers = ['ID', 'Name', 'Description', 'Properties']
+        else:
+            columns = ['ID', 'Name']
+            column_headers = columns
+        data = self.app.client_manager.volume.volume_types.list()
+        return (column_headers,
+                (utils.get_item_properties(
+                    s, columns,
+                    formatters={'Extra Specs': utils.format_dict},
+                ) for s in data))
+
+
+class SetVolumeType(command.Command):
+    """Set volume type properties"""
+
+    log = logging.getLogger(__name__ + '.SetVolumeType')
+
+    def get_parser(self, prog_name):
+        parser = super(SetVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            'volume_type',
+            metavar='<volume-type>',
+            help='Volume type to modify (name or ID)',
+        )
+        parser.add_argument(
+            '--name',
+            metavar='<name>',
+            help='Set volume type name',
+        )
+        parser.add_argument(
+            '--description',
+            metavar='<name>',
+            help='Set volume type description',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key=value>',
+            action=parseractions.KeyValueAction,
+            help='Property to add or modify for this volume type '
+                 '(repeat option to set multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        volume_type = utils.find_resource(
+            volume_client.volume_types, parsed_args.volume_type)
+
+        if (not parsed_args.name
+                and not parsed_args.description
+                and not parsed_args.property):
+            self.app.log.error("No changes requested\n")
+            return
+
+        kwargs = {}
+        if parsed_args.name:
+            kwargs['name'] = parsed_args.name
+        if parsed_args.description:
+            kwargs['description'] = parsed_args.description
+
+        if kwargs:
+            volume_client.volume_types.update(
+                volume_type.id,
+                **kwargs
+            )
+
+        if parsed_args.property:
+            volume_type.set_keys(parsed_args.property)
+
+        return
+
+
+class ShowVolumeType(show.ShowOne):
+    """Display volume type details"""
+
+    log = logging.getLogger(__name__ + ".ShowVolumeType")
+
+    def get_parser(self, prog_name):
+        parser = super(ShowVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            "volume_type",
+            metavar="<volume-type>",
+            help="Volume type to display (name or ID)"
+        )
+        return parser
+
+    def take_action(self, parsed_args):
+        self.log.debug("take_action: (%s)", parsed_args)
+        volume_client = self.app.client_manager.volume
+        volume_type = utils.find_resource(
+            volume_client.volume_types, parsed_args.volume_type)
+        properties = utils.format_dict(volume_type._info.pop('extra_specs'))
+        volume_type._info.update({'properties': properties})
+        return zip(*sorted(six.iteritems(volume_type._info)))
+
+
+class UnsetVolumeType(command.Command):
+    """Unset volume type properties"""
+
+    log = logging.getLogger(__name__ + '.UnsetVolumeType')
+
+    def get_parser(self, prog_name):
+        parser = super(UnsetVolumeType, self).get_parser(prog_name)
+        parser.add_argument(
+            'volume_type',
+            metavar='<volume-type>',
+            help='Volume type to modify (name or ID)',
+        )
+        parser.add_argument(
+            '--property',
+            metavar='<key>',
+            default=[],
+            required=True,
+            help='Property to remove from volume type '
+                 '(repeat option to remove multiple properties)',
+        )
+        return parser
+
+    @utils.log_method(log)
+    def take_action(self, parsed_args):
+        volume_client = self.app.client_manager.volume
+        volume_type = utils.find_resource(
+            volume_client.volume_types,
+            parsed_args.volume_type,
+        )
+        volume_type.unset_keys(parsed_args.property)
+        return
--- python_openstackclient.egg-info/SOURCES.txt.orig	2016-03-22 22:42:18.000000000 +0100
+++ python_openstackclient.egg-info/SOURCES.txt	2016-04-03 10:04:36.301693609 +0200
@@ -261,9 +261,16 @@ openstackclient/volume/__init__.py
 openstackclient/volume/client.py
 openstackclient/volume/v1/__init__.py
 openstackclient/volume/v1/backup.py
+openstackclient/volume/v1/qos_specs.py
 openstackclient/volume/v1/snapshot.py
 openstackclient/volume/v1/type.py
 openstackclient/volume/v1/volume.py
+openstackclient/volume/v2/__init__.py
+openstackclient/volume/v2/backup.py
+openstackclient/volume/v2/qos_specs.py
+openstackclient/volume/v2/snapshot.py
+openstackclient/volume/v2/type.py
+openstackclient/volume/v2/volume.py
 python-openstackclient/locale/python-openstackclient.pot
 python-openstackclient/locale/de/LC_MESSAGES/python-openstackclient.po
 python_openstackclient.egg-info/PKG-INFO
@@ -276,4 +283,4 @@ python_openstackclient.egg-info/requires
 python_openstackclient.egg-info/top_level.txt
 tools/install_venv.py
 tools/install_venv_common.py
-tools/with_venv.sh
\ No newline at end of file
+tools/with_venv.sh
--- setup.cfg.orig	2016-03-22 22:42:18.000000000 +0100
+++ setup.cfg	2016-04-03 10:09:29.610685076 +0200
@@ -288,11 +288,52 @@ openstack.volume.v1 =
 	volume_set = openstackclient.volume.v1.volume:SetVolume
 	volume_show = openstackclient.volume.v1.volume:ShowVolume
 	volume_unset = openstackclient.volume.v1.volume:UnsetVolume
-	volume_type_create = openstackclient.volume.v1.type:CreateVolumeType
-	volume_type_delete = openstackclient.volume.v1.type:DeleteVolumeType
-	volume_type_list = openstackclient.volume.v1.type:ListVolumeType
-	volume_type_set = openstackclient.volume.v1.type:SetVolumeType
-	volume_type_unset = openstackclient.volume.v1.type:UnsetVolumeType
+	volume_type_create = openstackclient.volume.v1.volume_type:CreateVolumeType
+	volume_type_delete = openstackclient.volume.v1.volume_type:DeleteVolumeType
+	volume_type_list = openstackclient.volume.v1.volume_type:ListVolumeType
+	volume_type_set = openstackclient.volume.v1.volume_type:SetVolumeType
+	volume_type_show = openstackclient.volume.v1.volume_type:ShowVolumeType
+	volume_type_unset = openstackclient.volume.v1.volume_type:UnsetVolumeType
+	volume_qos_associate = openstackclient.volume.v1.qos_specs:AssociateQos
+	volume_qos_create = openstackclient.volume.v1.qos_specs:CreateQos
+	volume_qos_delete = openstackclient.volume.v1.qos_specs:DeleteQos
+	volume_qos_disassociate = openstackclient.volume.v1.qos_specs:DisassociateQos
+	volume_qos_list = openstackclient.volume.v1.qos_specs:ListQos
+	volume_qos_set = openstackclient.volume.v1.qos_specs:SetQos
+	volume_qos_show = openstackclient.volume.v1.qos_specs:ShowQos
+	volume_qos_unset = openstackclient.volume.v1.qos_specs:UnsetQos
+openstack.volume.v2 = 
+	backup_create = openstackclient.volume.v2.backup:CreateBackup
+	backup_delete = openstackclient.volume.v2.backup:DeleteBackup
+	backup_list = openstackclient.volume.v2.backup:ListBackup
+	backup_restore = openstackclient.volume.v2.backup:RestoreBackup
+	backup_show = openstackclient.volume.v2.backup:ShowBackup
+	snapshot_create = openstackclient.volume.v2.snapshot:CreateSnapshot
+	snapshot_delete = openstackclient.volume.v2.snapshot:DeleteSnapshot
+	snapshot_list = openstackclient.volume.v2.snapshot:ListSnapshot
+	snapshot_set = openstackclient.volume.v2.snapshot:SetSnapshot
+	snapshot_show = openstackclient.volume.v2.snapshot:ShowSnapshot
+	snapshot_unset = openstackclient.volume.v2.snapshot:UnsetSnapshot
+	volume_create = openstackclient.volume.v2.volume:CreateVolume
+	volume_delete = openstackclient.volume.v2.volume:DeleteVolume
+	volume_list = openstackclient.volume.v2.volume:ListVolume
+	volume_set = openstackclient.volume.v2.volume:SetVolume
+	volume_show = openstackclient.volume.v2.volume:ShowVolume
+	volume_unset = openstackclient.volume.v2.volume:UnsetVolume
+	volume_type_create = openstackclient.volume.v2.volume_type:CreateVolumeType
+	volume_type_delete = openstackclient.volume.v2.volume_type:DeleteVolumeType
+	volume_type_list = openstackclient.volume.v2.volume_type:ListVolumeType
+	volume_type_set = openstackclient.volume.v2.volume_type:SetVolumeType
+	volume_type_show = openstackclient.volume.v2.volume_type:ShowVolumeType
+	volume_type_unset = openstackclient.volume.v2.volume_type:UnsetVolumeType
+	volume_qos_associate = openstackclient.volume.v2.qos_specs:AssociateQos
+	volume_qos_create = openstackclient.volume.v2.qos_specs:CreateQos
+	volume_qos_delete = openstackclient.volume.v2.qos_specs:DeleteQos
+	volume_qos_disassociate = openstackclient.volume.v2.qos_specs:DisassociateQos
+	volume_qos_list = openstackclient.volume.v2.qos_specs:ListQos
+	volume_qos_set = openstackclient.volume.v2.qos_specs:SetQos
+	volume_qos_show = openstackclient.volume.v2.qos_specs:ShowQos
+	volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos
 
 [build_sphinx]
 source-dir = doc/source
--- python_openstackclient.egg-info/entry_points.txt.orig	2016-03-22 22:42:17.000000000 +0100
+++ python_openstackclient.egg-info/entry_points.txt	2016-04-03 10:11:58.565680742 +0200
@@ -271,12 +271,54 @@ snapshot_unset = openstackclient.volume.
 volume_create = openstackclient.volume.v1.volume:CreateVolume
 volume_delete = openstackclient.volume.v1.volume:DeleteVolume
 volume_list = openstackclient.volume.v1.volume:ListVolume
+volume_qos_associate = openstackclient.volume.v1.qos_specs:AssociateQos
+volume_qos_create = openstackclient.volume.v1.qos_specs:CreateQos
+volume_qos_delete = openstackclient.volume.v1.qos_specs:DeleteQos
+volume_qos_disassociate = openstackclient.volume.v1.qos_specs:DisassociateQos
+volume_qos_list = openstackclient.volume.v1.qos_specs:ListQos
+volume_qos_set = openstackclient.volume.v1.qos_specs:SetQos
+volume_qos_show = openstackclient.volume.v1.qos_specs:ShowQos
+volume_qos_unset = openstackclient.volume.v1.qos_specs:UnsetQos
 volume_set = openstackclient.volume.v1.volume:SetVolume
 volume_show = openstackclient.volume.v1.volume:ShowVolume
-volume_type_create = openstackclient.volume.v1.type:CreateVolumeType
-volume_type_delete = openstackclient.volume.v1.type:DeleteVolumeType
-volume_type_list = openstackclient.volume.v1.type:ListVolumeType
-volume_type_set = openstackclient.volume.v1.type:SetVolumeType
-volume_type_unset = openstackclient.volume.v1.type:UnsetVolumeType
+volume_type_create = openstackclient.volume.v1.volume_type:CreateVolumeType
+volume_type_delete = openstackclient.volume.v1.volume_type:DeleteVolumeType
+volume_type_list = openstackclient.volume.v1.volume_type:ListVolumeType
+volume_type_set = openstackclient.volume.v1.volume_type:SetVolumeType
+volume_type_show = openstackclient.volume.v1.volume_type:ShowVolumeType
+volume_type_unset = openstackclient.volume.v1.volume_type:UnsetVolumeType
 volume_unset = openstackclient.volume.v1.volume:UnsetVolume
 
+[openstack.volume.v2]
+backup_create = openstackclient.volume.v2.backup:CreateBackup
+backup_delete = openstackclient.volume.v2.backup:DeleteBackup
+backup_list = openstackclient.volume.v2.backup:ListBackup
+backup_restore = openstackclient.volume.v2.backup:RestoreBackup
+backup_show = openstackclient.volume.v2.backup:ShowBackup
+snapshot_create = openstackclient.volume.v2.snapshot:CreateSnapshot
+snapshot_delete = openstackclient.volume.v2.snapshot:DeleteSnapshot
+snapshot_list = openstackclient.volume.v2.snapshot:ListSnapshot
+snapshot_set = openstackclient.volume.v2.snapshot:SetSnapshot
+snapshot_show = openstackclient.volume.v2.snapshot:ShowSnapshot
+snapshot_unset = openstackclient.volume.v2.snapshot:UnsetSnapshot
+volume_create = openstackclient.volume.v2.volume:CreateVolume
+volume_delete = openstackclient.volume.v2.volume:DeleteVolume
+volume_list = openstackclient.volume.v2.volume:ListVolume
+volume_qos_associate = openstackclient.volume.v2.qos_specs:AssociateQos
+volume_qos_create = openstackclient.volume.v2.qos_specs:CreateQos
+volume_qos_delete = openstackclient.volume.v2.qos_specs:DeleteQos
+volume_qos_disassociate = openstackclient.volume.v2.qos_specs:DisassociateQos
+volume_qos_list = openstackclient.volume.v2.qos_specs:ListQos
+volume_qos_set = openstackclient.volume.v2.qos_specs:SetQos
+volume_qos_show = openstackclient.volume.v2.qos_specs:ShowQos
+volume_qos_unset = openstackclient.volume.v2.qos_specs:UnsetQos
+volume_set = openstackclient.volume.v2.volume:SetVolume
+volume_show = openstackclient.volume.v2.volume:ShowVolume
+volume_type_create = openstackclient.volume.v2.volume_type:CreateVolumeType
+volume_type_delete = openstackclient.volume.v2.volume_type:DeleteVolumeType
+volume_type_list = openstackclient.volume.v2.volume_type:ListVolumeType
+volume_type_set = openstackclient.volume.v2.volume_type:SetVolumeType
+volume_type_show = openstackclient.volume.v2.volume_type:ShowVolumeType
+volume_type_unset = openstackclient.volume.v2.volume_type:UnsetVolumeType
+volume_unset = openstackclient.volume.v2.volume:UnsetVolume
+