Merge "Add anaconda support in the pxe boot driver"
This commit is contained in:
commit
ea6dff7d31
@ -14,6 +14,7 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
import copy
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ironic_lib import utils as ironic_utils
|
from ironic_lib import utils as ironic_utils
|
||||||
@ -29,6 +30,7 @@ from ironic.common import image_service as service
|
|||||||
from ironic.common import images
|
from ironic.common import images
|
||||||
from ironic.common import states
|
from ironic.common import states
|
||||||
from ironic.common import utils
|
from ironic.common import utils
|
||||||
|
from ironic.conductor import utils as manager_utils
|
||||||
from ironic.conf import CONF
|
from ironic.conf import CONF
|
||||||
from ironic.drivers.modules import boot_mode_utils
|
from ironic.drivers.modules import boot_mode_utils
|
||||||
from ironic.drivers.modules import deploy_utils
|
from ironic.drivers.modules import deploy_utils
|
||||||
@ -244,6 +246,54 @@ def get_pxe_config_file_path(node_uuid, ipxe_enabled=False):
|
|||||||
return os.path.join(get_root_dir(), node_uuid, 'config')
|
return os.path.join(get_root_dir(), node_uuid, 'config')
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_path_from_label(node_uuid, root_dir, label):
|
||||||
|
"""Generate absolute paths to various images from their name(label)
|
||||||
|
|
||||||
|
This method generates absolute file system path on the conductor where
|
||||||
|
various images need to be placed. For example the kickstart template, file
|
||||||
|
and stage2 squashfs.img needs to be placed in the ipxe_root_dir since they
|
||||||
|
will be transferred by anaconda ramdisk over http(s). The generated paths
|
||||||
|
will be added to the image_info dictionary as values.
|
||||||
|
|
||||||
|
:param node_uuid: the UUID of the node
|
||||||
|
:param root_dir: Directory in which the image must be placed
|
||||||
|
:param label: Name of the image
|
||||||
|
"""
|
||||||
|
if label == 'ks_template':
|
||||||
|
return os.path.join(get_ipxe_root_dir(), node_uuid, 'ks.cfg.template')
|
||||||
|
elif label == 'ks_cfg':
|
||||||
|
return os.path.join(get_ipxe_root_dir(), node_uuid, 'ks.cfg')
|
||||||
|
elif label == 'stage2':
|
||||||
|
return os.path.join(get_ipxe_root_dir(), node_uuid, 'LiveOS',
|
||||||
|
'squashfs.img')
|
||||||
|
else:
|
||||||
|
return os.path.join(root_dir, node_uuid, label)
|
||||||
|
|
||||||
|
|
||||||
|
def get_http_url_path_from_label(http_url, node_uuid, label):
|
||||||
|
"""Generate http url path to various image artifacts
|
||||||
|
|
||||||
|
This method generates http(s) urls for various image artifacts int the
|
||||||
|
webserver root. The generated urls will be added to the pxe_options dict
|
||||||
|
and used to render pxe/ipxe configuration templates.
|
||||||
|
|
||||||
|
:param http_url: URL to access the root of the webserver
|
||||||
|
:param node_uuid: the UUID of the node
|
||||||
|
:param label: Name of the image
|
||||||
|
"""
|
||||||
|
if label == 'ks_template':
|
||||||
|
return '/'.join([http_url, node_uuid, 'ks.cfg.template'])
|
||||||
|
elif label == 'ks_cfg':
|
||||||
|
return '/'.join([http_url, node_uuid, 'ks.cfg'])
|
||||||
|
elif label == 'stage2':
|
||||||
|
# we store stage2 in http_root/node_uuid/LiveOS/squashfs.img
|
||||||
|
# Specifying http://host/node_uuid as stage2 url will make anaconda
|
||||||
|
# automatically load the squashfs.img from LiveOS directory.
|
||||||
|
return '/'.join([http_url, node_uuid])
|
||||||
|
else:
|
||||||
|
return '/'.join([http_url, node_uuid, label])
|
||||||
|
|
||||||
|
|
||||||
def create_pxe_config(task, pxe_options, template=None, ipxe_enabled=False):
|
def create_pxe_config(task, pxe_options, template=None, ipxe_enabled=False):
|
||||||
"""Generate PXE configuration file and MAC address links for it.
|
"""Generate PXE configuration file and MAC address links for it.
|
||||||
|
|
||||||
@ -642,10 +692,39 @@ def get_instance_image_info(task, ipxe_enabled=False):
|
|||||||
i_info[label] = str(iproperties[label + '_id'])
|
i_info[label] = str(iproperties[label + '_id'])
|
||||||
node.instance_info = i_info
|
node.instance_info = i_info
|
||||||
node.save()
|
node.save()
|
||||||
for label in labels:
|
|
||||||
|
anaconda_labels = ()
|
||||||
|
if deploy_utils.get_boot_option(node) == 'kickstart':
|
||||||
|
# stage2 - Installer stage2 squashfs image
|
||||||
|
# ks_template - Anaconda kickstart template
|
||||||
|
# ks_cfg - rendered ks_template
|
||||||
|
anaconda_labels = ('stage2', 'ks_template', 'ks_cfg')
|
||||||
|
if not (i_info.get('stage2') and i_info.get('ks_template')):
|
||||||
|
iproperties = glance_service.show(
|
||||||
|
d_info['image_source']
|
||||||
|
)['properties']
|
||||||
|
for label in anaconda_labels:
|
||||||
|
# ks_template is an optional property on the image
|
||||||
|
if (label == 'ks_template'
|
||||||
|
and not iproperties.get('ks_template')):
|
||||||
|
i_info[label] = CONF.anaconda.default_ks_template
|
||||||
|
elif label == 'ks_cfg':
|
||||||
|
i_info[label] = ''
|
||||||
|
elif label == 'stage2' and 'stage2_id' not in iproperties:
|
||||||
|
msg = ("stage2_id property missing on the image. "
|
||||||
|
"The anaconda deploy interface requires stage2_id "
|
||||||
|
"property to be associated with the os image. ")
|
||||||
|
raise exception.ImageUnacceptable(msg)
|
||||||
|
else:
|
||||||
|
i_info[label] = str(iproperties['stage2_id'])
|
||||||
|
|
||||||
|
node.instance_info = i_info
|
||||||
|
node.save()
|
||||||
|
|
||||||
|
for label in labels + anaconda_labels:
|
||||||
image_info[label] = (
|
image_info[label] = (
|
||||||
i_info[label],
|
i_info[label],
|
||||||
os.path.join(root_dir, node.uuid, label)
|
get_file_path_from_label(node.uuid, root_dir, label)
|
||||||
)
|
)
|
||||||
|
|
||||||
return image_info
|
return image_info
|
||||||
@ -705,15 +784,18 @@ def build_instance_pxe_options(task, pxe_info, ipxe_enabled=False):
|
|||||||
node = task.node
|
node = task.node
|
||||||
|
|
||||||
for label, option in (('kernel', 'aki_path'),
|
for label, option in (('kernel', 'aki_path'),
|
||||||
('ramdisk', 'ari_path')):
|
('ramdisk', 'ari_path'),
|
||||||
|
('stage2', 'stage2_url'),
|
||||||
|
('ks_template', 'ks_template_path'),
|
||||||
|
('ks_cfg', 'ks_cfg_url')):
|
||||||
if label in pxe_info:
|
if label in pxe_info:
|
||||||
if ipxe_enabled:
|
if ipxe_enabled or label in ('stage2', 'ks_template', 'ks_cfg'):
|
||||||
# NOTE(pas-ha) do not use Swift TempURLs for kernel and
|
# NOTE(pas-ha) do not use Swift TempURLs for kernel and
|
||||||
# ramdisk of user image when boot_option is not local,
|
# ramdisk of user image when boot_option is not local,
|
||||||
# as this breaks instance reboot later when temp urls
|
# as this breaks instance reboot later when temp urls
|
||||||
# have timed out.
|
# have timed out.
|
||||||
pxe_opts[option] = '/'.join(
|
pxe_opts[option] = get_http_url_path_from_label(
|
||||||
[CONF.deploy.http_url, node.uuid, label])
|
CONF.deploy.http_url, node.uuid, label)
|
||||||
else:
|
else:
|
||||||
# It is possible that we don't have kernel/ramdisk or even
|
# It is possible that we don't have kernel/ramdisk or even
|
||||||
# image_source to determine if it's a whole disk image or not.
|
# image_source to determine if it's a whole disk image or not.
|
||||||
@ -810,7 +892,8 @@ def build_service_pxe_config(task, instance_image_info,
|
|||||||
root_uuid_or_disk_id,
|
root_uuid_or_disk_id,
|
||||||
ramdisk_boot=False,
|
ramdisk_boot=False,
|
||||||
ipxe_enabled=False,
|
ipxe_enabled=False,
|
||||||
is_whole_disk_image=None):
|
is_whole_disk_image=None,
|
||||||
|
anaconda_boot=False):
|
||||||
node = task.node
|
node = task.node
|
||||||
pxe_config_path = get_pxe_config_file_path(node.uuid,
|
pxe_config_path = get_pxe_config_file_path(node.uuid,
|
||||||
ipxe_enabled=ipxe_enabled)
|
ipxe_enabled=ipxe_enabled)
|
||||||
@ -844,7 +927,38 @@ def build_service_pxe_config(task, instance_image_info,
|
|||||||
is_whole_disk_image,
|
is_whole_disk_image,
|
||||||
deploy_utils.is_trusted_boot_requested(node),
|
deploy_utils.is_trusted_boot_requested(node),
|
||||||
deploy_utils.is_iscsi_boot(task), ramdisk_boot,
|
deploy_utils.is_iscsi_boot(task), ramdisk_boot,
|
||||||
ipxe_enabled=ipxe_enabled)
|
ipxe_enabled=ipxe_enabled, anaconda_boot=anaconda_boot)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_heartbeat_url(node_uuid):
|
||||||
|
|
||||||
|
api_version = 'v1'
|
||||||
|
heartbeat_api = '%s/heartbeat/{node_uuid}' % api_version
|
||||||
|
path = heartbeat_api.format(node_uuid=node_uuid)
|
||||||
|
return "/".join([deploy_utils.get_ironic_api_url(), path])
|
||||||
|
|
||||||
|
|
||||||
|
def build_kickstart_config_options(task):
|
||||||
|
"""Build the kickstart template options for a node
|
||||||
|
|
||||||
|
This method builds the kickstart template options for a node,
|
||||||
|
given all the required parameters.
|
||||||
|
|
||||||
|
The options should then be passed to pxe_utils.create_kickstart_config to
|
||||||
|
create the actual config files.
|
||||||
|
|
||||||
|
:param task: A TaskManager object
|
||||||
|
:returns: A dictionary of kickstart options to be used in the kickstart
|
||||||
|
template.
|
||||||
|
"""
|
||||||
|
ks_options = {}
|
||||||
|
node = task.node
|
||||||
|
manager_utils.add_secret_token(node, pregenerated=True)
|
||||||
|
node.save()
|
||||||
|
ks_options['liveimg_url'] = node.instance_info['image_url']
|
||||||
|
ks_options['agent_token'] = node.driver_internal_info['agent_secret_token']
|
||||||
|
ks_options['heartbeat_url'] = _build_heartbeat_url(node.uuid)
|
||||||
|
return ks_options
|
||||||
|
|
||||||
|
|
||||||
def get_volume_pxe_options(task):
|
def get_volume_pxe_options(task):
|
||||||
@ -949,7 +1063,8 @@ def validate_boot_parameters_for_trusted_boot(node):
|
|||||||
def prepare_instance_pxe_config(task, image_info,
|
def prepare_instance_pxe_config(task, image_info,
|
||||||
iscsi_boot=False,
|
iscsi_boot=False,
|
||||||
ramdisk_boot=False,
|
ramdisk_boot=False,
|
||||||
ipxe_enabled=False):
|
ipxe_enabled=False,
|
||||||
|
anaconda_boot=False):
|
||||||
"""Prepares the config file for PXE boot
|
"""Prepares the config file for PXE boot
|
||||||
|
|
||||||
:param task: a task from TaskManager.
|
:param task: a task from TaskManager.
|
||||||
@ -959,6 +1074,7 @@ def prepare_instance_pxe_config(task, image_info,
|
|||||||
:param ramdisk_boot: if the boot is to a ramdisk configuration.
|
:param ramdisk_boot: if the boot is to a ramdisk configuration.
|
||||||
:param ipxe_enabled: Default false boolean to indicate if ipxe
|
:param ipxe_enabled: Default false boolean to indicate if ipxe
|
||||||
is in use by the caller.
|
is in use by the caller.
|
||||||
|
:param anaconda_boot: if the boot is to a anaconda ramdisk configuration.
|
||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
node = task.node
|
node = task.node
|
||||||
@ -978,7 +1094,7 @@ def prepare_instance_pxe_config(task, image_info,
|
|||||||
node.uuid, ipxe_enabled=ipxe_enabled)
|
node.uuid, ipxe_enabled=ipxe_enabled)
|
||||||
if not os.path.isfile(pxe_config_path):
|
if not os.path.isfile(pxe_config_path):
|
||||||
pxe_options = build_pxe_config_options(
|
pxe_options = build_pxe_config_options(
|
||||||
task, image_info, service=ramdisk_boot,
|
task, image_info, service=ramdisk_boot or anaconda_boot,
|
||||||
ipxe_enabled=ipxe_enabled)
|
ipxe_enabled=ipxe_enabled)
|
||||||
if ipxe_enabled:
|
if ipxe_enabled:
|
||||||
pxe_config_template = (
|
pxe_config_template = (
|
||||||
@ -993,7 +1109,23 @@ def prepare_instance_pxe_config(task, image_info,
|
|||||||
pxe_config_path, None,
|
pxe_config_path, None,
|
||||||
boot_mode_utils.get_boot_mode(node), False,
|
boot_mode_utils.get_boot_mode(node), False,
|
||||||
iscsi_boot=iscsi_boot, ramdisk_boot=ramdisk_boot,
|
iscsi_boot=iscsi_boot, ramdisk_boot=ramdisk_boot,
|
||||||
ipxe_enabled=ipxe_enabled)
|
ipxe_enabled=ipxe_enabled, anaconda_boot=anaconda_boot)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_instance_kickstart_config(task, image_info, anaconda_boot=False):
|
||||||
|
"""Prepare to boot anaconda ramdisk by generating kickstart file
|
||||||
|
|
||||||
|
:param task: a task from TaskManager.
|
||||||
|
:param image_info: a dict of values of instance image
|
||||||
|
metadata to set on the configuration file.
|
||||||
|
:param anaconda_boot: if the boot is to a anaconda ramdisk configuration.
|
||||||
|
"""
|
||||||
|
if not anaconda_boot:
|
||||||
|
return
|
||||||
|
ks_options = build_kickstart_config_options(task)
|
||||||
|
kickstart_template = image_info['ks_template'][1]
|
||||||
|
ks_cfg = utils.render_template(kickstart_template, ks_options)
|
||||||
|
utils.write_to_file(image_info['ks_cfg'][1], ks_cfg)
|
||||||
|
|
||||||
|
|
||||||
@image_cache.cleanup(priority=25)
|
@image_cache.cleanup(priority=25)
|
||||||
@ -1012,14 +1144,30 @@ def cache_ramdisk_kernel(task, pxe_info, ipxe_enabled=False):
|
|||||||
"""Fetch the necessary kernels and ramdisks for the instance."""
|
"""Fetch the necessary kernels and ramdisks for the instance."""
|
||||||
ctx = task.context
|
ctx = task.context
|
||||||
node = task.node
|
node = task.node
|
||||||
|
t_pxe_info = copy.copy(pxe_info)
|
||||||
if ipxe_enabled:
|
if ipxe_enabled:
|
||||||
path = os.path.join(get_ipxe_root_dir(), node.uuid)
|
path = os.path.join(get_ipxe_root_dir(), node.uuid)
|
||||||
else:
|
else:
|
||||||
path = os.path.join(get_root_dir(), node.uuid)
|
path = os.path.join(get_root_dir(), node.uuid)
|
||||||
fileutils.ensure_tree(path)
|
fileutils.ensure_tree(path)
|
||||||
|
# anconda deploy will have 'stage2' as one of the labels in pxe_info dict
|
||||||
|
if 'stage2' in pxe_info.keys():
|
||||||
|
# stage2 will be stored in ipxe http directory. So make sure they
|
||||||
|
# exist.
|
||||||
|
fileutils.ensure_tree(
|
||||||
|
get_file_path_from_label(
|
||||||
|
node.uuid,
|
||||||
|
get_ipxe_root_dir(),
|
||||||
|
'stage2'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# ks_cfg is rendered later by the driver using ks_template. It cannot
|
||||||
|
# be fetched and cached.
|
||||||
|
t_pxe_info.pop('ks_cfg')
|
||||||
|
|
||||||
LOG.debug("Fetching necessary kernel and ramdisk for node %s",
|
LOG.debug("Fetching necessary kernel and ramdisk for node %s",
|
||||||
node.uuid)
|
node.uuid)
|
||||||
deploy_utils.fetch_images(ctx, TFTPImageCache(), list(pxe_info.values()),
|
deploy_utils.fetch_images(ctx, TFTPImageCache(), list(t_pxe_info.values()),
|
||||||
CONF.force_raw_images)
|
CONF.force_raw_images)
|
||||||
|
|
||||||
|
|
||||||
|
@ -131,7 +131,8 @@ def _replace_root_uuid(path, root_uuid):
|
|||||||
|
|
||||||
def _replace_boot_line(path, boot_mode, is_whole_disk_image,
|
def _replace_boot_line(path, boot_mode, is_whole_disk_image,
|
||||||
trusted_boot=False, iscsi_boot=False,
|
trusted_boot=False, iscsi_boot=False,
|
||||||
ramdisk_boot=False, ipxe_enabled=False):
|
ramdisk_boot=False, ipxe_enabled=False,
|
||||||
|
anaconda_boot=False):
|
||||||
if is_whole_disk_image:
|
if is_whole_disk_image:
|
||||||
boot_disk_type = 'boot_whole_disk'
|
boot_disk_type = 'boot_whole_disk'
|
||||||
elif trusted_boot:
|
elif trusted_boot:
|
||||||
@ -140,6 +141,8 @@ def _replace_boot_line(path, boot_mode, is_whole_disk_image,
|
|||||||
boot_disk_type = 'boot_iscsi'
|
boot_disk_type = 'boot_iscsi'
|
||||||
elif ramdisk_boot:
|
elif ramdisk_boot:
|
||||||
boot_disk_type = 'boot_ramdisk'
|
boot_disk_type = 'boot_ramdisk'
|
||||||
|
elif anaconda_boot:
|
||||||
|
boot_disk_type = 'boot_anaconda'
|
||||||
else:
|
else:
|
||||||
boot_disk_type = 'boot_partition'
|
boot_disk_type = 'boot_partition'
|
||||||
|
|
||||||
@ -163,7 +166,7 @@ def _replace_disk_identifier(path, disk_identifier):
|
|||||||
def switch_pxe_config(path, root_uuid_or_disk_id, boot_mode,
|
def switch_pxe_config(path, root_uuid_or_disk_id, boot_mode,
|
||||||
is_whole_disk_image, trusted_boot=False,
|
is_whole_disk_image, trusted_boot=False,
|
||||||
iscsi_boot=False, ramdisk_boot=False,
|
iscsi_boot=False, ramdisk_boot=False,
|
||||||
ipxe_enabled=False):
|
ipxe_enabled=False, anaconda_boot=False):
|
||||||
"""Switch a pxe config from deployment mode to service mode.
|
"""Switch a pxe config from deployment mode to service mode.
|
||||||
|
|
||||||
:param path: path to the pxe config file in tftpboot.
|
:param path: path to the pxe config file in tftpboot.
|
||||||
@ -178,15 +181,17 @@ def switch_pxe_config(path, root_uuid_or_disk_id, boot_mode,
|
|||||||
:param ramdisk_boot: if the boot is to be to a ramdisk configuration.
|
:param ramdisk_boot: if the boot is to be to a ramdisk configuration.
|
||||||
:param ipxe_enabled: A default False boolean value to tell the method
|
:param ipxe_enabled: A default False boolean value to tell the method
|
||||||
if the caller is using iPXE.
|
if the caller is using iPXE.
|
||||||
|
:param anaconda_boot: if the boot is to be to an anaconda configuration.
|
||||||
"""
|
"""
|
||||||
if not ramdisk_boot and root_uuid_or_disk_id is not None:
|
if (not (ramdisk_boot or anaconda_boot)
|
||||||
|
and root_uuid_or_disk_id is not None):
|
||||||
if not is_whole_disk_image:
|
if not is_whole_disk_image:
|
||||||
_replace_root_uuid(path, root_uuid_or_disk_id)
|
_replace_root_uuid(path, root_uuid_or_disk_id)
|
||||||
else:
|
else:
|
||||||
_replace_disk_identifier(path, root_uuid_or_disk_id)
|
_replace_disk_identifier(path, root_uuid_or_disk_id)
|
||||||
|
|
||||||
_replace_boot_line(path, boot_mode, is_whole_disk_image, trusted_boot,
|
_replace_boot_line(path, boot_mode, is_whole_disk_image, trusted_boot,
|
||||||
iscsi_boot, ramdisk_boot, ipxe_enabled)
|
iscsi_boot, ramdisk_boot, ipxe_enabled, anaconda_boot)
|
||||||
|
|
||||||
|
|
||||||
def check_for_missing_params(info_dict, error_msg, param_prefix=''):
|
def check_for_missing_params(info_dict, error_msg, param_prefix=''):
|
||||||
|
@ -31,6 +31,12 @@ kernel {% if pxe_options.ipxe_timeout > 0 %}--timeout {{ pxe_options.ipxe_timeou
|
|||||||
initrd {% if pxe_options.ipxe_timeout > 0 %}--timeout {{ pxe_options.ipxe_timeout }} {% endif %}{{ pxe_options.ari_path }} || goto boot_partition
|
initrd {% if pxe_options.ipxe_timeout > 0 %}--timeout {{ pxe_options.ipxe_timeout }} {% endif %}{{ pxe_options.ari_path }} || goto boot_partition
|
||||||
boot
|
boot
|
||||||
|
|
||||||
|
:boot_anaconda
|
||||||
|
imgfree
|
||||||
|
kernel {% if pxe_options.ipxe_timeout > 0 %}--timeout {{ pxe_options.ipxe_timeout }} {% endif %}{{ pxe_options.aki_path }} text {{ pxe_options.pxe_append_params|default("", true) }} inst.ks={{ pxe_options.ks_cfg_url }} inst.stage2={{ pxe_options.stage2_url }} initrd=ramdisk || goto boot_anaconda
|
||||||
|
initrd {% if pxe_options.ipxe_timeout > 0 %}--timeout {{ pxe_options.ipxe_timeout }} {% endif %}{{ pxe_options.ari_path }} || goto boot_anaconda
|
||||||
|
boot
|
||||||
|
|
||||||
:boot_ramdisk
|
:boot_ramdisk
|
||||||
imgfree
|
imgfree
|
||||||
{%- if pxe_options.boot_iso_url %}
|
{%- if pxe_options.boot_iso_url %}
|
||||||
|
@ -234,18 +234,23 @@ class PXEBaseMixin(object):
|
|||||||
boot_option = deploy_utils.get_boot_option(node)
|
boot_option = deploy_utils.get_boot_option(node)
|
||||||
boot_device = None
|
boot_device = None
|
||||||
instance_image_info = {}
|
instance_image_info = {}
|
||||||
if boot_option == "ramdisk":
|
if boot_option == "ramdisk" or boot_option == "kickstart":
|
||||||
instance_image_info = pxe_utils.get_instance_image_info(
|
instance_image_info = pxe_utils.get_instance_image_info(
|
||||||
task, ipxe_enabled=self.ipxe_enabled)
|
task, ipxe_enabled=self.ipxe_enabled)
|
||||||
pxe_utils.cache_ramdisk_kernel(task, instance_image_info,
|
pxe_utils.cache_ramdisk_kernel(task, instance_image_info,
|
||||||
ipxe_enabled=self.ipxe_enabled)
|
ipxe_enabled=self.ipxe_enabled)
|
||||||
|
|
||||||
if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk":
|
if (deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk"
|
||||||
|
or boot_option == "kickstart"):
|
||||||
pxe_utils.prepare_instance_pxe_config(
|
pxe_utils.prepare_instance_pxe_config(
|
||||||
task, instance_image_info,
|
task, instance_image_info,
|
||||||
iscsi_boot=deploy_utils.is_iscsi_boot(task),
|
iscsi_boot=deploy_utils.is_iscsi_boot(task),
|
||||||
ramdisk_boot=(boot_option == "ramdisk"),
|
ramdisk_boot=(boot_option == "ramdisk"),
|
||||||
|
anaconda_boot=(boot_option == "kickstart"),
|
||||||
ipxe_enabled=self.ipxe_enabled)
|
ipxe_enabled=self.ipxe_enabled)
|
||||||
|
pxe_utils.prepare_instance_kickstart_config(
|
||||||
|
task, instance_image_info,
|
||||||
|
anaconda_boot=(boot_option == "kickstart"))
|
||||||
boot_device = boot_devices.PXE
|
boot_device = boot_devices.PXE
|
||||||
|
|
||||||
elif boot_option != "local":
|
elif boot_option != "local":
|
||||||
|
@ -22,3 +22,8 @@ append tboot.gz --- {{pxe_options.aki_path}} root={{ ROOT }} ro text {{ pxe_opti
|
|||||||
label boot_ramdisk
|
label boot_ramdisk
|
||||||
kernel {{ pxe_options.aki_path }}
|
kernel {{ pxe_options.aki_path }}
|
||||||
append initrd={{ pxe_options.ari_path }} root=/dev/ram0 text {{ pxe_options.pxe_append_params|default("", true) }} {{ pxe_options.ramdisk_opts|default('', true) }}
|
append initrd={{ pxe_options.ari_path }} root=/dev/ram0 text {{ pxe_options.pxe_append_params|default("", true) }} {{ pxe_options.ramdisk_opts|default('', true) }}
|
||||||
|
|
||||||
|
label boot_anaconda
|
||||||
|
kernel {{ pxe_options.aki_path }}
|
||||||
|
append initrd={{ pxe_options.ari_path }} text {{ pxe_options.pxe_append_params|default("", true) }} inst.ks={{ pxe_options.ks_cfg_url }} inst.stage2={{ pxe_options.stage2_url }}
|
||||||
|
ipappend 2
|
||||||
|
@ -62,6 +62,8 @@ class TestPXEUtils(db_base.DbTestCase):
|
|||||||
'ipa-api-url': 'http://192.168.122.184:6385',
|
'ipa-api-url': 'http://192.168.122.184:6385',
|
||||||
'ipxe_timeout': 0,
|
'ipxe_timeout': 0,
|
||||||
'ramdisk_opts': 'ramdisk_param',
|
'ramdisk_opts': 'ramdisk_param',
|
||||||
|
'ks_cfg_url': 'http://fake/ks.cfg',
|
||||||
|
'stage2_url': 'http://fake/stage2'
|
||||||
}
|
}
|
||||||
|
|
||||||
self.ipxe_options = self.pxe_options.copy()
|
self.ipxe_options = self.pxe_options.copy()
|
||||||
@ -1144,6 +1146,81 @@ class PXEInterfacesTestCase(db_base.DbTestCase):
|
|||||||
|
|
||||||
boot_opt_mock.assert_called_once_with(task.node)
|
boot_opt_mock.assert_called_once_with(task.node)
|
||||||
|
|
||||||
|
@mock.patch('ironic.drivers.modules.deploy_utils.get_boot_option',
|
||||||
|
return_value='kickstart', autospec=True)
|
||||||
|
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
|
||||||
|
def test_get_instance_image_info_with_kickstart_boot_option(
|
||||||
|
self, image_show_mock, boot_opt_mock):
|
||||||
|
properties = {'properties': {u'kernel_id': u'instance_kernel_uuid',
|
||||||
|
u'ramdisk_id': u'instance_ramdisk_uuid',
|
||||||
|
u'stage2_id': u'instance_stage2_id'}}
|
||||||
|
|
||||||
|
expected_info = {'ramdisk':
|
||||||
|
('instance_ramdisk_uuid',
|
||||||
|
os.path.join(CONF.pxe.tftp_root,
|
||||||
|
self.node.uuid,
|
||||||
|
'ramdisk')),
|
||||||
|
'kernel':
|
||||||
|
('instance_kernel_uuid',
|
||||||
|
os.path.join(CONF.pxe.tftp_root,
|
||||||
|
self.node.uuid,
|
||||||
|
'kernel')),
|
||||||
|
'stage2':
|
||||||
|
('instance_stage2_id',
|
||||||
|
os.path.join(CONF.deploy.http_root,
|
||||||
|
self.node.uuid,
|
||||||
|
'LiveOS',
|
||||||
|
'squashfs.img')),
|
||||||
|
'ks_template':
|
||||||
|
(CONF.anaconda.default_ks_template,
|
||||||
|
os.path.join(CONF.deploy.http_root,
|
||||||
|
self.node.uuid,
|
||||||
|
'ks.cfg.template')),
|
||||||
|
'ks_cfg':
|
||||||
|
('',
|
||||||
|
os.path.join(CONF.deploy.http_root,
|
||||||
|
self.node.uuid,
|
||||||
|
'ks.cfg'))}
|
||||||
|
image_show_mock.return_value = properties
|
||||||
|
self.context.auth_token = 'fake'
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
image_info = pxe_utils.get_instance_image_info(
|
||||||
|
task, ipxe_enabled=False)
|
||||||
|
self.assertEqual(expected_info, image_info)
|
||||||
|
# In the absense of kickstart template in both instance_info and
|
||||||
|
# image default kickstart template is used
|
||||||
|
self.assertEqual(CONF.anaconda.default_ks_template,
|
||||||
|
image_info['ks_template'][0])
|
||||||
|
calls = [mock.call(task.node), mock.call(task.node)]
|
||||||
|
boot_opt_mock.assert_has_calls(calls)
|
||||||
|
# Instance info gets presedence over kickstart template on the
|
||||||
|
# image
|
||||||
|
properties['properties'] = {'ks_template': 'glance://template_id'}
|
||||||
|
task.node.instance_info['ks_template'] = 'https://server/fake.tmpl'
|
||||||
|
image_show_mock.return_value = properties
|
||||||
|
image_info = pxe_utils.get_instance_image_info(
|
||||||
|
task, ipxe_enabled=False)
|
||||||
|
self.assertEqual('https://server/fake.tmpl',
|
||||||
|
image_info['ks_template'][0])
|
||||||
|
|
||||||
|
@mock.patch('ironic.drivers.modules.deploy_utils.get_boot_option',
|
||||||
|
return_value='kickstart', autospec=True)
|
||||||
|
@mock.patch.object(image_service.GlanceImageService, 'show', autospec=True)
|
||||||
|
def test_get_instance_image_info_kickstart_stage2_missing(
|
||||||
|
self, image_show_mock, boot_opt_mock):
|
||||||
|
properties = {'properties': {u'kernel_id': u'instance_kernel_uuid',
|
||||||
|
u'ramdisk_id': u'instance_ramdisk_uuid'}}
|
||||||
|
|
||||||
|
image_show_mock.return_value = properties
|
||||||
|
self.context.auth_token = 'fake'
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
self.assertRaises(
|
||||||
|
exception.ImageUnacceptable, pxe_utils.get_instance_image_info,
|
||||||
|
task, ipxe_enabled=False
|
||||||
|
)
|
||||||
|
|
||||||
@mock.patch.object(deploy_utils, 'fetch_images', autospec=True)
|
@mock.patch.object(deploy_utils, 'fetch_images', autospec=True)
|
||||||
def test__cache_tftp_images_master_path(self, mock_fetch_image):
|
def test__cache_tftp_images_master_path(self, mock_fetch_image):
|
||||||
temp_dir = tempfile.mkdtemp()
|
temp_dir = tempfile.mkdtemp()
|
||||||
@ -1242,6 +1319,68 @@ class PXEInterfacesTestCase(db_base.DbTestCase):
|
|||||||
self.assertFalse(mock_log.called)
|
self.assertFalse(mock_log.called)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(pxe.PXEBoot, '__init__', lambda self: None)
|
||||||
|
class PXEBuildKickstartConfigOptionsTestCase(db_base.DbTestCase):
|
||||||
|
def setUp(self):
|
||||||
|
super(PXEBuildKickstartConfigOptionsTestCase, self).setUp()
|
||||||
|
n = {
|
||||||
|
'driver': 'fake-hardware',
|
||||||
|
'boot_interface': 'pxe',
|
||||||
|
'instance_info': INST_INFO_DICT,
|
||||||
|
'driver_info': DRV_INFO_DICT,
|
||||||
|
'driver_internal_info': DRV_INTERNAL_INFO_DICT,
|
||||||
|
}
|
||||||
|
n['instance_info']['image_url'] = 'http://ironic/node/os_image.tar'
|
||||||
|
self.config_temp_dir('http_root', group='deploy')
|
||||||
|
self.node = object_utils.create_test_node(self.context, **n)
|
||||||
|
|
||||||
|
@mock.patch.object(deploy_utils, 'get_ironic_api_url', autospec=True)
|
||||||
|
def test_build_kickstart_config_options_pxe(self, api_url_mock):
|
||||||
|
api_url_mock.return_value = 'http://ironic-api'
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
expected = {}
|
||||||
|
expected['liveimg_url'] = task.node.instance_info['image_url']
|
||||||
|
expected['heartbeat_url'] = (
|
||||||
|
'http://ironic-api/v1/heartbeat/%s' % task.node.uuid
|
||||||
|
)
|
||||||
|
ks_options = pxe_utils.build_kickstart_config_options(task)
|
||||||
|
self.assertTrue(ks_options.pop('agent_token'))
|
||||||
|
self.assertEqual(expected, ks_options)
|
||||||
|
|
||||||
|
@mock.patch('ironic.common.utils.render_template', autospec=True)
|
||||||
|
def test_prepare_instance_kickstart_config_not_anaconda_boot(self,
|
||||||
|
render_mock):
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
self.assertFalse(
|
||||||
|
pxe_utils.prepare_instance_kickstart_config(task, {})
|
||||||
|
)
|
||||||
|
render_mock.assert_not_called()
|
||||||
|
|
||||||
|
@mock.patch('ironic.common.utils.render_template', autospec=True)
|
||||||
|
@mock.patch('ironic.common.pxe_utils.build_kickstart_config_options',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
|
||||||
|
def test_prepare_instance_kickstart_config(self, write_mock,
|
||||||
|
ks_options_mock, render_mock):
|
||||||
|
image_info = {
|
||||||
|
'ks_cfg': ['', '/http_root/node_uuid/ks.cfg'],
|
||||||
|
'ks_template': ['tmpl_id', '/http_root/node_uuid/ks.cfg.template']
|
||||||
|
}
|
||||||
|
ks_options = {'liveimg_url': 'http://fake', 'agent_token': 'faketoken',
|
||||||
|
'heartbeat_url': 'http://fake_hb'}
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
ks_options_mock.return_value = ks_options
|
||||||
|
pxe_utils.prepare_instance_kickstart_config(task, image_info,
|
||||||
|
anaconda_boot=True)
|
||||||
|
render_mock.assert_called_with(image_info['ks_template'][1],
|
||||||
|
ks_options)
|
||||||
|
write_mock.assert_called_with(image_info['ks_cfg'][1],
|
||||||
|
render_mock.return_value)
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(pxe.PXEBoot, '__init__', lambda self: None)
|
@mock.patch.object(pxe.PXEBoot, '__init__', lambda self: None)
|
||||||
class PXEBuildConfigOptionsTestCase(db_base.DbTestCase):
|
class PXEBuildConfigOptionsTestCase(db_base.DbTestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -31,6 +31,12 @@ kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param initrd=ramd
|
|||||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||||
boot
|
boot
|
||||||
|
|
||||||
|
:boot_anaconda
|
||||||
|
imgfree
|
||||||
|
kernel http://1.2.3.4:1234/kernel text test_param inst.ks=http://fake/ks.cfg inst.stage2=http://fake/stage2 initrd=ramdisk || goto boot_anaconda
|
||||||
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_anaconda
|
||||||
|
boot
|
||||||
|
|
||||||
:boot_ramdisk
|
:boot_ramdisk
|
||||||
imgfree
|
imgfree
|
||||||
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
||||||
|
@ -31,6 +31,12 @@ kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param initrd=ramd
|
|||||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||||
boot
|
boot
|
||||||
|
|
||||||
|
:boot_anaconda
|
||||||
|
imgfree
|
||||||
|
kernel http://1.2.3.4:1234/kernel text test_param inst.ks=http://fake/ks.cfg inst.stage2=http://fake/stage2 initrd=ramdisk || goto boot_anaconda
|
||||||
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_anaconda
|
||||||
|
boot
|
||||||
|
|
||||||
:boot_ramdisk
|
:boot_ramdisk
|
||||||
imgfree
|
imgfree
|
||||||
sanboot http://1.2.3.4:1234/uuid/iso
|
sanboot http://1.2.3.4:1234/uuid/iso
|
||||||
|
@ -31,6 +31,12 @@ kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param initrd=ramd
|
|||||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||||
boot
|
boot
|
||||||
|
|
||||||
|
:boot_anaconda
|
||||||
|
imgfree
|
||||||
|
kernel http://1.2.3.4:1234/kernel text test_param inst.ks=http://fake/ks.cfg inst.stage2=http://fake/stage2 initrd=ramdisk || goto boot_anaconda
|
||||||
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_anaconda
|
||||||
|
boot
|
||||||
|
|
||||||
:boot_ramdisk
|
:boot_ramdisk
|
||||||
imgfree
|
imgfree
|
||||||
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
||||||
|
@ -31,6 +31,12 @@ kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param initrd=ramd
|
|||||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||||
boot
|
boot
|
||||||
|
|
||||||
|
:boot_anaconda
|
||||||
|
imgfree
|
||||||
|
kernel http://1.2.3.4:1234/kernel text test_param inst.ks=http://fake/ks.cfg inst.stage2=http://fake/stage2 initrd=ramdisk || goto boot_anaconda
|
||||||
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_anaconda
|
||||||
|
boot
|
||||||
|
|
||||||
:boot_ramdisk
|
:boot_ramdisk
|
||||||
imgfree
|
imgfree
|
||||||
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
||||||
|
@ -31,6 +31,12 @@ kernel http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_param initrd=ramd
|
|||||||
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||||
boot
|
boot
|
||||||
|
|
||||||
|
:boot_anaconda
|
||||||
|
imgfree
|
||||||
|
kernel http://1.2.3.4:1234/kernel text test_param inst.ks=http://fake/ks.cfg inst.stage2=http://fake/stage2 initrd=ramdisk || goto boot_anaconda
|
||||||
|
initrd http://1.2.3.4:1234/ramdisk || goto boot_anaconda
|
||||||
|
boot
|
||||||
|
|
||||||
:boot_ramdisk
|
:boot_ramdisk
|
||||||
imgfree
|
imgfree
|
||||||
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
kernel http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
||||||
|
@ -31,6 +31,12 @@ kernel --timeout 120 http://1.2.3.4:1234/kernel root={{ ROOT }} ro text test_par
|
|||||||
initrd --timeout 120 http://1.2.3.4:1234/ramdisk || goto boot_partition
|
initrd --timeout 120 http://1.2.3.4:1234/ramdisk || goto boot_partition
|
||||||
boot
|
boot
|
||||||
|
|
||||||
|
:boot_anaconda
|
||||||
|
imgfree
|
||||||
|
kernel --timeout 120 http://1.2.3.4:1234/kernel text test_param inst.ks=http://fake/ks.cfg inst.stage2=http://fake/stage2 initrd=ramdisk || goto boot_anaconda
|
||||||
|
initrd --timeout 120 http://1.2.3.4:1234/ramdisk || goto boot_anaconda
|
||||||
|
boot
|
||||||
|
|
||||||
:boot_ramdisk
|
:boot_ramdisk
|
||||||
imgfree
|
imgfree
|
||||||
kernel --timeout 120 http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
kernel --timeout 120 http://1.2.3.4:1234/kernel root=/dev/ram0 text test_param ramdisk_param initrd=ramdisk || goto boot_ramdisk
|
||||||
|
@ -600,7 +600,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
|||||||
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
|
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
|
||||||
switch_pxe_config_mock.assert_called_once_with(
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
||||||
'bios', False, False, False, False, ipxe_enabled=True)
|
'bios', False, False, False, False, ipxe_enabled=True,
|
||||||
|
anaconda_boot=False)
|
||||||
set_boot_device_mock.assert_called_once_with(task,
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
boot_devices.PXE,
|
boot_devices.PXE,
|
||||||
persistent=True)
|
persistent=True)
|
||||||
@ -649,7 +650,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
|||||||
ipxe_enabled=True)
|
ipxe_enabled=True)
|
||||||
switch_pxe_config_mock.assert_called_once_with(
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
||||||
'bios', False, False, False, False, ipxe_enabled=True)
|
'bios', False, False, False, False, ipxe_enabled=True,
|
||||||
|
anaconda_boot=False)
|
||||||
self.assertFalse(set_boot_device_mock.called)
|
self.assertFalse(set_boot_device_mock.called)
|
||||||
|
|
||||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||||
@ -766,7 +768,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
|||||||
ipxe_enabled=True)
|
ipxe_enabled=True)
|
||||||
switch_pxe_config_mock.assert_called_once_with(
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
pxe_config_path, None, boot_modes.LEGACY_BIOS, False,
|
pxe_config_path, None, boot_modes.LEGACY_BIOS, False,
|
||||||
ipxe_enabled=True, iscsi_boot=True, ramdisk_boot=False)
|
ipxe_enabled=True, iscsi_boot=True, ramdisk_boot=False,
|
||||||
|
anaconda_boot=False)
|
||||||
set_boot_device_mock.assert_called_once_with(task,
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
boot_devices.PXE,
|
boot_devices.PXE,
|
||||||
persistent=True)
|
persistent=True)
|
||||||
@ -812,7 +815,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
|||||||
ipxe_enabled=True)
|
ipxe_enabled=True)
|
||||||
switch_pxe_config_mock.assert_called_once_with(
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
pxe_config_path, None, boot_modes.LEGACY_BIOS, False,
|
pxe_config_path, None, boot_modes.LEGACY_BIOS, False,
|
||||||
ipxe_enabled=True, iscsi_boot=False, ramdisk_boot=True)
|
ipxe_enabled=True, iscsi_boot=False, ramdisk_boot=True,
|
||||||
|
anaconda_boot=False)
|
||||||
set_boot_device_mock.assert_called_once_with(task,
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
boot_devices.PXE,
|
boot_devices.PXE,
|
||||||
persistent=True)
|
persistent=True)
|
||||||
@ -880,7 +884,8 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
|||||||
persistent=True)
|
persistent=True)
|
||||||
switch_pxe_config_mock.assert_called_once_with(
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
||||||
'bios', True, False, False, False, ipxe_enabled=True)
|
'bios', True, False, False, False, ipxe_enabled=True,
|
||||||
|
anaconda_boot=False)
|
||||||
# No clean up
|
# No clean up
|
||||||
self.assertFalse(clean_up_pxe_config_mock.called)
|
self.assertFalse(clean_up_pxe_config_mock.called)
|
||||||
# No netboot configuration beyond the PXE files
|
# No netboot configuration beyond the PXE files
|
||||||
|
@ -74,6 +74,7 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
group='anaconda')
|
group='anaconda')
|
||||||
instance_info = INST_INFO_DICT
|
instance_info = INST_INFO_DICT
|
||||||
instance_info['deploy_key'] = 'fake-56789'
|
instance_info['deploy_key'] = 'fake-56789'
|
||||||
|
instance_info['image_url'] = 'http://fakeserver/os.tar.gz'
|
||||||
|
|
||||||
self.config(enabled_boot_interfaces=[self.boot_interface,
|
self.config(enabled_boot_interfaces=[self.boot_interface,
|
||||||
'ipxe', 'fake'])
|
'ipxe', 'fake'])
|
||||||
@ -527,7 +528,8 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
|
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
|
||||||
switch_pxe_config_mock.assert_called_once_with(
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
||||||
'bios', False, False, False, False, ipxe_enabled=False)
|
'bios', False, False, False, False, ipxe_enabled=False,
|
||||||
|
anaconda_boot=False)
|
||||||
set_boot_device_mock.assert_called_once_with(task,
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
boot_devices.PXE,
|
boot_devices.PXE,
|
||||||
persistent=True)
|
persistent=True)
|
||||||
@ -575,7 +577,8 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
ipxe_enabled=False)
|
ipxe_enabled=False)
|
||||||
switch_pxe_config_mock.assert_called_once_with(
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
pxe_config_path, "30212642-09d3-467f-8e09-21685826ab50",
|
||||||
'bios', False, False, False, False, ipxe_enabled=False)
|
'bios', False, False, False, False, ipxe_enabled=False,
|
||||||
|
anaconda_boot=False)
|
||||||
self.assertFalse(set_boot_device_mock.called)
|
self.assertFalse(set_boot_device_mock.called)
|
||||||
|
|
||||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||||
@ -727,7 +730,7 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
switch_pxe_config_mock.assert_called_once_with(
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
pxe_config_path, None,
|
pxe_config_path, None,
|
||||||
'bios', False, ipxe_enabled=False, iscsi_boot=False,
|
'bios', False, ipxe_enabled=False, iscsi_boot=False,
|
||||||
ramdisk_boot=True)
|
ramdisk_boot=True, anaconda_boot=False)
|
||||||
set_boot_device_mock.assert_called_once_with(task,
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
boot_devices.PXE,
|
boot_devices.PXE,
|
||||||
persistent=True)
|
persistent=True)
|
||||||
@ -740,6 +743,63 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
def test_prepare_instance_ramdisk_pxe_conf_exists(self):
|
def test_prepare_instance_ramdisk_pxe_conf_exists(self):
|
||||||
self._test_prepare_instance_ramdisk(config_file_exits=False)
|
self._test_prepare_instance_ramdisk(config_file_exits=False)
|
||||||
|
|
||||||
|
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||||
|
@mock.patch.object(deploy_utils, 'switch_pxe_config', autospec=True)
|
||||||
|
@mock.patch.object(pxe_utils, 'create_pxe_config', autospec=True)
|
||||||
|
@mock.patch.object(dhcp_factory, 'DHCPFactory', autospec=True)
|
||||||
|
@mock.patch.object(pxe_utils, 'cache_ramdisk_kernel', autospec=True)
|
||||||
|
@mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True)
|
||||||
|
@mock.patch('ironic.drivers.modules.deploy_utils.get_boot_option',
|
||||||
|
return_value='kickstart', autospec=True)
|
||||||
|
@mock.patch('ironic.drivers.modules.deploy_utils.get_ironic_api_url',
|
||||||
|
return_value='http://fakeserver/api', autospec=True)
|
||||||
|
@mock.patch('ironic.common.utils.render_template', autospec=True)
|
||||||
|
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
|
||||||
|
def test_prepare_instance_kickstart(
|
||||||
|
self, write_file_mock, render_mock, api_url_mock, boot_opt_mock,
|
||||||
|
get_image_info_mock, cache_mock, dhcp_factory_mock,
|
||||||
|
create_pxe_config_mock, switch_pxe_config_mock,
|
||||||
|
set_boot_device_mock):
|
||||||
|
image_info = {'kernel': ['ins_kernel_id', '/path/to/kernel'],
|
||||||
|
'ramdisk': ['ins_ramdisk_id', '/path/to/ramdisk'],
|
||||||
|
'stage2': ['ins_stage2_id', '/path/to/stage2'],
|
||||||
|
'ks_cfg': ['', '/path/to/ks.cfg'],
|
||||||
|
'ks_template': ['template_id', '/path/to/ks_template']}
|
||||||
|
get_image_info_mock.return_value = image_info
|
||||||
|
provider_mock = mock.MagicMock()
|
||||||
|
dhcp_factory_mock.return_value = provider_mock
|
||||||
|
self.node.provision_state = states.DEPLOYING
|
||||||
|
self.config(http_url='http://fake_url', group='deploy')
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
|
dhcp_opts = pxe_utils.dhcp_options_for_instance(
|
||||||
|
task, ipxe_enabled=False)
|
||||||
|
dhcp_opts += pxe_utils.dhcp_options_for_instance(
|
||||||
|
task, ipxe_enabled=False, ip_version=6)
|
||||||
|
pxe_config_path = pxe_utils.get_pxe_config_file_path(
|
||||||
|
task.node.uuid)
|
||||||
|
|
||||||
|
task.driver.boot.prepare_instance(task)
|
||||||
|
|
||||||
|
get_image_info_mock.assert_called_once_with(task,
|
||||||
|
ipxe_enabled=False)
|
||||||
|
cache_mock.assert_called_once_with(
|
||||||
|
task, image_info, False)
|
||||||
|
provider_mock.update_dhcp.assert_called_once_with(task, dhcp_opts)
|
||||||
|
render_mock.assert_called()
|
||||||
|
write_file_mock.assert_called_with(
|
||||||
|
'/path/to/ks.cfg', render_mock.return_value
|
||||||
|
)
|
||||||
|
create_pxe_config_mock.assert_called_once_with(
|
||||||
|
task, mock.ANY, CONF.pxe.pxe_config_template,
|
||||||
|
ipxe_enabled=False)
|
||||||
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
|
pxe_config_path, None,
|
||||||
|
'bios', False, ipxe_enabled=False, iscsi_boot=False,
|
||||||
|
ramdisk_boot=False, anaconda_boot=True)
|
||||||
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
|
boot_devices.PXE,
|
||||||
|
persistent=True)
|
||||||
|
|
||||||
@mock.patch.object(boot_mode_utils, 'deconfigure_secure_boot_if_needed',
|
@mock.patch.object(boot_mode_utils, 'deconfigure_secure_boot_if_needed',
|
||||||
autospec=True)
|
autospec=True)
|
||||||
@mock.patch.object(pxe_utils, 'clean_up_pxe_env', autospec=True)
|
@mock.patch.object(pxe_utils, 'clean_up_pxe_env', autospec=True)
|
||||||
@ -826,7 +886,7 @@ class PXERamdiskDeployTestCase(db_base.DbTestCase):
|
|||||||
switch_pxe_config_mock.assert_called_once_with(
|
switch_pxe_config_mock.assert_called_once_with(
|
||||||
pxe_config_path, None,
|
pxe_config_path, None,
|
||||||
'bios', False, ipxe_enabled=False, iscsi_boot=False,
|
'bios', False, ipxe_enabled=False, iscsi_boot=False,
|
||||||
ramdisk_boot=True)
|
ramdisk_boot=True, anaconda_boot=False)
|
||||||
set_boot_device_mock.assert_called_once_with(task,
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
boot_devices.PXE,
|
boot_devices.PXE,
|
||||||
persistent=True)
|
persistent=True)
|
||||||
|
@ -22,3 +22,8 @@ append tboot.gz --- /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/kernel root={
|
|||||||
label boot_ramdisk
|
label boot_ramdisk
|
||||||
kernel /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/kernel
|
kernel /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/kernel
|
||||||
append initrd=/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/ramdisk root=/dev/ram0 text test_param ramdisk_param
|
append initrd=/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/ramdisk root=/dev/ram0 text test_param ramdisk_param
|
||||||
|
|
||||||
|
label boot_anaconda
|
||||||
|
kernel /tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/kernel
|
||||||
|
append initrd=/tftpboot/1be26c0b-03f2-4d2e-ae87-c02d7f33c123/ramdisk text test_param inst.ks=http://fake/ks.cfg inst.stage2=http://fake/stage2
|
||||||
|
ipappend 2
|
||||||
|
Loading…
x
Reference in New Issue
Block a user