LogoopenSUSE Build Service > Projects
Sign Up | Log In

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

Index: python-openstackclient-1.5.0/openstackclient/volume/v2/backup.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/volume/v2/backup.py
+++ python-openstackclient-1.5.0/openstackclient/volume/v2/backup.py
@@ -13,17 +13,64 @@
 #
 
 """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")
@@ -47,8 +94,93 @@ class DeleteBackup(command.Command):
             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")
Index: python-openstackclient-1.5.0/openstackclient/volume/v2/qos_specs.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/volume/v2/qos_specs.py
@@ -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
Index: python-openstackclient-1.5.0/openstackclient/volume/v2/snapshot.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/volume/v2/snapshot.py
+++ python-openstackclient-1.5.0/openstackclient/volume/v2/snapshot.py
@@ -185,10 +185,10 @@ class SetSnapshot(command.Command):
                  '(repeat option to set multiple properties)',
         )
         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)
 
@@ -255,10 +255,10 @@ class UnsetSnapshot(command.Command):
                  '(repeat to remove multiple values)',
         )
         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)
 
Index: python-openstackclient-1.5.0/openstackclient/volume/v2/volume.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/volume/v2/volume.py
+++ python-openstackclient-1.5.0/openstackclient/volume/v2/volume.py
@@ -13,17 +13,150 @@
 #
 
 """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")
@@ -58,8 +191,185 @@ class DeleteVolume(command.Command):
             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')
@@ -72,12 +382,46 @@ class ShowVolume(show.ShowOne):
             help="Volume 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
         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
Index: python-openstackclient-1.5.0/openstackclient/volume/v2/volume_type.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/volume/v2/volume_type.py
+++ python-openstackclient-1.5.0/openstackclient/volume/v2/volume_type.py
@@ -16,14 +16,81 @@
 
 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")
@@ -45,8 +112,99 @@ class DeleteVolumeType(command.Command):
         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")
@@ -64,5 +222,40 @@ class ShowVolumeType(show.ShowOne):
         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
Index: python-openstackclient-1.5.0/python_openstackclient.egg-info/entry_points.txt
===================================================================
--- python-openstackclient-1.5.0.orig/python_openstackclient.egg-info/entry_points.txt
+++ python-openstackclient-1.5.0/python_openstackclient.egg-info/entry_points.txt
@@ -282,27 +282,55 @@ snapshot_show = openstackclient.volume.v
 snapshot_unset = openstackclient.volume.v1.snapshot:UnsetSnapshot
 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
 
Index: python-openstackclient-1.5.0/setup.cfg
===================================================================
--- python-openstackclient-1.5.0.orig/setup.cfg
+++ python-openstackclient-1.5.0/setup.cfg
@@ -299,26 +299,54 @@ openstack.volume.v1 =
 	volume_list = openstackclient.volume.v1.volume:ListVolume
 	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
 build-dir = doc/build
Index: python-openstackclient-1.5.0/python_openstackclient.egg-info/SOURCES.txt
===================================================================
--- python-openstackclient-1.5.0.orig/python_openstackclient.egg-info/SOURCES.txt
+++ python-openstackclient-1.5.0/python_openstackclient.egg-info/SOURCES.txt
@@ -288,12 +288,14 @@ openstackclient/volume/__init__.py
 openstackclient/volume/client.py
 openstackclient/volume/v1/__init__.py
 openstackclient/volume/v1/backup.py
 openstackclient/volume/v1/snapshot.py
-openstackclient/volume/v1/type.py
+openstackclient/volume/v1/qos_specs.py
+openstackclient/volume/v1/volume_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/volume.py
 openstackclient/volume/v2/volume_type.py
 python-openstackclient/locale/python-openstackclient.pot
@@ -305,5 +307,5 @@ python_openstackclient.egg-info/dependen
 python_openstackclient.egg-info/entry_points.txt
 python_openstackclient.egg-info/not-zip-safe
 python_openstackclient.egg-info/pbr.json
 python_openstackclient.egg-info/requires.txt
-python_openstackclient.egg-info/top_level.txt
\ No newline at end of file
+python_openstackclient.egg-info/top_level.txt
Index: python-openstackclient-1.5.0/openstackclient/volume/client.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/volume/client.py
+++ python-openstackclient-1.5.0/openstackclient/volume/client.py
@@ -18,9 +18,9 @@ import logging
 from openstackclient.common import utils
 
 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",
