Compare commits

...

3 commits

Author SHA1 Message Date
pedro f946a566a2 clarificación README-es.md 2024-10-16 00:32:39 +02:00
pedro a3644afc2b añade dependencias workbench-script 2024-10-16 00:32:02 +02:00
pedro a2e33d2b13 add logger and locale for spanish 2024-10-16 00:27:57 +02:00
8 changed files with 202 additions and 50 deletions

View file

@ -43,3 +43,15 @@ boot_iso_uefi_secureboot:
-enable-kvm -m 2G -vga qxl -netdev user,id=wan -device virtio-net,netdev=wan,id=nic1 \
-drive file=deploy/iso/workbench_debug.iso,cache=none,if=virtio,format=raw,index=0,media=disk \
-boot menu=on
es_gen_po:
cp locale/es/LC_MESSAGES/messages.po locale/es/LC_MESSAGES/messages.pot.bak
pygettext3 -p locale/es/LC_MESSAGES/ workbench-script.py
# src https://stackoverflow.com/questions/7496156/gettext-how-to-update-po-and-pot-files-after-the-source-is-modified
msgmerge -N locale/es/LC_MESSAGES/messages.pot.bak locale/es/LC_MESSAGES/messages.pot > locale/es/LC_MESSAGES/messages.po
rm locale/es/LC_MESSAGES/messages.pot.bak
rm locale/es/LC_MESSAGES/messages.pot
es_gen_mo:
msgfmt locale/es/LC_MESSAGES/messages.po -o locale/es/LC_MESSAGES/messages.mo

View file

@ -30,26 +30,11 @@ Comentarios sobre el borrado:
## Uso del script
Detalles del uso del script para los técnicos
Detalles del uso del script para técnicos
```
workbench.py [-h] -p PATH [-u URL] [-t TOKEN] [-d DEVICE] [-e {basic,baseline,enhanced}]
El script está diseñado para funcionar por defecto de forma intuitiva y sin configuración previa
OPCIONES
El -p es el unico que es obligatorio el resto son opcionales. Si no pones -e no borra.
-p path
deja el snapshot en el dir path (tal como /mnt)
-e tipo-borrado
tipo de borrado de disco: basic, baseline, enhanced. Nota: bueno si pones -e tampoco borra porque hay que cambiar el script pero es descomentar unas lineas y ya.
-d device
le dices que device quieres borrar y depende de -e que defines el tipo de borrado. Nota: si no le dices -d y solo usas -e borrara todo lo que pille, menos el de boot como dijimos (no probado)
-u tiene que ir con -t y se usa para enviar a una direccion con su token (no probado)
```
Se puede especificar un fichero de configuración con el argumento `--config`, y en `settings.ini.example` se puede encontrar un ejemplo de configuración
## Enfoque

View file

