summaryrefslogtreecommitdiff
path: root/openstackclient/volume
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient/volume')
-rw-r--r--openstackclient/volume/v1/volume_backup.py19
-rw-r--r--openstackclient/volume/v1/volume_type.py2
-rw-r--r--openstackclient/volume/v2/volume_backup.py44
-rw-r--r--openstackclient/volume/v2/volume_snapshot.py2
-rw-r--r--openstackclient/volume/v3/block_storage_cluster.py281
-rw-r--r--openstackclient/volume/v3/block_storage_resource_filter.py83
-rw-r--r--openstackclient/volume/v3/volume_attachment.py23
7 files changed, 432 insertions, 22 deletions
diff --git a/openstackclient/volume/v1/volume_backup.py b/openstackclient/volume/v1/volume_backup.py
index 1a83a3c0..790cb463 100644
--- a/openstackclient/volume/v1/volume_backup.py
+++ b/openstackclient/volume/v1/volume_backup.py
@@ -231,18 +231,23 @@ class RestoreVolumeBackup(command.Command):
parser.add_argument(
'volume',
metavar='<volume>',
- help=_('Volume to restore to (name or ID)')
+ nargs='?',
+ help=_('Volume to restore to (name or ID) (default to None)')
)
return parser
def take_action(self, 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)
+ backup = utils.find_resource(
+ volume_client.backups, parsed_args.backup,
+ )
+ volume_id = None
+ if parsed_args.volume is not None:
+ volume_id = utils.find_resource(
+ volume_client.volumes,
+ parsed_args.volume,
+ ).id
+ return volume_client.restores.restore(backup.id, volume_id)
class ShowVolumeBackup(command.ShowOne):
diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py
index 4f015d13..c584943e 100644
--- a/openstackclient/volume/v1/volume_type.py
+++ b/openstackclient/volume/v1/volume_type.py
@@ -411,7 +411,7 @@ class UnsetVolumeType(command.Command):
"--encryption-type",
action="store_true",
help=_("Remove the encryption type for this volume type "
- "(admin oly)"),
+ "(admin only)"),
)
return parser
diff --git a/openstackclient/volume/v2/volume_backup.py b/openstackclient/volume/v2/volume_backup.py
index f2d89dc7..d96b28e9 100644
--- a/openstackclient/volume/v2/volume_backup.py
+++ b/openstackclient/volume/v2/volume_backup.py
@@ -363,18 +363,50 @@ class RestoreVolumeBackup(command.ShowOne):
parser.add_argument(
"volume",
metavar="<volume>",
- help=_("Volume to restore to (name or ID)")
+ nargs="?",
+ help=_(
+ "Volume to restore to "
+ "(name or ID for existing volume, name only for new volume) "
+ "(default to None)"
+ )
+ )
+ parser.add_argument(
+ "--force",
+ action="store_true",
+ help=_(
+ "Restore the backup to an existing volume "
+ "(default to False)"
+ )
)
return parser
def take_action(self, 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)
- backup = volume_client.restores.restore(backup.id,
- destination_volume.id)
- return zip(*sorted(backup._info.items()))
+
+ volume_name = None
+ volume_id = None
+ try:
+ volume_id = utils.find_resource(
+ volume_client.volumes,
+ parsed_args.volume,
+ ).id
+ except Exception:
+ volume_name = parsed_args.volume
+ else:
+ # If we didn't fail, the volume must already exist. We only allow
+ # this to work if the user forced things
+ if not parsed_args.force:
+ msg = _(
+ "Volume '%s' already exists; if you want to restore the "
+ "backup to it you need to specify the '--force' option"
+ ) % parsed_args.volume
+ raise exceptions.CommandError(msg)
+
+ return volume_client.restores.restore(
+ backup.id, volume_id, volume_name,
+ )
class SetVolumeBackup(command.Command):
diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py
index 656f59d4..53d8d27f 100644
--- a/openstackclient/volume/v2/volume_snapshot.py
+++ b/openstackclient/volume/v2/volume_snapshot.py
@@ -98,7 +98,7 @@ class CreateVolumeSnapshot(command.ShowOne):
"--remote-source",
metavar="<key=value>",
action=parseractions.KeyValueAction,
- help=_("The attribute(s) of the exsiting remote volume snapshot "
+ help=_("The attribute(s) of the existing remote volume snapshot "
"(admin required) (repeat option to specify multiple "
"attributes) e.g.: '--remote-source source-name=test_name "
"--remote-source source-id=test_id'"),
diff --git a/openstackclient/volume/v3/block_storage_cluster.py b/openstackclient/volume/v3/block_storage_cluster.py
new file mode 100644
index 00000000..34b25efc
--- /dev/null
+++ b/openstackclient/volume/v3/block_storage_cluster.py
@@ -0,0 +1,281 @@
+# 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.
+
+from cinderclient import api_versions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
+from openstackclient.i18n import _
+
+
+def _format_cluster(cluster, detailed=False):
+ columns = (
+ 'name',
+ 'binary',
+ 'state',
+ 'status',
+ )
+ column_headers = (
+ 'Name',
+ 'Binary',
+ 'State',
+ 'Status',
+ )
+
+ if detailed:
+ columns += (
+ 'disabled_reason',
+ 'num_hosts',
+ 'num_down_hosts',
+ 'last_heartbeat',
+ 'created_at',
+ 'updated_at',
+ # optional columns, depending on whether replication is enabled
+ 'replication_status',
+ 'frozen',
+ 'active_backend_id',
+ )
+ column_headers += (
+ 'Disabled Reason',
+ 'Hosts',
+ 'Down Hosts',
+ 'Last Heartbeat',
+ 'Created At',
+ 'Updated At',
+ # optional columns, depending on whether replication is enabled
+ 'Replication Status',
+ 'Frozen',
+ 'Active Backend ID',
+ )
+
+ return (
+ column_headers,
+ utils.get_item_properties(
+ cluster,
+ columns,
+ ),
+ )
+
+
+class ListBlockStorageCluster(command.Lister):
+ """List block storage clusters.
+
+ This command requires ``--os-volume-api-version`` 3.7 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ '--cluster', metavar='<name>', default=None,
+ help=_(
+ 'Filter by cluster name, without backend will list '
+ 'all clustered services from the same cluster.'
+ ),
+ )
+ parser.add_argument(
+ '--binary',
+ metavar='<binary>',
+ help=_('Cluster binary.'),
+ )
+ parser.add_argument(
+ '--up',
+ action='store_true',
+ dest='is_up',
+ default=None,
+ help=_('Filter by up status.'),
+ )
+ parser.add_argument(
+ '--down',
+ action='store_false',
+ dest='is_up',
+ help=_('Filter by down status.'),
+ )
+ parser.add_argument(
+ '--disabled',
+ action='store_true',
+ dest='is_disabled',
+ default=None,
+ help=_('Filter by disabled status.'),
+ )
+ parser.add_argument(
+ '--enabled',
+ action='store_false',
+ dest='is_disabled',
+ help=_('Filter by enabled status.'),
+ )
+ parser.add_argument(
+ '--num-hosts',
+ metavar='<hosts>',
+ type=int,
+ default=None,
+ help=_('Filter by number of hosts in the cluster.'),
+ )
+ parser.add_argument(
+ '--num-down-hosts',
+ metavar='<hosts>',
+ type=int,
+ default=None,
+ help=_('Filter by number of hosts that are down.'),
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help=_("List additional fields in output")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.7'):
+ msg = _(
+ "--os-volume-api-version 3.7 or greater is required to "
+ "support the 'block storage cluster list' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ columns = ('Name', 'Binary', 'State', 'Status')
+ if parsed_args.long:
+ columns += (
+ 'Num Hosts',
+ 'Num Down Hosts',
+ 'Last Heartbeat',
+ 'Disabled Reason',
+ 'Created At',
+ 'Updated At',
+ )
+
+ data = volume_client.clusters.list(
+ name=parsed_args.cluster,
+ binary=parsed_args.binary,
+ is_up=parsed_args.is_up,
+ disabled=parsed_args.is_disabled,
+ num_hosts=parsed_args.num_hosts,
+ num_down_hosts=parsed_args.num_down_hosts,
+ detailed=parsed_args.long,
+ )
+
+ return (
+ columns,
+ (utils.get_item_properties(s, columns) for s in data),
+ )
+
+
+class SetBlockStorageCluster(command.Command):
+ """Set block storage cluster properties.
+
+ This command requires ``--os-volume-api-version`` 3.7 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'cluster',
+ metavar='<cluster>',
+ help=_('Name of block storage cluster to update (name only)')
+ )
+ parser.add_argument(
+ '--binary',
+ metavar='<binary>',
+ default='cinder-volume',
+ help=_(
+ "Name of binary to filter by; defaults to 'cinder-volume' "
+ "(optional)"
+ )
+ )
+ enabled_group = parser.add_mutually_exclusive_group()
+ enabled_group.add_argument(
+ '--enable',
+ action='store_false',
+ dest='disabled',
+ default=None,
+ help=_('Enable cluster')
+ )
+ enabled_group.add_argument(
+ '--disable',
+ action='store_true',
+ dest='disabled',
+ help=_('Disable cluster')
+ )
+ parser.add_argument(
+ '--disable-reason',
+ metavar='<reason>',
+ dest='disabled_reason',
+ help=_(
+ 'Reason for disabling the cluster '
+ '(should be used with --disable option)'
+ )
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.7'):
+ msg = _(
+ "--os-volume-api-version 3.7 or greater is required to "
+ "support the 'block storage cluster set' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ if parsed_args.disabled_reason and not parsed_args.disabled:
+ msg = _("Cannot specify --disable-reason without --disable")
+ raise exceptions.CommandError(msg)
+
+ cluster = volume_client.clusters.update(
+ parsed_args.cluster,
+ parsed_args.binary,
+ disabled=parsed_args.disabled,
+ disabled_reason=parsed_args.disabled_reason,
+ )
+
+ return _format_cluster(cluster, detailed=True)
+
+
+class ShowBlockStorageCluster(command.ShowOne):
+ """Show detailed information for a block storage cluster.
+
+ This command requires ``--os-volume-api-version`` 3.7 or greater.
+ """
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'cluster',
+ metavar='<cluster>',
+ help=_('Name of block storage cluster.'),
+ )
+ parser.add_argument(
+ '--binary',
+ metavar='<binary>',
+ help=_('Service binary.'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.7'):
+ msg = _(
+ "--os-volume-api-version 3.7 or greater is required to "
+ "support the 'block storage cluster show' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ cluster = volume_client.clusters.show(
+ parsed_args.cluster,
+ binary=parsed_args.binary,
+ )
+
+ return _format_cluster(cluster, detailed=True)
diff --git a/openstackclient/volume/v3/block_storage_resource_filter.py b/openstackclient/volume/v3/block_storage_resource_filter.py
new file mode 100644
index 00000000..4bcacf90
--- /dev/null
+++ b/openstackclient/volume/v3/block_storage_resource_filter.py
@@ -0,0 +1,83 @@
+# 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 V3 Resource Filters implementations"""
+
+from cinderclient import api_versions
+from osc_lib.command import command
+from osc_lib import exceptions
+from osc_lib import utils
+
+from openstackclient.i18n import _
+
+
+class ListBlockStorageResourceFilter(command.Lister):
+ _description = _('List block storage resource filters')
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.33'):
+ msg = _(
+ "--os-volume-api-version 3.33 or greater is required to "
+ "support the 'block storage resource filter list' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ column_headers = (
+ 'Resource',
+ 'Filters',
+ )
+
+ data = volume_client.resource_filters.list()
+
+ return (
+ column_headers,
+ (utils.get_item_properties(s, column_headers) for s in data)
+ )
+
+
+class ShowBlockStorageResourceFilter(command.ShowOne):
+ _description = _('Show filters for a block storage resource type')
+
+ def get_parser(self, prog_name):
+ parser = super().get_parser(prog_name)
+ parser.add_argument(
+ 'resource',
+ metavar='<resource>',
+ help=_('Resource to show filters for (name).')
+ )
+
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+
+ if volume_client.api_version < api_versions.APIVersion('3.33'):
+ msg = _(
+ "--os-volume-api-version 3.33 or greater is required to "
+ "support the 'block storage resource filter show' command"
+ )
+ raise exceptions.CommandError(msg)
+
+ data = volume_client.resource_filters.list(
+ resource=parsed_args.resource
+ )
+ if not data:
+ msg = _(
+ "No resource filter with a name of {parsed_args.resource}' "
+ "exists."
+ )
+ raise exceptions.CommandError(msg)
+ resource_filter = next(data)
+
+ return zip(*sorted(resource_filter._info.items()))
diff --git a/openstackclient/volume/v3/volume_attachment.py b/openstackclient/volume/v3/volume_attachment.py
index 39a9c37f..c7401837 100644
--- a/openstackclient/volume/v3/volume_attachment.py
+++ b/openstackclient/volume/v3/volume_attachment.py
@@ -51,18 +51,27 @@ def _format_attachment(attachment):
'Properties',
)
- # TODO(stephenfin): Improve output with the nested connection_info
- # field - cinderclient printed two things but that's equally ugly
- return (
- column_headers,
- utils.get_item_properties(
+ # VolumeAttachmentManager.create returns a dict while everything else
+ # returns a VolumeAttachment object
+ if isinstance(attachment, dict):
+ data = []
+ for column in columns:
+ if column == 'connection_info':
+ data.append(format_columns.DictColumn(attachment[column]))
+ continue
+ data.append(attachment[column])
+ else:
+ data = utils.get_item_properties(
attachment,
columns,
formatters={
'connection_info': format_columns.DictColumn,
},
- ),
- )
+ )
+
+ # TODO(stephenfin): Improve output with the nested connection_info
+ # field - cinderclient printed two things but that's equally ugly
+ return (column_headers, data)
class CreateVolumeAttachment(command.ShowOne):