Merge "Anaconda deploy handles configdrive correctly" into stable/xena

This commit is contained in:
Zuul 2022-03-20 23:37:16 +00:00 committed by Gerrit Code Review
commit e17d17affe
6 changed files with 42 additions and 10 deletions

View File

@ -55,7 +55,7 @@ def _get_config_drive_dict_from_iso(
iso_path=iso_file_path, outfp=b_buf
)
b_buf.seek(0)
content = b"\n".join(b_buf.readlines()).decode('utf-8')
content = b"".join(b_buf.readlines()).decode('utf-8')
drive_dict[target_file_path] = content
@ -113,8 +113,7 @@ def _fetch_config_drive_from_url(url):
"Can't download the configdrive content from '%(url)s'. "
"Reason: %(reason)s" %
{'url': url, 'reason': e})
config_drive_iso = decode_and_extract_config_drive_iso(config_drive)
return read_iso9600_config_drive(config_drive_iso)
return config_drive
def _write_config_drive_content(content, file_path):
@ -152,10 +151,15 @@ def prepare_config_drive(task,
if not config_drive:
return ks_config_drive
if not isinstance(config_drive, dict) and \
ironic_utils.is_http_url(config_drive):
if ironic_utils.is_http_url(config_drive):
config_drive = _fetch_config_drive_from_url(config_drive)
if not isinstance(config_drive, dict):
# The config drive is in iso6600 format, gzipped and base-64-encoded.
# Convert it to a dict.
config_drive_iso = decode_and_extract_config_drive_iso(config_drive)
config_drive = read_iso9600_config_drive(config_drive_iso)
for key in sorted(config_drive.keys()):
target_path = os.path.join(config_drive_path, key)
ks_config_drive += _write_config_drive_content(

View File

@ -995,6 +995,7 @@ def build_kickstart_config_options(task):
params['liveimg_url'] = node.instance_info['image_url']
params['agent_token'] = node.driver_internal_info['agent_secret_token']
params['heartbeat_url'] = _build_heartbeat_url(node.uuid)
params['config_drive'] = ks_utils.prepare_config_drive(task)
return {'ks_options': params}
@ -1105,6 +1106,7 @@ def validate_kickstart_template(ks_template):
"""
ks_options = {'liveimg_url': 'fake_image_url',
'agent_token': 'fake_token',
'config_drive': '',
'heartbeat_url': 'fake_heartbeat_url'}
params = {'ks_options': ks_options}
try:
@ -1223,9 +1225,6 @@ def prepare_instance_kickstart_config(task, image_info, anaconda_boot=False):
ks_options = build_kickstart_config_options(task)
kickstart_template = image_info['ks_template'][1]
ks_cfg = utils.render_template(kickstart_template, ks_options)
ks_config_drive = ks_utils.prepare_config_drive(task)
if ks_config_drive:
ks_cfg = ks_cfg + ks_config_drive
utils.write_to_file(image_info['ks_cfg'][1], ks_cfg)

View File

@ -27,7 +27,16 @@ liveimg --url {{ ks_options.liveimg_url }}
/usr/bin/curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-OpenStack-Ironic-API-Version: 1.72' -d '{"callback_url": "", "agent_token": "{{ ks_options.agent_token }}", "agent_status": "error", "agent_status_message": "Error: Deploying using anaconda. Check console for more information."}' {{ ks_options.heartbeat_url }}
%end
# Sending callback after the installation is mandatory
# Config-drive information, if any.
{{ ks_options.config_drive }}
# Sending callback after the installation is mandatory.
# This ought to be the last thing done; otherwise the
# ironic-conductor could reboot the node before anaconda
# finishes executing everything in this file.
# The sync makes sure that the data is flushed out to disk,
# before rebooting.
%post
sync
/usr/bin/curl -X POST -H 'Content-Type: application/json' -H 'Accept: application/json' -H 'X-OpenStack-Ironic-API-Version: 1.72' -d '{"callback_url": "", "agent_token": "{{ ks_options.agent_token }}", "agent_status": "end", "agent_status_message": "Deployment completed successfully."}' {{ ks_options.heartbeat_url }}
%end

View File

@ -114,7 +114,7 @@ echo $CONTENT | /usr/bin/base64 --decode > {file_path}\n\
expected = self._get_expected_ks_config_drive(self.config_drive_dict)
with task_manager.acquire(self.context, self.node.uuid) as task:
i_info = task.node.instance_info
i_info['configdrive'] = self.config_drive_dict
i_info['configdrive'] = CONFIG_DRIVE
task.node.instance_info = i_info
task.node.save()
self.assertEqual(expected, ks_utils.prepare_config_drive(task))

View File

@ -1399,6 +1399,7 @@ class PXEBuildKickstartConfigOptionsTestCase(db_base.DbTestCase):
shared=True) as task:
expected = {}
expected['liveimg_url'] = task.node.instance_info['image_url']
expected['config_drive'] = ''
expected['heartbeat_url'] = (
'http://ironic-api/v1/heartbeat/%s' % task.node.uuid
)

View File

@ -0,0 +1,19 @@
---
fixes:
- |
The anaconda deploy interface was treating the config drive
as a dict, whereas it could be a dict or in iso6600 format,
gzipped and base64-encoded. This has been fixed.
- |
The anaconda deploy interface was adding commands that deal with the
config drive, to the end of the kickstart config file. Which means
that they are handled after an ironic API request is sent (to the
conductor) to indicate that the node has been provisioned and is
ready to be rebooted. Which means that there is a possible race condition
wrt these commands being completed before the node is powered off.
A sync is added to ensure that all modifications have been written
to disk, before the API request is sent -- as the last thing.
- |
Extra newlines ('\n') were incorrectly added to the user data content.
This broke the content-type decoding and cloud-init was unable to
proces them. The extra newlines have been removed.