Merge lp:~nttdata/nova/kvm-pause-suspend into lp:~hudson-openstack/nova/trunk

Proposed by Kei Masumoto
Status: Merged
Approved by: Dan Prince
Approved revision: 987
Merged at revision: 1126
Proposed branch: lp:~nttdata/nova/kvm-pause-suspend
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 35 lines (+12/-4)
1 file modified
nova/virt/libvirt/connection.py (+12/-4)
To merge this branch: bzr merge lp:~nttdata/nova/kvm-pause-suspend
Reviewer Review Type Date Requested Status
Dan Prince (community) Approve
Vish Ishaya (community) Approve
Devin Carlen (community) Approve
Brian Lamar (community) Approve
Jay Pipes (community) Approve
justinsb (community) Needs Information
Review via email: mp+62255@code.launchpad.net

Description of the change

added pause/suspend implementation to nova.virt.libvirt_conn

To post a comment you must log in.
Revision history for this message
Isaku Yamahata (yamahata) wrote :
Download full text (3.2 KiB)

Hi. Repeating same logic seems ugly. How about something like the following?

def _do_dom(self, action, instance_name)
   if self.readonly:
      tmpconn ...

      action(dom)
      tmpconn.close()
   else:
      dom = ...
      action(dom)

def pause(...)
...
   self._do_dom(virtDoain.suspend)

...

thanks,

On Wed, May 25, 2011 at 08:23:29AM -0000, Kei Masumoto wrote:
> Kei Masumoto has proposed merging lp:~nttdata/nova/kvm-pause-suspend into lp:nova.
>
> Requested reviews:
> Nova Core (nova-core)
>
> For more details, see:
> https://code.launchpad.net/~nttdata/nova/kvm-pause-suspend/+merge/62255
>
> added pause/suspend implementation to nova.virt.libvirt_conn
> --
> https://code.launchpad.net/~nttdata/nova/kvm-pause-suspend/+merge/62255
> You are subscribed to branch lp:nova.

> === modified file 'nova/virt/libvirt_conn.py'
> --- nova/virt/libvirt_conn.py 2011-05-17 16:28:44 +0000
> +++ nova/virt/libvirt_conn.py 2011-05-25 08:17:27 +0000
> @@ -550,19 +550,54 @@
>
> @exception.wrap_exception
> def pause(self, instance, callback):
> - raise exception.ApiError("pause not supported for libvirt.")
> + """Pause VM instance"""
> + if self.read_only:
> + tmpconn = self._connect(self.libvirt_uri, False)
> + dom = tmpconn.lookupByName(instance.name)
> + dom.suspend()
> + tmpconn.close()
> + else:
> + dom = self._conn.lookupByName(instance.name)
> + dom.suspend()
>
> @exception.wrap_exception
> def unpause(self, instance, callback):
> - raise exception.ApiError("unpause not supported for libvirt.")
> + """Unpause paused VM instance"""
> + if self.read_only:
> + tmpconn = self._connect(self.libvirt_uri, False)
> + dom = tmpconn.lookupByName(instance.name)
> + dom.resume()
> + tmpconn.close()
> + else:
> + dom = self._conn.lookupByName(instance.name)
> + dom.resume()
>
> @exception.wrap_exception
> def suspend(self, instance, callback):
> - raise exception.ApiError("suspend not supported for libvirt")
> + """Suspend the specified instance"""
> + if self.read_only:
> + tmpconn = self._connect(self.libvirt_uri, False)
> + dom = tmpconn.lookupByName(instance.name)
> + dom.managedSave(0)
> + tmpconn.close()
> + else:
> + dom = self._conn.lookupByName(instance.name)
> + dom.managedSave(0)
>
> @exception.wrap_exception
> def resume(self, instance, callback):
> - raise exception.ApiError("resume not supported for libvirt")
> + """resume the specified instance"""
> + try:
> + if self.read_only:
> + tmpconn = self._connect(self.libvirt_uri, False)
> + dom = tmpconn.lookupByName(instance.name)
> + tmpconn.close()
> + else:
> + dom = self._conn.lookupByName(instance.name)
> + dom.create()
> + except libvirt.LibvirtError:
> + xml = self.to_xml(instance, None)
> + self._create_new_domain(xml)
>...

Read more...

Revision history for this message
Kei Masumoto (masumotok) wrote :

Hi, thanks for comments.

The reason why I simply wrote same logic is just I was not much confident to make it brief.
I'm gonna fix that soon..

Revision history for this message
Brian Lamar (blamar) wrote :

Hey Kei,

Thanks for the contribution!

I have a very big question as to *why* we need to be checking for read_only before every call. I see this pattern being used once or twice in the current code, but I would rather not repeat it unless someone can explain it to me :)

