
A new command is added to look up an existing snapshot: GET /vms/<vm-name>/snapshots/<snapshot-name> It returns the following values: * created: The time when the snapshot was created (in seconds, since the Unix Epoch); * name: The snapshot name; * parent: The name of the parent snapshot; * state: The corresponding VM state when the snapshot was created (currently, it can only be 'shutoff'); Signed-off-by: Crístian Viana <vianac@linux.vnet.ibm.com> --- docs/API.md | 10 ++++++++++ src/kimchi/i18n.py | 2 ++ src/kimchi/mockmodel.py | 8 ++++++++ src/kimchi/model/vmsnapshots.py | 43 +++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 12 ++++++++++++ 5 files changed, 75 insertions(+) diff --git a/docs/API.md b/docs/API.md index fe1c3cf..44cc825 100644 --- a/docs/API.md +++ b/docs/API.md @@ -194,6 +194,16 @@ Represents a snapshot of the Virtual Machine's primary monitor. * name: The snapshot name (optional, defaults to a value based on the current time). +### Sub-resource: Snapshot +**URI:** /vms/*:name*/snapshots/*:snapshot* +* **GET**: Retrieve snapshot information. + * created: The time when the snapshot was created + (in seconds, since the epoch). + * name: The snapshot name. + * parent: The name of the parent snapshot, or an empty string if there is + no parent. + * state: The corresponding domain's state when the snapshot was created. + ### Collection: Templates **URI:** /templates diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index 9c37931..159a2d2 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -313,4 +313,6 @@ messages = { "KCHSNAP0001E": _("Virtual machine '%(vm)s' must be stopped before creating a snapshot on it."), "KCHSNAP0002E": _("Unable to create snapshot '%(name)s' on virtual machine '%(vm)s'. Details: %(err)s"), + "KCHSNAP0003E": _("Snapshot '%(name)s' does not exist on virtual machine '%(vm)s'."), + "KCHSNAP0004E": _("Unable to retrieve snapshot '%(name)s' on virtual machine '%(vm)s'. Details: %(err)s"), } diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 211ae66..b6fb531 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -996,6 +996,14 @@ class MockModel(object): cb('OK', True) + def vmsnapshot_lookup(self, vm_name, name): + vm = self._get_vm(vm_name) + + try: + return vm.snapshots[name].info + except KeyError: + raise NotFoundError('KCHSNAP0003E', {'vm': vm_name, 'name': name}) + def tasks_get_list(self): with self.objstore as session: return session.get_list('task') diff --git a/src/kimchi/model/vmsnapshots.py b/src/kimchi/model/vmsnapshots.py index 1512d9e..1ce6e9f 100644 --- a/src/kimchi/model/vmsnapshots.py +++ b/src/kimchi/model/vmsnapshots.py @@ -21,6 +21,7 @@ import time import libvirt import lxml.etree as ET +from lxml import objectify from lxml.builder import E from kimchi.exception import InvalidOperation, NotFoundError, OperationFailed @@ -89,3 +90,45 @@ class VMSnapshotsModel(object): 'err': e.message}) cb('OK', True) + + +class VMSnapshotModel(object): + def __init__(self, **kargs): + self.conn = kargs['conn'] + + def lookup(self, vm_name, name): + vir_snap = self.get_vmsnapshot(vm_name, name) + + try: + snap_xml_str = vir_snap.getXMLDesc(0).decode('utf-8') + except libvirt.libvirtError, e: + raise OperationFailed('KCHSNAP0004E', {'name': name, + 'vm': vm_name, + 'err': e.message}) + + snap_xml = objectify.fromstring(snap_xml_str) + + try: + parent = unicode(snap_xml.parent.name) + except AttributeError: + parent = u'' + + return {'created': unicode(snap_xml.creationTime), + 'name': unicode(snap_xml.name), + 'parent': parent, + 'state': unicode(snap_xml.state)} + + def get_vmsnapshot(self, vm_name, name): + vir_dom = VMModel.get_vm(vm_name, self.conn) + + try: + return vir_dom.snapshotLookupByName(name) + except libvirt.libvirtError, e: + code = e.get_error_code() + if code == libvirt.VIR_ERR_NO_DOMAIN_SNAPSHOT: + raise NotFoundError('KCHSNAP0003E', {'name': name, + 'vm': vm_name}) + else: + raise OperationFailed('KCHSNAP0004E', {'name': name, + 'vm': vm_name, + 'err': e.message}) diff --git a/tests/test_rest.py b/tests/test_rest.py index 7e6d684..e6ce715 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -396,6 +396,18 @@ class RestTests(unittest.TestCase): task = json.loads(self.request('/tasks/%s' % task['id']).read()) self.assertEquals('finished', task['status']) + # Look up a snapshot + resp = self.request('/vms/test-vm/snapshots/foobar', '{}', 'GET') + self.assertEquals(404, resp.status) + resp = self.request('/vms/test-vm/snapshots/%s' % params['name'], '{}', + 'GET') + self.assertEquals(200, resp.status) + snap = json.loads(resp.read()) + self.assertTrue(int(time.time()) >= int(snap['created'])) + self.assertEquals(params['name'], snap['name']) + self.assertEquals(u'', snap['parent']) + self.assertEquals(u'shutoff', snap['state']) + # Delete the VM resp = self.request('/vms/test-vm', '{}', 'DELETE') self.assertEquals(204, resp.status) -- 1.9.3