bootloader: add support for initrd overlays
The bootloader plugin now supports the following options: initrd_add_img=IMAGE initrd_add_dir=DIR initrd_dst_img=PATHNAME The 'initrd_add_img' adds initrd overlay named IMAGE. The IMAGE is added from the current profile directory. If IMAGE begins with '/' it's taken as absolute path (e.g. initrd_add_img="/root/overlay.img"). The 'initrd_add_dir' creates initrd image from the DIR at first and then adds the image as a overlay. The DIR is taken from the current profile directory. If DIR begins with '/' it's taken as absolute path. The 'initrd_dst_img' sets the name and location of the resulting initrd image. Usually it is not needed to set it. By default the location of initrd images is /boot and the name of the image is taken as a basename of IMAGE or DIR. This can be overridden by 'initrd_dst_img' Currently grub2-mkconfig doesn't support initrd overlays, so the initrd settings are lost after 'grub2-mkconfig -o /boot/grub2/grub.cfg' is issued. There is grub2 RFE bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1427899 Resolves: rhbz#1414098 Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
This commit is contained in:
parent
4c1ed69f4a
commit
047a7335b0
10 changed files with 227 additions and 44 deletions
1
00_tuned
1
00_tuned
|
|
@ -26,3 +26,4 @@ tuned_bootcmdline_file=$tunedcfgdir/bootcmdline
|
|||
. $tuned_bootcmdline_file
|
||||
|
||||
echo "set tuned_params=\"$TUNED_BOOT_CMDLINE\""
|
||||
echo "set tuned_initrd=\"$TUNED_BOOT_INITRD_ADD\""
|
||||
|
|
|
|||
18
bootcmdline
18
bootcmdline
|
|
@ -1,6 +1,6 @@
|
|||
# This file specifies additional parameters to kernel boot command line.
|
||||
# Its content is set by the Tuned bootloader plugin and sourced by the
|
||||
# grub2-mkconfig (/etc/grub.d/00_tuned script).
|
||||
# This file specifies additional parameters to kernel boot command line and
|
||||
# initrd overlay images. Its content is set by the Tuned bootloader plugin
|
||||
# and sourced by the grub2-mkconfig (/etc/grub.d/00_tuned script).
|
||||
#
|
||||
# Please do not edit this file. Content of this file can be overwritten by
|
||||
# switch of Tuned profile.
|
||||
|
|
@ -21,5 +21,17 @@
|
|||
# grub2-mkconfig -o /boot/grub2/grub.cfg
|
||||
#
|
||||
# YOUR_ADDITIONAL_KERNEL_PARAMETERS will stay preserved.
|
||||
#
|
||||
# Similarly if you need to add initrd overlay image, create Tuned profile
|
||||
# containing the following:
|
||||
#
|
||||
# [bootloader]
|
||||
# initrd_add_img = INITRD_OVERLAY_IMAGE
|
||||
#
|
||||
# or to generate initrd overlay image from the directory:
|
||||
#
|
||||
# [bootloader]
|
||||
# initrd_add_dir = INITRD_OVERLAY_DIRECTORY
|
||||
|
||||
TUNED_BOOT_CMDLINE=
|
||||
TUNED_BOOT_INITRD_ADD=
|
||||
|
|
|
|||
|
|
@ -11,6 +11,8 @@ DEFAULT_PROFILE = "balanced"
|
|||
DEFAULT_STORAGE_FILE = "/run/tuned/save.pickle"
|
||||
LOAD_DIRECTORIES = ["/usr/lib/tuned", "/etc/tuned"]
|
||||
PERSISTENT_STORAGE_DIR = "/var/lib/tuned"
|
||||
PLUGIN_MAIN_UNIT_NAME = "main"
|
||||
BOOT_DIR = "/boot"
|
||||
|
||||
TMP_FILE_SUFFIX = ".tmp"
|
||||
# max. number of consecutive errors to give up
|
||||
|
|
@ -24,8 +26,11 @@ GRUB2_TUNED_TEMPLATE_PATH = GRUB2_CFG_DIR + "/" + GRUB2_TUNED_TEMPLATE_NAME
|
|||
GRUB2_TEMPLATE_HEADER_BEGIN = "### BEGIN /etc/grub.d/" + GRUB2_TUNED_TEMPLATE_NAME + " ###"
|
||||
GRUB2_TEMPLATE_HEADER_END = "### END /etc/grub.d/" + GRUB2_TUNED_TEMPLATE_NAME + " ###"
|
||||
GRUB2_TUNED_VAR = "tuned_params"
|
||||
GRUB2_TUNED_INITRD_VAR = "tuned_initrd"
|
||||
GRUB2_DEFAULT_ENV_FILE = "/etc/default/grub"
|
||||
INITRD_IMAGE_DIR = "/boot"
|
||||
BOOT_CMDLINE_TUNED_VAR = "TUNED_BOOT_CMDLINE"
|
||||
BOOT_CMDLINE_INITRD_ADD_VAR = "TUNED_BOOT_INITRD_ADD"
|
||||
BOOT_CMDLINE_FILE = "/etc/tuned/bootcmdline"
|
||||
|
||||
# modules plugin configuration
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ class Application(object):
|
|||
profile_factory = profiles.Factory()
|
||||
profile_merger = profiles.Merger()
|
||||
profile_locator = profiles.Locator(consts.LOAD_DIRECTORIES)
|
||||
profile_loader = profiles.Loader(profile_locator, profile_factory, profile_merger, self.variables)
|
||||
profile_loader = profiles.Loader(profile_locator, profile_factory, profile_merger, self.config, self.variables)
|
||||
|
||||
self._daemon = daemon.Daemon(unit_manager, profile_loader, profile_name, self.config, self)
|
||||
self._controller = controller.Controller(self._daemon, self.config)
|
||||
|
|
|
|||
|
|
@ -178,6 +178,12 @@ class Plugin(object):
|
|||
for device in devices:
|
||||
callback(instance, device)
|
||||
|
||||
def _instance_pre_static(self, instance, enabling):
|
||||
pass
|
||||
|
||||
def _instance_post_static(self, instance, enabling):
|
||||
pass
|
||||
|
||||
def instance_apply_tuning(self, instance):
|
||||
"""
|
||||
Apply static and dynamic tuning if the plugin instance is active.
|
||||
|
|
@ -186,7 +192,9 @@ class Plugin(object):
|
|||
return
|
||||
|
||||
if instance.has_static_tuning:
|
||||
self._instance_pre_static(instance, True)
|
||||
self._instance_apply_static(instance)
|
||||
self._instance_post_static(instance, True)
|
||||
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
|
||||
self._run_for_each_device(instance, self._instance_apply_dynamic)
|
||||
|
||||
|
|
@ -219,7 +227,9 @@ class Plugin(object):
|
|||
if instance.has_dynamic_tuning and self._global_cfg.get(consts.CFG_DYNAMIC_TUNING, consts.CFG_DEF_DYNAMIC_TUNING):
|
||||
self._run_for_each_device(instance, self._instance_unapply_dynamic)
|
||||
if instance.has_static_tuning:
|
||||
self._instance_pre_static(instance, False)
|
||||
self._instance_unapply_static(instance, profile_switch)
|
||||
self._instance_post_static(instance, False)
|
||||
|
||||
def _instance_apply_static(self, instance):
|
||||
self._execute_all_non_device_commands(instance)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import tuned.consts as consts
|
|||
|
||||
import os
|
||||
import re
|
||||
import tempfile
|
||||
|
||||
log = tuned.logs.get()
|
||||
|
||||
|
|
@ -27,6 +28,11 @@ class BootloaderPlugin(base.Plugin):
|
|||
def _instance_init(self, instance):
|
||||
instance._has_dynamic_tuning = False
|
||||
instance._has_static_tuning = True
|
||||
# controls grub2_cfg rewrites in _instance_post_static
|
||||
self.update_grub2_cfg = False
|
||||
self._initrd_dst_img = None
|
||||
self._cmdline = ""
|
||||
self._initrd = ""
|
||||
self._grub2_cfg_file_name = self._get_grub2_cfg_file()
|
||||
|
||||
def _instance_cleanup(self, instance):
|
||||
|
|
@ -36,7 +42,10 @@ class BootloaderPlugin(base.Plugin):
|
|||
def _get_config_options(cls):
|
||||
return {
|
||||
"grub2_cfg_file": None,
|
||||
"cmdline": "",
|
||||
"initrd_dst_img": None,
|
||||
"initrd_add_img": None,
|
||||
"initrd_add_dir": None,
|
||||
"cmdline": None,
|
||||
}
|
||||
|
||||
def _get_effective_options(self, options):
|
||||
|
|
@ -76,13 +85,12 @@ class BootloaderPlugin(base.Plugin):
|
|||
return f
|
||||
return None
|
||||
|
||||
def _patch_bootcmdline(self, value):
|
||||
return self._cmd.replace_in_file(consts.BOOT_CMDLINE_FILE, r"\b(" + consts.BOOT_CMDLINE_TUNED_VAR + \
|
||||
r"\s*=).*$", r"\1" + "\"" + str(value) + "\"")
|
||||
def _patch_bootcmdline(self, d):
|
||||
return self._cmd.add_modify_option_in_file(consts.BOOT_CMDLINE_FILE, d)
|
||||
|
||||
def _remove_grub2_tuning(self):
|
||||
self._patch_bootcmdline("")
|
||||
self._cmd.replace_in_file(self._grub2_cfg_file_name, r"\b(set\s+" + consts.GRUB2_TUNED_VAR + r"\s*=).*$", r"\1" + "\"\"")
|
||||
self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR : "", consts.BOOT_CMDLINE_INITRD_ADD_VAR : ""})
|
||||
self._cmd.add_modify_option_in_file(self._grub2_cfg_file_name, {"set\s+" + consts.GRUB2_TUNED_VAR : "", "set\s+" + consts.GRUB2_TUNED_INITRD_VAR : ""}, add = False)
|
||||
|
||||
def _instance_unapply_static(self, instance, profile_switch = False):
|
||||
if profile_switch:
|
||||
|
|
@ -93,19 +101,28 @@ class BootloaderPlugin(base.Plugin):
|
|||
log.debug("unpatching grub.cfg")
|
||||
cfg = re.sub(r"^\s*set\s+" + consts.GRUB2_TUNED_VAR + "\s*=.*\n", "", grub2_cfg, flags = re.MULTILINE)
|
||||
grub2_cfg = re.sub(r" *\$" + consts.GRUB2_TUNED_VAR, "", cfg, flags = re.MULTILINE)
|
||||
cfg = re.sub(r"^\s*set\s+" + consts.GRUB2_TUNED_INITRD_VAR + "\s*=.*\n", "", grub2_cfg, flags = re.MULTILINE)
|
||||
grub2_cfg = re.sub(r" *\$" + consts.GRUB2_TUNED_INITRD_VAR, "", cfg, flags = re.MULTILINE)
|
||||
cfg = re.sub(consts.GRUB2_TEMPLATE_HEADER_BEGIN + r"\n", "", grub2_cfg, flags = re.MULTILINE)
|
||||
return re.sub(consts.GRUB2_TEMPLATE_HEADER_END + r"\n+", "", cfg, flags = re.MULTILINE)
|
||||
|
||||
def _grub2_cfg_patch_initial(self, grub2_cfg, value):
|
||||
def _grub2_cfg_patch_initial(self, grub2_cfg, d):
|
||||
log.debug("initial patching of grub.cfg")
|
||||
cfg = re.sub(r"^(\s*###\s+END\s+[^#]+/00_header\s+### *)\n", r"\1\n\n" + consts.GRUB2_TEMPLATE_HEADER_BEGIN + "\nset " +
|
||||
consts.GRUB2_TUNED_VAR + "=\"" + str(value) + "\"\n" + consts.GRUB2_TEMPLATE_HEADER_END + r"\n", grub2_cfg, flags = re.MULTILINE)
|
||||
# add tuned parameters to all kernels
|
||||
grub2_cfg = re.sub(r"^(\s*linux(16|efi)?\s+.*)$", r"\1 $" + consts.GRUB2_TUNED_VAR, cfg, flags = re.MULTILINE)
|
||||
# remove tuned parameters from rescue kernels
|
||||
cfg = re.sub(r"^(\s*linux(?:16|efi)?\s+\S+rescue.*)\$" + consts.GRUB2_TUNED_VAR + r" *(.*)$", r"\1\2", grub2_cfg, flags = re.MULTILINE)
|
||||
# fix whitespaces in rescue kernels
|
||||
return re.sub(r"^(\s*linux(?:16|efi)?\s+\S+rescue.*) +$", r"\1", cfg, flags = re.MULTILINE)
|
||||
s = r"\1\n\n" + consts.GRUB2_TEMPLATE_HEADER_BEGIN + "\n"
|
||||
for opt in d:
|
||||
s += r"set " + self._cmd.escape(opt) + "=\"" + self._cmd.escape(d[opt]) + "\"\n"
|
||||
s += consts.GRUB2_TEMPLATE_HEADER_END + r"\n"
|
||||
grub2_cfg = re.sub(r"^(\s*###\s+END\s+[^#]+/00_header\s+### *)\n", s, grub2_cfg, flags = re.MULTILINE)
|
||||
|
||||
d2 = {"linux" : consts.GRUB2_TUNED_VAR, "initrd" : consts.GRUB2_TUNED_INITRD_VAR}
|
||||
for i in d2:
|
||||
# add tuned parameters to all kernels
|
||||
grub2_cfg = re.sub(r"^(\s*" + i + r"(16|efi)?\s+.*)$", r"\1 $" + d2[i], grub2_cfg, flags = re.MULTILINE)
|
||||
# remove tuned parameters from rescue kernels
|
||||
grub2_cfg = re.sub(r"^(\s*" + i + r"(?:16|efi)?\s+\S+rescue.*)\$" + d2[i] + r" *(.*)$", r"\1\2", grub2_cfg, flags = re.MULTILINE)
|
||||
# fix whitespaces in rescue kernels
|
||||
grub2_cfg = re.sub(r"^(\s*" + i + r"(?:16|efi)?\s+\S+rescue.*) +$", r"\1", grub2_cfg, flags = re.MULTILINE)
|
||||
return grub2_cfg
|
||||
|
||||
def _grub2_default_env_patch(self):
|
||||
grub2_default_env = self._cmd.read_file(consts.GRUB2_DEFAULT_ENV_FILE)
|
||||
|
|
@ -113,13 +130,20 @@ class BootloaderPlugin(base.Plugin):
|
|||
log.error("error reading '%s'" % consts.GRUB2_DEFAULT_ENV_FILE)
|
||||
return False
|
||||
|
||||
if re.search(r"^[^#]*\bGRUB_CMDLINE_LINUX_DEFAULT\s*=.*\\\$" + consts.GRUB2_TUNED_VAR + r"\b.*$", grub2_default_env, flags = re.MULTILINE) is None:
|
||||
d = {"GRUB_CMDLINE_LINUX_DEFAULT" : consts.GRUB2_TUNED_VAR, "GRUB_INITRD_OVERLAY" : consts.GRUB2_TUNED_INITRD_VAR}
|
||||
write = False
|
||||
for i in d:
|
||||
if re.search(r"^[^#]*\b" + i + r"\s*=.*\\\$" + d[i] + r"\b.*$", grub2_default_env, flags = re.MULTILINE) is None:
|
||||
write = True
|
||||
if grub2_default_env[-1] != "\n":
|
||||
grub2_default_env += "\n"
|
||||
grub2_default_env += i + "=\"${" + i + ":+$" + i + r" }\$" + d[i] + "\"\n"
|
||||
if write:
|
||||
log.debug("patching '%s'" % consts.GRUB2_DEFAULT_ENV_FILE)
|
||||
self._cmd.write_to_file(consts.GRUB2_DEFAULT_ENV_FILE,
|
||||
grub2_default_env + "GRUB_CMDLINE_LINUX_DEFAULT=\"${GRUB_CMDLINE_LINUX_DEFAULT:+$GRUB_CMDLINE_LINUX_DEFAULT }" + r"\$" + consts.GRUB2_TUNED_VAR + "\"\n")
|
||||
self._cmd.write_to_file(consts.GRUB2_DEFAULT_ENV_FILE, grub2_default_env)
|
||||
return True
|
||||
|
||||
def _grub2_cfg_patch(self, value):
|
||||
def _grub2_cfg_patch(self, d):
|
||||
log.debug("patching grub.cfg")
|
||||
if self._grub2_cfg_file_name is None:
|
||||
log.error("cannot find grub.cfg to patch, you need to regenerate it by hand by grub2-mkconfig")
|
||||
|
|
@ -129,20 +153,96 @@ class BootloaderPlugin(base.Plugin):
|
|||
log.error("error patching %s, you need to regenerate it by hand by grub2-mkconfig" % self._grub2_cfg_file_name)
|
||||
return False
|
||||
log.debug("adding boot command line parameters to '%s'" % self._grub2_cfg_file_name)
|
||||
(grub2_cfg_new, nsubs) = re.subn(r"\b(set\s+" + consts.GRUB2_TUNED_VAR + "\s*=).*$", r"\1" + "\"" + str(value) + "\"", grub2_cfg, flags = re.MULTILINE)
|
||||
if nsubs < 1 or re.search(r"\$" + consts.GRUB2_TUNED_VAR, grub2_cfg, flags = re.MULTILINE) is None:
|
||||
grub2_cfg_new = self._grub2_cfg_patch_initial(self._grub2_cfg_unpatch(grub2_cfg), value)
|
||||
grub2_cfg_new = grub2_cfg
|
||||
patch_initial = False
|
||||
for opt in d:
|
||||
(grub2_cfg_new, nsubs) = re.subn(r"\b(set\s+" + opt + "\s*=).*$", r"\1" + "\"" + d[opt] + "\"", grub2_cfg_new, flags = re.MULTILINE)
|
||||
if nsubs < 1 or re.search(r"\$" + opt, grub2_cfg, flags = re.MULTILINE) is None:
|
||||
patch_initial = True
|
||||
if patch_initial:
|
||||
grub2_cfg_new = self._grub2_cfg_patch_initial(self._grub2_cfg_unpatch(grub2_cfg), d)
|
||||
self._cmd.write_to_file(self._grub2_cfg_file_name, grub2_cfg_new)
|
||||
self._grub2_default_env_patch()
|
||||
return True
|
||||
|
||||
def _grub2_update(self):
|
||||
self._grub2_cfg_patch({consts.GRUB2_TUNED_VAR : self._cmdline, consts.GRUB2_TUNED_INITRD_VAR : self._initrd})
|
||||
self._patch_bootcmdline({consts.BOOT_CMDLINE_TUNED_VAR : self._cmdline, consts.BOOT_CMDLINE_INITRD_ADD_VAR : self._initrd})
|
||||
|
||||
def _init_initrd_dst_img(self, name):
|
||||
if self._initrd_dst_img is None:
|
||||
self._initrd_dst_img = os.path.join(consts.BOOT_DIR, os.path.basename(name))
|
||||
|
||||
def _install_initrd(self, img):
|
||||
log.info("installing initrd image as '%s'" % self._initrd_dst_img)
|
||||
img_name = os.path.basename(self._initrd_dst_img)
|
||||
self._cmd.copy(img, self._initrd_dst_img)
|
||||
self.update_grub2_cfg = True
|
||||
self._initrd = "/" + img_name
|
||||
|
||||
@command_custom("grub2_cfg_file")
|
||||
def _grub2_cfg_file(self, enabling, value, verify, ignore_missing):
|
||||
# nothing to verify
|
||||
if verify:
|
||||
return None
|
||||
if enabling and value is not None:
|
||||
self._grub2_cfg_file_name = value
|
||||
self._grub2_cfg_file_name = str(value)
|
||||
|
||||
@command_custom("initrd_dst_img")
|
||||
def _initrd_dst_img(self, enabling, value, verify, ignore_missing):
|
||||
# nothing to verify
|
||||
if verify:
|
||||
return None
|
||||
if enabling and value is not None:
|
||||
self._initrd_dst_img = str(value)
|
||||
if self._initrd_dst_img == "":
|
||||
return False
|
||||
if self._initrd_dst_img[0] != "/":
|
||||
self._initrd_dst_img = os.path.join(consts.BOOT_DIR, self._initrd_dst_img)
|
||||
|
||||
@command_custom("initrd_add_img", per_device = False, priority = 10)
|
||||
def _initrd_add_img(self, enabling, value, verify, ignore_missing):
|
||||
# nothing to verify
|
||||
if verify:
|
||||
return None
|
||||
if enabling and value is not None:
|
||||
src_img = str(value)
|
||||
self._init_initrd_dst_img(src_img)
|
||||
if src_img == "":
|
||||
return False
|
||||
if src_img[0] != "/":
|
||||
src_img = os.path.join(os.path.dirname(self._global_cfg.get("profile_location", "")), src_img)
|
||||
|
||||
self._install_initrd(src_img)
|
||||
|
||||
@command_custom("initrd_add_dir", per_device = False, priority = 10)
|
||||
def _initrd_add_dir(self, enabling, value, verify, ignore_missing):
|
||||
# nothing to verify
|
||||
if verify:
|
||||
return None
|
||||
if enabling and value is not None:
|
||||
src_dir = str(value)
|
||||
self._init_initrd_dst_img(src_dir)
|
||||
if src_dir == "":
|
||||
return False
|
||||
if src_dir[0] != "/":
|
||||
src_dir = os.path.join(os.path.dirname(self._global_cfg.get("profile_location", "./")), src_dir)
|
||||
if not os.path.isdir(src_dir):
|
||||
log.error("error: cannot create initrd image, source directory '%s' doesn't exist" % src_dir)
|
||||
return False
|
||||
|
||||
log.info("generating initrd image from directory '%s'" % src_dir)
|
||||
(fd, tmpfile) = tempfile.mkstemp(prefix = "tuned-bootloader-", suffix = ".tmp")
|
||||
log.debug("writing initrd image to temporary file '%s'" % tmpfile)
|
||||
os.close(fd)
|
||||
(rc, out) = self._cmd.execute("find . | cpio -co > %s" % tmpfile, cwd = src_dir, shell = True)
|
||||
log.debug("cpio log: %s" % out)
|
||||
if rc != 0:
|
||||
log.error("error generating initrd image")
|
||||
self._cmd.unlink(tmpfile, no_error = True)
|
||||
return False
|
||||
self._install_initrd(tmpfile)
|
||||
self._cmd.unlink(tmpfile)
|
||||
|
||||
@command_custom("cmdline", per_device = False, priority = 10)
|
||||
def _cmdline(self, enabling, value, verify, ignore_missing):
|
||||
|
|
@ -160,7 +260,12 @@ class BootloaderPlugin(base.Plugin):
|
|||
else:
|
||||
log.error(consts.STR_VERIFY_PROFILE_VALUE_FAIL % ("cmdline", str(cmdline_intersect), str(value_set)))
|
||||
return False
|
||||
if enabling:
|
||||
if enabling and value is not None:
|
||||
log.info("installing additional boot command line parameters to grub2")
|
||||
self._grub2_cfg_patch(v)
|
||||
self._patch_bootcmdline(v)
|
||||
self.update_grub2_cfg = True
|
||||
self._cmdline = v
|
||||
|
||||
def _instance_post_static(self, instance, enabling):
|
||||
if enabling and self.update_grub2_cfg:
|
||||
self._grub2_update()
|
||||
self.update_grub2_cfg = False
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import tuned.profiles.profile
|
||||
import tuned.profiles.variables
|
||||
from configobj import ConfigObj, ConfigObjError
|
||||
import tuned.consts as consts
|
||||
import os.path
|
||||
import collections
|
||||
import tuned.logs
|
||||
|
|
@ -14,12 +15,13 @@ class Loader(object):
|
|||
Profiles loader.
|
||||
"""
|
||||
|
||||
__slots__ = ["_profile_locator", "_profile_merger", "_profile_factory", "_variables"]
|
||||
__slots__ = ["_profile_locator", "_profile_merger", "_profile_factory", "_global_config", "_variables"]
|
||||
|
||||
def __init__(self, profile_locator, profile_factory, profile_merger, variables):
|
||||
def __init__(self, profile_locator, profile_factory, profile_merger, global_config, variables):
|
||||
self._profile_locator = profile_locator
|
||||
self._profile_factory = profile_factory
|
||||
self._profile_merger = profile_merger
|
||||
self._global_config = global_config
|
||||
self._variables = variables
|
||||
|
||||
def _create_profile(self, profile_name, config):
|
||||
|
|
@ -87,10 +89,13 @@ class Loader(object):
|
|||
for option in config_obj[section].keys():
|
||||
config[section][option] = config_obj[section][option]
|
||||
|
||||
# hack to notify plugins about profile location
|
||||
self._global_config.set("profile_location", file_name)
|
||||
|
||||
# TODO: HACK, this needs to be solved in a better way (better config parser)
|
||||
dir_name = os.path.dirname(file_name)
|
||||
for unit_name in config:
|
||||
if "script" in config[unit_name] and config[unit_name].get("script", None) is not None:
|
||||
dir_name = os.path.dirname(file_name)
|
||||
script_path = os.path.join(dir_name, config[unit_name]["script"])
|
||||
config[unit_name]["script"] = [os.path.normpath(script_path)]
|
||||
|
||||
|
|
|
|||
|
|
@ -73,8 +73,8 @@ class Locator(object):
|
|||
config = self.parse_config(profile_name)
|
||||
if config is None:
|
||||
return [False, "", "", ""]
|
||||
if "main" in config:
|
||||
d = config["main"]
|
||||
if consts.PLUGIN_MAIN_UNIT_NAME in config:
|
||||
d = config[consts.PLUGIN_MAIN_UNIT_NAME]
|
||||
else:
|
||||
d = dict()
|
||||
vals = [True, profile_name]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import tuned.profiles.unit
|
||||
import tuned.consts as consts
|
||||
import collections
|
||||
|
||||
class Profile(object):
|
||||
|
|
@ -15,13 +16,13 @@ class Profile(object):
|
|||
|
||||
def _init_options(self, config):
|
||||
self._options = {}
|
||||
if "main" in config:
|
||||
self._options = dict(config["main"])
|
||||
if consts.PLUGIN_MAIN_UNIT_NAME in config:
|
||||
self._options = dict(config[consts.PLUGIN_MAIN_UNIT_NAME])
|
||||
|
||||
def _init_units(self, config):
|
||||
self._units = collections.OrderedDict()
|
||||
for unit_name in config:
|
||||
if unit_name != "main":
|
||||
if unit_name != consts.PLUGIN_MAIN_UNIT_NAME:
|
||||
new_unit = self._create_unit(unit_name, config[unit_name])
|
||||
self._units[unit_name] = new_unit
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import errno
|
|||
import tuned.logs
|
||||
import copy
|
||||
import os
|
||||
import shutil
|
||||
import tuned.consts as consts
|
||||
from configobj import ConfigObj, ConfigObjError
|
||||
import re
|
||||
|
|
@ -33,6 +34,10 @@ class commands:
|
|||
def unquote(self, v):
|
||||
return re.sub("^\"(.*)\"$", r"\1", v)
|
||||
|
||||
# escape escape character (by default '\')
|
||||
def escape(self, s, what_escape = "\\", escape_by = "\\"):
|
||||
return s.replace(what_escape, "%s%s" % (escape_by, what_escape))
|
||||
|
||||
# clear escape characters (by default '\')
|
||||
def unescape(self, s, escape_char = "\\"):
|
||||
return s.replace(escape_char, "")
|
||||
|
|
@ -60,12 +65,16 @@ class commands:
|
|||
# Do multiple regex replaces in 's' according to lookup table described by
|
||||
# dictionary 'd', e.g.: d = {"re1": "replace1", "re2": "replace2", ...}
|
||||
# r can be regex precompiled by re_lookup_compile for speedup
|
||||
def multiple_re_replace(self, d, s, r = None):
|
||||
if len(d) == 0 or s is None:
|
||||
return s
|
||||
def multiple_re_replace(self, d, s, r = None, flags = 0):
|
||||
if d is None:
|
||||
if r is None:
|
||||
return s
|
||||
else:
|
||||
if len(d) == 0 or s is None:
|
||||
return s
|
||||
if r is None:
|
||||
r = self.re_lookup_compile(d)
|
||||
return r.sub(lambda mo: d.values()[mo.lastindex - 1], s)
|
||||
return r.sub(lambda mo: d.values()[mo.lastindex - 1], s, flags)
|
||||
|
||||
# Do regex lookup on 's' according to lookup table described by
|
||||
# dictionary 'd' and return corresponding value from the dictionary,
|
||||
|
|
@ -133,14 +142,49 @@ class commands:
|
|||
return False
|
||||
return True
|
||||
|
||||
def copy(self, src, dst, no_error = False):
|
||||
try:
|
||||
log.debug("copying file '%s' to '%s'" % (src, dst))
|
||||
shutil.copy(src, dst)
|
||||
except IOError as e:
|
||||
if not no_error:
|
||||
log.error("cannot copy file '%s' to '%s': %s" % (src, dst, e))
|
||||
|
||||
def replace_in_file(self, f, pattern, repl):
|
||||
data = self.read_file(f)
|
||||
if len(data) <= 0:
|
||||
return False;
|
||||
return self.write_to_file(f, re.sub(pattern, repl, data, flags = re.MULTILINE))
|
||||
|
||||
# do multiple replaces in file 'f' by using dictionary 'd',
|
||||
# e.g.: d = {"re1": val1, "re2": val2, ...}
|
||||
def multiple_replace_in_file(self, f, d):
|
||||
data = self.read_file(f)
|
||||
if len(data) <= 0:
|
||||
return False;
|
||||
return self.write_to_file(f, self.multiple_re_replace(d, data, flags = re.MULTILINE))
|
||||
|
||||
# makes sure that options from 'd' are set to values from 'd' in file 'f',
|
||||
# when needed it edits options or add new options if they don't
|
||||
# exist and 'add' is set to True, 'd' has the following form:
|
||||
# d = {"option_1": value_1, "option_2": value_2, ...}
|
||||
def add_modify_option_in_file(self, f, d, add = True):
|
||||
data = self.read_file(f)
|
||||
for opt in d:
|
||||
o = str(opt)
|
||||
v = str(d[opt])
|
||||
if re.search(r"\b" + o + r"\s*=.*$", data, flags = re.MULTILINE) is None:
|
||||
if add:
|
||||
if len(data) > 0 and data[-1] != "\n":
|
||||
data += "\n"
|
||||
data += "%s=\"%s\"\n" % (o, v)
|
||||
else:
|
||||
data = re.sub(r"\b(" + o + r"\s*=).*$", r"\1" + "\"" + v + "\"", data, flags = re.MULTILINE)
|
||||
|
||||
return self.write_to_file(f, data)
|
||||
|
||||
# "no_errors" can be list of return codes not treated as errors
|
||||
def execute(self, args, no_errors = []):
|
||||
def execute(self, args, shell = False, cwd = None, no_errors = []):
|
||||
retcode = 0
|
||||
if self._environment is None:
|
||||
self._environment = os.environ.copy()
|
||||
|
|
@ -149,7 +193,7 @@ class commands:
|
|||
self._debug("Executing %s." % str(args))
|
||||
out = ""
|
||||
try:
|
||||
proc = Popen(args, stdout=PIPE, stderr=PIPE, env=self._environment, close_fds=True)
|
||||
proc = Popen(args, stdout = PIPE, stderr = PIPE, env = self._environment, shell = shell, cwd = cwd, close_fds = True)
|
||||
out, err = proc.communicate()
|
||||
|
||||
retcode = proc.returncode
|
||||
|
|
|
|||
Loading…
Reference in a new issue