1.change the command name ``snapshot create/delete/list/ show/set/unset`` to ``volume snapshot create/delete/list/ show/set/unset``. 2.change the optional parameter "--name <name>" to a positional parameter "<snapshot-name>"; Change the positional parameter "<volume>" to a optional parameter "--volume <volume>" Change-Id: If03276ecdf6f0d96893d5ecf91c2aaa64929cff3 Implements: bp backup-snapshot-renamed-for-volume-resource Co-Authored-By: Sheel Rana <ranasheel2000@gmail.com>changes/54/369854/6
parent
970b0e0005
commit
23ee2fd8f0
@ -0,0 +1,171 @@
|
||||
===============
|
||||
volume snapshot
|
||||
===============
|
||||
|
||||
Block Storage v1, v2
|
||||
|
||||
volume snapshot create
|
||||
----------------------
|
||||
|
||||
Create new volume snapshot
|
||||
|
||||
.. program:: volume snapshot create
|
||||
.. code:: bash
|
||||
|
||||
os volume snapshot create
|
||||
[--volume <volume>]
|
||||
[--description <description>]
|
||||
[--force]
|
||||
[--property <key=value> [...] ]
|
||||
<snapshot-name>
|
||||
|
||||
.. option:: --volume <volume>
|
||||
|
||||
Volume to snapshot (name or ID) (default is <snapshot-name>)
|
||||
|
||||
.. option:: --description <description>
|
||||
|
||||
Description of the snapshot
|
||||
|
||||
.. option:: --force
|
||||
|
||||
Create a snapshot attached to an instance. Default is False
|
||||
|
||||
.. option:: --property <key=value>
|
||||
|
||||
Set a property to this snapshot (repeat option to set multiple properties)
|
||||
|
||||
*Volume version 2 only*
|
||||
|
||||
.. _volume_snapshot_create-snapshot-name:
|
||||
.. describe:: <snapshot-name>
|
||||
|
||||
Name of the new snapshot (default to None)
|
||||
|
||||
volume snapshot delete
|
||||
----------------------
|
||||
|
||||
Delete volume snapshot(s)
|
||||
|
||||
.. program:: volume snapshot delete
|
||||
.. code:: bash
|
||||
|
||||
os volume snapshot delete
|
||||
<snapshot> [<snapshot> ...]
|
||||
|
||||
.. _volume_snapshot_delete-snapshot:
|
||||
.. describe:: <snapshot>
|
||||
|
||||
Snapshot(s) to delete (name or ID)
|
||||
|
||||
volume snapshot list
|
||||
--------------------
|
||||
|
||||
List volume snapshots
|
||||
|
||||
.. program:: volume snapshot list
|
||||
.. code:: bash
|
||||
|
||||
os volume snapshot list
|
||||
[--all-projects]
|
||||
[--long]
|
||||
[--limit <limit>]
|
||||
[--marker <marker>]
|
||||
|
||||
.. option:: --all-projects
|
||||
|
||||
Include all projects (admin only)
|
||||
|
||||
.. option:: --long
|
||||
|
||||
List additional fields in output
|
||||
|
||||
.. option:: --limit <limit>
|
||||
|
||||
Maximum number of snapshots to display
|
||||
|
||||
*Volume version 2 only*
|
||||
|
||||
.. option:: --marker <marker>
|
||||
|
||||
The last snapshot ID of the previous page
|
||||
|
||||
*Volume version 2 only*
|
||||
|
||||
volume snapshot set
|
||||
-------------------
|
||||
|
||||
Set volume snapshot properties
|
||||
|
||||
.. program:: volume snapshot set
|
||||
.. code:: bash
|
||||
|
||||
os volume snapshot set
|
||||
[--name <name>]
|
||||
[--description <description>]
|
||||
[--property <key=value> [...] ]
|
||||
[--state <state>]
|
||||
<snapshot>
|
||||
|
||||
.. option:: --name <name>
|
||||
|
||||
New snapshot name
|
||||
|
||||
.. option:: --description <description>
|
||||
|
||||
New snapshot description
|
||||
|
||||
.. option:: --property <key=value>
|
||||
|
||||
Property to add or modify for this snapshot (repeat option to set multiple properties)
|
||||
|
||||
.. option:: --state <state>
|
||||
|
||||
New snapshot state.
|
||||
("available", "error", "creating", "deleting", or "error_deleting") (admin only)
|
||||
(This option simply changes the state of the snapshot in the database with
|
||||
no regard to actual status, exercise caution when using)
|
||||
|
||||
*Volume version 2 only*
|
||||
|
||||
.. _volume_snapshot_set-snapshot:
|
||||
.. describe:: <snapshot>
|
||||
|
||||
Snapshot to modify (name or ID)
|
||||
|
||||
volume snapshot show
|
||||
--------------------
|
||||
|
||||
Display volume snapshot details
|
||||
|
||||
.. program:: volume snapshot show
|
||||
.. code:: bash
|
||||
|
||||
os volume snapshot show
|
||||
<snapshot>
|
||||
|
||||
.. _volume_snapshot_show-snapshot:
|
||||
.. describe:: <snapshot>
|
||||
|
||||
Snapshot to display (name or ID)
|
||||
|
||||
volume snapshot unset
|
||||
---------------------
|
||||
|
||||
Unset volume snapshot properties
|
||||
|
||||
.. program:: volume snapshot unset
|
||||
.. code:: bash
|
||||
|
||||
os volume snapshot unset
|
||||
[--property <key>]
|
||||
<snapshot>
|
||||
|
||||
.. option:: --property <key>
|
||||
|
||||
Property to remove from snapshot (repeat option to remove multiple properties)
|
||||
|
||||
.. _volume_snapshot_unset-snapshot:
|
||||
.. describe:: <snapshot>
|
||||
|
||||
Snapshot to modify (name or ID)
|
@ -0,0 +1,305 @@
|
||||
# Copyright 2012-2013 OpenStack Foundation
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""Volume v1 Snapshot action implementations"""
|
||||
|
||||
import copy
|
||||
import logging
|
||||
|
||||
from osc_lib.cli import parseractions
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions
|
||||
from osc_lib import utils
|
||||
import six
|
||||
|
||||
from openstackclient.i18n import _
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateVolumeSnapshot(command.ShowOne):
|
||||
"""Create new volume snapshot"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateVolumeSnapshot, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'snapshot_name',
|
||||
metavar='<snapshot-name>',
|
||||
nargs="?",
|
||||
help=_('Name of the snapshot (default to None)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--volume',
|
||||
metavar='<volume>',
|
||||
help=_('Volume to snapshot (name or ID) '
|
||||
'(default is <snapshot-name>)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
metavar='<description>',
|
||||
help=_('Description of the snapshot'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--force',
|
||||
dest='force',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Create a snapshot attached to an instance. '
|
||||
'Default is False'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
volume = parsed_args.volume
|
||||
if not parsed_args.volume:
|
||||
volume = parsed_args.snapshot_name
|
||||
volume_id = utils.find_resource(volume_client.volumes,
|
||||
volume).id
|
||||
snapshot = volume_client.volume_snapshots.create(
|
||||
volume_id,
|
||||
parsed_args.force,
|
||||
parsed_args.snapshot_name,
|
||||
parsed_args.description
|
||||
)
|
||||
|
||||
snapshot._info.update(
|
||||
{'properties': utils.format_dict(snapshot._info.pop('metadata'))}
|
||||
)
|
||||
|
||||
return zip(*sorted(six.iteritems(snapshot._info)))
|
||||
|
||||
|
||||
class DeleteVolumeSnapshot(command.Command):
|
||||
"""Delete volume snapshot(s)"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'snapshots',
|
||||
metavar='<snapshot>',
|
||||
nargs="+",
|
||||
help=_('Snapshot(s) to delete (name or ID)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
result = 0
|
||||
|
||||
for i in parsed_args.snapshots:
|
||||
try:
|
||||
snapshot_id = utils.find_resource(
|
||||
volume_client.volume_snapshots, i).id
|
||||
volume_client.volume_snapshots.delete(snapshot_id)
|
||||
except Exception as e:
|
||||
result += 1
|
||||
LOG.error(_("Failed to delete snapshot with "
|
||||
"name or ID '%(snapshot)s': %(e)s"),
|
||||
{'snapshot': i, 'e': e})
|
||||
|
||||
if result > 0:
|
||||
total = len(parsed_args.snapshots)
|
||||
msg = (_("%(result)s of %(total)s snapshots failed "
|
||||
"to delete.") % {'result': result, 'total': total})
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
|
||||
class ListVolumeSnapshot(command.Lister):
|
||||
"""List volume snapshots"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListVolumeSnapshot, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--all-projects',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Include all projects (admin only)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--long',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('List additional fields in output'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
||||
def _format_volume_id(volume_id):
|
||||
"""Return a volume name if available
|
||||
|
||||
:param volume_id: a volume ID
|
||||
:rtype: either the volume ID or name
|
||||
"""
|
||||
|
||||
volume = volume_id
|
||||
if volume_id in volume_cache.keys():
|
||||
volume = volume_cache[volume_id].display_name
|
||||
return volume
|
||||
|
||||
if parsed_args.long:
|
||||
columns = ['ID', 'Display Name', 'Display Description', 'Status',
|
||||
'Size', 'Created At', 'Volume ID', 'Metadata']
|
||||
column_headers = copy.deepcopy(columns)
|
||||
column_headers[6] = 'Volume'
|
||||
column_headers[7] = 'Properties'
|
||||
else:
|
||||
columns = ['ID', 'Display Name', 'Display Description', 'Status',
|
||||
'Size']
|
||||
column_headers = copy.deepcopy(columns)
|
||||
|
||||
# Always update Name and Description
|
||||
column_headers[1] = 'Name'
|
||||
column_headers[2] = 'Description'
|
||||
|
||||
# Cache the volume list
|
||||
volume_cache = {}
|
||||
try:
|
||||
for s in self.app.client_manager.volume.volumes.list():
|
||||
volume_cache[s.id] = s
|
||||
except Exception:
|
||||
# Just forget it if there's any trouble
|
||||
pass
|
||||
|
||||
search_opts = {
|
||||
'all_tenants': parsed_args.all_projects,
|
||||
}
|
||||
|
||||
data = self.app.client_manager.volume.volume_snapshots.list(
|
||||
search_opts=search_opts)
|
||||
return (column_headers,
|
||||
(utils.get_item_properties(
|
||||
s, columns,
|
||||
formatters={'Metadata': utils.format_dict,
|
||||
'Volume ID': _format_volume_id},
|
||||
) for s in data))
|
||||
|
||||
|
||||
class SetVolumeSnapshot(command.Command):
|
||||
"""Set volume snapshot properties"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(SetVolumeSnapshot, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'snapshot',
|
||||
metavar='<snapshot>',
|
||||
help=_('Snapshot to modify (name or ID)')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--name',
|
||||
metavar='<name>',
|
||||
help=_('New snapshot name')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--description',
|
||||
metavar='<description>',
|
||||
help=_('New snapshot description')
|
||||
)
|
||||
parser.add_argument(
|
||||
'--property',
|
||||
metavar='<key=value>',
|
||||
action=parseractions.KeyValueAction,
|
||||
help=_('Property to add/change for this snapshot '
|
||||
'(repeat option to set multiple properties)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
snapshot = utils.find_resource(volume_client.volume_snapshots,
|
||||
parsed_args.snapshot)
|
||||
|
||||
result = 0
|
||||
if parsed_args.property:
|
||||
try:
|
||||
volume_client.volume_snapshots.set_metadata(
|
||||
snapshot.id, parsed_args.property)
|
||||
except Exception as e:
|
||||
LOG.error(_("Failed to set snapshot property: %s"), e)
|
||||
result += 1
|
||||
|
||||
kwargs = {}
|
||||
if parsed_args.name:
|
||||
kwargs['display_name'] = parsed_args.name
|
||||
if parsed_args.description:
|
||||
kwargs['display_description'] = parsed_args.description
|
||||
if kwargs:
|
||||
try:
|
||||
snapshot.update(**kwargs)
|
||||
except Exception as e:
|
||||
LOG.error(_("Failed to update snapshot display name "
|
||||
"or display description: %s"), e)
|
||||
result += 1
|
||||
|
||||
if result > 0:
|
||||
raise exceptions.CommandError(_("One or more of the "
|
||||
"set operations failed"))
|
||||
|
||||
|
||||
class ShowVolumeSnapshot(command.ShowOne):
|
||||
"""Display volume snapshot details"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ShowVolumeSnapshot, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'snapshot',
|
||||
metavar='<snapshot>',
|
||||
help=_('Snapshot to display (name or ID)')
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
snapshot = utils.find_resource(volume_client.volume_snapshots,
|
||||
parsed_args.snapshot)
|
||||
|
||||
snapshot._info.update(
|
||||
{'properties': utils.format_dict(snapshot._info.pop('metadata'))}
|
||||
)
|
||||
|
||||
return zip(*sorted(six.iteritems(snapshot._info)))
|
||||
|
||||
|
||||
class UnsetVolumeSnapshot(command.Command):
|
||||
"""Unset volume snapshot properties"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(UnsetVolumeSnapshot, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'snapshot',
|
||||
metavar='<snapshot>',
|
||||
help=_('Snapshot to modify (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--property',
|
||||
metavar='<key>',
|
||||
action='append',
|
||||
help=_('Property to remove from snapshot '
|
||||
'(repeat option to remove multiple properties)'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
snapshot = utils.find_resource(
|
||||
volume_client.volume_snapshots, parsed_args.snapshot)
|
||||
|
||||
if parsed_args.property:
|
||||
volume_client.volume_snapshots.delete_metadata(
|
||||
snapshot.id,
|
||||
parsed_args.property,
|
||||
)
|
@ -0,0 +1,338 @@
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
"""Volume v2 snapshot action implementations"""
|
||||
|
||||
import copy
|
||||
import logging
|
||||
|
||||
from osc_lib.cli import parseractions
|
||||
from osc_lib.command import command
|
||||
from osc_lib import exceptions
|
||||
from osc_lib import utils
|
||||
import six
|
||||
|
||||
from openstackclient.i18n import _
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class CreateVolumeSnapshot(command.ShowOne):
|
||||
"""Create new volume snapshot"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(CreateVolumeSnapshot, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"snapshot_name",
|
||||
metavar="<snapshot-name>",
|
||||
nargs="?",
|
||||
help=_("Name of the new snapshot (default to None)")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--volume",
|
||||
metavar="<volume>",
|
||||
help=_("Volume to snapshot (name or ID) "
|
||||
"(default is <snapshot-name>)")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--description",
|
||||
metavar="<description>",
|
||||
help=_("Description of the snapshot")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--force",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help=_("Create a snapshot attached to an instance. "
|
||||
"Default is False")
|
||||
)
|
||||
parser.add_argument(
|
||||
"--property",
|
||||
metavar="<key=value>",
|
||||
action=parseractions.KeyValueAction,
|
||||
help=_("Set a property to this snapshot "
|
||||
"(repeat option to set multiple properties)"),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
volume = parsed_args.volume
|
||||
if not parsed_args.volume:
|
||||
volume = parsed_args.snapshot_name
|
||||
volume_id = utils.find_resource(
|
||||
volume_client.volumes, volume).id
|
||||
snapshot = volume_client.volume_snapshots.create(
|
||||
volume_id,
|
||||
force=parsed_args.force,
|
||||
name=parsed_args.snapshot_name,
|
||||
description=parsed_args.description,
|
||||
metadata=parsed_args.property,
|
||||
)
|
||||
snapshot._info.update(
|
||||
{'properties': utils.format_dict(snapshot._info.pop('metadata'))}
|
||||
)
|
||||
return zip(*sorted(six.iteritems(snapshot._info)))
|
||||
|
||||
|
||||
class DeleteVolumeSnapshot(command.Command):
|
||||
"""Delete volume snapshot(s)"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
"snapshots",
|
||||
metavar="<snapshot>",
|
||||
nargs="+",
|
||||
help=_("Snapshot(s) to delete (name or ID)")
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
volume_client = self.app.client_manager.volume
|
||||
result = 0
|
||||
|
||||
for i in parsed_args.snapshots:
|
||||
try:
|
||||
snapshot_id = utils.find_resource(
|
||||
volume_client.volume_snapshots, i).id
|
||||
volume_client.volume_snapshots.delete(snapshot_id)
|
||||
except Exception as e:
|
||||
result += 1
|
||||
LOG.error(_("Failed to delete snapshot with "
|
||||
"name or ID '%(snapshot)s': %(e)s")
|
||||
% {'snapshot': i, 'e': e})
|
||||
|
||||
if result > 0:
|
||||
total = len(parsed_args.snapshots)
|
||||
msg = (_("%(result)s of %(total)s snapshots failed "
|
||||
"to delete.") % {'result': result, 'total': total})
|
||||
raise exceptions.CommandError(msg)
|
||||
|
||||
|
||||
class ListVolumeSnapshot(command.Lister):
|
||||
"""List volume snapshots"""
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(ListVolumeSnapshot, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'--all-projects',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('Include all projects (admin only)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--long',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help=_('List additional fields in output'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--marker',
|
||||
metavar='<marker>',
|
||||
help=_('The last snapshot ID of the previous page'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'--limit',
|
||||
type=int,
|
||||
action=parseractions.NonNegativeAction,
|
||||
metavar='<limit>',
|
||||
help=_('Maximum number of snapshots to display'),
|
||||
)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
|
||||
def _format_volume_id(volume_id):
|
||||
"""Return a volume name if available
|
||||
|
||||
:param volume_id: a volume ID
|
||||
:rtype: either the volume ID or name
|
||||
"""
|
||||
|
||||
volume = volume_id
|
||||
if volume_id in volume_cache.keys():
|
||||
volume = volume_cache[volume_id].name
|
||||
return volume
|
||||
|
||||
if parsed_args.long:
|
||||
columns = ['ID', 'Name', 'Description', 'Status',
|
||||
'Size', 'Created At', 'Volume ID', 'Metadata']
|
||||
column_headers = copy.deepcopy(columns)
|
||||
column_headers[6] = 'Volume'
|
||||
column_headers[7] = 'Properties'
|
||||
else:
|
||||
columns = ['ID', 'Name', 'Description', 'Status', 'Size']
|
||||
column_headers = copy.deepcopy(columns)
|
||||
|
||||
# Cache the volume list
|
||||
volume_cache = {}
|
||||
try:
|
||||
for s in self.app.client_manager.volume.volumes.list():
|
||||
volume_cache[s.id] = s
|
||||
except Exception:
|
||||
# Just forget it if there's any trouble
|
||||
pass
|
||||
|
||||
search_opts = {
|
||||
'all_tenants': parsed_args.all_projects,
|
||||
}
|
||||
|
||||
data = self.app.client_manager.volume.volume_snapshots.list(
|
||||
search_opts=search_opts,
|
||||
marker=parsed_args.marker,
|
||||
limit=parsed_args.limit,
|
||||
)
|
||||
return (column_headers,
|
||||
(utils.get_item_properties(
|
||||
s, columns,
|
||||
formatters={'Metadata': utils.format_dict,
|
||||
'Volume ID': _format_volume_id},
|
||||
) for s in data))
|
||||
|
||||
|
||||
class SetVolumeSnapshot(command. |