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(a)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