If the connection is read-only, then actions which require write privileges should fail. I don't see the point in creating a temporary connection.

Also, there is a "_lookup_by_name" method which might help with some error cases.

review: Needs Fixing
Revision history for this message
Kei Masumoto (masumotok) wrote :

Hi Brian.
Thanks for review!

Agreed to your all comments. Fixed them soon.
nova.virt.libvirt_conn._live_migration also checks read_only. I'm going to raise new bug report later.

Revision history for this message
justinsb (justin-fathomdb) wrote :

This looks good, except for the exception handling logic in resume.

I think you should check for the exact error code when you've got the exception, rather than assuming that every exception is the one you expect (e.g. unexpected exceptions are thrown when the libvirt daemon isn't started, or when you don't have the right permissions).

Also, if there is a specific case you're trying to handle, can you please add a comment to the code so that everyone knows what it is. In general, I'm not sure we should be putting extra logic into the resume function - it just feels like it's the wrong layer. But I could be wrong - what is the situation you're handling here?

review: Needs Information
Revision history for this message
Brian Lamar (blamar) wrote :

> Also, if there is a specific case you're trying to handle, can you please add
> a comment to the code so that everyone knows what it is. In general, I'm not
> sure we should be putting extra logic into the resume function - it just feels
> like it's the wrong layer. But I could be wrong - what is the situation
> you're handling here?

While I do think this is where the logic should go...I do think we should probably just fail if dom.resume() fails. Something like:

"Tried to resume a VM which was not suspended."

Creating a new VM when calling resume seems like a bit of a unneeded side-effect.

Revision history for this message
Kei Masumoto (masumotok) wrote :

Hi, thanks for review!

> Also, if there is a specific case you're trying to handle, can you please
> add a comment to the code so that everyone knows what it is.
Before I wrote some comments in source code, let me explain.

I have encountered situations that /etc/libvirt/qemu/instance-<id>.xml was erased by admin's mistakes after suspending instance( or anytime when instances have problem). Then just calling dom.create() doen't work(raise exception). I know that situation is not normally happens, so that error handling isn't inevitable and not essential. But few line of error handling code don’t disturb anything.

Can I add exception handling and comments, or should I erase all error handling here?

Revision history for this message
Kei Masumoto (masumotok) wrote :

Sorry Brian, I didn’t check your reply.

> Creating a new VM when calling resume seems like a bit of a unneeded side-effect.
OK. Error handling here should be erased.

Revision history for this message
Jay Pipes (jaypipes) wrote :

lgtm.

review: Approve
Revision history for this message
Kei Masumoto (masumotok) wrote :

Thanks for reviewing, Jay!

Revision history for this message
Brian Lamar (blamar) wrote :

I'd love to see some tests, but we're really not to the point where tests are a viable option. Also, because testing it so difficult I think there are some error cases that we're missing appropriate handling for... however this is better than not having it implemented in my opinion so thank you for implementing! :)

review: Approve
Revision history for this message
Devin Carlen (devcamcar) wrote :

lgtm!

review: Approve
Revision history for this message
Vish Ishaya (vishvananda) wrote :

this looks good to me as well

review: Approve
Revision history for this message
Dan Prince (dan-prince) wrote :

lgtm.

review: Approve
Revision history for this message
Kei Masumoto (masumotok) wrote :

To all reviewers: Thanks for taking time and review!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'nova/virt/libvirt/connection.py'
2--- nova/virt/libvirt/connection.py 2011-05-19 20:25:57 +0000
3+++ nova/virt/libvirt/connection.py 2011-05-30 05:18:25 +0000
4@@ -488,19 +488,27 @@
5
6 @exception.wrap_exception
7 def pause(self, instance, callback):
8- raise exception.ApiError("pause not supported for libvirt.")
9+ """Pause VM instance"""
10+ dom = self._lookup_by_name(instance.name)
11+ dom.suspend()
12
13 @exception.wrap_exception
14 def unpause(self, instance, callback):
15- raise exception.ApiError("unpause not supported for libvirt.")
16+ """Unpause paused VM instance"""
17+ dom = self._lookup_by_name(instance.name)
18+ dom.resume()
19
20 @exception.wrap_exception
21 def suspend(self, instance, callback):
22- raise exception.ApiError("suspend not supported for libvirt")
23+ """Suspend the specified instance"""
24+ dom = self._lookup_by_name(instance.name)
25+ dom.managedSave(0)
26
27 @exception.wrap_exception
28 def resume(self, instance, callback):
29- raise exception.ApiError("resume not supported for libvirt")
30+ """resume the specified instance"""
31+ dom = self._lookup_by_name(instance.name)
32+ dom.create()
33
34 @exception.wrap_exception
35 def rescue(self, instance):