Add ${RANDOM} variable to config file

This commit is contained in:
Orsiris de Jong 2023-02-02 20:14:19 +01:00
parent 4551cbe485
commit 74259dc7b7
3 changed files with 71 additions and 28 deletions

View file

@ -37,7 +37,10 @@ repo:
prometheus: prometheus:
## Supervision ## Supervision
metrics: true metrics: true
backup_job: ${HOSTNAME} # ${HOSTNAME} is a variable which can be replaced with a constant string # ${HOSTNAME} is a variable
# ${RANDOM}[n] is a variable containing 'n' random alphanumeric char
# The following example allows to have multiple hosts with the same hostname
backup_job: ${HOSTNAME}-${RANDOM}
# Prometheus metrics destination can be a http / https server with optional basic authentication (pushgateway), or a file path for node textfile collector to pickup # Prometheus metrics destination can be a http / https server with optional basic authentication (pushgateway), or a file path for node textfile collector to pickup
destination: # https://push.monitoring.example.tld/metrics/job/${BACKUP_JOB} where ${BACKUP_JOB} is defined in prometheus_backup_job destination: # https://push.monitoring.example.tld/metrics/job/${BACKUP_JOB} where ${BACKUP_JOB} is defined in prometheus_backup_job
# prometheus instance, becomes exported_instance when using a push gateway # prometheus instance, becomes exported_instance when using a push gateway
@ -65,3 +68,5 @@ options:
# every 10 NPBackup runs, we'll try an autoupgrade. Never set this lower than 2 since failed upgrades will prevent backups from succeeding # every 10 NPBackup runs, we'll try an autoupgrade. Never set this lower than 2 since failed upgrades will prevent backups from succeeding
interval: 10 interval: 10
backup_admin_password: NPBackup_00 backup_admin_password: NPBackup_00
# Available variables: ${HOSTNAME}, ${RANDOM}[n]
host_identity: ${HOSTNAME}-${RANDOM}[3]

View file

