Source code for cobbler.settings.migrations.helper

"""
Helper module which contains shared logic for adjusting the settings.
"""
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: 2021 Dominik Gedon <dgedon@suse.de>
# SPDX-FileCopyrightText: 2021 Enno Gotthold <egotthold@suse.de>
# SPDX-FileCopyrightText: Copyright SUSE LLC

import datetime
import os
from shutil import copytree
from typing import Any, Dict, List, Union


[docs]class Setting: """ Specifies a setting object """ def __init__(self, location: Union[str, List[str]], value: Any): """ Conutructor """ if isinstance(location, str): self.location = self.split_str_location(location) elif isinstance(location, list): # type: ignore self.location = location else: raise TypeError("location must be of type str or list.") self.value = value def __eq__(self, other: Any) -> bool: """ Compares 2 Setting objects for equality. Necesarry for the tests. """ if not isinstance(other, Setting): return False return self.value == other.value and self.location == other.location @property def key_name(self) -> str: """ Returns the location. """ return self.location[-1]
[docs] @staticmethod def split_str_location(location: str) -> List[str]: """ Split the given location at "." Necessary for nesting in our settings file :param location: Can be "manage.dhcp_v4" or "restart.dhcp_v4" for example. """ return location.split(".")
# Some algorithms taken from https://stackoverflow.com/a/14692746/4730773
[docs]def key_add(new: Setting, settings: Dict[str, Any]) -> None: """ Add a new settings key. :param new: The new setting to add. :param settings: [description] """ nested = new.location for key in nested[:-1]: if settings.get(key) is None: settings[key] = {} settings = settings[key] settings[nested[-1]] = new.value
[docs]def key_delete(delete: str, settings: Dict[str, Any]) -> None: """ Deletes a given setting :param delete: The name of the setting to be deleted. :param settings: The settings dict where the key should be deleted. """ delete_obj = Setting(delete, None) if len(delete_obj.location) == 1: del settings[delete_obj.key_name] else: del key_get(".".join(delete_obj.location[:-1]), settings).value[ delete_obj.key_name ]
[docs]def key_get(key: str, settings: Dict[str, Any]) -> Setting: """ Get a key from the settings :param key: The key to get in the form "a.b.c" :param settings: The dict to operate on. :return: The desired key from the settings dict """ # TODO: Check if key does not exist if not key: raise ValueError("Key must not be empty!") new = Setting(key, None) nested = new.location for keys in nested[:-1]: settings = settings[keys] new.value = settings[nested[-1]] return new
[docs]def key_move(move: Setting, new_location: List[str], settings: Dict[str, Any]) -> None: """ Delete the old setting and create a new key at ``new_location`` :param move: The name of the old key which should be moved. :param new_location: The location of the new key :param settings: The dict to operate on. """ new_setting = Setting(new_location, move.value) key_delete(".".join(move.location), settings) key_add(new_setting, settings)
[docs]def key_rename(old_name: Setting, new_name: str, settings: Dict[str, Any]) -> None: """ Wrapper for key_move() :param old_name: The old name :param new_name: The new name :param settings: """ new_location = old_name.location[:-1] + [new_name] key_move(old_name, new_location, settings)
[docs]def key_set_value(new: Setting, settings: Dict[str, Any]) -> None: """ Change the value of a setting. :param new: A Settings object with the new information. :param settings: The settings dict. """ nested = new.location for key in nested[:-1]: settings = settings[key] settings[nested[-1]] = new.value
[docs]def key_drop_if_default( settings: Dict[str, Any], defaults: Dict[str, Any] ) -> Dict[str, Any]: """ Drop all keys which values are identical to the default ones. :param settings: The current settings read from an external source :param defaults: The full settings with default values """ for key in list(settings.keys()): if isinstance(settings[key], dict): settings[key] = key_drop_if_default(settings[key], defaults[key]) if len(settings[key]) == 0: del settings[key] else: if settings[key] == defaults[key]: settings.pop(key) return settings
[docs]def backup_dir(dir_path: str) -> None: """ Copies the directory tree and adds a suffix ".backup.XXXXXXXXX" to it. :param dir_path: The full path to the directory which should be backed up. :raises FileNotFoundError: In case the path specified was not existing. """ src = os.path.normpath(dir_path) now_iso = datetime.datetime.now().isoformat() copytree(dir_path, f"{src}.backup.{now_iso}")