@@ -52,13 +52,17 @@ def make_client(instance):
     http_log_debug = utils.get_effective_log_level() <= logging.DEBUG
 
     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
 
@@ -67,11 +71,9 @@ def build_option_parser(parser):
     """Hook to add global options"""
     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
Index: python-openstackclient-1.5.0/openstackclient/common/utils.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/common/utils.py
+++ python-openstackclient-1.5.0/openstackclient/common/utils.py
@@ -25,8 +25,31 @@ from oslo_utils import importutils
 
 from openstackclient.common import exceptions
 
 
+def log_method(log, level=logging.DEBUG):
+    """Logs a method and its arguments when entered."""
+
+    def decorator(func):
+        func_name = func.__name__
+
+        @six.wraps(func)
+        def wrapper(self, *args, **kwargs):
+            if log.isEnabledFor(level):
+                pretty_args = []
+                if args:
+                    pretty_args.extend(str(a) for a in args)
+                if kwargs:
+                    pretty_args.extend(
+                        "%s=%s" % (k, v) for k, v in six.iteritems(kwargs))
+                log.log(level, "%s(%s)", func_name, ", ".join(pretty_args))
+            return func(self, *args, **kwargs)
+
+        return wrapper
+
+    return decorator
+
+
 def find_resource(manager, name_or_id, **kwargs):
     """Helper for the _find_* methods.
 
     :param manager: A client manager class
@@ -50,9 +73,9 @@ def find_resource(manager, name_or_id, *
 
     # Try to get entity as integer id
     try:
         if isinstance(name_or_id, int) or name_or_id.isdigit():
-            return manager.get(int(name_or_id))
+            return manager.get(int(name_or_id), **kwargs)
     # FIXME(dtroyer): The exception to catch here is dependent on which
     #                 client library the manager passed in belongs to.
     #                 Eventually this should be pulled from a common set
     #                 of client exceptions.
@@ -63,21 +86,24 @@ def find_resource(manager, name_or_id, *
             raise
 
     # Try directly using the passed value
     try:
-        return manager.get(name_or_id)
+        return manager.get(name_or_id, **kwargs)
     except Exception:
         pass
 
     if len(kwargs) == 0:
         kwargs = {}
 
-    # Prepare the kwargs for calling find
-    if 'NAME_ATTR' in manager.resource_class.__dict__:
-        # novaclient does this for oddball resources
-        kwargs[manager.resource_class.NAME_ATTR] = name_or_id
-    else:
-        kwargs['name'] = name_or_id
+    try:
+        # Prepare the kwargs for calling find
+        if 'NAME_ATTR' in manager.resource_class.__dict__:
+            # novaclient does this for oddball resources
+            kwargs[manager.resource_class.NAME_ATTR] = name_or_id
+        else:
+            kwargs['name'] = name_or_id
+    except Exception:
+        pass
 
     # finally try to find entity by name
     try:
         return manager.find(**kwargs)
@@ -94,9 +120,26 @@ def find_resource(manager, name_or_id, *
             msg = "More than one %s exists with the name '%s'." % \
                 (manager.resource_class.__name__.lower(), name_or_id)
             raise exceptions.CommandError(msg)
         else:
-            raise
+            pass
+
+    try:
+        for resource in manager.list():
+            # short circuit and return the first match
+            if (resource.get('id') == name_or_id or
+                    resource.get('name') == name_or_id):
+                return resource
+        else:
+            # we found no match, keep going to bomb out
+            pass
+    except Exception:
+        # in case the list fails for some reason
+        pass
+
+    # if we hit here, we've failed, report back this error:
+    msg = "Could not find resource %s" % name_or_id
+    raise exceptions.CommandError(msg)
 
 
 def format_dict(data):
     """Return a formatted string of key value pairs
@@ -185,11 +228,11 @@ def sort_items(items, sort_str):
     """Sort items based on sort keys and sort directions given by sort_str.
 
     :param items: a list or generator object of items
     :param sort_str: a string defining the sort rules, the format is
-    '<key1>:[direction1],<key2>:[direction2]...', direction can be 'asc'
-    for ascending or 'desc' for descending, if direction is not given,
-    it's ascending by default
+        '<key1>:[direction1],<key2>:[direction2]...', direction can be 'asc'
+        for ascending or 'desc' for descending, if direction is not given,
+        it's ascending by default
     :return: sorted items
     """
     if not sort_str:
         return items
