diff options
Diffstat (limited to 'ironic/common')
-rw-r--r-- | ironic/common/neutron.py | 4 | ||||
-rw-r--r-- | ironic/common/pxe_utils.py | 167 | ||||
-rw-r--r-- | ironic/common/tftp.py | 140 |
3 files changed, 168 insertions, 143 deletions
diff --git a/ironic/common/neutron.py b/ironic/common/neutron.py index 897f60610..034a177bd 100644 --- a/ironic/common/neutron.py +++ b/ironic/common/neutron.py @@ -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 " diff --git a/ironic/common/pxe_utils.py b/ironic/common/pxe_utils.py new file mode 100644 index 000000000..19685b8f0 --- /dev/null +++ b/ironic/common/pxe_utils.py @@ -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} + ] diff --git a/ironic/common/tftp.py b/ironic/common/tftp.py deleted file mode 100644 index 88399e0da..000000000 --- a/ironic/common/tftp.py +++ /dev/null @@ -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} - ] |