@ -7,15 +7,19 @@ __intname__ = "npbackup.configuration"
__author__ = "Orsiris de Jong" __author__ = "Orsiris de Jong"
__copyright__ = "Copyright (C) 2022-2023 NetInvent" __copyright__ = "Copyright (C) 2022-2023 NetInvent"
__license__ = "GPL-3.0-only" __license__ = "GPL-3.0-only"
__build__ = "2023020101" __build__ = "2023020102"
__version__ = "1.5.0 for npbackup 2.2.0+" __version__ = "1.6.0 for npbackup 2.2.0+"
from typing import Tuple, Optional
import sys import sys
from ruamel.yaml import YAML from ruamel.yaml import YAML
from logging import getLogger from logging import getLogger
import re
from cryptidy import symmetric_encryption as enc from cryptidy import symmetric_encryption as enc
from ofunctions.random import random_string
from npbackup.customization import ID_STRING from npbackup.customization import ID_STRING
# Try to import a private key, if not available, fallback to the default key # Try to import a private key, if not available, fallback to the default key
try: try:
from npbackup._private_secret_keys import AES_KEY, DEFAULT_BACKUP_ADMIN_PASSWORD from npbackup._private_secret_keys import AES_KEY, DEFAULT_BACKUP_ADMIN_PASSWORD
@ -57,7 +61,7 @@ empty_config_dict = {
} }
def decrypt_data(config_dict): def decrypt_data(config_dict: dict) -> dict:
try: try:
for option in ENCRYPTED_OPTIONS: for option in ENCRYPTED_OPTIONS:
try: try:
@ -95,7 +99,7 @@ def decrypt_data(config_dict):
return config_dict return config_dict
def encrypt_data(config_dict): def encrypt_data(config_dict: dict) -> dict:
for option in ENCRYPTED_OPTIONS: for option in ENCRYPTED_OPTIONS:
try: try:
if config_dict[option["section"]][option["name"]]: if config_dict[option["section"]][option["name"]]:
@ -121,7 +125,7 @@ def encrypt_data(config_dict):
return config_dict return config_dict
def is_encrypted(config_dict): def is_encrypted(config_dict: dict) -> bool:
try: try:
is_enc = True is_enc = True
for option in ENCRYPTED_OPTIONS: for option in ENCRYPTED_OPTIONS:
@ -142,25 +146,54 @@ def is_encrypted(config_dict):
# NoneType # NoneType
return False return False
def has_random_variables(config_dict: dict) -> Tuple[bool, dict]:
"""
Replaces ${RANDOM}[n] with n random alphanumeric chars
"""
is_modified = False
for section in config_dict.keys():
for entry in config_dict[section].keys():
if isinstance(config_dict[section][entry], str):
matches = re.search(r"\${RANDOM}\[(.*)\]", config_dict[section][entry])
print(matches)
if matches:
try:
char_quantity = int(matches.group(1))
except (ValueError, TypeError):
char_quantity = 1
print(random_string(char_quantity))
config_dict[section][entry] = re.sub(r"\${RANDOM}\[.*\]", random_string(char_quantity), config_dict[section][entry])
is_modified = True
return is_modified, config_dict
def load_config(config_file):
def load_config(config_file: str) -> Optional[dict]:
""" """
Using ruamel.yaml preserves comments and order of yaml files Using ruamel.yaml preserves comments and order of yaml files
""" """
try:
logger.debug("Using configuration file {}".format(config_file)) logger.debug("Using configuration file {}".format(config_file))
with open(config_file, "r", encoding="utf-8") as file_handle: with open(config_file, "r", encoding="utf-8") as file_handle:
# RoundTrip loader is default and preserves comments and ordering # RoundTrip loader is default and preserves comments and ordering
yaml = YAML(typ="rt") yaml = YAML(typ="rt")
config_dict = yaml.load(file_handle) config_dict = yaml.load(file_handle)
is_modified, config_dict = has_random_variables(config_dict)
if is_modified:
logger.info("Handling random variables in configuration files")
save_config(config_file, config_dict)
if not is_encrypted(config_dict): if not is_encrypted(config_dict):
logger.info("Encrypting non encrypted data in configuration file") logger.info("Encrypting non encrypted data in configuration file")
config_dict = encrypt_data(config_dict) config_dict = encrypt_data(config_dict)
save_config(config_file, config_dict) save_config(config_file, config_dict)
config_dict = decrypt_data(config_dict) config_dict = decrypt_data(config_dict)
return config_dict return config_dict
except OSError:
logger.critical("Cannot load configuration file from %s", config_file)
return None
def save_config(config_file, config_dict): def save_config(config_file: str, config_dict: dict) -> bool:
try:
with open(config_file, "w", encoding="utf-8") as file_handle: with open(config_file, "w", encoding="utf-8") as file_handle:
if not is_encrypted(config_dict): if not is_encrypted(config_dict):
config_dict = encrypt_data(config_dict) config_dict = encrypt_data(config_dict)
@ -168,3 +201,7 @@ def save_config(config_file, config_dict):
yaml.dump(config_dict, file_handle) yaml.dump(config_dict, file_handle)
# Since we deal with global objects in ruamel.yaml, we need to decrypt after saving # Since we deal with global objects in ruamel.yaml, we need to decrypt after saving
config_dict = decrypt_data(config_dict) config_dict = decrypt_data(config_dict)
return True
except OSError:
logger.critical("Cannot save configuartion file to %s", config_file)
return False

View file

@ -6,6 +6,7 @@ ofunctions.misc>=1.5.2
ofunctions.process>=1.4.0 ofunctions.process>=1.4.0
ofunctions.threading>=2.0.0 ofunctions.threading>=2.0.0
ofunctions.platform>=1.3.0 ofunctions.platform>=1.3.0
ofunctions.random
python-pidfile>=3.0.0 python-pidfile>=3.0.0
pysimplegui>=4.6.0 pysimplegui>=4.6.0
requests requests