@ -255,11 +255,10 @@ END
prepare_app() {
# prepare app during prepare_chroot_env
# Install hardware_metadata module
workbench_dir="${ISO_PATH}/chroot/opt/workbench"
${SUDO} mkdir -p "${workbench_dir}"
${SUDO} cp workbench-script.py "${workbench_dir}/"
${SUDO} cp requirements.txt "${workbench_dir}/"
${SUDO} cp -arp locale "${workbench_dir}/"
# startup script execution
cat > "${ISO_PATH}/chroot/root/.profile" <<END
@ -301,7 +300,7 @@ echo 'Install requirements'
# Install debian requirements
apt-get install -y --no-install-recommends \
sudo \
sudo locales \
python3 python3-dev python3-pip pipenv \
dmidecode smartmontools hwinfo pciutils lshw nfs-common < /dev/null
@ -355,6 +354,13 @@ apt-get install -y --no-install-recommends \
# Install app
${install_app_str}
# thanks src https://serverfault.com/questions/362903/how-do-you-set-a-locale-non-interactively-on-debian-ubuntu
export LANG=${LANG}
export LC_ALL=${LANG}
# this is a high level command that does locale-gen and update-locale altogether
dpkg-reconfigure --frontend=noninteractive locales
locale -a
# Autologin root user
# src https://wiki.archlinux.org/title/getty#Automatic_login_to_virtual_console
mkdir -p /etc/systemd/system/getty@tty1.service.d/
@ -396,6 +402,7 @@ CHROOT
}
prepare_chroot_env() {
LANG="${CUSTOM_LANG:-es_ES.UTF-8}"
# version of debian the bootstrap is going to build
# if no VERSION_CODENAME is specified we assume that the bootstrap is going to
# be build with the same version of debian being executed because some files

18
install-dependencies.sh Executable file
View file

@ -0,0 +1,18 @@
#!/bin/sh
# Copyright (c) 2024 Pedro <copyright@cas.cat>
# SPDX-License-Identifier: AGPL-3.0-or-later
set -e
set -u
# DEBUG
set -x
main() {
sudo apt install smartmontools lshw hwinfo dmidecode
}
main "${@}"
# written in emacs
# -*- mode: shell-script; -*-

Binary file not shown.

View file

@ -0,0 +1,107 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-15 21:15+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: workbench-script.py:48 workbench-script.py:53
msgid "Running command `%s`"
msgstr "Ejecutando comando `%s`"
#: workbench-script.py:284
msgid "Created snapshots directory at '%s'"
msgstr "Creado directorio de snapshots en '%s'"
#: workbench-script.py:287
msgid "Snapshot written in path '%s'"
msgstr "Snapshot escrito en ruta '%s'"
#: workbench-script.py:290
msgid ""
"Attempting to save file in actual path. Reason: Failed to write in snapshots "
"directory:\n"
" %s."
msgstr ""
"Probando de guardar el fichero en la ruta actual. Motivo: Fallo al intentar "
"escribir en el directorio de snapshots:\n"
" %s."
#: workbench-script.py:297
msgid "Snapshot written in fallback path '%s'"
msgstr "Snapshot escrito en ruta alternativa '%s'"
#: workbench-script.py:299
msgid ""
"Could not save snapshot locally. Reason: Failed to write in fallback path:\n"
" %s"
msgstr ""
"No pude guardar snapshots localmente. Motivo: Fallo al escribir en la ruta "
"alternativa:\n"
" %s"
#: workbench-script.py:316
msgid "Snapshot successfully sent to '%s'"
msgstr "Snapshot enviado con éxito a '%s'"
#: workbench-script.py:331
msgid ""
"Snapshot not remotely sent to URL '%s'. Do you have internet? Is your server "
"up & running? Is the url token authorized?\n"
" %s"
msgstr ""
"Snapshot no enviado remotamente a la URL '%s'. Tienes internet? Está el "
"servidor en marcha? Está autorizado el url token?\n"
" %s"
#: workbench-script.py:342
msgid "Found config file in path: %s."
msgstr "Encontrado fichero de configuración en ruta: %s."
#: workbench-script.py:353
msgid "Config file '%s' not found. Using default values."
msgstr ""
"Fichero de configuración '%s' no encontrado. Utilizando valores por defecto."
#: workbench-script.py:373
msgid "workbench-script.py [-h] [--config CONFIG]"
msgstr ""
#: workbench-script.py:374
msgid "Optional config loader for workbench."
msgstr "Cargador opcional de configuración para workbench"
#: workbench-script.py:377
msgid ""
"path to the config file. Defaults to 'settings.ini' in the current directory."
msgstr ""
"ruta al fichero de configuración. Por defecto es 'settings.ini' en el "
"directorio actual"
#: workbench-script.py:410
msgid "START"
msgstr "INICIO"
#: workbench-script.py:423
msgid ""
"This script must be run as root. Collected data will be incomplete or "
"unusable"
msgstr ""
"Es conveniente que este script sea ejecutado como administrador (root). Los "
"datos recopilados serán incompletos o no usables."
#: workbench-script.py:441
msgid "END"
msgstr "FIN"

View file

@ -1,2 +0,0 @@
ntplib
requests

View file

@ -8,6 +8,10 @@ import argparse
import configparser
import urllib.request
import gettext
import locale
import logging
from datetime import datetime
@ -33,7 +37,7 @@ def logs(f):
try:
return f(*args, **kwargs)
except Exception as err:
print(err)
logger.error(err)
return ''
return wrapper
@ -41,12 +45,12 @@ def logs(f):
@logs
def exec_cmd(cmd):
print(f'workbench: INFO: running command `{cmd}`')
logger.info(_('Running command `%s`'), cmd)
return os.popen(cmd).read()
@logs
def exec_cmd_erase(cmd):
print(cmd)
logger.info(_('Running command `%s`'), cmd)
return ''
# return os.popen(cmd).read()
@ -277,23 +281,22 @@ def save_snapshot_in_disk(snapshot, path):
try:
if not os.path.exists(snapshot_path):
os.makedirs(snapshot_path)
print(f"workbench: INFO: Created snapshots directory at '{snapshot_path}'")
logger.info(_("Created snapshots directory at '%s'"), snapshot_path)
with open(filename, "w") as f:
f.write(json.dumps(snapshot))
print(f"workbench: INFO: Snapshot written in path '{filename}'")
logger.info(_("Snapshot written in path '%s'"), filename)
except Exception as e:
try:
print(f"workbench: WARNING: Attempting to save in actual path. Reason: Failed to write in snapshots directory:\n {e}.")
logger.warning(_("Attempting to save file in actual path. Reason: Failed to write in snapshots directory:\n %s."), e)
fallback_filename = "{}/{}_{}.json".format(
path,
datetime.now().strftime("%Y%m%d-%H_%M_%S"),
snapshot['uuid'])
with open(fallback_filename, "w") as f:
f.write(json.dumps(snapshot))
print(f"workbench: INFO: Snapshot written in fallback path '{fallback_filename}'")
logger.warning(_("Snapshot written in fallback path '%s'"), fallback_filename)
except Exception as e:
print(f"workbench: ERROR: Could not save snapshot locally. Reason: Failed to write in fallback path:\n {e}")
logger.error(_("Could not save snapshot locally. Reason: Failed to write in fallback path:\n %s"), e)
# TODO sanitize url, if url is like this, it fails
# url = 'http://127.0.0.1:8000/api/snapshot/'
@ -310,13 +313,7 @@ def send_snapshot_to_devicehub(snapshot, token, url):
response_text = response.read().decode('utf-8')
if 200 <= status_code < 300:
print(f"workbench: INFO: Snapshot successfully sent to '{url}'")
else:
txt = "workbench: ERROR: Failed to send snapshot. HTTP {}: {}".format(
status_code,
response_text
)
raise Exception(txt)
logger.info(_("Snapshot successfully sent to '%s'"), url)
try:
response = json.loads(response_text)
@ -328,10 +325,10 @@ def send_snapshot_to_devicehub(snapshot, token, url):
if response.get("dhid"):
print("dhid: {}".format(response['dhid']))
except Exception:
print(response_text)
logger.error(response_text)
except Exception as e:
print(f"workbench: ERROR: Snapshot not remotely sent to URL '{url}'. Do you have internet? Is your server up & running? Is the url token authorized?\n {e}")
logger.error(_("Snapshot not remotely sent to URL '%s'. Do you have internet? Is your server up & running? Is the url token authorized?\n %s"), url, e)
def load_config(config_file="settings.ini"):
"""
@ -342,7 +339,7 @@ def load_config(config_file="settings.ini"):
if os.path.exists(config_file):
# If config file exists, read from it
print(f"workbench: INFO: Found config file in path: '{config_file}'.")
logger.info(_("Found config file in path: %s."), config_file)
config.read(config_file)
path = config.get('settings', 'path', fallback=os.getcwd())
# TODO validate that has http:// start
@ -353,7 +350,7 @@ def load_config(config_file="settings.ini"):
erase = config.get('settings', 'erase', fallback=None)
legacy = config.get('settings', 'legacy', fallback=None)
else:
print(f"workbench: ERROR: Config file '{config_file}' not found. Using default values.")
logger.error(_("Config file '%s' not found. Using default values."), config_file)
path = os.path.join(os.getcwd())
url, token, device, erase, legacy = None, None, None, None, None
@ -370,17 +367,45 @@ def parse_args():
"""
Parse config argument, if available
"""
parser = argparse.ArgumentParser(description="Optional config loader for workbench.")
parser = argparse.ArgumentParser(
usage=_("workbench-script.py [-h] [--config CONFIG]"),
description=_("Optional config loader for workbench."))
parser.add_argument(
'--config',
help="Path to the config file. Defaults to 'settings.ini' in the current directory.",
help=_("path to the config file. Defaults to 'settings.ini' in the current directory."),
default="settings.ini" # Fallback to 'settings.ini' by default
)
return parser.parse_args()
def prepare_lang():
locale_path = os.path.join(os.path.dirname(__file__), 'locale')
domain = 'messages'
gettext.bindtextdomain(domain, locale_path)
gettext.textdomain(domain)
global _
# with LANG=es_ES.UTF-8, it detects spanish
_ = gettext.gettext
# # this would force it to spanish
# lang = gettext.translation(domain, localedir=locale_path, languages=['es'])
# lang.install()
# _ = lang.gettext
def prepare_logger():
global logger
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
formatter = logging.Formatter('[%(asctime)s] workbench: %(levelname)s: %(message)s')
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
def main():
vline='\n___________\n\n'
print(f"{vline}workbench: START\n")
prepare_lang()
prepare_logger()
logger.info(_("START"))
# Parse the command-line arguments
args = parse_args()
@ -393,7 +418,7 @@ def main():
# TODO show warning if non root, means data is not complete
# if annotate as potentially invalid snapshot (pending the new API to be done)
if os.geteuid() != 0:
print("workbench: WARNING: This script must be run as root. Collected data will be incomplete or unusable")
logger.warning(_("This script must be run as root. Collected data will be incomplete or unusable"))
all_disks = get_disks()
snapshot = gen_snapshot(all_disks)
@ -405,13 +430,13 @@ def main():
if config.get("legacy"):
convert_to_legacy_snapshot(snapshot)
save_snapshot_in_disk(snapshot, config['path'])
if config['url']:
send_snapshot_to_devicehub(snapshot, config['token'], config['url'])
print(f"\nworkbench: END{vline}")
logger.info(_("END"))
if __name__ == '__main__':