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:
Gaurav Gupta 2011-10-19 10:54:27 -07:00
parent 2a0b3b5311
commit 5c38baf65a
4 changed files with 142 additions and 5 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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:

View File

@ -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))