
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; * state: The corresponding VM state when the snapshot was created (currently, it can only be 'shutoff'); * parent: The name of the parent snapshot; Signed-off-by: Crístian Viana <vianac@linux.vnet.ibm.com> --- docs/API.md | 10 ++++++++++ src/kimchi/i18n.py | 2 ++ src/kimchi/mockmodel.py | 18 ++++++++++++----- src/kimchi/model/vmsnapshots.py | 43 +++++++++++++++++++++++++++++++++++++++++ tests/test_rest.py | 12 ++++++++++++ 5 files changed, 80 insertions(+), 5 deletions(-) diff --git a/docs/API.md b/docs/API.md index fe1c3cf..616958a 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. + * name: The snapshot name. + * state: The corresponding domain's state when the snapshot was created. + * created: The time when the snapshot was created + (in seconds, since the epoch). + * parent: The name of the parent snapshot, or an empty string if there is + no parent. + ### 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 e06b01f..cb52750 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -989,18 +989,25 @@ class MockModel(object): parent = u'' for sn, s in vm.snapshots.iteritems(): - if s.info['current']: - s.info['current'] = False + if s.current: + s.current = False parent = sn break - snap_info = {'name': name, - 'parent': parent, + snap_info = {'parent': parent, 'state': vm.info['state']} vm.snapshots[name] = MockVMSnapshot(vm_name, name, snap_info) 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') @@ -1622,10 +1629,11 @@ class MockVMSnapshot(object): def __init__(self, vm_name, name, params={}): self.vm = vm_name self.name = name + self.current = True self.info = {'created': params.get('created', unicode(int(time.time()))), - 'current': params.get('current', True), + 'name': name, 'parent': params.get('parent', u''), 'state': params.get('state', u'shutoff')} diff --git a/src/kimchi/model/vmsnapshots.py b/src/kimchi/model/vmsnapshots.py index 8701c03..27fd052 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 @@ -93,3 +94,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 {'name': unicode(snap_xml.name), + 'state': unicode(snap_xml.state), + 'created': unicode(snap_xml.creationTime), + 'parent': parent} + + 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