Merge lp:~zulcss/nova/nova-lxc into lp:~hudson-openstack/nova/trunk

Proposed by Chuck Short
Status: Superseded
Proposed branch: lp:~zulcss/nova/nova-lxc
Merge into: lp:~hudson-openstack/nova/trunk
Diff against target: 355 lines (+168/-61)
5 files modified
Authors (+1/-0)
nova/tests/test_virt.py (+43/-0)
nova/virt/disk.py (+32/-0)
nova/virt/libvirt.xml.template (+72/-59)
nova/virt/libvirt_conn.py (+20/-2)
To merge this branch: bzr merge lp:~zulcss/nova/nova-lxc
Reviewer Review Type Date Requested Status
Vish Ishaya (community) Needs Fixing
termie (community) Needs Fixing
Thierry Carrez (community) ffe Approve
Soren Hansen (community) Needs Fixing
Rick Clark (community) Approve
Jay Pipes (community) Approve
Review via email: mp+51469@code.launchpad.net

This proposal has been superseded by a proposal from 2011-03-28.

Description of the change

This branch adds support for linux containers (LXC) to nova. It uses the libvirt LXC driver to start and stop the instance.

To post a comment you must log in.
Revision history for this message
Vish Ishaya (vishvananda) wrote :

While I'm very excited about using lxc with nova, I am unable to get this to work. The instance reports being launched correctly but doesn't seem to actually succeed
virsh --connect lxc:/// list shows nothing and I get the following error in
/var/log/libvirt/lxc/instance-00000001.log
17:09:05.465: error : lxcFdForward:231 : read of fd 7 failed: Input/output error
Can we get some sort of instructions as to the setup necessary to make this work. Does it require a special image? Does the image have to match the host os? Do we need to do special setup to get it to work? apt-get install lxc? anything else?

FWIW I am testing using the ttylinux (ami-tty). Maverick is my host os.

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

Ok, I think I figured this out mostly. I created an ubuntu image using lxc-ubuntu and it seems to work properly. The busybox image launches properly as well but doesn't seem to be running sshd. I think having a ttylinux/busybox version that runs sshd and cloud-init would be very useful for testing.

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

Hi Chuck!

149 + utils.execute('mkdir -p %s' % container_dir)

Does the above need a sudo?

Also, I note that there are no test cases included for this new functionality. Could we add some?

Cheers!
jay

review: Needs Information
Revision history for this message
Chuck Short (zulcss) wrote :

> Hi Chuck!
>
> 149 + utils.execute('mkdir -p %s' % container_dir)
>
> Does the above need a sudo?
>
> Also, I note that there are no test cases included for this new functionality.
> Could we add some?
>
> Cheers!
> jay

Hi Jay,

It doesnt need sudo because its running as the nova user. Also the tests were added in revision 753.

chuck

Revision history for this message
Chuck Short (zulcss) wrote :

> While I'm very excited about using lxc with nova, I am unable to get this to
> work. The instance reports being launched correctly but doesn't seem to
> actually succeed
> virsh --connect lxc:/// list shows nothing and I get the following error in
> /var/log/libvirt/lxc/instance-00000001.log
> 17:09:05.465: error : lxcFdForward:231 : read of fd 7 failed: Input/output
> error
> Can we get some sort of instructions as to the setup necessary to make this
> work. Does it require a special image? Does the image have to match the host
> os? Do we need to do special setup to get it to work? apt-get install lxc?
> anything else?
>
> FWIW I am testing using the ttylinux (ami-tty). Maverick is my host os.

Right Ive added some documentation. It can be found at:

http://wiki.openstack.org/LXC

You will also need a newer version of libvirt (preferably the one from natty).

chuck

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

Ok, we definitely need to add the new libvirt to our ppa to support older installs.

I'm still working on getting this running properly. I'll let you know if i run into any issues.

Vish

On Feb 28, 2011, at 10:59 AM, Chuck Short wrote:

>> While I'm very excited about using lxc with nova, I am unable to get this to
>> work. The instance reports being launched correctly but doesn't seem to
>> actually succeed
>> virsh --connect lxc:/// list shows nothing and I get the following error in
>> /var/log/libvirt/lxc/instance-00000001.log
>> 17:09:05.465: error : lxcFdForward:231 : read of fd 7 failed: Input/output
>> error
>> Can we get some sort of instructions as to the setup necessary to make this
>> work. Does it require a special image? Does the image have to match the host
>> os? Do we need to do special setup to get it to work? apt-get install lxc?
>> anything else?
>>
>> FWIW I am testing using the ttylinux (ami-tty). Maverick is my host os.
>
> Right Ive added some documentation. It can be found at:
>
> http://wiki.openstack.org/LXC
>
> You will also need a newer version of libvirt (preferably the one from natty).
>
> chuck
> --
> https://code.launchpad.net/~zulcss/nova/nova-lxc/+merge/51469
> You are reviewing the proposed merge of lp:~zulcss/nova/nova-lxc into lp:nova.

Revision history for this message
Soren Hansen (soren) wrote :

You don't seem to ever free the loopback you set up in setup_container?

review: Needs Information
Revision history for this message
Chuck Short (zulcss) wrote :
Revision history for this message
Soren Hansen (soren) wrote :

2011/3/7 Chuck Short <email address hidden>:
> http://bazaar.launchpad.net/~zulcss/nova/nova-lxc/revision/752

AFAICT, that will still leak loopback devices (and corresponding
mapped partitions)?

--
Soren Hansen        | http://linux2go.dk/
Ubuntu Developer    | http://www.ubuntu.com/
OpenStack Developer | http://www.openstack.org/

Revision history for this message
Chuck Short (zulcss) wrote :

> 2011/3/7 Chuck Short <email address hidden>:
> > http://bazaar.launchpad.net/~zulcss/nova/nova-lxc/revision/752
>
> AFAICT, that will still leak loopback devices (and corresponding
> mapped partitions)?
>
> --
> Soren Hansen        | http://linux2go.dk/
> Ubuntu Developer    | http://www.ubuntu.com/
> OpenStack Developer | http://www.openstack.org/

Ok I think I cleared this up in the last commit.

chuck

Revision history for this message
Nachi Ueno (nati-ueno) wrote :

LXC support on Nova is pretty cool!
Would you please add procedure in http://wiki.openstack.org/LXC to nova.sh script ?

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

lgtm.

review: Approve
Revision history for this message
Soren Hansen (soren) wrote :

> > 2011/3/7 Chuck Short <email address hidden>:
> > > http://bazaar.launchpad.net/~zulcss/nova/nova-lxc/revision/752
> > AFAICT, that will still leak loopback devices (and corresponding
> > mapped partitions)?
> Ok I think I cleared this up in the last commit.

Sorry, I don't see it. Can you explain how the partition devices and loopback devices get cleaned up? It's not obvious to me, I'm afraid.

review: Needs Information
Revision history for this message
Chuck Short (zulcss) wrote :
Revision history for this message
Soren Hansen (soren) wrote :

2011/3/14 Chuck Short <email address hidden>:
> Soren,
>
> Please see:
>
> http://bazaar.launchpad.net/~zulcss/nova/nova-lxc/revision/758

I did, but I still don't see it. Ok, from the top:

=== modified file 'nova/virt/disk.py'
--- nova/virt/disk.py 2011-03-12 23:53:10 +0000
+++ nova/virt/disk.py 2011-03-14 20:56:29 +0000
@@ -115,6 +115,39 @@
         _unlink_device(device, nbd)

+def setup_container(image, container_dir=None, partition=None, nbd=False):
+ """Setup the LXC container
+
+ It will mount the loopback image to the container directory in order
+ to create the root filesystem for the container
+ """
+ device = _link_device(image, nbd)

