Compare commits
3 commits
9954f70e6c
...
f946a566a2
Author | SHA1 | Date | |
---|---|---|---|
f946a566a2 | |||
a3644afc2b | |||
a2e33d2b13 |
12
Makefile
12
Makefile
|
@ -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
|
||||
|
|
21
README-es.md
21
README-es.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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
18
install-dependencies.sh
Executable 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; -*-
|
BIN
locale/es/LC_MESSAGES/messages.mo
Normal file
BIN
locale/es/LC_MESSAGES/messages.mo
Normal file
Binary file not shown.
107
locale/es/LC_MESSAGES/messages.po
Normal file
107
locale/es/LC_MESSAGES/messages.po
Normal 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"
|
|
@ -1,2 +0,0 @@
|
|||
ntplib
|
||||
requests
|
|
@ -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__':
|
||||
|
|
Loading…
Reference in a new issue