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 \ -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 \ -drive file=deploy/iso/workbench_debug.iso,cache=none,if=virtio,format=raw,index=0,media=disk \
-boot menu=on -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 ## Uso del script
Detalles del uso del script para los técnicos Detalles del uso del script para técnicos
``` El script está diseñado para funcionar por defecto de forma intuitiva y sin configuración previa
workbench.py [-h] -p PATH [-u URL] [-t TOKEN] [-d DEVICE] [-e {basic,baseline,enhanced}]
OPCIONES 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
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)
```
## Enfoque ## Enfoque

View file

@ -255,11 +255,10 @@ END
prepare_app() { prepare_app() {
# prepare app during prepare_chroot_env # prepare app during prepare_chroot_env
# Install hardware_metadata module
workbench_dir="${ISO_PATH}/chroot/opt/workbench" workbench_dir="${ISO_PATH}/chroot/opt/workbench"
${SUDO} mkdir -p "${workbench_dir}" ${SUDO} mkdir -p "${workbench_dir}"
${SUDO} cp workbench-script.py "${workbench_dir}/" ${SUDO} cp workbench-script.py "${workbench_dir}/"
${SUDO} cp requirements.txt "${workbench_dir}/" ${SUDO} cp -arp locale "${workbench_dir}/"
# startup script execution # startup script execution
cat > "${ISO_PATH}/chroot/root/.profile" <<END cat > "${ISO_PATH}/chroot/root/.profile" <<END
@ -301,7 +300,7 @@ echo 'Install requirements'
# Install debian requirements # Install debian requirements
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
sudo \ sudo locales \
python3 python3-dev python3-pip pipenv \ python3 python3-dev python3-pip pipenv \
dmidecode smartmontools hwinfo pciutils lshw nfs-common < /dev/null dmidecode smartmontools hwinfo pciutils lshw nfs-common < /dev/null
@ -355,6 +354,13 @@ apt-get install -y --no-install-recommends \
# Install app # Install app
${install_app_str} ${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 # Autologin root user
# src https://wiki.archlinux.org/title/getty#Automatic_login_to_virtual_console # src https://wiki.archlinux.org/title/getty#Automatic_login_to_virtual_console
mkdir -p /etc/systemd/system/getty@tty1.service.d/ mkdir -p /etc/systemd/system/getty@tty1.service.d/
@ -396,6 +402,7 @@ CHROOT
} }
prepare_chroot_env() { prepare_chroot_env() {
LANG="${CUSTOM_LANG:-es_ES.UTF-8}"
# version of debian the bootstrap is going to build # version of debian the bootstrap is going to build
# if no VERSION_CODENAME is specified we assume that the bootstrap is going to # 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 # 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 configparser
import urllib.request import urllib.request
import gettext
import locale
import logging
from datetime import datetime from datetime import datetime
@ -33,7 +37,7 @@ def logs(f):
try: try:
return f(*args, **kwargs) return f(*args, **kwargs)
except Exception as err: except Exception as err:
print(err) logger.error(err)
return '' return ''
return wrapper return wrapper
@ -41,12 +45,12 @@ def logs(f):
@logs @logs
def exec_cmd(cmd): def exec_cmd(cmd):
print(f'workbench: INFO: running command `{cmd}`') logger.info(_('Running command `%s`'), cmd)
return os.popen(cmd).read() return os.popen(cmd).read()
@logs @logs
def exec_cmd_erase(cmd): def exec_cmd_erase(cmd):
print(cmd) logger.info(_('Running command `%s`'), cmd)
return '' return ''
# return os.popen(cmd).read() # return os.popen(cmd).read()
@ -277,23 +281,22 @@ def save_snapshot_in_disk(snapshot, path):
try: try:
if not os.path.exists(snapshot_path): if not os.path.exists(snapshot_path):
os.makedirs(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: with open(filename, "w") as f:
f.write(json.dumps(snapshot)) 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: except Exception as e:
try: 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( fallback_filename = "{}/{}_{}.json".format(
path, path,
datetime.now().strftime("%Y%m%d-%H_%M_%S"), datetime.now().strftime("%Y%m%d-%H_%M_%S"),
snapshot['uuid']) snapshot['uuid'])
with open(fallback_filename, "w") as f: with open(fallback_filename, "w") as f:
f.write(json.dumps(snapshot)) 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: 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 # TODO sanitize url, if url is like this, it fails
# url = 'http://127.0.0.1:8000/api/snapshot/' # 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') response_text = response.read().decode('utf-8')
if 200 <= status_code < 300: if 200 <= status_code < 300:
print(f"workbench: INFO: Snapshot successfully sent to '{url}'") logger.info(_("Snapshot successfully sent to '%s'"), url)
else:
txt = "workbench: ERROR: Failed to send snapshot. HTTP {}: {}".format(
status_code,
response_text
)
raise Exception(txt)
try: try:
response = json.loads(response_text) response = json.loads(response_text)
@ -328,10 +325,10 @@ def send_snapshot_to_devicehub(snapshot, token, url):
if response.get("dhid"): if response.get("dhid"):
print("dhid: {}".format(response['dhid'])) print("dhid: {}".format(response['dhid']))
except Exception: except Exception:
print(response_text) logger.error(response_text)
except Exception as e: 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"): 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 os.path.exists(config_file):
# If config file exists, read from it # 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) config.read(config_file)
path = config.get('settings', 'path', fallback=os.getcwd()) path = config.get('settings', 'path', fallback=os.getcwd())
# TODO validate that has http:// start # TODO validate that has http:// start
@ -353,7 +350,7 @@ def load_config(config_file="settings.ini"):
erase = config.get('settings', 'erase', fallback=None) erase = config.get('settings', 'erase', fallback=None)
legacy = config.get('settings', 'legacy', fallback=None) legacy = config.get('settings', 'legacy', fallback=None)
else: 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()) path = os.path.join(os.getcwd())
url, token, device, erase, legacy = None, None, None, None, None url, token, device, erase, legacy = None, None, None, None, None
@ -370,17 +367,45 @@ def parse_args():
""" """
Parse config argument, if available 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( parser.add_argument(
'--config', '--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 default="settings.ini" # Fallback to 'settings.ini' by default
) )
return parser.parse_args() 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(): def main():
vline='\n___________\n\n' prepare_lang()
print(f"{vline}workbench: START\n") prepare_logger()
logger.info(_("START"))
# Parse the command-line arguments # Parse the command-line arguments
args = parse_args() args = parse_args()
@ -393,7 +418,7 @@ def main():
# TODO show warning if non root, means data is not complete # 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 annotate as potentially invalid snapshot (pending the new API to be done)
if os.geteuid() != 0: 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() all_disks = get_disks()
snapshot = gen_snapshot(all_disks) snapshot = gen_snapshot(all_disks)
@ -411,7 +436,7 @@ def main():
if config['url']: if config['url']:
send_snapshot_to_devicehub(snapshot, config['token'], config['url']) send_snapshot_to_devicehub(snapshot, config['token'], config['url'])
print(f"\nworkbench: END{vline}") logger.info(_("END"))
if __name__ == '__main__': if __name__ == '__main__':