Added support for listing/creating/deleting snapshots of nova volumes. Also
implemented the supporting CLI commands. Requires the OS API extension, 'os-snapshots'
This commit is contained in:
parent
2a0b3b5311
commit
5c38baf65a
10
README.rst
10
README.rst
@ -56,7 +56,7 @@ endpoint::
|
||||
export NOVA_URL=http://example.com:5000/v2.0/
|
||||
|
||||
Since Keystone can return multiple regions in the Service Catalog, you
|
||||
can specify the one you want with ``--region_name`` (or
|
||||
can specify the one you want with ``--region_name`` (or
|
||||
``export NOVA_REGION_NAME``). It defaults to the first in the list returned.
|
||||
|
||||
You'll find complete documentation on the shell by running
|
||||
@ -124,6 +124,12 @@ You'll find complete documentation on the shell by running
|
||||
secgroup-list List security groups for the curent tenant.
|
||||
secgroup-list-rules List rules for a security group.
|
||||
show Show details about the given server.
|
||||
snapshot-create Add a new snapshot.
|
||||
snapshot-delete Remove a snapshot.
|
||||
snapshot-list List all the snapshots.
|
||||
snapshot-show Show details about a snapshot.
|
||||
suspend Suspend a server.
|
||||
unpause Unpause a server.
|
||||
unrescue Unrescue a server.
|
||||
volume-attach Attach a volume to a server.
|
||||
volume-create Add a new volume.
|
||||
@ -190,7 +196,7 @@ Quick-start using keystone::
|
||||
[...]
|
||||
>>> nt.keypairs.list()
|
||||
[...]
|
||||
|
||||
|
||||
# if you want to use the keystone api to modify users/tenants:
|
||||
>>> from novaclient import client
|
||||
>>> conn = client.HTTPClient(USER, PASS, TENANT, KEYSTONE_URL)
|
||||
|
@ -38,6 +38,7 @@ class Client(object):
|
||||
|
||||
# extensions
|
||||
self.volumes = volumes.VolumeManager(self)
|
||||
self.snapshots = volumes.SnapshotManager(self)
|
||||
self.keypairs = keypairs.KeypairManager(self)
|
||||
self.zones = zones.ZoneManager(self)
|
||||
self.quotas = quotas.QuotaSetManager(self)
|
||||
|
@ -253,7 +253,7 @@ def do_zone_boot(cs, args):
|
||||
min_count=min_count,
|
||||
max_count=max_count)
|
||||
print "Reservation ID=", reservation_id
|
||||
|
||||
|
||||
|
||||
def _translate_flavor_keys(collection):
|
||||
convert = [('ram', 'memory_mb'), ('disk', 'local_gb')]
|
||||
@ -760,11 +760,18 @@ def _find_volume(cs, volume):
|
||||
"""Get a volume by ID."""
|
||||
return utils.find_resource(cs.volumes, volume)
|
||||
|
||||
def _find_snapshot(cs, snapshot):
|
||||
"""Get a snapshot by ID."""
|
||||
return utils.find_resource(cs.snapshots, snapshot)
|
||||
|
||||
def _print_volume(cs, volume):
|
||||
utils.print_dict(volume._info)
|
||||
|
||||
|
||||
def _print_snapshot(cs, snapshot):
|
||||
utils.print_dict(snapshot._info)
|
||||
|
||||
|
||||
def _translate_volume_keys(collection):
|
||||
convert = [('displayName', 'display_name')]
|
||||
for item in collection:
|
||||
@ -774,6 +781,15 @@ def _translate_volume_keys(collection):
|
||||
setattr(item, to_key, item._info[from_key])
|
||||
|
||||
|
||||
def _translate_snapshot_keys(collection):
|
||||
convert = [('displayName', 'display_name'), ('volumeId', 'volume_id')]
|
||||
for item in collection:
|
||||
keys = item.__dict__.keys()
|
||||
for from_key, to_key in convert:
|
||||
if from_key in keys and to_key not in keys:
|
||||
setattr(item, to_key, item._info[from_key])
|
||||
|
||||
|
||||
def do_volume_list(cs, args):
|
||||
"""List all the volumes."""
|
||||
volumes = cs.volumes.list()
|
||||
@ -851,6 +867,53 @@ def do_volume_detach(cs, args):
|
||||
cs.volumes.delete_server_volume(_find_server(cs, args.server).id,
|
||||
args.attachment_id)
|
||||
|
||||
def do_snapshot_list(cs, args):
|
||||
"""List all the snapshots."""
|
||||
snapshots = cs.snapshots.list()
|
||||
_translate_snapshot_keys(snapshots)
|
||||
utils.print_list(snapshots, ['ID', 'Volume ID', 'Status', 'Display Name',
|
||||
'Size'])
|
||||
|
||||
|
||||
@utils.arg('snapshot', metavar='<snapshot>', help='ID of the snapshot.')
|
||||
def do_snapshot_show(cs, args):
|
||||
"""Show details about a snapshot."""
|
||||
snapshot = _find_snapshot(cs, args.snapshot)
|
||||
_print_snapshot(cs, snapshot)
|
||||
|
||||
|
||||
@utils.arg('volume_id',
|
||||
metavar='<volume_id>',
|
||||
type=int,
|
||||
help='ID of the volume to snapshot')
|
||||
@utils.arg('--force',
|
||||
metavar='<force>',
|
||||
help='Optional flag to indicate whether to snapshot a volume even if its '
|
||||
'attached to an instance. (Default=False)',
|
||||
default=False)
|
||||
@utils.arg('--display_name', metavar='<display_name>',
|
||||
help='Optional snapshot name. (Default=None)',
|
||||
default=None)
|
||||
@utils.arg('--display_description', metavar='<display_description>',
|
||||
help='Optional snapshot description. (Default=None)',
|
||||
default=None)
|
||||
def do_snapshot_create(cs, args):
|
||||
"""Add a new snapshot."""
|
||||
cs.snapshots.create(args.volume_id,
|
||||
args.force,
|
||||
args.display_name,
|
||||
args.display_description)
|
||||
|
||||
|
||||
@utils.arg('snapshot_id',
|
||||
metavar='<snapshot_id>',
|
||||
help='ID of the snapshot to delete.')
|
||||
def do_snapshot_delete(cs, args):
|
||||
"""Remove a snapshot."""
|
||||
snapshot = _find_snapshot(cs, args.snapshot_id)
|
||||
snapshot.delete()
|
||||
|
||||
|
||||
def _print_floating_ip_list(floating_ips):
|
||||
utils.print_list(floating_ips, ['Ip', 'Instance Id', 'Fixed Ip'])
|
||||
|
||||
@ -1057,7 +1120,7 @@ def do_keypair_add(cs, args):
|
||||
pub_key = f.read()
|
||||
except IOError, e:
|
||||
raise exceptions.CommandError("Can't open or read '%s': %s" % (pub_key, e))
|
||||
|
||||
|
||||
keypair = cs.keypairs.create(name, pub_key)
|
||||
|
||||
if not pub_key:
|
||||
|
@ -33,6 +33,19 @@ class Volume(base.Resource):
|
||||
"""
|
||||
return self.manager.delete(self)
|
||||
|
||||
class Snapshot(base.Resource):
|
||||
"""
|
||||
A Snapshot is a point-in-time snapshot of an openstack volume.
|
||||
"""
|
||||
def __repr__(self):
|
||||
return "<Snapshot: %s>" % self.id
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
Delete this snapshot.
|
||||
"""
|
||||
return self.manager.delete(self)
|
||||
|
||||
|
||||
class VolumeManager(base.ManagerWithFind):
|
||||
"""
|
||||
@ -46,7 +59,7 @@ class VolumeManager(base.ManagerWithFind):
|
||||
Create a volume.
|
||||
|
||||
:param size: Size of volume in GB
|
||||
:param snapshot_id: ID of the snapshot
|
||||
:param snapshot_id: ID of the snapshot
|
||||
:param display_name: Name of the volume
|
||||
:param display_description: Description of the volume
|
||||
:rtype: :class:`Volume`
|
||||
@ -130,3 +143,57 @@ class VolumeManager(base.ManagerWithFind):
|
||||
"""
|
||||
return self._delete("/servers/%s/os-volume_attachments/%s" % (server_id,
|
||||
attachment_id,))
|
||||
|
||||
|
||||
class SnapshotManager(base.ManagerWithFind):
|
||||
"""
|
||||
Manage :class:`Snapshot` resources.
|
||||
"""
|
||||
resource_class = Snapshot
|
||||
|
||||
def create(self, volume_id, force=False,
|
||||
display_name=None, display_description=None):
|
||||
|
||||
"""
|
||||
Create a snapshot of the given volume.
|
||||
|
||||
:param volume_id: The ID of the volume to snapshot.
|
||||
:param force: If force is True, create a snapshot even if the volume is
|
||||
attached to an instance. Default is False.
|
||||
:param display_name: Name of the snapshot
|
||||
:param display_description: Description of the snapshot
|
||||
:rtype: :class:`Snapshot`
|
||||
"""
|
||||
body = {'snapshot': {'volume_id': volume_id,
|
||||
'force': force,
|
||||
'display_name': display_name,
|
||||
'display_description': display_description}}
|
||||
return self._create('/os-snapshots', body, 'snapshot')
|
||||
|
||||
def get(self, snapshot_id):
|
||||
"""
|
||||
Get a snapshot.
|
||||
|
||||
:param snapshot_id: The ID of the snapshot to get.
|
||||
:rtype: :class:`Snapshot`
|
||||
"""
|
||||
return self._get("/os-snapshots/%s" % snapshot_id, "snapshot")
|
||||
|
||||
def list(self, detailed=True):
|
||||
"""
|
||||
Get a list of all snapshots.
|
||||
|
||||
:rtype: list of :class:`Snapshot`
|
||||
"""
|
||||
if detailed is True:
|
||||
return self._list("/os-snapshots/detail", "snapshots")
|
||||
else:
|
||||
return self._list("/os-snapshots", "snapshots")
|
||||
|
||||
def delete(self, snapshot):
|
||||
"""
|
||||
Delete a snapshot.
|
||||
|
||||
:param snapshot: The :class:`Snapshot` to delete.
|
||||
"""
|
||||
self._delete("/os-snapshots/%s" % base.getid(snapshot))
|
||||
|
Loading…
x
Reference in New Issue
Block a user