diff --git a/etc/nova/rootwrap.d/lxd.filters b/etc/nova/rootwrap.d/lxd.filters index ff47c318..d16d35b3 100644 --- a/etc/nova/rootwrap.d/lxd.filters +++ b/etc/nova/rootwrap.d/lxd.filters @@ -2,4 +2,4 @@ # This file should be owned by (and only-writable by) the root user [Filters] -tar: CommandFilter, tar, root +zfs: CommandFilter, zfs, root diff --git a/nova/tests/unit/virt/lxd/test_driver.py b/nova/tests/unit/virt/lxd/test_driver.py index 2dc6777b..e7cae9d8 100644 --- a/nova/tests/unit/virt/lxd/test_driver.py +++ b/nova/tests/unit/virt/lxd/test_driver.py @@ -126,6 +126,7 @@ class LXDDriverTest(test.NoDBTestCase): lxd_driver.setup_image = mock.Mock() lxd_driver.vif_driver = mock.Mock() lxd_driver.firewall_driver = mock.Mock() + lxd_driver._add_ephemeral = mock.Mock() lxd_driver.create_profile = mock.Mock(return_value={ 'name': instance.name, 'config': {}, 'devices': {}}) @@ -146,6 +147,8 @@ class LXDDriverTest(test.NoDBTestCase): instance, network_info) fd.apply_instance_filter.assert_called_once_with( instance, network_info) + lxd_driver._add_ephemeral.assert_called_once_with( + block_device_info, lxd_driver.client.host_info, instance) def test_spawn_already_exists(self): """InstanceExists is raised if the container already exists.""" @@ -165,6 +168,39 @@ class LXDDriverTest(test.NoDBTestCase): ctx, instance, image_meta, injected_files, admin_password, None, None) + @mock.patch('os.stat') + @mock.patch('nova.virt.lxd.driver.container_utils.get_container_storage') + @mock.patch.object(driver.utils, 'execute') + @mock.patch('nova.virt.driver.block_device_info_get_ephemerals') + def test_add_ephemerals_with_zfs( + self, block_device_info_get_ephemerals, execute, + get_container_storage, stat): + ctx = context.get_admin_context() + block_device_info_get_ephemerals.return_value = [ + {'virtual_name': 'ephemerals0'}] + instance = fake_instance.fake_instance_obj(ctx, name='test') + block_device_info = mock.Mock() + lxd_config = {'environment': {'storage': 'zfs'}, + 'config': {'storage.zfs_pool_name': 'zfs'}} + get_container_storage.return_value = '/path' + stat.return_value.st_uid = 1234 + + lxd_driver = driver.LXDDriver(None) + lxd_driver.init_host(None) + lxd_driver._add_ephemeral(block_device_info, lxd_config, instance) + + block_device_info_get_ephemerals.assert_called_once_with( + block_device_info) + + expected_calls = [ + mock.call( + 'zfs', 'create', '-o', 'mountpoint=/path', '-o', 'quota=0G', + 'zfs/instance-00000001-ephemeral', run_as_root=True), + mock.call('chown', 1234, '/path', run_as_root=True) + ] + + self.assertEqual(expected_calls, execute.call_args_list) + def test_destroy(self): mock_profile = mock.Mock() mock_container = mock.Mock() @@ -202,22 +238,52 @@ class LXDDriverTest(test.NoDBTestCase): instance = fake_instance.fake_instance_obj(ctx, name='test') network_info = [mock.Mock()] instance_dir = utils.get_instance_dir(instance.name) + block_device_info = mock.Mock() lxd_driver = driver.LXDDriver(None) lxd_driver.init_host(None) lxd_driver.vif_driver = mock.Mock() lxd_driver.firewall_driver = mock.Mock() + lxd_driver._remove_ephemeral = mock.Mock() - lxd_driver.cleanup(ctx, instance, network_info) + lxd_driver.cleanup(ctx, instance, network_info, block_device_info) lxd_driver.vif_driver.unplug.assert_called_once_with( instance, network_info[0]) lxd_driver.firewall_driver.unfilter_instance.assert_called_once_with( instance, network_info) + lxd_driver._remove_ephemeral.assert_called_once_with( + block_device_info, lxd_driver.client.host_info, instance) execute.assert_called_once_with( 'chown', '-R', 'user:user', instance_dir, run_as_root=True) rmtree.assert_called_once_with(instance_dir) + @mock.patch.object(driver.utils, 'execute') + @mock.patch('nova.virt.driver.block_device_info_get_ephemerals') + def test_remove_emepheral_with_zfs( + self, block_device_info_get_ephemerals, execute): + block_device_info_get_ephemerals.return_value = [ + {'virtual_name': 'ephemerals0'}] + + ctx = context.get_admin_context() + instance = fake_instance.fake_instance_obj(ctx, name='test') + block_device_info = mock.Mock() + lxd_config = {'environment': {'storage': 'zfs'}, + 'config': {'storage.zfs_pool_name': 'zfs'}} + + lxd_driver = driver.LXDDriver(None) + lxd_driver.init_host(None) + lxd_driver._remove_ephemeral(block_device_info, lxd_config, instance) + + block_device_info_get_ephemerals.assert_called_once_with( + block_device_info) + + expected_calls = [ + mock.call('zfs', 'destroy', 'zfs/instance-00000001-ephemeral', + run_as_root=True) + ] + self.assertEqual(expected_calls, execute.call_args_list) + def test_reboot(self): ctx = context.get_admin_context() instance = fake_instance.fake_instance_obj(ctx, name='test') diff --git a/nova/virt/lxd/driver.py b/nova/virt/lxd/driver.py index c7de3136..e0223c29 100644 --- a/nova/virt/lxd/driver.py +++ b/nova/virt/lxd/driver.py @@ -218,11 +218,13 @@ class LXDDriver(driver.ComputeDriver): } LOG.debug(container_config) container = self.client.containers.create(container_config, wait=True) - container.start() # XXX: rockstar (6 Jul 2016) - _add_ephemeral is only used here, - # and hasn't really been audited. It may need a cleanup. - self._add_ephemeral(block_device_info, instance) + # and hasn't really been audited. It may need a cleanup + lxd_config = self.client.host_info + self._add_ephemeral(block_device_info, lxd_config, instance) + + container.start() def destroy(self, context, instance, network_info, block_device_info=None, destroy_disks=True, migrate_data=None): @@ -257,6 +259,11 @@ class LXDDriver(driver.ComputeDriver): pass self.firewall_driver.unfilter_instance(instance, network_info) + # XXX: zulcss (14 Jul 2016) - _remove_ephemeral is only used here, + # and hasn't really been audited. It may need a cleanup + lxd_config = self.client.host_info + self._remove_ephemeral(block_device_info, lxd_config, instance) + name = pwd.getpwuid(os.getuid()).pw_name container_dir = container_utils.get_instance_dir(instance.name) @@ -384,26 +391,51 @@ class LXDDriver(driver.ComputeDriver): # have not been through the cleanup process. We know the cleanup process # is complete when there is no more code below this comment, and the # comment can be removed. - def _add_ephemeral(self, block_device_info, instance): - if instance.get('ephemeral_gb', 0) != 0: - ephemerals = block_device_info.get('ephemerals', []) + def _add_ephemeral(self, block_device_info, lxd_config, instance): + ephemeral_storage = driver.block_device_info_get_ephemerals( + block_device_info) + if ephemeral_storage: + storage_driver = lxd_config['environment']['storage'] + container_root = container_utils.get_container_rootfs( + instance.name) - root_dir = container_utils.get_container_rootfs(instance.name) - if ephemerals == []: - ephemeral_src = container_utils.get_container_storage( - ephemerals['virtual_name'], instance.name) - fileutils.ensure_tree(ephemeral_src) - utils.execute('chown', - os.stat(root_dir).st_uid, - ephemeral_src, run_as_root=True) - else: - for id, ephx in enumerate(ephemerals): - ephemeral_src = container_utils.get_container_storage( - ephx['virtual_name'], instance.name) - fileutils.ensure_tree(ephemeral_src) - utils.execute('chown', - os.stat(root_dir).st_uid, - ephemeral_src, run_as_root=True) + for ephemeral in ephemeral_storage: + storage_dir = container_utils.get_container_storage( + ephemeral['virtual_name'], instance.name) + if storage_driver == 'zfs': + zfs_pool = lxd_config['config']['storage.zfs_pool_name'] + + utils.execute( + 'zfs', 'create', + '-o', 'mountpoint=%s' % storage_dir, + '-o', 'quota=%sG' % instance.ephemeral_gb, + '%s/%s-ephemeral' % (zfs_pool, instance.name), + run_as_root=True) + else: + reason = _('Unsupport LXD storage detected. Supported' + ' storage drivers is zfs.') + raise exception.NovaException(reason) + + utils.execute( + 'chown', os.stat(container_root).st_uid, + storage_dir, run_as_root=True) + + def _remove_ephemeral(self, block_device_info, lxd_config, instance): + """Remove empeheral device from the instance.""" + ephemeral_storage = driver.block_device_info_get_ephemerals( + block_device_info) + if ephemeral_storage: + storage_driver = lxd_config['environment']['storage'] + + for ephemeral in ephemeral_storage: + if storage_driver == 'zfs': + zfs_pool = \ + lxd_config['config']['storage.zfs_pool_name'] + + utils.execute( + 'zfs', 'destroy', + '%s/%s-ephemeral' % (zfs_pool, instance.name), + run_as_root=True) def snapshot(self, context, instance, image_id, update_task_state): lock_path = str(os.path.join(CONF.instances_path, 'locks'))