summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChuck Short <chuck.short@canonical.com>2012-01-31 20:53:24 -0500
committerChuck Short <chuck.short@canonical.com>2012-02-08 16:10:59 -0500
commite40b659d320b3c6894862b87adf1011e31cbf8fc (patch)
treef337193476a7979123ef68bf8bf87a996c68d5ec
parent799713e4db7f55ccc6acaa033fa082075e28a6a9 (diff)
Add support for LXC volumes.
This introduces volume support for LXC containers in Nova. The way that this works is that when a device is attached to an LXC container is that, the xml is parsed to find out which device to connect to the LXC container, binds the device to the LXC container, and allow the device through cgroups. This bug fixes LP: #924601. Change-Id: I00b41426ae8354b3cd4212655ecb48319a63aa9b Signed-off-by: Chuck Short <chuck.short@canonical.com>
-rw-r--r--nova/tests/test_libvirt.py21
-rw-r--r--nova/virt/disk/api.py20
-rw-r--r--nova/virt/libvirt/connection.py60
3 files changed, 99 insertions, 2 deletions
diff --git a/nova/tests/test_libvirt.py b/nova/tests/test_libvirt.py
index 4ce882992..c83e5f8eb 100644
--- a/nova/tests/test_libvirt.py
+++ b/nova/tests/test_libvirt.py
@@ -195,6 +195,27 @@ class LibvirtVolumeTestCase(test.TestCase):
libvirt_driver.disconnect_volume(connection_info, mount_device)
connection_info = vol_driver.terminate_connection(vol, self.connr)
+ def test_libvirt_lxc_volume(self):
+ self.stubs.Set(os.path, 'exists', lambda x: True)
+ vol_driver = volume_driver.ISCSIDriver()
+ libvirt_driver = volume.LibvirtISCSIVolumeDriver(self.fake_conn)
+ location = '10.0.2.15:3260'
+ name = 'volume-00000001'
+ iqn = 'iqn.2010-10.org.openstack:%s' % name
+ vol = {'id': 1,
+ 'name': name,
+ 'provider_auth': None,
+ 'provider_location': '%s,fake %s' % (location, iqn)}
+ connection_info = vol_driver.initialize_connection(vol, self.connr)
+ mount_device = "vde"
+ xml = libvirt_driver.connect_volume(connection_info, mount_device)
+ tree = xml_to_tree(xml)
+ dev_str = '/dev/disk/by-path/ip-%s-iscsi-%s-lun-0' % (location, iqn)
+ self.assertEqual(tree.get('type'), 'block')
+ self.assertEqual(tree.find('./source').get('dev'), dev_str)
+ libvirt_driver.disconnect_volume(connection_info, mount_device)
+ connection_info = vol_driver.terminate_connection(vol, self.connr)
+
class CacheConcurrencyTestCase(test.TestCase):
def setUp(self):
diff --git a/nova/virt/disk/api.py b/nova/virt/disk/api.py
index 929e17a55..91f82b470 100644
--- a/nova/virt/disk/api.py
+++ b/nova/virt/disk/api.py
@@ -106,6 +106,26 @@ def extend(image, size):
utils.execute('resize2fs', image, check_exit_code=False)
+def bind(src, target, instance_name):
+ """Bind device to a filesytem"""
+ if src:
+ utils.execute('touch', target, run_as_root=True)
+ utils.execute('mount', '-o', 'bind', src, target,
+ run_as_root=True)
+ s = os.stat(src)
+ cgroup_info = "c %s:%s rwm" % (os.major(s.st_rdev),
+ os.minor(s.st_rdev))
+ cgroups_path = \
+ "/sys/fs/cgroup/devices/sysdefault/libvirt/lxc/%s/devices.allow" \
+ % instance_name
+ utils.execute('echo', '>', cgroup_info, cgroups_path, run_as_root=True)
+
+
+def unbind(target):
+ if target:
+ utils.execute('umount', target, run_as_root=True)
+
+
class _DiskImage(object):
"""Provide operations on a disk image file."""
diff --git a/nova/virt/libvirt/connection.py b/nova/virt/libvirt/connection.py
index 2bd9ac329..4f253b692 100644
--- a/nova/virt/libvirt/connection.py
+++ b/nova/virt/libvirt/connection.py
@@ -449,7 +449,11 @@ class LibvirtConnection(driver.ComputeDriver):
xml = self.volume_driver_method('connect_volume',
connection_info,
mount_device)
- virt_dom.attachDevice(xml)
+
+ if FLAGS.libvirt_type == 'lxc':
+ self._attach_lxc_volume(xml, virt_dom, instance_name)
+ else:
+ virt_dom.attachDevice(xml)
@staticmethod
def _get_disk_xml(xml, device):
@@ -476,13 +480,65 @@ class LibvirtConnection(driver.ComputeDriver):
xml = self._get_disk_xml(virt_dom.XMLDesc(0), mount_device)
if not xml:
raise exception.DiskNotFound(location=mount_device)
- virt_dom.detachDevice(xml)
+ if FLAGS.libvirt_type == 'lxc':
+ self._detach_lxc_volume(xml, vort_dom, instance_name)
+ else:
+ virt_dom.detachDevice(xml)
finally:
self.volume_driver_method('disconnect_volume',
connection_info,
mount_device)
@exception.wrap_exception()
+ def _attach_lxc_volume(self, xml, virt_dom, instance_name):
+ LOG.info(_('attaching LXC block device'))
+
+ lxc_container_root = self.get_lxc_container_root(virt_dom)
+ lxc_host_volume = self.get_lxc_host_device(xml)
+ lxc_container_device = self.get_lxc_container_target(xml)
+ lxc_container_target = "%s/%s" % (lxc_container_root,
+ lxc_container_device)
+
+ if lxc_container_target:
+ disk.bind(lxc_host_volume, lxc_container_target, instance_name)
+
+ @exception.wrap_exception()
+ def _detach_lxc_volume(self, xml, virt_dom, instance_name):
+ LOG.info(_('detaching LXC block device'))
+
+ lxc_container_root = self.get_lxc_container_root(virt_dom)
+ lxc_host_volume = self.get_lxc_host_device(xml)
+ lxc_container_device = self.get_lxc_container_target(xml)
+ lxc_container_target = "%s/%s" % (lxc_container_root,
+ lxc_container_device)
+
+ if lxc_container_target:
+ disk.unbind(lxc_container_target)
+
+ @staticmethod
+ def get_lxc_container_root(virt_dom):
+ xml = virt_dom.XMLDesc(0)
+ doc = ElementTree.fromstring(xml)
+ filesystem_block = doc.findall('./devices/filesystem')
+ for cnt, filesystem_nodes in enumerate(filesystem_block):
+ return filesystem_nodes[cnt].get('dir')
+
+ @staticmethod
+ def get_lxc_host_device(xml):
+ dom = minidom.parseString(xml)
+
+ for device in dom.getElementsByTagName('source'):
+ return device.getAttribute('dev')
+
+ @staticmethod
+ def get_lxc_container_target(xml):
+ dom = minidom.parseString(xml)
+
+ for device in dom.getElementsByTagName('target'):
+ filesystem = device.getAttribute('dev')
+ return 'dev/%s' % filesystem
+
+ @exception.wrap_exception()
def snapshot(self, context, instance, image_href):
"""Create snapshot from a running VM instance.