Rename/update common/tftp.py to common/pxe_utils.py
The functions in the common/tftp.py are not related to TFTP, the functions are PXE related and most of them are used to generate the PXE configuration file. Moving it from tftp.py to pxe_utils.py will make it less confusing and is also needed by the iPXE work since iPXE doesn't generate any configuration file under the TFTP folder. The dhcp_options_for_instance() method was updated to not receive the PXE boot file image from the parameters and just get it from the configuration because we were always using the value from the configuration anyway. Implements: blueprint ipxe-boot Change-Id: Icfbaa4d6cdb9cbe0c392bf3dbd50ad980c70560c
This commit is contained in:
parent
036c79e38f
commit
a9d7a6567f
|
@ -972,6 +972,13 @@
|
|||
# Directory where images are stored on disk. (string value)
|
||||
#images_path=/var/lib/ironic/images/
|
||||
|
||||
# IP address of Ironic compute node's tftp server. (string
|
||||
# value)
|
||||
#tftp_server=$my_ip
|
||||
|
||||
# Ironic compute node's tftp root path. (string value)
|
||||
#tftp_root=/tftpboot
|
||||
|
||||
# Directory where master tftp images are stored on disk.
|
||||
# (string value)
|
||||
#tftp_master_path=/tftpboot/master_images
|
||||
|
@ -1016,17 +1023,3 @@
|
|||
#libvirt_uri=qemu:///system
|
||||
|
||||
|
||||
[tftp]
|
||||
|
||||
#
|
||||
# Options defined in ironic.common.tftp
|
||||
#
|
||||
|
||||
# IP address of Ironic compute node's tftp server. (string
|
||||
# value)
|
||||
#tftp_server=$my_ip
|
||||
|
||||
# Ironic compute node's tftp root path. (string value)
|
||||
#tftp_root=/tftpboot
|
||||
|
||||
|
||||
|
|
|
@ -23,7 +23,6 @@ from oslo.config import cfg
|
|||
from ironic.api import acl
|
||||
from ironic.common import exception
|
||||
from ironic.common import keystone
|
||||
from ironic.common import tftp
|
||||
from ironic.drivers.modules import ssh
|
||||
from ironic.openstack.common import log as logging
|
||||
|
||||
|
@ -144,9 +143,8 @@ def get_node_vif_ids(task):
|
|||
return port_vifs
|
||||
|
||||
|
||||
def update_neutron(task, pxe_bootfile_name):
|
||||
def update_neutron(task, options):
|
||||
"""Send or update the DHCP BOOT options to Neutron for this node."""
|
||||
options = tftp.dhcp_options_for_instance(pxe_bootfile_name)
|
||||
vifs = get_node_vif_ids(task)
|
||||
if not vifs:
|
||||
LOG.warning(_("No VIFs found for node %(node)s when attempting to "
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
#
|
||||
# Copyright 2014 Rackspace, Inc
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
import jinja2
|
||||
from oslo.config import cfg
|
||||
|
||||
from ironic.common import utils
|
||||
from ironic.drivers import utils as driver_utils
|
||||
from ironic.openstack.common import fileutils
|
||||
from ironic.openstack.common import log as logging
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
PXE_CFG_DIR_NAME = 'pxelinux.cfg'
|
||||
|
||||
|
||||
def _ensure_config_dirs_exist(node_uuid):
|
||||
"""Ensure that the node's and PXE configuration directories exist.
|
||||
|
||||
:param node_uuid: the UUID of the node.
|
||||
|
||||
"""
|
||||
tftp_root = CONF.pxe.tftp_root
|
||||
fileutils.ensure_tree(os.path.join(tftp_root, node_uuid))
|
||||
fileutils.ensure_tree(os.path.join(tftp_root, PXE_CFG_DIR_NAME))
|
||||
|
||||
|
||||
def _build_pxe_config(pxe_options, template):
|
||||
"""Build the PXE boot configuration file.
|
||||
|
||||
This method builds the PXE boot configuration file by rendering the
|
||||
template with the given parameters.
|
||||
|
||||
:param pxe_options: A dict of values to set on the configuration file.
|
||||
:param template: The PXE configuration template.
|
||||
:returns: A formatted string with the file content.
|
||||
|
||||
"""
|
||||
tmpl_path, tmpl_file = os.path.split(template)
|
||||
env = jinja2.Environment(loader=jinja2.FileSystemLoader(tmpl_path))
|
||||
template = env.get_template(tmpl_file)
|
||||
return template.render({'pxe_options': pxe_options,
|
||||
'ROOT': '{{ ROOT }}'})
|
||||
|
||||
|
||||
def _link_mac_pxe_configs(task):
|
||||
"""Link each MAC address with the PXE configuration file.
|
||||
|
||||
:param task: A TaskManager instance.
|
||||
|
||||
"""
|
||||
pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)
|
||||
for mac in driver_utils.get_node_mac_addresses(task):
|
||||
mac_path = _get_pxe_mac_path(mac)
|
||||
utils.unlink_without_raise(mac_path)
|
||||
utils.create_link_without_raise(pxe_config_file_path, mac_path)
|
||||
|
||||
|
||||
def _get_pxe_mac_path(mac):
|
||||
"""Convert a MAC address into a PXE config file name.
|
||||
|
||||
:param mac: A MAC address string in the format xx:xx:xx:xx:xx:xx.
|
||||
:returns: the path to the config file.
|
||||
|
||||
"""
|
||||
return os.path.join(
|
||||
CONF.pxe.tftp_root,
|
||||
PXE_CFG_DIR_NAME,
|
||||
"01-" + mac.replace(":", "-").lower()
|
||||
)
|
||||
|
||||
|
||||
def get_deploy_kr_info(node_uuid, driver_info):
|
||||
"""Get uuid and tftp path for deploy kernel and ramdisk.
|
||||
|
||||
Note: driver_info should be validated outside of this method.
|
||||
"""
|
||||
image_info = {}
|
||||
for label in ('deploy_kernel', 'deploy_ramdisk'):
|
||||
# the values for these keys will look like "glance://image-uuid"
|
||||
image_info[label] = (
|
||||
str(driver_info[label]).split('/')[-1],
|
||||
os.path.join(CONF.pxe.tftp_root, node_uuid, label)
|
||||
)
|
||||
return image_info
|
||||
|
||||
|
||||
def get_pxe_config_file_path(node_uuid):
|
||||
"""Generate the path for the node's PXE configuration file.
|
||||
|
||||
:param node_uuid: the UUID of the node.
|
||||
:returns: The path to the node's PXE configuration file.
|
||||
|
||||
"""
|
||||
return os.path.join(CONF.pxe.tftp_root, node_uuid, 'config')
|
||||
|
||||
|
||||
def create_pxe_config(task, pxe_options, template=None):
|
||||
"""Generate PXE configuration file and MAC address links for it.
|
||||
|
||||
This method will generate the PXE configuration file for the task's
|
||||
node under a directory named with the UUID of that node. For each
|
||||
MAC address (port) of that node, a symlink for the configuration file
|
||||
will be created under the PXE configuration directory, so regardless
|
||||
of which port boots first they'll get the same PXE configuration.
|
||||
|
||||
:param task: A TaskManager instance.
|
||||
:param pxe_options: A dictionary with the PXE configuration
|
||||
parameters.
|
||||
:param template: The PXE configuration template. If no template is
|
||||
given the CONF.pxe.pxe_config_template will be used.
|
||||
|
||||
"""
|
||||
LOG.debug("Building PXE config for node %s", task.node.uuid)
|
||||
|
||||
if template is None:
|
||||
template = CONF.pxe.pxe_config_template
|
||||
|
||||
_ensure_config_dirs_exist(task.node.uuid)
|
||||
|
||||
pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)
|
||||
pxe_config = _build_pxe_config(pxe_options, template)
|
||||
utils.write_to_file(pxe_config_file_path, pxe_config)
|
||||
_link_mac_pxe_configs(task)
|
||||
|
||||
|
||||
def clean_up_pxe_config(task):
|
||||
"""Clean up the TFTP environment for the task's node.
|
||||
|
||||
:param task: A TaskManager instance.
|
||||
|
||||
"""
|
||||
LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)
|
||||
|
||||
for mac in driver_utils.get_node_mac_addresses(task):
|
||||
utils.unlink_without_raise(_get_pxe_mac_path(mac))
|
||||
|
||||
utils.rmtree_without_raise(os.path.join(CONF.pxe.tftp_root,
|
||||
task.node.uuid))
|
||||
|
||||
|
||||
def dhcp_options_for_instance():
|
||||
"""Retrieves the DHCP PXE boot options."""
|
||||
return [{'opt_name': 'bootfile-name',
|
||||
'opt_value': CONF.pxe.pxe_bootfile_name},
|
||||
{'opt_name': 'server-ip-address',
|
||||
'opt_value': CONF.pxe.tftp_server},
|
||||
{'opt_name': 'tftp-server',
|
||||
'opt_value': CONF.pxe.tftp_server}
|
||||
]
|
|
@ -1,140 +0,0 @@
|
|||
#
|
||||
# Copyright 2014 Rackspace, Inc
|
||||
# All Rights Reserved
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import os
|
||||
|
||||
import jinja2
|
||||
from oslo.config import cfg
|
||||
|
||||
from ironic.common import utils
|
||||
from ironic.drivers import utils as driver_utils
|
||||
from ironic.openstack.common import fileutils
|
||||
from ironic.openstack.common import log as logging
|
||||
|
||||
|
||||
tftp_opts = [
|
||||
cfg.StrOpt('tftp_server',
|
||||
default='$my_ip',
|
||||
help='IP address of Ironic compute node\'s tftp server.',
|
||||
deprecated_group='pxe'),
|
||||
cfg.StrOpt('tftp_root',
|
||||
default='/tftpboot',
|
||||
help='Ironic compute node\'s tftp root path.',
|
||||
deprecated_group='pxe')
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.register_opts(tftp_opts, group='tftp')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_deploy_kr_info(node_uuid, driver_info):
|
||||
"""Get uuid and tftp path for deploy kernel and ramdisk.
|
||||
|
||||
Note: driver_info should be validated outside of this method.
|
||||
"""
|
||||
image_info = {}
|
||||
for label in ('deploy_kernel', 'deploy_ramdisk'):
|
||||
# the values for these keys will look like "glance://image-uuid"
|
||||
image_info[label] = (
|
||||
str(driver_info[label]).split('/')[-1],
|
||||
os.path.join(CONF.tftp.tftp_root, node_uuid, label)
|
||||
)
|
||||
return image_info
|
||||
|
||||
|
||||
def create_pxe_config(task, pxe_options, pxe_config_template):
|
||||
"""Generate PXE configuration file and MAC symlinks for it."""
|
||||
node = task.node
|
||||
fileutils.ensure_tree(os.path.join(CONF.tftp.tftp_root,
|
||||
node.uuid))
|
||||
fileutils.ensure_tree(os.path.join(CONF.tftp.tftp_root,
|
||||
'pxelinux.cfg'))
|
||||
|
||||
pxe_config_file_path = get_pxe_config_file_path(node.uuid)
|
||||
pxe_config = build_pxe_config(node, pxe_options, pxe_config_template)
|
||||
utils.write_to_file(pxe_config_file_path, pxe_config)
|
||||
_write_mac_pxe_configs(task)
|
||||
|
||||
|
||||
def clean_up_pxe_config(task):
|
||||
"""Clean up the TFTP environment for the task's node."""
|
||||
node = task.node
|
||||
|
||||
utils.unlink_without_raise(get_pxe_config_file_path(node.uuid))
|
||||
for port in driver_utils.get_node_mac_addresses(task):
|
||||
utils.unlink_without_raise(get_pxe_mac_path(port))
|
||||
|
||||
utils.rmtree_without_raise(os.path.join(CONF.tftp.tftp_root, node.uuid))
|
||||
|
||||
|
||||
def _write_mac_pxe_configs(task):
|
||||
"""Create a file in the PXE config directory for each MAC so regardless
|
||||
of which port boots first, they'll get the same PXE config.
|
||||
"""
|
||||
pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)
|
||||
for port in driver_utils.get_node_mac_addresses(task):
|
||||
mac_path = get_pxe_mac_path(port)
|
||||
utils.unlink_without_raise(mac_path)
|
||||
utils.create_link_without_raise(pxe_config_file_path, mac_path)
|
||||
|
||||
|
||||
def build_pxe_config(node, pxe_options, pxe_config_template):
|
||||
"""Build the PXE config file for a node
|
||||
|
||||
This method builds the PXE boot configuration file for a node,
|
||||
given all the required parameters.
|
||||
|
||||
:param pxe_options: A dict of values to set on the configuration file
|
||||
:returns: A formatted string with the file content.
|
||||
"""
|
||||
LOG.debug("Building PXE config for deployment %s."), node['id']
|
||||
|
||||
tmpl_path, tmpl_file = os.path.split(pxe_config_template)
|
||||
env = jinja2.Environment(loader=jinja2.FileSystemLoader(tmpl_path))
|
||||
template = env.get_template(tmpl_file)
|
||||
return template.render({'pxe_options': pxe_options,
|
||||
'ROOT': '{{ ROOT }}'})
|
||||
|
||||
|
||||
def get_pxe_mac_path(mac):
|
||||
"""Convert a MAC address into a PXE config file name.
|
||||
|
||||
:param mac: A mac address string in the format xx:xx:xx:xx:xx:xx.
|
||||
:returns: the path to the config file.
|
||||
"""
|
||||
return os.path.join(
|
||||
CONF.tftp.tftp_root,
|
||||
'pxelinux.cfg',
|
||||
"01-" + mac.replace(":", "-").lower()
|
||||
)
|
||||
|
||||
|
||||
def get_pxe_config_file_path(node_uuid):
|
||||
"""Generate the path for an instances PXE config file."""
|
||||
return os.path.join(CONF.tftp.tftp_root, node_uuid, 'config')
|
||||
|
||||
|
||||
def dhcp_options_for_instance(pxe_bootfile_name):
|
||||
"""Retrives the DHCP PXE boot options."""
|
||||
return [{'opt_name': 'bootfile-name',
|
||||
'opt_value': pxe_bootfile_name},
|
||||
{'opt_name': 'server-ip-address',
|
||||
'opt_value': CONF.tftp.tftp_server},
|
||||
{'opt_name': 'tftp-server',
|
||||
'opt_value': CONF.tftp.tftp_server}
|
||||
]
|
|
@ -27,8 +27,8 @@ from ironic.common import images
|
|||
from ironic.common import keystone
|
||||
from ironic.common import neutron
|
||||
from ironic.common import paths
|
||||
from ironic.common import pxe_utils
|
||||
from ironic.common import states
|
||||
from ironic.common import tftp
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
|
@ -56,6 +56,12 @@ pxe_opts = [
|
|||
cfg.StrOpt('images_path',
|
||||
default='/var/lib/ironic/images/',
|
||||
help='Directory where images are stored on disk.'),
|
||||
cfg.StrOpt('tftp_server',
|
||||
default='$my_ip',
|
||||
help='IP address of Ironic compute node\'s tftp server.'),
|
||||
cfg.StrOpt('tftp_root',
|
||||
default='/tftpboot',
|
||||
help='Ironic compute node\'s tftp root path.'),
|
||||
cfg.StrOpt('tftp_master_path',
|
||||
default='/tftpboot/master_images',
|
||||
help='Directory where master tftp images are stored on disk.'),
|
||||
|
@ -187,8 +193,8 @@ def _build_pxe_config_options(node, pxe_info, ctx):
|
|||
This method builds the PXE boot options for a node,
|
||||
given all the required parameters.
|
||||
|
||||
The options should then be passed to tftp.create_pxe_config to create
|
||||
the actual config files.
|
||||
The options should then be passed to pxe_utils.create_pxe_config to
|
||||
create the actual config files.
|
||||
|
||||
:param node: a single Node.
|
||||
:param pxe_info: a dict of values to set on the configuration file
|
||||
|
@ -233,7 +239,7 @@ def _get_image_file_path(node_uuid):
|
|||
|
||||
def _get_token_file_path(node_uuid):
|
||||
"""Generate the path for PKI token file."""
|
||||
return os.path.join(CONF.tftp.tftp_root, 'token-' + node_uuid)
|
||||
return os.path.join(CONF.pxe.tftp_root, 'token-' + node_uuid)
|
||||
|
||||
|
||||
class PXEImageCache(image_cache.ImageCache):
|
||||
|
@ -315,7 +321,7 @@ def _fetch_images(ctx, cache, images_info):
|
|||
def _cache_tftp_images(ctx, node, pxe_info):
|
||||
"""Fetch the necessary kernels and ramdisks for the instance."""
|
||||
fileutils.ensure_tree(
|
||||
os.path.join(CONF.tftp.tftp_root, node.uuid))
|
||||
os.path.join(CONF.pxe.tftp_root, node.uuid))
|
||||
LOG.debug("Fetching kernel and ramdisk for node %s",
|
||||
node.uuid)
|
||||
_fetch_images(ctx, TFTPImageCache(), pxe_info.values())
|
||||
|
@ -329,7 +335,7 @@ def _cache_instance_image(ctx, node):
|
|||
to the appropriate places on local disk.
|
||||
|
||||
Both sets of kernel and ramdisk are needed for PXE booting, so these
|
||||
are stored under CONF.tftp.tftp_root.
|
||||
are stored under CONF.pxe.tftp_root.
|
||||
|
||||
At present, the AMI is cached and certain files are injected.
|
||||
Debian/ubuntu-specific assumptions are made regarding the injected
|
||||
|
@ -363,7 +369,7 @@ def _get_tftp_image_info(node, ctx):
|
|||
d_info = _parse_deploy_info(node)
|
||||
image_info = {}
|
||||
|
||||
image_info.update(tftp.get_deploy_kr_info(node.uuid, d_info))
|
||||
image_info.update(pxe_utils.get_deploy_kr_info(node.uuid, d_info))
|
||||
|
||||
i_info = node.instance_info
|
||||
labels = ('kernel', 'ramdisk')
|
||||
|
@ -378,7 +384,7 @@ def _get_tftp_image_info(node, ctx):
|
|||
for label in labels:
|
||||
image_info[label] = (
|
||||
i_info[label],
|
||||
os.path.join(CONF.tftp.tftp_root, node.uuid, label)
|
||||
os.path.join(CONF.pxe.tftp_root, node.uuid, label)
|
||||
)
|
||||
|
||||
return image_info
|
||||
|
@ -503,7 +509,8 @@ class PXEDeploy(base.DeployInterface):
|
|||
# TODO(yuriyz): more secure way needed for pass auth token
|
||||
# to deploy ramdisk
|
||||
_create_token_file(task)
|
||||
neutron.update_neutron(task, CONF.pxe.pxe_bootfile_name)
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance()
|
||||
neutron.update_neutron(task, dhcp_opts)
|
||||
manager_utils.node_set_boot_device(task, 'pxe', persistent=True)
|
||||
manager_utils.node_power_action(task, states.REBOOT)
|
||||
|
||||
|
@ -535,7 +542,8 @@ class PXEDeploy(base.DeployInterface):
|
|||
pxe_info = _get_tftp_image_info(task.node, task.context)
|
||||
pxe_options = _build_pxe_config_options(task.node, pxe_info,
|
||||
task.context)
|
||||
tftp.create_pxe_config(task, pxe_options, CONF.pxe.pxe_config_template)
|
||||
pxe_utils.create_pxe_config(task, pxe_options,
|
||||
CONF.pxe.pxe_config_template)
|
||||
_cache_tftp_images(task.context, task.node, pxe_info)
|
||||
|
||||
def clean_up(self, task):
|
||||
|
@ -555,13 +563,14 @@ class PXEDeploy(base.DeployInterface):
|
|||
utils.unlink_without_raise(path)
|
||||
TFTPImageCache().clean_up()
|
||||
|
||||
tftp.clean_up_pxe_config(task)
|
||||
pxe_utils.clean_up_pxe_config(task)
|
||||
|
||||
_destroy_images(d_info, node.uuid)
|
||||
_destroy_token_file(node)
|
||||
|
||||
def take_over(self, task):
|
||||
neutron.update_neutron(task, CONF.pxe.pxe_bootfile_name)
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance()
|
||||
neutron.update_neutron(task, dhcp_opts)
|
||||
|
||||
|
||||
class VendorPassthru(base.VendorInterface):
|
||||
|
@ -581,7 +590,7 @@ class VendorPassthru(base.VendorInterface):
|
|||
'lun': kwargs.get('lun', '1'),
|
||||
'image_path': _get_image_file_path(node.uuid),
|
||||
'pxe_config_path':
|
||||
tftp.get_pxe_config_file_path(node.uuid),
|
||||
pxe_utils.get_pxe_config_file_path(node.uuid),
|
||||
'root_mb': 1024 * int(d_info['root_gb']),
|
||||
'swap_mb': int(d_info['swap_mb']),
|
||||
'ephemeral_mb': 1024 * int(d_info['ephemeral_gb']),
|
||||
|
|
|
@ -29,8 +29,8 @@ from ironic.common.glance_service import base_image_service
|
|||
from ironic.common import image_service
|
||||
from ironic.common import keystone
|
||||
from ironic.common import neutron
|
||||
from ironic.common import pxe_utils
|
||||
from ironic.common import states
|
||||
from ironic.common import tftp
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
|
@ -223,22 +223,22 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
|||
|
||||
expected_info = {'ramdisk':
|
||||
('instance_ramdisk_uuid',
|
||||
os.path.join(CONF.tftp.tftp_root,
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'ramdisk')),
|
||||
'kernel':
|
||||
('instance_kernel_uuid',
|
||||
os.path.join(CONF.tftp.tftp_root,
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'kernel')),
|
||||
'deploy_ramdisk':
|
||||
('deploy_ramdisk_uuid',
|
||||
os.path.join(CONF.tftp.tftp_root,
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'deploy_ramdisk')),
|
||||
'deploy_kernel':
|
||||
('deploy_kernel_uuid',
|
||||
os.path.join(CONF.tftp.tftp_root,
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'deploy_kernel'))}
|
||||
show_mock.return_value = properties
|
||||
|
@ -258,7 +258,7 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
|||
self.node.instance_info.get('ramdisk'))
|
||||
|
||||
@mock.patch.object(utils, 'random_alnum')
|
||||
@mock.patch.object(tftp, 'build_pxe_config')
|
||||
@mock.patch.object(pxe_utils, '_build_pxe_config')
|
||||
def test_build_pxe_config_options(self, build_pxe_mock, random_alnum_mock):
|
||||
self.config(pxe_append_params='test_param', group='pxe')
|
||||
# NOTE: right '/' should be removed from url string
|
||||
|
@ -286,19 +286,19 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
|||
u'c02d7f33c123/deploy_kernel'
|
||||
}
|
||||
image_info = {'deploy_kernel': ('deploy_kernel',
|
||||
os.path.join(CONF.tftp.tftp_root,
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'deploy_kernel')),
|
||||
'deploy_ramdisk': ('deploy_ramdisk',
|
||||
os.path.join(CONF.tftp.tftp_root,
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'deploy_ramdisk')),
|
||||
'kernel': ('kernel_id',
|
||||
os.path.join(CONF.tftp.tftp_root,
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'kernel')),
|
||||
'ramdisk': ('ramdisk_id',
|
||||
os.path.join(CONF.tftp.tftp_root,
|
||||
os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'ramdisk'))
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ class PXEPrivateMethodsTestCase(db_base.DbTestCase):
|
|||
@mock.patch.object(pxe, '_fetch_images')
|
||||
def test__cache_tftp_images_master_path(self, mock_fetch_image):
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
self.config(tftp_root=temp_dir, group='tftp')
|
||||
self.config(tftp_root=temp_dir, group='pxe')
|
||||
self.config(tftp_master_path=os.path.join(temp_dir,
|
||||
'tftp_master_path'),
|
||||
group='pxe')
|
||||
|
@ -499,7 +499,7 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
|||
self.context = context.get_admin_context()
|
||||
self.context.auth_token = '4562138218392831'
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.config(tftp_root=self.temp_dir, group='tftp')
|
||||
self.config(tftp_root=self.temp_dir, group='pxe')
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
self.config(images_path=self.temp_dir, group='pxe')
|
||||
mgr_utils.mock_the_extension_manager(driver="fake_pxe")
|
||||
|
@ -646,7 +646,7 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
|||
@mock.patch.object(pxe, '_get_tftp_image_info')
|
||||
@mock.patch.object(pxe, '_cache_tftp_images')
|
||||
@mock.patch.object(pxe, '_build_pxe_config_options')
|
||||
@mock.patch.object(tftp, 'create_pxe_config')
|
||||
@mock.patch.object(pxe_utils, 'create_pxe_config')
|
||||
def test_prepare(self, mock_pxe_config,
|
||||
mock_build_pxe, mock_cache_tftp_images,
|
||||
mock_tftp_img_info):
|
||||
|
@ -675,6 +675,7 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
|||
fake_img_path = '/test/path/test.img'
|
||||
mock_get_image_file_path.return_value = fake_img_path
|
||||
mock_get_image_mb.return_value = 1
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance()
|
||||
|
||||
with task_manager.acquire(self.context,
|
||||
self.node.uuid, shared=False) as task:
|
||||
|
@ -685,7 +686,7 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
|||
mock_get_image_file_path.assert_called_once_with(task.node.uuid)
|
||||
mock_get_image_mb.assert_called_once_with(fake_img_path)
|
||||
mock_update_neutron.assert_called_once_with(
|
||||
task, CONF.pxe.pxe_bootfile_name)
|
||||
task, dhcp_opts)
|
||||
mock_node_set_boot.assert_called_once_with(task, 'pxe',
|
||||
persistent=True)
|
||||
mock_node_power_action.assert_called_once_with(task, states.REBOOT)
|
||||
|
@ -724,11 +725,12 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
|||
|
||||
@mock.patch.object(neutron, 'update_neutron')
|
||||
def test_take_over(self, update_neutron_mock):
|
||||
dhcp_opts = pxe_utils.dhcp_options_for_instance()
|
||||
with task_manager.acquire(
|
||||
self.context, self.node.uuid, shared=True) as task:
|
||||
task.driver.deploy.take_over(task)
|
||||
update_neutron_mock.assert_called_once_with(
|
||||
task, CONF.pxe.pxe_bootfile_name)
|
||||
task, dhcp_opts)
|
||||
|
||||
@mock.patch.object(pxe, 'InstanceImageCache')
|
||||
def test_continue_deploy_good(self, mock_image_cache):
|
||||
|
@ -837,8 +839,8 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
|||
@mock.patch.object(pxe, '_get_tftp_image_info')
|
||||
def clean_up_config(self, get_tftp_image_info_mock, master=None):
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
self.config(tftp_root=temp_dir, group='tftp')
|
||||
tftp_master_dir = os.path.join(CONF.tftp.tftp_root,
|
||||
self.config(tftp_root=temp_dir, group='pxe')
|
||||
tftp_master_dir = os.path.join(CONF.pxe.tftp_root,
|
||||
'tftp_master')
|
||||
self.config(tftp_master_path=tftp_master_dir, group='pxe')
|
||||
os.makedirs(tftp_master_dir)
|
||||
|
@ -858,16 +860,16 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
|||
uuid='bb43dc0b-03f2-4d2e-ae87-c02d7f33cc53',
|
||||
node_id='123')))
|
||||
|
||||
d_kernel_path = os.path.join(CONF.tftp.tftp_root,
|
||||
d_kernel_path = os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid, 'deploy_kernel')
|
||||
image_info = {'deploy_kernel': ('deploy_kernel_uuid', d_kernel_path)}
|
||||
|
||||
get_tftp_image_info_mock.return_value = image_info
|
||||
|
||||
pxecfg_dir = os.path.join(CONF.tftp.tftp_root, 'pxelinux.cfg')
|
||||
pxecfg_dir = os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg')
|
||||
os.makedirs(pxecfg_dir)
|
||||
|
||||
instance_dir = os.path.join(CONF.tftp.tftp_root,
|
||||
instance_dir = os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid)
|
||||
image_dir = os.path.join(CONF.pxe.images_path, self.node.uuid)
|
||||
os.makedirs(instance_dir)
|
||||
|
@ -889,7 +891,7 @@ class PXEDriverTestCase(db_base.DbTestCase):
|
|||
os.link(master_deploy_kernel_path, deploy_kernel_path)
|
||||
os.link(master_instance_path, image_path)
|
||||
if master == 'in_use':
|
||||
deploy_kernel_link = os.path.join(CONF.tftp.tftp_root,
|
||||
deploy_kernel_link = os.path.join(CONF.pxe.tftp_root,
|
||||
'deploy_kernel_link')
|
||||
image_link = os.path.join(CONF.pxe.images_path,
|
||||
'image_link')
|
||||
|
|
|
@ -21,7 +21,7 @@ from oslo.config import cfg
|
|||
|
||||
from ironic.common import exception
|
||||
from ironic.common import neutron
|
||||
from ironic.common import tftp
|
||||
from ironic.common import pxe_utils
|
||||
from ironic.common import utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.db import api as dbapi
|
||||
|
@ -224,7 +224,7 @@ class TestNeutron(base.TestCase):
|
|||
@mock.patch('ironic.common.neutron.NeutronAPI.update_port_dhcp_opts')
|
||||
@mock.patch('ironic.common.neutron.get_node_vif_ids')
|
||||
def test_update_neutron(self, mock_gnvi, mock_updo, mock_wait_neutron):
|
||||
opts = tftp.dhcp_options_for_instance(CONF.pxe.pxe_bootfile_name)
|
||||
opts = pxe_utils.dhcp_options_for_instance()
|
||||
mock_gnvi.return_value = {'port-uuid': 'vif-uuid'}
|
||||
with task_manager.acquire(self.context,
|
||||
self.node.uuid) as task:
|
||||
|
|
|
@ -19,20 +19,22 @@ import os
|
|||
import mock
|
||||
from oslo.config import cfg
|
||||
|
||||
from ironic.common import tftp
|
||||
from ironic.common import pxe_utils
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.db import api as dbapi
|
||||
from ironic.openstack.common import context
|
||||
from ironic.tests.conductor import utils as mgr_utils
|
||||
from ironic.tests.db import base as db_base
|
||||
from ironic.tests.db import utils as dbutils
|
||||
from ironic.tests.objects import utils as object_utils
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestNetworkUtils(db_base.DbTestCase):
|
||||
class TestPXEUtils(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestNetworkUtils, self).setUp()
|
||||
super(TestPXEUtils, self).setUp()
|
||||
mgr_utils.mock_the_extension_manager(driver="fake")
|
||||
self.dbapi = dbapi.get_instance()
|
||||
self.context = context.get_admin_context()
|
||||
|
@ -54,10 +56,10 @@ class TestNetworkUtils(db_base.DbTestCase):
|
|||
}
|
||||
self.node = object_utils.create_test_node(self.context)
|
||||
|
||||
def test_build_pxe_config(self):
|
||||
def test__build_pxe_config(self):
|
||||
|
||||
rendered_template = tftp.build_pxe_config(
|
||||
self.node, self.pxe_options, CONF.pxe.pxe_config_template)
|
||||
rendered_template = pxe_utils._build_pxe_config(
|
||||
self.pxe_options, CONF.pxe.pxe_config_template)
|
||||
|
||||
expected_template = open(
|
||||
'ironic/tests/drivers/pxe_config.template').read()
|
||||
|
@ -85,67 +87,72 @@ class TestNetworkUtils(db_base.DbTestCase):
|
|||
mock.call('/tftpboot/pxelinux.cfg/01-00-11-22-33-44-55-67')
|
||||
]
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
tftp._write_mac_pxe_configs(task)
|
||||
pxe_utils._link_mac_pxe_configs(task)
|
||||
|
||||
unlink_mock.assert_has_calls(unlink_calls)
|
||||
create_link_mock.assert_has_calls(create_link_calls)
|
||||
|
||||
@mock.patch('ironic.common.utils.write_to_file')
|
||||
@mock.patch('ironic.common.tftp.build_pxe_config')
|
||||
@mock.patch.object(pxe_utils, '_build_pxe_config')
|
||||
@mock.patch('ironic.openstack.common.fileutils.ensure_tree')
|
||||
def test_create_pxe_config(self, ensure_tree_mock, build_mock,
|
||||
write_mock):
|
||||
build_mock.return_value = self.pxe_options
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
tftp.create_pxe_config(task, self.pxe_options,
|
||||
CONF.pxe.pxe_config_template)
|
||||
build_mock.assert_called_with(task.node, self.pxe_options,
|
||||
CONF.pxe.pxe_config_template)
|
||||
pxe_utils.create_pxe_config(task, self.pxe_options,
|
||||
CONF.pxe.pxe_config_template)
|
||||
build_mock.assert_called_with(self.pxe_options,
|
||||
CONF.pxe.pxe_config_template)
|
||||
ensure_calls = [
|
||||
mock.call(os.path.join(CONF.tftp.tftp_root, self.node.uuid)),
|
||||
mock.call(os.path.join(CONF.tftp.tftp_root, 'pxelinux.cfg'))
|
||||
mock.call(os.path.join(CONF.pxe.tftp_root, self.node.uuid)),
|
||||
mock.call(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg'))
|
||||
]
|
||||
ensure_tree_mock.has_calls(ensure_calls)
|
||||
|
||||
pxe_config_file_path = tftp.get_pxe_config_file_path(self.node.uuid)
|
||||
write_mock.assert_called_with(pxe_config_file_path, self.pxe_options)
|
||||
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
|
||||
write_mock.assert_called_with(pxe_cfg_file_path, self.pxe_options)
|
||||
|
||||
@mock.patch('ironic.common.utils.rmtree_without_raise', autospec=True)
|
||||
@mock.patch('ironic.common.utils.unlink_without_raise', autospec=True)
|
||||
def test_clean_up_pxe_config(self, unlink_mock, rmtree_mock):
|
||||
address = "aa:aa:aa:aa:aa:aa"
|
||||
pdict = dbutils.get_test_port(node_uuid=self.node.uuid,
|
||||
address=address)
|
||||
self.dbapi.create_port(pdict)
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
tftp.clean_up_pxe_config(task)
|
||||
pxe_utils.clean_up_pxe_config(task)
|
||||
|
||||
unlink_mock.assert_called_once_with(
|
||||
tftp.get_pxe_config_file_path(self.node.uuid))
|
||||
unlink_mock.assert_called_once_with("/tftpboot/pxelinux.cfg/01-%s"
|
||||
% address.replace(':', '-'))
|
||||
rmtree_mock.assert_called_once_with(
|
||||
os.path.join(CONF.tftp.tftp_root, self.node.uuid))
|
||||
os.path.join(CONF.pxe.tftp_root, self.node.uuid))
|
||||
|
||||
def test_get_pxe_mac_path(self):
|
||||
def test__get_pxe_mac_path(self):
|
||||
mac = '00:11:22:33:44:55:66'
|
||||
self.assertEqual('/tftpboot/pxelinux.cfg/01-00-11-22-33-44-55-66',
|
||||
tftp.get_pxe_mac_path(mac))
|
||||
pxe_utils._get_pxe_mac_path(mac))
|
||||
|
||||
def test_get_pxe_config_file_path(self):
|
||||
self.assertEqual(os.path.join(CONF.tftp.tftp_root,
|
||||
self.assertEqual(os.path.join(CONF.pxe.tftp_root,
|
||||
self.node.uuid,
|
||||
'config'),
|
||||
tftp.get_pxe_config_file_path(self.node.uuid))
|
||||
pxe_utils.get_pxe_config_file_path(self.node.uuid))
|
||||
|
||||
def test_dhcp_options_for_instance(self):
|
||||
self.config(tftp_server='192.0.2.1', group='tftp')
|
||||
self.config(tftp_server='192.0.2.1', group='pxe')
|
||||
self.config(pxe_bootfile_name='fake-bootfile', group='pxe')
|
||||
expected_info = [{'opt_name': 'bootfile-name',
|
||||
'opt_value': CONF.pxe.pxe_bootfile_name},
|
||||
'opt_value': 'fake-bootfile'},
|
||||
{'opt_name': 'server-ip-address',
|
||||
'opt_value': '192.0.2.1'},
|
||||
{'opt_name': 'tftp-server',
|
||||
'opt_value': '192.0.2.1'}
|
||||
]
|
||||
self.assertEqual(expected_info, tftp.dhcp_options_for_instance(
|
||||
CONF.pxe.pxe_bootfile_name))
|
||||
self.assertEqual(expected_info, pxe_utils.dhcp_options_for_instance())
|
||||
|
||||
def test_get_deploy_kr_info(self):
|
||||
self.config(tftp_root='/tftp', group='tftp')
|
||||
self.config(tftp_root='/tftp', group='pxe')
|
||||
node_uuid = 'fake-node'
|
||||
driver_info = {
|
||||
'deploy_kernel': 'glance://deploy-kernel',
|
||||
|
@ -159,14 +166,14 @@ class TestNetworkUtils(db_base.DbTestCase):
|
|||
'/tftp/fake-node/deploy_ramdisk'),
|
||||
}
|
||||
|
||||
kr_info = tftp.get_deploy_kr_info(node_uuid, driver_info)
|
||||
kr_info = pxe_utils.get_deploy_kr_info(node_uuid, driver_info)
|
||||
self.assertEqual(expected, kr_info)
|
||||
|
||||
def test_get_deploy_kr_info_bad_driver_info(self):
|
||||
self.config(tftp_root='/tftp', group='tftp')
|
||||
self.config(tftp_root='/tftp', group='pxe')
|
||||
node_uuid = 'fake-node'
|
||||
driver_info = {}
|
||||
self.assertRaises(KeyError,
|
||||
tftp.get_deploy_kr_info,
|
||||
pxe_utils.get_deploy_kr_info,
|
||||
node_uuid,
|
||||
driver_info)
|
Loading…
Reference in New Issue