Do not use separate partition for cloudinit configuration

In our usecases the separate partition is not needed. It is enough just
to put cloudinit configuration into the root filesystem.
This also allows to avoid a race condition which sometimes happens: some
process deletes the folder in tmp where the configuration partition is
mounted resulting in cloudinit failure to read its configuration.

Change-Id: Ib3efb4f517a5cf86dbf91ee53ac00108d4624dcd
Closes-Bug: #1652002
This commit is contained in:
Georgy Kibardin 2017-02-20 12:29:05 +03:00
parent 9f40d27bbe
commit b9842ce714
3 changed files with 84 additions and 12 deletions

View File

@ -214,3 +214,4 @@ log_file=/var/log/fuel-agent.log
# Default password for root user
# (string value)
default_root_password=r00tme
use_configdrive=false

View File

@ -131,6 +131,11 @@ opts = [
default=True,
help='Create configdrive file, use pre-builded if set to False'
),
cfg.BoolOpt(
'use_configdrive',
default=True,
help='Use separate partition for cloudinit configuration'
),
cfg.BoolOpt(
'fix_udev_net_rules',
default=True,
@ -363,15 +368,7 @@ class Manager(object):
fu.umount_fs(mount_point)
os.rmdir(mount_point)
def _prepare_configdrive_files(self):
# see data sources part of cloud-init documentation
# for directory structure
cd_root = tempfile.mkdtemp(dir=CONF.tmp_path)
cd_latest = os.path.join(cd_root, 'openstack', 'latest')
md_output_path = os.path.join(cd_latest, 'meta_data.json')
ud_output_path = os.path.join(cd_latest, 'user_data')
os.makedirs(cd_latest)
def _generate_cloudinit_config(self, metadata, userdata):
cc_output_path = os.path.join(CONF.tmp_path, 'cloud_config.txt')
bh_output_path = os.path.join(CONF.tmp_path, 'boothook.txt')
@ -392,13 +389,23 @@ class Manager(object):
tmpl_dir,
self.driver.configdrive_scheme.template_names('meta_data_json'),
self.driver.configdrive_scheme.template_data(),
md_output_path
metadata
)
utils.execute(
'write-mime-multipart', '--output=%s' % ud_output_path,
'write-mime-multipart', '--output=%s' % userdata,
'%s:text/cloud-boothook' % bh_output_path,
'%s:text/cloud-config' % cc_output_path)
def _prepare_configdrive_files(self):
# see data sources part of cloud-init documentation
# for directory structure
cd_root = tempfile.mkdtemp(dir=CONF.tmp_path)
cd_latest = os.path.join(cd_root, 'openstack', 'latest')
md_output_path = os.path.join(cd_latest, 'meta_data.json')
ud_output_path = os.path.join(cd_latest, 'user_data')
os.makedirs(cd_latest)
self._generate_cloudinit_config(md_output_path, ud_output_path)
return [os.path.join(cd_root, 'openstack')]
def do_configdrive(self):
@ -430,6 +437,23 @@ class Manager(object):
md5=md5,
)
def _prepare_cloudinit_config_files(self, target_dir):
# see data sources part of cloud-init documentation
# for directory structure
md_output_path = os.path.join(target_dir, 'meta-data')
ud_output_path = os.path.join(target_dir, 'user-data')
os.makedirs(target_dir)
self._generate_cloudinit_config(md_output_path, ud_output_path)
def inject_cloudinit_config(self):
root_fs = self.driver.partition_scheme.fs_by_mount('/')
root = fu.mount_fs_temp(root_fs.type, str(root_fs.device))
try:
self._prepare_cloudinit_config_files(
os.path.join(root, 'var/lib/cloud/seed/nocloud'))
finally:
fu.umount_fs(root)
def do_copyimage(self):
LOG.debug('--- Copying images (do_copyimage) ---')
for image in self.driver.image_scheme.images:
@ -488,6 +512,8 @@ class Manager(object):
LOG.debug('Extending %s %s' %
(image.format, image.target_device))
fu.extend_fs(image.format, image.target_device)
if not CONF.use_configdrive:
self.inject_cloudinit_config()
self.move_files_to_their_places()
def move_files_to_their_places(self, remove_src=True):
@ -968,7 +994,8 @@ class Manager(object):
def do_provisioning(self):
LOG.debug('--- Provisioning (do_provisioning) ---')
self.do_partitioning()
self.do_configdrive()
if CONF.use_configdrive:
self.do_configdrive()
self.do_copyimage()
self.do_bootloader()
LOG.debug('--- Provisioning END (do_provisioning) ---')

View File

@ -538,6 +538,42 @@ class TestManager(unittest2.TestCase):
'%s/%s:text/cloud-boothook' % (CONF.tmp_path, 'boothook.txt'),
'%s/%s:text/cloud-config' % (CONF.tmp_path, 'cloud_config.txt'))
@mock.patch('os.makedirs')
@mock.patch.object(utils, 'execute')
@mock.patch.object(utils, 'render_and_save')
def test_prepare_cloudinit_config(self, mock_u_ras, mock_u_e,
mock_makedirs):
self.mgr._prepare_cloudinit_config_files(
'/var/lib/cloud/seed/nocloud')
mock_makedirs.assert_called_once_with('/var/lib/cloud/seed/nocloud')
mock_u_ras_expected_calls = [
mock.call(CONF.nc_template_path,
['cloud_config_pro_fi-le.jinja2',
'cloud_config_pro.jinja2',
'cloud_config_pro_fi.jinja2',
'cloud_config.jinja2'],
mock.ANY, '%s/%s' % (CONF.tmp_path, 'cloud_config.txt')),
mock.call(CONF.nc_template_path,
['boothook_pro_fi-le.jinja2',
'boothook_pro.jinja2',
'boothook_pro_fi.jinja2',
'boothook.jinja2'],
mock.ANY, '%s/%s' % (CONF.tmp_path, 'boothook.txt')),
mock.call(CONF.nc_template_path,
['meta_data_json_pro_fi-le.jinja2',
'meta_data_json_pro.jinja2',
'meta_data_json_pro_fi.jinja2',
'meta_data_json.jinja2'],
mock.ANY, '/var/lib/cloud/seed/nocloud/meta-data')]
self.assertEqual(mock_u_ras_expected_calls, mock_u_ras.call_args_list)
mock_u_e.assert_called_once_with(
'write-mime-multipart',
'--output=/var/lib/cloud/seed/nocloud/user-data',
'%s/%s:text/cloud-boothook' % (CONF.tmp_path, 'boothook.txt'),
'%s/%s:text/cloud-config' % (CONF.tmp_path, 'cloud_config.txt'))
@mock.patch('fuel_agent.manager.fu', create=True)
@mock.patch('os.path.isdir')
@mock.patch('os.rmdir')
@ -670,6 +706,14 @@ class TestManager(unittest2.TestCase):
self.assertEqual(mock_fu_ef_expected_calls, mock_fu_ef.call_args_list)
self.assertTrue(mock_mfttp.called)
@mock.patch('fuel_agent.manager.Manager.inject_cloudinit_config')
@mock.patch('fuel_agent.manager.Manager.move_files_to_their_places')
@mock.patch('fuel_agent.manager.CONF.use_configdrive', False)
def test_cloudconfig_iinjection(self, mock_mfttp, mock_icc):
with mock.patch.object(self.mgr.driver.image_scheme, 'images', []):
self.mgr.do_copyimage()
mock_icc.assert_called_once_with()
@mock.patch.object(fu, 'get_fs_type')
@mock.patch.object(manager.os.path, 'exists')
@mock.patch.object(hu, 'is_block_device')