Compare commits
4 commits
2020e15b5e
...
19e3801d2c
| Author | SHA1 | Date | |
|---|---|---|---|
| 19e3801d2c | |||
| 4b988bbe46 | |||
| 78e7cf55c6 | |||
| 9876a03ac7 |
3 changed files with 400 additions and 101 deletions
|
|
@ -735,8 +735,14 @@ def build_cloud_config(
|
|||
f" layout: {args.keyboard_layout}",
|
||||
f" variant: {args.keyboard_variant}",
|
||||
]
|
||||
lines.append("package_update: true")
|
||||
lines.append(f"package_upgrade: {'false' if args.no_upgrade else 'true'}")
|
||||
# apt update/upgrade désactivés par défaut : sur un réseau lent/instable
|
||||
# ils font pendre cloud-init au 1er boot (et retardent la dispo SSH). SSH
|
||||
# est déjà présent dans les images cloud ; on l'active via runcmd sans apt.
|
||||
# --apt-update réactive apt update (+ upgrade, sauf --no-upgrade).
|
||||
do_update = args.apt_update
|
||||
do_upgrade = args.apt_update and not args.no_upgrade
|
||||
lines.append(f"package_update: {'true' if do_update else 'false'}")
|
||||
lines.append(f"package_upgrade: {'true' if do_upgrade else 'false'}")
|
||||
|
||||
# openssh-server : garantit un serveur SSH sur toutes les distros (les
|
||||
# images Debian genericcloud notamment ne l'ont pas toujours activé).
|
||||
|
|
@ -1102,6 +1108,13 @@ def build_parser() -> argparse.ArgumentParser:
|
|||
action="store_true",
|
||||
help="N'exécute pas package_upgrade au premier boot.",
|
||||
)
|
||||
g_cloud.add_argument(
|
||||
"--apt-update",
|
||||
action="store_true",
|
||||
help="Exécute « apt update » au 1er boot (package_update). Désactivé "
|
||||
"par défaut : évite que cloud-init se bloque sur un miroir lent/"
|
||||
"injoignable (SSH est déjà présent dans les images cloud).",
|
||||
)
|
||||
|
||||
g_run = p.add_argument_group("Exécution")
|
||||
g_run.add_argument(
|
||||
|
|
|
|||
|
|
@ -197,21 +197,33 @@ class TODO:
|
|||
|
||||
def prompt_execute(self):
|
||||
help_info = f"""{self._menu_header()}
|
||||
[1] {t("Automation - Demonstration of developed features")}
|
||||
[2] {t("Code - Developer tools")}
|
||||
[3] {t("Config - Configuration file management")}
|
||||
[4] {t("Database - Database tools")}
|
||||
[5] {t("Doc - Documentation search")}
|
||||
[6] {t("Git - Git tools")}
|
||||
[7] {t("GPT code - AI assistant tools")}
|
||||
[8] {t("Language - Change language / Changer la langue")}
|
||||
[9] {t("Network - Network tools")}
|
||||
[10] {t("Process - Execution tools")}
|
||||
[11] {t("Run - Execute and install an instance")}
|
||||
[12] {t("Security - Dependency security audit")}
|
||||
[13] {t("Test - Test an Odoo module")}
|
||||
[14] {t("Update - Update all developed staging source code")}
|
||||
[15] {t("Deploy - Deploy ERPLibre locally")}
|
||||
|
||||
── {t("Development")} ──
|
||||
[1] {t("Code - Developer tools")}
|
||||
[2] {t("Config - Configuration file management")}
|
||||
[3] {t("Run - Execute and install an instance")}
|
||||
[4] {t("Test - Test an Odoo module")}
|
||||
[5] {t("Process - Execution tools")}
|
||||
|
||||
── {t("Data")} ──
|
||||
[6] {t("Database - Database tools")}
|
||||
|
||||
── {t("Sources & documentation")} ──
|
||||
[7] {t("Git - Git tools")}
|
||||
[8] {t("Update - Update all developed staging source code")}
|
||||
[9] {t("Doc - Documentation search")}
|
||||
|
||||
── {t("AI & automation")} ──
|
||||
[10] {t("GPT code - AI assistant tools")}
|
||||
[11] {t("Automation - Demonstration of developed features")}
|
||||
|
||||
── {t("Deployment, network & security")} ──
|
||||
[12] {t("Deploy - Deploy ERPLibre locally")}
|
||||
[13] {t("Network - Network tools")}
|
||||
[14] {t("Security - Dependency security audit")}
|
||||
|
||||
── {t("Preferences")} ──
|
||||
[15] {t("Language - Change language / Changer la langue")}
|
||||
[0] {t("Back")}
|
||||
"""
|
||||
while True:
|
||||
|
|
@ -220,65 +232,65 @@ class TODO:
|
|||
if status == "0":
|
||||
return
|
||||
elif status == "1":
|
||||
status = self.prompt_execute_function()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "2":
|
||||
status = self.prompt_execute_code()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "3":
|
||||
elif status == "2":
|
||||
status = self.prompt_execute_config()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "4":
|
||||
status = self.prompt_execute_database()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "5":
|
||||
status = self.prompt_execute_doc()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "6":
|
||||
status = self.prompt_execute_git()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "7":
|
||||
status = self.prompt_execute_gpt_code()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "8":
|
||||
status = self._change_language()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "9":
|
||||
status = self.prompt_execute_network()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "10":
|
||||
status = self.prompt_execute_process()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "11":
|
||||
elif status == "3":
|
||||
status = self.prompt_execute_instance()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "12":
|
||||
status = self.prompt_execute_security()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "13":
|
||||
elif status == "4":
|
||||
status = self.prompt_execute_test()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "14":
|
||||
elif status == "5":
|
||||
status = self.prompt_execute_process()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "6":
|
||||
status = self.prompt_execute_database()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "7":
|
||||
status = self.prompt_execute_git()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "8":
|
||||
status = self.prompt_execute_update()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "15":
|
||||
elif status == "9":
|
||||
status = self.prompt_execute_doc()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "10":
|
||||
status = self.prompt_execute_gpt_code()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "11":
|
||||
status = self.prompt_execute_function()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "12":
|
||||
status = self.prompt_execute_deploy()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "13":
|
||||
status = self.prompt_execute_network()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "14":
|
||||
status = self.prompt_execute_security()
|
||||
if status is not False:
|
||||
return
|
||||
elif status == "15":
|
||||
status = self._change_language()
|
||||
if status is not False:
|
||||
return
|
||||
else:
|
||||
print(t("Command not found !"))
|
||||
|
||||
|
|
@ -854,6 +866,7 @@ class TODO:
|
|||
print(f"{t('QEMU deploy script not found: ')}{script_path}")
|
||||
return False
|
||||
choices = [
|
||||
{"section": t("Deployment")},
|
||||
{"prompt_description": t("Deploy a new VM")},
|
||||
{
|
||||
"prompt_description": t(
|
||||
|
|
@ -861,15 +874,18 @@ class TODO:
|
|||
)
|
||||
},
|
||||
{"prompt_description": t("Download a cloud image only")},
|
||||
{"prompt_description": t("List VMs (virsh list --all)")},
|
||||
{"prompt_description": t("Show a VM IP address")},
|
||||
{"prompt_description": t("List available images and specs")},
|
||||
{
|
||||
"prompt_description": t(
|
||||
"Deploy ERPLibre infra (one minimal VM per image)"
|
||||
)
|
||||
},
|
||||
{"section": t("Manage")},
|
||||
{"prompt_description": t("List VMs (virsh list --all)")},
|
||||
{"prompt_description": t("Show a VM IP address")},
|
||||
{"prompt_description": t("Open the console on a VM")},
|
||||
{"prompt_description": t("Delete VM(s)")},
|
||||
{"section": t("Catalog")},
|
||||
{"prompt_description": t("List available images and specs")},
|
||||
]
|
||||
config_entries = self.config_file.get_config("qemu_from_makefile")
|
||||
if config_entries:
|
||||
|
|
@ -888,23 +904,27 @@ class TODO:
|
|||
elif status == "3":
|
||||
self._qemu_download_image()
|
||||
elif status == "4":
|
||||
self._qemu_list_vms()
|
||||
elif status == "5":
|
||||
self._qemu_show_ip()
|
||||
elif status == "6":
|
||||
self._qemu_list_images()
|
||||
elif status == "7":
|
||||
self._qemu_deploy_infra()
|
||||
elif status == "5":
|
||||
self._qemu_list_vms()
|
||||
elif status == "6":
|
||||
self._qemu_show_ip()
|
||||
elif status == "7":
|
||||
self._qemu_console()
|
||||
elif status == "8":
|
||||
self._qemu_delete_vm()
|
||||
elif status == "9":
|
||||
self._qemu_list_images()
|
||||
else:
|
||||
cmd_no_found = True
|
||||
try:
|
||||
int_cmd = int(status)
|
||||
if 0 < int_cmd <= len(choices):
|
||||
# Ignore les entrées de section pour mapper le numéro
|
||||
# affiché sur la bonne commande (config incluse).
|
||||
real = [c for c in choices if not c.get("section")]
|
||||
if 0 < int_cmd <= len(real):
|
||||
cmd_no_found = False
|
||||
instance = choices[int_cmd - 1]
|
||||
self.execute_from_configuration(instance)
|
||||
self.execute_from_configuration(real[int_cmd - 1])
|
||||
except ValueError:
|
||||
pass
|
||||
if cmd_no_found:
|
||||
|
|
@ -934,11 +954,15 @@ class TODO:
|
|||
# sous sudo (HOME=/root) et ne pourrait pas déduire ~ correctement.
|
||||
ssh_key = os.path.expanduser(ssh_key)
|
||||
|
||||
use_password = False
|
||||
if not ssh_key:
|
||||
ans = input(t("No SSH key found. Set a password instead? (Y/n): "))
|
||||
# Défaut oui : tout sauf une réponse négative explicite vaut oui.
|
||||
use_password = not self._is_no(ans)
|
||||
# Mot de passe console (défaut « erplibre ») : permet la connexion par
|
||||
# la console série (virsh console) EN PLUS de la clé SSH. Sans lui, le
|
||||
# compte est verrouillé et la console refuse tout login.
|
||||
password = (
|
||||
input(
|
||||
t("Console password for 'erplibre' (default: erplibre): ")
|
||||
).strip()
|
||||
or "erplibre"
|
||||
)
|
||||
|
||||
force = False
|
||||
if not dry_run:
|
||||
|
|
@ -958,8 +982,8 @@ class TODO:
|
|||
parts += ["--disk-size", disk_size]
|
||||
if ssh_key:
|
||||
parts += ["--ssh-key", ssh_key]
|
||||
if use_password:
|
||||
parts.append("--ask-password")
|
||||
if password:
|
||||
parts += ["--password", password]
|
||||
if force:
|
||||
parts.append("--force")
|
||||
if dry_run:
|
||||
|
|
@ -969,8 +993,14 @@ class TODO:
|
|||
|
||||
cmd = " ".join(shlex.quote(p) for p in parts)
|
||||
print(f"{t('Will execute:')} {cmd}")
|
||||
if password:
|
||||
print(f"🔑 {t('Console/SSH login:')} erplibre / {password}")
|
||||
self.execute.exec_command_live(cmd, source_erplibre=False)
|
||||
|
||||
# Proposer d'enregistrer la VM dans ~/.ssh/config (après le 1er boot).
|
||||
if not dry_run:
|
||||
self._qemu_offer_ssh_config(name, "erplibre")
|
||||
|
||||
def _qemu_download_image(self):
|
||||
script_path = self._qemu_script_path()
|
||||
distro = self._qemu_prompt_distro()
|
||||
|
|
@ -998,17 +1028,81 @@ class TODO:
|
|||
|
||||
def _qemu_show_ip(self):
|
||||
# Affiche d'abord les VM (avec leur ID) pour que l'utilisateur sache
|
||||
# quel nom/ID saisir, puis demande lequel.
|
||||
# quel nom/ID saisir, puis demande lequel (ou « all » pour toutes).
|
||||
self._qemu_list_vms()
|
||||
print()
|
||||
name = input(t("VM name or ID (or 'all'): ")).strip()
|
||||
if not name:
|
||||
print(t("VM name is required!"))
|
||||
return
|
||||
if name.lower() in ("all", "tous", "*"):
|
||||
targets = self._qemu_list_domains()
|
||||
if not targets:
|
||||
print(t("No VM found."))
|
||||
return
|
||||
else:
|
||||
targets = [name]
|
||||
for tgt in targets:
|
||||
cmd = f"sudo virsh domifaddr {shlex.quote(tgt)} --source lease"
|
||||
print(f"\n{t('Will execute:')} {cmd}")
|
||||
self.execute.exec_command_live(cmd, source_erplibre=False)
|
||||
|
||||
def _qemu_console(self):
|
||||
# Liste les VM, demande laquelle, rappelle comment quitter (Ctrl+])
|
||||
# puis ouvre la console série interactive.
|
||||
self._qemu_list_vms()
|
||||
print()
|
||||
name = input(t("VM name or ID: ")).strip()
|
||||
if not name:
|
||||
print(t("VM name is required!"))
|
||||
return
|
||||
cmd = f"sudo virsh domifaddr {shlex.quote(name)} --source lease"
|
||||
print(f"\n💡 {t('To leave the console, press Ctrl+] (then Enter).')}")
|
||||
print(
|
||||
f"👤 {t('Default login (if set at deploy): erplibre / erplibre')}"
|
||||
)
|
||||
cmd = f"sudo virsh console {shlex.quote(name)}"
|
||||
print(f"{t('Will execute:')} {cmd}")
|
||||
self.execute.exec_command_live(cmd, source_erplibre=False)
|
||||
|
||||
def _qemu_offer_ssh_config(self, name, user):
|
||||
"""Propose d'enregistrer la VM dans ~/.ssh/config (bloc Host name)."""
|
||||
if not self._is_yes(input(t("Add this VM to ~/.ssh/config? (y/N): "))):
|
||||
return
|
||||
print(t("Waiting for the VM IP (DHCP lease)..."))
|
||||
ip = self._qemu_vm_ip(name)
|
||||
if not ip:
|
||||
print(t("No IP yet; add it later once the VM has booted."))
|
||||
return
|
||||
self._write_ssh_config_entry(name, user, ip)
|
||||
|
||||
def _write_ssh_config_entry(self, host, user, ip):
|
||||
"""Écrit/remplace un bloc « Host <host> » dans ~/.ssh/config."""
|
||||
cfg = os.path.expanduser("~/.ssh/config")
|
||||
os.makedirs(os.path.dirname(cfg), exist_ok=True)
|
||||
existing = ""
|
||||
if os.path.exists(cfg):
|
||||
with open(cfg, encoding="utf-8") as fh:
|
||||
existing = fh.read()
|
||||
# Retire un ancien bloc du même Host (jusqu'au prochain Host / EOF).
|
||||
pattern = re.compile(
|
||||
rf"(?ms)^[ \t]*Host[ \t]+{re.escape(host)}[ \t]*\n"
|
||||
r"(?:[ \t]+.*\n?)*"
|
||||
)
|
||||
existing = pattern.sub("", existing).rstrip("\n")
|
||||
block = (
|
||||
f"Host {host}\n"
|
||||
f" HostName {ip}\n"
|
||||
f" User {user}\n"
|
||||
# IP DHCP réutilisées entre VM -> on évite l'erreur de clé d'hôte.
|
||||
f" StrictHostKeyChecking no\n"
|
||||
f" UserKnownHostsFile /dev/null\n"
|
||||
)
|
||||
content = (existing + "\n\n" + block) if existing else block
|
||||
with open(cfg, "w", encoding="utf-8") as fh:
|
||||
fh.write(content)
|
||||
os.chmod(cfg, 0o600)
|
||||
print(f"✅ {t('Added to ~/.ssh/config:')} ssh {host}")
|
||||
|
||||
def _qemu_list_domains(self):
|
||||
"""Noms des VM libvirt définies (via virsh)."""
|
||||
try:
|
||||
|
|
@ -1335,7 +1429,21 @@ class TODO:
|
|||
if self._is_yes(ans):
|
||||
install_branch = self._qemu_pick_branch()
|
||||
|
||||
# 5) Confirmation puis déploiement séquentiel.
|
||||
add_ssh_config = self._is_yes(
|
||||
input(t("Add each VM to ~/.ssh/config? (y/N): "))
|
||||
)
|
||||
|
||||
# Nombre de déploiements en parallèle (défaut : min(nb VM, 4)).
|
||||
default_par = min(len(selected), 4)
|
||||
raw = input(
|
||||
f"{t('Parallel deployments (default:')} {default_par}): "
|
||||
).strip()
|
||||
try:
|
||||
parallelism = max(1, int(raw)) if raw else default_par
|
||||
except ValueError:
|
||||
parallelism = default_par
|
||||
|
||||
# 5) Confirmation puis déploiement (en parallèle).
|
||||
ans = input(f"\n{t('Deploy these VMs now? (y/N): ')}")
|
||||
if not self._is_yes(ans):
|
||||
print(t("Cancelled."))
|
||||
|
|
@ -1343,10 +1451,11 @@ class TODO:
|
|||
|
||||
script_path = self._qemu_script_path()
|
||||
deployed = []
|
||||
jobs = [] # (name, parts) des VM à créer
|
||||
for d, v, _ram, _disk in selected:
|
||||
name = self._qemu_infra_name(d, v)
|
||||
if self._qemu_domain_exists(name):
|
||||
print(f"\n⏭ {name}: {t('already exists, skipped.')}")
|
||||
print(f"⏭ {name}: {t('already exists, skipped.')}")
|
||||
deployed.append(name)
|
||||
continue
|
||||
parts = [
|
||||
|
|
@ -1358,24 +1467,64 @@ class TODO:
|
|||
v,
|
||||
"--name",
|
||||
name,
|
||||
# Mot de passe console « erplibre » (+ clé SSH). --no-wait-ip :
|
||||
# ne bloque pas 90s par VM, l'IP est collectée après coup.
|
||||
"--password",
|
||||
"erplibre",
|
||||
"--no-wait-ip",
|
||||
]
|
||||
if ssh_key:
|
||||
parts += ["--ssh-key", ssh_key]
|
||||
if install_branch:
|
||||
parts += ["--package", "git"]
|
||||
parts.append("-y")
|
||||
cmd = " ".join(shlex.quote(p) for p in parts)
|
||||
print(f"\n▶ {name}: {cmd}")
|
||||
self.execute.exec_command_live(cmd, source_erplibre=False)
|
||||
deployed.append(name)
|
||||
jobs.append((name, parts))
|
||||
|
||||
# 6) Installation ERPLibre (clone) si demandée.
|
||||
if jobs:
|
||||
from concurrent.futures import (
|
||||
ThreadPoolExecutor,
|
||||
as_completed,
|
||||
)
|
||||
|
||||
workers = min(parallelism, len(jobs))
|
||||
print(
|
||||
f"\n{t('Deploying')} {len(jobs)} VM "
|
||||
f"({t('parallel jobs:')} {workers})…"
|
||||
)
|
||||
|
||||
def _run(job):
|
||||
jname, jparts = job
|
||||
res = subprocess.run(jparts, capture_output=True, text=True)
|
||||
out = (res.stdout or "") + (res.stderr or "")
|
||||
return jname, res.returncode, out
|
||||
|
||||
with ThreadPoolExecutor(max_workers=workers) as pool:
|
||||
futures = [pool.submit(_run, j) for j in jobs]
|
||||
for fut in as_completed(futures):
|
||||
jname, rc, out = fut.result()
|
||||
mark = "✅" if rc == 0 else "❌"
|
||||
print(f"\n{mark} {jname} (rc={rc})")
|
||||
tail = [ln for ln in out.strip().splitlines() if ln][-4:]
|
||||
for ln in tail:
|
||||
print(f" {ln}")
|
||||
if rc == 0:
|
||||
deployed.append(jname)
|
||||
|
||||
# 6) ~/.ssh/config (une fois les VM démarrées et l'IP attribuée).
|
||||
if add_ssh_config:
|
||||
for name in deployed:
|
||||
ip = self._qemu_vm_ip(name)
|
||||
if ip:
|
||||
self._write_ssh_config_entry(name, "erplibre", ip)
|
||||
|
||||
# 7) Installation ERPLibre (clone) si demandée.
|
||||
if install_branch:
|
||||
print(f"\n{t('Cloning ERPLibre on each VM')} ({install_branch})…")
|
||||
for name in deployed:
|
||||
self._qemu_install_erplibre_vm(name, ssh_key, install_branch)
|
||||
|
||||
print(f"\n✅ {t('ERPLibre infra deployment done.')}")
|
||||
print(f" {t('Default login:')} erplibre / erplibre")
|
||||
print(f" {t('Manage with:')} sudo virsh list --all")
|
||||
|
||||
def _deploy_clone_erplibre(self):
|
||||
|
|
@ -2140,13 +2289,16 @@ class TODO:
|
|||
def prompt_execute_database(self):
|
||||
print(f"🤖 {t('Make changes to databases!')}")
|
||||
choices = [
|
||||
{"section": t("Backup")},
|
||||
{"prompt_description": t("Create backup (.zip)")},
|
||||
{
|
||||
"prompt_description": t(
|
||||
"Download database to create backup (.zip)"
|
||||
)
|
||||
},
|
||||
{"section": t("Restore")},
|
||||
{"prompt_description": t("Restore from backup (.zip)")},
|
||||
{"prompt_description": t("Create backup (.zip)")},
|
||||
{"section": t("Danger zone")},
|
||||
{"prompt_description": t("Erase a database")},
|
||||
]
|
||||
help_info = self.fill_help_info(choices)
|
||||
|
|
@ -2157,11 +2309,11 @@ class TODO:
|
|||
if status == "0":
|
||||
return False
|
||||
elif status == "1":
|
||||
self.db_manager.download_database_backup_cli()
|
||||
elif status == "2":
|
||||
self.db_manager.restore_from_database()
|
||||
elif status == "3":
|
||||
self.db_manager.create_backup_from_database()
|
||||
elif status == "2":
|
||||
self.db_manager.download_database_backup_cli()
|
||||
elif status == "3":
|
||||
self.db_manager.restore_from_database()
|
||||
elif status == "4":
|
||||
self.db_manager.drop_database()
|
||||
else:
|
||||
|
|
@ -2199,12 +2351,15 @@ class TODO:
|
|||
f"🤖 {t('Manage RTK (Rust Token Killer) for token optimization!')}"
|
||||
)
|
||||
choices = [
|
||||
{"section": t("Setup")},
|
||||
{"prompt_description": t("Install RTK")},
|
||||
{"prompt_description": t("Check RTK version")},
|
||||
{"prompt_description": t("Show cumulative token savings")},
|
||||
{"prompt_description": t("Discover optimization opportunities")},
|
||||
{"prompt_description": t("Initialize global auto-rewrite hook")},
|
||||
{"section": t("Status")},
|
||||
{"prompt_description": t("Check RTK version")},
|
||||
{"prompt_description": t("Check RTK status")},
|
||||
{"prompt_description": t("Show cumulative token savings")},
|
||||
{"section": t("Optimize")},
|
||||
{"prompt_description": t("Discover optimization opportunities")},
|
||||
]
|
||||
help_info = self.fill_help_info(choices)
|
||||
|
||||
|
|
@ -2216,15 +2371,15 @@ class TODO:
|
|||
elif status == "1":
|
||||
self.rtk_install()
|
||||
elif status == "2":
|
||||
self.rtk_check_version()
|
||||
elif status == "3":
|
||||
self.rtk_show_gain()
|
||||
elif status == "4":
|
||||
self.rtk_discover()
|
||||
elif status == "5":
|
||||
self.rtk_init_global()
|
||||
elif status == "6":
|
||||
elif status == "3":
|
||||
self.rtk_check_version()
|
||||
elif status == "4":
|
||||
self.rtk_check_status()
|
||||
elif status == "5":
|
||||
self.rtk_show_gain()
|
||||
elif status == "6":
|
||||
self.rtk_discover()
|
||||
else:
|
||||
print(t("Command not found !"))
|
||||
|
||||
|
|
@ -2313,10 +2468,12 @@ class TODO:
|
|||
def prompt_execute_config(self):
|
||||
print(f"🤖 {t('Manage ERPLibre and Odoo configuration!')}")
|
||||
choices = [
|
||||
{"section": t("Generate")},
|
||||
{"prompt_description": t("Generate all configuration")},
|
||||
{"prompt_description": t("Generate from pre-configuration")},
|
||||
{"prompt_description": t("Generate from backup file")},
|
||||
{"prompt_description": t("Generate from database")},
|
||||
{"section": t("Advanced")},
|
||||
{"prompt_description": t("Setup queue job for parallelism")},
|
||||
]
|
||||
help_info = self.fill_help_info(choices)
|
||||
|
|
|
|||
|
|
@ -153,6 +153,119 @@ TRANSLATIONS = {
|
|||
"fr": "Virtualisation & notifications",
|
||||
"en": "Virtualization & notifications",
|
||||
},
|
||||
"Development": {
|
||||
"fr": "Développement",
|
||||
"en": "Development",
|
||||
},
|
||||
"Data": {
|
||||
"fr": "Données",
|
||||
"en": "Data",
|
||||
},
|
||||
"Sources & documentation": {
|
||||
"fr": "Sources & documentation",
|
||||
"en": "Sources & documentation",
|
||||
},
|
||||
"AI & automation": {
|
||||
"fr": "IA & automatisation",
|
||||
"en": "AI & automation",
|
||||
},
|
||||
"Deployment, network & security": {
|
||||
"fr": "Déploiement, réseau & sécurité",
|
||||
"en": "Deployment, network & security",
|
||||
},
|
||||
"Preferences": {
|
||||
"fr": "Préférences",
|
||||
"en": "Preferences",
|
||||
},
|
||||
"Deployment": {
|
||||
"fr": "Déploiement",
|
||||
"en": "Deployment",
|
||||
},
|
||||
"Manage": {
|
||||
"fr": "Gérer",
|
||||
"en": "Manage",
|
||||
},
|
||||
"Catalog": {
|
||||
"fr": "Catalogue",
|
||||
"en": "Catalog",
|
||||
},
|
||||
"Open the console on a VM": {
|
||||
"fr": "Ouvrir la console d'une VM",
|
||||
"en": "Open the console on a VM",
|
||||
},
|
||||
"Backup": {
|
||||
"fr": "Sauvegarde",
|
||||
"en": "Backup",
|
||||
},
|
||||
"Restore": {
|
||||
"fr": "Restauration",
|
||||
"en": "Restore",
|
||||
},
|
||||
"Danger zone": {
|
||||
"fr": "Zone dangereuse",
|
||||
"en": "Danger zone",
|
||||
},
|
||||
"Setup": {
|
||||
"fr": "Installation",
|
||||
"en": "Setup",
|
||||
},
|
||||
"Status": {
|
||||
"fr": "Statut",
|
||||
"en": "Status",
|
||||
},
|
||||
"Optimize": {
|
||||
"fr": "Optimisation",
|
||||
"en": "Optimize",
|
||||
},
|
||||
"Generate": {
|
||||
"fr": "Génération",
|
||||
"en": "Generate",
|
||||
},
|
||||
"Advanced": {
|
||||
"fr": "Avancé",
|
||||
"en": "Advanced",
|
||||
},
|
||||
"To leave the console, press Ctrl+] (then Enter).": {
|
||||
"fr": "Pour quitter la console, appuyez sur Ctrl+] (puis Entrée).",
|
||||
"en": "To leave the console, press Ctrl+] (then Enter).",
|
||||
},
|
||||
"Default login (if set at deploy): erplibre / erplibre": {
|
||||
"fr": "Login par défaut (si défini au déploiement) : "
|
||||
"erplibre / erplibre",
|
||||
"en": "Default login (if set at deploy): erplibre / erplibre",
|
||||
},
|
||||
"Console password for 'erplibre' (default: erplibre): ": {
|
||||
"fr": "Mot de passe console pour « erplibre » (défaut : erplibre) : ",
|
||||
"en": "Console password for 'erplibre' (default: erplibre): ",
|
||||
},
|
||||
"Console/SSH login:": {
|
||||
"fr": "Connexion console/SSH :",
|
||||
"en": "Console/SSH login:",
|
||||
},
|
||||
"Default login:": {
|
||||
"fr": "Login par défaut :",
|
||||
"en": "Default login:",
|
||||
},
|
||||
"Add this VM to ~/.ssh/config? (y/N): ": {
|
||||
"fr": "Ajouter cette VM à ~/.ssh/config ? (o/N) : ",
|
||||
"en": "Add this VM to ~/.ssh/config? (y/N): ",
|
||||
},
|
||||
"Add each VM to ~/.ssh/config? (y/N): ": {
|
||||
"fr": "Ajouter chaque VM à ~/.ssh/config ? (o/N) : ",
|
||||
"en": "Add each VM to ~/.ssh/config? (y/N): ",
|
||||
},
|
||||
"Waiting for the VM IP (DHCP lease)...": {
|
||||
"fr": "Attente de l'IP de la VM (bail DHCP)...",
|
||||
"en": "Waiting for the VM IP (DHCP lease)...",
|
||||
},
|
||||
"No IP yet; add it later once the VM has booted.": {
|
||||
"fr": "Pas encore d'IP ; à ajouter plus tard une fois la VM démarrée.",
|
||||
"en": "No IP yet; add it later once the VM has booted.",
|
||||
},
|
||||
"Added to ~/.ssh/config:": {
|
||||
"fr": "Ajouté à ~/.ssh/config :",
|
||||
"en": "Added to ~/.ssh/config:",
|
||||
},
|
||||
"SSH address input method": {
|
||||
"fr": "Méthode de saisie de l'adresse SSH",
|
||||
"en": "SSH address input method",
|
||||
|
|
@ -1174,6 +1287,18 @@ TRANSLATIONS = {
|
|||
"fr": "Clonage d'ERPLibre sur chaque VM",
|
||||
"en": "Cloning ERPLibre on each VM",
|
||||
},
|
||||
"Parallel deployments (default:": {
|
||||
"fr": "Déploiements en parallèle (défaut :",
|
||||
"en": "Parallel deployments (default:",
|
||||
},
|
||||
"Deploying": {
|
||||
"fr": "Déploiement de",
|
||||
"en": "Deploying",
|
||||
},
|
||||
"parallel jobs:": {
|
||||
"fr": "tâches parallèles :",
|
||||
"en": "parallel jobs:",
|
||||
},
|
||||
"ERPLibre infra deployment done.": {
|
||||
"fr": "Déploiement de l'infra ERPLibre terminé.",
|
||||
"en": "ERPLibre infra deployment done.",
|
||||
|
|
@ -1222,6 +1347,10 @@ TRANSLATIONS = {
|
|||
"fr": "Nom ou ID de la VM : ",
|
||||
"en": "VM name or ID: ",
|
||||
},
|
||||
"VM name or ID (or 'all'): ": {
|
||||
"fr": "Nom ou ID de la VM (ou « all ») : ",
|
||||
"en": "VM name or ID (or 'all'): ",
|
||||
},
|
||||
"RAM in MB (blank = version minimum): ": {
|
||||
"fr": "RAM en Mo (vide = minimum de la version) : ",
|
||||
"en": "RAM in MB (blank = version minimum): ",
|
||||
|
|
|
|||
Loading…
Reference in a new issue