Merge "Fix directories permission for tftpboot"

This commit is contained in:
Jenkins 2017-05-20 12:21:08 +00:00 committed by Gerrit Code Review
commit fda94477d4
5 changed files with 118 additions and 11 deletions

View File

@ -3309,6 +3309,17 @@
# caching. (string value)
#tftp_master_path = /tftpboot/master_images
# The permission that will be applied to the TFTP folders upon
# creation. This should be set to the permission such that the
# tftpserver has access to read the contents of the configured
# TFTP folder. This setting is only required when the
# operating system's umask is restrictive such that ironic-
# conductor is creating files that cannot be read by the TFTP
# server. Setting to <None> will result in the operating
# system's umask to be utilized for the creation of new tftp
# folders. (integer value)
#dir_permission = <None>
# Bootfile DHCP parameter. (string value)
#pxe_bootfile_name = pxelinux.0

View File

@ -49,8 +49,17 @@ def _ensure_config_dirs_exist(node_uuid):
"""
root_dir = get_root_dir()
fileutils.ensure_tree(os.path.join(root_dir, node_uuid))
fileutils.ensure_tree(os.path.join(root_dir, PXE_CFG_DIR_NAME))
node_dir = os.path.join(root_dir, node_uuid)
pxe_dir = os.path.join(root_dir, PXE_CFG_DIR_NAME)
# NOTE: We should only change the permissions if the folder
# does not exist. i.e. if defined, an operator could have
# already created it and placed specific ACLs upon the folder
# which may not recurse downward.
for directory in (node_dir, pxe_dir):
if not os.path.isdir(directory):
fileutils.ensure_tree(directory)
if CONF.pxe.dir_permission:
os.chmod(directory, CONF.pxe.dir_permission)
def _link_mac_pxe_configs(task):

View File

@ -77,6 +77,17 @@ opts = [
help=_('On ironic-conductor node, directory where master TFTP '
'images are stored on disk. '
'Setting to <None> disables image caching.')),
cfg.IntOpt('dir_permission',
help=_("The permission that will be applied to the TFTP "
"folders upon creation. This should be set to the "
"permission such that the tftpserver has access to "
"read the contents of the configured TFTP folder. This "
"setting is only required when the operating system's "
"umask is restrictive such that ironic-conductor is "
"creating files that cannot be read by the TFTP server. "
"Setting to <None> will result in the operating "
"system's umask to be utilized for the creation of new "
"tftp folders.")),
cfg.StrOpt('pxe_bootfile_name',
default='pxelinux.0',
help=_('Bootfile DHCP parameter.')),

View File

@ -277,11 +277,12 @@ class TestPXEUtils(db_base.DbTestCase):
unlink_mock.assert_called_once_with('/tftpboot/10.10.0.1.conf')
create_link_mock.assert_has_calls(create_link_calls)
@mock.patch.object(os, 'chmod', autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config(self, ensure_tree_mock, render_mock,
write_mock):
write_mock, chmod_mock):
with task_manager.acquire(self.context, self.node.uuid) as task:
pxe_utils.create_pxe_config(task, self.pxe_options,
CONF.pxe.pxe_config_template)
@ -291,23 +292,84 @@ class TestPXEUtils(db_base.DbTestCase):
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'}
)
node_dir = os.path.join(CONF.pxe.tftp_root, self.node.uuid)
pxe_dir = os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg')
ensure_calls = [
mock.call(os.path.join(CONF.pxe.tftp_root, self.node.uuid)),
mock.call(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg'))
mock.call(node_dir), mock.call(pxe_dir),
]
ensure_tree_mock.assert_has_calls(ensure_calls)
chmod_mock.assert_not_called()
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
write_mock.assert_called_with(pxe_cfg_file_path,
render_mock.return_value)
@mock.patch.object(os, 'chmod', autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config_set_dir_permission(self, ensure_tree_mock,
render_mock,
write_mock, chmod_mock):
self.config(dir_permission=0o755, group='pxe')
with task_manager.acquire(self.context, self.node.uuid) as task:
pxe_utils.create_pxe_config(task, self.pxe_options,
CONF.pxe.pxe_config_template)
render_mock.assert_called_with(
CONF.pxe.pxe_config_template,
{'pxe_options': self.pxe_options,
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'}
)
node_dir = os.path.join(CONF.pxe.tftp_root, self.node.uuid)
pxe_dir = os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg')
ensure_calls = [
mock.call(node_dir), mock.call(pxe_dir),
]
ensure_tree_mock.assert_has_calls(ensure_calls)
chmod_calls = [mock.call(node_dir, 0o755), mock.call(pxe_dir, 0o755)]
chmod_mock.assert_has_calls(chmod_calls)
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
write_mock.assert_called_with(pxe_cfg_file_path,
render_mock.return_value)
@mock.patch.object(os.path, 'isdir', autospec=True)
@mock.patch.object(os, 'chmod', autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config_existing_dirs(self, ensure_tree_mock,
render_mock,
write_mock, chmod_mock,
isdir_mock):
self.config(dir_permission=0o755, group='pxe')
with task_manager.acquire(self.context, self.node.uuid) as task:
isdir_mock.return_value = True
pxe_utils.create_pxe_config(task, self.pxe_options,
CONF.pxe.pxe_config_template)
render_mock.assert_called_with(
CONF.pxe.pxe_config_template,
{'pxe_options': self.pxe_options,
'ROOT': '{{ ROOT }}',
'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'}
)
ensure_tree_mock.assert_has_calls([])
chmod_mock.assert_not_called()
isdir_mock.assert_has_calls([])
pxe_cfg_file_path = pxe_utils.get_pxe_config_file_path(self.node.uuid)
write_mock.assert_called_with(pxe_cfg_file_path,
render_mock.return_value)
@mock.patch.object(os, 'chmod', autospec=True)
@mock.patch('ironic.common.pxe_utils._link_ip_address_pxe_configs',
autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config_uefi_elilo(self, ensure_tree_mock, render_mock,
write_mock, link_ip_configs_mock):
write_mock, link_ip_configs_mock,
chmod_mock):
self.config(
uefi_pxe_config_template=('ironic/drivers/modules/'
'elilo_efi_pxe_config.template'),
@ -320,9 +382,10 @@ class TestPXEUtils(db_base.DbTestCase):
ensure_calls = [
mock.call(os.path.join(CONF.pxe.tftp_root, self.node.uuid)),
mock.call(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg'))
mock.call(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg')),
]
ensure_tree_mock.assert_has_calls(ensure_calls)
chmod_mock.assert_not_called()
render_mock.assert_called_with(
CONF.pxe.uefi_pxe_config_template,
{'pxe_options': self.pxe_options,
@ -334,13 +397,15 @@ class TestPXEUtils(db_base.DbTestCase):
write_mock.assert_called_with(pxe_cfg_file_path,
render_mock.return_value)
@mock.patch.object(os, 'chmod', autospec=True)
@mock.patch('ironic.common.pxe_utils._link_ip_address_pxe_configs',
autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config_uefi_grub(self, ensure_tree_mock, render_mock,
write_mock, link_ip_configs_mock):
write_mock, link_ip_configs_mock,
chmod_mock):
grub_tmplte = "ironic/drivers/modules/pxe_grub_config.template"
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.properties['capabilities'] = 'boot_mode:uefi'
@ -349,9 +414,10 @@ class TestPXEUtils(db_base.DbTestCase):
ensure_calls = [
mock.call(os.path.join(CONF.pxe.tftp_root, self.node.uuid)),
mock.call(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg'))
mock.call(os.path.join(CONF.pxe.tftp_root, 'pxelinux.cfg')),
]
ensure_tree_mock.assert_has_calls(ensure_calls)
chmod_mock.assert_not_called()
render_mock.assert_called_with(
grub_tmplte,
{'pxe_options': self.pxe_options,
@ -363,13 +429,15 @@ class TestPXEUtils(db_base.DbTestCase):
write_mock.assert_called_with(pxe_cfg_file_path,
render_mock.return_value)
@mock.patch.object(os, 'chmod', autospec=True)
@mock.patch('ironic.common.pxe_utils._link_mac_pxe_configs',
autospec=True)
@mock.patch('ironic.common.utils.write_to_file', autospec=True)
@mock.patch('ironic.common.utils.render_template', autospec=True)
@mock.patch('oslo_utils.fileutils.ensure_tree', autospec=True)
def test_create_pxe_config_uefi_ipxe(self, ensure_tree_mock, render_mock,
write_mock, link_mac_pxe_mock):
write_mock, link_mac_pxe_mock,
chmod_mock):
self.config(ipxe_enabled=True, group='pxe')
ipxe_template = "ironic/drivers/modules/ipxe_config.template"
with task_manager.acquire(self.context, self.node.uuid) as task:
@ -379,9 +447,10 @@ class TestPXEUtils(db_base.DbTestCase):
ensure_calls = [
mock.call(os.path.join(CONF.deploy.http_root, self.node.uuid)),
mock.call(os.path.join(CONF.deploy.http_root, 'pxelinux.cfg'))
mock.call(os.path.join(CONF.deploy.http_root, 'pxelinux.cfg')),
]
ensure_tree_mock.assert_has_calls(ensure_calls)
chmod_mock.assert_not_called()
render_mock.assert_called_with(
ipxe_template,
{'pxe_options': self.ipxe_options,

View File

@ -0,0 +1,7 @@
---
fixes:
- Adds the capability for an operator to explicitly define the permission
for created tftpboot folders. This provides the ability for ironic to be
utilized with a restrictive umask, where the tftp server may not be able
to read the file. Introduced a new config option ``[pxe]/dir_permission``
to specify the permission for the tftpboot directories to be created with.