[Kimchi-devel] [PATCH 3/3] Add support for VM suspend and resume

Royce Lv lvroyce at linux.vnet.ibm.com
Tue Jan 27 08:08:07 UTC 2015


In real model, vm lookup need to be updated because vm has no statistics 
to present,
and snapshots can be created when vm is "paused"(we just allow it to be 
taken when vm is "shutoff" previously), etc.
Please update all previous state judgement clause  and check if it works 
for "pause" "running" "shutoff".

On 01/22/2015 09:52 AM, Crístian Viana wrote:
> The user is now able to suspend the VM and resume its execution in a
> later moment with the following commands:
>
> POST /vms/<vm-name>/suspend
> POST /vms/<vm-name>/resume
>
> Signed-off-by: Crístian Viana <vianac at linux.vnet.ibm.com>
> ---
>   src/kimchi/control/vms.py |  2 ++
>   src/kimchi/i18n.py        |  4 ++++
>   src/kimchi/model/vms.py   | 41 +++++++++++++++++++++++++++++++++++++++++
>   tests/test_model.py       | 20 ++++++++++++++++++++
>   tests/test_rest.py        | 28 ++++++++++++++++++++++++++++
>   5 files changed, 95 insertions(+)
>
> diff --git a/src/kimchi/control/vms.py b/src/kimchi/control/vms.py
> index 1a84b6c..4f1f578 100644
> --- a/src/kimchi/control/vms.py
> +++ b/src/kimchi/control/vms.py
> @@ -45,6 +45,8 @@ class VM(Resource):
>           self.reset = self.generate_action_handler('reset')
>           self.connect = self.generate_action_handler('connect')
>           self.clone = self.generate_action_handler_task('clone')
> +        self.suspend = self.generate_action_handler('suspend')
> +        self.resume = self.generate_action_handler('resume')
>   
>       @property
>       def data(self):
> diff --git a/src/kimchi/i18n.py b/src/kimchi/i18n.py
> index 4eccc3e..79c0b23 100644
> --- a/src/kimchi/i18n.py
> +++ b/src/kimchi/i18n.py
> @@ -106,6 +106,10 @@ messages = {
>       "KCHVM0033E": _("Virtual machine '%(name)s' must be stopped before cloning it."),
>       "KCHVM0034E": _("Insufficient disk space to clone virtual machine '%(name)s'"),
>       "KCHVM0035E": _("Unable to clone VM '%(name)s'. Details: %(err)s"),
> +    "KCHVM0036E": _("Cannot suspend VM '%(name)s' because it is not running."),
> +    "KCHVM0037E": _("Unable to suspend VM '%(name)s'. Details: %(err)s"),
> +    "KCHVM0038E": _("Cannot resume VM '%(name)s' because it is not paused."),
> +    "KCHVM0039E": _("Unable to resume VM '%(name)s'. Details: %(err)s"),
>   
>       "KCHVMHDEV0001E": _("VM %(vmid)s does not contain directly assigned host device %(dev_name)s."),
>       "KCHVMHDEV0002E": _("The host device %(dev_name)s is not allowed to directly assign to VM."),
> diff --git a/src/kimchi/model/vms.py b/src/kimchi/model/vms.py
> index 5128d1e..55a80e6 100644
> --- a/src/kimchi/model/vms.py
> +++ b/src/kimchi/model/vms.py
> @@ -939,6 +939,47 @@ class VMModel(object):
>               kimchi_log.error('Error trying to delete vm screenshot from '
>                                'database due error: %s', e.message)
>   
> +    def suspend(self, name):
> +        """Suspend the virtual machine's execution and puts it in the
> +        state 'paused'. Use the function "resume" to restore its state.
> +        If the VM is not running, an exception will be raised.
> +
> +        Parameters:
> +        name -- the name of the VM to be suspended.
> +        """
> +        vm = self.lookup(name)
> +        if vm['state'] != 'running':
> +            raise InvalidOperation('KCHVM0036E', {'name': name})
> +
> +        vir_dom = self.get_vm(name, self.conn)
> +
> +        try:
> +            vir_dom.suspend()
> +        except libvirt.libvirtError, e:
> +            raise OperationFailed('KCHVM0037E', {'name': name,
> +                                                 'err': e.message})
> +
> +    def resume(self, name):
> +        """Resume the virtual machine's execution and puts it in the
> +        state 'running'. The VM should have been suspended previously by the
> +        function "suspend" and be in the state 'paused', otherwise an exception
> +        will be raised.
> +
> +        Parameters:
> +        name -- the name of the VM to be resumed.
> +        """
> +        vm = self.lookup(name)
> +        if vm['state'] != 'paused':
> +            raise InvalidOperation('KCHVM0038E', {'name': name})
> +
> +        vir_dom = self.get_vm(name, self.conn)
> +
> +        try:
> +            vir_dom.resume()
> +        except libvirt.libvirtError, e:
> +            raise OperationFailed('KCHVM0039E', {'name': name,
> +                                                 'err': e.message})
> +
>   
>   class VMScreenshotModel(object):
>       def __init__(self, **kargs):
> diff --git a/tests/test_model.py b/tests/test_model.py
> index c956007..2013f3c 100644
> --- a/tests/test_model.py
> +++ b/tests/test_model.py
> @@ -187,6 +187,26 @@ class ModelTests(unittest.TestCase):
>               self.assertRaises(NotFoundError, inst.vmsnapshot_delete,
>                                 u'kimchi-vm', u'foobar')
>   
> +            # suspend and resume the VM
> +            info = inst.vm_lookup(u'kimchi-vm')
> +            self.assertEquals(info['state'], 'shutoff')
> +            self.assertRaises(InvalidOperation, inst.vm_suspend, u'kimchi-vm')
> +            inst.vm_start(u'kimchi-vm')
> +            info = inst.vm_lookup(u'kimchi-vm')
> +            self.assertEquals(info['state'], 'running')
> +            inst.vm_suspend(u'kimchi-vm')
> +            info = inst.vm_lookup(u'kimchi-vm')
> +            self.assertEquals(info['state'], 'paused')
> +            self.assertRaises(InvalidParameter, inst.vm_update, u'kimchi-vm',
> +                              {'name': 'foo'})
> +            inst.vm_resume(u'kimchi-vm')
> +            info = inst.vm_lookup(u'kimchi-vm')
> +            self.assertEquals(info['state'], 'running')
> +            self.assertRaises(InvalidOperation, inst.vm_resume, u'kimchi-vm')
> +            # leave the VM suspended to make sure a paused VM can be
> +            # deleted correctly
> +            inst.vm_suspend(u'kimchi-vm')
> +
>           vms = inst.vms_get_list()
>           self.assertFalse('kimchi-vm' in vms)
>   
> diff --git a/tests/test_rest.py b/tests/test_rest.py
> index 7416463..6ff7cf8 100644
> --- a/tests/test_rest.py
> +++ b/tests/test_rest.py
> @@ -460,6 +460,34 @@ class RestTests(unittest.TestCase):
>                               '{}', 'DELETE')
>           self.assertEquals(204, resp.status)
>   
> +        # Suspend the VM
> +        resp = self.request('/vms/test-vm', '{}', 'GET')
> +        self.assertEquals(200, resp.status)
> +        vm = json.loads(resp.read())
> +        self.assertEquals(vm['state'], 'shutoff')
> +        resp = self.request('/vms/test-vm/suspend', '{}', 'POST')
> +        self.assertEquals(400, resp.status)
> +        resp = self.request('/vms/test-vm/start', '{}', 'POST')
> +        self.assertEquals(200, resp.status)
> +        resp = self.request('/vms/test-vm', '{}', 'GET')
> +        self.assertEquals(200, resp.status)
> +        vm = json.loads(resp.read())
> +        self.assertEquals(vm['state'], 'running')
> +        resp = self.request('/vms/test-vm/suspend', '{}', 'POST')
> +        self.assertEquals(200, resp.status)
> +        resp = self.request('/vms/test-vm', '{}', 'GET')
> +        self.assertEquals(200, resp.status)
> +        vm = json.loads(resp.read())
> +        self.assertEquals(vm['state'], 'paused')
> +
> +        # Resume the VM
> +        resp = self.request('/vms/test-vm/resume', '{}', 'POST')
> +        self.assertEquals(200, resp.status)
> +        resp = self.request('/vms/test-vm', '{}', 'GET')
> +        self.assertEquals(200, resp.status)
> +        vm = json.loads(resp.read())
> +        self.assertEquals(vm['state'], 'running')
> +
>           # Delete the VM
>           resp = self.request('/vms/test-vm', '{}', 'DELETE')
>           self.assertEquals(204, resp.status)




More information about the Kimchi-devel mailing list