Merge "Anaconda deploy handles configdrive correctly"
This commit is contained in:
commit
910448b8b3
|
@ -55,7 +55,7 @@ def _get_config_drive_dict_from_iso(
|
||||||
iso_path=iso_file_path, outfp=b_buf
|
iso_path=iso_file_path, outfp=b_buf
|
||||||
)
|
)
|
||||||
b_buf.seek(0)
|
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
|
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'. "
|
"Can't download the configdrive content from '%(url)s'. "
|
||||||
"Reason: %(reason)s" %
|
"Reason: %(reason)s" %
|
||||||
{'url': url, 'reason': e})
|
{'url': url, 'reason': e})
|
||||||
config_drive_iso = decode_and_extract_config_drive_iso(config_drive)
|
return config_drive
|
||||||
return read_iso9600_config_drive(config_drive_iso)
|
|
||||||
|
|
||||||
|
|
||||||
def _write_config_drive_content(content, file_path):
|
def _write_config_drive_content(content, file_path):
|
||||||
|
@ -152,10 +151,15 @@ def prepare_config_drive(task,
|
||||||
if not config_drive:
|
if not config_drive:
|
||||||
return ks_config_drive
|
return ks_config_drive
|
||||||
|
|
||||||
if not isinstance(config_drive, dict) and \
|
if ironic_utils.is_http_url(config_drive):
|
||||||
ironic_utils.is_http_url(config_drive):
|
|
||||||
config_drive = _fetch_config_drive_from_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()):
|
for key in sorted(config_drive.keys()):
|
||||||
target_path = os.path.join(config_drive_path, key)
|
target_path = os.path.join(config_drive_path, key)
|
||||||
ks_config_drive += _write_config_drive_content(
|
ks_config_drive += _write_config_drive_content(
|
||||||
|
|
|
@ -957,6 +957,7 @@ def build_kickstart_config_options(task):
|
||||||
node.uuid
|
node.uuid
|
||||||
)
|
)
|
||||||
params['heartbeat_url'] = heartbeat_url
|
params['heartbeat_url'] = heartbeat_url
|
||||||
|
params['config_drive'] = ks_utils.prepare_config_drive(task)
|
||||||
return {'ks_options': params}
|
return {'ks_options': params}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1067,6 +1068,7 @@ def validate_kickstart_template(ks_template):
|
||||||
"""
|
"""
|
||||||
ks_options = {'liveimg_url': 'fake_image_url',
|
ks_options = {'liveimg_url': 'fake_image_url',
|
||||||
'agent_token': 'fake_token',
|
'agent_token': 'fake_token',
|
||||||
|
'config_drive': '',
|
||||||
'heartbeat_url': 'fake_heartbeat_url'}
|
'heartbeat_url': 'fake_heartbeat_url'}
|
||||||
params = {'ks_options': ks_options}
|
params = {'ks_options': ks_options}
|
||||||
try:
|
try:
|
||||||
|
@ -1185,9 +1187,6 @@ def prepare_instance_kickstart_config(task, image_info, anaconda_boot=False):
|
||||||
ks_options = build_kickstart_config_options(task)
|
ks_options = build_kickstart_config_options(task)
|
||||||
kickstart_template = image_info['ks_template'][1]
|
kickstart_template = image_info['ks_template'][1]
|
||||||
ks_cfg = utils.render_template(kickstart_template, ks_options)
|
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,
|
utils.write_to_file(image_info['ks_cfg'][1], ks_cfg,
|
||||||
CONF.pxe.file_permission)
|
CONF.pxe.file_permission)
|
||||||
|
|
||||||
|
|
|
@ -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 }}
|
/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
|
%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
|
%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 }}
|
/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
|
%end
|
||||||
|
|
|
@ -114,7 +114,7 @@ echo $CONTENT | /usr/bin/base64 --decode > {file_path}\n\
|
||||||
expected = self._get_expected_ks_config_drive(self.config_drive_dict)
|
expected = self._get_expected_ks_config_drive(self.config_drive_dict)
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
i_info = task.node.instance_info
|
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.instance_info = i_info
|
||||||
task.node.save()
|
task.node.save()
|
||||||
self.assertEqual(expected, ks_utils.prepare_config_drive(task))
|
self.assertEqual(expected, ks_utils.prepare_config_drive(task))
|
||||||
|
|
|
@ -1514,6 +1514,7 @@ class PXEBuildKickstartConfigOptionsTestCase(db_base.DbTestCase):
|
||||||
shared=True) as task:
|
shared=True) as task:
|
||||||
expected = {}
|
expected = {}
|
||||||
expected['liveimg_url'] = task.node.instance_info['image_url']
|
expected['liveimg_url'] = task.node.instance_info['image_url']
|
||||||
|
expected['config_drive'] = ''
|
||||||
expected['heartbeat_url'] = (
|
expected['heartbeat_url'] = (
|
||||||
'http://ironic-api/v1/heartbeat/%s' % task.node.uuid
|
'http://ironic-api/v1/heartbeat/%s' % task.node.uuid
|
||||||
)
|
)
|
||||||
|
|
|
@ -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.
|
Loading…
Reference in New Issue