Ok, so here you attach the disk image through nbd or losetup.

+ try:
+ if not partition is None:
+ # create partition
+ utils.execute('sudo kpartx -a %s' % device)

This needs to be changed to utils.execute('sudo', 'kparts', '-a', etc)..

Also, why is this needed? nbd devices should benefit from the kernel's
built in partition detection. If it's for non-nbd devices, make it
conditional.

+ mapped_device = '/dev/mapper/%p%s' % (device.split('/')[-1],
+ partition)
+ else:
+ mapped_device = device
+
+ utils.execute('sudo mount %s %s' %(mapped_device, container_dir))

This too needs to be changed to pass individual argv elements separately.

+
+ except Exception as e:
+ LOG.warn(_('Unable to mount container'))
+ if not partition is None:
+ # remove partitions
+ utils.execute('sudo kpartx -s %s' % device)

What does "kpartx -s" do?

+ _unlink_device(device, nbd)
+
+def destroy_container(target, instance, nbd=False):
+ """Destroy the container once it terminates"""
+ try:
+ utils.execute('sudo umount %s/rootfs' % target)

Here you unmount the rootfs. What about the mapped partitions from
kparts and the nbd device. Where do they get cleaned up?

+ image = os.path.join(FLAGS.instances_path, instance['name'],
'' + 'disk')

What's this for?

+ except Exception as e:

This is scary. Can you make this more specific (and you can leave off
the "as e" bit if you're not using the exception for anything.

--
Soren Hansen        | http://linux2go.dk/
Ubuntu Developer    | http://www.ubuntu.com/
OpenStack Developer | http://www.openstack.org/

Revision history for this message
Soren Hansen (soren) wrote :

> === modified file 'nova/virt/disk.py'
> --- nova/virt/disk.py 2011-03-12 23:53:10 +0000
> +++ nova/virt/disk.py 2011-03-15 10:46:53 +0000
> @@ -115,6 +115,34 @@
> _unlink_device(device, nbd)
>
>
> +def setup_container(image, container_dir=None, partition=None, nbd=False):
> + """Setup the LXC container
> +
> + It will mount the loopback image to the container directory in order
> + to create the root filesystem for the container
> + """
> + device = _link_device(image, nbd)
> + err = utils.execute('sudo', 'mount', mapped_device, container_dir)
> + if err:

This will always evaluate as True. utils.execute returns a 2-tuple.

> + raise exception.Error(_('Failed to mount filesystem: %s')
> + % err)
> + _unlink_device(device, nbd)

> +
> +def destroy_container(target, instance, nbd=False):
> + """Destroy the container once it terminates"""
> + try:
> + container_dir = '%s/rootfs' % target
> + utils.execute('sudo', 'umount', container_dir)
> + finally:
> + image = os.path.join(FLAGS.instances_path, instance['name'], '' + 'disk')
> + out, err = utils.execute('sudo', 'losetup', '--find', '--show', image)
> + device = out.strip()
> + if err:
> + raise execption.Error(_('Could not find loopback image: %s')
> + %err)
> + utils.execute('sudo', 'losetup', '--detach', device)
> +
> +

You're still not clearing out the mapped partitions that you create with kpartx. Instead you seem to be creating an additional loopback device or is that not what losetup --find does?

review: Needs Fixing
Revision history for this message
Soren Hansen (soren) wrote :

Ok, Chuck just pointed out to me that he stopped using kpartx altogether, which I completely missed in my last review, so here's another review where I stop being an idiot.

> === modified file 'nova/virt/disk.py'
> --- nova/virt/disk.py 2011-03-12 23:53:10 +0000
> +++ nova/virt/disk.py 2011-03-15 10:46:53 +0000
> @@ -115,6 +115,34 @@
> _unlink_device(device, nbd)
>
>
> +def setup_container(image, container_dir=None, partition=None, nbd=False):
> + """Setup the LXC container
> +
> + It will mount the loopback image to the container directory in order
> + to create the root filesystem for the container
> + """
> + device = _link_device(image, nbd)
> + err = utils.execute('sudo', 'mount', mapped_device, container_dir)

What's mapped_device? I'm not really sure what the assumptions here are. In what format are you expecting these LXC images to be distributed?

> + if err:

The comment from my previous review still applies here.

> + raise exception.Error(_('Failed to mount filesystem: %s')
> + % err)
> + _unlink_device(device, nbd)
> +
> +def destroy_container(target, instance, nbd=False):
> + """Destroy the container once it terminates"""
> + try:
> + container_dir = '%s/rootfs' % target
> + utils.execute('sudo', 'umount', container_dir)
> + finally:
> + image = os.path.join(FLAGS.instances_path, instance['name'], '' + 'disk')
> + out, err = utils.execute('sudo', 'losetup', '--find', '--show', image)

I still think this will create another loopback device..

> + device = out.strip()
> + if err:
> + raise execption.Error(_('Could not find loopback image: %s')
> + %err)
> + utils.execute('sudo', 'losetup', '--detach', device)

...and then remove it again?

With regard to unit tests, it would be nice if they'd check:
 * if os/type was set to exe,
 * if os/init was set to /sbin/init,
 * and that that filesystem was set correctly.

After all, the number of conditionals in that template is making it a bit difficult to verify by just looking at it.

review: Needs Fixing
Revision history for this message
Chuck Short (zulcss) wrote :

> Ok, Chuck just pointed out to me that he stopped using kpartx altogether,
> which I completely missed in my last review, so here's another review where I
> stop being an idiot.
>
> > === modified file 'nova/virt/disk.py'
> > --- nova/virt/disk.py 2011-03-12 23:53:10 +0000
> > +++ nova/virt/disk.py 2011-03-15 10:46:53 +0000
> > @@ -115,6 +115,34 @@
> > _unlink_device(device, nbd)
> >
> >
> > +def setup_container(image, container_dir=None, partition=None, nbd=False):
> > + """Setup the LXC container
> > +
> > + It will mount the loopback image to the container directory in order
> > + to create the root filesystem for the container
> > + """
> > + device = _link_device(image, nbd)
> > + err = utils.execute('sudo', 'mount', mapped_device, container_dir)
>
> What's mapped_device? I'm not really sure what the assumptions here are. In
> what format are you expecting these LXC images to be distributed?

Ok well spotted. The assumption is that they are using a regular UEC image. LXC container support has already been added in the latest imgaes.

>
> > + if err:
>
> The comment from my previous review still applies here.
>
> > + raise exception.Error(_('Failed to mount filesystem: %s')
> > + % err)
> > + _unlink_device(device, nbd)
> > +
> > +def destroy_container(target, instance, nbd=False):
> > + """Destroy the container once it terminates"""
> > + try:
> > + container_dir = '%s/rootfs' % target
> > + utils.execute('sudo', 'umount', container_dir)
> > + finally:
> > + image = os.path.join(FLAGS.instances_path, instance['name'], '' +
> 'disk')
> > + out, err = utils.execute('sudo', 'losetup', '--find', '--show',
> image)
>
> I still think this will create another loopback device..
>

It wont, its getting the information associated with the associated image.

> > + device = out.strip()
> > + if err:
> > + raise execption.Error(_('Could not find loopback image: %s')
> > + %err)
> > + utils.execute('sudo', 'losetup', '--detach', device)
>
> ...and then remove it again?
>

Deleting it when the image is shutdown.

>
> With regard to unit tests, it would be nice if they'd check:
> * if os/type was set to exe,
> * if os/init was set to /sbin/init,
> * and that that filesystem was set correctly.
>

Sure I can do that.

> After all, the number of conditionals in that template is making it a bit
> difficult to verify by just looking at it.

Revision history for this message
Soren Hansen (soren) wrote :

2011/3/15 Chuck Short <email address hidden>:
>> What's mapped_device? I'm not really sure what the assumptions here are. In
>> what format are you expecting these LXC images to be distributed?
>
> Ok well spotted. The assumption is that they are using a regular UEC image. LXC container support has already been added in the latest imgaes.

So only raw disk images are supported? Ok.

>>> +        raise exception.Error(_('Failed to mount filesystem: %s')
>>> +                             % err)
>>> +        _unlink_device(device, nbd)
>>> +
>>> +def destroy_container(target, instance, nbd=False):
>>> +    """Destroy the container once it terminates"""
>>> +    try:
>>> +        container_dir = '%s/rootfs' % target
>>> +        utils.execute('sudo', 'umount', container_dir)
>>> +    finally:
>>> +        image = os.path.join(FLAGS.instances_path, instance['name'], '' +
>> 'disk')
>>> +        out, err = utils.execute('sudo', 'losetup', '--find', '--show',
>> image)
>> I still think this will create another loopback device..
> It wont, its getting the information associated with the associated image

Revision history for this message
Soren Hansen (soren) wrote :

My review seems to have gotten cut short. :( /me retries

Revision history for this message
Soren Hansen (soren) wrote :

These are the missing bits:

>> I still think this will create another loopback device..
> It wont, its getting the information associated with the associated image.

What am I doing wrong/differently here:
[2011-03-15 13:45:03] soren@lenny:/tmp/loopbacktest$ dd if=/dev/zero
of=testimg.img bs=512 count=1k
1024+0 blokke ind
1024+0 blokke ud
524288 byte (524 kB) kopieret, 0,0075592 s, 69,4 MB/s
[2011-03-15 13:45:15] soren@lenny:/tmp/loopbacktest$ sudo losetup
--find --show $(pwd)/testimg.img
[sudo] password for soren:
/dev/loop0
[2011-03-15 13:45:38] soren@lenny:/tmp/loopbacktest$ sudo losetup
--find --show $(pwd)/testimg.img
/dev/loop1
[2011-03-15 13:45:38] soren@lenny:/tmp/loopbacktest$ sudo losetup
--find --show $(pwd)/testimg.img
/dev/loop2
[2011-03-15 13:45:39] soren@lenny:/tmp/loopbacktest$ sudo losetup
--find --show $(pwd)/testimg.img
/dev/loop3
[2011-03-15 13:45:39] soren@lenny:/tmp/loopbacktest$ sudo losetup
--find --show $(pwd)/testimg.img
/dev/loop4
[2011-03-15 13:45:39] soren@lenny:/tmp/loopbacktest$ sudo losetup
--find --show $(pwd)/testimg.img
/dev/loop5
[2011-03-15 13:45:40] soren@lenny:/tmp/loopbacktest$ sudo losetup
--find --show $(pwd)/testimg.img
/dev/loop6
[2011-03-15 13:45:40] soren@lenny:/tmp/loopbacktest$ sudo losetup
--find --show $(pwd)/testimg.img
/dev/loop7

Each time I get a new loopback device.

Revision history for this message
Soren Hansen (soren) wrote :

In my defense, it was must lovelier formatted when I sent it the first time. :-/

Revision history for this message
Chuck Short (zulcss) wrote :

> In my defense, it was must lovelier formatted when I sent it the first time.
> :-/

Hah..

I think I addressed your comments, it should be ready to go now.

chuck

Revision history for this message
Soren Hansen (soren) wrote :

======================================================================
ERROR: Failure: SyntaxError (can't assign to function call (test_virt.py, line 262))
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/pymodules/python2.7/nose/loader.py", line 382, in loadTestsFromName
    addr.filename, addr.module)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 39, in importFromPath
    return self.importFromDir(dir_path, fqname)
  File "/usr/lib/pymodules/python2.7/nose/importer.py", line 86, in importFromDir
    mod = load_module(part_fqname, fh, filename, desc)
  File "/home/soren/src/openstack/nova/nova/nova/tests/test_virt.py", line 262
    for i (check, expected_result) in enumerate(check):
SyntaxError: can't assign to function call

======================================================================
FAIL: test_authors_up_to_date (nova.tests.test_misc.ProjectTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/soren/src/openstack/nova/nova/nova/tests/test_misc.py", line 59, in test_authors_up_to_date
    '%r not listed in Authors' % missing)
AssertionError: set([u'<email address hidden>']) not listed in Authors

----------------------------------------------------------------------

Revision history for this message
Chuck Short (zulcss) wrote :

> ======================================================================
> ERROR: Failure: SyntaxError (can't assign to function call (test_virt.py, line
> 262))
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File "/usr/lib/pymodules/python2.7/nose/loader.py", line 382, in
> loadTestsFromName
> addr.filename, addr.module)
> File "/usr/lib/pymodules/python2.7/nose/importer.py", line 39, in
> importFromPath
> return self.importFromDir(dir_path, fqname)
> File "/usr/lib/pymodules/python2.7/nose/importer.py", line 86, in
> importFromDir
> mod = load_module(part_fqname, fh, filename, desc)
> File "/home/soren/src/openstack/nova/nova/nova/tests/test_virt.py", line 262
> for i (check, expected_result) in enumerate(check):
> SyntaxError: can't assign to function call
>
> ======================================================================
> FAIL: test_authors_up_to_date (nova.tests.test_misc.ProjectTestCase)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
> File "/home/soren/src/openstack/nova/nova/nova/tests/test_misc.py", line 59,
> in test_authors_up_to_date
> '%r not listed in Authors' % missing)
> AssertionError: set([u'<email address hidden>']) not listed in Authors
>
> ----------------------------------------------------------------------

