
A new command is added to revert a virtual machine to a specified snapshot: POST /vms/<vm-name>/snapshots/<snapshot-name>/revert It changes the specified VM state to the exact same one as it was when the snapshot was created. Signed-off-by: Crístian Viana <vianac@linux.vnet.ibm.com> --- docs/API.md | 5 +++++ src/kimchi/control/vm/snapshots.py | 1 + src/kimchi/i18n.py | 1 + src/kimchi/mockmodel.py | 14 ++++++++++++++ src/kimchi/model/vmsnapshots.py | 10 ++++++++++ tests/test_rest.py | 14 ++++++++++++++ 6 files changed, 45 insertions(+) diff --git a/docs/API.md b/docs/API.md index 23c787b..d4ed015 100644 --- a/docs/API.md +++ b/docs/API.md @@ -206,6 +206,11 @@ Represents a snapshot of the Virtual Machine's primary monitor. * state: The corresponding domain's state when the snapshot was created. * **DELETE**: Delete snapshot. If the snapshot has any children, they will be merged automatically with the snapshot's parent. +* **POST**: See "Snapshot actions (POST)" + +**Snapshot Actions (POST):** + +* revert: Revert the domain to the given snapshot. ### Sub-resource: Current snapshot **URI:** /vms/*:name*/snapshots/current diff --git a/src/kimchi/control/vm/snapshots.py b/src/kimchi/control/vm/snapshots.py index d491015..bbebc9a 100644 --- a/src/kimchi/control/vm/snapshots.py +++ b/src/kimchi/control/vm/snapshots.py @@ -39,6 +39,7 @@ class VMSnapshot(Resource): self.ident = ident self.model_args = [self.vm, self.ident] self.uri_fmt = '/vms/%s/snapshots/%s' + self.revert = self.generate_action_handler('revert') @property def data(self): diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py index b1b8060..b95c707 100644 --- a/src/kimchi/i18n.py +++ b/src/kimchi/i18n.py @@ -319,4 +319,5 @@ messages = { "KCHSNAP0006E": _("Unable to delete snapshot '%(name)s' on virtual machine '%(vm)s'. Details: %(err)s"), "KCHSNAP0007E": _("Virtual machine '%(vm)s' does not have a current snapshot."), "KCHSNAP0008E": _("Unable to retrieve current snapshot on virtual machine '%(vm)s'. Details: %(err)s"), + "KCHSNAP0009E": _("Unable to revert virtual machine '%(vm)s' to snapshot '%(name)s'. Details: %(err)s"), } diff --git a/src/kimchi/mockmodel.py b/src/kimchi/mockmodel.py index 75855f6..ed57cb7 100644 --- a/src/kimchi/mockmodel.py +++ b/src/kimchi/mockmodel.py @@ -1025,6 +1025,20 @@ class MockModel(object): except KeyError: raise NotFoundError('KCHSNAP0003E', {'vm': vm_name, 'name': name}) + def vmsnapshot_revert(self, vm_name, name): + vm = self._get_vm(vm_name) + + try: + snap = vm.snapshots[name] + except KeyError: + raise NotFoundError('KCHSNAP0003E', {'vm': vm_name, 'name': name}) + + current_snapshot_name = self.currentvmsnapshot_lookup(vm_name)['name'] + vm.snapshots[current_snapshot_name].current = False + snap.current = True + + vm.info['state'] = snap.info['state'] + 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 b6702b9..e8e2294 100644 --- a/src/kimchi/model/vmsnapshots.py +++ b/src/kimchi/model/vmsnapshots.py @@ -138,6 +138,16 @@ class VMSnapshotModel(object): 'vm': vm_name, 'err': e.message}) + def revert(self, vm_name, name): + try: + vir_dom = VMModel.get_vm(vm_name, self.conn) + vir_snap = self.get_vmsnapshot(vm_name, name) + vir_dom.revertToSnapshot(vir_snap, 0) + except libvirt.libvirtError, e: + raise OperationFailed('KCHSNAP0009E', {'name': name, + 'vm': vm_name, + 'err': e.message}) + def get_vmsnapshot(self, vm_name, name): vir_dom = VMModel.get_vm(vm_name, self.conn) diff --git a/tests/test_rest.py b/tests/test_rest.py index 1129aec..b1bfbcc 100644 --- a/tests/test_rest.py +++ b/tests/test_rest.py @@ -443,6 +443,20 @@ class RestTests(unittest.TestCase): snap = json.loads(resp.read()) self.assertEquals(snap_name, snap['name']) + # Revert to snapshot + resp = self.request('/vms/test-vm/snapshots/%s/revert' % + params['name'], '{}', 'POST') + self.assertEquals(200, resp.status) + snap = json.loads(resp.read()) + resp = self.request('/vms/test-vm', '{}', 'GET') + self.assertEquals(200, resp.status) + vm = json.loads(resp.read()) + self.assertEquals(vm['state'], snap['state']) + resp = self.request('/vms/test-vm/snapshots/current', '{}', 'GET') + self.assertEquals(200, resp.status) + current_snap = json.loads(resp.read()) + self.assertEquals(snap, current_snap) + # Delete a snapshot resp = self.request('/vms/test-vm/snapshots/foobar', '{}', 'DELETE') self.assertEquals(404, resp.status) -- 1.9.3