summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChuck Short <zulcss@ubuntu.com>2011-03-29 20:08:11 +0000
committerTarmac <>2011-03-29 20:08:11 +0000
commitcdb006321436de92d790ca97c668c0b725da5d2b (patch)
tree1e0418b910d5948b01ac9b96ba952358be4918e4
parentee212133d32b1ec249c9d47f00581820f0c627c6 (diff)
parent05a654211ab902cbb5b1b345dd3285efb1c6bf71 (diff)
downloadnova-cdb006321436de92d790ca97c668c0b725da5d2b.tar.gz
nova-cdb006321436de92d790ca97c668c0b725da5d2b.tar.xz
nova-cdb006321436de92d790ca97c668c0b725da5d2b.zip
This branch adds support for linux containers (LXC) to nova. It uses the libvirt LXC driver to start and stop the instance.
-rw-r--r--Authors1
-rw-r--r--nova/tests/test_virt.py43
-rw-r--r--nova/virt/disk.py35
-rw-r--r--nova/virt/libvirt.xml.template31
-rw-r--r--nova/virt/libvirt_conn.py23
5 files changed, 121 insertions, 12 deletions
diff --git a/Authors b/Authors
index 09759ddcb..eccf38a43 100644
--- a/Authors
+++ b/Authors
@@ -12,6 +12,7 @@ Chiradeep Vittal <chiradeep@cloud.com>
Chmouel Boudjnah <chmouel@chmouel.com>
Chris Behrens <cbehrens@codestud.com>
Christian Berendt <berendt@b1-systems.de>
+Chuck Short <zulcss@ubuntu.com>
Cory Wright <corywright@gmail.com>
Dan Prince <dan.prince@rackspace.com>
David Pravec <David.Pravec@danix.org>
diff --git a/nova/tests/test_virt.py b/nova/tests/test_virt.py
index 3a03159ff..958c8e3e2 100644
--- a/nova/tests/test_virt.py
+++ b/nova/tests/test_virt.py
@@ -225,6 +225,49 @@ class LibvirtConnTestCase(test.TestCase):
self._check_xml_and_uri(instance_data, expect_kernel=True,
expect_ramdisk=True, rescue=True)
+ def test_lxc_container_and_uri(self):
+ instance_data = dict(self.test_instance)
+ self._check_xml_and_container(instance_data)
+
+ def _check_xml_and_container(self, instance):
+ user_context = context.RequestContext(project=self.project,
+ user=self.user)
+ instance_ref = db.instance_create(user_context, instance)
+ host = self.network.get_network_host(user_context.elevated())
+ network_ref = db.project_get_network(context.get_admin_context(),
+ self.project.id)
+
+ fixed_ip = {'address': self.test_ip,
+ 'network_id': network_ref['id']}
+
+ ctxt = context.get_admin_context()
+ fixed_ip_ref = db.fixed_ip_create(ctxt, fixed_ip)
+ db.fixed_ip_update(ctxt, self.test_ip,
+ {'allocated': True,
+ 'instance_id': instance_ref['id']})
+
+ self.flags(libvirt_type='lxc')
+ conn = libvirt_conn.LibvirtConnection(True)
+
+ uri = conn.get_uri()
+ self.assertEquals(uri, 'lxc:///')
+
+ xml = conn.to_xml(instance_ref)
+ tree = xml_to_tree(xml)
+
+ check = [
+ (lambda t: t.find('.').get('type'), 'lxc'),
+ (lambda t: t.find('./os/type').text, 'exe'),
+ (lambda t: t.find('./devices/filesystem/target').get('dir'), '/')]
+
+ for i, (check, expected_result) in enumerate(check):
+ self.assertEqual(check(tree),
+ expected_result,
+ '%s failed common check %d' % (xml, i))
+
+ target = tree.find('./devices/filesystem/source').get('dir')
+ self.assertTrue(len(target) > 0)
+
def _check_xml_and_uri(self, instance, expect_ramdisk, expect_kernel,
rescue=False):
user_context = context.RequestContext(project=self.project,
diff --git a/nova/virt/disk.py b/nova/virt/disk.py
index 25e4f54a9..ddea1a1f7 100644
--- a/nova/virt/disk.py
+++ b/nova/virt/disk.py
@@ -116,6 +116,41 @@ def inject_data(image, key=None, net=None, partition=None, nbd=False):
_unlink_device(device, nbd)
+def setup_container(image, container_dir=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.
+
+ LXC does not support qcow2 images yet.
+ """
+ try:
+ device = _link_device(image, nbd)
+ utils.execute('sudo', 'mount', device, container_dir)
+ except Exception, exn:
+ LOG.exception(_('Failed to mount filesystem: %s'), exn)
+ _unlink_device(device, nbd)
+
+
+def destroy_container(target, instance, nbd=False):
+ """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.
+
+ LXC does not support qcow2 images yet.
+ """
+ try:
+ container_dir = '%s/rootfs' % target
+ utils.execute('sudo', 'umount', container_dir)
+ finally:
+ out, err = utils.execute('sudo', 'losetup', '-a')
+ for loop in out.splitlines():
+ if instance['name'] in loop:
+ device = loop.split(loop, ':')
+ _unlink_device(device, nbd)
+
+
def _link_device(image, nbd):
"""Link image to device using loopback or nbd"""
if nbd:
diff --git a/nova/virt/libvirt.xml.template b/nova/virt/libvirt.xml.template
index d74a9e85b..36d18ed95 100644
--- a/nova/virt/libvirt.xml.template
+++ b/nova/virt/libvirt.xml.template
@@ -2,7 +2,12 @@
<name>${name}</name>
<memory>${memory_kb}</memory>
<os>
-#if $type == 'uml'
+#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'
<type>uml</type>
@@ -44,7 +49,13 @@
</features>
<vcpu>${vcpus}</vcpu>
<devices>
-#if $getVar('rescue', False)
+#if $type == 'lxc'
+ <filesystem type='mount'>
+ <source dir='${basepath}/rootfs'/>
+ <target dir='/'/>
+ </filesystem>
+#else
+ #if $getVar('rescue', False)
<disk type='file'>
<driver type='${driver_type}'/>
<source file='${basepath}/disk.rescue'/>
@@ -55,18 +66,19 @@
<source file='${basepath}/disk'/>
<target dev='${disk_prefix}b' bus='${disk_bus}'/>
</disk>
-#else
+ #else
<disk type='file'>
<driver type='${driver_type}'/>
<source file='${basepath}/disk'/>
<target dev='${disk_prefix}a' bus='${disk_bus}'/>
</disk>
- #if $getVar('local', False)
- <disk type='file'>
- <driver type='${driver_type}'/>
- <source file='${basepath}/disk.local'/>
- <target dev='${disk_prefix}b' bus='${disk_bus}'/>
- </disk>
+ #if $getVar('local', False)
+ <disk type='file'>
+ <driver type='${driver_type}'/>
+ <source file='${basepath}/disk.local'/>
+ <target dev='${disk_prefix}b' bus='${disk_bus}'/>
+ </disk>
+ #end if
#end if
#end if
@@ -87,7 +99,6 @@
</filterref>
</interface>
#end for
-
<!-- The order is significant here. File must be defined first -->
<serial type="file">
<source path='${basepath}/console.log'/>
diff --git a/nova/virt/libvirt_conn.py b/nova/virt/libvirt_conn.py
index 80eb64f3c..ae3a967a4 100644
--- a/nova/virt/libvirt_conn.py
+++ b/nova/virt/libvirt_conn.py
@@ -20,7 +20,7 @@
"""
A connection to a hypervisor through libvirt.
-Supports KVM, QEMU, UML, and XEN.
+Supports KVM, LXC, QEMU, UML, and XEN.
**Related Flags**
@@ -86,7 +86,7 @@ flags.DEFINE_string('libvirt_xml_template',
flags.DEFINE_string('libvirt_type',
'kvm',
'Libvirt domain type (valid options are: '
- 'kvm, qemu, uml, xen)')
+ 'kvm, lxc, qemu, uml, xen)')
flags.DEFINE_string('libvirt_uri',
'',
'Override the default libvirt URI (which is dependent'
@@ -266,6 +266,8 @@ class LibvirtConnection(driver.ComputeDriver):
uri = FLAGS.libvirt_uri or 'uml:///system'
elif FLAGS.libvirt_type == 'xen':
uri = FLAGS.libvirt_uri or 'xen:///'
+ elif FLAGS.libvirt_type == 'lxc':
+ uri = FLAGS.libvirt_uri or 'lxc:///'
else:
uri = FLAGS.libvirt_uri or 'qemu:///system'
return uri
@@ -344,6 +346,8 @@ class LibvirtConnection(driver.ComputeDriver):
instance_name = instance['name']
LOG.info(_('instance %(instance_name)s: deleting instance files'
' %(target)s') % locals())
+ if FLAGS.libvirt_type == 'lxc':
+ disk.destroy_container(target, instance, nbd=FLAGS.use_cow_images)
if os.path.exists(target):
shutil.rmtree(target)
@@ -625,6 +629,9 @@ class LibvirtConnection(driver.ComputeDriver):
instance['name'])
data = self._flush_xen_console(virsh_output)
fpath = self._append_to_file(data, console_log)
+ elif FLAGS.libvirt_type == 'lxc':
+ # LXC is also special
+ LOG.info(_("Unable to read LXC console"))
else:
fpath = console_log
@@ -738,6 +745,10 @@ class LibvirtConnection(driver.ComputeDriver):
f.write(libvirt_xml)
f.close()
+ if FLAGS.libvirt_type == 'lxc':
+ container_dir = '%s/rootfs' % basepath(suffix='')
+ utils.execute('mkdir', '-p', container_dir)
+
# NOTE(vish): No need add the suffix to console.log
os.close(os.open(basepath('console.log', ''),
os.O_CREAT | os.O_WRONLY, 0660))
@@ -797,6 +808,9 @@ class LibvirtConnection(driver.ComputeDriver):
if not inst['kernel_id']:
target_partition = "1"
+ if FLAGS.libvirt_type == 'lxc':
+ target_partition = None
+
key = str(inst['key_data'])
net = None
@@ -842,6 +856,11 @@ class LibvirtConnection(driver.ComputeDriver):
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,
+ nbd=FLAGS.use_cow_images)
except Exception as e:
# This could be a windows image, or a vmdk format disk
LOG.warn(_('instance %(inst_name)s: ignoring error injecting'