Ok thats fixed now...test suite runs cleanly now.

Revision history for this message
Soren Hansen (soren) wrote :

nova/tests/test_virt.py:375:20: E111 indentation is not a multiple of four
                   self.assertEqual(check(tree),
                   ^
    Use 4 spaces per indentation level.

    For really old code that you don't want to mess up, you can continue to
    use 8-space tabs.

    Okay: a = 1
    Okay: if a == 0:\n a = 1
    E111: a = 1

    Okay: for item in items:\n pass
    E112: for item in items:\npass

    Okay: a = 1\nb = 2
    E113: a = 1\n b = 2
nova/virt/disk.py:135:1: W293 blank line contains whitespace

^
    JCR: Trailing whitespace is superfluous.
    FBM: Except when it occurs as part of a blank line (i.e. the line is
         nothing but whitespace). According to Python docs[1] a line with only
         whitespace is considered a blank line, and is to be ignored. However,
         matching a blank line to its indentation level avoids mistakenly
         terminating a multi-line statement (e.g. class declaration) when
         pasting code into the standard Python interpreter.

         [1] http://docs.python.org/reference/lexical_analysis.html#blank-lines

    The warning returned varies on whether the line itself is blank, for easier
    filtering for those who want to indent their blank lines.

    Okay: spam(1)
    W291: spam(1)\s
    W293: class Foo(object):\n \n bang = 12

Revision history for this message
Chuck Short (zulcss) wrote :

> nova/tests/test_virt.py:375:20: E111 indentation is not a multiple of four
> self.assertEqual(check(tree),
> ^
> Use 4 spaces per indentation level.
>
> For really old code that you don't want to mess up, you can continue to
> use 8-space tabs.
>
> Okay: a = 1
> Okay: if a == 0:\n a = 1
> E111: a = 1
>
> Okay: for item in items:\n pass
> E112: for item in items:\npass
>
> Okay: a = 1\nb = 2
> E113: a = 1\n b = 2
> nova/virt/disk.py:135:1: W293 blank line contains whitespace
>
> ^
> JCR: Trailing whitespace is superfluous.
> FBM: Except when it occurs as part of a blank line (i.e. the line is
> nothing but whitespace). According to Python docs[1] a line with only
> whitespace is considered a blank line, and is to be ignored. However,
> matching a blank line to its indentation level avoids mistakenly
> terminating a multi-line statement (e.g. class declaration) when
> pasting code into the standard Python interpreter.
>
> [1] http://docs.python.org/reference/lexical_analysis.html#blank-
> lines
>
> The warning returned varies on whether the line itself is blank, for
> easier
> filtering for those who want to indent their blank lines.
>
> Okay: spam(1)
> W291: spam(1)\s
> W293: class Foo(object):\n \n bang = 12

And thats fixed.

Revision history for this message
Soren Hansen (soren) wrote :
Download full text (5.9 KiB)

> === modified file 'Authors'
> --- Authors 2011-03-15 18:19:47 +0000
> +++ Authors 2011-03-17 12:08:41 +0000
> @@ -11,6 +11,7 @@
> Chmouel Boudjnah <email address hidden>
> Chris Behrens <email address hidden>
> Christian Berendt <email address hidden>
> +Chuck Short <email address hidden>

If you want a different e-mail here, you can look at the .mailmap file.

> Cory Wright <email address hidden>
> Dan Prince <email address hidden>
> David Pravec <email address hidden>
>
> === modified file 'nova/tests/test_virt.py'
> --- nova/tests/test_virt.py 2011-03-14 17:59:41 +0000
> +++ nova/tests/test_virt.py 2011-03-17 12:08:41 +0000
[...]
> @@ -302,8 +342,9 @@
> (lambda t: t.find('./devices/serial/source').get(
> 'path').split('/')[1], 'console.log'),
> (lambda t: t.find('./memory').text, '2097152')]
> +
> if rescue:
> - common_checks += [
> + common_checks = [

Why? That will effectively disable a bunch of checks.

> (lambda t: t.findall('./devices/disk/source')[0].get(
> 'file').split('/')[1], 'disk.rescue'),
> (lambda t: t.findall('./devices/disk/source')[1].get(
> @@ -326,14 +367,14 @@
> xml = conn.to_xml(instance_ref, rescue)
> tree = xml_to_tree(xml)
> for i, (check, expected_result) in enumerate(checks):
> - self.assertEqual(check(tree),
> - expected_result,
> - '%s failed check %d' % (xml, i))
> + self.assertEqual(check(tree),
> + expected_result,
> + '%s failed check %d' % (xml, i))

What's going on here?

>
> for i, (check, expected_result) in enumerate(common_checks):
> - self.assertEqual(check(tree),
> - expected_result,
> - '%s failed common check %d' % (xml, i))
> + self.assertEqual(check(tree),
> + expected_result,
> + '%s failed common check %d' % (xml, i))

...and here?

> === modified file 'nova/virt/disk.py'
> --- nova/virt/disk.py 2011-03-14 17:59:41 +0000
> +++ nova/virt/disk.py 2011-03-17 12:08:41 +0000
> @@ -23,6 +23,7 @@
> """
>
> import os
> +import string
> import tempfile
> import time
>
> @@ -115,6 +116,36 @@
> _unlink_device(device, nbd)
>
>
> +def setup_container(image, container_dir=None, partition=None, nbd=False):
> + """Setup the LXC container
> +
> + It will mount the loopback image to the container directory in order
> + to create the root filesystem for the container
> + """
> + device = _link_device(image, nbd)
> + err = utils.execute('sudo', 'mount', device, container_dir)
> + if err:
> + raise exception.Error(_('Failed to mount filesystem: %s')
> + % err)
> + _unlink_device(device, nbd)
> +
> +
> +def destroy_container(target, instance, nbd=False):
> + """Destroy the container once it termin...

Read more...

review: Needs Fixing
Revision history for this message
Soren Hansen (soren) wrote :

> > nova/tests/test_virt.py:375:20: E111 indentation is not a multiple of four
[...]
> > nova/virt/disk.py:135:1: W293 blank line contains whitespace
> And thats fixed.

Um.. No. Maybe you forgot to commit/push?

Revision history for this message
Chuck Short (zulcss) wrote :
Download full text (6.5 KiB)

> > === modified file 'Authors'
> > --- Authors 2011-03-15 18:19:47 +0000
> > +++ Authors 2011-03-17 12:08:41 +0000
> > @@ -11,6 +11,7 @@
> > Chmouel Boudjnah <email address hidden>
> > Chris Behrens <email address hidden>
> > Christian Berendt <email address hidden>
> > +Chuck Short <email address hidden>
>
> If you want a different e-mail here, you can look at the .mailmap file.
>
> > Cory Wright <email address hidden>
> > Dan Prince <email address hidden>
> > David Pravec <email address hidden>
> >
> > === modified file 'nova/tests/test_virt.py'
> > --- nova/tests/test_virt.py 2011-03-14 17:59:41 +0000
> > +++ nova/tests/test_virt.py 2011-03-17 12:08:41 +0000
> [...]
> > @@ -302,8 +342,9 @@
> > (lambda t: t.find('./devices/serial/source').get(
> > 'path').split('/')[1], 'console.log'),
> > (lambda t: t.find('./memory').text, '2097152')]
> > +
> > if rescue:
> > - common_checks += [
> > + common_checks = [
>
> Why? That will effectively disable a bunch of checks.

This was a left over when I was trying to add tests for lxc.
>
> > (lambda t: t.findall('./devices/disk/source')[0].get(
> > 'file').split('/')[1], 'disk.rescue'),
> > (lambda t: t.findall('./devices/disk/source')[1].get(
> > @@ -326,14 +367,14 @@
> > xml = conn.to_xml(instance_ref, rescue)
> > tree = xml_to_tree(xml)
> > for i, (check, expected_result) in enumerate(checks):
> > - self.assertEqual(check(tree),
> > - expected_result,
> > - '%s failed check %d' % (xml, i))
> > + self.assertEqual(check(tree),
> > + expected_result,
> > + '%s failed check %d' % (xml, i))
>
> What's going on here?

ditto

> >
> > for i, (check, expected_result) in enumerate(common_checks):
> > - self.assertEqual(check(tree),
> > - expected_result,
> > - '%s failed common check %d' % (xml, i))
> > + self.assertEqual(check(tree),
> > + expected_result,
> > + '%s failed common check %d' % (xml, i))
>
> ...and here?

done

> > === modified file 'nova/virt/disk.py'
> > --- nova/virt/disk.py 2011-03-14 17:59:41 +0000
> > +++ nova/virt/disk.py 2011-03-17 12:08:41 +0000
> > @@ -23,6 +23,7 @@
> > """
> >
> > import os
> > +import string
> > import tempfile
> > import time
> >
> > @@ -115,6 +116,36 @@
> > _unlink_device(device, nbd)
> >
> >
> > +def setup_container(image, container_dir=None, partition=None, nbd=False):
> > + """Setup the LXC container
> > +
> > + It will mount the loopback image to the container directory in order
> > + to create the root filesystem for the container
> > + """
> > + device = _link_device(image, nbd)
> > + err = utils.execute('sudo', 'mount', device, container_dir)
> > + if err:
> > + raise except...

Read more...

Revision history for this message
Rick Clark (dendrobates) wrote :

I've tested this and it works for me. I am approving this, but I want to make sure that Soren is satisfied that all his concerns were addressed before merging it.

review: Approve
Revision history for this message
Soren Hansen (soren) wrote :
Download full text (6.1 KiB)

> === modified file 'nova/virt/disk.py'
> --- nova/virt/disk.py 2011-03-17 15:29:04 +0000
> +++ nova/virt/disk.py 2011-03-24 10:26:05 +0000
> @@ -115,6 +115,37 @@
> _unlink_device(device, nbd)
>
>
> +def setup_container(image, container_dir=None, partition=None):

You don't use the partition kwarg, so don't pass it. It's confusing.

> + """Setup the LXC container
> +
> + It will mount the loopback image to the container directory in order
> + to create the root filesystem for the container
> + """
> + nbd = False
> + device = _link_device(image, nbd)
> + err = utils.execute('sudo', 'mount', device, container_dir)
> + if err:
> + raise exception.Error(_('Failed to mount filesystem: %s')
> + % err)
> + _unlink_device(device, nbd)
> +
> +
> +def destroy_container(target, instance):
> + """Destroy the container once it terminates
> +
> + It will umount the container that is mounted, try to find the loopback
> + device associated with the container and delete it.
> + """
> + try:
> + container_dir = '%s/rootfs' % target
> + utils.execute('sudo', 'umount', container_dir)
> + finally:
> + for loop in utils.popen('sudo losetup -a').readlines():

There's no such things as utils.popen.

> + if instance['name'] in loop:
> + device = loop.split(loop, ':')
> + utils.execute('sudo', 'losetup', '--detach', device)
> +
> +
> def _link_device(image, nbd):
> """Link image to device using loopback or nbd"""
> if nbd:
>
> === modified file 'nova/virt/libvirt.xml.template'
> --- nova/virt/libvirt.xml.template 2011-03-23 19:46:24 +0000
> +++ nova/virt/libvirt.xml.template 2011-03-24 10:26:05 +0000
> @@ -2,6 +2,12 @@
> <name>${name}</name>
> <memory>${memory_kb}</memory>
> <os>
> +#if $type == 'lxc'
> + #set $disk_prefix = ''
> + #set $disk_bus = ''
> + <type>exe</type>
> + <init>/sbin/init</init>
> +#else
> #if $type == 'uml'
> #set $disk_prefix = 'ubd'
> #set $disk_bus = 'uml'
> @@ -37,6 +43,7 @@
> <boot dev="hd" />
> #end if
> #end if
> + #end if

Your indentations don't line up.

> #end if
> </os>
> <features>
> @@ -44,6 +51,12 @@
> </features>
> <vcpu>${vcpus}</vcpu>
> <devices>
> +#if $type == 'lxc'
> + <filesystem type='mount'>
> + <source dir='${basepath}/rootfs'/>
> + <target dir='/'/>
> + </filesystem>
> +#else

You're still missing a unit test for this. What's the difficulty?

> #if $getVar('rescue', False)
> <disk type='file'>
> <driver type='${driver_type}'/>
> @@ -68,6 +81,7 @@
> <target dev='${disk_prefix}b' bus='${disk_bus}'/>
> </disk>
> #end if
> + #end if

Again, odd indentation. It's hard enough to read as it is. Please make sure that #if's line up with their corresponding "#end if"s.

> === modified file 'nova/virt/libvirt_conn.py'
> --- nova/virt/libvirt_conn.py 2011-03-24 10:15:52 +0000
> +++ nova/virt/libvirt_conn.py 2011-03-24 10:26:05 +0000
> @@ -20,7 +20,7 @@
> """
> A connection to a hypervisor through libvirt.
>
> -Supports KVM, ...

Read more...

review: Needs Fixing
Revision history for this message
Soren Hansen (soren) wrote :

> === modified file 'nova/virt/disk.py'
> --- nova/virt/disk.py 2011-03-14 17:59:41 +0000
> +++ nova/virt/disk.py 2011-03-24 22:32:44 +0000
> @@ -115,6 +115,38 @@
> _unlink_device(device, nbd)
>
>
> +def setup_container(image, container_dir=None, partition=None):

Still accepting an unused partition argument.

> + """Setup the LXC container
> +
> + It will mount the loopback image to the container directory in order
> + to create the root filesystem for the container
> + """
> + nbd = False
> + device = _link_device(image, nbd)
> + err = utils.execute('sudo', 'mount', device, container_dir)
> + if err:

As I've pointed out before, utils.execute returns a 2-tuple. 2-tuples evaluate to True, so as far as I can see, this will always evaluate to True?

> + raise exception.Error(_('Failed to mount filesystem: %s')
> + % err)
> + _unlink_device(device, nbd)
> +
> +

> +def destroy_container(target, instance):
> + """Destroy the container once it terminates
> +
> + It will umount the container that is mounted, try to find the loopback
> + device associated with the container and delete it.
> + """
> + try:
> + container_dir = '%s/rootfs' % target
> + utils.execute('sudo', 'umount', container_dir)
> + finally:
> + out, err = utils('sudo', 'losetup', '-a')

There's no function named utils.

> + for loop in out.splitlines():
> + if instance['name'] in loop:
> + device = loop.split(loop, ':')
> + utils.execute('sudo', 'losetup', '--detach', device)
> +
> +
> def _link_device(image, nbd):
> """Link image to device using loopback or nbd"""
> if nbd:
>

> === modified file 'nova/virt/libvirt.xml.template'
> --- nova/virt/libvirt.xml.template 2011-03-02 01:12:47 +0000
> +++ nova/virt/libvirt.xml.template 2011-03-24 22:32:44 +0000

Ok, if you take what you have there now and apply this patch:

    http://paste.ubuntu.com/585085/plain/

...then it's fine. Indentation-wise, at least.

> === modified file 'nova/virt/libvirt_conn.py'
> --- nova/virt/libvirt_conn.py 2011-03-24 17:53:54 +0000
> +++ nova/virt/libvirt_conn.py 2011-03-24 22:32:44 +0000
> @@ -720,6 +734,11 @@
> disk.inject_data(basepath('disk'), key, net,
> partition=target_partition,
> nbd=FLAGS.use_cow_images)
> +
> + if FLAGS.libvirt_type == 'lxc':
> + disk.setup_container(basepath('disk'),
> + container_dir=container_dir,
> + partition=target_partition)

...and still passing the unused partition argument.

review: Needs Fixing
Revision history for this message
Soren Hansen (soren) wrote :

This is a very cool feature, by the way. I'm really looking forward to getting it in. We're close :)

Revision history for this message
Chuck Short (zulcss) wrote :
Download full text (3.2 KiB)

> > === modified file 'nova/virt/disk.py'
> > --- nova/virt/disk.py 2011-03-14 17:59:41 +0000
> > +++ nova/virt/disk.py 2011-03-24 22:32:44 +0000
> > @@ -115,6 +115,38 @@
> > _unlink_device(device, nbd)
> >
> >
> > +def setup_container(image, container_dir=None, partition=None):
>
> Still accepting an unused partition argument.
>

Fixed

> > + """Setup the LXC container
> > +
> > + It will mount the loopback image to the container directory in order
> > + to create the root filesystem for the container
> > + """
> > + nbd = False
> > + device = _link_device(image, nbd)
> > + err = utils.execute('sudo', 'mount', device, container_dir)
> > + if err:
>
> As I've pointed out before, utils.execute returns a 2-tuple. 2-tuples evaluate
> to True, so as far as I can see, this will always evaluate to True?

There is the case where the nbd module is not loaded when the device is created so probably the the check be something like: (pseudocode)

if device:
  raise exception

>
> > + raise exception.Error(_('Failed to mount filesystem: %s')
> > + % err)
> > + _unlink_device(device, nbd)
> > +
> > +
>
> > +def destroy_container(target, instance):
> > + """Destroy the container once it terminates
> > +
> > + It will umount the container that is mounted, try to find the loopback
> > + device associated with the container and delete it.
> > + """
> > + try:
> > + container_dir = '%s/rootfs' % target
> > + utils.execute('sudo', 'umount', container_dir)
> > + finally:
> > + out, err = utils('sudo', 'losetup', '-a')
>
> There's no function named utils.
>

fixed

> > + for loop in out.splitlines():
> > + if instance['name'] in loop:
> > + device = loop.split(loop, ':')
> > + utils.execute('sudo', 'losetup', '--detach', device)
> > +
> > +
> > def _link_device(image, nbd):
> > """Link image to device using loopback or nbd"""
> > if nbd:
> >
>
> > === modified file 'nova/virt/libvirt.xml.template'
> > --- nova/virt/libvirt.xml.template 2011-03-02 01:12:47 +0000
> > +++ nova/virt/libvirt.xml.template 2011-03-24 22:32:44 +0000
>
> Ok, if you take what you have there now and apply this patch:
>
> http://paste.ubuntu.com/585085/plain/
>
> ...then it's fine. Indentation-wise, at least.
>
> > === modified file 'nova/virt/libvirt_conn.py'
> > --- nova/virt/libvirt_conn.py 2011-03-24 17:53:54 +0000
> > +++ nova/virt/libvirt_conn.py 2011-03-24 22:32:44 +0000
> > @@ -720,6 +734,11 @@
> > disk.inject_data(basepath('disk'), key, net,
> > partition=target_partition,
> > nbd=FLAGS.use_cow_images)
> > +
> > + if FLAGS.libvirt_type == 'lxc':
> > + disk.setup_container(basepath('disk'),
> > + container_dir=container_dir,
> > + partition=target_partition)
>
> ...and still passing the unused partition argument.

One or more two things, I havent added the test for the filesystem because i havent f...

Read more...

Revision history for this message
Soren Hansen (soren) wrote :

2011/3/25 Chuck Short <email address hidden>:
>> > +    """Setup the LXC container
>> > +
>> > +    It will mount the loopback image to the container directory in order
>> > +    to create the root filesystem for the container
>> > +    """
>> > +    nbd = False
>> > +    device = _link_device(image, nbd)
>> > +    err = utils.execute('sudo', 'mount', device, container_dir)
>> > +    if err:
>>
>> As I've pointed out before, utils.execute returns a 2-tuple. 2-tuples evaluate
>> to True, so as far as I can see, this will always evaluate to True?
> There is the case where the nbd module is not loaded when the device is created so probably the the check be something like: (pseudocode)
>
> if device:
>  raise exception

You've lost me entirely. My point is this: utils.execute returns a
2-tuple (or raises an exception). The 2-tuple consists of what was
output on the process' stdout and stderr, respectively. You're storing
this 2-tuple in a single variable, so "if err" will, as far as I can
see, always evaluate to True? If that is so, aren't you always
unmounting your container immediately after mounting it?

> One or more two things, I havent added the test for the filesystem because i havent figured out how to access the chunk in the xml.

There are a number of examples of poking around in the XML in the
libvirt tests already. There are even checks that verify that the disk
paths are set correctly based on whether it's a rescue system or not.
Can you share what you've tried and we can try to work it out together?

--
Soren Hansen        | http://linux2go.dk/
Ubuntu Developer    | http://www.ubuntu.com/
OpenStack Developer | http://www.openstack.org/

Revision history for this message
Thierry Carrez (ttx) wrote :

Interesting feature, self-contained, almost there: FFE granted for late merging by Tuesday EOD

review: Approve (ffe)
Revision history for this message
termie (termie) wrote :

- you've got extra whitespaces at lines: 33, 38
- 41: you should be using self.flags(libvirt_type='lxc') to override flags so that they are automatically reset at the end of the test
- your test seems to rely on libvirt being available which is not always the case, perhaps you should skip the test if libvirt is not available? otherwise it doesn't seem like it is testing anything.
- at line 51 you should move line 52 up to that line and indent line 53 to to match it
- 71: please add punctuation to your docstring
- 81: missing a space on your indent
- in the new xml template you appear to have removed a feature related to multiple nics, what was the reasoning behind that?

other than that stuff, looks pretty cool :)

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

termie mentioned this, but it appears that the following is all due to a bad merge:

         <interface type='bridge'>
255 - <source bridge='${nic.bridge_name}'/>
256 - <mac address='${nic.mac_address}'/>
257 + <source bridge='${bridge_name}'/>
258 + <mac address='${mac_address}'/>
259 <!-- <model type='virtio'/> CANT RUN virtio network right now -->
260 - <filterref filter="nova-instance-${name}-${nic.id}">
261 - <parameter name="IP" value="${nic.ip_address}" />
262 - <parameter name="DHCPSERVER" value="${nic.dhcp_server}" />
263 -#if $getVar('nic.extra_params', False)
264 - ${nic.extra_params}
265 + <filterref filter="nova-instance-${name}">
266 + <parameter name="IP" value="${ip_address}" />
267 + <parameter name="DHCPSERVER" value="${dhcp_server}" />
268 +#if $getVar('extra_params', False)
269 + ${extra_params}
270 #end if
271 -#if $getVar('nic.gateway_v6', False)
272 - <parameter name="RASERVER" value="${nic.gateway_v6}" />
273 +#if $getVar('gateway_v6', False)
274 + <parameter name="RASERVER" value="${gateway_v6}" />
275 #end if

review: Needs Fixing
lp:~zulcss/nova/nova-lxc updated
804. By Chuck Short

Merge trunk

805. By Chuck Short

Fix libvirt merge mistake

806. By Chuck Short

Add more unit tests for lxc

807. By Chuck Short

More pep8 corrections

808. By Chuck Short

Dont make the test fail

809. By Chuck Short

use self.flags in virt test

Revision history for this message
Chuck Short (zulcss) wrote :

> 2011/3/25 Chuck Short <email address hidden>:
> >> > +    """Setup the LXC container
> >> > +
> >> > +    It will mount the loopback image to the container directory in order
> >> > +    to create the root filesystem for the container
> >> > +    """
> >> > +    nbd = False
> >> > +    device = _link_device(image, nbd)
> >> > +    err = utils.execute('sudo', 'mount', device, container_dir)
> >> > +    if err:
> >>
> >> As I've pointed out before, utils.execute returns a 2-tuple. 2-tuples
> evaluate
> >> to True, so as far as I can see, this will always evaluate to True?
> > There is the case where the nbd module is not loaded when the device is
> created so probably the the check be something like: (pseudocode)
> >
> > if device:
> >  raise exception
>
> You've lost me entirely. My point is this: utils.execute returns a
> 2-tuple (or raises an exception). The 2-tuple consists of what was
> output on the process' stdout and stderr, respectively. You're storing
> this 2-tuple in a single variable, so "if err" will, as far as I can
> see, always evaluate to True? If that is so, aren't you always
> unmounting your container immediately after mounting it?

I see what you are saying now I have corrected this now.

>
> > One or more two things, I havent added the test for the filesystem because i
> havent figured out how to access the chunk in the xml.
>
> There are a number of examples of poking around in the XML in the
> libvirt tests already. There are even checks that verify that the disk
> paths are set correctly based on whether it's a rescue system or not.
> Can you share what you've tried and we can try to work it out together?

I didnt think of looking at the libvirt tests. I believe I have added the tests that you have wanted.

> --
> Soren Hansen        | http://linux2go.dk/
> Ubuntu Developer    | http://www.ubuntu.com/
> OpenStack Developer | http://www.openstack.org/

Revision history for this message
Chuck Short (zulcss) wrote :

> termie mentioned this, but it appears that the following is all due to a bad
> merge:
>
> <interface type='bridge'>
> 255 - <source bridge='${nic.bridge_name}'/>
> 256 - <mac address='${nic.mac_address}'/>
> 257 + <source bridge='${bridge_name}'/>
> 258 + <mac address='${mac_address}'/>
> 259 <!-- <model type='virtio'/> CANT RUN virtio network
> right now -->
> 260 - <filterref filter="nova-instance-${name}-${nic.id}">
> 261 - <parameter name="IP" value="${nic.ip_address}" />
> 262 - <parameter name="DHCPSERVER"
> value="${nic.dhcp_server}" />
> 263 -#if $getVar('nic.extra_params', False)
> 264 - ${nic.extra_params}
> 265 + <filterref filter="nova-instance-${name}">
> 266 + <parameter name="IP" value="${ip_address}" />
> 267 + <parameter name="DHCPSERVER" value="${dhcp_server}"
> />
> 268 +#if $getVar('extra_params', False)
> 269 + ${extra_params}
> 270 #end if
> 271 -#if $getVar('nic.gateway_v6', False)
> 272 - <parameter name="RASERVER" value="${nic.gateway_v6}"
> />
> 273 +#if $getVar('gateway_v6', False)
> 274 + <parameter name="RASERVER" value="${gateway_v6}" />
> 275 #end if

It was a bad merge, I have corrected it.

lp:~zulcss/nova/nova-lxc updated
810. By Chuck Short

Fix up libvirt.xml.template

811. By Chuck Short

Pass along the nbd flags although we dont support it just yet

812. By Chuck Short

Merge trunk

813. By Chuck Short

Catch the error that mount might through a bit better

814. By Chuck Short

Fix pep8 error

815. By Chuck Short

Fix up docstring

816. By Chuck Short

Style fixes

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Authors'
2--- Authors 2011-03-25 13:38:57 +0000
3+++ Authors 2011-03-28 18:50:24 +0000
4@@ -12,6 +12,7 @@
5 Chmouel Boudjnah <chmouel@chmouel.com>
6 Chris Behrens <cbehrens@codestud.com>
7 Christian Berendt <berendt@b1-systems.de>
8+Chuck Short <zulcss@ubuntu.com>
9 Cory Wright <corywright@gmail.com>
10 Dan Prince <dan.prince@rackspace.com>
11 David Pravec <David.Pravec@danix.org>
12
13=== modified file 'nova/tests/test_virt.py'
14--- nova/tests/test_virt.py 2011-03-25 00:16:53 +0000
15+++ nova/tests/test_virt.py 2011-03-28 18:50:24 +0000
16@@ -225,6 +225,49 @@
17 self._check_xml_and_uri(instance_data, expect_kernel=True,
18 expect_ramdisk=True, rescue=True)
19
20+ def test_lxc_container_and_uri(self):
21+ instance_data = dict(self.test_instance)
22+ self._check_xml_and_container(instance_data)
23+
24+ def _check_xml_and_container(self, instance):
25+ user_context = context.RequestContext(project=self.project,
26+ user=self.user)
27+ instance_ref = db.instance_create(user_context, instance)
28+ host = self.network.get_network_host(user_context.elevated())
29+ network_ref = db.project_get_network(context.get_admin_context(),
30+ self.project.id)
31+
32+ fixed_ip = {'address': self.test_ip,
33+ 'network_id': network_ref['id']}
34+
35+ ctxt = context.get_admin_context()
36+ fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip)
37+ db.fixed_ip_update(ctxt, self.test_ip,
38+ {'allocated': True,
39+ 'instance_id': instance_ref['id']})
40+
41+ self.flags(libvirt_type='lxc')
42+ conn = libvirt_conn.LibvirtConnection(True)
43+
44+ uri = conn.get_uri()
45+ self.assertEquals(uri, 'lxc:///')
46+
47+ xml = conn.to_xml(instance_ref)
48+ tree = xml_to_tree(xml)
49+
50+ check = [
51+ (lambda t: t.find('.').get('type'), 'lxc'),
52+ (lambda t: t.find('./os/type').text, 'exe'),
53+ (lambda t: t.find('./devices/filesystem/target').get('dir'), '/')]
54+
55+ for i, (check, expected_result) in enumerate(check):
56+ self.assertEqual(check(tree),
57+ expected_result,
58+ '%s failed common check %d' % (xml, i))
59+
60+ target = tree.find('./devices/filesystem/source').get('dir')
61+ self.assertTrue(len(target) > 0)
62+
63 def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel,
64 rescue=False):
65 user_context = context.RequestContext(project=self.project,
66
67=== modified file 'nova/virt/disk.py'
68--- nova/virt/disk.py 2011-03-24 22:39:39 +0000
69+++ nova/virt/disk.py 2011-03-28 18:50:24 +0000
70@@ -116,6 +116,38 @@
71 _unlink_device(device, nbd)
72
73
74+def setup_container(image, container_dir=None):
75+ """Setup the LXC container
76+
77+ It will mount the loopback image to the container directory in order
78+ to create the root filesystem for the container
79+ """
80+ nbd = "False"
81+ device = _link_device(image, nbd)
82+ out, err = utils.execute('sudo', 'mount', device, container_dir)
83+ if err:
84+ raise exception.Error(_('Failed to mount filesystem: %s')
85+ % err)
86+ _unlink_device(device, nbd)
87+
88+
89+def destroy_container(target, instance):
90+ """Destroy the container once it terminates
91+
92+ It will umount the container that is mounted, try to find the loopback
93+ device associated with the container and delete it.
94+ """
95+ try:
96+ container_dir = '%s/rootfs' % target
97+ utils.execute('sudo', 'umount', container_dir)
98+ finally:
99+ out, err = utils.execute('sudo', 'losetup', '-a')
100+ for loop in out.splitlines():
101+ if instance['name'] in loop:
102+ device = loop.split(loop, ':')
103+ utils.execute('sudo', 'losetup', '--detach', device)
104+
105+
106 def _link_device(image, nbd):
107 """Link image to device using loopback or nbd"""
108 if nbd:
109
110=== modified file 'nova/virt/libvirt.xml.template'
111--- nova/virt/libvirt.xml.template 2011-03-23 18:56:24 +0000
112+++ nova/virt/libvirt.xml.template 2011-03-28 18:50:24 +0000
113@@ -2,41 +2,48 @@
114 <name>${name}</name>
115 <memory>${memory_kb}</memory>
116 <os>
117-#if $type == 'uml'
118- #set $disk_prefix = 'ubd'
119- #set $disk_bus = 'uml'
120- <type>uml</type>
121- <kernel>/usr/bin/linux</kernel>
122- <root>/dev/ubda</root>
123+#if $type == 'lxc'
124+ #set $disk_prefix = ''
125+ #set $disk_bus = ''
126+ <type>exe</type>
127+ <init>/sbin/init</init>
128 #else
129- #if $type == 'xen'
130- #set $disk_prefix = 'sd'
131- #set $disk_bus = 'scsi'
132- <type>linux</type>
133- <root>/dev/xvda</root>
134- #else
135- #set $disk_prefix = 'vd'
136- #set $disk_bus = 'virtio'
137- <type>hvm</type>
138- #end if
139- #if $getVar('rescue', False)
140- <kernel>${basepath}/kernel.rescue</kernel>
141- <initrd>${basepath}/ramdisk.rescue</initrd>
142- #else
143- #if $getVar('kernel', None)
144- <kernel>${kernel}</kernel>
145- #if $type == 'xen'
146- <cmdline>ro</cmdline>
147- #else
148- <cmdline>root=/dev/vda console=ttyS0</cmdline>
149- #end if
150- #if $getVar('ramdisk', None)
151- <initrd>${ramdisk}</initrd>
152- #end if
153- #else
154- <boot dev="hd" />
155- #end if
156- #end if
157+ #if $type == 'uml'
158+ #set $disk_prefix = 'ubd'
159+ #set $disk_bus = 'uml'
160+ <type>uml</type>
161+ <kernel>/usr/bin/linux</kernel>
162+ <root>/dev/ubda</root>
163+ #else
164+ #if $type == 'xen'
165+ #set $disk_prefix = 'sd'
166+ #set $disk_bus = 'scsi'
167+ <type>linux</type>
168+ <root>/dev/xvda</root>
169+ #else
170+ #set $disk_prefix = 'vd'
171+ #set $disk_bus = 'virtio'
172+ <type>hvm</type>
173+ #end if
174+ #if $getVar('rescue', False)
175+ <kernel>${basepath}/kernel.rescue</kernel>
176+ <initrd>${basepath}/ramdisk.rescue</initrd>
177+ #else
178+ #if $getVar('kernel', None)
179+ <kernel>${kernel}</kernel>
180+ #if $type == 'xen'
181+ <cmdline>ro</cmdline>
182+ #else
183+ <cmdline>root=/dev/vda console=ttyS0</cmdline>
184+ #end if
185+ #if $getVar('ramdisk', None)
186+ <initrd>${ramdisk}</initrd>
187+ #end if
188+ #else
189+ <boot dev="hd" />
190+ #end if
191+ #end if
192+ #end if
193 #end if
194 </os>
195 <features>
196@@ -44,30 +51,37 @@
197 </features>
198 <vcpu>${vcpus}</vcpu>
199 <devices>
200-#if $getVar('rescue', False)
201- <disk type='file'>
202- <driver type='${driver_type}'/>
203- <source file='${basepath}/disk.rescue'/>
204- <target dev='${disk_prefix}a' bus='${disk_bus}'/>
205- </disk>
206- <disk type='file'>
207- <driver type='${driver_type}'/>
208- <source file='${basepath}/disk'/>
209- <target dev='${disk_prefix}b' bus='${disk_bus}'/>
210- </disk>
211+#if $type == 'lxc'
212+ <filesystem type='mount'>
213+ <source dir='${basepath}/rootfs'/>
214+ <target dir='/'/>
215+ </filesystem>
216 #else
217- <disk type='file'>
218- <driver type='${driver_type}'/>
219- <source file='${basepath}/disk'/>
220- <target dev='${disk_prefix}a' bus='${disk_bus}'/>
221- </disk>
222- #if $getVar('local', False)
223- <disk type='file'>
224- <driver type='${driver_type}'/>
225- <source file='${basepath}/disk.local'/>
226- <target dev='${disk_prefix}b' bus='${disk_bus}'/>
227- </disk>
228- #end if
229+ #if $getVar('rescue', False)
230+ <disk type='file'>
231+ <driver type='${driver_type}'/>
232+ <source file='${basepath}/disk.rescue'/>
233+ <target dev='${disk_prefix}a' bus='${disk_bus}'/>
234+ </disk>
235+ <disk type='file'>
236+ <driver type='${driver_type}'/>
237+ <source file='${basepath}/disk'/>
238+ <target dev='${disk_prefix}b' bus='${disk_bus}'/>
239+ </disk>
240+ #else
241+ <disk type='file'>
242+ <driver type='${driver_type}'/>
243+ <source file='${basepath}/disk'/>
244+ <target dev='${disk_prefix}a' bus='${disk_bus}'/>
245+ </disk>
246+ #if $getVar('local', False)
247+ <disk type='file'>
248+ <driver type='${driver_type}'/>
249+ <source file='${basepath}/disk.local'/>
250+ <target dev='${disk_prefix}b' bus='${disk_bus}'/>
251+ </disk>
252+ #end if
253+#end if
254 #end if
255
256 #for $nic in $nics
257@@ -77,7 +91,7 @@
258 <!-- <model type='virtio'/> CANT RUN virtio network right now -->
259 <filterref filter="nova-instance-${name}-${nic.id}">
260 <parameter name="IP" value="${nic.ip_address}" />
261- <parameter name="DHCPSERVER" value="${nic.dhcp_server}" />
262+ <parameter name="DHCPSERVER" value="${nic.dhcp_server}" />
263 #if $getVar('nic.extra_params', False)
264 ${nic.extra_params}
265 #end if
266@@ -87,7 +101,6 @@
267 </filterref>
268 </interface>
269 #end for
270-
271 <!-- The order is significant here. File must be defined first -->
272 <serial type="file">
273 <source path='${basepath}/console.log'/>
274
275=== modified file 'nova/virt/libvirt_conn.py'
276--- nova/virt/libvirt_conn.py 2011-03-28 17:47:11 +0000
277+++ nova/virt/libvirt_conn.py 2011-03-28 18:50:24 +0000
278@@ -20,7 +20,7 @@
279 """
280 A connection to a hypervisor through libvirt.
281
282-Supports KVM, QEMU, UML, and XEN.
283+Supports KVM, LXC, QEMU, UML, and XEN.
284
285 **Related Flags**
286
287@@ -86,7 +86,7 @@
288 flags.DEFINE_string('libvirt_type',
289 'kvm',
290 'Libvirt domain type (valid options are: '
291- 'kvm, qemu, uml, xen)')
292+ 'kvm, lxc, qemu, uml, xen)')
293 flags.DEFINE_string('libvirt_uri',
294 '',
295 'Override the default libvirt URI (which is dependent'
296@@ -266,6 +266,8 @@
297 uri = FLAGS.libvirt_uri or 'uml:///system'
298 elif FLAGS.libvirt_type == 'xen':
299 uri = FLAGS.libvirt_uri or 'xen:///'
300+ elif FLAGS.libvirt_type == 'lxc':
301+ uri = FLAGS.libvirt_uri or 'lxc:///'
302 else:
303 uri = FLAGS.libvirt_uri or 'qemu:///system'
304 return uri
305@@ -344,6 +346,8 @@
306 instance_name = instance['name']
307 LOG.info(_('instance %(instance_name)s: deleting instance files'
308 ' %(target)s') % locals())
309+ if FLAGS.libvirt_type == 'lxc':
310+ disk.destroy_container(target, instance)
311 if os.path.exists(target):
312 shutil.rmtree(target)
313
314@@ -625,6 +629,9 @@
315 instance['name'])
316 data = self._flush_xen_console(virsh_output)
317 fpath = self._append_to_file(data, console_log)
318+ elif FLAGS.libvirt_type == 'lxc':
319+ # LXC is also special
320+ LOG.info(_("Unable to read LXC console"))
321 else:
322 fpath = console_log
323
324@@ -738,6 +745,10 @@
325 f.write(libvirt_xml)
326 f.close()
327
328+ if FLAGS.libvirt_type == 'lxc':
329+ container_dir = '%s/rootfs' % basepath(suffix='')
330+ utils.execute('mkdir', '-p', container_dir)
331+
332 # NOTE(vish): No need add the suffix to console.log
333 os.close(os.open(basepath('console.log', ''),
334 os.O_CREAT | os.O_WRONLY, 0660))
335@@ -797,6 +808,9 @@
336 if not inst['kernel_id']:
337 target_partition = "1"
338
339+ if FLAGS.libvirt_type == 'lxc':
340+ target_partition = None
341+
342 key = str(inst['key_data'])
343 net = None
344
345@@ -842,6 +856,10 @@
346 disk.inject_data(basepath('disk'), key, net,
347 partition=target_partition,
348 nbd=FLAGS.use_cow_images)
349+
350+ if FLAGS.libvirt_type == 'lxc':
351+ disk.setup_container(basepath('disk'),
352+ container_dir=container_dir)
353 except Exception as e:
354 # This could be a windows image, or a vmdk format disk
355 LOG.warn(_('instance %(inst_name)s: ignoring error injecting'