@@ -367,4 +410,12 @@ def read_blob_file_contents(blob_file):
         return blob
     except IOError:
         msg = "Error occurred trying to read from file %s"
         raise exceptions.CommandError(msg % blob_file)
+
+
+def build_kwargs_dict(arg_name, value):
+    """Return a dictionary containing `arg_name` if `value` is set."""
+    kwargs = {}
+    if value:
+        kwargs[arg_name] = value
+    return kwargs
Index: python-openstackclient-1.5.0/openstackclient/volume/v1/backup.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/volume/v1/backup.py
+++ python-openstackclient-1.5.0/openstackclient/volume/v1/backup.py
@@ -56,10 +56,10 @@ class CreateBackup(show.ShowOne):
             help='Description of the backup',
         )
         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(
@@ -87,10 +87,10 @@ class DeleteBackup(command.Command):
             help='Backup(s) to delete (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
         for backup in parsed_args.backups:
             backup_id = utils.find_resource(volume_client.backups,
                                             backup).id
@@ -112,10 +112,10 @@ class ListBackup(lister.Lister):
             help='List additional fields in output',
         )
         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
 
@@ -171,10 +171,10 @@ class RestoreBackup(command.Command):
             metavar='<volume>',
             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)
         destination_volume = utils.find_resource(volume_client.volumes,
@@ -195,10 +195,10 @@ class ShowBackup(show.ShowOne):
             metavar='<backup>',
             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)
         backup._info.pop('links')
Index: python-openstackclient-1.5.0/openstackclient/volume/v1/qos_specs.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/volume/v1/qos_specs.py
@@ -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
Index: python-openstackclient-1.5.0/openstackclient/volume/v1/snapshot.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/volume/v1/snapshot.py
+++ python-openstackclient-1.5.0/openstackclient/volume/v1/snapshot.py
@@ -58,10 +58,10 @@ class CreateSnapshot(show.ShowOne):
             help='Create a snapshot attached to an instance. Default is False',
         )
         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
         snapshot = volume_client.volume_snapshots.create(
@@ -92,10 +92,10 @@ class DeleteSnapshot(command.Command):
             help='Snapshot(s) to delete (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
         for snapshot in parsed_args.snapshots:
             snapshot_id = utils.find_resource(volume_client.volume_snapshots,
                                               snapshot).id
@@ -117,10 +117,10 @@ class ListSnapshot(lister.Lister):
             help='List additional fields in output',
         )
         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
 
@@ -193,10 +193,10 @@ class SetSnapshot(command.Command):
                  '(repeat option to set multiple properties)',
         )
         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)
 
@@ -230,10 +230,10 @@ class ShowSnapshot(show.ShowOne):
             metavar='<snapshot>',
             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)
 
@@ -262,13 +262,14 @@ class UnsetSnapshot(command.Command):
             action='append',
             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)
 
Index: python-openstackclient-1.5.0/openstackclient/volume/v1/type.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/volume/v1/type.py
+++ /dev/null
@@ -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
Index: python-openstackclient-1.5.0/openstackclient/volume/v1/volume.py
===================================================================
--- python-openstackclient-1.5.0.orig/openstackclient/volume/v1/volume.py
+++ python-openstackclient-1.5.0/openstackclient/volume/v1/volume.py
@@ -101,10 +101,10 @@ 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
         volume_client = self.app.client_manager.volume
@@ -185,10 +185,10 @@ class DeleteVolume(command.Command):
                  '(defaults to False)',
         )
         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(
                 volume_client.volumes, volume)
@@ -229,10 +229,10 @@ class ListVolume(lister.Lister):
             help='List additional fields in output',
         )
         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
 
@@ -350,10 +350,10 @@ class SetVolume(command.Command):
                  '(repeat option to set multiple properties)',
         )
         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)
 
         if parsed_args.size:
@@ -398,10 +398,10 @@ class ShowVolume(show.ShowOne):
             help='Volume 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
         volume = utils.find_resource(volume_client.volumes, parsed_args.volume)
         # Map 'metadata' column to 'properties'
         volume._info.update(
@@ -436,13 +436,14 @@ class UnsetVolume(command.Command):
             action='append',
             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)
 
Index: python-openstackclient-1.5.0/openstackclient/volume/v1/volume_type.py
===================================================================
--- /dev/null
+++ python-openstackclient-1.5.0/openstackclient/volume/v1/volume_type.py
@@ -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)))