i18n: complete OCI runtime and maintenance Slovak diagnostics

This commit is contained in:
VAIO73
2026-10-21 07:06:04 +00:00
parent 37a6c02111
commit 6c05688196
15 changed files with 746 additions and 337 deletions
+2 -2
View File
@@ -93,8 +93,8 @@ def main() -> None:
rootfs = Path(sys.argv[1])
configuration = json.loads(sys.argv[2])
path, changed = update_encoding(rootfs, configuration)
state = "updated" if changed else "already applied"
print(f"Jellyfin configuration {state}: /{path.relative_to(rootfs.resolve())}")
state = translate("Jellyfin configuration updated:") if changed else translate("Jellyfin configuration already applied:")
print(f"{state} /{path.relative_to(rootfs.resolve())}")
if __name__ == "__main__":
+4 -2
View File
@@ -20,6 +20,7 @@ import re
import subprocess
import sys
import time
from oci_ui import translate
CONFIGS = Path('/etc/pve/lxc')
HOOKS = Path('/var/lib/lxc')
@@ -121,12 +122,13 @@ def check(vmid, settle=0):
except BlockingIOError:
return
for name, version in repair(vmid, settle):
print(f'CT {vmid}: DHCP client of {name} (IPv{version}) started again', flush=True)
print(translate('CT {vmid}: DHCP client of {name} (IPv{version}) started again').format(
vmid=vmid, name=name, version=version), flush=True)
def main():
if len(sys.argv) != 2 or not (sys.argv[1].isdigit() or sys.argv[1] == '--all'):
print('usage: oci_dhcp_lease.py <vmid> | --all', file=sys.stderr)
print(translate('Usage:') + ' oci_dhcp_lease.py <vmid> | --all', file=sys.stderr)
return 2
LOCKS.mkdir(parents=True, exist_ok=True)
if sys.argv[1] == '--all':
+4 -3
View File
@@ -10,6 +10,7 @@ import sys
import time
import oci_instances as instances
from oci_ui import translate
# Names given by the OCI installers and by updates; other archives are never touched.
NAME = re.compile(r'(?:proxmenux-update-[a-z0-9]+-[0-9a-f]{64}'
@@ -82,10 +83,10 @@ def main(arguments):
command = arguments[0] if arguments else 'prune'
if command == 'prune':
for path, size in prune():
print(f'removed unused image archive: {path} ({size} bytes)')
print(translate('removed unused image archive: {path} ({size} bytes)').format(path=path, size=size))
return 0
if command not in ('list', 'remove') or not all(a.isdigit() for a in arguments[1:]):
print('usage: oci_image_cache.py [prune | list VMID... | remove VMID...]', file=sys.stderr)
print(translate('Usage:') + ' oci_image_cache.py [prune | list VMID... | remove VMID...]', file=sys.stderr)
return 2
with instances.locked(instances.ROOT):
archives = archives_of([int(a) for a in arguments[1:]])
@@ -103,5 +104,5 @@ if __name__ == '__main__':
try:
sys.exit(main(sys.argv[1:]))
except (OSError, ValueError, BlockingIOError) as error:
print(f'image cache: {error}', file=sys.stderr)
print(f"{translate('Image cache')}: {error}", file=sys.stderr)
sys.exit(1)
+1 -1
View File
@@ -284,7 +284,7 @@ def change(root, primary_id, acceleration, render_device=None, gfx_override=None
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('vmid', type=int, help='main container of the application')
parser.add_argument('vmid', type=int, help=translate('main container of the application'))
parser.add_argument('--acceleration', required=True, choices=PROFILES)
parser.add_argument('--render-device')
parser.add_argument('--gfx-override')
+1 -1
View File
@@ -301,7 +301,7 @@ def main(argv=None):
diagnose = sub.add_parser('diagnose')
diagnose.add_argument('vmid', type=int)
diagnose.add_argument('--check-registry', action='store_true')
record = sub.add_parser('record', help='Internal installer operation; not legacy adoption')
record = sub.add_parser('record', help=translate('Internal installer operation; not legacy adoption'))
record.add_argument('vmid', type=int)
for flag in ('template', 'deployment', 'archive', 'digest'):
record.add_argument('--' + flag, required=True)
+2 -2
View File
@@ -1029,9 +1029,9 @@ def main():
parser.add_argument('--root', type=Path, default=instances.ROOT)
parser.add_argument('--start', action='store_true')
parser.add_argument('--host-firewall', type=int, action='append', default=[], metavar='VMID',
help='Add the host firewall rule of this container; one option for each confirmed rule')
help=translate('Add the host firewall rule of this container; one option for each confirmed rule'))
parser.add_argument('--automatic', action='store_true',
help='Only the applications that need no question: the ones the cluster keeps a record of')
help=translate('Only the applications that need no question: the ones the cluster keeps a record of'))
args = parser.parse_args()
if os.geteuid() != 0:
parser.error(translate('Root privileges are required'))
+3 -1
View File
@@ -12,6 +12,7 @@ import argparse
import re
import subprocess
import sys
from oci_ui import translate
# A model of five operators (convolution, pooling and a dense layer), enough
# to make ROCm compile and run on the GPU without downloading anything.
@@ -72,7 +73,8 @@ def main():
try:
check(args.vmid)
except RuntimeError as error:
print(error, file=sys.stderr)
message = translate('the GPU did not answer in time') if str(error) == 'the GPU did not answer in time' else str(error)
print(message, file=sys.stderr)
return 1
return 0
+2 -1
View File
@@ -18,6 +18,7 @@ import time
import oci_instances as instances
from oci_installation_state import parse_config
from oci_ui import translate
APPS = Path('/etc/proxmenux/apps')
SOCKET = Path('/run/proxmenux-monitor/oci-check.sock')
@@ -206,7 +207,7 @@ def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--root', type=Path, default=instances.ROOT)
parser.add_argument('--configs', type=Path, default=Path('/etc/pve/lxc'))
parser.add_argument('--progress', action='store_true', help='Emit numeric progress for the OCI menu')
parser.add_argument('--progress', action='store_true', help=translate('Emit numeric progress for the OCI menu'))
args = parser.parse_args()
try:
if args.progress:
+6 -3
View File
@@ -19,6 +19,7 @@ import time
import oci_instances as instances
import oci_operation_notice
from oci_ui import translate
UNIT = 'proxmenux-oci-watchdog.service'
UNIT_FILE = Path('/etc/systemd/system') / UNIT
@@ -191,7 +192,8 @@ def look(states, now):
lambda seen: stop_requested(read_tasks(), vmid, seen))
data = {'app_name': name, 'vmid': vmid, 'containers': f'CT {vmid}'}
if action == 'start':
print(f'CT {vmid} ({name}) stopped on its own; starting it again (attempt {state["attempts"]})', flush=True)
print(translate('CT {vmid} ({name}) stopped on its own; starting it again (attempt {attempt})').format(
vmid=vmid, name=name, attempt=state['attempts']), flush=True)
try:
started = start(vmid)
except (OSError, subprocess.SubprocessError):
@@ -199,7 +201,8 @@ def look(states, now):
if started and notice_due(state, now):
oci_operation_notice.notify('oci_watchdog_restarted', data)
elif action == 'give-up':
print(f'CT {vmid} ({name}) could not be kept running; it is left stopped', flush=True)
print(translate('CT {vmid} ({name}) could not be kept running; it is left stopped').format(
vmid=vmid, name=name), flush=True)
oci_operation_notice.notify('oci_watchdog_failed', data)
@@ -290,7 +293,7 @@ def main():
try:
vmids = set_watchdog(instances.ROOT, args.vmid, args.state == 'on')
except BlockingIOError:
print('Another OCI operation is using the instance registry.', file=sys.stderr)
print(translate('Another OCI operation is using the instance registry.'), file=sys.stderr)
return 3
except (OSError, ValueError, KeyError) as error:
print(str(error) or type(error).__name__, file=sys.stderr)
+32 -7
View File
@@ -4,9 +4,32 @@
from __future__ import annotations
import ctypes
import json
import os
import platform
import sys
from pathlib import Path
from functools import lru_cache
@lru_cache(maxsize=1)
def _messages() -> dict:
# This helper is also installed alone in libexec: importing oci_ui would
# break that existing caller. Read the same catalog using stdlib only.
base = Path(os.environ.get('PMX_BASE_DIR', '/usr/local/share/proxmenux'))
try:
language = json.loads((base / 'config.json').read_text(encoding='utf-8')).get('language', 'en')
if language == 'en' or not isinstance(language, str) or not language:
return {}
data = json.loads((base / 'lang' / (language + '.json')).read_text(encoding='utf-8'))
return data if isinstance(data, dict) else {}
except (OSError, ValueError, AttributeError):
return {}
def translate(text: str) -> str:
value = _messages().get(text)
return value if isinstance(value, str) and value else text
AT_FDCWD = -100
@@ -41,14 +64,16 @@ def fail(step: str) -> None:
def main() -> int:
if len(sys.argv) != 5 or sys.argv[4] not in {"rw", "ro"}:
print(f"usage: {sys.argv[0]} PID SOURCE TARGET rw|ro", file=sys.stderr)
print(f"{translate('Usage:')} {sys.argv[0]} PID SOURCE TARGET rw|ro", file=sys.stderr)
return 2
machine = platform.machine().lower()
if machine not in SYSCALLS:
print(f"unsupported host architecture: {machine}", file=sys.stderr)
print(f"{translate('Unsupported host architecture:')} {machine}", file=sys.stderr)
return 2
open_tree_nr, move_mount_nr, mount_setattr_nr = SYSCALLS[machine]
pid, source, target, mode = sys.argv[1:]
# The host catalog is no longer reachable after chroot into the container.
_messages()
libc = ctypes.CDLL(None, use_errno=True)
libc.syscall.restype = ctypes.c_long
libc.setns.argtypes = (ctypes.c_int, ctypes.c_int)
@@ -60,7 +85,7 @@ def main() -> int:
ct_root = os.open(f"/proc/{pid}/root", os.O_PATH | os.O_DIRECTORY | os.O_CLOEXEC)
try:
if libc.setns(ct_ns, CLONE_NEWNS) != 0:
fail("enter container namespace")
fail(translate("enter container namespace"))
os.fchdir(ct_root)
os.chroot(".")
os.chdir("/")
@@ -71,7 +96,7 @@ def main() -> int:
OPEN_TREE_CLONE | os.O_CLOEXEC,
)
if tree < 0:
fail("clone source mount tree")
fail(translate("clone source mount tree"))
try:
if mode == "ro":
attributes = MountAttr(attr_set=MOUNT_ATTR_RDONLY)
@@ -84,9 +109,9 @@ def main() -> int:
ctypes.sizeof(attributes),
)
if result != 0:
fail("make cloned mount tree read-only")
fail(translate("make cloned mount tree read-only"))
if libc.setns(host_ns, CLONE_NEWNS) != 0:
fail("return to host namespace")
fail(translate("return to host namespace"))
os.fchdir(host_root)
os.chroot(".")
os.chdir("/")
@@ -99,7 +124,7 @@ def main() -> int:
MOVE_MOUNT_F_EMPTY_PATH,
)
if result != 0:
fail("publish mount tree")
fail(translate("publish mount tree"))
finally:
os.close(tree)
finally:
+1 -1
View File
@@ -164,7 +164,7 @@ def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("archive", type=Path)
parser.add_argument("--extracted-mib", action="store_true",
help="print the estimated unpacked size in MiB (0 when unknown)")
help=translate("print the estimated unpacked size in MiB (0 when unknown)"))
args = parser.parse_args()
if args.extracted_mib:
try: