Merge remote-tracking branch 'origin/develop' into HEAD

# Conflicts:
#	AppImage/messages/es/common.json
This commit is contained in:
MacRimi
2026-10-18 17:49:06 +02:00
1073 changed files with 135316 additions and 4690 deletions
@@ -38,6 +38,18 @@ DEFAULT_CONTEXT = "Context: Technical message for Proxmox and IT. Translate:"
# provider, then restore the exact source spelling afterwards. Keep longer # provider, then restore the exact source spelling afterwards. Keep longer
# terms first so ``gasket`` cannot consume part of ``gasket-dkms``. # terms first so ``gasket`` cannot consume part of ``gasket-dkms``.
PROTECTED_TECHNICAL_TERMS = ( PROTECTED_TECHNICAL_TERMS = (
# Product names, kept whole. Left to a translator, "Proxmox Backup Server"
# became "Proxmox Sicherungsserver" and "Serveur de sauvegarde Proxmox",
# and when a provider rightly handed it back unchanged the string was
# retried and then dropped as untranslated.
"Proxmox Backup Server",
"Proxmox Datacenter Manager",
"Proxmox Mail Gateway",
"Proxmox VE",
"ProxMenux Monitor",
"ProxMenux Optimizations",
"Secure Gateway",
"Tailscale",
"google/gasket-driver", "google/gasket-driver",
"feranick/gasket-driver", "feranick/gasket-driver",
"libedgetpu1-std", "libedgetpu1-std",
+77 -6
View File
@@ -73,6 +73,24 @@ TECHNICAL_TERMS = (
"DKMS", "DKMS",
"SSH", "SSH",
"API", "API",
"Flask",
"SQLite",
"Prometheus",
"Grafana",
"Uptime Kuma",
"PWA",
"Fail2Ban",
"Tailscale",
"JavaScript",
"TypeScript",
"Next.js",
"Coral",
"rclone",
"journald",
"gasket",
"apex",
"Alpine",
"OpenVINO",
) )
PROTECTED_PATTERNS = ( PROTECTED_PATTERNS = (
@@ -88,9 +106,13 @@ PROTECTED_PATTERNS = (
re.compile(r"(?<![A-Za-z0-9<])/(?:[A-Za-z0-9._~:@%+=-]+/)*[A-Za-z0-9._~:@%+=-]+"), re.compile(r"(?<![A-Za-z0-9<])/(?:[A-Za-z0-9._~:@%+=-]+/)*[A-Za-z0-9._~:@%+=-]+"),
re.compile(r"\b[A-Za-z0-9_.-]+\.(?:json|ya?ml|toml|conf|service|socket|sh|py|tsx?|jsx?|md)\b"), re.compile(r"\b[A-Za-z0-9_.-]+\.(?:json|ya?ml|toml|conf|service|socket|sh|py|tsx?|jsx?|md)\b"),
re.compile(r"(?<![\w.@])@[A-Za-z_][A-Za-z0-9_]*"), re.compile(r"(?<![\w.@])@[A-Za-z_][A-Za-z0-9_]*"),
# Identifiers and encoded delimiters remain literal even outside <code>.
re.compile(r"(?<![A-Za-z0-9_])_?[A-Za-z][A-Za-z0-9]*(?:_[A-Za-z0-9]+)+(?![A-Za-z0-9_])"),
re.compile(r"\b[A-Za-z0-9]*(?:[a-z][A-Z])[A-Za-z0-9]*\b"),
re.compile(r"&(?:[A-Za-z]+|#[0-9]+|#x[0-9A-Fa-f]+);"),
) )
LITERAL_TAG_RE = re.compile( LITERAL_TAG_RE = re.compile(
r"<(code|kbd|pre)\b[^>]*>.*?</\1>", r"<(code|codeNw|codeXs|kbd|pre)\b[^>]*>.*?</\1>",
re.IGNORECASE | re.DOTALL, re.IGNORECASE | re.DOTALL,
) )
@@ -109,16 +131,23 @@ ANY_TAG_RE = re.compile(r"</?[A-Za-z][^<>]*>")
BARE_SENTINELS = False BARE_SENTINELS = False
PLACEHOLDER_RE = re.compile(r"\{[A-Za-z_][A-Za-z0-9_.-]*\}") PLACEHOLDER_RE = re.compile(r"\{[A-Za-z_][A-Za-z0-9_.-]*\}")
NON_TRANSLATABLE_KEYS = { NON_TRANSLATABLE_KEYS = {
"accent",
"categoryslug",
"command", "command",
"code", "code",
"href", "href",
"icon", "icon",
"iconbg",
"id", "id",
"menucmd",
"path", "path",
"route", "route",
"slug", "slug",
"src", "src",
"url", "url",
"color",
"tone",
"variant",
} }
SOURCE_STATE_VERSION = 1 SOURCE_STATE_VERSION = 1
@@ -186,12 +215,34 @@ def should_copy(source: str, path: tuple[str | int, ...]) -> bool:
if not source.strip() or not re.search(r"[A-Za-z]", source): if not source.strip() or not re.search(r"[A-Za-z]", source):
return True return True
last = str(path[-1]).lower() if path else "" last = str(path[-1]).lower() if path else ""
# These fields describe the surrounding snippet; they are prose, even
# though their names end in "Code".
if last in {"aftercode", "beforecode"}:
return False
# This UI breadcrumb is prose, rather than a filesystem path.
if last == "uipath" and "<strong>" in source:
return False
if last == "flow" and source in {"loader", "dedicated"}:
return True
# These exact values drive rendering/navigation; similarly named visible
# labels remain translatable.
if last == "branch" and source in {"main", "develop", "feature/*"}:
return True
if last == "kind" and source in {"internal", "external"}:
return True
if last == "key" and source in {"linux", "nas", "windows"}:
return True
if last in NON_TRANSLATABLE_KEYS or any( if last in NON_TRANSLATABLE_KEYS or any(
last.endswith(suffix) last.endswith(suffix)
for suffix in ("url", "href", "path", "command", "code", "icon") for suffix in ("url", "href", "path", "command", "code", "icon")
): ):
return True return True
if re.fullmatch(r"https?://\S+", source) or re.fullmatch(r"/[A-Za-z0-9_./:@%+=-]+", source): if re.fullmatch(r"https?://\S+", source) or re.fullmatch(r"/\S+", source):
return True
# Standalone literal blocks contain examples, not prose. Separators between
# several blocks do not make their command/configuration contents translatable.
without_literals = LITERAL_TAG_RE.sub("", source)
if without_literals != source and not re.search(r"[A-Za-z]", ANY_TAG_RE.sub("", without_literals)):
return True return True
if re.fullmatch(r"[A-Z0-9_.:/+-]{2,}", source): if re.fullmatch(r"[A-Z0-9_.:/+-]{2,}", source):
return True return True
@@ -202,6 +253,15 @@ def leaf_token(path: tuple[str | int, ...]) -> str:
return json.dumps(path, ensure_ascii=False, separators=(",", ":")) return json.dumps(path, ensure_ascii=False, separators=(",", ":"))
def literal_values_match(source: Any, target: Any) -> bool:
"""Literal commands and rendering metadata must follow the source too."""
return all(
not should_copy(leaf.source, leaf.path)
or get_at_path(target, leaf.path) == leaf.source
for leaf in iter_leaves(source)
)
def source_fingerprints(source: Any) -> dict[str, str]: def source_fingerprints(source: Any) -> dict[str, str]:
return { return {
leaf_token(leaf.path): hashlib.sha256(leaf.source.encode("utf-8")).hexdigest()[:20] leaf_token(leaf.path): hashlib.sha256(leaf.source.encode("utf-8")).hexdigest()[:20]
@@ -240,7 +300,7 @@ def protect_rich_tags(text: str) -> tuple[str, dict[str, str]]:
def replace(match: re.Match[str]) -> str: def replace(match: re.Match[str]) -> str:
original = match.group(1) original = match.group(1)
if original.lower() in {"code", "kbd", "pre"}: if original.lower() in {"code", "codenw", "codexs", "kbd", "pre"}:
return match.group(0) return match.group(0)
key = original.lower() key = original.lower()
internal = names.get(key) internal = names.get(key)
@@ -329,11 +389,14 @@ def protect_text(text: str) -> tuple[str, dict[str, str]]:
def restore_text(text: str, mapping: dict[str, str]) -> str: def restore_text(text: str, mapping: dict[str, str]) -> str:
missing = [token for token in mapping if token not in text] # A local model may append a digit to a bare sentinel. A substring match
# would silently restore the prefix and leak the extra digit into prose.
patterns = {token: re.compile(re.escape(token) + r"(?![0-9])") for token in mapping}
missing = [token for token, pattern in patterns.items() if not pattern.search(text)]
if missing: if missing:
raise ValueError(f"translation provider changed protected token {missing[0]}") raise ValueError(f"translation provider changed protected token {missing[0]}")
for token, original in mapping.items(): for token, original in mapping.items():
text = text.replace(token, original) text = patterns[token].sub(lambda match: original, text)
return text return text
@@ -347,6 +410,11 @@ def validate_contract(source: str, target: str) -> None:
raise ValueError("literal code changed") raise ValueError("literal code changed")
if sorted(PLACEHOLDER_RE.findall(source)) != sorted(PLACEHOLDER_RE.findall(target)): if sorted(PLACEHOLDER_RE.findall(source)) != sorted(PLACEHOLDER_RE.findall(target)):
raise ValueError("placeholder contract changed") raise ValueError("placeholder contract changed")
# Bare numeric protection markers once leaked a trailing zero just
# outside an otherwise intact code span. Literal/ICU checks cannot see it.
residue = re.compile(r'(?:</(?:code|codeNw|codeXs|pre|kbd)>|<strong>)(0+)(?=\s|[;:/<().,-]|$)')
if len(residue.findall(target)) > len(residue.findall(source)):
raise ValueError("unexpected numeric residue next to literal markup")
def provider_function(args: argparse.Namespace) -> Callable[[str, str], str]: def provider_function(args: argparse.Namespace) -> Callable[[str, str], str]:
@@ -822,7 +890,10 @@ def main() -> int:
leaves = pending_leaves(source, target, queued_tokens) leaves = pending_leaves(source, target, queued_tokens)
total_strings += len(iter_leaves(source)) total_strings += len(iter_leaves(source))
missing_strings += len(leaves) missing_strings += len(leaves)
schema_changed = not schema_matches(source, target) schema_changed = (
not schema_matches(source, target)
or not literal_values_match(source, target)
)
if leaves or schema_changed: if leaves or schema_changed:
pending.append( pending.append(
( (
@@ -0,0 +1,77 @@
#!/usr/bin/env python3
"""Check active terminal translations offline, without generating any text.
Validates formatting fields and literal commands/identifiers. Missing values
can retain English fallback; --require-complete makes them errors. This does
not establish the accuracy or fluency of the translated prose.
"""
from __future__ import annotations
import argparse
from pathlib import Path
import re
from build_translation_cache import (
DEFAULT_LANGUAGES, STRUCTURAL_LITERAL_RE, extract_catalog_texts,
extract_python_texts, extract_translate_texts,
)
from build_web_docs_i18n import read_json, validate_contract
def active_sources(root: Path) -> list[str]:
# Match build-translation-cache.yml, including launchers outside scripts/.
extras = [root / name for name in ('menu', 'install_proxmenux.sh', 'install_proxmenux_beta.sh')]
texts = extract_translate_texts(root / 'scripts', extras)
texts += extract_translate_texts(root / 'oci/remote')
texts += extract_python_texts([root / 'scripts', root / 'oci/src', root / 'oci/remote'])
texts += extract_catalog_texts([root / 'oci/catalog'])
return sorted({text for text in texts if '$' not in text and '`' not in text})
def validate_message(source: str, target: str, *, title_separator: bool = False) -> None:
validate_contract(source, target)
for match in STRUCTURAL_LITERAL_RE.finditer(source):
literal = match.group()
# A page title may replace its decorative separator with a dash.
# In command examples, a pipe remains a shell operator.
if title_separator and literal == '|':
continue
# DNS hostnames are case-insensitive. Paths, field names and command
# names are not, so do not case-fold those to hide a broken example.
is_hostname = re.fullmatch(r'[A-Za-z0-9.-]+\.(?:com|org|net|io)', literal)
if literal not in target and not (is_hostname and literal.casefold() in target.casefold()):
raise ValueError(f'literal command or identifier changed: {literal}')
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--root', type=Path, default=Path('.'))
parser.add_argument('--languages', default=','.join(DEFAULT_LANGUAGES))
parser.add_argument('--require-complete', action='store_true')
args = parser.parse_args()
root = args.root.resolve()
texts = active_sources(root)
errors = 0
for language in args.languages.split(','):
if language not in DEFAULT_LANGUAGES:
parser.error(f'Unsupported locale: {language}')
catalog = read_json(root / 'lang' / f'{language}.json') or {}
missing = 0
for source in texts:
target = catalog.get(source)
if not isinstance(target, str) or not target.strip():
missing += 1
continue
try:
validate_message(source, target)
except ValueError as error:
print(f'[{language}] {source!r}: {error}')
errors += 1
print(f'[{language}] active={len(texts)} missing={missing}')
if args.require_complete and missing:
errors += 1
return 1 if errors else 0
if __name__ == '__main__':
raise SystemExit(main())
@@ -0,0 +1,75 @@
#!/usr/bin/env python3
"""Check documentation translation structure and literal contracts offline.
Missing locales/leaves are allowed by default because the website falls back
to English. Use --require-complete when validating a catalog completion PR.
This checks data integrity, not translation fluency or linguistic accuracy.
"""
from __future__ import annotations
import argparse
from pathlib import Path
from build_web_docs_i18n import (
DEFAULT_LANGUAGES,
get_at_path,
iter_leaves,
literal_values_match,
read_json,
schema_matches,
source_files,
)
from check_terminal_catalogs import validate_message
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--messages-dir", type=Path, default=Path("web/messages"))
parser.add_argument("--languages", default=",".join(DEFAULT_LANGUAGES))
parser.add_argument("--require-complete", action="store_true")
args = parser.parse_args()
root = args.messages_dir.resolve()
files = source_files(root / "en", ".")
errors = 0
for language in args.languages.split(","):
language = language.strip()
if language not in DEFAULT_LANGUAGES:
parser.error(f"Unsupported locale: {language}")
missing = checked = present_files = 0
for file in files:
relative = file.relative_to(root / "en")
source = read_json(file)
target = read_json(root / language / relative)
if target is not None:
present_files += 1
if args.require_complete and not schema_matches(source, target):
print(f"[{language}] {relative}: source/target schema differs")
errors += 1
for leaf in iter_leaves(source):
localized = get_at_path(target, leaf.path)
if not isinstance(localized, str) or (leaf.source.strip() and not localized.strip()):
missing += 1
continue
try:
validate_message(
leaf.source, localized,
title_separator=bool(
leaf.path and leaf.path[0] == "meta"
and leaf.path[-1] in ("title", "ogTitle", "twitterTitle")
),
)
except ValueError as exc:
print(f"[{language}] {relative}:{'.'.join(map(str, leaf.path))}: {exc}")
errors += 1
checked += 1
if args.require_complete and not literal_values_match(source, target):
print(f"[{language}] {relative}: literal commands or rendering metadata differ")
errors += 1
print(f"[{language}] files={present_files}/{len(files)} strings={checked} missing={missing}")
if args.require_complete and missing:
errors += 1
return 1 if errors else 0
if __name__ == "__main__":
raise SystemExit(main())
+2
View File
@@ -13,6 +13,7 @@ import sys
import tempfile import tempfile
PYTHON_FILES = ( PYTHON_FILES = (
"AppImage/scripts/tests/test_native_app_discovery.py",
"tests/test_audit_presentation.py", "tests/test_audit_presentation.py",
"tests/test_audit_safety_wording.py", "tests/test_audit_safety_wording.py",
"tests/test_audit_policy.py", "tests/test_audit_policy.py",
@@ -24,6 +25,7 @@ PYTHON_FILES = (
"AppImage/scripts/tests/test_health_persistence_recurrence.py", "AppImage/scripts/tests/test_health_persistence_recurrence.py",
) )
NODE_FILES = ( NODE_FILES = (
"tests/lxc_updates/test_native_app_discovery_ui.cjs",
"tests/lxc_updates/test_docker_delegated_ui.cjs", "tests/lxc_updates/test_docker_delegated_ui.cjs",
"tests/test_audit_diagnostic_document.cjs", "tests/test_audit_diagnostic_document.cjs",
"tests/test_audit_policy.cjs", "tests/test_audit_policy.cjs",
@@ -60,6 +60,124 @@ class WebDocsI18nTests(unittest.TestCase):
target = {"title": "Actualizaciones", "url": "https://example.com"} target = {"title": "Actualizaciones", "url": "https://example.com"}
self.assertEqual(MODULE.pending_leaves(source, target), []) self.assertEqual(MODULE.pending_leaves(source, target), [])
def test_rendering_metadata_is_copied_but_visible_labels_are_translated(self):
for key, value in (("tone", "amber"), ("variant", "source"),
("accent", "border-blue-300 bg-blue-50"),
("iconBg", "bg-blue-100 text-blue-700"),
("color", "green")):
with self.subTest(key=key):
self.assertTrue(MODULE.should_copy(value, ("steps", 0, key)))
self.assertFalse(MODULE.should_copy(value, ("label",)))
self.assertEqual(MODULE.merge_tree({key: value}, {key: "translated"}, {}), {key: value})
self.assertFalse(MODULE.should_copy("Translation", ("type",)))
self.assertFalse(MODULE.should_copy("English", ("lang",)))
for value in ("loader", "dedicated"):
self.assertTrue(MODULE.should_copy(value, ("cards", 0, "flow")))
self.assertFalse(MODULE.should_copy(value, ("label",)))
def test_literal_only_examples_and_api_routes_are_not_translation_gaps(self):
for source in ("<code>apt update</code>",
"<code>net0</code> / <code>net1</code>.",
"/api/task-log/&lt;upid&gt;", "/api/vms/<vmid>/config"):
with self.subTest(source=source):
self.assertTrue(MODULE.should_copy(source, ("cell",)))
self.assertFalse(MODULE.should_copy("Run <code>apt update</code> first.", ("body",)))
self.assertFalse(MODULE.should_copy("After running the command, check its output.", ("afterCode",)))
self.assertFalse(MODULE.should_copy("Before running the command, read this.", ("beforeCode",)))
self.assertTrue(MODULE.should_copy("curl http://localhost:8008/api/health", ("curlCode",)))
def test_inline_identifiers_and_encoded_delimiters_round_trip(self):
source = "Set RS_SKIP_PATHS, call _rs_apply_hot and keep localStorage at &lt;host&gt;."
protected, mapping = MODULE.protect_text(source)
for literal in ("RS_SKIP_PATHS", "_rs_apply_hot", "localStorage", "&lt;", "&gt;"):
self.assertIn(literal, mapping.values())
self.assertNotIn(literal, protected)
self.assertEqual(MODULE.restore_text(protected, mapping), source)
def test_custom_code_spans_keep_commands_literal(self):
for tag in ("codeNw", "codeXs"):
source = f"Run <{tag}>pct config 100</{tag}>."
with self.subTest(tag=tag):
protected, mapping = MODULE.protect_rich_tags(source)
self.assertEqual(protected, source)
self.assertEqual(mapping, {})
with self.assertRaisesRegex(ValueError, "literal code changed"):
MODULE.validate_contract(source, source.replace("config", "configuration"))
with self.assertRaisesRegex(ValueError, "numeric residue"):
MODULE.validate_contract(source, source.replace(f"</{tag}>", f"</{tag}>0"))
self.assertTrue(MODULE.should_copy(f"<{tag}>pct config 100</{tag}>", ("example",)))
def test_navigation_identifiers_are_distinct_from_visible_labels(self):
values = {
"categorySlug": ["system", "network"],
"menuCmd": ["menu"],
"branch": ["main", "develop", "feature/*"],
"kind": ["internal", "external"],
"key": ["nas", "linux", "windows"],
}
for key, items in values.items():
for value in items:
with self.subTest(key=key, value=value):
self.assertTrue(MODULE.should_copy(value, ("cards", 0, key)))
self.assertFalse(MODULE.should_copy(value, ("label",)))
self.assertFalse(MODULE.should_copy("Translation", ("kind",)))
self.assertFalse(MODULE.should_copy("Storage", ("branch",)))
def test_ui_breadcrumb_is_prose_while_paths_remain_literal(self):
source = "In the UI: <strong>Datacenter → Backup → Retention</strong>:"
self.assertFalse(MODULE.should_copy(source, ("retention", "uiPath")))
self.assertTrue(MODULE.should_copy("/etc/pve/storage.cfg", ("uiPath",)))
self.assertTrue(MODULE.should_copy("/etc/pve/storage.cfg", ("configPath",)))
def test_bare_sentinel_with_added_digit_is_rejected(self):
with mock.patch.object(MODULE, "BARE_SENTINELS", True):
protected, mapping = MODULE.protect_text("ProxMenux collects metrics.")
token = next(iter(mapping))
with self.assertRaisesRegex(ValueError, "changed protected token"):
MODULE.restore_text(protected.replace(token, token + "0"), mapping)
self.assertEqual(MODULE.restore_text(protected, mapping), "ProxMenux collects metrics.")
def test_numeric_residue_outside_intact_literal_is_rejected(self):
examples = [
('Run <code>pct config</code> first.', 'Spustite <code>pct config</code>0 najprv.'),
('<strong><code>Restart=on-failure</code></strong> restarts the service.',
'<strong>0<code>Restart=on-failure</code></strong> reštartuje službu.'),
]
for source, target in examples:
with self.subTest(source=source):
with self.assertRaisesRegex(ValueError, 'numeric residue'):
MODULE.validate_contract(source, target)
MODULE.validate_contract('The result is <code>echo</code>0.',
'Výsledok je <code>echo</code>0.')
def test_product_and_kernel_module_names_are_protected(self):
source = "Flask uses SQLite and journald; rclone works with Coral, gasket, apex and Alpine."
protected, mapping = MODULE.protect_text(source)
for name in ("Flask", "SQLite", "journald", "rclone", "Coral", "gasket", "apex", "Alpine"):
self.assertIn(name, mapping.values())
self.assertNotIn(name, protected)
self.assertEqual(MODULE.restore_text(protected, mapping), source)
def test_literal_only_changes_sync_without_retranslating_human_prose(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
source = root / "messages/en/common.json"
target = root / "messages/de/common.json"
source.parent.mkdir(parents=True)
target.parent.mkdir(parents=True)
source.write_text(json.dumps({"title": "Updates", "code": "tool --version=2", "tone": "blue"}))
target.write_text(json.dumps({"title": "Aktualisierungen", "code": "tool --version=1", "tone": "blau"}))
provider = mock.Mock(side_effect=AssertionError("Existing prose must not be retranslated"))
self.assertEqual(self.run_generator(root, provider), 0)
self.assertEqual(json.loads(target.read_text()), {
"title": "Aktualisierungen", "code": "tool --version=2", "tone": "blue",
})
provider.assert_not_called()
before = target.stat().st_mtime_ns
self.assertEqual(self.run_generator(root, provider), 0)
self.assertEqual(target.stat().st_mtime_ns, before)
provider.assert_not_called()
def test_changed_source_text_is_pending_even_with_existing_translation(self): def test_changed_source_text_is_pending_even_with_existing_translation(self):
source = {"title": "Updated installation guidance"} source = {"title": "Updated installation guidance"}
target = {"title": "Vorherige Installationsanleitung"} target = {"title": "Vorherige Installationsanleitung"}
@@ -80,177 +80,74 @@ class CommandDescriptionsTests(unittest.TestCase):
self.assertEqual(catalog("sv")["terminal"]["commandDescriptions"][index], self.assertEqual(catalog("sv")["terminal"]["commandDescriptions"][index],
FIXTURE["locales"]["sv"][index]) FIXTURE["locales"]["sv"][index])
def test_workflow_default_catalogs_rerun_without_provider_or_writes(self): def assert_completed_catalogs_are_stable(self, *, future_messages=None):
workflow = (ROOT / ".github/workflows/build-i18n-messages.yml").read_text() workflow = (ROOT / ".github/workflows/build-i18n-messages.yml").read_text()
languages = re.search(r'LANGS="\$\{LANGS:-(.*?)\}"', workflow).group(1) languages = re.search(r'LANGS="\$\{LANGS:-(.*?)\}"', workflow).group(1)
self.assertEqual(languages.split(","), list(MODULE.DEFAULT_LANGUAGES)) self.assertEqual(languages.split(","), list(MODULE.DEFAULT_LANGUAGES))
locales = ["en", *languages.split(",")]
shipped = {ROOT / f"AppImage/messages/{lang}/common.json": catalog(lang) for lang in locales}
original_bytes = {path: path.read_bytes() for path in shipped}
def strings(node, path=()):
# Independent traversal: every existing nonempty string, including
# explicit English and obsolete human keys, must survive completion.
if isinstance(node, str) and node:
yield path, node
elif isinstance(node, dict):
for key, value in node.items():
yield from strings(value, (*path, key))
elif isinstance(node, list):
for index, value in enumerate(node):
yield from strings(value, (*path, index))
with tempfile.TemporaryDirectory() as directory: with tempfile.TemporaryDirectory() as directory:
root = Path(directory) root = Path(directory).resolve()
for lang in ["en", *languages.split(",")]: for path in shipped:
path = root / lang / "common.json" destination = root / path.parent.name / "common.json"
path.parent.mkdir() destination.parent.mkdir()
shutil.copyfile(ROOT / f"AppImage/messages/{lang}/common.json", path) shutil.copyfile(path, destination)
# This steady-state fixture assumes generation already finished. if future_messages is not None:
# Seed only intentional message additions; keep the source = json.loads((root / "en/common.json").read_text())
# recovered command arrays and the repo catalogs exact. Runtime source["futureFeatureFixture"] = future_messages
# missing-key fallback is covered by the JSX seam tests and by (root / "en/common.json").write_text(json.dumps(source))
# test_storage_messages.cjs and test_health_text.cjs.
if lang != "en":
temporary = json.loads(path.read_text())
for section, key in (
("archives", "emptyMessage"),
("archives", "formerJob"),
("archives", "formerJobTitle"),
("archives", "hiddenOtherNodes"),
("archives", "scopeAllNodes"),
("archives", "scopeAriaLabel"),
("archives", "scopeShowAll"),
("archives", "scopeThisNode"),
("destinations", "backupsKeptMessage"),
("destinations", "localAttachHelpMessage"),
):
temporary["backup"][section].setdefault(
key, catalog("en")["backup"][section][key])
temporary["storage"].setdefault(
"savedSmartData", catalog("en")["storage"]["savedSmartData"])
report = temporary["storage"]["smartReport"]
source_report = catalog("en")["storage"]["smartReport"]
for key in ("passedAssessment", "noReallocatedSectorsReported",
"passedMeaningTitle", "passedMeaning"):
report.setdefault(key, source_report[key])
for key in ("passedTitle", "passedText"):
report["recommendations"].setdefault(
key, source_report["recommendations"][key])
# New health-popup messages deliberately use runtime English
# fallback until each locale is translated. Model generator
# steady state only in this disposable copy, as for thermal keys.
for key in (
'certificateExpired',
'certificateExpiresDays',
'certificateInconclusive',
'cpuCheckFailed',
'cpuHighSustained',
'cpuSustained',
'fail2banCheckUnavailable',
'fail2banInactive',
'failedLoginsWithinThreshold',
'kernelPackagesMore',
'kernelPveUpdates',
'loginCheckUnavailable',
'memoryCheckFailed',
'memoryPressure',
'noFailedLogins',
'noSwapConfigured',
'noTemperatureSensor',
'nonRunningKernelUpdates',
'pendingPackageUpdates',
'pendingSecurityUpdates',
'pendingSecurityUpdatesDays',
'pendingSecurityUpdatesUnpatched',
'pveVersionAvailable',
'ramHighSustained',
'ramSustained',
'runningKernelUpdate',
'securityCheckUnavailable',
'sensorHighSamplesSpan',
'swapRamPressure',
'systemNotUpdatedMonths',
'systemNotUpdatedYear',
'temperatureElevated',
'unknownDetail',
'updatesAptError',
'updatesAptTimeout',
'updatesCheckUnavailable',
'uptimeKernelAdvice',
'uptimeUnavailable',
):
temporary["healthStatus"]["details"].setdefault(
key, catalog("en")["healthStatus"]["details"][key])
# New thermal messages also await automatic translation.
# Seed only these intentional additions in the temporary copy.
for key in (
"temperatureNoDisks", "temperatureNormal", "temperatureHighCount",
"temperatureCriticalCount", "temperatureUnavailable",
"temperatureIncomplete", "temperatureUnavailableCount",
):
temporary["storage"].setdefault(key, catalog("en")["storage"][key])
# Every shipped locale carries the temperature alert fallback
# keys; the seed only guards a catalog that lacks them. The
# runtime fallback is tested separately.
source_fallback = catalog("en")["runtime"]["notifications"]["fallback"]
fallback = temporary["runtime"]["notifications"]["fallback"]
for key in ("temperatureAlertTitle", "temperatureAlertBody",
"recordedReason", "recordedDetails"):
fallback.setdefault(key, source_fallback[key])
# Slovak remains the exact upstream catalog; model the
# pending outcome-key generator additions in disposable
# copies rather than modifying its curated values.
if lang == 'sk':
local = temporary['runtime']['notifications']
source = catalog('en')['runtime']['notifications']
for key, value in source['backup'].items():
local.setdefault('backup', {}).setdefault(key, value)
local['channels']['email']['status'].setdefault(
'unconfirmed', source['channels']['email']['status']['unconfirmed'])
local['channels']['email']['status'].setdefault(
'completed_with_warnings', source['channels']['email']['status']['completed_with_warnings'])
path.write_text(json.dumps(temporary, ensure_ascii=False))
# Model steady state after the bot fills these intentional new
# messages; keep repository locales and all other leaves intact.
if lang != "en":
messages = json.loads(path.read_text())
english = catalog("en")
for section, key in (("encryption", "pbsHelp"),
("keyfileActions", "pveKeyDescription")):
messages["backup"][section].setdefault(key, english["backup"][section][key])
for key in ("sshUserHelpMessage", "sshKeyHelpMessage", "sshKeySetupTitle",
"sshKeySetupHelp", "sshAuthorizedKeyHelp"):
messages["backup"]["destinations"].setdefault(
key, english["backup"]["destinations"][key])
messages["backup"]["actions"].setdefault(
"prepareSshKey", english["backup"]["actions"]["prepareSshKey"])
# New LXC observation labels intentionally ship only EN/SK.
# Model the bot's future output in temporary catalogs, just
# like the other pending keys above; never rewrite locales.
messages["scriptTerminal"].setdefault("endOfInput", english["scriptTerminal"]["endOfInput"])
messages["vmLxc"]["scheduled"].setdefault(
"reasonStoppedContainer",
english["vmLxc"]["scheduled"]["reasonStoppedContainer"],
)
messages["vmLxc"]["scheduled"].setdefault(
"reasonStoppedContainerHistorical",
english["vmLxc"]["scheduled"]["reasonStoppedContainerHistorical"],
)
messages["vmLxc"]["scheduled"].setdefault(
"runSkippedStatus",
english["vmLxc"]["scheduled"]["runSkippedStatus"],
)
for key in ("noShell", "enterFailed"):
messages["lxcTerminal"]["errors"].setdefault(key, english["lxcTerminal"]["errors"][key])
# The notification history panel texts await the bot as well.
for key, value in english["settings"]["notifications"]["history"].items():
messages["settings"]["notifications"].setdefault("history", {}).setdefault(key, value)
for key in ("checkNow", "checkReadOnlyHelp", "checkCooldown",
"checkStale", "checkStaleHelp", "checkBeforeUpdate",
"startBeforeCheck", "notCheckedYet",
"checkStillRunning", "refreshCheckHelp",
"checkInProgress", "checkSystemPackages", "runCustomCommand",
"installedVersionUnverified", "versionStatusUnknown"):
messages["vmLxc"]["updates"].setdefault(
key, english["vmLxc"]["updates"][key])
for key in ("startPending", "shutdownPending", "rebootPending", "stopPending"):
messages["vmLxc"]["actions"].setdefault(
key, english["vmLxc"]["actions"][key])
for key in ("osDockerScope", "dockerImageMethod", "unverifiedTargetsHelp"):
messages["vmLxc"]["bulkUpdate"].setdefault(
key, english["vmLxc"]["bulkUpdate"][key])
messages["vmLxc"]["errors"].setdefault(
"controlNotConfirmed", english["vmLxc"]["errors"]["controlNotConfirmed"])
path.write_text(json.dumps(messages, ensure_ascii=False))
before = {p: p.read_bytes() for p in root.glob("*/common.json")}
argv = [str(SCRIPT), "--source", str(root / "en/common.json"), argv = [str(SCRIPT), "--source", str(root / "en/common.json"),
"--messages-dir", str(root), "--languages", languages, "--sleep", "0"] "--messages-dir", str(root), "--languages", languages, "--sleep", "0"]
output = io.StringIO() output = io.StringIO()
# Establish steady state with the real generator and a deterministic
# offline provider. Translation happens only in disposable catalogs;
# adding an EN-only feature never needs a hand-maintained seed list.
with mock.patch.object(sys, "argv", argv), \
mock.patch.object(MODULE, "translate_one", side_effect=lambda text, lang, *_: f"TEST {lang} {text}") as seed, \
mock.patch.object(MODULE, "write_json", wraps=MODULE.write_json) as initial_writer, \
redirect_stdout(output), redirect_stderr(output):
self.assertEqual(MODULE.main(), 0, output.getvalue())
self.assertNotRegex(output.getvalue(), r"(?m)^Completed with [0-9]+ translation failures")
self.assertTrue(all(call.args[0].is_relative_to(root) for call in initial_writer.call_args_list))
for path, original in shipped.items():
completed = json.loads((root / path.parent.name / "common.json").read_text())
for keys, value in strings(original):
actual = completed
for key in keys:
actual = actual[key]
self.assertEqual(actual, value, f"{path.parent.name}: {keys}")
# The recovered arrays remain exact, including frozen English.
if path.parent.name != "en":
self.assertEqual(completed["terminal"]["commandDescriptions"],
FIXTURE["locales"].get(path.parent.name,
original["terminal"]["commandDescriptions"]))
if future_messages is not None and path.parent.name != "en":
self.assertEqual(completed["futureFeatureFixture"], {
"title": f"TEST {path.parent.name} Future feature {{count}}",
"items": [f"TEST {path.parent.name} First item", {"label": f"TEST {path.parent.name} Second item"}],
"enabled": False, "count": 2, "empty": [],
})
if future_messages is not None:
self.assertGreaterEqual(seed.call_count, 3 * len(languages.split(",")))
before = {p: p.read_bytes() for p in root.glob("*/common.json")}
output = io.StringIO()
# Keep the original strict assertion: once completed, any provider
# call or catalog write is a regression. No production locale edits.
with mock.patch.object(sys, "argv", argv), \ with mock.patch.object(sys, "argv", argv), \
mock.patch.object(MODULE, "translate_one", side_effect=AssertionError("provider forbidden")) as provider, \ mock.patch.object(MODULE, "translate_one", side_effect=AssertionError("provider forbidden")) as provider, \
mock.patch.object(MODULE, "write_json", side_effect=AssertionError("writes forbidden")) as writer, \ mock.patch.object(MODULE, "write_json", side_effect=AssertionError("writes forbidden")) as writer, \
@@ -262,6 +159,17 @@ class CommandDescriptionsTests(unittest.TestCase):
self.assertEqual(before, {p: p.read_bytes() for p in before}) self.assertEqual(before, {p: p.read_bytes() for p in before})
for lang in languages.split(","): for lang in languages.split(","):
self.assertIn(f"{lang}: nothing to translate", output.getvalue()) self.assertIn(f"{lang}: nothing to translate", output.getvalue())
self.assertEqual(original_bytes, {path: path.read_bytes() for path in shipped})
def test_workflow_default_catalogs_rerun_without_provider_or_writes(self):
self.assert_completed_catalogs_are_stable()
def test_new_english_feature_preserves_curated_values_and_reruns_without_writes(self):
self.assert_completed_catalogs_are_stable(future_messages={
"title": "Future feature {count}",
"items": ["First item", {"label": "Second item"}],
"enabled": False, "count": 2, "empty": [],
})
def test_actual_consumer_lookup_null_fallback_and_explicit_english(self): def test_actual_consumer_lookup_null_fallback_and_explicit_english(self):
node = require_node() node = require_node()
@@ -0,0 +1,16 @@
"""Keep the canonical conduct policy and its English website source in sync."""
import json
from pathlib import Path
import unittest
ROOT = Path(__file__).resolve().parents[3]
class DocumentationMarkdownTests(unittest.TestCase):
def test_english_conduct_body_matches_canonical_policy(self):
source = json.loads((ROOT / 'web/messages/en/docs/about/code-of-conduct.json').read_text(encoding='utf-8'))
self.assertEqual(source['contentMarkdown'], (ROOT / 'CODE_OF_CONDUCT.md').read_text(encoding='utf-8'))
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,30 @@
"""Keep machine generation explicit without losing automatic validation."""
from pathlib import Path
import unittest
import yaml
ROOT = Path(__file__).resolve().parents[3]
class DocumentationReviewWorkflowTests(unittest.TestCase):
def workflow(self, name):
return yaml.load(
(ROOT / '.github/workflows' / name).read_text(encoding='utf-8'),
Loader=yaml.BaseLoader,
)
def test_generation_requires_explicit_dispatch(self):
triggers = self.workflow('build-web-docs-i18n.yml')['on']
self.assertEqual(set(triggers), {'workflow_dispatch'})
def test_website_changes_keep_automatic_offline_validation(self):
triggers = self.workflow('test-i18n.yml')['on']
for event in ('pull_request', 'push'):
with self.subTest(event=event):
self.assertIn('web/**', triggers[event]['paths'])
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,30 @@
"""Every shipped terminal language must be selectable during installation."""
from pathlib import Path
import re
import sys
import unittest
SCRIPTS = Path(__file__).resolve().parents[1]
ROOT = SCRIPTS.parents[1]
sys.path.insert(0, str(SCRIPTS))
from build_translation_cache import DEFAULT_LANGUAGES
class InstallerLanguageTests(unittest.TestCase):
def test_both_installers_offer_every_shipped_language(self):
expected = {'en', *DEFAULT_LANGUAGES}
for name in ('install_proxmenux.sh', 'install_proxmenux_beta.sh'):
with self.subTest(installer=name):
source = (ROOT / name).read_text(encoding='utf-8')
function = re.search(r'^select_language\(\) \{(.*?)^\}', source, re.M | re.S)
self.assertIsNotNone(function, 'Missing installer language selector')
offered = re.findall(r'^\s*"([a-z]{2})"\s+"[^"]+"', function.group(1), re.M)
self.assertEqual(set(offered), expected)
self.assertEqual(len(offered), len(expected), 'Duplicate language option')
# English is the source text in the scripts, not a JSON catalog.
for language in set(offered) - {'en'}:
self.assertTrue((ROOT / 'lang' / f'{language}.json').is_file(), language)
if __name__ == '__main__':
unittest.main()
+29 -2
View File
@@ -4,9 +4,13 @@ lives: the scripts it launches are the ones the menus launch."""
import importlib.util import importlib.util
import json import json
import os
from pathlib import Path from pathlib import Path
import re import re
import runpy import runpy
import shlex
import subprocess
import tempfile
import unittest import unittest
ROOT = Path(__file__).resolve().parents[3] ROOT = Path(__file__).resolve().parents[3]
@@ -42,11 +46,13 @@ class IndexTests(unittest.TestCase):
self.assertEqual(unknown, []) self.assertEqual(unknown, [])
self.assertEqual([item["menu"] for item in self.index if item["menu"] not in self.spanish], []) self.assertEqual([item["menu"] for item in self.index if item["menu"] not in self.spanish], [])
def test_every_script_a_menu_launches_can_be_found(self): def test_every_script_a_pve_menu_launches_can_be_found(self):
indexed = {item["script"] for item in self.index} indexed = {item["script"] for item in self.index}
launched = set() launched = set()
for menu in sorted((SCRIPTS / "menus").glob("*.sh")): for menu in sorted((SCRIPTS / "menus").glob("*.sh")):
if menu.name in ("main_menu.sh", "lxc_menu.sh"): # PBS replaces the PVE main menu and does not use its search.
# The exclusive platform dispatch is executed in the test below.
if menu.name in ("main_menu.sh", "lxc_menu.sh", "pbs_menu.sh"):
continue continue
launched |= set(re.findall(r'bash "\$LOCAL_SCRIPTS/([A-Za-z0-9_./-]+\.sh)"', menu.read_text())) launched |= set(re.findall(r'bash "\$LOCAL_SCRIPTS/([A-Za-z0-9_./-]+\.sh)"', menu.read_text()))
launched -= {"menus/main_menu.sh"} launched -= {"menus/main_menu.sh"}
@@ -54,6 +60,27 @@ class IndexTests(unittest.TestCase):
launched -= {"post_install/auto_post_install.sh", "utilities/proxmox_update.sh", "utilities/upgrade_pve8_to_pve9.sh"} launched -= {"post_install/auto_post_install.sh", "utilities/proxmox_update.sh", "utilities/upgrade_pve8_to_pve9.sh"}
self.assertEqual(sorted(launched - indexed), []) self.assertEqual(sorted(launched - indexed), [])
def test_pbs_replaces_the_searchable_pve_menu(self):
menu = (SCRIPTS / "menus/main_menu.sh").read_text()
dispatch = menu[menu.rindex('source "$LOCAL_SCRIPTS/global/platform-functions.sh"'):]
with tempfile.TemporaryDirectory() as directory:
scripts = Path(directory)
(scripts / "global").mkdir()
(scripts / "menus").mkdir()
helper = scripts / "global/platform-functions.sh"
helper.write_text(
"source " + shlex.quote(str(SCRIPTS / "global/platform-functions.sh")) + "\n"
'pmx_platform_field() { printf "%s" "$TEST_PROXMENUX_PRODUCT"; }\n')
(scripts / "menus/pbs_menu.sh").write_text('printf "PBS menu\\n"\n')
harness = 'show_menu() { printf "PVE search menu\\n"; }\n' + dispatch
for product, expected in (("pve", "PVE search menu"), ("pbs", "PBS menu")):
with self.subTest(product=product):
result = subprocess.run(["bash", "-e", "-c", harness], text=True, capture_output=True,
env={**os.environ, "LOCAL_SCRIPTS": str(scripts),
"TEST_PROXMENUX_PRODUCT": product})
self.assertEqual(result.returncode, 0, result.stderr)
self.assertEqual(result.stdout.strip(), expected)
def test_the_main_menu_offers_the_search_and_opens_what_it_hands_back(self): def test_the_main_menu_offers_the_search_and_opens_what_it_hands_back(self):
menu = (SCRIPTS / "menus/main_menu.sh").read_text() menu = (SCRIPTS / "menus/main_menu.sh").read_text()
self.assertIn('search "$(translate "Search options and applications")"', menu) self.assertIn('search "$(translate "Search options and applications")"', menu)
@@ -5,6 +5,7 @@ import json
from pathlib import Path from pathlib import Path
import re import re
import runpy import runpy
import subprocess
import sys import sys
import tempfile import tempfile
from types import ModuleType, SimpleNamespace from types import ModuleType, SimpleNamespace
@@ -52,7 +53,8 @@ class InventoryMessages(unittest.TestCase):
state = ModuleType('oci_installation_state') state = ModuleType('oci_installation_state')
state.parse_config = lambda raw: dict(line.split(': ', 1) for line in raw.decode().splitlines()) state.parse_config = lambda raw: dict(line.split(': ', 1) for line in raw.decode().splitlines())
scope = {'sys': SimpleNamespace(path=[]), 'source_text': lambda text: text or '', scope = {'sys': SimpleNamespace(path=[]), 'source_text': lambda text: text or '',
'translate': translate, 're': re} 'translate': translate, 're': re, 'subprocess': subprocess,
'_selected_configuration': remover.guest_config}
summary = extracted(MENU, '_removal_summary', scope) summary = extracted(MENU, '_removal_summary', scope)
with patch.dict(sys.modules, {'oci_instances': instances, 'oci_remove': remover, with patch.dict(sys.modules, {'oci_instances': instances, 'oci_remove': remover,
'oci_installation_state': state}): 'oci_installation_state': state}):
@@ -56,7 +56,8 @@ class RemovalWordings(unittest.TestCase):
state = ModuleType('oci_installation_state') state = ModuleType('oci_installation_state')
state.parse_config = lambda raw: {} state.parse_config = lambda raw: {}
scope = {'sys': SimpleNamespace(path=[]), 'source_text': lambda value: value or '', scope = {'sys': SimpleNamespace(path=[]), 'source_text': lambda value: value or '',
'translate': translated or (lambda value: value), 're': re} 'translate': translated or (lambda value: value), 're': re,
'_selected_configuration': remover.guest_config, 'subprocess': subprocess}
with patch.dict(sys.modules, {'oci_instances': instances, 'oci_remove': remover, with patch.dict(sys.modules, {'oci_instances': instances, 'oci_remove': remover,
'oci_installation_state': state}): 'oci_installation_state': state}):
return extract(MENU, '_removal_summary', scope)(Path('/inert'), 101) return extract(MENU, '_removal_summary', scope)(Path('/inert'), 101)
@@ -87,7 +88,8 @@ class RemovalWordings(unittest.TestCase):
with patch.dict(sys.modules, {'oci_instances': instances, 'oci_remove': remover, with patch.dict(sys.modules, {'oci_instances': instances, 'oci_remove': remover,
'oci_installation_state': state}): 'oci_installation_state': state}):
scope = {'sys': SimpleNamespace(path=[]), 'source_text': lambda value: value or '', scope = {'sys': SimpleNamespace(path=[]), 'source_text': lambda value: value or '',
'translate': lambda value: value, 're': re} 'translate': lambda value: value, 're': re,
'_selected_configuration': remover.guest_config, 'subprocess': subprocess}
preview = extract(MENU, '_removal_summary', scope)(Path('/inert'), 102) preview = extract(MENU, '_removal_summary', scope)(Path('/inert'), 102)
self.assertIn(WHOLE, preview) self.assertIn(WHOLE, preview)
@@ -42,13 +42,13 @@ class SelectionSetupWording(TestCase):
shutil=SimpleNamespace(which=lambda _: '/fake/pct'), saved_inventory=lambda _: [], shutil=SimpleNamespace(which=lambda _: '/fake/pct'), saved_inventory=lambda _: [],
_clean_orphans=lambda project: None, _clean_orphans=lambda project: None,
recover_automatically=lambda project: None, offer_recovery=lambda project, ui: None, recover_automatically=lambda project: None, offer_recovery=lambda project, ui: None,
carry_records=lambda project, **options: {}, carry_records=lambda project, **options: {}, _records_unread=lambda project: False,
translate=lambda s: s) translate=lambda s: s)
fn(Path('/fixture'), ui) fn(Path('/fixture'), ui)
ui.message.assert_called_once_with(EMPTY, 'OCI management') ui.message.assert_called_once_with(EMPTY, 'OCI management')
ui.choose.assert_not_called() ui.choose.assert_not_called()
def _menu(self, inventories, selections): def _menu(self, inventories, selections, unread=True):
rows = iter(inventories) rows = iter(inventories)
ui = SimpleNamespace(message=Mock(), choose=Mock(side_effect=selections)) ui = SimpleNamespace(message=Mock(), choose=Mock(side_effect=selections))
refresh = Mock(return_value={}) refresh = Mock(return_value={})
@@ -57,7 +57,8 @@ class SelectionSetupWording(TestCase):
shutil=SimpleNamespace(which=lambda _: '/fake/pct'), saved_inventory=lambda _: next(rows), shutil=SimpleNamespace(which=lambda _: '/fake/pct'), saved_inventory=lambda _: next(rows),
_clean_orphans=lambda project: None, _clean_orphans=lambda project: None,
recover_automatically=lambda project: None, offer_recovery=lambda project, ui: None, recover_automatically=lambda project: None, offer_recovery=lambda project, ui: None,
carry_records=refresh, manage_instance=manage, menu_size=lambda: 10, carry_records=refresh, _records_unread=lambda project: unread,
manage_instance=manage, menu_size=lambda: 10,
STATUS_LABELS={}, translate=lambda s: s) STATUS_LABELS={}, translate=lambda s: s)
fn(Path('/fixture'), ui) fn(Path('/fixture'), ui)
return ui, refresh, manage return ui, refresh, manage
@@ -67,7 +68,14 @@ class SelectionSetupWording(TestCase):
ui, refresh, manage = self._menu([[row], [row]], ['101', None]) ui, refresh, manage = self._menu([[row], [row]], ['101', None])
self.assertEqual(ui.choose.call_count, 2) self.assertEqual(ui.choose.call_count, 2)
manage.assert_called_once() manage.assert_called_once()
refresh.assert_called_once_with(Path('/fixture'), mount_stopped=False, shown=True) refresh.assert_called_once_with(Path('/fixture'), mount_stopped=False, shown=True,
heading='Manage installed OCI applications')
def test_the_list_opens_without_the_sync_notice_once_every_configuration_is_cached(self):
row = {'vmid': 101, 'title': 'Jellyfin', 'hostname': 'jellyfin', 'status': 'installed', 'image': 'jellyfin/jellyfin'}
_, refresh, _ = self._menu([[row]], [None], unread=False)
refresh.assert_called_once_with(Path('/fixture'), mount_stopped=False, shown=False,
heading='Manage installed OCI applications')
def test_removing_the_last_application_returns_without_a_message(self): def test_removing_the_last_application_returns_without_a_message(self):
row = {'vmid': 101, 'title': 'Jellyfin', 'hostname': 'jellyfin', 'status': 'installed', 'image': 'jellyfin/jellyfin'} row = {'vmid': 101, 'title': 'Jellyfin', 'hostname': 'jellyfin', 'status': 'installed', 'image': 'jellyfin/jellyfin'}
@@ -3,6 +3,7 @@ import ast
from contextlib import nullcontext from contextlib import nullcontext
import copy import copy
import json import json
import os
from pathlib import Path from pathlib import Path
import re import re
import runpy import runpy
@@ -129,7 +130,9 @@ class UpdateWording(unittest.TestCase):
reconcile = ModuleType('oci_instance_reconcile') reconcile = ModuleType('oci_instance_reconcile')
reconcile.propose = lambda *args: None reconcile.propose = lambda *args: None
scope = {'sys': SimpleNamespace(path=[], executable='python3'), scope = {'sys': SimpleNamespace(path=[], executable='python3'),
'os': os, 'tempfile': tempfile, 'Path': Path,
'translate': lambda text: text, 'check_selected': lambda project, row: row, 'translate': lambda text: text, 'check_selected': lambda project, row: row,
'_selected_configuration': lambda vmid: instances.command('pct', 'config', str(vmid)),
'images': SimpleNamespace(offer_removal=lambda *args: None), 'images': SimpleNamespace(offer_removal=lambda *args: None),
'carry_records': lambda project, **options: {}, 'carry_records': lambda project, **options: {},
'_work_backup_ready': lambda project, ui, vmid: True, '_work_backup_ready': lambda project, ui, vmid: True,
@@ -0,0 +1,82 @@
"""Offline regressions for copyable commands and runtime format fields."""
import importlib.util
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
SCRIPTS = Path(__file__).resolve().parents[1]
ROOT = SCRIPTS.parents[1]
sys.path.insert(0, str(SCRIPTS))
SPEC = importlib.util.spec_from_file_location('check_terminal_catalogs', SCRIPTS / 'check_terminal_catalogs.py')
MODULE = importlib.util.module_from_spec(SPEC)
assert SPEC and SPEC.loader
SPEC.loader.exec_module(MODULE)
class TerminalCatalogsTests(unittest.TestCase):
def test_extractor_includes_launchers_remote_scripts_and_catalog_text(self):
fixtures = {
'menu': 'translate "Main launcher"',
'install_proxmenux.sh': 'translate "Stable installer"',
'install_proxmenux_beta.sh': 'translate "Beta installer"',
'scripts/check.sh': 'translate "Host shell script"',
'scripts/check.py': 'translate("Host Python script")',
'oci/src/check.py': 'N_("OCI Python script")',
'oci/remote/check.py': 'translate("Remote Python script")',
'oci/remote/check.sh': 'translate "Remote shell script"',
'oci/catalog/app.json': json.dumps({'catalog_ui': {'tagline': {'en_US': 'Application tagline'}}}),
}
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
for name, content in fixtures.items():
file = root / name
file.parent.mkdir(parents=True, exist_ok=True)
file.write_text(content, encoding='utf-8')
self.assertEqual(set(MODULE.active_sources(root)), {
'Main launcher', 'Stable installer', 'Beta installer',
'Host shell script', 'Host Python script', 'OCI Python script',
'Remote Python script', 'Remote shell script', 'Application tagline',
})
def test_runtime_format_field_cannot_be_translated(self):
source = 'Kimai has no default account. Enter the container with: pct enter {main_vmid}'
with self.assertRaisesRegex(ValueError, 'placeholder'):
MODULE.validate_message(source, 'Kimai nemá účet: pct enter {main vmid}')
MODULE.validate_message(source, 'Kimai nemá účet: pct enter {main_vmid}')
def test_copyable_command_and_environment_variable_are_literal(self):
examples = [
('Example: pct config 110 | grep unprivileged', 'Ejemplo: configuración pct 110 | grep sin privilegios'),
("Use 'pct restore' to recover the disks.", "Verwenden Sie 'pct restart', um die Datenträger wiederherzustellen."),
('Invalid ALLOWED_HOSTS value', 'Valeur HÔTES INDÉSIRABLES'),
]
for source, target in examples:
with self.subTest(source=source):
with self.assertRaisesRegex(ValueError, 'literal command or identifier'):
MODULE.validate_message(source, target)
MODULE.validate_message('Example: pct config 110 | grep unprivileged',
'Ejemplo: pct config 110 | grep unprivileged')
def test_dns_capitalization_does_not_relax_file_path_case(self):
MODULE.validate_message('Cannot reach download.proxmox.com.',
'Download.proxmox.com ist nicht erreichbar.')
with self.assertRaisesRegex(ValueError, 'literal command or identifier'):
MODULE.validate_message('Check /etc/pve/storage.cfg', 'Prüfen Sie /etc/PVE/storage.cfg')
def test_shipped_active_catalogs_keep_literal_contracts(self):
result = subprocess.run(
[sys.executable, '-B', str(SCRIPTS / 'check_terminal_catalogs.py'),
'--root', str(ROOT), '--require-complete'],
capture_output=True, text=True, timeout=60,
)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
for locale in MODULE.DEFAULT_LANGUAGES:
self.assertIn(f'[{locale}] active=', result.stdout)
self.assertNotIn('missing=1', result.stdout)
if __name__ == '__main__':
unittest.main()
@@ -15,7 +15,7 @@ class VmBackupJobsWiring(TestCase):
source = ROUTES.read_text() source = ROUTES.read_text()
routes = re.findall(r'@vm_backup_bp\.route\("([^"]+)", methods=\["(\w+)"\]\)\n@(\w+)\ndef (\w+)', source) routes = re.findall(r'@vm_backup_bp\.route\("([^"]+)", methods=\["(\w+)"\]\)\n@(\w+)\ndef (\w+)', source)
self.assertEqual(len(routes), source.count('@vm_backup_bp.route(')) self.assertEqual(len(routes), source.count('@vm_backup_bp.route('))
self.assertEqual(len(routes), 8) self.assertEqual(len(routes), 10)
for path, method, guard, name in routes: for path, method, guard, name in routes:
expected = 'require_auth' if method == 'GET' else 'require_admin_scope' expected = 'require_auth' if method == 'GET' else 'require_admin_scope'
self.assertEqual(guard, expected, f'{method} {path} ({name})') self.assertEqual(guard, expected, f'{method} {path} ({name})')
@@ -0,0 +1,94 @@
"""Validate complete catalogs without rejecting intentional English fallback."""
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import unittest
SCRIPT = Path(__file__).resolve().parents[1] / "check_web_docs_catalogs.py"
class WebDocsCatalogTests(unittest.TestCase):
def run_check(self, root, *options):
return subprocess.run(
[sys.executable, "-B", str(SCRIPT), "--messages-dir", str(root),
"--languages", "de", *options],
capture_output=True, text=True,
)
def write_catalog(self, root, locale, content):
file = root / locale / "common.json"
file.parent.mkdir(parents=True, exist_ok=True)
file.write_text(json.dumps(content), encoding="utf-8")
def test_partial_catalog_keeps_fallback_but_complete_mode_reports_gap(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
self.write_catalog(root, "en", {"title": "Updates", "body": "Check the host."})
self.write_catalog(root, "de", {"title": "Aktualisierungen"})
partial = self.run_check(root)
self.assertEqual(partial.returncode, 0, partial.stdout + partial.stderr)
self.assertIn("missing=1", partial.stdout)
complete = self.run_check(root, "--require-complete")
self.assertEqual(complete.returncode, 1, complete.stdout + complete.stderr)
self.assertIn("schema differs", complete.stdout)
def test_translated_code_is_rejected_in_both_modes(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
self.write_catalog(root, "en", {"body": "Run <code>apt update</code>."})
self.write_catalog(root, "de", {"body": "Führe <code>apt aktualisieren</code> aus."})
for options in ((), ("--require-complete",)):
with self.subTest(options=options):
result = self.run_check(root, *options)
self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
def test_bare_commands_and_operators_are_not_prose(self):
examples = [
("Restore with pct restore.", "Mit pct restart wiederherstellen."),
("pveversion | grep pve-manager", "pveversion grep pve-manager"),
("cd /opt/my-app && ./update.sh", "cd /opt/meine-app && ./update.sh"),
]
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
for source, target in examples:
with self.subTest(source=source):
self.write_catalog(root, "en", {"body": source})
self.write_catalog(root, "de", {"body": target})
result = self.run_check(root)
self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
self.assertIn("literal command or identifier", result.stdout)
def test_title_separator_can_change_without_relaxing_body_pipes(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
self.write_catalog(root, "en", {"meta": {"title": "Storage | ProxMenux"}})
self.write_catalog(root, "de", {"meta": {"title": "Speicher — ProxMenux"}})
result = self.run_check(root, "--require-complete")
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
self.write_catalog(root, "en", {"meta": {"description": "pveversion | grep pve-manager"}})
self.write_catalog(root, "de", {"meta": {"description": "pveversion grep pve-manager"}})
result = self.run_check(root)
self.assertEqual(result.returncode, 1, result.stdout + result.stderr)
def test_shipped_web_catalogs_keep_literal_contracts(self):
messages = SCRIPT.parents[2] / "web" / "messages"
result = subprocess.run(
[sys.executable, "-B", str(SCRIPT), "--messages-dir", str(messages)],
capture_output=True, text=True, timeout=60,
)
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
def test_literal_metadata_is_required_but_labels_can_be_localized(self):
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
self.write_catalog(root, "en", {"label": "Updates", "tone": "blue"})
self.write_catalog(root, "de", {"label": "Aktualisierungen", "tone": "blau"})
invalid = self.run_check(root, "--require-complete")
self.assertEqual(invalid.returncode, 1, invalid.stdout + invalid.stderr)
self.assertIn("rendering metadata differ", invalid.stdout)
self.write_catalog(root, "de", {"label": "Aktualisierungen", "tone": "blue"})
valid = self.run_check(root, "--require-complete")
self.assertEqual(valid.returncode, 0, valid.stdout + valid.stderr)
@@ -0,0 +1,41 @@
"""Exercise the static-export language repair on real HTML files."""
from pathlib import Path
import subprocess
import tempfile
import unittest
from test_command_descriptions import require_node
ROOT = Path(__file__).resolve().parents[3]
SCRIPT = ROOT / "web/scripts/localize-export.mjs"
class WebExportLanguageTests(unittest.TestCase):
def test_locales_nested_pages_preserved_content_and_idempotence(self):
node = require_node()
html = '<!doctype html><html lang="en" class="dark"><body><h1>Text</h1><script>const example=\'<html lang="en">\';</script></body></html>'
with tempfile.TemporaryDirectory() as temporary:
root = Path(temporary)
locales = ["en", "es", "de", "fr", "it", "pt", "sk", "sv"]
for locale in locales:
file = root / "out" / locale / "docs/monitor/index.html"
file.parent.mkdir(parents=True)
file.write_text(html)
for relative in ["out/index.html", "out/assets/index.html"]:
file = root / relative
file.parent.mkdir(parents=True, exist_ok=True)
file.write_text(html)
result = subprocess.run([node, str(SCRIPT)], cwd=root, capture_output=True, text=True)
self.assertEqual(result.returncode, 0, result.stderr)
self.assertIn("8 localized pages", result.stdout)
for locale in locales:
actual = (root / "out" / locale / "docs/monitor/index.html").read_text()
self.assertEqual(actual, html.replace('<html lang="en"', f'<html lang="{locale}"', 1))
for relative in ["out/index.html", "out/assets/index.html"]:
self.assertEqual((root / relative).read_text(), html)
tracked = root / "out/de/docs/monitor/index.html"
before = tracked.stat().st_mtime_ns
second = subprocess.run([node, str(SCRIPT)], cwd=root, capture_output=True, text=True)
self.assertEqual(second.returncode, 0, second.stderr)
self.assertEqual(tracked.stat().st_mtime_ns, before)
+3 -8
View File
@@ -1,14 +1,9 @@
name: Build web documentation translations name: Build web documentation translations
on: on:
push: # Source and locale corrections are reviewed together. An automatic run
branches: [develop] # would treat the changed English fingerprints as stale translations and
paths: # overwrite those reviewed locale strings with provider output.
- 'web/messages/en/*.json'
- 'web/messages/en/**/*.json'
- '.github/scripts/build_web_docs_i18n.py'
- '.github/scripts/build_translation_cache.py'
- '.github/workflows/build-web-docs-i18n.yml'
workflow_dispatch: workflow_dispatch:
inputs: inputs:
languages: languages:
@@ -0,0 +1,50 @@
name: Apprise Slack and Matrix
on:
pull_request:
paths:
- '.github/workflows/test-apprise-services.yml'
- 'AppImage/scripts/notification_channels.py'
- 'AppImage/components/apprise-service-help.tsx'
- 'AppImage/components/notification-settings.tsx'
- 'tests/test_apprise_services.py'
- 'tests/test_apprise_service_help.cjs'
push:
branches: [main, develop]
paths:
- 'AppImage/scripts/notification_channels.py'
- 'AppImage/components/apprise-service-help.tsx'
- 'tests/test_apprise_services.py'
- 'tests/test_apprise_service_help.cjs'
workflow_dispatch:
permissions:
contents: read
jobs:
apprise-services:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
matrix:
apprise: ['1.7.0', '2.0.1']
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- uses: actions/setup-node@v6
with:
node-version: '22.14.0'
- name: Install fixture dependencies
run: |
python -m pip install 'apprise==${{ matrix.apprise }}'
npm ci --prefix .github/ci/offline-node --ignore-scripts --no-audit --no-fund
- name: Test real provider plugins with mocked HTTP
run: python -B -m unittest discover -s tests -p test_apprise_services.py -v
- name: Render provider help and locale fallback
env:
NODE_PATH: ${{ github.workspace }}/.github/ci/offline-node/node_modules
run: node tests/test_apprise_service_help.cjs
@@ -0,0 +1,50 @@
name: Backup storage retention
on:
pull_request:
paths:
- '.github/workflows/test-backup-storage-retention.yml'
- 'AppImage/scripts/flask_vm_backup_routes.py'
- 'AppImage/components/backup-storage-retention.tsx'
- 'AppImage/components/backup-tab.tsx'
- 'AppImage/components/vm-backup-jobs.tsx'
- 'AppImage/lib/backup-retention.ts'
- 'tests/test_vm_backup_jobs_api.py'
- 'tests/test_backup_storage_retention.cjs'
push:
branches: [main, develop]
paths:
- 'AppImage/scripts/flask_vm_backup_routes.py'
- 'AppImage/components/backup-storage-retention.tsx'
- 'AppImage/lib/backup-retention.ts'
- 'tests/test_vm_backup_jobs_api.py'
- 'tests/test_backup_storage_retention.cjs'
workflow_dispatch:
permissions:
contents: read
jobs:
storage-retention:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- uses: actions/setup-node@v6
with:
node-version: '22.14.0'
- name: Install fixture dependencies
run: |
python -m pip install 'Flask==3.1.2'
npm ci --prefix .github/ci/offline-node --ignore-scripts --no-audit --no-fund
- name: Test native job and storage configuration APIs
run: python -B -m unittest discover -s tests -p test_vm_backup_jobs_api.py -v
- name: Test shared retention properties
env:
NODE_PATH: ${{ github.workspace }}/.github/ci/offline-node/node_modules
run: node tests/test_backup_storage_retention.cjs
+73
View File
@@ -0,0 +1,73 @@
name: Figurine installation
on:
pull_request:
paths:
- '.github/workflows/test-figurine.yml'
- 'tests/test_figurine_installation.py'
- 'scripts/post_install/customizable_post_install.sh'
- 'AppImage/scripts/post_install_versions.py'
- 'AppImage/scripts/flask_proxmenux_routes.py'
push:
branches: [main, develop]
paths:
- '.github/workflows/test-figurine.yml'
- 'tests/test_figurine_installation.py'
- 'scripts/post_install/customizable_post_install.sh'
- 'AppImage/scripts/post_install_versions.py'
- 'AppImage/scripts/flask_proxmenux_routes.py'
workflow_dispatch:
permissions:
contents: read
jobs:
installer-and-release:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Check shell syntax and isolated installer regressions
run: |
bash -n scripts/post_install/customizable_post_install.sh
python3 -B -m unittest discover -s tests -p test_figurine_installation.py -v
- name: Install ARM user-mode emulation for release smoke tests
run: |
sudo apt-get update -qq
sudo apt-get install -y --no-install-recommends qemu-user
- name: Verify and run the pinned AMD64, ARM64 and ARMv7 release binaries
env:
FIGURINE_VERSION: '2.1.1'
shell: bash
run: |
set -euo pipefail
scratch="$(mktemp -d)"
trap 'rm -rf "$scratch"' EXIT
while read -r arch sha; do
archive="figurine_linux_${arch}_v${FIGURINE_VERSION}.tar.gz"
target="$scratch/$arch"
mkdir -p "$target"
curl --fail --location --retry 3 --output "$target/$archive" \
"https://github.com/arsham/figurine/releases/download/v${FIGURINE_VERSION}/$archive"
printf '%s %s\n' "$sha" "$target/$archive" | sha256sum --check
tar -xf "$target/$archive" -C "$target"
runner=()
case "$arch" in
arm64) runner=(qemu-aarch64) ;;
arm) runner=(qemu-arm) ;;
esac
"${runner[@]}" "$target/deploy/figurine" --version | tee "$target/version"
grep -F "figurine version v$FIGURINE_VERSION" "$target/version"
"${runner[@]}" "$target/deploy/figurine" -f "3d.flf" "ProxMenux-test" > "$target/banner"
test -s "$target/banner"
printf 'PASS: %s version and login banner\n' "$arch"
done <<'ASSETS'
amd64 f2b1c076942c94009d133df0f304a234a0994bf590b40f2927668324d578306a
arm64 611550ddb31a75437be3f8587bcdd029d0d890f4d820b7f494a2c6615fecba10
arm 429e66bded486d49f730c4a6559e728cd1e9a163a0f56a85867e2cbec010171d
ASSETS
+54
View File
@@ -0,0 +1,54 @@
name: Guest backup archives
on:
pull_request:
paths:
- '.github/workflows/test-guest-backups.yml'
- 'AppImage/scripts/guest_backup_archives.py'
- 'AppImage/scripts/flask_server.py'
- 'AppImage/scripts/jwt_middleware.py'
- 'AppImage/scripts/build_appimage.sh'
- 'AppImage/components/guest-backup-list.tsx'
- 'AppImage/components/virtual-machines.tsx'
- 'AppImage/lib/guest-backup-archives.ts'
- 'AppImage/messages/**'
- 'tests/test_guest_backup_archives.*'
push:
branches: [main, develop]
paths:
- 'AppImage/scripts/guest_backup_archives.py'
- 'AppImage/scripts/flask_server.py'
- 'AppImage/scripts/jwt_middleware.py'
- 'AppImage/components/guest-backup-list.tsx'
- 'AppImage/components/virtual-machines.tsx'
- 'AppImage/lib/guest-backup-archives.ts'
- 'tests/test_guest_backup_archives.*'
workflow_dispatch:
permissions:
contents: read
jobs:
guest-backups:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- uses: actions/setup-node@v6
with:
node-version: '22.14.0'
- name: Install fixture dependencies
run: |
python -m pip install 'Flask==3.1.2'
npm ci --prefix .github/ci/offline-node --ignore-scripts --no-audit --no-fund
- name: Test native deletion, protection, authorization and cache invalidation
run: python -B -m unittest discover -s tests -p test_guest_backup_archives.py -v
- name: Test frontend deletion and completion handling
env:
NODE_PATH: ${{ github.workspace }}/.github/ci/offline-node/node_modules
run: node tests/test_guest_backup_archives.cjs
+14 -2
View File
@@ -1,4 +1,4 @@
name: Offline i18n tests name: Offline script regressions
on: on:
pull_request: pull_request:
@@ -9,15 +9,20 @@ on:
- 'AppImage/messages/**' - 'AppImage/messages/**'
- 'AppImage/lib/i18n/**' - 'AppImage/lib/i18n/**'
- 'AppImage/components/terminal-panel.tsx' - 'AppImage/components/terminal-panel.tsx'
- 'web/**'
- 'CONTRIBUTING.md' - 'CONTRIBUTING.md'
# Scheduler consumers and the offline extractor's .sh/.func inputs. # Scheduler consumers and the offline extractor's .sh/.func inputs.
- 'scripts/**/*.sh' - 'scripts/**/*.sh'
- 'scripts/**/*.func' - 'scripts/**/*.func'
- 'scripts/**/*.py' - 'scripts/**/*.py'
- 'lang/*.json' - 'lang/*.json'
- 'menu'
- 'install_proxmenux.sh'
- 'install_proxmenux_beta.sh'
- 'oci/src/**/*.py' - 'oci/src/**/*.py'
- 'oci/catalog/**/*.json' - 'oci/catalog/**/*.json'
- 'oci/remote/**/*.sh' - 'oci/remote/**/*.sh'
- 'oci/remote/**/*.py'
push: push:
branches: [main, develop] branches: [main, develop]
paths: paths:
@@ -27,21 +32,28 @@ on:
- 'AppImage/messages/**' - 'AppImage/messages/**'
- 'AppImage/lib/i18n/**' - 'AppImage/lib/i18n/**'
- 'AppImage/components/terminal-panel.tsx' - 'AppImage/components/terminal-panel.tsx'
- 'web/**'
- 'CONTRIBUTING.md' - 'CONTRIBUTING.md'
# Scheduler consumers and the offline extractor's .sh/.func inputs. # Scheduler consumers and the offline extractor's .sh/.func inputs.
- 'scripts/**/*.sh' - 'scripts/**/*.sh'
- 'scripts/**/*.func' - 'scripts/**/*.func'
- 'scripts/**/*.py' - 'scripts/**/*.py'
- 'lang/*.json' - 'lang/*.json'
- 'menu'
- 'install_proxmenux.sh'
- 'install_proxmenux_beta.sh'
- 'oci/src/**/*.py' - 'oci/src/**/*.py'
- 'oci/catalog/**/*.json' - 'oci/catalog/**/*.json'
- 'oci/remote/**/*.sh' - 'oci/remote/**/*.sh'
- 'oci/remote/**/*.py'
workflow_dispatch: workflow_dispatch:
permissions: permissions:
contents: read contents: read
jobs: jobs:
# Preserve this check ID for existing branch protection rules. This suite
# includes menu, download and scheduler regressions as well as i18n tests.
offline-i18n: offline-i18n:
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 5 timeout-minutes: 5
@@ -61,7 +73,7 @@ jobs:
test -x /usr/bin/jq test -x /usr/bin/jq
- name: Install OCI wording test dependency - name: Install OCI wording test dependency
run: python3 -m pip install 'PyYAML>=6,<7' run: python3 -m pip install 'PyYAML>=6,<7'
- name: Run complete offline script test suite - name: Run offline i18n and script regressions
env: env:
PYTHONDONTWRITEBYTECODE: '1' PYTHONDONTWRITEBYTECODE: '1'
run: python3 -m unittest discover -s .github/scripts/tests -v run: python3 -m unittest discover -s .github/scripts/tests -v
@@ -0,0 +1,55 @@
name: Disk policy and host monitor regressions
on:
pull_request:
paths:
- '.github/workflows/test-lvm-disk-policy.yml'
- 'AppImage/scripts/lvm_device_scope.py'
- 'AppImage/scripts/disk_temperature_history.py'
- 'AppImage/scripts/flask_server.py'
- 'AppImage/scripts/health_monitor.py'
- 'AppImage/scripts/flask_notification_routes.py'
- 'AppImage/scripts/proxmox_storage_monitor.py'
- 'AppImage/scripts/build_appimage.sh'
- 'AppImage/scripts/tests/test_host_backup_node.py'
- 'AppImage/scripts/tests/test_proxmox_node_name.py'
- 'AppImage/scripts/tests/test_pve_notifications_cfg_writes.py'
- 'tests/test_lvm_disk_read_policy.py'
push:
branches: [main, develop]
paths:
- 'AppImage/scripts/lvm_device_scope.py'
- 'AppImage/scripts/disk_temperature_history.py'
- 'AppImage/scripts/flask_server.py'
- 'AppImage/scripts/health_monitor.py'
- 'AppImage/scripts/flask_notification_routes.py'
- 'AppImage/scripts/proxmox_storage_monitor.py'
- 'AppImage/scripts/build_appimage.sh'
- 'AppImage/scripts/tests/test_host_backup_node.py'
- 'AppImage/scripts/tests/test_proxmox_node_name.py'
- 'AppImage/scripts/tests/test_pve_notifications_cfg_writes.py'
- 'tests/test_lvm_disk_read_policy.py'
workflow_dispatch:
permissions:
contents: read
jobs:
disk-policy:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Test automatic LVM readers with mocked inventory and policy
run: python -I -B -m unittest discover -s tests -p test_lvm_disk_read_policy.py -v
- name: Test backup node ownership and former jobs
run: python -I -B -m unittest discover -s AppImage/scripts/tests -p test_host_backup_node.py -v
- name: Test local node lookup without Proxmox API commands
run: python -I -B -m unittest discover -s AppImage/scripts/tests -p test_proxmox_node_name.py -v
- name: Test notification configuration writes against a cluster filesystem fixture
run: python -I -B -m unittest discover -s AppImage/scripts/tests -p test_pve_notifications_cfg_writes.py -v
+48
View File
@@ -28,7 +28,55 @@ jobs:
python-version: '3.13' python-version: '3.13'
- name: Install OCI test dependencies - name: Install OCI test dependencies
run: python3 -m pip install -r oci/requirements.txt run: python3 -m pip install -r oci/requirements.txt
- name: Run OCI image reuse and resource edit regressions
env:
PYTHONDONTWRITEBYTECODE: '1'
run: |
python3 -m unittest discover -s oci/tests -p test_modify_image_reuse.py -v
python3 -m unittest discover -s oci/tests -p test_native_modify.py -v
python3 -m unittest discover -s oci/tests -p test_modify_review.py -v
python3 -m unittest discover -s oci/tests -p test_modify_review_transaction.py -v
python3 -m unittest discover -s oci/tests -p test_stage_release.py -v
- name: Run carried record, recovery and configuration cache regressions
env:
PYTHONDONTWRITEBYTECODE: '1'
run: |
python3 -m unittest discover -s oci/tests -p 'test_carried*.py' -v
python3 -m unittest discover -s oci/tests -p test_restore_recovery.py -v
- name: Run read-only volume preparation regressions
env:
PYTHONDONTWRITEBYTECODE: '1'
run: |
test -x /bin/bash
test -x /usr/bin/jq
python3 -m unittest discover -s oci/tests -p test_readonly_volume_preparation.py -v
- name: Run offline registry account tests - name: Run offline registry account tests
env: env:
PYTHONDONTWRITEBYTECODE: '1' PYTHONDONTWRITEBYTECODE: '1'
run: python3 -m unittest discover -s oci/tests -p 'test_registry_*.py' -v run: python3 -m unittest discover -s oci/tests -p 'test_registry_*.py' -v
- name: Run OCI Modify latency regressions
env:
PYTHONDONTWRITEBYTECODE: '1'
run: |
python3 -m unittest discover -s oci/tests -p test_modify_latency.py -v
python3 -m unittest discover -s oci/tests -p test_menu_performance.py -v
python3 -m unittest discover -s oci/tests -p test_dialog_escape.py -v
python3 -m unittest discover -s oci/tests -p test_work_backup.py -v
python3 -m unittest discover -s oci/tests -p test_recreation.py -v
- name: Run OCI modification menu regressions
env:
PYTHONDONTWRITEBYTECODE: '1'
run: |
python3 -m unittest discover -s oci/tests -p test_modify_menu.py -v
python3 -m unittest discover -s oci/tests -p test_manage_sync_notice.py -v
python3 -m unittest discover -s oci/tests -p test_recreation.py -v
python3 -m unittest discover -s oci/tests -p test_shared_directories.py -v
python3 -m unittest discover -s oci/tests -p 'test_wizard*.py' -v
- name: Run external OCI settings regressions
env:
PYTHONDONTWRITEBYTECODE: '1'
run: |
python3 -m unittest discover -s oci/tests -p 'test_external_tags.py' -v
python3 -m unittest discover -s oci/tests -p 'test_instance_reconcile.py' -v
python3 -m unittest discover -s oci/tests -p 'test_guessed_endpoint_healthcheck.py' -v
python3 -m unittest discover -s oci/tests -p 'test_runtime_home_*.py' -v
+6 -2
View File
@@ -12,6 +12,7 @@ on:
- 'AppImage/package-lock.json' - 'AppImage/package-lock.json'
- 'AppImage/scripts/*.py' - 'AppImage/scripts/*.py'
- 'AppImage/scripts/tests/test_health_persistence_recurrence.py' - 'AppImage/scripts/tests/test_health_persistence_recurrence.py'
- 'AppImage/scripts/tests/test_native_app_discovery.py'
- 'AppImage/app/**' - 'AppImage/app/**'
- 'AppImage/components/**' - 'AppImage/components/**'
- 'AppImage/hooks/**' - 'AppImage/hooks/**'
@@ -32,6 +33,7 @@ on:
- 'AppImage/package-lock.json' - 'AppImage/package-lock.json'
- 'AppImage/scripts/*.py' - 'AppImage/scripts/*.py'
- 'AppImage/scripts/tests/test_health_persistence_recurrence.py' - 'AppImage/scripts/tests/test_health_persistence_recurrence.py'
- 'AppImage/scripts/tests/test_native_app_discovery.py'
- 'AppImage/app/**' - 'AppImage/app/**'
- 'AppImage/components/**' - 'AppImage/components/**'
- 'AppImage/hooks/**' - 'AppImage/hooks/**'
@@ -56,7 +58,9 @@ jobs:
- uses: actions/setup-python@v5 - uses: actions/setup-python@v5
with: with:
python-version: '3.11' python-version: '3.11'
- name: Run nine qualified Python files - name: Install native discovery route fixture dependency
run: python3 -m pip install 'Flask>=3,<4'
- name: Run qualified Python files
env: env:
PYTHONDONTWRITEBYTECODE: '1' PYTHONDONTWRITEBYTECODE: '1'
run: python3 -I -B .github/scripts/run_offline_qualified.py --lane python run: python3 -I -B .github/scripts/run_offline_qualified.py --lane python
@@ -82,7 +86,7 @@ jobs:
run: | run: |
npm ci --prefix .github/ci/offline-node --legacy-peer-deps --ignore-scripts --no-audit --no-fund npm ci --prefix .github/ci/offline-node --legacy-peer-deps --ignore-scripts --no-audit --no-fund
ln -s ../.github/ci/offline-node/node_modules AppImage/node_modules ln -s ../.github/ci/offline-node/node_modules AppImage/node_modules
- name: Run eleven qualified Node files - name: Run qualified Node files
env: env:
NODE_PATH: ${{ github.workspace }}/AppImage/node_modules NODE_PATH: ${{ github.workspace }}/AppImage/node_modules
PYTHONDONTWRITEBYTECODE: '1' PYTHONDONTWRITEBYTECODE: '1'
+66
View File
@@ -0,0 +1,66 @@
name: PBS support
on:
pull_request:
paths:
- '.github/workflows/test-pbs.yml'
- 'AppImage/scripts/*pbs*.py'
- 'AppImage/scripts/tests/test_pbs.py'
- 'AppImage/scripts/health_monitor.py'
- 'AppImage/scripts/auth_manager.py'
- 'AppImage/scripts/build_appimage.sh'
- 'AppImage/components/pbs-*.tsx'
- 'AppImage/components/proxmox-dashboard.tsx'
- 'AppImage/tests/pbs-navigation.cjs'
- 'scripts/global/proxmox_platform.py'
- 'scripts/global/platform-functions.sh'
- 'scripts/global/repository-functions.sh'
- 'scripts/menus/pbs_menu.sh'
- 'scripts/utilities/pbs_update.sh'
- 'scripts/global/update-pbs.sh'
- 'scripts/menus/menu_post_install.sh'
- 'scripts/post_install/*.sh'
push:
branches: [main, develop]
paths:
- 'AppImage/scripts/*pbs*.py'
- 'scripts/global/proxmox_platform.py'
- '.github/workflows/test-pbs.yml'
workflow_dispatch:
permissions:
contents: read
jobs:
pbs:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
python: ['3.11', '3.13']
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- name: Install test dependencies
run: python -m pip install Flask PyJWT cryptography pyotp segno
- name: Platform, real TLS, APIs, health and AppImage packaging
run: |
python -B -m unittest discover -s AppImage/scripts/tests -p test_pbs.py -v
python -B -m unittest discover -s AppImage/scripts/tests -p test_auth_manager_ssl_reload.py -v
for script in scripts/global/platform-functions.sh scripts/global/repository-functions.sh scripts/menus/pbs_menu.sh scripts/utilities/pbs_update.sh scripts/menus/main_menu.sh scripts/post_install/auto_post_install.sh scripts/post_install/customizable_post_install.sh scripts/post_install/uninstall-tools.sh scripts/global/update-pbs.sh scripts/menus/menu_post_install.sh; do
bash -n "$script"
done
- uses: actions/setup-node@v6
if: matrix.python == '3.11'
with:
node-version: '22.14.0'
- name: Dashboard platform routing
if: matrix.python == '3.11'
run: |
npm ci --prefix .github/ci/offline-node --legacy-peer-deps --ignore-scripts --no-audit --no-fund
NODE_PATH="$PWD/.github/ci/offline-node/node_modules" node AppImage/tests/pbs-navigation.cjs
@@ -0,0 +1,32 @@
"use client"
import { ExternalLink } from "lucide-react"
import { useT } from "../lib/i18n/provider"
// These are examples, never defaults: opening help must not replace a saved
// destination or put placeholder credentials into the notification settings.
export function AppriseServiceHelp() {
const t = useT()
return <details className="rounded-md border border-border bg-muted/20 p-2 text-xs">
<summary className="cursor-pointer font-medium">{t("settings.notifications.ui.appriseServiceHelp")}</summary>
<div className="grid gap-3 pt-3 sm:grid-cols-2">
<div className="space-y-1.5 min-w-0">
<h4 className="font-medium">Slack</h4>
<p className="text-muted-foreground">{t("settings.notifications.ui.appriseSlackSteps")}</p>
<code className="block break-all rounded bg-background p-2 text-[10px]">https://hooks.slack.com/services/TOKEN_A/TOKEN_B/TOKEN_C</code>
<a href="https://appriseit.com/services/slack/" target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1 text-cyan-500 hover:underline">
{t("settings.notifications.ui.appriseSlackDocs")} <ExternalLink className="h-3 w-3" />
</a>
</div>
<div className="space-y-1.5 min-w-0">
<h4 className="font-medium">Matrix</h4>
<p className="text-muted-foreground">{t("settings.notifications.ui.appriseMatrixSteps")}</p>
<code className="block break-all rounded bg-background p-2 text-[10px]">matrixs://USER:PASSWORD@matrix.example.com/!ROOM_ID:matrix.example.com</code>
<a href="https://appriseit.com/services/matrix/" target="_blank" rel="noopener noreferrer" className="inline-flex items-center gap-1 text-cyan-500 hover:underline">
{t("settings.notifications.ui.appriseMatrixDocs")} <ExternalLink className="h-3 w-3" />
</a>
</div>
</div>
<p className="mt-3 text-muted-foreground">{t("settings.notifications.ui.appriseServiceFinish")}</p>
</details>
}
@@ -0,0 +1,105 @@
"use client"
import { useState } from "react"
import useSWR from "swr"
import { HardDrive, Loader2, Pencil, RefreshCw } from "lucide-react"
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"
import { Badge } from "./ui/badge"
import { Button } from "./ui/button"
import { Checkbox } from "./ui/checkbox"
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "./ui/dialog"
import { Input } from "./ui/input"
import { Label } from "./ui/label"
import { fetchApi } from "../lib/api-config"
import { buildRetention, parseRetention, RETENTION } from "../lib/backup-retention"
import { useT } from "../lib/i18n/provider"
interface StorageRetention { id: string; type: string; prune_backups: string; digest: string }
const fetcher = (url: string) => fetchApi<{ storages: StorageRetention[] }>(url)
export function BackupStorageRetention() {
const t = useT()
const { data, error, mutate, isValidating } = useSWR("/api/vm-backup-storages", fetcher)
const [editing, setEditing] = useState<StorageRetention | null>(null)
const [fields, setFields] = useState<Record<string, string>>({})
const [keepAll, setKeepAll] = useState(false)
const [useDefault, setUseDefault] = useState(false)
const [busy, setBusy] = useState(false)
const [message, setMessage] = useState("")
const [saveError, setSaveError] = useState("")
function edit(storage: StorageRetention) {
const policy = parseRetention(storage.prune_backups)
setFields(policy.fields)
setKeepAll(policy.keepAll)
setUseDefault(!storage.prune_backups)
setSaveError("")
setMessage("")
setEditing(storage)
}
async function save() {
if (!editing || busy) return
const prune = useDefault ? "" : buildRetention(fields, keepAll)
if (!useDefault && !prune) return setSaveError(t("backup.storageRetention.choosePolicy"))
setBusy(true)
setSaveError("")
try {
await fetchApi(`/api/vm-backup-storages/${encodeURIComponent(editing.id)}`, {
method: "PUT", body: JSON.stringify({ digest: editing.digest, prune_backups: prune }),
})
setMessage(t("backup.storageRetention.saved"))
setEditing(null)
await mutate()
} catch (error) {
setSaveError(`${t("backup.storageRetention.saveFailed")} ${error instanceof Error ? error.message : ""}`)
} finally { setBusy(false) }
}
return <Card className="border-border bg-card/50">
<CardHeader className="pb-3 flex-row items-center justify-between gap-2">
<CardTitle className="text-base flex items-center gap-2"><HardDrive className="h-4 w-4" />{t("backup.storageRetention.title")}</CardTitle>
<Button variant="ghost" size="sm" disabled={isValidating} onClick={() => { void mutate().catch(() => {}) }}>
<RefreshCw className={`h-4 w-4 mr-2 ${isValidating ? "animate-spin" : ""}`} />{t("backup.storageRetention.refresh")}
</Button>
</CardHeader>
<CardContent className="space-y-3">
<p className="text-xs text-muted-foreground">{t("backup.storageRetention.description")}</p>
{message && <p role="status" className="text-xs text-emerald-500">{message}</p>}
{error ? <p role="alert" className="text-xs text-destructive">{t("backup.storageRetention.loadFailed")}</p> : !data ?
<Loader2 className="h-4 w-4 animate-spin" /> : !data.storages.length ?
<p className="text-xs text-muted-foreground">{t("backup.storageRetention.empty")}</p> :
data.storages.map(storage => <div key={storage.id} className="flex flex-wrap gap-2 items-center border rounded-lg p-3">
<span className="font-medium text-sm">{storage.id}</span><Badge variant="outline">{storage.type}</Badge>
<span className="text-xs font-mono text-muted-foreground break-all">{storage.prune_backups || t("backup.storageRetention.default")}</span>
<Button variant="outline" size="sm" className="ml-auto gap-1" onClick={() => edit(storage)} disabled={!storage.digest || busy}>
<Pencil className="h-3 w-3" />{t("backup.storageRetention.edit")}
</Button>
</div>)}
</CardContent>
<Dialog open={!!editing} onOpenChange={open => { if (!open && !busy) setEditing(null) }}>
<DialogContent className="bg-accent [&_input]:bg-background">
<DialogHeader><DialogTitle>{t("backup.storageRetention.editTitle", { storage: editing?.id ?? "" })}</DialogTitle>
<DialogDescription>{t("backup.storageRetention.effect")}</DialogDescription></DialogHeader>
<Label className="flex items-center gap-2"><Checkbox checked={useDefault} disabled={busy} onCheckedChange={checked => {
setUseDefault(checked === true)
if (checked) { setKeepAll(false); setFields({}) }
}} />{t("backup.storageRetention.useDefault")}</Label>
<Label className="flex items-center gap-2"><Checkbox checked={keepAll} disabled={useDefault || busy} onCheckedChange={checked => {
setKeepAll(checked === true)
if (checked) setFields({})
}} />{t("backup.storageRetention.keepAll")}</Label>
<div className="grid grid-cols-2 gap-3">
{RETENTION.map(key => <div key={key} className="space-y-1">
<Label htmlFor={`storage-retention-${key}`}>{t(`backup.retention.keep${key[0].toUpperCase()}${key.slice(1)}`)}</Label>
<Input id={`storage-retention-${key}`} type="number" min={1} max={99999} step={1} disabled={useDefault || keepAll || busy}
value={fields[key] ?? ""} onChange={event => setFields(previous => ({ ...previous, [key]: event.target.value }))} />
</div>)}
</div>
{saveError && <p role="alert" className="text-xs text-destructive break-words">{saveError}</p>}
<DialogFooter><Button variant="outline" disabled={busy} onClick={() => setEditing(null)}>{t("backup.storageRetention.cancel")}</Button>
<Button disabled={busy} onClick={save}>{busy && <Loader2 className="h-4 w-4 mr-2 animate-spin" />}{t("backup.storageRetention.save")}</Button></DialogFooter>
</DialogContent>
</Dialog>
</Card>
}
+7 -4
View File
@@ -7,9 +7,11 @@ import { useState } from "react"
import { DatabaseBackup } from "lucide-react" import { DatabaseBackup } from "lucide-react"
import { HostBackup } from "./host-backup" import { HostBackup } from "./host-backup"
import { VmBackupJobs } from "./vm-backup-jobs" import { VmBackupJobs } from "./vm-backup-jobs"
import { PBSPanel } from "./pbs-panel"
import { BackupStorageRetention } from "./backup-storage-retention"
import { useT } from "../lib/i18n/provider" import { useT } from "../lib/i18n/provider"
type BackupView = "host" | "guests" type BackupView = "host" | "guests" | "pbs"
export function BackupTab() { export function BackupTab() {
const t = useT() const t = useT()
@@ -29,7 +31,7 @@ export function BackupTab() {
className="flex w-full rounded-lg border border-border bg-muted/40 p-1 gap-1 className="flex w-full rounded-lg border border-border bg-muted/40 p-1 gap-1
sm:inline-flex sm:w-auto" sm:inline-flex sm:w-auto"
> >
{(["host", "guests"] as const).map((key) => ( {(["host", "guests", "pbs"] as const).map((key) => (
<button <button
key={key} key={key}
type="button" type="button"
@@ -41,7 +43,7 @@ export function BackupTab() {
: "text-muted-foreground hover:text-foreground hover:bg-background/60" : "text-muted-foreground hover:text-foreground hover:bg-background/60"
}`} }`}
> >
{t(`backup.viewSwitch.${key}`)} {key === "pbs" ? t("pbs.title") : t(`backup.viewSwitch.${key}`)}
</button> </button>
))} ))}
</div> </div>
@@ -51,7 +53,8 @@ export function BackupTab() {
<div className={view === "host" ? "" : "hidden"}> <div className={view === "host" ? "" : "hidden"}>
<HostBackup /> <HostBackup />
</div> </div>
{view === "guests" && <VmBackupJobs />} {view === "guests" && <><VmBackupJobs /><BackupStorageRetention /></>}
{view === "pbs" && <PBSPanel />}
</div> </div>
) )
} }
+135
View File
@@ -0,0 +1,135 @@
"use client"
import { useEffect, useRef, useState } from "react"
import { Archive, Clock, Loader2, LockKeyhole, RefreshCw, Trash2 } from "lucide-react"
import { Badge } from "./ui/badge"
import { Button } from "./ui/button"
import { Checkbox } from "./ui/checkbox"
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "./ui/dialog"
import { useT } from "@/lib/i18n/provider"
import { deleteGuestBackups, type GuestBackup } from "../lib/guest-backup-archives"
export function GuestBackupList({ vmid, backups, loading, onRefresh, storageColor }: {
vmid: number
backups: GuestBackup[]
loading: boolean
onRefresh: () => Promise<void>
storageColor?: (storage: string) => { bg: string; text: string; border: string }
}) {
const t = useT()
const [selected, setSelected] = useState<string[]>([])
const [confirm, setConfirm] = useState(false)
const [busy, setBusy] = useState(false)
const [refreshing, setRefreshing] = useState(false)
const [message, setMessage] = useState("")
const [error, setError] = useState("")
const mounted = useRef(true)
useEffect(() => {
mounted.current = true
return () => { mounted.current = false }
}, [])
// A fresh list can remove a selection or reveal a newly protected archive.
useEffect(() => {
setSelected(previous => previous.filter(volid => backups.some(backup => backup.volid === volid && !backup.protected)))
}, [backups])
const chosen = backups.filter(backup => selected.includes(backup.volid) && !backup.protected)
const selectable = backups.filter(backup => !backup.protected)
const disabled = loading || busy || refreshing
const storageClass = (storage: string) => {
const color = storageColor?.(storage)
return color ? `${color.bg} ${color.text} ${color.border}` : ""
}
async function refresh() {
setRefreshing(true)
setError("")
try { await onRefresh() } catch { if (mounted.current) setError(t("vmLxc.backups.refreshFailed")) }
finally { if (mounted.current) setRefreshing(false) }
}
async function remove() {
if (!chosen.length || busy) return
setBusy(true)
setError("")
setMessage("")
try {
const results = await deleteGuestBackups(vmid, chosen)
if (!mounted.current) return
const deleted = results.filter(result => result.status === "deleted").length
const unconfirmed = results.length - deleted
setMessage(t("vmLxc.backups.deletedCount", { count: deleted }))
if (unconfirmed) setError(t("vmLxc.backups.unconfirmedCount", { count: unconfirmed }))
setSelected([])
} catch (error) {
if (mounted.current) setError(`${t("vmLxc.backups.deleteFailed")} ${error instanceof Error ? error.message : ""}`)
} finally {
// A timeout can hide a successful native task. Always bypass both caches.
try { await onRefresh() } catch { if (mounted.current) setError(t("vmLxc.backups.refreshFailed")) }
if (mounted.current) { setBusy(false); setConfirm(false) }
}
}
return <>
<div className="flex items-center justify-between gap-2 mb-3 shrink-0 flex-wrap">
<div className="flex items-center gap-2">
<span className="text-xs text-muted-foreground">{t("vmLxc.backups.available")}</span>
<Badge variant="secondary" className="text-xs h-5">{backups.length}</Badge>
</div>
<div className="flex items-center gap-2">
<Button size="sm" variant="ghost" className="h-7 text-xs gap-1" disabled={disabled} onClick={refresh}>
<RefreshCw className={`h-3 w-3 ${refreshing ? "animate-spin" : ""}`} />{t("vmLxc.backups.refresh")}
</Button>
<Button size="sm" variant="destructive" className="h-7 text-xs gap-1" disabled={disabled || !chosen.length} onClick={() => setConfirm(true)}>
{busy ? <Loader2 className="h-3 w-3 animate-spin" /> : <Trash2 className="h-3 w-3" />}
{t("vmLxc.backups.deleteSelected", { count: chosen.length })}
</Button>
</div>
</div>
{message && <p role="status" className="text-xs text-muted-foreground mb-2">{message}</p>}
{error && <p role="alert" className="text-xs text-destructive mb-2 break-words">{error}</p>}
{loading ? <div className="flex-1 flex items-center justify-center text-muted-foreground min-h-0">
<Loader2 className="h-4 w-4 animate-spin mr-2" /><span className="text-sm">{t("vmLxc.backups.loading")}</span>
</div> : backups.length === 0 ? <div className="flex-1 flex flex-col items-center justify-center text-muted-foreground min-h-0">
<Archive className="h-12 w-12 mb-3 opacity-30" /><span className="text-sm">{t("vmLxc.backups.empty")}</span>
<span className="text-xs mt-1">{t("vmLxc.backups.emptyHint")}</span>
</div> : <>
<label className="flex items-center gap-2 mb-3 text-xs text-muted-foreground">
<Checkbox checked={selectable.length > 0 && chosen.length === Math.min(20, selectable.length)} disabled={disabled || !selectable.length}
onCheckedChange={checked => setSelected(checked ? selectable.slice(0, 20).map(backup => backup.volid) : [])} />
{t("vmLxc.backups.selectHint")}
</label>
<div className="space-y-2 flex-1 overflow-y-auto min-h-0 pr-1 -mr-1">
{backups.map(backup => <label key={backup.volid} className="flex items-center gap-2 p-3 rounded-lg bg-muted/30 hover:bg-muted/50 transition-colors">
<Checkbox aria-label={`${t("vmLxc.backups.selectBackup")} ${backup.date} ${backup.storage}`} checked={selected.includes(backup.volid)}
disabled={disabled || !!backup.protected || (chosen.length >= 20 && !selected.includes(backup.volid))}
onCheckedChange={checked => setSelected(previous => checked ? [...previous, backup.volid] : previous.filter(id => id !== backup.volid))} />
<div className="w-2 h-2 rounded-full bg-green-500 shrink-0" />
<Clock className="h-4 w-4 text-muted-foreground shrink-0" />
<span className="text-sm min-w-0 truncate" title={backup.volid}>{backup.date}</span>
{backup.protected && <Badge variant="secondary" className="gap-1 text-xs"><LockKeyhole className="h-3 w-3" />{t("vmLxc.backups.protected")}</Badge>}
<Badge variant="outline" className={`text-xs ml-auto shrink-0 ${storageClass(backup.storage)}`}>{backup.storage}</Badge>
<Badge variant="outline" className="font-mono shrink-0">{backup.size_human}</Badge>
</label>)}
</div>
</>}
<Dialog open={confirm} onOpenChange={value => { if (!busy) setConfirm(value) }}>
<DialogContent onEscapeKeyDown={event => { if (busy) event.preventDefault() }} onPointerDownOutside={event => { if (busy) event.preventDefault() }}>
<DialogHeader>
<DialogTitle>{t("vmLxc.backups.confirmTitle", { count: chosen.length })}</DialogTitle>
<DialogDescription>{t("vmLxc.backups.confirmDescription", { vmid })}</DialogDescription>
</DialogHeader>
<ul className="max-h-48 overflow-auto text-xs space-y-2 break-all">
{chosen.map(backup => <li key={backup.volid}>{backup.volid}</li>)}
</ul>
{busy && <p role="status" className="text-sm text-muted-foreground">{t("vmLxc.backups.deleting")}</p>}
<DialogFooter>
<Button variant="outline" disabled={busy} onClick={() => setConfirm(false)}>{t("vmLxc.backups.cancel")}</Button>
<Button variant="destructive" disabled={busy || !chosen.length} onClick={remove}>
{busy && <Loader2 className="h-4 w-4 animate-spin mr-2" />}{t("vmLxc.backups.confirmDelete")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
}
+13 -6
View File
@@ -42,6 +42,7 @@ import {
} from "lucide-react" } from "lucide-react"
import { ScriptTerminalModal } from "./script-terminal-modal" import { ScriptTerminalModal } from "./script-terminal-modal"
import { useT } from "@/lib/i18n/provider" import { useT } from "@/lib/i18n/provider"
import { useIsPbs } from "@/lib/use-platform"
interface CategoryCheck { interface CategoryCheck {
status: string status: string
@@ -119,6 +120,7 @@ const CATEGORIES = [
export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatusModalProps) { export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatusModalProps) {
const t = useT() const t = useT()
const isPbs = useIsPbs()
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [healthData, setHealthData] = useState<HealthDetails | null>(null) const [healthData, setHealthData] = useState<HealthDetails | null>(null)
const [dismissedItems, setDismissedItems] = useState<DismissedError[]>([]) const [dismissedItems, setDismissedItems] = useState<DismissedError[]>([])
@@ -460,6 +462,11 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
return originalStatus?.toUpperCase() || "UNKNOWN" return originalStatus?.toUpperCase() || "UNKNOWN"
} }
// Checks the host reports as not applicable (guests on PBS) are not listed or counted.
const visibleCategories = CATEGORIES.filter(({ key }) =>
!(healthData?.details?.[key as keyof typeof healthData.details] as { not_applicable?: boolean } | undefined)?.not_applicable)
const categoryLabel = (key: string) => t(`healthStatus.categories.${isPbs && key === "services" ? "pbsServices" : key}`)
const getHealthStats = () => { const getHealthStats = () => {
if (!healthData?.details) return { total: 0, healthy: 0, info: 0, warnings: 0, critical: 0, unknown: 0 } if (!healthData?.details) return { total: 0, healthy: 0, info: 0, warnings: 0, critical: 0, unknown: 0 }
@@ -469,7 +476,7 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
let critical = 0 let critical = 0
let unknown = 0 let unknown = 0
CATEGORIES.forEach(({ key }) => { visibleCategories.forEach(({ key }) => {
const categoryData = healthData.details[key as keyof typeof healthData.details] const categoryData = healthData.details[key as keyof typeof healthData.details]
if (categoryData) { if (categoryData) {
const effectiveStatus = getEffectiveStatus(key, categoryData.status) const effectiveStatus = getEffectiveStatus(key, categoryData.status)
@@ -481,7 +488,7 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
} }
}) })
return { total: CATEGORIES.length, healthy, info, warnings, critical, unknown } return { total: visibleCategories.length, healthy, info, warnings, critical, unknown }
} }
const stats = getHealthStats() const stats = getHealthStats()
@@ -759,7 +766,7 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
{/* Category List */} {/* Category List */}
<div className="space-y-2"> <div className="space-y-2">
{CATEGORIES.map(({ key, Icon }) => { {visibleCategories.map(({ key, Icon }) => {
const categoryData = healthData.details[key as keyof typeof healthData.details] const categoryData = healthData.details[key as keyof typeof healthData.details]
const originalStatus = categoryData?.status || "UNKNOWN" const originalStatus = categoryData?.status || "UNKNOWN"
const status = getEffectiveStatus(key, originalStatus) const status = getEffectiveStatus(key, originalStatus)
@@ -784,7 +791,7 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
</div> </div>
<div className="flex-1 min-w-0 overflow-hidden"> <div className="flex-1 min-w-0 overflow-hidden">
<div className="flex items-center gap-1.5 sm:gap-2"> <div className="flex items-center gap-1.5 sm:gap-2">
<p className="font-medium text-xs sm:text-sm truncate">{t(`healthStatus.categories.${key}`)}</p> <p className="font-medium text-xs sm:text-sm truncate">{categoryLabel(key)}</p>
{hasChecks && ( {hasChecks && (
<span className="text-[10px] text-muted-foreground shrink-0"> <span className="text-[10px] text-muted-foreground shrink-0">
({Object.values(checks).filter(c => c.installed !== false).length}) ({Object.values(checks).filter(c => c.installed !== false).length})
@@ -871,7 +878,7 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
{filteredDismissed.map((item) => { {filteredDismissed.map((item) => {
const catMeta = CATEGORIES.find(c => c.category === item.category || c.key === item.category) const catMeta = CATEGORIES.find(c => c.category === item.category || c.key === item.category)
const CatIcon = catMeta?.Icon || BellOff const CatIcon = catMeta?.Icon || BellOff
const catLabel = catMeta ? t(`healthStatus.categories.${catMeta.key}`) : item.category const catLabel = catMeta ? categoryLabel(catMeta.key) : item.category
const isPermanent = item.permanent || item.suppression_remaining_hours === -1 const isPermanent = item.permanent || item.suppression_remaining_hours === -1
return ( return (
@@ -936,7 +943,7 @@ export function HealthStatusModal({ open, onOpenChange, getApiUrl }: HealthStatu
<div key={cs.key} className="flex items-center justify-between gap-2"> <div key={cs.key} className="flex items-center justify-between gap-2">
<div className="flex items-center gap-2 min-w-0"> <div className="flex items-center gap-2 min-w-0">
<CatIcon className="h-3 w-3 sm:h-3.5 sm:w-3.5 text-blue-400/70 shrink-0" /> <CatIcon className="h-3 w-3 sm:h-3.5 sm:w-3.5 text-blue-400/70 shrink-0" />
<span className="text-[11px] sm:text-xs text-blue-400/80 truncate">{catMeta ? t(`healthStatus.categories.${catMeta.key}`) : cs.label}</span> <span className="text-[11px] sm:text-xs text-blue-400/80 truncate">{catMeta ? categoryLabel(catMeta.key) : cs.label}</span>
</div> </div>
<Badge variant="outline" className="text-[9px] sm:text-[10px] border-blue-500/30 text-blue-400/80 bg-transparent shrink-0"> <Badge variant="outline" className="text-[9px] sm:text-[10px] border-blue-500/30 text-blue-400/80 bg-transparent shrink-0">
{durationLabel} {durationLabel}
+107 -19
View File
@@ -19,7 +19,7 @@
* Secure Gateway. * Secure Gateway.
*/ */
import { useCallback, useEffect, useMemo, useState } from "react" import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { import {
Loader2, Save, RefreshCw, Trash2, Package, ExternalLink, Loader2, Save, RefreshCw, Trash2, Package, ExternalLink,
AlertTriangle, Info, PlusCircle, Pencil, ChevronDown, ChevronRight, EyeOff, AlertTriangle, Info, PlusCircle, Pencil, ChevronDown, ChevronRight, EyeOff,
@@ -93,6 +93,7 @@ interface AppConfig {
health_path?: string health_path?: string
logo_url?: string logo_url?: string
helper_slug?: string helper_slug?: string
discovery_id?: string
// Set when the application runs inside Docker: registered for its identity // Set when the application runs inside Docker: registered for its identity
// and installed version, updated by the image it comes from. // and installed version, updated by the image it comes from.
update_via?: string update_via?: string
@@ -121,7 +122,18 @@ interface PortDetail {
logo_url: string logo_url: string
} }
interface NativeDiscovery {
vmid: number
status: "not_scanned" | "complete" | "partial" | "error" | "busy"
stale: boolean
candidates: DetectedApp[]
}
interface DetectedApp { interface DetectedApp {
discovery_id?: string
source?: "native_metadata"
installed_version?: string | null
evidence?: Array<{ kind: string; value: string }>
slug: string slug: string
name: string name: string
logo_url?: string | null logo_url?: string | null
@@ -462,12 +474,37 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
const [loading, setLoading] = useState(!seed) const [loading, setLoading] = useState(!seed)
const [sidecar, setSidecar] = useState<SidecarResponse | null>(seed?.sidecar ?? null) const [sidecar, setSidecar] = useState<SidecarResponse | null>(seed?.sidecar ?? null)
const [suggestions, setSuggestions] = useState<Suggestions | null>(seed?.suggestions ?? null) const [suggestions, setSuggestions] = useState<Suggestions | null>(seed?.suggestions ?? null)
const [nativeDiscovery, setNativeDiscovery] = useState<NativeDiscovery | null>(null)
const nativeRequest = useRef(0)
const targetRef = useRef(vmid)
targetRef.current = vmid
const targetGeneration = useRef(0)
const previousTarget = useRef(vmid)
if (previousTarget.current !== vmid) {
previousTarget.current = vmid
targetGeneration.current += 1
}
useEffect(() => {
const generation = targetGeneration.current
const request = ++nativeRequest.current
setNativeDiscovery(null)
setEditing(null)
setDetectionNotice(null)
setSearchingApplications(false)
setSaving(false)
if (managed) return
fetchApi<NativeDiscovery>(`/api/vms/${vmid}/apps/native-discovery`).then(result => {
if (targetRef.current === vmid && targetGeneration.current === generation
&& nativeRequest.current === request) setNativeDiscovery(result)
}).catch(() => { /* cached metadata is optional; never initiate a scan */ })
return () => { targetGeneration.current += 1 }
}, [vmid, managed])
const [adguardSetupAvailable, setAdguardSetupAvailable] = useState(false) const [adguardSetupAvailable, setAdguardSetupAvailable] = useState(false)
const [error, setError] = useState<string | null>(null) const [error, setError] = useState<string | null>(null)
const [searchingApplications, setSearchingApplications] = useState(false) const [searchingApplications, setSearchingApplications] = useState(false)
const [detectionNotice, setDetectionNotice] = useState<{ found: boolean; text: string } | null>(null) const [detectionNotice, setDetectionNotice] = useState<{ found: boolean; text: string } | null>(null)
// Editor state // Editor state
const [editing, setEditing] = useState<{ appId: string | null; draft: AppConfig } | null>(null) const [editing, setEditing] = useState<{ appId: string | null; draft: AppConfig; targetVmid: number } | null>(null)
const [binaryArgsInput, setBinaryArgsInput] = useState("") const [binaryArgsInput, setBinaryArgsInput] = useState("")
const [commandArgvInput, setCommandArgvInput] = useState("") const [commandArgvInput, setCommandArgvInput] = useState("")
const [saving, setSaving] = useState(false) const [saving, setSaving] = useState(false)
@@ -522,6 +559,8 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
// `initialData` or the shared cache, the sidecar is already // `initialData` or the shared cache, the sidecar is already
// populated and the user shouldn't see a flash of "Loading…" // populated and the user shouldn't see a flash of "Loading…"
// while we revalidate. // while we revalidate.
const generation = targetGeneration.current
const isCurrent = () => targetRef.current === vmid && targetGeneration.current === generation
if (!sidecar) setLoading(true) if (!sidecar) setLoading(true)
setError(null) setError(null)
try { try {
@@ -533,14 +572,14 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
// the "Loading applications…" flash that used to show while a // the "Loading applications…" flash that used to show while a
// second, racing fetch caught up. // second, racing fetch caught up.
const bundle = await fetchLxcApps(vmid) const bundle = await fetchLxcApps(vmid)
if (bundle) { if (bundle && isCurrent()) {
setSidecar(bundle.sidecar) setSidecar(bundle.sidecar)
setSuggestions(bundle.suggestions) setSuggestions(bundle.suggestions)
} }
} catch (e: any) { } catch (e: any) {
setError(e?.message || t("vmLxc.appEditor.loadFailed")) if (isCurrent()) setError(e?.message || t("vmLxc.appEditor.loadFailed"))
} finally { } finally {
setLoading(false) if (isCurrent()) setLoading(false)
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [vmid, managed]) }, [vmid, managed])
@@ -617,7 +656,7 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
return msg return msg
} }
const editorOpen = !!editing const editorOpen = !!editing && editing.targetVmid === vmid
// The picker catalog is only needed after the user opens the editor. // The picker catalog is only needed after the user opens the editor.
useEffect(() => { useEffect(() => {
@@ -653,8 +692,8 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
// (`extras[]` from the backend). Both use the same DetectedApp // (`extras[]` from the backend). Both use the same DetectedApp
// shape so the empty state renders them uniformly. // shape so the empty state renders them uniformly.
const detectedList: DetectedApp[] = useMemo(() => { const detectedList: DetectedApp[] = useMemo(() => {
if (!suggestions) return [] const out: DetectedApp[] = [...(nativeDiscovery?.vmid === vmid ? nativeDiscovery.candidates : [])]
const out: DetectedApp[] = [] if (!suggestions) return out
// A ProxMenux OCI install has no helper slug — nothing was installed by a // A ProxMenux OCI install has no helper slug — nothing was installed by a
// community script — so its template id identifies it instead. // community script — so its template id identifies it instead.
const primarySlug = suggestions.helper_slug || suggestions.oci_instance?.template_id const primarySlug = suggestions.helper_slug || suggestions.oci_instance?.template_id
@@ -679,12 +718,12 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
} }
} }
return out return out
}, [suggestions]) }, [suggestions, nativeDiscovery, vmid])
// Registered slugs — used to filter the detection list down to // Registered slugs — used to filter the detection list down to
// what the user hasn't already registered on this CT. // what the user hasn't already registered on this CT.
const registeredSlugs = useMemo( const registeredSlugs = useMemo(
() => new Set(apps.map((a) => a.helper_slug).filter(Boolean) as string[]), () => new Set(apps.flatMap((a) => [a.helper_slug, a.discovery_id]).filter(Boolean) as string[]),
[apps], [apps],
) )
// Dismissed slugs — persisted in the sidecar. Chips the user // Dismissed slugs — persisted in the sidecar. Chips the user
@@ -750,14 +789,32 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
} }
const searchInstalledApplications = async () => { const searchInstalledApplications = async () => {
const generation = targetGeneration.current
const request = ++nativeRequest.current
const isCurrent = () => targetRef.current === vmid && targetGeneration.current === generation
&& nativeRequest.current === request
const before = new Set([...visibleDetected, ...visibleWorkloads].map((item) => item.slug)) const before = new Set([...visibleDetected, ...visibleWorkloads].map((item) => item.slug))
setSearchingApplications(true) setSearchingApplications(true)
setDetectionNotice(null) setDetectionNotice(null)
setError(null) setError(null)
try { try {
let native = await fetchApi<NativeDiscovery>(`/api/vms/${vmid}/apps/native-discovery`, {
method: "POST",
})
if (!isCurrent()) return
setNativeDiscovery(native)
if (native.status === "error" || native.status === "busy") {
setDetectionNotice({ found: false, text: t("vmLxc.appEditor.nativeScanFailed") })
return
}
const result: Suggestions = await fetchApi(`/api/vms/${vmid}/apps/suggestions`, { const result: Suggestions = await fetchApi(`/api/vms/${vmid}/apps/suggestions`, {
method: "POST", method: "POST",
}) })
if (!isCurrent()) return
// Recompose against the refreshed catalog without another native scan.
native = await fetchApi<NativeDiscovery>(`/api/vms/${vmid}/apps/native-discovery`)
if (!isCurrent()) return
setNativeDiscovery(native)
setSuggestions(result) setSuggestions(result)
if (sidecar) setLxcAppsCached(vmid, sidecar, result) if (sidecar) setLxcAppsCached(vmid, sidecar, result)
@@ -765,11 +822,14 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
if (result.helper_slug) detected.add(result.helper_slug) if (result.helper_slug) detected.add(result.helper_slug)
for (const item of result.extras || []) detected.add(item.slug) for (const item of result.extras || []) detected.add(item.slug)
for (const item of result.docker_workloads || []) detected.add(item.slug) for (const item of result.docker_workloads || []) detected.add(item.slug)
for (const item of native.candidates) detected.add(item.slug)
const visible = [...detected].filter( const visible = [...detected].filter(
(slug) => !registeredSlugs.has(slug) && !dismissedSlugs.has(slug), (slug) => !registeredSlugs.has(slug) && !dismissedSlugs.has(slug),
) )
const newCount = visible.filter((slug) => !before.has(slug)).length const newCount = visible.filter((slug) => !before.has(slug)).length
if (newCount === 1) { if (native.status === "partial") {
setDetectionNotice({ found: visible.length > 0, text: t("vmLxc.appEditor.nativeScanPartial") })
} else if (newCount === 1) {
setDetectionNotice({ found: true, text: t("vmLxc.appEditor.oneNewApplicationDetected") }) setDetectionNotice({ found: true, text: t("vmLxc.appEditor.oneNewApplicationDetected") })
} else if (newCount > 1) { } else if (newCount > 1) {
setDetectionNotice({ setDetectionNotice({
@@ -785,9 +845,9 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
}) })
} }
} catch (e: any) { } catch (e: any) {
setError(e?.message || t("vmLxc.appEditor.detectionFailed")) if (isCurrent()) setError(e?.message || t("vmLxc.appEditor.detectionFailed"))
} finally { } finally {
setSearchingApplications(false) if (isCurrent()) setSearchingApplications(false)
} }
} }
@@ -804,6 +864,7 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
existing?: AppEntry, existing?: AppEntry,
opts?: { withTracking?: boolean, preset?: DetectedApp }, opts?: { withTracking?: boolean, preset?: DetectedApp },
) => { ) => {
const generation = targetGeneration.current
let seed: AppConfig let seed: AppConfig
if (existing) { if (existing) {
seed = { seed = {
@@ -832,6 +893,7 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
health_path: existing.health_path || "", health_path: existing.health_path || "",
logo_url: existing.logo_url || "", logo_url: existing.logo_url || "",
helper_slug: existing.helper_slug || "", helper_slug: existing.helper_slug || "",
...(existing.discovery_id ? { discovery_id: existing.discovery_id } : {}),
// Preserved explicitly: saving replaces the whole record, so dropping // Preserved explicitly: saving replaces the whole record, so dropping
// it here would silently un-delegate the app the first time someone // it here would silently un-delegate the app the first time someone
// edits one of its ports. // edits one of its ports.
@@ -862,7 +924,11 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
const p = opts.preset const p = opts.preset
seed.name = p.name seed.name = p.name
seed.logo_url = p.logo_url || "" seed.logo_url = p.logo_url || ""
seed.helper_slug = p.slug if (p.discovery_id) {
seed.discovery_id = p.discovery_id
seed.update_method = "none"
seed.update_command = ""
} else seed.helper_slug = p.slug
// Docker endpoints come from the real published host-port mappings // Docker endpoints come from the real published host-port mappings
// listed under Web links. Do not pre-save a catalog default such as // listed under Web links. Do not pre-save a catalog default such as
// 9000; the user explicitly chooses which workload links to add. // 9000; the user explicitly chooses which workload links to add.
@@ -943,9 +1009,10 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
setShowAdvanced(false) setShowAdvanced(false)
} }
} }
if (targetRef.current !== vmid || targetGeneration.current !== generation) return
setBinaryArgsInput((seed.binary_args || []).join(", ")) setBinaryArgsInput((seed.binary_args || []).join(", "))
setCommandArgvInput((seed.command_argv || []).join(", ")) setCommandArgvInput((seed.command_argv || []).join(", "))
setEditing({ appId: existing?.id || null, draft: seed }) setEditing({ appId: existing?.id || null, draft: seed, targetVmid: vmid })
setDetectorTest(null) setDetectorTest(null)
setError(null) setError(null)
}, [suggestions, vmid, sidecar, isOciAdguard, adguardSetupAvailable]) }, [suggestions, vmid, sidecar, isOciAdguard, adguardSetupAvailable])
@@ -959,7 +1026,9 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
} }
const save = async () => { const save = async () => {
if (!editing) return if (!editing || editing.targetVmid !== vmid || targetRef.current !== vmid) return
const generation = targetGeneration.current
const isCurrent = () => targetRef.current === vmid && targetGeneration.current === generation
setSaving(true) setSaving(true)
setError(null) setError(null)
try { try {
@@ -973,6 +1042,7 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
body: JSON.stringify(editing.draft), body: JSON.stringify(editing.draft),
}) })
if ((r as any).error) throw new Error((r as any).error) if ((r as any).error) throw new Error((r as any).error)
if (!isCurrent()) return
setSidecar(r) setSidecar(r)
setLxcAppsCached(vmid, r, suggestions) setLxcAppsCached(vmid, r, suggestions)
setEditing(null) setEditing(null)
@@ -980,9 +1050,9 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
setCommandArgvInput("") setCommandArgvInput("")
onChange?.() onChange?.()
} catch (e: any) { } catch (e: any) {
setError(e?.message || t("vmLxc.appEditor.saveFailed")) if (isCurrent()) setError(e?.message || t("vmLxc.appEditor.saveFailed"))
} finally { } finally {
setSaving(false) if (isCurrent()) setSaving(false)
} }
} }
@@ -1283,7 +1353,7 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
} }
// ── Editor ───────────────────────────────────────────────────── // ── Editor ─────────────────────────────────────────────────────
if (editing) { if (editorOpen && editing) {
const draft = editing.draft const draft = editing.draft
const method = draft.installed_via || "" const method = draft.installed_via || ""
const isPackaged = method === "dpkg" || method === "apk" const isPackaged = method === "dpkg" || method === "apk"
@@ -2452,6 +2522,24 @@ export function LxcAppPanel({ vmid, ctIp, onChange, managed, initialData, oci }:
? t("vmLxc.appEditor.versionDetected", { version: d.tracking_suggestion.detected_version || "" }) ? t("vmLxc.appEditor.versionDetected", { version: d.tracking_suggestion.detected_version || "" })
: t("vmLxc.appEditor.detectedInContainer")} : t("vmLxc.appEditor.detectedInContainer")}
</div> </div>
{d.source === "native_metadata" && (
<div className="text-xs text-muted-foreground mt-1 break-all">
<div>{d.installed_version
? t("vmLxc.appEditor.versionDetected", { version: d.installed_version })
: t("vmLxc.appEditor.nativeVersionUnknown")}</div>
{(nativeDiscovery?.stale || nativeDiscovery?.status === "partial") && (
<div className="text-amber-400">{t("vmLxc.appEditor.nativeEvidenceStale")}</div>
)}
{d.evidence?.map((e, index) => <div key={index}>
{t(e.kind === "tcp_listener" ? "vmLxc.appEditor.nativeEvidencePort"
: e.kind === "installation_file" ? "vmLxc.appEditor.nativeEvidenceFile"
: e.kind === "process_executable" ? "vmLxc.appEditor.nativeEvidenceProcess"
: e.kind === "working_directory" ? "vmLxc.appEditor.nativeEvidenceDirectory"
: "vmLxc.appEditor.nativeEvidenceService")}: {e.value}
</div>)}
<div>{t("vmLxc.appEditor.nativeRegisterOnly")}</div>
</div>
)}
{d.tracking_suggestion?.detector_source === "legacy_fallback" && ( {d.tracking_suggestion?.detector_source === "legacy_fallback" && (
<div className="text-[10px] text-amber-400/90 mt-0.5"> <div className="text-[10px] text-amber-400/90 mt-0.5">
{t("vmLxc.appEditor.legacyDetectorUsed")} {t("vmLxc.appEditor.legacyDetectorUsed")}
+283
View File
@@ -0,0 +1,283 @@
"use client"
import { useEffect, useState } from "react"
import useSWR, { mutate as refreshCache } from "swr"
import { ArrowLeft, ChevronDown, DatabaseBackup, ExternalLink, Plus, Server, Trash2 } from "lucide-react"
import { Button } from "./ui/button"
import { Input } from "./ui/input"
import { Label } from "./ui/label"
import { Switch } from "./ui/switch"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "./ui/dialog"
import {
DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger,
} from "./ui/dropdown-menu"
import { fetchApi } from "../lib/api-config"
import { useT } from "../lib/i18n/provider"
interface MonitorLink {
key: string
name: string
product: "pve" | "pbs"
group: "cluster" | "standalone" | "pbs"
url: string | null
status: "online" | "offline" | "unknown"
native_url?: string | null
manual: boolean
hidden: boolean
custom_url: boolean
}
interface MonitorLinks { self: { name: string; product: string }; links: MonitorLink[] }
const GROUPS = ["cluster", "standalone", "pbs"] as const
const fetcher = (url: string) => fetchApi<MonitorLinks>(url)
const ORIGIN_KEY = "proxmenux-came-from"
interface Origin { url: string; name: string; product: "pve" | "pbs" }
// The Monitor a jump came from, as the browser reached it. Only a bare http(s)
// origin is accepted, and the parameters leave the address at once.
function takeOrigin(): (Origin & { fresh?: boolean }) | null {
if (typeof window === "undefined") return null
const params = new URLSearchParams(window.location.search)
const from = params.get("pmx_from")
if (from !== null) {
const name = params.get("pmx_name") || ""
const product = params.get("pmx_product") === "pbs" ? "pbs" : "pve"
for (const key of ["pmx_from", "pmx_name", "pmx_product"]) params.delete(key)
const query = params.toString()
window.history.replaceState(null, "", window.location.pathname + (query ? `?${query}` : "") + window.location.hash)
try {
const parsed = new URL(from)
if (/^https?:$/.test(parsed.protocol) && parsed.origin === from && parsed.origin !== window.location.origin) {
const origin: Origin = { url: parsed.origin, name: (name || parsed.hostname).slice(0, 64), product }
try { sessionStorage.setItem(ORIGIN_KEY, JSON.stringify(origin)) } catch { /* private mode */ }
return { ...origin, fresh: true }
}
} catch { /* not an address */ }
}
try {
const stored = JSON.parse(sessionStorage.getItem(ORIGIN_KEY) || "null")
return stored && typeof stored.url === "string" && /^https?:\/\//.test(stored.url) ? stored : null
} catch { return null }
}
// Where a link leads: its Monitor, or the native PBS interface when no Monitor answers there.
function destination(link: MonitorLink): { href: string; native: boolean } | null {
if (link.product === "pbs" && link.status !== "online" && link.native_url) return { href: link.native_url, native: true }
return link.url ? { href: link.url, native: false } : null
}
function ProductIcon({ product, className }: { product: string; className?: string }) {
return product === "pbs" ? <DatabaseBackup className={className} /> : <Server className={className} />
}
export function MonitorSwitcher({ nodeName, compact = false }: { nodeName: string; compact?: boolean }) {
const t = useT()
const [manageOpen, setManageOpen] = useState(false)
// The backend keeps the list ready, so the menu opens already filled.
const { data, error, isLoading } = useSWR<MonitorLinks>("/api/monitor-links", fetcher, { revalidateOnFocus: false, refreshInterval: 300000 })
const [origin, setOrigin] = useState<(Origin & { fresh?: boolean }) | null>(null)
const [offerOrigin, setOfferOrigin] = useState(false)
const [adding, setAdding] = useState(false)
const [addError, setAddError] = useState("")
useEffect(() => { setOrigin(takeOrigin()) }, [])
const originKnown = !!origin && !!data?.links.some(link => link.url === origin.url)
useEffect(() => {
// Offered once, right after arriving, when this Monitor has no way back yet.
if (origin?.fresh && data && !originKnown) setOfferOrigin(true)
}, [origin, data, originKnown])
// The address carries where the jump comes from, so the other Monitor can offer the way back.
const withOrigin = (href: string) => {
const params = new URLSearchParams({ pmx_from: window.location.origin, pmx_name: nodeName, pmx_product: data?.self.product === "pbs" ? "pbs" : "pve" })
return `${href}/?${params.toString()}`
}
const open = (href: string, newTab: boolean) => {
if (newTab) window.open(href, "_blank", "noopener,noreferrer")
else window.location.assign(href)
}
const go = (link: MonitorLink, newTab: boolean) => {
const target = destination(link)
if (!target) return
if (target.native) open(target.href, true)
else open(withOrigin(target.href), newTab)
}
const addOrigin = async () => {
if (!origin) return
setAdding(true); setAddError("")
try {
await fetchApi("/api/monitor-links", { method: "POST", body: JSON.stringify({ name: origin.name, url: origin.url, product: origin.product }) })
await refreshCache("/api/monitor-links")
setOfferOrigin(false)
} catch (err) {
setAddError(err instanceof Error ? err.message : t("monitorLinks.saveFailed"))
} finally { setAdding(false) }
}
return <div onClick={event => event.stopPropagation()}>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" size="sm" className={`gap-2 border-border bg-transparent hover:bg-secondary ${compact ? "h-8 px-2.5 text-xs" : ""}`}>
<Server className={compact ? "h-3.5 w-3.5" : "h-4 w-4"} />
<span className="max-w-[12rem] truncate">{t("status.node", { node: nodeName })}</span>
<ChevronDown className="h-3.5 w-3.5 opacity-70" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-72">
<DropdownMenuLabel className="text-xs font-medium text-muted-foreground">{t("monitorLinks.thisNode")}</DropdownMenuLabel>
<DropdownMenuItem disabled className="gap-2 opacity-100">
<span className="h-2 w-2 rounded-full bg-green-500" />
<ProductIcon product={data?.self.product || ""} className="h-4 w-4 text-muted-foreground" />
<span className="truncate font-medium">{nodeName}</span>
</DropdownMenuItem>
{origin && !originKnown && <DropdownMenuItem className="gap-2" onSelect={() => open(withOrigin(origin.url), false)}>
<ArrowLeft className="h-4 w-4 text-muted-foreground" />
<span className="min-w-0 flex-1 truncate">{t("monitorLinks.backTo", { name: origin.name })}</span>
</DropdownMenuItem>}
{isLoading && <p className="px-2 py-1.5 text-xs text-muted-foreground">{t("monitorLinks.checking")}</p>}
{error && <p className="px-2 py-1.5 text-xs text-amber-500">{t("monitorLinks.loadFailed")}</p>}
{data && data.links.length === 0 && <p className="px-2 py-1.5 text-xs text-muted-foreground">{t("monitorLinks.empty")}</p>}
{data && GROUPS.map(group => {
const rows = data.links.filter(link => link.group === group)
if (rows.length === 0) return null
return <div key={group}>
<DropdownMenuSeparator />
<DropdownMenuLabel className="text-xs font-medium text-muted-foreground">{t(`monitorLinks.groups.${group}`)}</DropdownMenuLabel>
{rows.map(link => {
const target = destination(link)
return <DropdownMenuItem key={link.key} disabled={!target} className="gap-2" onSelect={() => go(link, false)}>
<span className={`h-2 w-2 shrink-0 rounded-full ${link.status === "online" ? "bg-green-500" : "bg-gray-500"}`} />
<ProductIcon product={link.product} className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="min-w-0 flex-1 truncate">{link.name}</span>
{target?.native
? <span className="shrink-0 text-[11px] text-muted-foreground">{t("monitorLinks.pbsInterface")}</span>
: link.status !== "online" && <span className="shrink-0 text-[11px] text-muted-foreground">{t("monitorLinks.noResponse")}</span>}
{target && <button type="button" aria-label={t("monitorLinks.openNewTab")} title={t("monitorLinks.openNewTab")}
className="shrink-0 rounded p-0.5 text-muted-foreground hover:text-foreground"
onClick={event => { event.preventDefault(); event.stopPropagation(); go(link, true) }}>
<ExternalLink className="h-3.5 w-3.5" />
</button>}
</DropdownMenuItem>
})}
</div>
})}
<DropdownMenuSeparator />
<DropdownMenuItem className="gap-2" onSelect={() => setManageOpen(true)}>
<Plus className="h-4 w-4" />{t("monitorLinks.manage")}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
{manageOpen && <ManageLinksDialog open={manageOpen} onOpenChange={setManageOpen} />}
{origin && <Dialog open={offerOrigin} onOpenChange={setOfferOrigin}>
<DialogContent className="max-w-md" onClick={event => event.stopPropagation()}>
<DialogHeader>
<DialogTitle>{t("monitorLinks.cameFromTitle")}</DialogTitle>
<DialogDescription>{t("monitorLinks.cameFromBody", { name: origin.name })}</DialogDescription>
</DialogHeader>
<p className="break-all rounded-md border border-border bg-muted/40 px-3 py-2 font-mono text-sm">{origin.url}</p>
{addError && <p role="alert" className="text-sm text-red-500">{addError}</p>}
<div className="flex justify-end gap-2">
<Button variant="outline" disabled={adding} onClick={() => setOfferOrigin(false)}>{t("monitorLinks.notNow")}</Button>
<Button disabled={adding} onClick={addOrigin}><Plus className="mr-2 h-4 w-4" />{t("monitorLinks.add")}</Button>
</div>
</DialogContent>
</Dialog>}
</div>
}
function ManageLinksDialog({ open, onOpenChange }: { open: boolean; onOpenChange: (open: boolean) => void }) {
const t = useT()
const { data, mutate } = useSWR<MonitorLinks>("/api/monitor-links?all=1", fetcher, { revalidateOnFocus: false })
const [form, setForm] = useState({ name: "", url: "", product: "pve" })
const [addresses, setAddresses] = useState<Record<string, string>>({})
const [busy, setBusy] = useState(false)
const [error, setError] = useState("")
const run = async (action: () => Promise<unknown>) => {
setBusy(true); setError("")
try {
await action()
await mutate()
await refreshCache("/api/monitor-links")
} catch (err) {
setError(err instanceof Error ? err.message : t("monitorLinks.saveFailed"))
} finally { setBusy(false) }
}
const send = (path: string, method: string, body?: unknown) =>
fetchApi(path, { method, ...(body === undefined ? {} : { body: JSON.stringify(body) }) })
return <Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-w-2xl max-h-[85vh] overflow-y-auto" onClick={event => event.stopPropagation()}>
<DialogHeader>
<DialogTitle>{t("monitorLinks.dialogTitle")}</DialogTitle>
<DialogDescription>{t("monitorLinks.dialogDescription")}</DialogDescription>
</DialogHeader>
<div className="space-y-3 rounded-lg border border-border p-4">
<p className="text-sm font-medium">{t("monitorLinks.addTitle")}</p>
<div className="grid gap-3 sm:grid-cols-[1fr_1.5fr_auto]">
<div className="space-y-1.5">
<Label htmlFor="monitor-link-name">{t("monitorLinks.name")}</Label>
<Input id="monitor-link-name" value={form.name} disabled={busy} onChange={event => setForm({ ...form, name: event.target.value })} />
</div>
<div className="space-y-1.5">
<Label htmlFor="monitor-link-url">{t("monitorLinks.address")}</Label>
<Input id="monitor-link-url" value={form.url} disabled={busy} placeholder="https://192.168.1.20:8008"
onChange={event => setForm({ ...form, url: event.target.value })} />
</div>
<div className="space-y-1.5">
<Label htmlFor="monitor-link-product">{t("monitorLinks.type")}</Label>
<Select value={form.product} disabled={busy} onValueChange={product => setForm({ ...form, product })}>
<SelectTrigger id="monitor-link-product" className="w-full sm:w-56"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value="pve">Proxmox VE</SelectItem>
<SelectItem value="pbs">Proxmox Backup Server</SelectItem>
</SelectContent>
</Select>
</div>
</div>
<Button size="sm" disabled={busy || !form.url.trim()} onClick={() => run(async () => {
await send("/api/monitor-links", "POST", form)
setForm({ name: "", url: "", product: "pve" })
})}><Plus className="mr-2 h-4 w-4" />{t("monitorLinks.add")}</Button>
</div>
{error && <p role="alert" className="text-sm text-red-500">{error}</p>}
<div className="space-y-3">
{data?.links.length === 0 && <p className="text-sm text-muted-foreground">{t("monitorLinks.empty")}</p>}
{data?.links.map(link => {
const address = addresses[link.key] ?? (link.manual || link.custom_url ? link.url || "" : "")
return <div key={link.key} className={`space-y-2 rounded-lg border border-border p-3 ${link.hidden ? "opacity-60" : ""}`}>
<div className="flex items-center gap-2">
<ProductIcon product={link.product} className="h-4 w-4 text-muted-foreground" />
<span className="min-w-0 flex-1 truncate font-medium">{link.name}</span>
<span className="text-xs text-muted-foreground">{link.manual ? t("monitorLinks.added") : t("monitorLinks.detected")}</span>
{!link.manual && <label className="flex items-center gap-2 text-xs text-muted-foreground">
{t("monitorLinks.visible")}
<Switch checked={!link.hidden} disabled={busy} className="data-[state=checked]:bg-blue-600"
onCheckedChange={checked => run(() => send(`/api/monitor-links/${encodeURIComponent(link.key)}`, "PATCH", { hidden: !checked }))} />
</label>}
{link.manual && <Button variant="ghost" size="sm" disabled={busy} aria-label={t("monitorLinks.remove")}
onClick={() => run(() => send(`/api/monitor-links/${encodeURIComponent(link.key)}`, "DELETE"))}>
<Trash2 className="h-4 w-4 text-red-500" />
</Button>}
</div>
<div className="flex gap-2">
<Input value={address} disabled={busy} aria-label={t("monitorLinks.address")}
placeholder={link.manual ? "" : `${t("monitorLinks.autoAddress")}${link.url ? `: ${link.url}` : ""}`}
onChange={event => setAddresses({ ...addresses, [link.key]: event.target.value })} />
<Button variant="outline" size="sm" className="h-10" disabled={busy || addresses[link.key] === undefined}
onClick={() => run(async () => {
await send(`/api/monitor-links/${encodeURIComponent(link.key)}`, "PATCH", { url: addresses[link.key] })
setAddresses(({ [link.key]: _, ...rest }) => rest)
})}>{t("monitorLinks.save")}</Button>
</div>
</div>
})}
</div>
</DialogContent>
</Dialog>
}
+12 -4
View File
@@ -1,5 +1,6 @@
"use client" "use client"
import { AppriseServiceHelp } from "./apprise-service-help"
import { useState, useEffect, useCallback } from "react" import { useState, useEffect, useCallback } from "react"
import { useTheme } from "next-themes" import { useTheme } from "next-themes"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"
@@ -12,6 +13,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from ".
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "./ui/dialog" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "./ui/dialog"
import { fetchApi } from "../lib/api-config" import { fetchApi } from "../lib/api-config"
import { useT } from "../lib/i18n/provider" import { useT } from "../lib/i18n/provider"
import { useIsPbs } from "../lib/use-platform"
import { import {
Bell, BellOff, Send, CheckCircle2, XCircle, Loader2, Bell, BellOff, Send, CheckCircle2, XCircle, Loader2,
AlertTriangle, Info, Settings2, Zap, Eye, EyeOff, AlertTriangle, Info, Settings2, Zap, Eye, EyeOff,
@@ -153,6 +155,8 @@ function validateGotifyUrl(url: string): { error?: string; warning?: string } {
} }
const EVENT_CATEGORIES = ["vm_ct", "backup", "resources", "storage", "network", "security", "cluster", "services", "health", "updates", "hardware", "system", "other"].map(key => ({ key })) const EVENT_CATEGORIES = ["vm_ct", "backup", "resources", "storage", "network", "security", "cluster", "services", "health", "updates", "hardware", "system", "other"].map(key => ({ key }))
// Guests, vzdump backups and the cluster do not exist on Proxmox Backup Server.
const PVE_ONLY_CATEGORIES = new Set(["vm_ct", "backup", "cluster"])
const CHANNEL_TYPES = ["telegram", "gotify", "discord", "email", "pushover", "apprise"] as const const CHANNEL_TYPES = ["telegram", "gotify", "discord", "email", "pushover", "apprise"] as const
@@ -329,6 +333,7 @@ const DEFAULT_CONFIG: NotificationConfig = {
export function NotificationSettings() { export function NotificationSettings() {
const t = useT() const t = useT()
const isPbs = useIsPbs()
const { resolvedTheme } = useTheme() const { resolvedTheme } = useTheme()
const [config, setConfig] = useState<NotificationConfig>(DEFAULT_CONFIG) const [config, setConfig] = useState<NotificationConfig>(DEFAULT_CONFIG)
const [status, setStatus] = useState<ServiceStatus | null>(null) const [status, setStatus] = useState<ServiceStatus | null>(null)
@@ -702,7 +707,7 @@ export function NotificationSettings() {
<Label className="text-[11px] text-muted-foreground">{t("settings.notifications.ui.notificationCategories")}</Label> <Label className="text-[11px] text-muted-foreground">{t("settings.notifications.ui.notificationCategories")}</Label>
</div> </div>
<div className="space-y-2"> <div className="space-y-2">
{EVENT_CATEGORIES.filter(cat => cat.key !== "other").map(cat => { {EVENT_CATEGORIES.filter(cat => cat.key !== "other" && !(isPbs && PVE_ONLY_CATEGORIES.has(cat.key))).map(cat => {
const isEnabled = overrides.categories[cat.key] ?? true const isEnabled = overrides.categories[cat.key] ?? true
const isExpanded = expandedCategories.has(`${chName}.${cat.key}`) const isExpanded = expandedCategories.has(`${chName}.${cat.key}`)
// Hide the LXC update toggle when the user has disabled the // Hide the LXC update toggle when the user has disabled the
@@ -921,7 +926,7 @@ export function NotificationSettings() {
const wasEnabled = originalConfig.enabled const wasEnabled = originalConfig.enabled
const isNowDisabled = !nextConfig.enabled const isNowDisabled = !nextConfig.enabled
if (wasEnabled && isNowDisabled) { if (wasEnabled && isNowDisabled && !isPbs) {
try { try {
await fetchApi("/api/notifications/proxmox/cleanup-webhook", { method: "POST" }) await fetchApi("/api/notifications/proxmox/cleanup-webhook", { method: "POST" })
} catch { } catch {
@@ -1216,8 +1221,10 @@ export function NotificationSettings() {
setConfig(newConfig) setConfig(newConfig)
setOriginalConfig(newConfig) setOriginalConfig(newConfig)
// 2) Auto-configure PVE webhook // 2) Auto-configure PVE webhook; PBS has no /etc/pve notifications
try { if (isPbs) {
setWebhookSetup({ status: "idle", fallback_commands: [], error: "" })
} else try {
const setup = await fetchApi<{ const setup = await fetchApi<{
configured: boolean configured: boolean
secret?: string secret?: string
@@ -2061,6 +2068,7 @@ export function NotificationSettings() {
Enable toggle + Test button pattern as the other Enable toggle + Test button pattern as the other
channels. */} channels. */}
<TabsContent value="apprise" className="space-y-3 pt-2"> <TabsContent value="apprise" className="space-y-3 pt-2">
<AppriseServiceHelp />
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Label className="text-xs font-medium">{t("settings.notifications.ui.enableApprise")}</Label> <Label className="text-xs font-medium">{t("settings.notifications.ui.enableApprise")}</Label>
+112
View File
@@ -0,0 +1,112 @@
"use client"
import { CalendarClock, Database } from "lucide-react"
import { Badge } from "./ui/badge"
import { Progress } from "./ui/progress"
import { formatStorage } from "../lib/utils"
import { useT } from "../lib/i18n/provider"
export interface PBSDatastore {
name: string
path?: string
total?: number | null
used?: number | null
avail?: number | null
error?: string
"backend-type"?: string
"estimated-full-date"?: number
"gc-status"?: { upid?: string | null; "removed-bytes"?: number; "pending-bytes"?: number }
}
export function datastorePercent(store: PBSDatastore): number | null {
if (store.total == null || store.used == null || store.total <= 0) return null
return (store.used / store.total) * 100
}
// Same thresholds and colours as the PVE storage cards.
export function usageTone(percent: number): { bar: string; text: string; used: string } {
if (percent > 90) return { bar: "[&>div]:bg-red-500", text: "text-red-500", used: "text-red-400" }
if (percent > 75) return { bar: "[&>div]:bg-yellow-500", text: "text-yellow-500", used: "text-yellow-400" }
return { bar: "[&>div]:bg-blue-500", text: "text-muted-foreground", used: "text-blue-400" }
}
const gib = (bytes: number) => formatStorage(bytes / 1024 ** 3)
// A datastore without a readable capacity keeps its message: it is never shown as empty.
export function DatastoreUsageRow({ store, compact = false }: { store: PBSDatastore; compact?: boolean }) {
const t = useT()
const percent = datastorePercent(store)
if (percent == null) {
return <div className="space-y-1">
<div className="flex items-center justify-between gap-2 text-sm">
<span className="font-medium text-foreground truncate">{store.name}</span>
<span className="text-xs text-muted-foreground">—</span>
</div>
<p className="text-xs text-amber-500">{store.error || t("pbs.capacityUnavailable")}</p>
</div>
}
const tone = usageTone(percent)
return <div className="space-y-1">
<div className="flex items-center justify-between gap-2 text-sm">
<span className="font-medium text-foreground truncate">{store.name}</span>
<span className={`text-xs font-semibold tabular-nums ${tone.text}`}>{percent.toFixed(1)}%</span>
</div>
<Progress value={percent} className={`${compact ? "h-1.5" : "h-2"} ${tone.bar}`} />
{!compact && <div className="flex justify-between text-xs text-muted-foreground tabular-nums">
<span>{gib(store.used!)} / {gib(store.total!)}</span>
<span>{t("pbs.free")}: <span className="font-semibold text-green-500">{gib(store.avail ?? store.total! - store.used!)}</span></span>
</div>}
</div>
}
// Same layout as the "Proxmox storage" cards of the PVE Monitor.
export function PBSDatastoreCard({ store }: { store: PBSDatastore }) {
const t = useT()
const percent = datastorePercent(store)
const tone = percent == null ? null : usageTone(percent)
const fullDate = store["estimated-full-date"]
const type = store["backend-type"] || "datastore"
return <div className={`border rounded-lg p-4 ${store.error ? "border-red-500/50 bg-red-500/5" : ""}`}>
<div className="flex items-center justify-between gap-2 mb-3">
<div className="flex items-center gap-2 md:gap-3 min-w-0">
<Database className="h-5 w-5 text-muted-foreground flex-shrink-0" />
<h3 className="font-semibold text-base md:text-lg truncate">{store.name}</h3>
<Badge className="bg-blue-500/10 text-blue-400 border-blue-500/20">{type}</Badge>
</div>
<div className="flex items-center gap-2 flex-shrink-0">
<Badge className={store.error ? "bg-red-500/10 text-red-500 border-red-500/20" : "bg-green-500/10 text-green-500 border-green-500/20"}>
{store.error ? t("storage.status.error") : t("storage.status.active")}
</Badge>
{percent != null && <span className="text-sm font-medium">{percent.toFixed(2)}%</span>}
</div>
</div>
{percent != null && tone ? (
<div className="space-y-2">
<Progress value={percent} className={`h-2 ${tone.bar}`} />
<div className="grid grid-cols-3 gap-4 text-sm">
<div>
<p className="text-muted-foreground">{t("storage.total")}</p>
<p className="font-medium">{gib(store.total!)}</p>
</div>
<div>
<p className="text-muted-foreground">{t("storage.used")}</p>
<p className={`font-medium ${tone.used}`}>{gib(store.used!)}</p>
</div>
<div>
<p className="text-muted-foreground">{t("storage.available")}</p>
<p className="font-medium text-green-400">{gib(store.avail ?? store.total! - store.used!)}</p>
</div>
</div>
</div>
) : (
<p className="text-sm text-muted-foreground">{store.error || t("storage.capacityNotReported")}</p>
)}
{(store.path || fullDate !== undefined) && <div className="mt-3 flex flex-wrap items-center gap-x-4 gap-y-1 text-xs text-muted-foreground">
{store.path && <span className="font-mono truncate" title={store.path}>{store.path}</span>}
{fullDate !== undefined && <span className="flex items-center">
<CalendarClock className="mr-1.5 h-3.5 w-3.5" />{t("pbs.estimatedFull")}:
<span className="ml-1 font-medium text-foreground">{fullDate > 0 ? new Date(fullDate * 1000).toLocaleDateString() : t("pbs.estimatedFullUnknown")}</span>
</span>}
</div>}
</div>
}
+247
View File
@@ -0,0 +1,247 @@
"use client"
import { useEffect, useState } from "react"
import useSWR from "swr"
import {
Activity, ArchiveRestore, Database, DatabaseBackup, Download, ListChecks,
RefreshCw, Scissors, Server, Settings2, ShieldCheck, Terminal, Trash2,
} from "lucide-react"
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"
import { Badge } from "./ui/badge"
import { Button } from "./ui/button"
import { Input } from "./ui/input"
import { Label } from "./ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
import { PBSDatastoreCard, type PBSDatastore } from "./pbs-datastore-usage"
import { fetchApi } from "../lib/api-config"
import { useT } from "../lib/i18n/provider"
interface Connection { id: string; name: string; url: string; token_id: string; fingerprint: string; has_token: boolean }
interface Connections { local_available: boolean; servers: Connection[] }
interface Task { upid: string; worker_type?: string; worker_id?: string; starttime?: number; endtime?: number; status?: string }
interface Job { id: string; store?: string; schedule?: string; "last-run-state"?: string }
interface PBSStatus {
name: string; version: string | null; fetched_at: number; services: Record<string, string>;
datastores: PBSDatastore[] | null; tasks: Task[] | null;
sync: Job[] | null; verify: Job[] | null; prune: Job[] | null; errors: Record<string, string>;
}
const emptyForm = { id: "", name: "", url: "", token_id: "", fingerprint: "", token_secret: "" }
const NEW_CONNECTION = "__new__"
const badgeTone = {
ok: "bg-green-500/10 text-green-500 border-green-500/20",
warn: "bg-amber-500/10 text-amber-500 border-amber-500/20",
error: "bg-red-500/10 text-red-500 border-red-500/20",
running: "bg-blue-500/10 text-blue-500 border-blue-500/20",
muted: "bg-muted text-muted-foreground border-border",
}
const TASK_ICONS: Record<string, typeof Activity> = {
backup: DatabaseBackup, reader: ArchiveRestore, verify: ShieldCheck, verificationjob: ShieldCheck,
verify_group: ShieldCheck, verify_snapshot: ShieldCheck, prune: Scissors, prunejob: Scissors,
garbage_collection: Trash2, sync: RefreshCw, syncjob: RefreshCw, aptupdate: Download,
termproxy: Terminal, dircreate: Database, "create-datastore": Database,
}
// PBS reports "OK", "WARNINGS: n" or the error text; a task without one is still running.
function taskState(task: Task): keyof typeof badgeTone {
if (!task.status) return task.endtime ? "muted" : "running"
if (task.status === "OK") return "ok"
if (task.status.startsWith("WARNINGS")) return "warn"
return "error"
}
function jobState(state?: string): keyof typeof badgeTone {
if (!state) return "muted"
if (state === "ok") return "ok"
if (state.toLowerCase().startsWith("warn")) return "warn"
return "error"
}
function formatDuration(seconds: number) {
if (!Number.isFinite(seconds) || seconds < 0) return "—"
if (seconds < 60) return `${Math.round(seconds)}s`
const minutes = Math.floor(seconds / 60)
if (minutes < 60) return `${minutes}m ${Math.round(seconds % 60)}s`
const hours = Math.floor(minutes / 60)
return hours < 24 ? `${hours}h ${minutes % 60}m` : `${Math.floor(hours / 24)}d ${hours % 24}h`
}
// `localOnly` is the PBS host's own Monitor: no connection picker, the
// header Refresh already reloads the page data.
export function PBSPanel({ localOnly = false }: { localOnly?: boolean } = {}) {
const t = useT()
const { data: connections, error: connectionError, mutate: reloadConnections } = useSWR<Connections>(
"/api/pbs/servers", url => fetchApi<Connections>(url), { revalidateOnFocus: false })
const [selected, setSelected] = useState("")
const [manage, setManage] = useState(false)
const [form, setForm] = useState(emptyForm)
const [busy, setBusy] = useState(false)
const [message, setMessage] = useState("")
const [error, setError] = useState("")
const { data, error: statusError, isLoading, mutate } = useSWR<PBSStatus>(
selected ? `/api/pbs/status?server=${encodeURIComponent(selected)}` : null,
url => fetchApi<PBSStatus>(url), { refreshInterval: 30000, revalidateOnFocus: false })
useEffect(() => {
if (!connections) return
if (selected === "local" && connections.local_available) return
if (connections.servers.some(row => row.id === selected)) return
setSelected(connections.local_available ? "local" : connections.servers[0]?.id || "")
}, [connections, selected])
const save = async () => {
setBusy(true); setError(""); setMessage("")
try {
const result = await fetchApi<{ id: string }>("/api/pbs/servers", { method: "POST", body: JSON.stringify({
...form, id: form.id || undefined, token_secret: form.token_secret || undefined,
}) })
await reloadConnections()
setSelected(result.id)
setForm(emptyForm)
setMessage(t("pbs.saved"))
} catch (err) { setError(err instanceof Error ? err.message : t("pbs.saveFailed")) }
finally { setBusy(false) }
}
const remove = async () => {
if (!form.id || !window.confirm(t("pbs.removeConfirm"))) return
setBusy(true); setError(""); setMessage("")
try {
await fetchApi(`/api/pbs/servers/${encodeURIComponent(form.id)}`, { method: "DELETE" })
setForm(emptyForm)
await reloadConnections()
} catch (err) { setError(err instanceof Error ? err.message : t("pbs.saveFailed")) }
finally { setBusy(false) }
}
const now = Date.now() / 1000
return <div className="space-y-4 md:space-y-6">
{!localOnly && <div className="flex flex-wrap items-center gap-3">
<h2 className="flex items-center text-2xl font-semibold text-foreground"><Server className="mr-2 h-5 w-5" />{t("pbs.title")}</h2>
<Select value={selected || undefined} onValueChange={setSelected}>
<SelectTrigger aria-label={t("pbs.server")} className="w-full sm:ml-auto sm:w-56"><SelectValue placeholder={t("pbs.chooseServer")} /></SelectTrigger>
<SelectContent>
{connections?.local_available && <SelectItem value="local">{t("pbs.local")}</SelectItem>}
{connections?.servers.map(row => <SelectItem key={row.id} value={row.id}>{row.name}</SelectItem>)}
</SelectContent>
</Select>
<Button variant="outline" size="sm" disabled={isLoading || !selected} onClick={() => mutate()}>
<RefreshCw className={`mr-2 h-4 w-4 ${isLoading ? "animate-spin" : ""}`} />{t("pbs.refresh")}
</Button>
<Button variant="outline" size="sm" onClick={() => setManage(!manage)}>
<Settings2 className="mr-2 h-4 w-4" />{t("pbs.manage")}
</Button>
</div>}
{(connectionError || statusError) && <p role="alert" className="text-sm text-red-500">
{String((connectionError || statusError)?.message || t("pbs.loadFailed"))}</p>}
{isLoading && <p role="status" className="text-sm text-muted-foreground">{t("pbs.loading")}</p>}
{!selected && !connectionError && connections && <p className="text-sm text-muted-foreground">{t("pbs.noServer")}</p>}
{manage && !localOnly && <Card>
<CardHeader><CardTitle>{t("pbs.manage")}</CardTitle></CardHeader>
<CardContent className="space-y-4">
<p className="text-sm text-muted-foreground">{t("pbs.permissionsHelp")}</p>
<Select value={form.id || NEW_CONNECTION} onValueChange={value => {
const row = connections?.servers.find(item => item.id === value)
setForm(row ? { ...row, token_secret: "" } : emptyForm)
}}>
<SelectTrigger aria-label={t("pbs.editConnection")} className="w-full"><SelectValue /></SelectTrigger>
<SelectContent>
<SelectItem value={NEW_CONNECTION}>{t("pbs.add")}</SelectItem>
{connections?.servers.map(row => <SelectItem key={row.id} value={row.id}>{row.name}</SelectItem>)}
</SelectContent>
</Select>
<div className="grid gap-4 md:grid-cols-2">
{(["name", "url", "token_id", "token_secret", "fingerprint"] as const).map(field =>
<div key={field} className="space-y-2">
<Label htmlFor={`pbs-${field}`}>{t(`pbs.${field}`)}</Label>
<Input id={`pbs-${field}`} disabled={busy} type={field === "token_secret" ? "password" : field === "url" ? "url" : "text"}
autoComplete={field === "token_secret" ? "new-password" : "off"}
value={form[field]} onChange={event => setForm({ ...form, [field]: event.target.value })} />
</div>)}
</div>
<p className="text-xs text-muted-foreground">{t("pbs.tlsHelp")}</p>
{error && <p role="alert" className="text-sm text-red-500">{error}</p>}
{message && <p role="status" className="text-sm text-green-500">{message}</p>}
<div className="flex gap-2">
<Button disabled={busy} onClick={save}>{busy ? t("pbs.testing") : t("pbs.save")}</Button>
{form.id && <Button variant="destructive" disabled={busy} onClick={remove}>{t("pbs.remove")}</Button>}
</div>
</CardContent>
</Card>}
{data && <>
<div className="flex flex-wrap items-center gap-2 text-sm text-muted-foreground">
<span>{data.name} · PBS {data.version || "—"} · {t("pbs.sampled")} {new Date(data.fetched_at * 1000).toLocaleTimeString()}</span>
{Object.entries(data.services).map(([service, state]) =>
<Badge key={service} variant="outline" className={state === "active" ? badgeTone.ok : badgeTone.error}>{service}: {state}</Badge>)}
</div>
{Object.entries(data.errors).map(([section, detail]) => <p key={section} role="alert" className="text-sm text-amber-500">{t(`pbs.${section}`)}: {detail}</p>)}
<Card className="bg-card border-border">
<CardHeader><CardTitle className="flex items-center text-foreground"><Database className="mr-2 h-5 w-5" />{t("pbs.datastores")}</CardTitle></CardHeader>
<CardContent>
{data.datastores?.length === 0 && <p className="text-sm text-muted-foreground">{t("pbs.noDatastores")}</p>}
{data.datastores && data.datastores.length > 0 && <div className="space-y-4">
{data.datastores.map(store => <PBSDatastoreCard key={store.name} store={store} />)}
</div>}
</CardContent>
</Card>
<Card className="bg-card border-border">
<CardHeader><CardTitle className="flex items-center text-foreground"><Activity className="mr-2 h-5 w-5" />{t("pbs.tasks")}</CardTitle></CardHeader>
<CardContent>
{data.tasks?.length === 0 && <p className="text-sm text-muted-foreground">{t("pbs.noTasks")}</p>}
{data.tasks && data.tasks.length > 0 && <div className="overflow-x-auto"><table className="w-full text-sm">
<thead><tr className="border-b border-border text-left text-xs text-muted-foreground">
<th className="py-2 pr-3 font-medium">{t("pbs.task")}</th>
<th className="py-2 pr-3 font-medium">{t("pbs.started")}</th>
<th className="py-2 pr-3 font-medium">{t("pbs.duration")}</th>
<th className="py-2 font-medium">{t("pbs.status")}</th>
</tr></thead>
<tbody>{data.tasks.map(row => {
const Icon = TASK_ICONS[row.worker_type || ""] || Activity
const state = taskState(row)
const end = row.endtime ?? (state === "running" ? now : undefined)
return <tr key={row.upid} className="border-b border-border/60 last:border-0">
<td className="py-2 pr-3"><span className="flex items-center gap-2">
<Icon className="h-4 w-4 shrink-0 text-muted-foreground" />
<span className="text-foreground">{row.worker_type}</span>
{row.worker_id && <span className="truncate text-muted-foreground">{row.worker_id}</span>}
</span></td>
<td className="whitespace-nowrap py-2 pr-3 tabular-nums text-muted-foreground">{row.starttime ? new Date(row.starttime * 1000).toLocaleString() : "—"}</td>
<td className="whitespace-nowrap py-2 pr-3 tabular-nums text-muted-foreground">{row.starttime && end ? formatDuration(end - row.starttime) : "—"}</td>
<td className="py-2"><Badge variant="outline" className={`${badgeTone[state]} max-w-[16rem] truncate`} title={row.status}>
{state === "running" ? t("pbs.running") : row.status || "—"}
</Badge></td>
</tr>
})}</tbody>
</table></div>}
</CardContent>
</Card>
<Card className="bg-card border-border">
<CardHeader><CardTitle className="flex items-center text-foreground"><ListChecks className="mr-2 h-5 w-5" />{t("pbs.jobs")}</CardTitle></CardHeader>
<CardContent>
{(() => {
const jobs = (["sync", "verify", "prune"] as const).flatMap(kind => (data[kind] || []).map(row => ({ kind, row })))
if (jobs.length === 0) return <p className="text-sm text-muted-foreground">{t("pbs.noJobs")}</p>
return <div className="overflow-x-auto"><table className="w-full text-sm">
<thead><tr className="border-b border-border text-left text-xs text-muted-foreground">
<th className="py-2 pr-3 font-medium">{t("pbs.job")}</th>
<th className="py-2 pr-3 font-medium">{t("pbs.datastores")}</th>
<th className="py-2 pr-3 font-medium">{t("pbs.schedule")}</th>
<th className="py-2 font-medium">{t("pbs.status")}</th>
</tr></thead>
<tbody>{jobs.map(({ kind, row }) => <tr key={`${kind}-${row.id}`} className="border-b border-border/60 last:border-0">
<td className="py-2 pr-3"><span className="text-foreground">{t(`pbs.${kind}`)}</span> <span className="text-muted-foreground">· {row.id}</span></td>
<td className="py-2 pr-3 text-muted-foreground">{row.store || "—"}</td>
<td className="py-2 pr-3 font-mono text-xs text-muted-foreground">{row.schedule || "—"}</td>
<td className="py-2"><Badge variant="outline" className={badgeTone[jobState(row["last-run-state"])]}>{row["last-run-state"] || "—"}</Badge></td>
</tr>)}</tbody>
</table></div>
})()}
</CardContent>
</Card>
</>}
</div>
}
+61 -25
View File
@@ -18,6 +18,8 @@ import { Security } from "./security"
import { Profile } from "./profile" import { Profile } from "./profile"
import { About } from "./about" import { About } from "./about"
import { BackupTab } from "./backup-tab" import { BackupTab } from "./backup-tab"
import { PBSPanel } from "./pbs-panel"
import { MonitorSwitcher } from "./monitor-switcher"
import { OnboardingCarousel } from "./onboarding-carousel" import { OnboardingCarousel } from "./onboarding-carousel"
import { HealthStatusModal } from "./health-status-modal" import { HealthStatusModal } from "./health-status-modal"
import { ReleaseNotesModal, useVersionCheck } from "./release-notes-modal" import { ReleaseNotesModal, useVersionCheck } from "./release-notes-modal"
@@ -45,7 +47,9 @@ import {
ClipboardCheck, ClipboardCheck,
Info, Info,
DatabaseBackup, DatabaseBackup,
Database,
ChevronDown, ChevronDown,
Clock,
} from "lucide-react" } from "lucide-react"
import Image from "next/image" import Image from "next/image"
import { ThemeToggle } from "./theme-toggle" import { ThemeToggle } from "./theme-toggle"
@@ -97,9 +101,34 @@ const _dashboardPrefetchFetcher = (url: string) => fetchApi(url)
export function ProxmoxDashboard() { export function ProxmoxDashboard() {
const t = useT() const t = useT()
useSWR("/api/apps/custom-links", _dashboardPrefetchFetcher, { revalidateOnFocus: false }) const { data, error } = useSWR<{ product: string }>("/api/platform", url => fetchApi<{ product: string }>(url), { revalidateOnFocus: false })
useSWR("/api/apps/categories", _dashboardPrefetchFetcher, { revalidateOnFocus: false }) if (!data && !error) return <p role="status" className="p-8">{t("app.loading")}</p>
const { order: tabOrder } = useTabOrder() if (data?.product === "pbs") return <PBSDashboard />
return <PVEDashboard />
}
function PVEDashboard() {
return <MonitorDashboard product="pve" />
}
function PBSDashboard() {
return <MonitorDashboard product="pbs" />
}
type Product = "pve" | "pbs"
type TopTab = TabId | "datastores"
// Proxmox Backup Server has no guests, apps or PVE storage: it keeps the
// same header, health and administration with its own top-level sections.
const PBS_TABS: TopTab[] = ["overview", "datastores", "terminal", "admin"]
function MonitorDashboard({ product }: { product: Product }) {
const t = useT()
const isPbs = product === "pbs"
useSWR(isPbs ? null : "/api/apps/custom-links", _dashboardPrefetchFetcher, { revalidateOnFocus: false })
useSWR(isPbs ? null : "/api/apps/categories", _dashboardPrefetchFetcher, { revalidateOnFocus: false })
const { order: userTabOrder } = useTabOrder()
const tabOrder: TopTab[] = isPbs ? PBS_TABS : userTabOrder
const [systemStatus, setSystemStatus] = useState<SystemStatus>({ const [systemStatus, setSystemStatus] = useState<SystemStatus>({
status: "healthy", status: "healthy",
uptime: "Loading...", uptime: "Loading...",
@@ -117,8 +146,8 @@ export function ProxmoxDashboard() {
// runs post-hydration; the tiny flash is acceptable and matches the // runs post-hydration; the tiny flash is acceptable and matches the
// pattern next-themes uses for the same reason. // pattern next-themes uses for the same reason.
useEffect(() => { useEffect(() => {
setActiveTab(firstActualTab()) setActiveTab(isPbs ? "overview" : firstActualTab())
}, []) }, [isPbs])
const [infoCount, setInfoCount] = useState(0) const [infoCount, setInfoCount] = useState(0)
const [updateAvailable, setUpdateAvailable] = useState(false) const [updateAvailable, setUpdateAvailable] = useState(false)
const [showNavigation, setShowNavigation] = useState(true) const [showNavigation, setShowNavigation] = useState(true)
@@ -391,6 +420,7 @@ export function ProxmoxDashboard() {
const getActiveTabLabel = () => { const getActiveTabLabel = () => {
switch (activeTab) { switch (activeTab) {
case "overview": return t("navigation.overview") case "overview": return t("navigation.overview")
case "datastores": return t("pbs.datastores")
case "apps": return t("navigation.apps") case "apps": return t("navigation.apps")
case "vms": return t("navigation.virtualMachines") case "vms": return t("navigation.virtualMachines")
case "storage": return t("navigation.storage") case "storage": return t("navigation.storage")
@@ -410,8 +440,9 @@ export function ProxmoxDashboard() {
return ( return (
<div className="min-h-screen bg-background"> <div className="min-h-screen bg-background">
<OnboardingCarousel /> {/* Both describe the PVE Monitor. */}
<ReleaseNotesModal open={showReleaseNotes} onClose={() => setShowReleaseNotes(false)} /> {!isPbs && <OnboardingCarousel />}
{!isPbs && <ReleaseNotesModal open={showReleaseNotes} onClose={() => setShowReleaseNotes(false)} />}
{!isServerConnected && ( {!isServerConnected && (
<div className="bg-red-500/10 border-b border-red-500/20 px-6 py-3"> <div className="bg-red-500/10 border-b border-red-500/20 px-6 py-3">
@@ -464,22 +495,17 @@ export function ProxmoxDashboard() {
</div> </div>
<div className="min-w-0"> <div className="min-w-0">
<h1 className="text-lg md:text-xl font-semibold text-foreground truncate">{t("app.title")}</h1> <h1 className="text-lg md:text-xl font-semibold text-foreground truncate">{t("app.title")}</h1>
<p className="text-xs md:text-sm text-muted-foreground">{t("app.description")}</p> <p className="text-xs md:text-sm text-muted-foreground">{t(isPbs ? "app.descriptionPbs" : "app.description")}</p>
<div className="lg:hidden flex items-center gap-1 text-xs text-muted-foreground mt-0.5"> <div className="lg:hidden flex items-center gap-1 text-xs text-muted-foreground mt-0.5">
<Server className="h-3 w-3" /> <Clock className="h-3 w-3" />
<span className="truncate">{t("status.node", { node: displayServerName })}</span> <span className="truncate">{t("status.uptime", { uptime: displayUptime })}</span>
</div> </div>
</div> </div>
</div> </div>
{/* Desktop Actions */} {/* Desktop Actions */}
<div className="hidden lg:flex items-center space-x-4"> <div className="hidden lg:flex items-center space-x-4">
<div className="flex items-center space-x-2"> <MonitorSwitcher nodeName={displayServerName} />
<Server className="h-4 w-4 text-muted-foreground" />
<div className="text-sm">
<div className="font-medium text-foreground">{t("status.node", { node: displayServerName })}</div>
</div>
</div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<Badge variant="outline" className={statusColor}> <Badge variant="outline" className={statusColor}>
@@ -580,9 +606,7 @@ export function ProxmoxDashboard() {
</Badge> </Badge>
)} )}
</div> </div>
<span className="text-xs text-muted-foreground whitespace-nowrap"> <MonitorSwitcher nodeName={displayServerName} compact />
{t("status.uptime", { uptime: displayUptime })}
</span>
</div> </div>
</div> </div>
</header> </header>
@@ -623,7 +647,7 @@ export function ProxmoxDashboard() {
{ value: "audit", label: t("navigation.audit"), Icon: ClipboardCheck, default: false }, { value: "audit", label: t("navigation.audit"), Icon: ClipboardCheck, default: false },
{ value: "settings", label: t("navigation.settings"), Icon: SettingsIcon, default: false }, { value: "settings", label: t("navigation.settings"), Icon: SettingsIcon, default: false },
{ value: "about", label: t("navigation.about"), Icon: Info, default: false }, { value: "about", label: t("navigation.about"), Icon: Info, default: false },
] ].filter(item => !isPbs || item.value !== "audit")
const activeNodeItem = NODE_ITEMS.find(i => i.value === activeTab) const activeNodeItem = NODE_ITEMS.find(i => i.value === activeTab)
const activeAdminItem = ADMIN_ITEMS.find(i => i.value === activeTab) const activeAdminItem = ADMIN_ITEMS.find(i => i.value === activeTab)
const isNodeActive = activeNodeItem !== undefined const isNodeActive = activeNodeItem !== undefined
@@ -689,9 +713,10 @@ export function ProxmoxDashboard() {
</DropdownMenuContent> </DropdownMenuContent>
</DropdownMenu> </DropdownMenu>
) )
const renderTop = (id: TabId) => { const renderTop = (id: TopTab) => {
switch (id) { switch (id) {
case "overview": return renderDirect("overview", LayoutDashboard, t("navigation.overview")) case "overview": return renderDirect("overview", LayoutDashboard, t("navigation.overview"))
case "datastores": return renderDirect("datastores", Database, t("pbs.datastores"))
case "apps": return renderDirect("apps", Grid3x3, t("navigation.apps")) case "apps": return renderDirect("apps", Grid3x3, t("navigation.apps"))
case "vms": return renderDirect("vms", Boxes, t("navigation.virtualMachines")) case "vms": return renderDirect("vms", Boxes, t("navigation.virtualMachines"))
case "backup": return renderDirect("backup", DatabaseBackup, t("navigation.backup")) case "backup": return renderDirect("backup", DatabaseBackup, t("navigation.backup"))
@@ -701,7 +726,7 @@ export function ProxmoxDashboard() {
} }
} }
return ( return (
<TabsList className="hidden lg:grid w-full grid-cols-7 bg-card border border-border"> <TabsList className={`hidden lg:grid w-full ${isPbs ? "grid-cols-4" : "grid-cols-7"} bg-card border border-border`}>
{tabOrder.map(renderTop)} {tabOrder.map(renderTop)}
</TabsList> </TabsList>
) )
@@ -762,6 +787,7 @@ export function ProxmoxDashboard() {
{tabOrder.map((id): React.ReactNode => { {tabOrder.map((id): React.ReactNode => {
switch (id) { switch (id) {
case "overview": return btn("overview", LayoutDashboard, t("navigation.overview")) case "overview": return btn("overview", LayoutDashboard, t("navigation.overview"))
case "datastores": return btn("datastores", Database, t("pbs.datastores"))
case "apps": return btn("apps", Grid3x3, t("navigation.apps")) case "apps": return btn("apps", Grid3x3, t("navigation.apps"))
case "vms": return btn("vms", Boxes, t("navigation.virtualMachines")) case "vms": return btn("vms", Boxes, t("navigation.virtualMachines"))
case "backup": return btn("backup", DatabaseBackup, t("navigation.backup")) case "backup": return btn("backup", DatabaseBackup, t("navigation.backup"))
@@ -777,7 +803,7 @@ export function ProxmoxDashboard() {
<React.Fragment key="admin"> <React.Fragment key="admin">
{btn("logs", ScrollText, t("navigation.systemLogs"))} {btn("logs", ScrollText, t("navigation.systemLogs"))}
{btn("security", ShieldCheck, t("navigation.security"))} {btn("security", ShieldCheck, t("navigation.security"))}
{btn("audit", ClipboardCheck, t("navigation.audit"))} {!isPbs && btn("audit", ClipboardCheck, t("navigation.audit"))}
{btn("settings", SettingsIcon, t("navigation.settings"))} {btn("settings", SettingsIcon, t("navigation.settings"))}
{btn("about", Info, t("navigation.about"))} {btn("about", Info, t("navigation.about"))}
</React.Fragment> </React.Fragment>
@@ -811,9 +837,16 @@ export function ProxmoxDashboard() {
5 s / 59 s refresh intervals keep the data fresh in the 5 s / 59 s refresh intervals keep the data fresh in the
background — reopening the tab is instant. */} background — reopening the tab is instant. */}
<TabsContent value="overview" forceMount className="space-y-4 md:space-y-6 mt-0 data-[state=inactive]:hidden"> <TabsContent value="overview" forceMount className="space-y-4 md:space-y-6 mt-0 data-[state=inactive]:hidden">
<SystemOverview key={`overview-${componentKey}`} /> <SystemOverview key={`overview-${componentKey}`} product={product} />
</TabsContent> </TabsContent>
{isPbs && (
<TabsContent value="datastores" className="space-y-4 md:space-y-6 mt-0">
<PBSPanel key={`datastores-${componentKey}`} localOnly />
</TabsContent>
)}
{!isPbs && <>
<TabsContent value="apps" className="space-y-4 md:space-y-6 mt-0"> <TabsContent value="apps" className="space-y-4 md:space-y-6 mt-0">
<AppsDashboard key={`apps-${componentKey}`} /> <AppsDashboard key={`apps-${componentKey}`} />
</TabsContent> </TabsContent>
@@ -839,11 +872,13 @@ export function ProxmoxDashboard() {
<TabsContent value="hardware" className="space-y-4 md:space-y-6 mt-0"> <TabsContent value="hardware" className="space-y-4 md:space-y-6 mt-0">
<Hardware key={`hardware-${componentKey}`} /> <Hardware key={`hardware-${componentKey}`} />
</TabsContent> </TabsContent>
</>}
<TabsContent value="logs" className="space-y-4 md:space-y-6 mt-0"> <TabsContent value="logs" className="space-y-4 md:space-y-6 mt-0">
<SystemLogs key={`logs-${componentKey}`} /> <SystemLogs key={`logs-${componentKey}`} />
</TabsContent> </TabsContent>
{!isPbs && <>
<TabsContent value="audit" className="space-y-4 md:space-y-6 mt-0"> <TabsContent value="audit" className="space-y-4 md:space-y-6 mt-0">
<AuditReport key={`audit-${componentKey}`} /> <AuditReport key={`audit-${componentKey}`} />
</TabsContent> </TabsContent>
@@ -851,13 +886,14 @@ export function ProxmoxDashboard() {
<TabsContent value="backup" className="space-y-4 md:space-y-6 mt-0"> <TabsContent value="backup" className="space-y-4 md:space-y-6 mt-0">
<BackupTab key={`backup-${componentKey}`} /> <BackupTab key={`backup-${componentKey}`} />
</TabsContent> </TabsContent>
</>}
<TabsContent value="terminal" className="mt-0"> <TabsContent value="terminal" className="mt-0">
<TerminalPanel key={`terminal-${componentKey}`} /> <TerminalPanel key={`terminal-${componentKey}`} />
</TabsContent> </TabsContent>
<TabsContent value="security" className="space-y-4 md:space-y-6 mt-0"> <TabsContent value="security" className="space-y-4 md:space-y-6 mt-0">
<Security key={`security-${componentKey}`} /> <Security key={`security-${componentKey}`} hostType={product} />
</TabsContent> </TabsContent>
{/* Profile tab — not surfaced in the top tabs nav. The only {/* Profile tab — not surfaced in the top tabs nav. The only
+9 -4
View File
@@ -66,7 +66,7 @@ function validatePasswordStrength(pw: string, t: (key: string) => string): strin
return null return null
} }
export function Security() { export function Security({ hostType = "pve" }: { hostType?: "pve" | "pbs" } = {}) {
const { language, t } = useI18n() const { language, t } = useI18n()
const st = (key: string, params?: Record<string, string | number>) => t(`securityPage.${key}`, params) const st = (key: string, params?: Record<string, string | number>) => t(`securityPage.${key}`, params)
const interfaceTypeLabel = (type: string) => const interfaceTypeLabel = (type: string) =>
@@ -292,9 +292,11 @@ export function Security() {
checkAuthStatus() checkAuthStatus()
loadApiTokens() loadApiTokens()
loadSslStatus() loadSslStatus()
loadFirewallStatus() if (hostType === "pve") {
loadNetworkInterfaces() loadFirewallStatus()
loadSecurityTools() loadNetworkInterfaces()
loadSecurityTools()
}
}, []) }, [])
const loadFirewallStatus = async () => { const loadFirewallStatus = async () => {
@@ -2778,6 +2780,7 @@ ${(report.sections && report.sections.length > 0) ? `
)} )}
{/* ── Proxmox VE Security Group ── */} {/* ── Proxmox VE Security Group ── */}
{hostType === "pve" && <>
<div className="flex items-center gap-3 mt-4"> <div className="flex items-center gap-3 mt-4">
<h2 className="text-sm font-semibold uppercase tracking-wider text-orange-500">Proxmox VE</h2> <h2 className="text-sm font-semibold uppercase tracking-wider text-orange-500">Proxmox VE</h2>
<div className="flex-1 h-px bg-orange-500/20" /> <div className="flex-1 h-px bg-orange-500/20" />
@@ -4833,6 +4836,8 @@ ${(report.sections && report.sections.length > 0) ? `
</div> </div>
)} )}
</>}
<TwoFactorSetup <TwoFactorSetup
open={show2FASetup} open={show2FASetup}
onClose={() => setShow2FASetup(false)} onClose={() => setShow2FASetup(false)}
+13 -6
View File
@@ -16,6 +16,7 @@ import { Input } from "./ui/input"
import { getNetworkUnit } from "../lib/format-network" import { getNetworkUnit } from "../lib/format-network"
import { fetchApi } from "../lib/api-config" import { fetchApi } from "../lib/api-config"
import { SUPPORTED_LANGUAGES, useI18n } from "../lib/i18n/provider" import { SUPPORTED_LANGUAGES, useI18n } from "../lib/i18n/provider"
import { useIsPbs } from "../lib/use-platform"
import type { LanguageCode } from "../lib/i18n/languages" import type { LanguageCode } from "../lib/i18n/languages"
import { NavTabOrderCard } from "./nav-tab-order-card" import { NavTabOrderCard } from "./nav-tab-order-card"
@@ -312,6 +313,7 @@ interface NetworkInterface {
export function Settings() { export function Settings() {
const { language, setLanguage, t } = useI18n() const { language, setLanguage, t } = useI18n()
const isPbs = useIsPbs()
const tFallback = (key: string, fallback: string) => { const tFallback = (key: string, fallback: string) => {
const translated = t(key) const translated = t(key)
return translated === key ? fallback : translated return translated === key ? fallback : translated
@@ -1124,7 +1126,7 @@ export function Settings() {
</Card> </Card>
{/* Navigation Tab Order — user-orderable top-level tabs */} {/* Navigation Tab Order — user-orderable top-level tabs */}
<NavTabOrderCard /> {!isPbs && <NavTabOrderCard />}
{/* Network Units Settings */} {/* Network Units Settings */}
<Card> <Card>
@@ -1280,7 +1282,7 @@ export function Settings() {
{/* Per-category rows */} {/* Per-category rows */}
<div className="divide-y divide-border/50"> <div className="divide-y divide-border/50">
{suppressionCategories.map((cat) => { {suppressionCategories.filter(cat => !isPbs || healthCategoryKey(cat) !== "vms").map((cat) => {
const IconComp = CATEGORY_ICONS[cat.icon] || HeartPulse const IconComp = CATEGORY_ICONS[cat.icon] || HeartPulse
const effectiveHours = getEffectiveHours(cat) const effectiveHours = getEffectiveHours(cat)
const isCustomMode = effectiveHours === -2 || (cat.key in customValues) const isCustomMode = effectiveHours === -2 || (cat.key in customValues)
@@ -1288,7 +1290,8 @@ export function Settings() {
const isLong = effectiveHours >= 720 && effectiveHours !== -1 && effectiveHours !== -2 const isLong = effectiveHours >= 720 && effectiveHours !== -1 && effectiveHours !== -2
const hasChanged = cat.key in pendingChanges && pendingChanges[cat.key] !== cat.hours const hasChanged = cat.key in pendingChanges && pendingChanges[cat.key] !== cat.hours
const selectVal = isCustomMode ? "custom" : getSelectValue(effectiveHours, cat.key) const selectVal = isCustomMode ? "custom" : getSelectValue(effectiveHours, cat.key)
const catLabel = tFallback(`settings.healthMonitor.categories.${healthCategoryKey(cat)}`, cat.label) const categoryKey = isPbs && healthCategoryKey(cat) === "services" ? "pbsServices" : healthCategoryKey(cat)
const catLabel = tFallback(`settings.healthMonitor.categories.${categoryKey}`, cat.label)
return ( return (
<div key={cat.key}> <div key={cat.key}>
@@ -1496,6 +1499,7 @@ export function Settings() {
</CardContent> </CardContent>
</Card> </Card>
{!isPbs && <>
{/* Remote Storage Exclusions */} {/* Remote Storage Exclusions */}
<Card> <Card>
<CardHeader> <CardHeader>
@@ -1678,6 +1682,7 @@ export function Settings() {
)} )}
</CardContent> </CardContent>
</Card> </Card>
</>}
{/* Network Interface Exclusions */} {/* Network Interface Exclusions */}
<Card> <Card>
@@ -1875,6 +1880,7 @@ export function Settings() {
is re-enabled). */} is re-enabled). */}
<LxcUpdateDetection /> <LxcUpdateDetection />
{!isPbs && <>
{/* GitHub API — optional authentication for app upstream checks. */} {/* GitHub API — optional authentication for app upstream checks. */}
<Card> <Card>
<CardHeader> <CardHeader>
@@ -1981,15 +1987,16 @@ export function Settings() {
)} )}
</CardContent> </CardContent>
</Card> </Card>
</>}
{/* Notification Settings */} {/* Notification Settings (PVE only for now) */}
<NotificationSettings /> {!isPbs && <NotificationSettings />}
{/* Issue #195: snippets storage selector. Only renders when more {/* Issue #195: snippets storage selector. Only renders when more
than one storage advertises content=snippets — on a typical than one storage advertises content=snippets — on a typical
standalone host with just `local` there's nothing to choose, standalone host with just `local` there's nothing to choose,
so showing an empty selector would be noise. */} so showing an empty selector would be noise. */}
{snippetsCandidates.length > 1 && ( {!isPbs && snippetsCandidates.length > 1 && (
<Card> <Card>
<CardHeader> <CardHeader>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
+70 -10
View File
@@ -1,10 +1,12 @@
"use client" "use client"
import { useState, useEffect } from "react" import { useState, useEffect } from "react"
import useSWR from "swr"
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card" import { Card, CardContent, CardHeader, CardTitle } from "./ui/card"
import { Progress } from "./ui/progress" import { Progress } from "./ui/progress"
import { Badge } from "./ui/badge" import { Badge } from "./ui/badge"
import { Cpu, MemoryStick, Thermometer, Server, Zap, AlertCircle, HardDrive, Network, ChevronRight } from "lucide-react" import { Cpu, MemoryStick, Thermometer, Server, Zap, AlertCircle, HardDrive, Network, ChevronRight, Database } from "lucide-react"
import { DatastoreUsageRow, type PBSDatastore } from "./pbs-datastore-usage"
import { NodeMetricsCharts } from "./node-metrics-charts" import { NodeMetricsCharts } from "./node-metrics-charts"
import { NetworkTrafficChart } from "./network-traffic-chart" import { NetworkTrafficChart } from "./network-traffic-chart"
import { TemperatureDetailModal } from "./temperature-detail-modal" import { TemperatureDetailModal } from "./temperature-detail-modal"
@@ -185,9 +187,17 @@ const getUnitsSettings = (): "Bytes" | "Bits" => {
return raw && raw.toLowerCase() === "bits" ? "Bits" : "Bytes" return raw && raw.toLowerCase() === "bits" ? "Bits" : "Bytes"
} }
export function SystemOverview() { export function SystemOverview({ product = "pve" }: { product?: "pve" | "pbs" } = {}) {
const t = useT() const t = useT()
const { language } = useI18n() const { language } = useI18n()
const isPbs = product === "pbs"
// On Proxmox Backup Server the datastores take the place of guests and PVE storage.
const { data: pbsStatus, error: pbsError } = useSWR<{ datastores: PBSDatastore[] | null }>(
isPbs ? "/api/pbs/status?server=local" : null,
(url: string) => fetchApi<{ datastores: PBSDatastore[] | null }>(url),
{ refreshInterval: 60000, revalidateOnFocus: false },
)
const datastores = pbsStatus?.datastores ?? null
const [systemData, setSystemData] = useState<SystemData | null>(null) const [systemData, setSystemData] = useState<SystemData | null>(null)
const [vmData, setVmData] = useState<VMData[]>([]) const [vmData, setVmData] = useState<VMData[]>([])
const [storageData, setStorageData] = useState<StorageData | null>(null) const [storageData, setStorageData] = useState<StorageData | null>(null)
@@ -212,8 +222,8 @@ export function SystemOverview() {
const fetchAllData = async () => { const fetchAllData = async () => {
const [systemResult, vmResult, storageResults, networkResult] = await Promise.all([ const [systemResult, vmResult, storageResults, networkResult] = await Promise.all([
fetchSystemData().finally(() => setLoadingStates((prev) => ({ ...prev, system: false }))), fetchSystemData().finally(() => setLoadingStates((prev) => ({ ...prev, system: false }))),
fetchVMData().finally(() => setLoadingStates((prev) => ({ ...prev, vms: false }))), (isPbs ? Promise.resolve([]) : fetchVMData()).finally(() => setLoadingStates((prev) => ({ ...prev, vms: false }))),
Promise.all([fetchStorageData(), fetchProxmoxStorageData()]).finally(() => Promise.all([fetchStorageData(), isPbs ? Promise.resolve(null) : fetchProxmoxStorageData()]).finally(() =>
setLoadingStates((prev) => ({ ...prev, storage: false })), setLoadingStates((prev) => ({ ...prev, storage: false })),
), ),
fetchNetworkData().finally(() => setLoadingStates((prev) => ({ ...prev, network: false }))), fetchNetworkData().finally(() => setLoadingStates((prev) => ({ ...prev, network: false }))),
@@ -248,12 +258,13 @@ export function SystemOverview() {
}, 5000) }, 5000)
const vmInterval = setInterval(async () => { const vmInterval = setInterval(async () => {
if (isPbs) return
const data = await fetchVMData() const data = await fetchVMData()
setVmData(data) setVmData(data)
}, 59000) }, 59000)
const storageInterval = setInterval(async () => { const storageInterval = setInterval(async () => {
const [storage, proxmoxStorage] = await Promise.all([fetchStorageData(), fetchProxmoxStorageData()]) const [storage, proxmoxStorage] = await Promise.all([fetchStorageData(), isPbs ? Promise.resolve(null) : fetchProxmoxStorageData()])
if (storage) setStorageData(storage) if (storage) setStorageData(storage)
if (proxmoxStorage) setProxmoxStorageData(proxmoxStorage) if (proxmoxStorage) setProxmoxStorageData(proxmoxStorage)
}, 59000) }, 59000)
@@ -278,7 +289,7 @@ export function SystemOverview() {
clearInterval(networkInterval) clearInterval(networkInterval)
window.removeEventListener("networkUnitChanged" as any, handleUnitChange) window.removeEventListener("networkUnitChanged" as any, handleUnitChange)
} }
}, [t]) }, [t, isPbs])
if (!hasAttemptedLoad || loadingStates.system) { if (!hasAttemptedLoad || loadingStates.system) {
return ( return (
@@ -527,7 +538,46 @@ export function SystemOverview() {
</CardContent> </CardContent>
</Card> </Card>
{/* ── Active VM & LXC (preview restyle v2: pills mismo tamaño que "X running") ── */} {isPbs ? (
<Card className="bg-card border-border">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">{t("pbs.datastores")}</CardTitle>
<Database className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
{!datastores && !pbsError ? (
<div className="space-y-2 animate-pulse">
<div className="h-10 bg-muted rounded w-20"></div>
<div className="h-5 bg-muted rounded w-32"></div>
</div>
) : !datastores ? (
<p className="text-sm text-amber-500">{t("pbs.loadFailed")}</p>
) : datastores.length === 0 ? (
<p className="text-sm text-muted-foreground">{t("pbs.noDatastores")}</p>
) : (
<div className="space-y-3">
<div className="flex items-end justify-between">
<span className="text-4xl font-bold leading-none text-foreground">{datastores.length}</span>
{datastores.some(store => store.error) ? (
<Badge variant="outline" className="bg-amber-500/10 text-amber-500 border-amber-500/20">
{t("pbs.datastoresUnavailable", { count: datastores.filter(store => store.error).length })}
</Badge>
) : (
<Badge variant="outline" className="bg-green-500/10 text-green-500 border-green-500/20">
{t("pbs.datastoresOnline")}
</Badge>
)}
</div>
{datastores.slice(0, 2).map(store => <DatastoreUsageRow key={store.name} store={store} compact />)}
{datastores.length > 2 && (
<p className="text-xs text-muted-foreground">{t("pbs.moreDatastores", { count: datastores.length - 2 })}</p>
)}
</div>
)}
</CardContent>
</Card>
) : (
/* ── Active VM & LXC (preview restyle v2: pills mismo tamaño que "X running") ── */
<Card className="bg-card border-border"> <Card className="bg-card border-border">
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2"> <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium text-muted-foreground">{t("overview.activeVmLxc")}</CardTitle> <CardTitle className="text-sm font-medium text-muted-foreground">{t("overview.activeVmLxc")}</CardTitle>
@@ -565,6 +615,7 @@ export function SystemOverview() {
)} )}
</CardContent> </CardContent>
</Card> </Card>
)}
<Card <Card
className={`bg-card border-border ${systemData.temperature > 0 ? "cursor-pointer hover:bg-white/5 transition-colors" : ""}`} className={`bg-card border-border ${systemData.temperature > 0 ? "cursor-pointer hover:bg-white/5 transition-colors" : ""}`}
@@ -708,7 +759,16 @@ export function SystemOverview() {
</div> </div>
</div> </div>
{vmLxcStorages && vmLxcStorages.length > 0 ? ( {isPbs ? (
<div className="space-y-3">
<div className="text-xs font-medium text-muted-foreground mb-2">{t("pbs.datastores")}</div>
{!datastores ? (
<div className="text-center py-4 text-muted-foreground text-sm">{pbsError ? t("pbs.loadFailed") : t("pbs.loading")}</div>
) : datastores.length === 0 ? (
<div className="text-center py-4 text-muted-foreground text-sm">{t("pbs.noDatastores")}</div>
) : datastores.map(store => <DatastoreUsageRow key={store.name} store={store} />)}
</div>
) : vmLxcStorages && vmLxcStorages.length > 0 ? (
<div className="space-y-2 pb-3 border-b border-border"> <div className="space-y-2 pb-3 border-b border-border">
<div className="text-xs font-medium text-muted-foreground mb-2">{t("overview.vmLxcStorage")}</div> <div className="text-xs font-medium text-muted-foreground mb-2">{t("overview.vmLxcStorage")}</div>
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
@@ -744,7 +804,7 @@ export function SystemOverview() {
</div> </div>
)} )}
{localStorage && ( {!isPbs && localStorage && (
<div className="space-y-2"> <div className="space-y-2">
<div className="text-xs font-medium text-muted-foreground mb-2">{t("overview.localStorageSystem")}</div> <div className="text-xs font-medium text-muted-foreground mb-2">{t("overview.localStorageSystem")}</div>
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
@@ -899,7 +959,7 @@ export function SystemOverview() {
<span className="text-foreground">{formatSystemUptime(systemData.uptime)}</span> <span className="text-foreground">{formatSystemUptime(systemData.uptime)}</span>
</div> </div>
<div className="flex justify-between"> <div className="flex justify-between">
<span className="text-muted-foreground">{t("overview.proxmoxVersion")}</span> <span className="text-muted-foreground">{t(isPbs ? "overview.pbsVersion" : "overview.proxmoxVersion")}</span>
<span className="text-foreground">{systemData.proxmox_version || t("app.notAvailable")}</span> <span className="text-foreground">{systemData.proxmox_version || t("app.notAvailable")}</span>
</div> </div>
<div className="flex justify-between"> <div className="flex justify-between">
+27 -120
View File
@@ -11,7 +11,7 @@ import { Badge } from "./ui/badge"
import { Progress } from "./ui/progress" import { Progress } from "./ui/progress"
import { Button } from "./ui/button" import { Button } from "./ui/button"
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "./ui/dialog" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "./ui/dialog"
import { Server, Play, Square, Cpu, MemoryStick, HardDrive, Network, Power, RotateCcw, StopCircle, Container, ChevronDown, ChevronUp, ChevronRight, Terminal, Archive, Plus, PlusCircle, Loader2, Clock, Database, Shield, Bell, FileText, Settings2, Activity, Package, RefreshCw, EthernetPort, ArrowUpCircle, Info, CheckCircle2, EyeOff, Eye, Trash2, Check, X, AlertTriangle, AlertCircle, Search, Pin, Tag as TagIcon } from 'lucide-react' import { Server, Play, Square, Cpu, MemoryStick, HardDrive, Network, Power, RotateCcw, StopCircle, Container, ChevronDown, ChevronUp, ChevronRight, Terminal, Archive, Plus, PlusCircle, Loader2, Database, Shield, FileText, Settings2, Activity, Package, RefreshCw, EthernetPort, ArrowUpCircle, Info, CheckCircle2, EyeOff, Eye, Trash2, Check, X, AlertTriangle, AlertCircle, Search, Pin, Tag as TagIcon } from 'lucide-react'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"
import { Checkbox } from "./ui/checkbox" import { Checkbox } from "./ui/checkbox"
import { Switch } from "./ui/switch" import { Switch } from "./ui/switch"
@@ -23,6 +23,8 @@ import { MetricsView } from "./metrics-dialog"
import { LxcTerminalModal } from "./lxc-terminal-modal" import { LxcTerminalModal } from "./lxc-terminal-modal"
import { ScriptTerminalModal } from "./script-terminal-modal" import { ScriptTerminalModal } from "./script-terminal-modal"
import { LxcAppPanel, ThemeAwareLogo, ociImageLabel } from "./lxc-app-panel" import { LxcAppPanel, ThemeAwareLogo, ociImageLabel } from "./lxc-app-panel"
import { GuestBackupList } from "./guest-backup-list"
import type { GuestBackup } from "../lib/guest-backup-archives"
import { OciConsoleLogPanel } from "./oci-console-log-panel" import { OciConsoleLogPanel } from "./oci-console-log-panel"
import { AppUpdaterEditor, type AppUpdateMethod } from "./app-updater-editor" import { AppUpdaterEditor, type AppUpdateMethod } from "./app-updater-editor"
import { formatStorage } from "../lib/utils" import { formatStorage } from "../lib/utils"
@@ -399,16 +401,7 @@ interface BackupStorage {
avail_human?: string avail_human?: string
} }
interface VMBackup { type VMBackup = GuestBackup
volid: string
storage: string
type: string
size: number
size_human: string
timestamp: number
date: string
notes?: string
}
// Sprint 13.29: shape returned by /api/lxc/<vmid>/mount-points. Lives // Sprint 13.29: shape returned by /api/lxc/<vmid>/mount-points. Lives
// next to VMBackup since both are LXC-modal data structures. // next to VMBackup since both are LXC-modal data structures.
@@ -970,7 +963,6 @@ export function VirtualMachines() {
const [showBackupModal, setShowBackupModal] = useState(false) const [showBackupModal, setShowBackupModal] = useState(false)
const [backupMode, setBackupMode] = useState<string>("snapshot") const [backupMode, setBackupMode] = useState<string>("snapshot")
const [backupProtected, setBackupProtected] = useState(false) const [backupProtected, setBackupProtected] = useState(false)
const [backupNotification, setBackupNotification] = useState<string>("auto")
const [backupNotes, setBackupNotes] = useState<string>("{{guestname}}") const [backupNotes, setBackupNotes] = useState<string>("{{guestname}}")
const [backupPbsChangeMode, setBackupPbsChangeMode] = useState<string>("default") const [backupPbsChangeMode, setBackupPbsChangeMode] = useState<string>("default")
@@ -1615,7 +1607,7 @@ export function VirtualMachines() {
} }
} }
const fetchVmBackups = async (vmid: number) => { const fetchVmBackups = async (vmid: number, force = false) => {
// Stale-while-revalidate with a 6-hour freshness gate. // Stale-while-revalidate with a 6-hour freshness gate.
// //
// 1. If we already have backups (bulk hydration or a previous // 1. If we already have backups (bulk hydration or a previous
@@ -1632,15 +1624,16 @@ export function VirtualMachines() {
// 3. Loading spinner only on the true first load. // 3. Loading spinner only on the true first load.
const GATE_MS = 6 * 60 * 60 * 1000 const GATE_MS = 6 * 60 * 60 * 1000
const seed = vmModalCacheRef.current.backups.get(vmid) const seed = vmModalCacheRef.current.backups.get(vmid)
const isStale = !seed || (Date.now() - seed.fetchedAt) > GATE_MS const isStale = force || !seed || (Date.now() - seed.fetchedAt) > GATE_MS
if (!seed) setLoadingBackups(true) if (!seed) setLoadingBackups(true)
try { try {
const url = isStale const url = isStale
? `/api/vms/${vmid}/backups?fresh=1` ? `/api/vms/${vmid}/backups?fresh=1`
: `/api/vms/${vmid}/backups` : `/api/vms/${vmid}/backups`
const response = await fetchApi<{ backups?: VMBackup[] }>(url) const response = await fetchApi<{ backups?: VMBackup[]; error?: string }>(url)
if (response.error || !Array.isArray(response.backups)) throw new Error(response.error || "Backup inventory unavailable")
if (response.backups) { if (response.backups) {
setVmBackups(response.backups) if (selectedVMRef.current?.vmid === vmid) setVmBackups(response.backups)
vmModalCacheRef.current.backups.set(vmid, { backups: response.backups, fetchedAt: Date.now() }) vmModalCacheRef.current.backups.set(vmid, { backups: response.backups, fetchedAt: Date.now() })
} }
} catch (error) { } catch (error) {
@@ -1648,9 +1641,10 @@ export function VirtualMachines() {
// Only clear the visible list if we had nothing to show // Only clear the visible list if we had nothing to show
// in the first place — a transient network hiccup must not // in the first place — a transient network hiccup must not
// wipe the stale-but-useful view the user is looking at. // wipe the stale-but-useful view the user is looking at.
if (!seed) setVmBackups([]) if (!seed && selectedVMRef.current?.vmid === vmid) setVmBackups([])
if (force) throw error
} finally { } finally {
if (!seed) setLoadingBackups(false) if (!seed && selectedVMRef.current?.vmid === vmid) setLoadingBackups(false)
} }
} }
@@ -1703,7 +1697,6 @@ export function VirtualMachines() {
// Reset modal to defaults // Reset modal to defaults
setBackupMode("snapshot") setBackupMode("snapshot")
setBackupProtected(false) setBackupProtected(false)
setBackupNotification("auto")
setBackupNotes("{{guestname}}") setBackupNotes("{{guestname}}")
setBackupPbsChangeMode("default") setBackupPbsChangeMode("default")
// Auto-select first storage if none selected // Auto-select first storage if none selected
@@ -1727,7 +1720,6 @@ export function VirtualMachines() {
mode: backupMode, mode: backupMode,
compress: "zstd", compress: "zstd",
protected: backupProtected, protected: backupProtected,
notification: backupNotification,
notes: backupNotes, notes: backupNotes,
pbs_change_detection: backupPbsChangeMode pbs_change_detection: backupPbsChangeMode
}), }),
@@ -1884,40 +1876,6 @@ export function VirtualMachines() {
setTerminalOpen(true) setTerminalOpen(true)
} }
const handleDownloadLogs = async (vmid: number, vmName: string) => {
try {
const data = await fetchApi(`/api/vms/${vmid}/logs`)
// Format logs as plain text
let logText = `=== ${t("vmLxc.logs.header", { name: vmName, vmid })} ===\n`
logText += `${t("vmLxc.logs.node")}: ${data.node}\n`
logText += `${t("vmLxc.logs.type")}: ${data.type}\n`
logText += `${t("vmLxc.logs.totalLines")}: ${data.log_lines}\n`
logText += `${t("vmLxc.logs.generated")}: ${new Date().toISOString()}\n`
logText += `\n${"=".repeat(80)}\n\n`
if (data.logs && Array.isArray(data.logs)) {
data.logs.forEach((log: any) => {
if (typeof log === "object" && log.t) {
logText += `${log.t}\n`
} else if (typeof log === "string") {
logText += `${log}\n`
}
})
}
const blob = new Blob([logText], { type: "text/plain" })
const url = URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = `${vmName}-${vmid}-logs.txt`
a.click()
URL.revokeObjectURL(url)
} catch (error) {
console.error("Error downloading logs:", error)
}
}
const getStatusColor = (status: string) => { const getStatusColor = (status: string) => {
switch (status) { switch (status) {
case "running": case "running":
@@ -2868,8 +2826,7 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
onClick?: () => void, onClick?: () => void,
) => { ) => {
if (!uc) return null if (!uc) return null
const stale = uc.stale === true || (!!uc.last_check && isLxcUpdateStale(uc)) if (!uc.available || !uc.count || uc.count <= 0) return null
if (!stale && (!uc.available || !uc.count || uc.count <= 0)) return null
const last = uc.last_check const last = uc.last_check
? new Date(uc.last_check).toLocaleString() ? new Date(uc.last_check).toLocaleString()
: "—" : "—"
@@ -2917,8 +2874,8 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
space — matters most on the LXC card row where uptime, space — matters most on the LXC card row where uptime,
IP and other chips compete for width. Count alone with IP and other chips compete for width. Count alone with
the up-arrow reads unambiguously as "pending updates". */} the up-arrow reads unambiguously as "pending updates". */}
{stale ? <Clock className={iconSize} /> : <ArrowUpCircle className={iconSize} />} <ArrowUpCircle className={iconSize} />
{stale ? t("vmLxc.updates.checkStale") : uc.count} {uc.count}
{/* Chevron only when the badge is wired up as a clickable {/* Chevron only when the badge is wired up as a clickable
shortcut — its absence on the dashboard card avoids shortcut — its absence on the dashboard card avoids
implying interactivity where there isn't any (the whole implying interactivity where there isn't any (the whole
@@ -7460,7 +7417,7 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
)} )}
{/* Backups Tab */} {/* Backups Tab */}
{activeModalTab === "backups" && ( {activeModalTab === "backups" && selectedVM && (
/* Backups tab layout: the Card fills the whole tab /* Backups tab layout: the Card fills the whole tab
body so the backup list can scroll inside its body so the backup list can scroll inside its
bordered card instead of leaving empty space bordered card instead of leaving empty space
@@ -7495,48 +7452,17 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
{/* Divider */} {/* Divider */}
<div className="border-t border-border/50 mb-4 shrink-0" /> <div className="border-t border-border/50 mb-4 shrink-0" />
{/* Backup List */} <GuestBackupList
<div className="flex items-center justify-between mb-3 shrink-0"> key={selectedVM.vmid}
<span className="text-xs text-muted-foreground">{t("vmLxc.backups.available")}</span> vmid={selectedVM.vmid}
<Badge variant="secondary" className="text-xs h-5">{vmBackups.length}</Badge> backups={vmBackups}
</div> loading={loadingBackups}
onRefresh={async () => {
{loadingBackups ? ( vmModalCacheRef.current.backups.delete(selectedVM.vmid)
<div className="flex-1 flex items-center justify-center text-muted-foreground min-h-0"> await fetchVmBackups(selectedVM.vmid, true)
<Loader2 className="h-4 w-4 animate-spin mr-2" /> }}
<span className="text-sm">{t("vmLxc.backups.loading")}</span> storageColor={getStorageColor}
</div> />
) : vmBackups.length === 0 ? (
<div className="flex-1 flex flex-col items-center justify-center text-muted-foreground min-h-0">
<Archive className="h-12 w-12 mb-3 opacity-30" />
<span className="text-sm">{t("vmLxc.backups.empty")}</span>
<span className="text-xs mt-1">{t("vmLxc.backups.emptyHint")}</span>
</div>
) : (
<div className="space-y-2 flex-1 overflow-y-auto min-h-0 pr-1 -mr-1">
{vmBackups.map((backup, index) => (
<div
key={`backup-${backup.volid}-${index}`}
className="flex items-center justify-between p-3 rounded-lg bg-muted/30 hover:bg-muted/50 transition-colors"
>
<div className="flex items-center gap-2 flex-1 min-w-0">
<div className="w-2 h-2 rounded-full bg-green-500 flex-shrink-0" />
<Clock className="h-4 w-4 text-muted-foreground flex-shrink-0" />
<span className="text-sm text-foreground">{backup.date}</span>
<Badge
variant="outline"
className={`text-xs ml-auto flex-shrink-0 ${getStorageColor(backup.storage).bg} ${getStorageColor(backup.storage).text} ${getStorageColor(backup.storage).border}`}
>
{backup.storage}
</Badge>
</div>
<Badge variant="outline" className="font-mono ml-2 flex-shrink-0">
{backup.size_human}
</Badge>
</div>
))}
</div>
)}
</CardContent> </CardContent>
</Card> </Card>
</div> </div>
@@ -7889,25 +7815,6 @@ const handleDownloadLogs = async (vmid: number, vmName: string) => {
</div> </div>
</div> </div>
{/* Notification Row */}
<div className="space-y-2">
<Label className="text-sm flex items-center gap-1.5">
<Bell className="h-3.5 w-3.5" />
{t("vmLxc.backupModal.notification")}
</Label>
<Select value={backupNotification} onValueChange={setBackupNotification}>
<SelectTrigger className="bg-background">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="auto">{t("vmLxc.backupModal.useGlobalSettings")}</SelectItem>
<SelectItem value="always">{t("vmLxc.backupModal.alwaysNotify")}</SelectItem>
<SelectItem value="failure">{t("vmLxc.backupModal.notifyOnFailure")}</SelectItem>
<SelectItem value="never">{t("vmLxc.backupModal.neverNotify")}</SelectItem>
</SelectContent>
</Select>
</div>
{/* Protected Checkbox */} {/* Protected Checkbox */}
<div className="flex items-center space-x-2"> <div className="flex items-center space-x-2">
<Checkbox <Checkbox
+1 -18
View File
@@ -21,6 +21,7 @@ import {
import { fetchApi } from "../lib/api-config" import { fetchApi } from "../lib/api-config"
import { cancelButtonClass } from "../lib/utils" import { cancelButtonClass } from "../lib/utils"
import { useT } from "../lib/i18n/provider" import { useT } from "../lib/i18n/provider"
import { RETENTION, parseRetention, buildRetention } from "../lib/backup-retention"
interface VmBackupJob { interface VmBackupJob {
id: string id: string
@@ -70,24 +71,6 @@ const EMPTY_FORM: FormState = {
mode: "snapshot", compress: "zstd", retention: {}, keepAll: false, notes: "{{guestname}}", attached: [], mode: "snapshot", compress: "zstd", retention: {}, keepAll: false, notes: "{{guestname}}", attached: [],
} }
// The limits a job can keep, in the order Proxmox lists them.
const RETENTION = ["last", "hourly", "daily", "weekly", "monthly", "yearly"] as const
const parseRetention = (prune: string) => {
const fields: Record<string, string> = {}
let keepAll = false
for (const part of prune.split(",")) {
const [key, value] = part.trim().split("=")
if (key === "keep-all") keepAll = value === "1"
else if (key?.startsWith("keep-") && Number(value) > 0) fields[key.slice(5)] = String(Number(value))
}
return { fields, keepAll }
}
const buildRetention = (fields: Record<string, string>, keepAll: boolean) => {
const parts = RETENTION.filter((key) => Number(fields[key]) > 0).map((key) => `keep-${key}=${Number(fields[key])}`)
return parts.length ? parts.join(",") : keepAll ? "keep-all=1" : ""
}
const isOn = (value: unknown) => value === 1 || value === true || value === "1" const isOn = (value: unknown) => value === 1 || value === true || value === "1"
const jobEnabled = (job: VmBackupJob) => job.enabled === undefined || isOn(job.enabled) const jobEnabled = (job: VmBackupJob) => job.enabled === undefined || isOn(job.enabled)
+18
View File
@@ -0,0 +1,18 @@
// Native Proxmox retention properties, shared by storage and job editors.
export const RETENTION = ["last", "hourly", "daily", "weekly", "monthly", "yearly"] as const
export function parseRetention(prune: string) {
const fields: Record<string, string> = {}
let keepAll = false
for (const part of prune.split(",")) {
const [key, value] = part.trim().split("=")
if (key === "keep-all") keepAll = value === "1"
else if (key?.startsWith("keep-") && Number(value) > 0) fields[key.slice(5)] = String(Number(value))
}
return { fields, keepAll }
}
export function buildRetention(fields: Record<string, string>, keepAll: boolean) {
const parts = RETENTION.filter(key => Number(fields[key]) > 0).map(key => `keep-${key}=${Number(fields[key])}`)
return parts.length ? parts.join(",") : keepAll ? "keep-all=1" : ""
}
+55
View File
@@ -0,0 +1,55 @@
import { fetchApi } from './api-config'
export interface GuestBackup {
volid: string
storage: string
type: string
size: number
size_human: string
timestamp: number
date: string
notes?: string
protected?: boolean
}
export interface BackupDeletionResult {
volid: string
status: 'deleted' | 'pending' | 'unconfirmed'
task?: string
error?: string
}
// Exposed separately so callers can invalidate their inventory even after a
// lost POST response. Never retry the destructive request automatically.
export async function deleteGuestBackups(vmid: number, backups: GuestBackup[]): Promise<BackupDeletionResult[]> {
const response = await fetchApi<{ results: BackupDeletionResult[] }>(`/api/vms/${vmid}/backups/delete`, {
method: 'POST',
body: JSON.stringify({ confirm: true, backups: backups.map(({ volid, storage }) => ({ volid, storage })) }),
// Up to 20 archives, checked first and then deleted four at a time.
signal: AbortSignal.timeout(300_000),
})
if (!Array.isArray(response.results) || response.results.length !== backups.length ||
response.results.some((result, index) => result.volid !== backups[index].volid ||
!['deleted', 'pending', 'unconfirmed'].includes(result.status))) throw new Error('deletionUnconfirmed')
return Promise.all(response.results.map(async result => {
if (result.status !== 'pending' || !result.task) return result
const deadline = Date.now() + 120_000
let failures = 0
while (Date.now() < deadline) {
await new Promise(resolve => setTimeout(resolve, 1500))
try {
const status = await fetchApi<BackupDeletionResult>(
`/api/vms/${vmid}/backups/delete-status?task=${encodeURIComponent(result.task!)}`,
{ signal: AbortSignal.timeout(10_000) },
)
if (status.volid !== result.volid || !['deleted', 'pending', 'unconfirmed'].includes(status.status)) break
if (status.status !== 'pending') return status
failures = 0
} catch {
if (++failures >= 3) break
}
}
return { ...result, status: 'unconfirmed', error: 'deletionUnconfirmed' }
}))
}
+8
View File
@@ -0,0 +1,8 @@
import useSWR from "swr"
import { fetchApi } from "./api-config"
// Shares the dashboard's /api/platform request through the SWR cache.
export function useIsPbs(): boolean {
const { data } = useSWR<{ product: string }>("/api/platform", (url: string) => fetchApi<{ product: string }>(url), { revalidateOnFocus: false })
return data?.product === "pbs"
}
+158 -12
View File
@@ -8,7 +8,8 @@
"unknown": "Unbekannt", "unknown": "Unbekannt",
"notAvailable": "N/A", "notAvailable": "N/A",
"serverOffline": "Server offline", "serverOffline": "Server offline",
"supportProject": "Unterstützen Sie das Projekt und tragen Sie dazu bei" "supportProject": "Unterstützen Sie das Projekt und tragen Sie dazu bei",
"descriptionPbs": "Proxmox Backup Server Dashboard"
}, },
"common": { "common": {
"notAvailable": "N/A", "notAvailable": "N/A",
@@ -165,7 +166,8 @@
}, },
"runningCountOne": "{count} läuft", "runningCountOne": "{count} läuft",
"runningCountFew": "{count} laufen", "runningCountFew": "{count} laufen",
"runningCountMany": "{count} laufen" "runningCountMany": "{count} laufen",
"pbsVersion": "PBS-Version:"
}, },
"storage": { "storage": {
"loadingTitle": "Speicherdaten werden geladen...", "loadingTitle": "Speicherdaten werden geladen...",
@@ -1110,7 +1112,21 @@
"available": "Verfügbare Backups", "available": "Verfügbare Backups",
"loading": "Backups werden geladen...", "loading": "Backups werden geladen...",
"empty": "Keine Backups gefunden", "empty": "Keine Backups gefunden",
"emptyHint": "Erstellen Sie Ihr erstes Backup über die Schaltfläche oben" "emptyHint": "Erstellen Sie Ihr erstes Backup über die Schaltfläche oben",
"refresh": "Aktualisieren",
"refreshFailed": "Sicherungen konnten nicht aktualisiert werden. Aktualisieren Sie erneut, um ihren aktuellen Zustand zu bestätigen.",
"deleteSelected": "Ausgewählte löschen ({count})",
"deletedCount": "{count} Sicherung(en) gelöscht.",
"unconfirmedCount": "Die Löschung von {count} Sicherung(en) wurde nicht bestätigt. Aktualisieren Sie die Liste und prüfen Sie die Proxmox-Aufgaben, bevor Sie es erneut versuchen.",
"deleteFailed": "Die Löschung konnte nicht bestätigt werden. Aktualisieren Sie die Liste und prüfen Sie die Proxmox-Aufgaben, bevor Sie es erneut versuchen.",
"selectHint": "Ungeschützte Sicherungen auswählen (bis zu 20 pro Durchgang)",
"selectBackup": "Sicherung auswählen",
"protected": "Geschützt",
"confirmTitle": "{count} Sicherung(en) löschen?",
"confirmDescription": "Diese Sicherungsarchive für VM/CT {vmid} werden dauerhaft gelöscht. Geschützte Sicherungen sind ausgeschlossen. Der Gast wird dadurch nicht gelöscht.",
"deleting": "Warten, bis Proxmox die ausgewählten Archive gelöscht hat…",
"cancel": "Abbrechen",
"confirmDelete": "Sicherungen löschen"
}, },
"updatesPanel": { "updatesPanel": {
"pendingTitle": "Ausstehende Paketaktualisierungen", "pendingTitle": "Ausstehende Paketaktualisierungen",
@@ -1721,7 +1737,17 @@
"portCategoryCustomPlaceholder": "Geben Sie eine Kategorie ein und drücken Sie Enter (Esc zum Abbrechen)", "portCategoryCustomPlaceholder": "Geben Sie eine Kategorie ein und drücken Sie Enter (Esc zum Abbrechen)",
"portCustomUrlPlaceholder": "Benutzerdefinierte URL (z. B. https://vault.example.com) überschreibt IP:port", "portCustomUrlPlaceholder": "Benutzerdefinierte URL (z. B. https://vault.example.com) überschreibt IP:port",
"stackMemberTitle": "Teil einer Multi-Container-Anwendung", "stackMemberTitle": "Teil einer Multi-Container-Anwendung",
"stackMemberBody": "Dieser Container gehört zu einer Anwendung, deren Hauptcontainer {primary} ist. Die Anwendung, ihre Weblinks und ihre Version werden dort angezeigt." "stackMemberBody": "Dieser Container gehört zu einer Anwendung, deren Hauptcontainer {primary} ist. Die Anwendung, ihre Weblinks und ihre Version werden dort angezeigt.",
"nativeEvidencePort": "Lauschender TCP-Port",
"nativeEvidenceFile": "Installierte Datei",
"nativeEvidenceProcess": "Laufende ausführbare Datei",
"nativeEvidenceDirectory": "Arbeitsverzeichnis",
"nativeEvidenceService": "Dienstdefinition",
"nativeScanFailed": "Die Anwendungssuche konnte nicht abgeschlossen werden. Frühere Ergebnisse sind möglicherweise veraltet; versuchen Sie es erneut.",
"nativeScanPartial": "Die Anwendungssuche ist unvollständig. Prüfen Sie die gefundenen Hinweise und versuchen Sie es erneut.",
"nativeVersionUnknown": "Installierte Version unbekannt",
"nativeEvidenceStale": "Die Hinweise sind möglicherweise veraltet oder unvollständig.",
"nativeRegisterOnly": "Bestätigen Sie die Registrierung im Editor. Eine bekannte Version wird als manueller Wert gespeichert; es wird kein Updater eingerichtet."
}, },
"statusFilter": { "statusFilter": {
"ariaLabel": "Virtuelle Maschinen und Container filtern", "ariaLabel": "Virtuelle Maschinen und Container filtern",
@@ -1817,7 +1843,8 @@
"logs": "Systemprotokolle", "logs": "Systemprotokolle",
"updates": "Systemaktualisierungen", "updates": "Systemaktualisierungen",
"security": "Sicherheit & Zertifikate", "security": "Sicherheit & Zertifikate",
"temperature": "Temperatur" "temperature": "Temperatur",
"pbsServices": "PBS Services"
} }
}, },
"remoteStorage": { "remoteStorage": {
@@ -2078,7 +2105,7 @@
"webhookUrl": "Webhook-URL", "webhookUrl": "Webhook-URL",
"enableEmail": "E-Mail aktivieren", "enableEmail": "E-Mail aktivieren",
"smtpHost": "SMTP-Host", "smtpHost": "SMTP-Host",
"port": "Hafen", "port": "Port",
"tlsMode": "TLS-Modus", "tlsMode": "TLS-Modus",
"noTls": "Keine (Port 25)", "noTls": "Keine (Port 25)",
"username": "Benutzername", "username": "Benutzername",
@@ -2111,7 +2138,13 @@
"inactiveFooter": "Aktivieren Sie Benachrichtigungen, um System-, Gesundheits- und Sicherheitswarnungen über die konfigurierten Kanäle zu erhalten.", "inactiveFooter": "Aktivieren Sie Benachrichtigungen, um System-, Gesundheits- und Sicherheitswarnungen über die konfigurierten Kanäle zu erhalten.",
"notificationLanguage": "Benachrichtigungssprache", "notificationLanguage": "Benachrichtigungssprache",
"selectNotificationLanguage": "Benachrichtigungssprache auswählen", "selectNotificationLanguage": "Benachrichtigungssprache auswählen",
"notificationLanguageHint": "Wird für deterministische Laufzeitbenachrichtigungen verwendet, auch wenn KI deaktiviert ist." "notificationLanguageHint": "Wird für deterministische Laufzeitbenachrichtigungen verwendet, auch wenn KI deaktiviert ist.",
"appriseServiceHelp": "Slack und Matrix einrichten",
"appriseSlackSteps": "Erstellen Sie in Ihrer Slack-App einen eingehenden Webhook für den Zielkanal und fügen Sie dessen vollständige HTTPS-URL in das Feld Apprise-URL ein. Eine Umwandlung ist nicht erforderlich.",
"appriseSlackDocs": "Slack-URL-Formate und Einrichtung",
"appriseMatrixSteps": "Verwenden Sie ein Benachrichtigungskonto, das in den Raum schreiben darf. Ersetzen Sie den Beispielbenutzer, das Passwort, den Homeserver und die vollständige Raum-ID; kodieren Sie Sonderzeichen in Zugangsdaten für URLs. Das Schema matrixs verwendet HTTPS. Informationen zur Unterstützung von Tokens, SSO und verschlüsselten Räumen in Ihrer installierten Apprise-Version finden Sie in der Dokumentation.",
"appriseMatrixDocs": "Matrix-URL-Formate und Einrichtung",
"appriseServiceFinish": "Aktivieren Sie Apprise, geben Sie Ihre URL ein, wählen Sie Benachrichtigungskategorien oder einzelne Ereignisse aus, speichern Sie und verwenden Sie anschließend Test senden. Diese Beispiele ändern Ihre gespeicherte URL nicht. Dieser Apprise-Kanal hat ein Ziel; verwenden Sie die Apprise-API für mehrere Dienste gleichzeitig."
}, },
"ai": { "ai": {
"title": "KI-Verbesserung", "title": "KI-Verbesserung",
@@ -2545,9 +2578,9 @@
"twoFactor": { "twoFactor": {
"title": "Zwei-Faktor-Authentifizierung", "title": "Zwei-Faktor-Authentifizierung",
"hint": "Fügen Sie nach dem Passwort einen Einmalcode aus einer Authentifizierungs-App hinzu.", "hint": "Fügen Sie nach dem Passwort einen Einmalcode aus einer Authentifizierungs-App hinzu.",
"enable": "Aktivieren Sie 2FA", "enable": "2FA aktivieren",
"enabled": "2FA ist aktiviert.", "enabled": "2FA ist aktiviert.",
"disable": "Deaktivieren Sie 2FA", "disable": "2FA deaktivieren",
"disableTitle": "Deaktivieren Sie die Zwei-Faktor-Authentifizierung", "disableTitle": "Deaktivieren Sie die Zwei-Faktor-Authentifizierung",
"disableDescription": "Bestätigen Sie mit Ihrem Passwort und einem aktuellen 2FA-Code oder Backup-Code.", "disableDescription": "Bestätigen Sie mit Ihrem Passwort und einem aktuellen 2FA-Code oder Backup-Code.",
"codeOrBackup": "2FA-Code oder Backup-Code", "codeOrBackup": "2FA-Code oder Backup-Code",
@@ -2690,7 +2723,7 @@
"interfaceOptional": "Schnittstelle (optional)", "interfaceOptional": "Schnittstelle (optional)",
"applyTo": "Gelten", "applyTo": "Gelten",
"commentOptional": "Kommentar (optional)", "commentOptional": "Kommentar (optional)",
"port": "Hafen", "port": "Port",
"source": "Quelle", "source": "Quelle",
"level": "Ebene", "level": "Ebene",
"interface": "Schnittstelle", "interface": "Schnittstelle",
@@ -2982,7 +3015,7 @@
"needQrOrKey": "Ein QR-Code oder manueller Schlüssel aus diesem Setup.", "needQrOrKey": "Ein QR-Code oder manueller Schlüssel aus diesem Setup.",
"needBackupCodes": "Backup-Codes werden an einem sicheren Ort gespeichert.", "needBackupCodes": "Backup-Codes werden an einem sicheren Ort gespeichert.",
"starting": "Beginnt...", "starting": "Beginnt...",
"startSetup": "Starten Sie die Einrichtung", "startSetup": "Einrichtung starten",
"scanTitle": "Scannen Sie den QR-Code", "scanTitle": "Scannen Sie den QR-Code",
"scanDescription": "Öffnen Sie Ihre Authentifizierungs-App und scannen Sie diesen Code.", "scanDescription": "Öffnen Sie Ihre Authentifizierungs-App und scannen Sie diesen Code.",
"qrCodeAlt": "2FA QR-Code", "qrCodeAlt": "2FA QR-Code",
@@ -4756,6 +4789,24 @@
"thursday": "Donnerstag", "thursday": "Donnerstag",
"tuesday": "Dienstag", "tuesday": "Dienstag",
"wednesday": "Mittwoch" "wednesday": "Mittwoch"
},
"storageRetention": {
"title": "Aufbewahrung im Speicher",
"keepAll": "Alle Sicherungen behalten",
"description": "In Proxmox gespeicherte Standardaufbewahrung für Sicherungen. Sicherungsaufträge mit einer eigenen Richtlinie überschreiben diesen Standard.",
"refresh": "Aktualisieren",
"saved": "Aufbewahrungsrichtlinie des Speichers gespeichert. Es wurde keine Bereinigung ausgeführt.",
"choosePolicy": "Wählen Sie Alle behalten oder mindestens eine Aufbewahrungsanzahl größer als null.",
"saveFailed": "Aufbewahrungsrichtlinie des Speichers konnte nicht gespeichert werden. Aktualisieren Sie die Liste, falls die Konfiguration geändert wurde.",
"loadFailed": "Aufbewahrungsrichtlinie des Speichers konnte nicht geladen werden.",
"empty": "Keine Sicherungsspeicher gefunden.",
"default": "Proxmox-Standard",
"edit": "Bearbeiten",
"editTitle": "Aufbewahrung für {storage}",
"effect": "Gilt für zukünftige Sicherungsläufe, die diese Speicherrichtlinie übernehmen. Das Speichern löscht keine vorhandenen Sicherungen. Geschützte Sicherungen bleiben geschützt.",
"cancel": "Abbrechen",
"save": "Aufbewahrung speichern",
"useDefault": "Proxmox-Standard verwenden (keine Richtlinie für diesen Speicher)"
} }
}, },
"healthStatus": { "healthStatus": {
@@ -4786,7 +4837,8 @@
"services": "PVE-Dienste", "services": "PVE-Dienste",
"logs": "Systemprotokolle", "logs": "Systemprotokolle",
"updates": "Systemaktualisierungen", "updates": "Systemaktualisierungen",
"security": "Sicherheit & Zertifikate" "security": "Sicherheit & Zertifikate",
"pbsServices": "PBS-Dienste"
}, },
"checks": { "checks": {
"cpu_usage": "CPU-Auslastung", "cpu_usage": "CPU-Auslastung",
@@ -7230,5 +7282,99 @@
"diagnosticsOmitted": "Weitere Diagnosezeilen oder Text ausgelassen: {count}. Originalbericht bleibt erhalten." "diagnosticsOmitted": "Weitere Diagnosezeilen oder Text ausgelassen: {count}. Originalbericht bleibt erhalten."
} }
} }
},
"pbs": {
"title": "Proxmox Backup Server",
"server": "PBS-Server",
"chooseServer": "PBS-Server auswählen",
"local": "Lokaler PBS",
"refresh": "Aktualisieren",
"manage": "PBS-Verbindungen verwalten",
"loadFailed": "PBS-Status konnte nicht geladen werden.",
"loading": "PBS-Daten werden geladen…",
"noServer": "Fügen Sie eine PBS-Verbindung hinzu, um Datenspeicher, Aufgaben und Zeitpläne anzuzeigen.",
"permissionsHelp": "Aktivieren Sie die Monitor-Authentifizierung, bevor Sie Zugangsdaten speichern. Verwenden Sie ein eigenes PBS-API-Token mit der Rolle Audit auf / sowohl für den Benutzer als auch für die Token-ACLs, jeweils mit Vererbung. Dieses Panel liest PBS-Daten ausschließlich.",
"editConnection": "Zu bearbeitende Verbindung",
"add": "Neue Verbindung hinzufügen",
"name": "Name",
"url": "HTTPS-URL (für den nativen PBS-Port :8007 angeben)",
"token_id": "API-Token-ID (user@realm!token)",
"token_secret": "API-Token-Geheimnis (leer lassen, um das gespeicherte Geheimnis beizubehalten)",
"fingerprint": "SHA256-Zertifikatfingerabdruck (optional)",
"tlsHelp": "Lassen Sie den Fingerabdruck für die normale Prüfung von Zertifizierungsstelle und Hostname leer. Kopieren Sie bei einem selbstsignierten Zertifikat dessen SHA256-Fingerabdruck aus der PBS-Konsole oder einem anderen vertrauenswürdigen Kanal. Der Fingerabdruck wird vor der Übermittlung von Zugangsdaten geprüft.",
"saved": "PBS-Verbindung geprüft und gespeichert.",
"saveFailed": "PBS-Verbindung konnte nicht gespeichert werden.",
"testing": "Verbindung wird geprüft…",
"save": "Prüfen und speichern",
"remove": "Verbindung entfernen",
"removeConfirm": "Diese gespeicherte PBS-Verbindung und ihr Token aus Monitor entfernen? PBS-Daten und -Aufträge bleiben unverändert.",
"sampled": "Erfasst am",
"datastores": "Datenspeicher",
"total": "Dateisystemkapazität",
"used": "Belegt",
"free": "Verfügbar",
"noDatastores": "Keine zugänglichen Datenspeicher. Prüfen Sie die Datenspeicherkonfiguration und die Berechtigungen des Benutzers und Tokens.",
"tasks": "Letzte Aufgaben",
"noTasks": "Es wurden keine aktuellen Aufgaben zurückgegeben.",
"task": "Aufgabe",
"started": "Gestartet",
"status": "Status",
"running": "Wird ausgeführt",
"jobs": "Wartungszeitpläne",
"job": "Auftrag",
"schedule": "Zeitplan",
"sync": "Synchronisierung",
"verify": "Verifizierung",
"prune": "Bereinigung",
"overview": "Übersicht",
"logs": "Systemprotokolle",
"terminal": "Terminal",
"security": "Sicherheit",
"logout": "Abmelden",
"navigation": "PBS-Navigation",
"cpu": "CPU",
"memory": "Arbeitsspeicher",
"uptime": "Betriebszeit",
"capacityUnavailable": "Kapazität nicht verfügbar.",
"datastoresUnavailable": "{count} nicht verfügbar",
"datastoresOnline": "Alle verfügbar",
"moreDatastores": "+{count} weitere Datenspeicher",
"estimatedFull": "Voraussichtlich voll",
"estimatedFullUnknown": "Noch nicht genügend Daten",
"duration": "Dauer",
"noJobs": "Es sind keine Synchronisierungs-, Überprüfungs- oder Löschzeitpläne konfiguriert."
},
"monitorLinks": {
"groups": {
"cluster": "Cluster",
"standalone": "Eigenständige Knoten",
"pbs": "Proxmox Backup Server"
},
"thisNode": "Dieser Knoten",
"checking": "Knoten werden überprüft…",
"loadFailed": "Die Knotenliste konnte nicht geladen werden.",
"empty": "Noch keine anderen Knoten. Fügen Sie einen hinzu, um seinen Monitor von hier aus zu öffnen.",
"noResponse": "Keine Antwort",
"pbsInterface": "PBS-Schnittstelle",
"openNewTab": "In einem neuen Tab öffnen",
"manage": "Knoten hinzufügen oder verwalten…",
"dialogTitle": "Verknüpfte Knoten",
"dialogDescription": "Clusterknoten und konfigurierte Proxmox Backup Server werden automatisch erkannt. Jeder Monitor behält seine eigene Anmeldung.",
"addTitle": "Einen Knoten hinzufügen",
"name": "Name",
"address": "Adresse überwachen",
"type": "Geben Sie ein",
"add": "Hinzufügen",
"save": "Speichern",
"remove": "Entfernen",
"visible": "Sichtbar",
"autoAddress": "Automatisch",
"detected": "Erkannt",
"added": "Hinzugefügt",
"saveFailed": "Die Knotenliste konnte nicht gespeichert werden.",
"backTo": "Zurück zu {name}",
"cameFromTitle": "Monitorknoten hinzufügen?",
"cameFromBody": "{name} zu den verknüpften Knoten dieses Monitors hinzufügen.",
"notNow": "Nicht jetzt"
} }
} }
File diff suppressed because one or more lines are too long
+153 -7
View File
@@ -1,8 +1,70 @@
{ {
"pbs": {
"title": "Proxmox Backup Server",
"server": "Servidor PBS",
"chooseServer": "Selecciona un servidor PBS",
"local": "PBS local",
"refresh": "Actualizar",
"manage": "Gestionar conexiones PBS",
"loadFailed": "No se pudo consultar el estado de PBS.",
"loading": "Cargando datos de PBS…",
"noServer": "Añade una conexión PBS para ver datastores, tareas y programaciones.",
"permissionsHelp": "Activa la autenticación de Monitor antes de guardar credenciales. Usa un token API dedicado de PBS con el rol Audit en / para el usuario y el token, con propagación. Este panel solo consulta datos de PBS.",
"editConnection": "Conexión que deseas editar",
"add": "Añadir una conexión",
"name": "Nombre",
"url": "URL HTTPS (incluye :8007 para el puerto nativo de PBS)",
"token_id": "ID del token API (usuario@realm!token)",
"token_secret": "Secreto del token API (vacío para conservar el guardado)",
"fingerprint": "Huella SHA256 del certificado (opcional)",
"tlsHelp": "Deja la huella vacía para verificar la CA y el nombre del servidor. Si el certificado es autofirmado, copia su huella SHA256 desde la consola de PBS u otro canal fiable. Se verifica antes de enviar las credenciales.",
"saved": "Conexión PBS verificada y guardada.",
"saveFailed": "No se pudo guardar la conexión PBS.",
"testing": "Verificando conexión…",
"save": "Verificar y guardar",
"remove": "Eliminar conexión",
"removeConfirm": "¿Eliminar de Monitor esta conexión PBS y su token guardado? Los datos y las tareas de PBS no se modifican.",
"sampled": "Consulta realizada a las",
"datastores": "Datastores",
"total": "Capacidad del sistema de archivos",
"used": "Usado",
"free": "Disponible",
"noDatastores": "No hay datastores accesibles. Revisa su configuración y los permisos del usuario y del token.",
"tasks": "Tareas recientes",
"noTasks": "No se han devuelto tareas recientes.",
"task": "Tarea",
"started": "Inicio",
"status": "Estado",
"running": "En ejecución",
"jobs": "Programaciones de mantenimiento",
"job": "Tarea",
"schedule": "Programación",
"sync": "Sincronización",
"verify": "Verificación",
"prune": "Prune",
"overview": "Resumen",
"logs": "Registros del sistema",
"terminal": "Terminal",
"security": "Seguridad",
"logout": "Cerrar sesión",
"navigation": "Navegación de PBS",
"cpu": "CPU",
"memory": "Memoria",
"uptime": "Tiempo de actividad",
"capacityUnavailable": "Capacidad no disponible.",
"datastoresUnavailable": "{count} no disponibles",
"datastoresOnline": "Todos disponibles",
"moreDatastores": "+{count} datastores más",
"estimatedFull": "Lleno previsto",
"estimatedFullUnknown": "Aún no hay datos suficientes",
"duration": "Duración",
"noJobs": "No hay programaciones de sincronización, verificación ni prune."
},
"_meta": "Community translation target. Copy keys from ../en/common.json and translate the values.", "_meta": "Community translation target. Copy keys from ../en/common.json and translate the values.",
"app": { "app": {
"title": "ProxMenux Monitor", "title": "ProxMenux Monitor",
"description": "Panel del sistema Proxmox", "description": "Panel del sistema Proxmox",
"descriptionPbs": "Panel de Proxmox Backup Server",
"loading": "Cargando...", "loading": "Cargando...",
"connecting": "Conexión al ProxMenux Monitor", "connecting": "Conexión al ProxMenux Monitor",
"unknown": "Desconocido", "unknown": "Desconocido",
@@ -140,6 +202,7 @@
"dayMany": "{count} días" "dayMany": "{count} días"
}, },
"proxmoxVersion": "Versión Proxmox:", "proxmoxVersion": "Versión Proxmox:",
"pbsVersion": "Versión PBS:",
"kernel": "Núcleo:", "kernel": "Núcleo:",
"availableUpdates": "Actualizaciones disponibles:", "availableUpdates": "Actualizaciones disponibles:",
"packages": "paquetes", "packages": "paquetes",
@@ -607,7 +670,7 @@
"controllerBusyTime": "Minutos totales que el controlador pasó procesando comandos de E/S. Los valores altos indican una carga de trabajo pesada y sostenida.", "controllerBusyTime": "Minutos totales que el controlador pasó procesando comandos de E/S. Los valores altos indican una carga de trabajo pesada y sostenida.",
"errorLogEntries": "Número de entradas en el registro de información de errores. A menudo incluye artefactos benignos de autoevaluación; verifique con Errores de Medios.", "errorLogEntries": "Número de entradas en el registro de información de errores. A menudo incluye artefactos benignos de autoevaluación; verifique con Errores de Medios.",
"warningTempTime": "Total de minutos transcurridos por encima del umbral de temperatura de advertencia. Provoca una limitación del rendimiento. El cero es ideal.", "warningTempTime": "Total de minutos transcurridos por encima del umbral de temperatura de advertencia. Provoca una limitación del rendimiento. El cero es ideal.",
"criticalTempTime": "Minutos totales pasados ​​por encima del umbral de temperatura crítica. La unidad puede apagarse para evitar daños. Siempre debe ser cero.", "criticalTempTime": "Minutos totales pasados por encima del umbral de temperatura crítica. La unidad puede apagarse para evitar daños. Siempre debe ser cero.",
"warningCompositeTemperatureTime": "Minutos totales que la temperatura compuesta superó el umbral de advertencia.", "warningCompositeTemperatureTime": "Minutos totales que la temperatura compuesta superó el umbral de advertencia.",
"criticalCompositeTemperatureTime": "Minutos totales la temperatura compuesta superó el umbral crítico. Debe ser cero.", "criticalCompositeTemperatureTime": "Minutos totales la temperatura compuesta superó el umbral crítico. Debe ser cero.",
"thermalManagementT1TransCount": "Número de veces que la unidad entró en aceleración térmica ligera (T1). Indica problemas de enfriamiento.", "thermalManagementT1TransCount": "Número de veces que la unidad entró en aceleración térmica ligera (T1). Indica problemas de enfriamiento.",
@@ -1110,7 +1173,21 @@
"available": "Backups disponibles", "available": "Backups disponibles",
"loading": "Cargando backups...", "loading": "Cargando backups...",
"empty": "No se encontraron backups", "empty": "No se encontraron backups",
"emptyHint": "Crea tu primer backup con el botón de arriba" "emptyHint": "Crea tu primer backup con el botón de arriba",
"refresh": "Actualizar",
"refreshFailed": "No se pudieron actualizar los backups. Vuelve a actualizar para confirmar su estado actual.",
"deleteSelected": "Eliminar los seleccionados ({count})",
"deletedCount": "{count} backup(s) eliminado(s).",
"unconfirmedCount": "No se ha confirmado la eliminación de {count} backup(s). Actualiza la lista y comprueba las tareas de Proxmox antes de volver a intentarlo.",
"deleteFailed": "No se pudo confirmar la eliminación. Actualiza la lista y comprueba las tareas de Proxmox antes de volver a intentarlo.",
"selectHint": "Selecciona backups sin protección (hasta 20 por lote)",
"selectBackup": "Seleccionar backup",
"protected": "Protegido",
"confirmTitle": "¿Eliminar {count} backup(s)?",
"confirmDescription": "Los archivos de backup de la VM/CT {vmid} se eliminarán de forma permanente. Los backups protegidos quedan fuera. La VM o el contenedor no se eliminan.",
"deleting": "Esperando a que Proxmox termine de eliminar los archivos seleccionados…",
"cancel": "Cancelar",
"confirmDelete": "Eliminar backups"
}, },
"updatesPanel": { "updatesPanel": {
"pendingTitle": "Actualizaciones de paquetes pendientes", "pendingTitle": "Actualizaciones de paquetes pendientes",
@@ -1646,6 +1723,16 @@
"searchApplications": "Buscar aplicaciones", "searchApplications": "Buscar aplicaciones",
"searchingApplications": "Buscando aplicaciones…", "searchingApplications": "Buscando aplicaciones…",
"noApplicationsDetected": "No se han detectado aplicaciones.", "noApplicationsDetected": "No se han detectado aplicaciones.",
"nativeEvidencePort": "Puerto TCP a la escucha",
"nativeEvidenceFile": "Archivo instalado",
"nativeEvidenceProcess": "Ejecutable en marcha",
"nativeEvidenceDirectory": "Directorio de trabajo",
"nativeEvidenceService": "Definición del servicio",
"nativeScanFailed": "La búsqueda de aplicaciones no ha podido terminar. Lo detectado antes puede estar desactualizado; vuelve a intentarlo.",
"nativeScanPartial": "La búsqueda de aplicaciones está incompleta. Revisa lo que se ha encontrado y vuelve a intentarlo.",
"nativeVersionUnknown": "Versión instalada desconocida",
"nativeEvidenceStale": "Lo detectado puede estar desactualizado o incompleto.",
"nativeRegisterOnly": "Confirma el registro en el editor. Una versión conocida se guarda como valor manual; no se configura ningún actualizador.",
"noNewApplicationsDetected": "No se han detectado aplicaciones nuevas.", "noNewApplicationsDetected": "No se han detectado aplicaciones nuevas.",
"oneNewApplicationDetected": "Se ha detectado una aplicación nueva.", "oneNewApplicationDetected": "Se ha detectado una aplicación nueva.",
"newApplicationsDetected": "Se han detectado {count} aplicaciones nuevas.", "newApplicationsDetected": "Se han detectado {count} aplicaciones nuevas.",
@@ -1814,6 +1901,7 @@
"network": "Interfaces de red", "network": "Interfaces de red",
"vms": "Máquinas virtuales y contenedores", "vms": "Máquinas virtuales y contenedores",
"services": "Servicios PVE", "services": "Servicios PVE",
"pbsServices": "Servicios PBS",
"logs": "Registros del sistema", "logs": "Registros del sistema",
"updates": "Actualizaciones del sistema", "updates": "Actualizaciones del sistema",
"security": "Seguridad y certificados", "security": "Seguridad y certificados",
@@ -2111,7 +2199,13 @@
"inactiveFooter": "Habilite notificaciones para recibir alertas del sistema, de salud y de seguridad a través de los canales configurados.", "inactiveFooter": "Habilite notificaciones para recibir alertas del sistema, de salud y de seguridad a través de los canales configurados.",
"notificationLanguage": "Idioma de notificación", "notificationLanguage": "Idioma de notificación",
"selectNotificationLanguage": "Seleccione el idioma de notificación", "selectNotificationLanguage": "Seleccione el idioma de notificación",
"notificationLanguageHint": "Se utiliza para notificaciones deterministas en tiempo de ejecución incluso cuando la IA está deshabilitada." "notificationLanguageHint": "Se utiliza para notificaciones deterministas en tiempo de ejecución incluso cuando la IA está deshabilitada.",
"appriseServiceHelp": "Configuración de Slack y Matrix",
"appriseSlackSteps": "Crea un webhook entrante en tu aplicación de Slack para el canal de destino y pega su URL HTTPS completa en URL de Apprise. No hace falta convertirla.",
"appriseSlackDocs": "Formatos de URL y configuración de Slack",
"appriseMatrixSteps": "Utiliza una cuenta de notificaciones que pueda publicar en la sala. Sustituye el usuario, la contraseña, el servidor de origen y el ID completo de la sala del ejemplo; codifica para URL los caracteres especiales de las credenciales. El esquema matrixs utiliza HTTPS. Consulta en la documentación la compatibilidad con tokens, SSO y salas cifradas de tu versión de Apprise.",
"appriseMatrixDocs": "Formatos de URL y configuración de Matrix",
"appriseServiceFinish": "Activa Apprise, introduce tu URL, elige las categorías de notificación o los eventos individuales, guarda y utiliza Enviar prueba. Estos ejemplos no cambian tu URL guardada. Este canal de Apprise tiene un único destino; utiliza la API de Apprise para varios servicios a la vez."
}, },
"ai": { "ai": {
"title": "mejora de la IA", "title": "mejora de la IA",
@@ -2579,7 +2673,7 @@
"expires": "Vence", "expires": "Vence",
"selfSignedWarning": "Los certificados autofirmados pueden mostrar una advertencia en el navegador. Esto es normal para las configuraciones locales de Proxmox.", "selfSignedWarning": "Los certificados autofirmados pueden mostrar una advertencia en el navegador. Esto es normal para las configuraciones locales de Proxmox.",
"configuring": "Configurando...", "configuring": "Configurando...",
"useProxmoxCertificate": "Utilice el certificado Proxmox", "useProxmoxCertificate": "Usar certificado de Proxmox",
"noProxmoxCertificate": "No se detectó ningún certificado Proxmox.", "noProxmoxCertificate": "No se detectó ningún certificado Proxmox.",
"useCustomCertificate": "Usar certificado personalizado", "useCustomCertificate": "Usar certificado personalizado",
"customPaths": "Rutas de certificados personalizadas", "customPaths": "Rutas de certificados personalizadas",
@@ -3566,8 +3660,8 @@
"short": { "short": {
"hour": "Pasada 1 hora", "hour": "Pasada 1 hora",
"day": "Pasadas 24h", "day": "Pasadas 24h",
"week": "Pasados ​​los 7 días", "week": "Pasados los 7 días",
"month": "Pasados ​​los 30 días", "month": "Pasados los 30 días",
"year": "Último 1 año" "year": "Último 1 año"
}, },
"last": { "last": {
@@ -4756,6 +4850,24 @@
"thursday": "Jueves", "thursday": "Jueves",
"tuesday": "Martes", "tuesday": "Martes",
"wednesday": "Miércoles" "wednesday": "Miércoles"
},
"storageRetention": {
"useDefault": "Usar el valor por defecto de Proxmox (sin política en este almacenamiento)",
"title": "Retención del almacenamiento",
"keepAll": "Conservar todos los backups",
"description": "Retención por defecto de los backups, guardada en Proxmox. Las tareas de backup con su propia política tienen prioridad sobre ella.",
"refresh": "Actualizar",
"saved": "Retención guardada. No se ha purgado ningún backup.",
"choosePolicy": "Elige Conservar todos o al menos un número de retención mayor que cero.",
"saveFailed": "No se pudo guardar la retención del almacenamiento. Actualiza la lista si ha cambiado la configuración.",
"loadFailed": "No se pudo cargar la retención del almacenamiento.",
"empty": "No hay almacenamientos de backups.",
"default": "Valor por defecto de Proxmox",
"edit": "Editar",
"editTitle": "Retención de {storage}",
"effect": "Se aplica a los próximos backups que hereden la política de este almacenamiento. Guardar no elimina los backups existentes, y los protegidos siguen protegidos.",
"cancel": "Cancelar",
"save": "Guardar retención"
} }
}, },
"healthStatus": { "healthStatus": {
@@ -4783,7 +4895,8 @@
"disks": "E/S de disco y errores", "disks": "E/S de disco y errores",
"network": "Interfaces de red", "network": "Interfaces de red",
"vms": "Máquinas virtuales y contenedores", "vms": "Máquinas virtuales y contenedores",
"services": "servicios PVE", "services": "Servicios PVE",
"pbsServices": "Servicios PBS",
"logs": "Registros del sistema", "logs": "Registros del sistema",
"updates": "Actualizaciones del sistema", "updates": "Actualizaciones del sistema",
"security": "Seguridad y certificados" "security": "Seguridad y certificados"
@@ -7230,5 +7343,38 @@
"diagnosticsOmitted": "Líneas o texto de diagnóstico omitidos: {count}. Se conserva el informe original." "diagnosticsOmitted": "Líneas o texto de diagnóstico omitidos: {count}. Se conserva el informe original."
} }
} }
},
"monitorLinks": {
"thisNode": "Este nodo",
"checking": "Comprobando nodos…",
"loadFailed": "No se pudo cargar la lista de nodos.",
"empty": "Aún no hay otros nodos. Añade uno para abrir su Monitor desde aquí.",
"groups": {
"cluster": "Clúster",
"standalone": "Nodos independientes",
"pbs": "Proxmox Backup Server"
},
"noResponse": "Sin respuesta",
"pbsInterface": "Interfaz de PBS",
"openNewTab": "Abrir en una pestaña nueva",
"manage": "Añadir o gestionar nodos…",
"dialogTitle": "Nodos enlazados",
"dialogDescription": "Los nodos del clúster y los Proxmox Backup Server configurados se detectan automáticamente. Cada Monitor mantiene su propio inicio de sesión.",
"addTitle": "Añadir un nodo",
"name": "Nombre",
"address": "Dirección del Monitor",
"type": "Tipo",
"add": "Añadir",
"save": "Guardar",
"remove": "Eliminar",
"visible": "Visible",
"autoAddress": "Automática",
"detected": "Detectado",
"added": "Añadido",
"saveFailed": "No se pudo guardar la lista de nodos.",
"backTo": "Volver a {name}",
"cameFromTitle": "¿Añadir nodo Monitor?",
"cameFromBody": "Añade {name} a los nodos enlazados de este Monitor.",
"notNow": "Ahora no"
} }
} }
+157 -11
View File
@@ -8,7 +8,8 @@
"unknown": "Inconnu", "unknown": "Inconnu",
"notAvailable": "N/A", "notAvailable": "N/A",
"serverOffline": "Serveur hors ligne", "serverOffline": "Serveur hors ligne",
"supportProject": "Soutenir et contribuer au projet" "supportProject": "Soutenir et contribuer au projet",
"descriptionPbs": "Tableau de bord Proxmox Backup Server"
}, },
"common": { "common": {
"notAvailable": "N/A", "notAvailable": "N/A",
@@ -165,7 +166,8 @@
}, },
"runningCountOne": "{count} en cours d'exécution", "runningCountOne": "{count} en cours d'exécution",
"runningCountFew": "{count} en cours d'exécution", "runningCountFew": "{count} en cours d'exécution",
"runningCountMany": "{count} en cours d'exécution" "runningCountMany": "{count} en cours d'exécution",
"pbsVersion": "Version PBS :"
}, },
"storage": { "storage": {
"loadingTitle": "Chargement des données de stockage...", "loadingTitle": "Chargement des données de stockage...",
@@ -1110,7 +1112,21 @@
"available": "Sauvegardes disponibles", "available": "Sauvegardes disponibles",
"loading": "Chargement des sauvegardes...", "loading": "Chargement des sauvegardes...",
"empty": "Aucune sauvegarde trouvée", "empty": "Aucune sauvegarde trouvée",
"emptyHint": "Créez votre première sauvegarde en utilisant le bouton ci-dessus" "emptyHint": "Créez votre première sauvegarde en utilisant le bouton ci-dessus",
"refresh": "Actualiser",
"refreshFailed": "Impossible d'actualiser les sauvegardes. Actualisez à nouveau pour confirmer leur état actuel.",
"deleteSelected": "Supprimer la sélection ({count})",
"deletedCount": "{count} sauvegarde(s) supprimée(s).",
"unconfirmedCount": "La suppression de {count} sauvegarde(s) n'a pas été confirmée. Actualisez la liste et vérifiez les tâches Proxmox avant de réessayer.",
"deleteFailed": "La suppression n'a pas pu être confirmée. Actualisez la liste et vérifiez les tâches Proxmox avant de réessayer.",
"selectHint": "Sélectionnez les sauvegardes non protégées (jusqu'à 20 par lot)",
"selectBackup": "Sélectionner la sauvegarde",
"protected": "Protégée",
"confirmTitle": "Supprimer {count} sauvegarde(s) ?",
"confirmDescription": "Ces archives de sauvegarde de la VM/CT {vmid} seront définitivement supprimées. Les sauvegardes protégées sont exclues. Cela ne supprime pas l'invité.",
"deleting": "En attente de la fin de la suppression des archives sélectionnées par Proxmox…",
"cancel": "Annuler",
"confirmDelete": "Supprimer les sauvegardes"
}, },
"updatesPanel": { "updatesPanel": {
"pendingTitle": "Mises à jour des packages en attente", "pendingTitle": "Mises à jour des packages en attente",
@@ -1721,7 +1737,17 @@
"portCategoryCustomPlaceholder": "Tapez une catégorie et appuyez sur Entrée (Esc pour annuler)", "portCategoryCustomPlaceholder": "Tapez une catégorie et appuyez sur Entrée (Esc pour annuler)",
"portCustomUrlPlaceholder": "URL personnalisée (par exemple https://vault.example.com) — remplace IP:port", "portCustomUrlPlaceholder": "URL personnalisée (par exemple https://vault.example.com) — remplace IP:port",
"stackMemberTitle": "Fait partie d'une application multi-conteneurs", "stackMemberTitle": "Fait partie d'une application multi-conteneurs",
"stackMemberBody": "Ce conteneur fait partie d'une application dont le conteneur principal est {primary}. L'application, ses liens web et sa version y sont affichés." "stackMemberBody": "Ce conteneur fait partie d'une application dont le conteneur principal est {primary}. L'application, ses liens web et sa version y sont affichés.",
"nativeEvidencePort": "Port TCP en écoute",
"nativeEvidenceFile": "Fichier installé",
"nativeEvidenceProcess": "Exécutable en cours d'exécution",
"nativeEvidenceDirectory": "Répertoire de travail",
"nativeEvidenceService": "Définition du service",
"nativeScanFailed": "La recherche d'applications n'a pas pu se terminer. Les résultats précédents peuvent être obsolètes ; réessayez.",
"nativeScanPartial": "La recherche d'applications est incomplète. Vérifiez les éléments trouvés et réessayez.",
"nativeVersionUnknown": "Version installée inconnue",
"nativeEvidenceStale": "Les éléments trouvés peuvent être obsolètes ou incomplets.",
"nativeRegisterOnly": "Confirmez l'enregistrement dans l'éditeur. Une version connue est enregistrée comme valeur manuelle ; aucun outil de mise à jour n'est configuré."
}, },
"statusFilter": { "statusFilter": {
"ariaLabel": "Filtrer les machines virtuelles et les conteneurs", "ariaLabel": "Filtrer les machines virtuelles et les conteneurs",
@@ -1817,7 +1843,8 @@
"logs": "Journaux système", "logs": "Journaux système",
"updates": "Mises à jour du système", "updates": "Mises à jour du système",
"security": "Sécurité et certificats", "security": "Sécurité et certificats",
"temperature": "Température" "temperature": "Température",
"pbsServices": "Services PBS"
} }
}, },
"remoteStorage": { "remoteStorage": {
@@ -2111,7 +2138,13 @@
"inactiveFooter": "Activez les notifications pour recevoir des alertes système, de santé et de sécurité via les canaux configurés.", "inactiveFooter": "Activez les notifications pour recevoir des alertes système, de santé et de sécurité via les canaux configurés.",
"notificationLanguage": "Langue de notification", "notificationLanguage": "Langue de notification",
"selectNotificationLanguage": "Sélectionnez la langue de notification", "selectNotificationLanguage": "Sélectionnez la langue de notification",
"notificationLanguageHint": "Utilisé pour les notifications d'exécution déterministes, même lorsque l'IA est désactivée." "notificationLanguageHint": "Utilisé pour les notifications d'exécution déterministes, même lorsque l'IA est désactivée.",
"appriseServiceHelp": "Configuration de Slack et Matrix",
"appriseSlackSteps": "Créez un webhook entrant dans votre application Slack pour le canal cible, puis collez son URL HTTPS complète dans le champ URL Apprise. Aucune conversion n'est nécessaire.",
"appriseSlackDocs": "Formats d'URL et configuration de Slack",
"appriseMatrixSteps": "Utilisez un compte de notification autorisé à publier dans le salon. Remplacez l'utilisateur, le mot de passe, le serveur d'origine et l'identifiant complet du salon de l'exemple ; encodez les caractères spéciaux des identifiants pour les URL. Le schéma matrixs utilise HTTPS. Consultez la documentation pour la prise en charge des jetons, du SSO et des salons chiffrés dans votre version d'Apprise.",
"appriseMatrixDocs": "Formats d'URL et configuration de Matrix",
"appriseServiceFinish": "Activez Apprise, saisissez votre URL, choisissez les catégories de notification ou les événements individuels, enregistrez, puis utilisez Envoyer un test. Ces exemples ne modifient pas votre URL enregistrée. Ce canal Apprise possède une destination ; utilisez l'API Apprise pour plusieurs services à la fois."
}, },
"ai": { "ai": {
"title": "Amélioration de l'IA", "title": "Amélioration de l'IA",
@@ -2980,7 +3013,7 @@
"youWillNeed": "Vous aurez besoin de :", "youWillNeed": "Vous aurez besoin de :",
"needApp": "Une application d'authentification telle que Aegis, 1Password, Bitwarden ou Google Authenticator.", "needApp": "Une application d'authentification telle que Aegis, 1Password, Bitwarden ou Google Authenticator.",
"needQrOrKey": "Un code QR ou une clé manuelle de cette configuration.", "needQrOrKey": "Un code QR ou une clé manuelle de cette configuration.",
"needBackupCodes": "Codes de sauvegarde enregistrés dans un endroit sûr.", "needBackupCodes": "Codes de secours enregistrés dans un endroit sûr.",
"starting": "Départ...", "starting": "Départ...",
"startSetup": "Démarrer la configuration", "startSetup": "Démarrer la configuration",
"scanTitle": "Scannez le code QR", "scanTitle": "Scannez le code QR",
@@ -2993,10 +3026,10 @@
"verifying": "Vérification...", "verifying": "Vérification...",
"verifyAndEnable": "Vérifier et activer", "verifyAndEnable": "Vérifier et activer",
"enabledTitle": "2FA est activé.", "enabledTitle": "2FA est activé.",
"enabledDescription": "Enregistrez les codes de sauvegarde avant de fermer cette fenêtre.", "enabledDescription": "Enregistrez les codes de secours avant de fermer cette fenêtre.",
"saveCodesTitle": "Enregistrez vos codes de sauvegarde", "saveCodesTitle": "Enregistrez vos codes de secours",
"saveCodesDescription": "Chaque code peut être utilisé une fois si vous perdez l'accès à votre application d'authentification.", "saveCodesDescription": "Chaque code peut être utilisé une fois si vous perdez l'accès à votre application d'authentification.",
"backupCodes": "Codes de sauvegarde", "backupCodes": "Codes de secours",
"copyAll": "Copier tout", "copyAll": "Copier tout",
"finish": "Finition" "finish": "Finition"
}, },
@@ -4756,6 +4789,24 @@
"thursday": "Jeudi", "thursday": "Jeudi",
"tuesday": "Mardi", "tuesday": "Mardi",
"wednesday": "Mercredi" "wednesday": "Mercredi"
},
"storageRetention": {
"title": "Rétention du stockage",
"keepAll": "Conserver toutes les sauvegardes",
"description": "Rétention par défaut des sauvegardes enregistrée dans Proxmox. Les tâches de sauvegarde avec une politique explicite remplacent cette valeur par défaut.",
"refresh": "Actualiser",
"saved": "Rétention du stockage enregistrée. Aucune purge n'a été exécutée.",
"choosePolicy": "Choisissez Tout conserver ou au moins un nombre de rétention supérieur à zéro.",
"saveFailed": "Impossible d'enregistrer la rétention du stockage. Actualisez la liste si la configuration a changé.",
"loadFailed": "Impossible de charger la rétention du stockage.",
"empty": "Aucun stockage de sauvegarde trouvé.",
"default": "Valeur par défaut de Proxmox",
"edit": "Modifier",
"editTitle": "Rétention de {storage}",
"effect": "S'applique aux futures sauvegardes qui héritent de cette politique de stockage. L'enregistrement ne supprime pas les sauvegardes existantes. Les sauvegardes protégées restent protégées.",
"cancel": "Annuler",
"save": "Enregistrer la rétention",
"useDefault": "Utiliser la valeur par défaut de Proxmox (aucune politique sur ce stockage)"
} }
}, },
"healthStatus": { "healthStatus": {
@@ -4786,7 +4837,8 @@
"services": "Services PVE", "services": "Services PVE",
"logs": "Journaux système", "logs": "Journaux système",
"updates": "Mises à jour du système", "updates": "Mises à jour du système",
"security": "Sécurité et certificats" "security": "Sécurité et certificats",
"pbsServices": "services PBS"
}, },
"checks": { "checks": {
"cpu_usage": "Utilisation du processeur", "cpu_usage": "Utilisation du processeur",
@@ -7230,5 +7282,99 @@
"diagnosticsOmitted": "Lignes ou texte de diagnostic omis : {count}. Rapport original conservé." "diagnosticsOmitted": "Lignes ou texte de diagnostic omis : {count}. Rapport original conservé."
} }
} }
},
"pbs": {
"title": "Proxmox Backup Server",
"server": "Serveur PBS",
"chooseServer": "Choisir un serveur PBS",
"local": "PBS local",
"refresh": "Actualiser",
"manage": "Gérer les connexions PBS",
"loadFailed": "Impossible de charger l'état de PBS.",
"loading": "Chargement des données PBS…",
"noServer": "Ajoutez une connexion PBS pour afficher les magasins de données, les tâches et les planifications.",
"permissionsHelp": "Activez l'authentification Monitor avant d'enregistrer des identifiants. Utilisez un jeton API PBS dédié avec le rôle Audit sur / pour les ACL de l'utilisateur et du jeton, avec propagation. Ce panneau accède aux données PBS uniquement en lecture.",
"editConnection": "Connexion à modifier",
"add": "Ajouter une connexion",
"name": "Nom",
"url": "URL HTTPS (inclure :8007 pour le port natif de PBS)",
"token_id": "Identifiant du jeton API (user@realm!token)",
"token_secret": "Secret du jeton API (laisser vide pour conserver le secret enregistré)",
"fingerprint": "Empreinte SHA256 du certificat (facultative)",
"tlsHelp": "Laissez l'empreinte vide pour la vérification habituelle de l'autorité de certification et du nom d'hôte. Pour un certificat autosigné, copiez son empreinte SHA256 depuis la console PBS ou un autre canal de confiance. L'empreinte est vérifiée avant l'envoi des identifiants.",
"saved": "Connexion PBS vérifiée et enregistrée.",
"saveFailed": "Impossible d'enregistrer la connexion PBS.",
"testing": "Vérification de la connexion…",
"save": "Vérifier et enregistrer",
"remove": "Supprimer la connexion",
"removeConfirm": "Supprimer de Monitor cette connexion PBS enregistrée et son jeton ? Les données et tâches PBS restent inchangées.",
"sampled": "Relevé effectué à",
"datastores": "Magasins de données",
"total": "Capacité du système de fichiers",
"used": "Utilisé",
"free": "Disponible",
"noDatastores": "Aucun magasin de données accessible. Vérifiez leur configuration et les permissions de l'utilisateur et du jeton.",
"tasks": "Tâches récentes",
"noTasks": "Aucune tâche récente n'a été renvoyée.",
"task": "Tâche",
"started": "Démarrée",
"status": "État",
"running": "En cours",
"jobs": "Planifications de maintenance",
"job": "Tâche planifiée",
"schedule": "Planification",
"sync": "Synchronisation",
"verify": "Vérification",
"prune": "Purge",
"overview": "Vue d'ensemble",
"logs": "Journaux système",
"terminal": "Terminal",
"security": "Sécurité",
"logout": "Se déconnecter",
"navigation": "Navigation PBS",
"cpu": "CPU",
"memory": "Mémoire",
"uptime": "Durée de fonctionnement",
"capacityUnavailable": "Capacité non disponible.",
"datastoresUnavailable": "{count} indisponible",
"datastoresOnline": "Tous disponibles",
"moreDatastores": "+{count} banques de données supplémentaires",
"estimatedFull": "Estimé complet",
"estimatedFullUnknown": "Pas encore assez de données",
"duration": "Durée",
"noJobs": "Aucune planification de synchronisation, de vérification ou d'élagage n'est configurée."
},
"monitorLinks": {
"groups": {
"cluster": "Cluster",
"standalone": "nœuds autonomes",
"pbs": "Proxmox Backup Server"
},
"thisNode": "Ce nœud",
"checking": "Vérification des nœuds…",
"loadFailed": "Impossible de charger la liste des nœuds.",
"empty": "Aucun autre nœud pour l'instant. Ajoutez-en un pour ouvrir son Monitor depuis ici.",
"noResponse": "Pas de réponse",
"pbsInterface": "interface PBS",
"openNewTab": "Ouvrir dans un nouvel onglet",
"manage": "Ajouter ou gérer des nœuds…",
"dialogTitle": "nœuds liés",
"dialogDescription": "Les nœuds du cluster et les Proxmox Backup Server configurés sont détectés automatiquement. Chaque Monitor conserve sa propre connexion.",
"addTitle": "ajouter un nœud",
"name": "Nom",
"address": "Adresse du moniteur",
"type": "Type",
"add": "Ajouter",
"save": "Enregistrer",
"remove": "Supprimer",
"visible": "Visible",
"autoAddress": "Automatique",
"detected": "Détecté",
"added": "ajouté",
"saveFailed": "Impossible d'enregistrer la liste des nœuds.",
"backTo": "Retour à {name}",
"cameFromTitle": "Ajouter un nœud Moniteur ?",
"cameFromBody": "Ajoutez {name} aux nœuds liés de ce moniteur.",
"notNow": "Pas maintenant"
} }
} }
+153 -7
View File
@@ -8,7 +8,8 @@
"unknown": "Sconosciuto", "unknown": "Sconosciuto",
"notAvailable": "N/A", "notAvailable": "N/A",
"serverOffline": "Server offline", "serverOffline": "Server offline",
"supportProject": "Sostieni e contribuisci al progetto" "supportProject": "Sostieni e contribuisci al progetto",
"descriptionPbs": "Dashboard di Proxmox Backup Server"
}, },
"common": { "common": {
"notAvailable": "N/A", "notAvailable": "N/A",
@@ -165,7 +166,8 @@
}, },
"runningCountOne": "{count} in esecuzione", "runningCountOne": "{count} in esecuzione",
"runningCountFew": "{count} in esecuzione", "runningCountFew": "{count} in esecuzione",
"runningCountMany": "{count} in esecuzione" "runningCountMany": "{count} in esecuzione",
"pbsVersion": "Versione PBS:"
}, },
"storage": { "storage": {
"loadingTitle": "Caricamento dati storage...", "loadingTitle": "Caricamento dati storage...",
@@ -1110,7 +1112,21 @@
"available": "Backup disponibili", "available": "Backup disponibili",
"loading": "Caricamento backup...", "loading": "Caricamento backup...",
"empty": "Nessun backup trovato", "empty": "Nessun backup trovato",
"emptyHint": "Crea il primo backup con il pulsante in alto" "emptyHint": "Crea il primo backup con il pulsante in alto",
"refresh": "Aggiorna",
"refreshFailed": "Impossibile aggiornare i backup. Aggiorna di nuovo per confermarne lo stato attuale.",
"deleteSelected": "Elimina selezionati ({count})",
"deletedCount": "{count} backup eliminati.",
"unconfirmedCount": "L'eliminazione di {count} backup non è stata confermata. Aggiorna l'elenco e controlla le attività di Proxmox prima di riprovare.",
"deleteFailed": "Impossibile confermare l'eliminazione. Aggiorna l'elenco e controlla le attività di Proxmox prima di riprovare.",
"selectHint": "Seleziona i backup non protetti (fino a 20 per gruppo)",
"selectBackup": "Seleziona backup",
"protected": "Protetto",
"confirmTitle": "Eliminare {count} backup?",
"confirmDescription": "Questi archivi di backup della VM/CT {vmid} verranno eliminati definitivamente. I backup protetti sono esclusi. Questo non elimina l'ospite.",
"deleting": "In attesa che Proxmox termini l'eliminazione degli archivi selezionati…",
"cancel": "Annulla",
"confirmDelete": "Elimina backup"
}, },
"updatesPanel": { "updatesPanel": {
"pendingTitle": "Aggiornamenti pacchetti in sospeso", "pendingTitle": "Aggiornamenti pacchetti in sospeso",
@@ -1721,7 +1737,17 @@
"portCategoryCustomPlaceholder": "Digita una categoria e premi Enter (Esc per annullare)", "portCategoryCustomPlaceholder": "Digita una categoria e premi Enter (Esc per annullare)",
"portCustomUrlPlaceholder": "URL personalizzato (ad es. https://vault.example.com) — sostituisce IP:port", "portCustomUrlPlaceholder": "URL personalizzato (ad es. https://vault.example.com) — sostituisce IP:port",
"stackMemberTitle": "Parte di un'applicazione multi-container", "stackMemberTitle": "Parte di un'applicazione multi-container",
"stackMemberBody": "Questo container supporta un’applicazione il cui container principale è {primary}. L’applicazione, i suoi collegamenti web e la sua versione sono mostrati lì." "stackMemberBody": "Questo container supporta un’applicazione il cui container principale è {primary}. L’applicazione, i suoi collegamenti web e la sua versione sono mostrati lì.",
"nativeEvidencePort": "Porta TCP in ascolto",
"nativeEvidenceFile": "File installato",
"nativeEvidenceProcess": "Eseguibile in esecuzione",
"nativeEvidenceDirectory": "Directory di lavoro",
"nativeEvidenceService": "Definizione del servizio",
"nativeScanFailed": "La ricerca delle applicazioni non è stata completata. I risultati precedenti potrebbero non essere aggiornati; riprova.",
"nativeScanPartial": "La ricerca delle applicazioni è incompleta. Controlla gli elementi trovati e riprova.",
"nativeVersionUnknown": "Versione installata sconosciuta",
"nativeEvidenceStale": "Gli elementi trovati potrebbero non essere aggiornati o essere incompleti.",
"nativeRegisterOnly": "Conferma la registrazione nell'editor. Una versione nota viene salvata come valore manuale; non viene configurato alcun aggiornamento automatico."
}, },
"statusFilter": { "statusFilter": {
"ariaLabel": "Filtra macchine virtuali e container", "ariaLabel": "Filtra macchine virtuali e container",
@@ -1817,7 +1843,8 @@
"logs": "Log di sistema", "logs": "Log di sistema",
"updates": "Aggiornamenti di sistema", "updates": "Aggiornamenti di sistema",
"security": "Sicurezza e certificati", "security": "Sicurezza e certificati",
"temperature": "Temperatura" "temperature": "Temperatura",
"pbsServices": "Servizi PBS"
} }
}, },
"remoteStorage": { "remoteStorage": {
@@ -2111,7 +2138,13 @@
"inactiveFooter": "Abilita le notifiche per ricevere avvisi di sistema, integrità e sicurezza attraverso i canali configurati.", "inactiveFooter": "Abilita le notifiche per ricevere avvisi di sistema, integrità e sicurezza attraverso i canali configurati.",
"notificationLanguage": "Lingua notifiche", "notificationLanguage": "Lingua notifiche",
"selectNotificationLanguage": "Seleziona lingua notifiche", "selectNotificationLanguage": "Seleziona lingua notifiche",
"notificationLanguageHint": "Usata per le notifiche runtime deterministiche anche quando l’IA è disabilitata." "notificationLanguageHint": "Usata per le notifiche runtime deterministiche anche quando l’IA è disabilitata.",
"appriseServiceHelp": "Configurazione di Slack e Matrix",
"appriseSlackSteps": "Crea un webhook in ingresso nell'app Slack per il canale di destinazione, quindi incolla il suo URL HTTPS completo nel campo URL di Apprise. Non è necessaria alcuna conversione.",
"appriseSlackDocs": "Formati URL e configurazione di Slack",
"appriseMatrixSteps": "Usa un account di notifica autorizzato a pubblicare nella stanza. Sostituisci utente, password, homeserver e ID completo della stanza dell'esempio; codifica per URL i caratteri speciali nelle credenziali. Lo schema matrixs usa HTTPS. Consulta la documentazione per il supporto di token, SSO e stanze cifrate nella versione di Apprise installata.",
"appriseMatrixDocs": "Formati URL e configurazione di Matrix",
"appriseServiceFinish": "Abilita Apprise, inserisci il tuo URL, scegli le categorie di notifica o i singoli eventi, salva e poi usa Invia test. Questi esempi non modificano l'URL salvato. Questo canale Apprise ha una sola destinazione; usa l'API Apprise per più servizi contemporaneamente."
}, },
"ai": { "ai": {
"title": "Miglioramento con IA", "title": "Miglioramento con IA",
@@ -4756,6 +4789,24 @@
"thursday": "Giovedì", "thursday": "Giovedì",
"tuesday": "Martedì", "tuesday": "Martedì",
"wednesday": "Mercoledì" "wednesday": "Mercoledì"
},
"storageRetention": {
"title": "Conservazione dell'archiviazione",
"keepAll": "Mantieni tutti i backup",
"description": "Conservazione predefinita dei backup salvata in Proxmox. Le attività di backup con una politica esplicita hanno la precedenza su questo valore.",
"refresh": "Aggiorna",
"saved": "Politica di conservazione dell'archiviazione salvata. Non è stata eseguita alcuna pulizia.",
"choosePolicy": "Scegli Mantieni tutti oppure almeno un numero di conservazione maggiore di zero.",
"saveFailed": "Impossibile salvare la politica di conservazione dell'archiviazione. Aggiorna l'elenco se la configurazione è cambiata.",
"loadFailed": "Impossibile caricare la politica di conservazione dell'archiviazione.",
"empty": "Nessuno spazio di archiviazione dei backup trovato.",
"default": "Valore predefinito di Proxmox",
"edit": "Modifica",
"editTitle": "Conservazione per {storage}",
"effect": "Si applica alle future esecuzioni dei backup che ereditano questa politica di archiviazione. Il salvataggio non elimina i backup esistenti. I backup protetti rimangono protetti.",
"cancel": "Annulla",
"save": "Salva conservazione",
"useDefault": "Usa l'impostazione predefinita di Proxmox (nessuna policy su questo storage)"
} }
}, },
"healthStatus": { "healthStatus": {
@@ -4786,7 +4837,8 @@
"services": "Servizi PVE", "services": "Servizi PVE",
"logs": "Log di sistema", "logs": "Log di sistema",
"updates": "Aggiornamenti di sistema", "updates": "Aggiornamenti di sistema",
"security": "Sicurezza e certificati" "security": "Sicurezza e certificati",
"pbsServices": "servizi PBS"
}, },
"checks": { "checks": {
"cpu_usage": "Utilizzo CPU", "cpu_usage": "Utilizzo CPU",
@@ -7230,5 +7282,99 @@
"diagnosticsOmitted": "Righe o testo diagnostico omessi: {count}. Il report originale è conservato." "diagnosticsOmitted": "Righe o testo diagnostico omessi: {count}. Il report originale è conservato."
} }
} }
},
"pbs": {
"title": "Proxmox Backup Server",
"server": "Server PBS",
"chooseServer": "Scegli un server PBS",
"local": "PBS locale",
"refresh": "Aggiorna",
"manage": "Gestisci le connessioni PBS",
"loadFailed": "Impossibile caricare lo stato di PBS.",
"loading": "Caricamento dei dati PBS…",
"noServer": "Aggiungi una connessione PBS per visualizzare datastore, attività e pianificazioni.",
"permissionsHelp": "Abilita l'autenticazione di Monitor prima di salvare le credenziali. Usa un token API PBS dedicato con il ruolo Audit su / sia nelle ACL dell'utente sia in quelle del token, con propagazione. Questo pannello legge soltanto i dati PBS.",
"editConnection": "Connessione da modificare",
"add": "Aggiungi una nuova connessione",
"name": "Nome",
"url": "URL HTTPS (includere :8007 per la porta nativa di PBS)",
"token_id": "ID del token API (user@realm!token)",
"token_secret": "Segreto del token API (lasciare vuoto per mantenere quello salvato)",
"fingerprint": "Impronta SHA256 del certificato (facoltativa)",
"tlsHelp": "Lascia vuota l'impronta per la normale verifica dell'autorità di certificazione e del nome host. Per un certificato autofirmato, copia la sua impronta SHA256 dalla console PBS o da un altro canale attendibile. L'impronta viene verificata prima dell'invio delle credenziali.",
"saved": "Connessione PBS verificata e salvata.",
"saveFailed": "Impossibile salvare la connessione PBS.",
"testing": "Verifica della connessione…",
"save": "Verifica e salva",
"remove": "Rimuovi connessione",
"removeConfirm": "Rimuovere da Monitor questa connessione PBS salvata e il suo token? I dati e le attività PBS non vengono modificati.",
"sampled": "Rilevato alle",
"datastores": "Datastore",
"total": "Capacità del filesystem",
"used": "Usato",
"free": "Disponibile",
"noDatastores": "Nessun datastore accessibile. Controlla la configurazione dei datastore e i permessi dell'utente e del token.",
"tasks": "Attività recenti",
"noTasks": "Non sono state restituite attività recenti.",
"task": "Attività",
"started": "Avviata",
"status": "Stato",
"running": "In esecuzione",
"jobs": "Pianificazioni di manutenzione",
"job": "Attività pianificata",
"schedule": "Pianificazione",
"sync": "Sincronizzazione",
"verify": "Verifica",
"prune": "Pulizia",
"overview": "Panoramica",
"logs": "Registri di sistema",
"terminal": "Terminale",
"security": "Sicurezza",
"logout": "Disconnetti",
"navigation": "Navigazione PBS",
"cpu": "CPU",
"memory": "Memoria",
"uptime": "Tempo di attività",
"capacityUnavailable": "Capacità non disponibile.",
"datastoresUnavailable": "{count} non disponibile",
"datastoresOnline": "Tutti disponibili",
"moreDatastores": "+{count} altri archivi dati",
"estimatedFull": "Pieno stimato",
"estimatedFullUnknown": "dati non ancora sufficienti",
"duration": "Durata",
"noJobs": "non è configurata alcuna pianificazione di sincronizzazione, verifica o eliminazione."
},
"monitorLinks": {
"groups": {
"cluster": "cluster",
"standalone": "nodi autonomi",
"pbs": "Proxmox Backup Server"
},
"thisNode": "questo nodo",
"checking": "Controllo dei nodi…",
"loadFailed": "impossibile caricare l'elenco dei nodi.",
"empty": "Ancora nessun altro nodo. Aggiungine uno per aprire il suo Monitor da qui.",
"noResponse": "Nessuna risposta",
"pbsInterface": "interfaccia PBS",
"openNewTab": "apri in una nuova scheda",
"manage": "aggiungi o gestisci nodi...",
"dialogTitle": "nodi collegati",
"dialogDescription": "I nodi del cluster e i Proxmox Backup Server configurati vengono rilevati automaticamente. Ogni Monitor mantiene il proprio accesso.",
"addTitle": "aggiungi un nodo",
"name": "Nome",
"address": "monitora l'indirizzo",
"type": "digita",
"add": "aggiungi",
"save": "salva",
"remove": "rimuovere",
"visible": "visibile",
"autoAddress": "automatico",
"detected": "rilevato",
"added": "aggiunto",
"saveFailed": "impossibile salvare l'elenco dei nodi.",
"backTo": "torna a {name}",
"cameFromTitle": "aggiungi il nodo Monitor?",
"cameFromBody": "aggiungi {name} ai nodi collegati di questo monitor.",
"notNow": "Non ora"
} }
} }
+180 -34
View File
@@ -8,7 +8,8 @@
"unknown": "Desconhecido", "unknown": "Desconhecido",
"notAvailable": "N/D", "notAvailable": "N/D",
"serverOffline": "Servidor off-line", "serverOffline": "Servidor off-line",
"supportProject": "Apoie e contribua para o projeto" "supportProject": "Apoie e contribua para o projeto",
"descriptionPbs": "Painel do Proxmox Backup Server"
}, },
"common": { "common": {
"notAvailable": "N/D", "notAvailable": "N/D",
@@ -165,7 +166,8 @@
}, },
"runningCountOne": "{count} em execução", "runningCountOne": "{count} em execução",
"runningCountFew": "{count} em execução", "runningCountFew": "{count} em execução",
"runningCountMany": "{count} em execução" "runningCountMany": "{count} em execução",
"pbsVersion": "Versão PBS:"
}, },
"storage": { "storage": {
"loadingTitle": "Carregando dados de armazenamento...", "loadingTitle": "Carregando dados de armazenamento...",
@@ -353,7 +355,7 @@
"downloadJson": "Baixar JSON", "downloadJson": "Baixar JSON",
"delete": "Excluir", "delete": "Excluir",
"confirmDelete": "Excluir este registro de teste?", "confirmDelete": "Excluir este registro de teste?",
"note": "Os resultados dos testes são armazenados localmente e usados ​​para gerar relatórios SMART detalhados." "note": "Os resultados dos testes são armazenados localmente e usados para gerar relatórios SMART detalhados."
}, },
"smartReport": { "smartReport": {
"title": "Relatório de saúde SMART", "title": "Relatório de saúde SMART",
@@ -595,8 +597,8 @@
"availableSpare": "Porcentagem de blocos NAND sobressalentes restantes para substituição de blocos defeituosos. Um alerta é acionado abaixo do limite.", "availableSpare": "Porcentagem de blocos NAND sobressalentes restantes para substituição de blocos defeituosos. Um alerta é acionado abaixo do limite.",
"availableSpareThreshold": "Mínimo definido pelo fabricante para peças sobressalentes disponíveis. Abaixo disso, a unidade sinaliza um aviso crítico.", "availableSpareThreshold": "Mínimo definido pelo fabricante para peças sobressalentes disponíveis. Abaixo disso, a unidade sinaliza um aviso crítico.",
"percentageUsed": "Impulsione a estimativa da resistência consumida com base nos ciclos de gravação reais versus nominais. 100% significa que o TBW avaliado foi atingido; a unidade pode continuar funcionando além disso.", "percentageUsed": "Impulsione a estimativa da resistência consumida com base nos ciclos de gravação reais versus nominais. 100% significa que o TBW avaliado foi atingido; a unidade pode continuar funcionando além disso.",
"mediaErrors": "Erros de leitura/gravação irrecuperáveis ​​no flash NAND. Qualquer valor diferente de zero indica dano celular permanente. Contagem crescente significa substituição em breve.", "mediaErrors": "Erros de leitura/gravação irrecuperáveis no flash NAND. Qualquer valor diferente de zero indica dano celular permanente. Contagem crescente significa substituição em breve.",
"mediaIntegrityErrors": "Erros irrecuperáveis ​​detectados pelo controlador. Diferente de zero significa risco de corrupção de dados.", "mediaIntegrityErrors": "Erros irrecuperáveis detectados pelo controlador. Diferente de zero significa risco de corrupção de dados.",
"unsafeShutdowns": "Perdas de energia sem descarga ou desligamento adequado. Contagens muito altas correm o risco de corrupção de metadados e problemas de firmware.", "unsafeShutdowns": "Perdas de energia sem descarga ou desligamento adequado. Contagens muito altas correm o risco de corrupção de metadados e problemas de firmware.",
"nvmePowerCycles": "Ciclos totais de ligar/desligar. A ciclagem frequente aumenta o desgaste do conector e do capacitor.", "nvmePowerCycles": "Ciclos totais de ligar/desligar. A ciclagem frequente aumenta o desgaste do conector e do capacitor.",
"nvmePowerOnHours": "Total de horas acumuladas em que o inversor esteve ligado desde a fabricação.", "nvmePowerOnHours": "Total de horas acumuladas em que o inversor esteve ligado desde a fabricação.",
@@ -624,7 +626,7 @@
"reallocatedSectorCount": "Setores defeituosos substituídos por setores sobressalentes do pool de reserva. Uma contagem crescente significa degradação da unidade.", "reallocatedSectorCount": "Setores defeituosos substituídos por setores sobressalentes do pool de reserva. Uma contagem crescente significa degradação da unidade.",
"retiredBlockCount": "Blocos NAND retirados devido a desgaste ou falha (SSD). Semelhante à contagem de setores realocados para HDDs.", "retiredBlockCount": "Blocos NAND retirados devido a desgaste ou falha (SSD). Semelhante à contagem de setores realocados para HDDs.",
"reallocatedEventCount": "Número de operações de remapeamento realizadas. Cada evento significa que um setor defeituoso foi substituído.", "reallocatedEventCount": "Número de operações de remapeamento realizadas. Cada evento significa que um setor defeituoso foi substituído.",
"currentPendingSector": "Setores instáveis ​​esperando para serem remapeados na próxima gravação. Eles podem ser resolvidos ou serem realocados permanentemente.", "currentPendingSector": "Setores instáveis esperando para serem remapeados na próxima gravação. Eles podem ser resolvidos ou serem realocados permanentemente.",
"pendingSectors": "Setores esperando para serem remapeados. Eles podem ser resolvidos ou realocados.", "pendingSectors": "Setores esperando para serem remapeados. Eles podem ser resolvidos ou realocados.",
"offlineUncorrectable": "Setores que falharam durante a verificação offline e não puderam ser corrigidos. Indica possível perda de dados.", "offlineUncorrectable": "Setores que falharam durante a verificação offline e não puderam ser corrigidos. Indica possível perda de dados.",
"offlineUncorrectableSectorCount": "Setores incorrigíveis encontrados durante a verificação em segundo plano. Os dados sobre estes setores são perdidos.", "offlineUncorrectableSectorCount": "Setores incorrigíveis encontrados durante a verificação em segundo plano. Os dados sobre estes setores são perdidos.",
@@ -667,7 +669,7 @@
"endToEndError": "Corrupção de dados detectada entre o cache do controlador e a interface do host. Deve ser sempre zero.", "endToEndError": "Corrupção de dados detectada entre o cache do controlador e a interface do host. Deve ser sempre zero.",
"endToEndErrorDetectionCount": "Número de erros de paridade no caminho de dados. Diferente de zero indica problemas no controlador.", "endToEndErrorDetectionCount": "Número de erros de paridade no caminho de dados. Diferente de zero indica problemas no controlador.",
"wearLevelingCount": "Média de ciclos de apagamento por bloco NAND (SSD). VALOR mais baixo significa mais desgaste consumido.", "wearLevelingCount": "Média de ciclos de apagamento por bloco NAND (SSD). VALOR mais baixo significa mais desgaste consumido.",
"wearRangeDelta": "Diferença entre blocos mais usados ​​e menos usados ​​(SSD). Valores altos indicam desgaste irregular.", "wearRangeDelta": "Diferença entre blocos mais usados e menos usados (SSD). Valores altos indicam desgaste irregular.",
"mediaWearoutIndicator": "Estimativa de vida útil restante do SSD Intel. Começa em 100 e diminui para 0 conforme a resistência é consumida.", "mediaWearoutIndicator": "Estimativa de vida útil restante do SSD Intel. Começa em 100 e diminui para 0 conforme a resistência é consumida.",
"ssdLifeLeft": "Porcentagem estimada de vida útil restante do SSD com base no desgaste da NAND.", "ssdLifeLeft": "Porcentagem estimada de vida útil restante do SSD com base no desgaste da NAND.",
"percentLifetimeRemain": "Porcentagem estimada de vida útil restante. 100 significa novo; 0 significa fim da vida útil nominal.", "percentLifetimeRemain": "Porcentagem estimada de vida útil restante. 100 significa novo; 0 significa fim da vida útil nominal.",
@@ -744,7 +746,7 @@
"nvmeWarningTitle": "Aviso crítico NVMe ativo (0x{value})", "nvmeWarningTitle": "Aviso crítico NVMe ativo (0x{value})",
"nvmeWarningText": "O controlador NVMe gerou um alerta. Faça backup dos dados imediatamente e investigue mais a fundo.", "nvmeWarningText": "O controlador NVMe gerou um alerta. Faça backup dos dados imediatamente e investigue mais a fundo.",
"nvmeMediaTitle": "Erros de mídia NVMe detectados ({count})", "nvmeMediaTitle": "Erros de mídia NVMe detectados ({count})",
"nvmeMediaText": "Erros irrecuperáveis ​​de flash NAND indicam danos físicos. Faça backup dos dados e planeje a substituição.", "nvmeMediaText": "Erros irrecuperáveis de flash NAND indicam danos físicos. Faça backup dos dados e planeje a substituição.",
"unsafeTitle": "Contagem alta de desligamentos inseguros ({count})", "unsafeTitle": "Contagem alta de desligamentos inseguros ({count})",
"unsafeText": "Perdas frequentes de energia aumentam o risco de corrupção do firmware. Garanta energia estável ou use um UPS.", "unsafeText": "Perdas frequentes de energia aumentam o risco de corrupção do firmware. Garanta energia estável ou use um UPS.",
"seagateTitle": "Seagate Raw_Read_Error_Rate - Comportamento normal", "seagateTitle": "Seagate Raw_Read_Error_Rate - Comportamento normal",
@@ -1084,7 +1086,7 @@
"start": "Iniciar", "start": "Iniciar",
"shutdown": "Desligar", "shutdown": "Desligar",
"reboot": "Reiniciar", "reboot": "Reiniciar",
"forceStop": "Forçar parada", "forceStop": "Forçar paragem",
"working": "Processando...", "working": "Processando...",
"startPending": "Iniciando...", "startPending": "Iniciando...",
"shutdownPending": "Desligando...", "shutdownPending": "Desligando...",
@@ -1092,7 +1094,7 @@
"stopPending": "Parando..." "stopPending": "Parando..."
}, },
"confirm": { "confirm": {
"forceStopTitle": "Forçar parada", "forceStopTitle": "Forçar paragem",
"rebootTitle": "Reinício", "rebootTitle": "Reinício",
"forceStopDescription": "Isso ignora a sequência de desligamento do sistema operacional convidado e pode corromper bancos de dados ou sistemas de arquivos em execução. O convidado é morto imediatamente.", "forceStopDescription": "Isso ignora a sequência de desligamento do sistema operacional convidado e pode corromper bancos de dados ou sistemas de arquivos em execução. O convidado é morto imediatamente.",
"rebootDescription": "Isso força uma reinicialização sem esperar que o sistema operacional convidado libere as gravações pendentes. Use um desligamento elegante quando possível.", "rebootDescription": "Isso força uma reinicialização sem esperar que o sistema operacional convidado libere as gravações pendentes. Use um desligamento elegante quando possível.",
@@ -1110,7 +1112,21 @@
"available": "Backups disponíveis", "available": "Backups disponíveis",
"loading": "Carregando backups...", "loading": "Carregando backups...",
"empty": "Nenhum backup encontrado", "empty": "Nenhum backup encontrado",
"emptyHint": "Crie seu primeiro backup usando o botão acima" "emptyHint": "Crie seu primeiro backup usando o botão acima",
"refresh": "Atualizar",
"refreshFailed": "Não foi possível atualizar as cópias de segurança. Atualize novamente para confirmar o seu estado atual.",
"deleteSelected": "Eliminar selecionadas ({count})",
"deletedCount": "{count} cópia(s) de segurança eliminada(s).",
"unconfirmedCount": "A eliminação de {count} cópia(s) de segurança não foi confirmada. Atualize a lista e verifique as tarefas do Proxmox antes de tentar novamente.",
"deleteFailed": "Não foi possível confirmar a eliminação. Atualize a lista e verifique as tarefas do Proxmox antes de tentar novamente.",
"selectHint": "Selecione cópias de segurança sem proteção (até 20 por lote)",
"selectBackup": "Selecionar cópia de segurança",
"protected": "Protegida",
"confirmTitle": "Eliminar {count} cópia(s) de segurança?",
"confirmDescription": "Estes arquivos de cópia de segurança da VM/CT {vmid} serão eliminados permanentemente. As cópias protegidas estão excluídas. Isto não elimina o convidado.",
"deleting": "À espera que o Proxmox termine de eliminar os arquivos selecionados…",
"cancel": "Cancelar",
"confirmDelete": "Eliminar cópias de segurança"
}, },
"updatesPanel": { "updatesPanel": {
"pendingTitle": "Atualizações de pacote pendentes", "pendingTitle": "Atualizações de pacote pendentes",
@@ -1721,7 +1737,17 @@
"portCategoryCustomPlaceholder": "Digite uma categoria e pressione Enter (Esc para cancelar)", "portCategoryCustomPlaceholder": "Digite uma categoria e pressione Enter (Esc para cancelar)",
"portCustomUrlPlaceholder": "URL personalizada (por exemplo, https://vault.example.com) — substitui IP:port", "portCustomUrlPlaceholder": "URL personalizada (por exemplo, https://vault.example.com) — substitui IP:port",
"stackMemberTitle": "Parte de um aplicativo multicontêiner", "stackMemberTitle": "Parte de um aplicativo multicontêiner",
"stackMemberBody": "Este contêiner faz parte de um aplicativo cujo contêiner principal é {primary}. O aplicativo, seus links da web e sua versão são mostrados lá." "stackMemberBody": "Este contêiner faz parte de um aplicativo cujo contêiner principal é {primary}. O aplicativo, seus links da web e sua versão são mostrados lá.",
"nativeEvidencePort": "Porta TCP em escuta",
"nativeEvidenceFile": "Arquivo instalado",
"nativeEvidenceProcess": "Executável em execução",
"nativeEvidenceDirectory": "Diretório de trabalho",
"nativeEvidenceService": "Definição do serviço",
"nativeScanFailed": "A pesquisa de aplicativos não pôde ser concluída. Os resultados anteriores podem estar desatualizados; tente novamente.",
"nativeScanPartial": "A pesquisa de aplicativos está incompleta. Revise os itens encontrados e tente novamente.",
"nativeVersionUnknown": "Versão instalada desconhecida",
"nativeEvidenceStale": "Os itens encontrados podem estar desatualizados ou incompletos.",
"nativeRegisterOnly": "Confirme o registro no editor. Uma versão conhecida é salva como valor manual; nenhum atualizador é configurado."
}, },
"statusFilter": { "statusFilter": {
"ariaLabel": "Filtrar máquinas virtuais e contêineres", "ariaLabel": "Filtrar máquinas virtuais e contêineres",
@@ -1771,7 +1797,7 @@
}, },
"healthMonitor": { "healthMonitor": {
"title": "Monitor de Saúde", "title": "Monitor de Saúde",
"description": "Configure por quanto tempo os alertas dispensados ​​permanecem ocultos para cada categoria. As alterações se aplicam a alertas ignorados existentes e futuros.", "description": "Configure por quanto tempo os alertas dispensados permanecem ocultos para cada categoria. As alterações se aplicam a alertas ignorados existentes e futuros.",
"category": "Categoria", "category": "Categoria",
"suppressionDuration": "Duração da Supressão", "suppressionDuration": "Duração da Supressão",
"hours": "Horas", "hours": "Horas",
@@ -1781,7 +1807,7 @@
"footerNote": "Essas configurações se aplicam quando você descarta um aviso do Health Monitor. Alertas críticos de temperatura da CPU sempre aparecem para proteger seu hardware.", "footerNote": "Essas configurações se aplicam quando você descarta um aviso do Health Monitor. Alertas críticos de temperatura da CPU sempre aparecem para proteger seu hardware.",
"activeSuppressions": "Supressões Ativas", "activeSuppressions": "Supressões Ativas",
"activeSuppressionsDescription": "Alertas que você ocultou do Health Monitor. As demissões permanentes só podem ser revertidas aqui. A edição requer o Health Monitor", "activeSuppressionsDescription": "Alertas que você ocultou do Health Monitor. As demissões permanentes só podem ser revertidas aqui. A edição requer o Health Monitor",
"noActiveSuppressions": "Sem supressões ativas. Os alertas dispensados ​​do Health Monitor aparecerão aqui.", "noActiveSuppressions": "Sem supressões ativas. Os alertas dispensados do Health Monitor aparecerão aqui.",
"permanent": "Permanente", "permanent": "Permanente",
"daysRemaining": "{count}d restantes", "daysRemaining": "{count}d restantes",
"hoursRemaining": "{count}h restantes", "hoursRemaining": "{count}h restantes",
@@ -1817,7 +1843,8 @@
"logs": "Registros do sistema", "logs": "Registros do sistema",
"updates": "Atualizações do sistema", "updates": "Atualizações do sistema",
"security": "Segurança e Certificados", "security": "Segurança e Certificados",
"temperature": "Temperatura" "temperature": "Temperatura",
"pbsServices": "Serviços PBS"
} }
}, },
"remoteStorage": { "remoteStorage": {
@@ -2081,8 +2108,8 @@
"port": "Porta", "port": "Porta",
"tlsMode": "Modo TLS", "tlsMode": "Modo TLS",
"noTls": "Nenhum (porta 25)", "noTls": "Nenhum (porta 25)",
"username": "Nome de usuário", "username": "Nome de utilizador",
"password": "Senha", "password": "Palavra-passe",
"appPassword": "Senha do aplicativo", "appPassword": "Senha do aplicativo",
"fromAddress": "Do endereço", "fromAddress": "Do endereço",
"toAddresses": "Para endereços (separados por vírgula)", "toAddresses": "Para endereços (separados por vírgula)",
@@ -2111,7 +2138,13 @@
"inactiveFooter": "Habilite notificações para receber alertas de sistema, integridade e segurança por meio dos canais configurados.", "inactiveFooter": "Habilite notificações para receber alertas de sistema, integridade e segurança por meio dos canais configurados.",
"notificationLanguage": "Idioma de notificação", "notificationLanguage": "Idioma de notificação",
"selectNotificationLanguage": "Selecione o idioma de notificação", "selectNotificationLanguage": "Selecione o idioma de notificação",
"notificationLanguageHint": "Usado para notificações determinísticas de tempo de execução, mesmo quando a IA está desabilitada." "notificationLanguageHint": "Usado para notificações determinísticas de tempo de execução, mesmo quando a IA está desabilitada.",
"appriseServiceHelp": "Configuração do Slack e do Matrix",
"appriseSlackSteps": "Crie um webhook de entrada na sua aplicação Slack para o canal de destino e cole o seu URL HTTPS completo no campo URL do Apprise. Não é necessária qualquer conversão.",
"appriseSlackDocs": "Formatos de URL e configuração do Slack",
"appriseMatrixSteps": "Utilize uma conta de notificações que possa publicar na sala. Substitua o utilizador, a palavra-passe, o homeserver e o ID completo da sala do exemplo; codifique os caracteres especiais das credenciais para URL. O esquema matrixs usa HTTPS. Consulte a documentação sobre o suporte de tokens, SSO e salas cifradas na versão do Apprise instalada.",
"appriseMatrixDocs": "Formatos de URL e configuração do Matrix",
"appriseServiceFinish": "Ative o Apprise, introduza o seu URL, escolha as categorias de notificação ou os eventos individuais, guarde e utilize Enviar teste. Estes exemplos não alteram o URL guardado. Este canal do Apprise tem um destino; utilize a API do Apprise para vários serviços em simultâneo."
}, },
"ai": { "ai": {
"title": "Aprimoramento de IA", "title": "Aprimoramento de IA",
@@ -2336,8 +2369,8 @@
}, },
"login": { "login": {
"subtitle": "Faça login para acessar seu painel", "subtitle": "Faça login para acessar seu painel",
"username": "Nome de usuário", "username": "Nome de utilizador",
"password": "Senha", "password": "Palavra-passe",
"usernamePlaceholder": "Digite seu nome de usuário", "usernamePlaceholder": "Digite seu nome de usuário",
"passwordPlaceholder": "Digite sua senha", "passwordPlaceholder": "Digite sua senha",
"rememberMe": "Lembrar", "rememberMe": "Lembrar",
@@ -2378,9 +2411,9 @@
"skipFailed": "Falha ao ignorar a autenticação", "skipFailed": "Falha ao ignorar a autenticação",
"savePreferenceFailed": "Falha ao salvar preferência", "savePreferenceFailed": "Falha ao salvar preferência",
"setupFailed": "Falha ao configurar a autenticação", "setupFailed": "Falha ao configurar a autenticação",
"username": "Nome de usuário", "username": "Nome de utilizador",
"usernamePlaceholder": "Digite o nome de usuário", "usernamePlaceholder": "Digite o nome de usuário",
"password": "Senha", "password": "Palavra-passe",
"passwordPlaceholder": "Digite a senha", "passwordPlaceholder": "Digite a senha",
"confirmPassword": "Confirme sua senha", "confirmPassword": "Confirme sua senha",
"confirmPasswordPlaceholder": "Confirme sua senha", "confirmPasswordPlaceholder": "Confirme sua senha",
@@ -2523,9 +2556,9 @@
"enableHint": "Adicione um nome de usuário e uma senha antes de expor este painel fora da sua rede privada.", "enableHint": "Adicione um nome de usuário e uma senha antes de expor este painel fora da sua rede privada.",
"enable": "Habilitar autenticação", "enable": "Habilitar autenticação",
"setupTitle": "Criar login", "setupTitle": "Criar login",
"username": "Nome de usuário", "username": "Nome de utilizador",
"usernamePlaceholder": "administrador", "usernamePlaceholder": "administrador",
"password": "Senha", "password": "Palavra-passe",
"passwordPlaceholder": "Digite a senha", "passwordPlaceholder": "Digite a senha",
"confirmPassword": "Confirme sua senha", "confirmPassword": "Confirme sua senha",
"confirmPasswordPlaceholder": "Digite a senha novamente", "confirmPasswordPlaceholder": "Digite a senha novamente",
@@ -2545,7 +2578,7 @@
"twoFactor": { "twoFactor": {
"title": "Autenticação de dois fatores", "title": "Autenticação de dois fatores",
"hint": "Adicione um código único de um aplicativo autenticador após a senha.", "hint": "Adicione um código único de um aplicativo autenticador após a senha.",
"enable": "Habilitar 2FA", "enable": "Ativar 2FA",
"enabled": "2FA está habilitado.", "enabled": "2FA está habilitado.",
"disable": "Desativar 2FA", "disable": "Desativar 2FA",
"disableTitle": "Desative a autenticação de dois fatores", "disableTitle": "Desative a autenticação de dois fatores",
@@ -3567,7 +3600,7 @@
"hour": "Passada 1h", "hour": "Passada 1h",
"day": "Passadas 24 horas", "day": "Passadas 24 horas",
"week": "Passado 7 dias", "week": "Passado 7 dias",
"month": "Passados ​​30 dias", "month": "Passados 30 dias",
"year": "Último 1 ano" "year": "Último 1 ano"
}, },
"last": { "last": {
@@ -4019,7 +4052,7 @@
}, },
"backends": { "backends": {
"borgDescription": "Repositório Borg para backups de host desduplicados.", "borgDescription": "Repositório Borg para backups de host desduplicados.",
"borgDescriptionTimerOnly": "Os destinos Borg são usados ​​pelos temporizadores do Monitor, não pelos trabalhos PVE vzdump.", "borgDescriptionTimerOnly": "Os destinos Borg são usados pelos temporizadores do Monitor, não pelos trabalhos PVE vzdump.",
"borgShortDescription": "Repositório Borg desduplicado", "borgShortDescription": "Repositório Borg desduplicado",
"local": "Local", "local": "Local",
"localDescription": "Pasta neste host ou em uma unidade USB montada.", "localDescription": "Pasta neste host ou em uma unidade USB montada.",
@@ -4250,7 +4283,7 @@
"onCalendarExpression": "Expressão OnCalendar", "onCalendarExpression": "Expressão OnCalendar",
"ownerLabel": "Proprietário", "ownerLabel": "Proprietário",
"passphrase": "Senha", "passphrase": "Senha",
"password": "Senha", "password": "Palavra-passe",
"pathLabel": "Caminho", "pathLabel": "Caminho",
"pbsRepository": "Repositório PBS", "pbsRepository": "Repositório PBS",
"profileLabel": "Perfil", "profileLabel": "Perfil",
@@ -4283,7 +4316,7 @@
"timeOfDay": "Hora do dia", "timeOfDay": "Hora do dia",
"type": "Tipo", "type": "Tipo",
"used": "Usado", "used": "Usado",
"username": "Nome de usuário", "username": "Nome de utilizador",
"vmsLxcs": "VMs/LXCs", "vmsLxcs": "VMs/LXCs",
"when": "Quando", "when": "Quando",
"whenLabel": "Quando", "whenLabel": "Quando",
@@ -4547,7 +4580,7 @@
"customPickDescriptionMiddle": "através do", "customPickDescriptionMiddle": "através do",
"destructiveTitle": "A restauração completa REMOVERÁ o seguinte (criado após o backup)", "destructiveTitle": "A restauração completa REMOVERÁ o seguinte (criado após o backup)",
"noPathsMatch": "Nenhum caminho corresponde ao filtro.", "noPathsMatch": "Nenhum caminho corresponde ao filtro.",
"noRestorablePaths": "Este backup não contém caminhos restauráveis ​​— apenas a restauração completa é significativa aqui.", "noRestorablePaths": "Este backup não contém caminhos restauráveis — apenas a restauração completa é significativa aqui.",
"olderThanTargetKernel": "é mais antigo que o kernel alvo", "olderThanTargetKernel": "é mais antigo que o kernel alvo",
"operatorConfigDescription": "As seguintes configurações criadas pelo operador serão mescladas no destino automaticamente, sem alterar os padrões vinculados ao kernel. É executado na restauração completa e personalizada.", "operatorConfigDescription": "As seguintes configurações criadas pelo operador serão mescladas no destino automaticamente, sem alterar os padrões vinculados ao kernel. É executado na restauração completa e personalizada.",
"operatorConfigTitle": "Configuração do operador reaplicada por meio de mesclagem independente de kernel", "operatorConfigTitle": "Configuração do operador reaplicada por meio de mesclagem independente de kernel",
@@ -4756,6 +4789,24 @@
"thursday": "Quinta-feira", "thursday": "Quinta-feira",
"tuesday": "Terça-feira", "tuesday": "Terça-feira",
"wednesday": "Quarta-feira" "wednesday": "Quarta-feira"
},
"storageRetention": {
"title": "Retenção do armazenamento",
"keepAll": "Manter todas as cópias de segurança",
"description": "Retenção predefinida de cópias de segurança guardada no Proxmox. As tarefas de cópia com uma política explícita substituem este valor predefinido.",
"refresh": "Atualizar",
"saved": "Retenção do armazenamento guardada. Não foi executada nenhuma limpeza.",
"choosePolicy": "Escolha Manter todas ou pelo menos uma quantidade de retenção superior a zero.",
"saveFailed": "Não foi possível guardar a retenção do armazenamento. Atualize a lista se a configuração tiver mudado.",
"loadFailed": "Não foi possível carregar a retenção do armazenamento.",
"empty": "Não foram encontrados armazenamentos de cópias de segurança.",
"default": "Predefinição do Proxmox",
"edit": "Editar",
"editTitle": "Retenção para {storage}",
"effect": "Aplica-se às futuras cópias de segurança que herdem esta política de armazenamento. Guardar não elimina as cópias existentes. As cópias protegidas permanecem protegidas.",
"cancel": "Cancelar",
"save": "Guardar retenção",
"useDefault": "Use o padrão Proxmox (nenhuma política neste armazenamento)"
} }
}, },
"healthStatus": { "healthStatus": {
@@ -4786,7 +4837,8 @@
"services": "Serviços PVE", "services": "Serviços PVE",
"logs": "Registros do sistema", "logs": "Registros do sistema",
"updates": "Atualizações do sistema", "updates": "Atualizações do sistema",
"security": "Segurança e certificados" "security": "Segurança e certificados",
"pbsServices": "serviços PBS"
}, },
"checks": { "checks": {
"cpu_usage": "Uso da CPU", "cpu_usage": "Uso da CPU",
@@ -4912,7 +4964,7 @@
}, },
"noIssues": "Nenhum problema detectado", "noIssues": "Nenhum problema detectado",
"updateNow": "Atualizar agora", "updateNow": "Atualizar agora",
"dismissedItems": "Itens dispensados ​​({count})", "dismissedItems": "Itens dispensados ({count})",
"permanent": "Permanente", "permanent": "Permanente",
"dismissed": "Dispensado", "dismissed": "Dispensado",
"wasStatus": "era {status}", "wasStatus": "era {status}",
@@ -4949,7 +5001,7 @@
"hint": "PNG, JPEG, WebP ou GIF. Até 2 MB. Use uma imagem quadrada ou corte-a primeiro para obter o melhor resultado no cabeçalho." "hint": "PNG, JPEG, WebP ou GIF. Até 2 MB. Use uma imagem quadrada ou corte-a primeiro para obter o melhor resultado no cabeçalho."
}, },
"username": { "username": {
"label": "Nome de usuário", "label": "Nome de utilizador",
"help": "Este é o nome de login. Para alterá-lo, desative a autenticação e configure-a novamente em Segurança." "help": "Este é o nome de login. Para alterá-lo, desative a autenticação e configure-a novamente em Segurança."
}, },
"displayName": { "displayName": {
@@ -7182,7 +7234,7 @@
"latency": "Latência", "latency": "Latência",
"event": "Evento", "event": "Evento",
"sourceIp": "IP de origem", "sourceIp": "IP de origem",
"username": "Nome de usuário", "username": "Nome de utilizador",
"service": "Serviço", "service": "Serviço",
"jail": "Jail", "jail": "Jail",
"failures": "Falhas", "failures": "Falhas",
@@ -7230,5 +7282,99 @@
"diagnosticsOmitted": "Linhas ou texto de diagnóstico omitidos: {count}. Relatório original preservado." "diagnosticsOmitted": "Linhas ou texto de diagnóstico omitidos: {count}. Relatório original preservado."
} }
} }
},
"pbs": {
"title": "Proxmox Backup Server",
"server": "Servidor PBS",
"chooseServer": "Escolher um servidor PBS",
"local": "PBS local",
"refresh": "Atualizar",
"manage": "Gerir ligações PBS",
"loadFailed": "Não foi possível carregar o estado do PBS.",
"loading": "A carregar dados do PBS…",
"noServer": "Adicione uma ligação PBS para ver os armazenamentos de dados, tarefas e agendamentos.",
"permissionsHelp": "Ative a autenticação do Monitor antes de guardar credenciais. Utilize um token API PBS dedicado com a função Audit em / nas ACL do utilizador e do token, com propagação. Este painel apenas lê dados do PBS.",
"editConnection": "Ligação a editar",
"add": "Adicionar uma nova ligação",
"name": "Nome",
"url": "URL HTTPS (inclua :8007 para a porta nativa do PBS)",
"token_id": "ID do token API (user@realm!token)",
"token_secret": "Segredo do token API (deixe vazio para manter o segredo guardado)",
"fingerprint": "Impressão digital SHA256 do certificado (opcional)",
"tlsHelp": "Deixe a impressão digital vazia para a verificação normal da autoridade de certificação e do nome de anfitrião. Para um certificado autoassinado, copie a sua impressão digital SHA256 da consola PBS ou de outro canal de confiança. A impressão digital é verificada antes do envio de credenciais.",
"saved": "Ligação PBS verificada e guardada.",
"saveFailed": "Não foi possível guardar a ligação PBS.",
"testing": "A verificar a ligação…",
"save": "Verificar e guardar",
"remove": "Remover ligação",
"removeConfirm": "Remover do Monitor esta ligação PBS guardada e o respetivo token? Os dados e as tarefas PBS não são afetados.",
"sampled": "Recolhido em",
"datastores": "Armazenamentos de dados",
"total": "Capacidade do sistema de ficheiros",
"used": "Utilizado",
"free": "Disponível",
"noDatastores": "Nenhum armazenamento de dados acessível. Verifique a configuração e as permissões do utilizador e do token.",
"tasks": "Tarefas recentes",
"noTasks": "Não foram devolvidas tarefas recentes.",
"task": "Tarefa",
"started": "Iniciada",
"status": "Estado",
"running": "Em execução",
"jobs": "Agendamentos de manutenção",
"job": "Tarefa agendada",
"schedule": "Agendamento",
"sync": "Sincronização",
"verify": "Verificação",
"prune": "Limpeza",
"overview": "Visão geral",
"logs": "Registos do sistema",
"terminal": "Terminal",
"security": "Segurança",
"logout": "Terminar sessão",
"navigation": "Navegação PBS",
"cpu": "CPU",
"memory": "Memória",
"uptime": "Tempo de atividade",
"capacityUnavailable": "Capacidade não disponível.",
"datastoresUnavailable": "{count} indisponível",
"datastoresOnline": "Todos disponíveis",
"moreDatastores": "+{count} mais armazenamentos de dados",
"estimatedFull": "estimativa completa",
"estimatedFullUnknown": "Not enough data yet",
"duration": "Duração",
"noJobs": "nenhuma programação de sincronização, verificação ou remoção está configurada."
},
"monitorLinks": {
"groups": {
"cluster": "Cluster",
"standalone": "nós autônomos",
"pbs": "Proxmox Backup Server"
},
"thisNode": "Este nó",
"checking": "Verificando nós…",
"loadFailed": "não foi possível carregar a lista de nós.",
"empty": "Ainda não há outros nós. Adicione um para abrir o seu Monitor a partir daqui.",
"noResponse": "Sem resposta",
"pbsInterface": "interface PBS",
"openNewTab": "abrir em uma nova guia",
"manage": "Adicionar ou gerenciar nós…",
"dialogTitle": "nós vinculados",
"dialogDescription": "Os nós do cluster e os Proxmox Backup Server configurados são detectados automaticamente. Cada Monitor mantém o seu próprio login.",
"addTitle": "Adicionar um nó",
"name": "Nome",
"address": "endereço do monitor",
"type": "Tipo",
"add": "Adicionar",
"save": "Salvar",
"remove": "Remover",
"visible": "Visível",
"autoAddress": "Automático",
"detected": "Detectado",
"added": "Adicionado",
"saveFailed": "não foi possível salvar a lista de nós.",
"backTo": "Voltar para {name}",
"cameFromTitle": "Adicionar nó Monitor?",
"cameFromBody": "Adicione {name} aos nós vinculados deste Monitor.",
"notNow": "agora não"
} }
} }
+152 -6
View File
@@ -7,7 +7,8 @@
"unknown": "Neznáme", "unknown": "Neznáme",
"notAvailable": "Nedostupné", "notAvailable": "Nedostupné",
"serverOffline": "Server je offline", "serverOffline": "Server je offline",
"supportProject": "Podporte projekt alebo prispejte vlastnými úpravami" "supportProject": "Podporte projekt alebo prispejte vlastnými úpravami",
"descriptionPbs": "Proxmox Backup Server Dashboard"
}, },
"common": { "common": {
"notAvailable": "Nedostupné", "notAvailable": "Nedostupné",
@@ -164,7 +165,8 @@
"errors": { "errors": {
"serverUnavailableTitle": "Server Monitora nie je dostupný", "serverUnavailableTitle": "Server Monitora nie je dostupný",
"serverUnavailableDescription": "Nedá sa pripojiť k serveru Monitora. Skontrolujte, či služba beží, a skúste to znova." "serverUnavailableDescription": "Nedá sa pripojiť k serveru Monitora. Skontrolujte, či služba beží, a skúste to znova."
} },
"pbsVersion": "Verzia PBS:"
}, },
"storage": { "storage": {
"loadingTitle": "Načítavam úložiská...", "loadingTitle": "Načítavam úložiská...",
@@ -1131,7 +1133,21 @@
"available": "Dostupné zálohy", "available": "Dostupné zálohy",
"loading": "Načítavam zálohy...", "loading": "Načítavam zálohy...",
"empty": "Nenašli sa žiadne zálohy", "empty": "Nenašli sa žiadne zálohy",
"emptyHint": "Prvú zálohu vytvoríte tlačidlom vyššie" "emptyHint": "Prvú zálohu vytvoríte tlačidlom vyššie",
"refresh": "Obnoviť",
"refreshFailed": "Nepodarilo sa obnoviť zálohy. Obnovte ich znova, aby ste potvrdili ich aktuálny stav.",
"deleteSelected": "Odstrániť vybrané ({count})",
"deletedCount": "Odstránené zálohy: {count}.",
"unconfirmedCount": "Odstránenie záloh ({count}) nebolo potvrdené. Pred ďalším pokusom obnovte zoznam a skontrolujte úlohy v Proxmoxe.",
"deleteFailed": "Odstránenie sa nepodarilo potvrdiť. Pred ďalším pokusom obnovte zoznam a skontrolujte úlohy v Proxmoxe.",
"selectHint": "Vyberte nechránené zálohy (najviac 20 v jednej dávke)",
"selectBackup": "Vybrať zálohu",
"protected": "Chránená",
"confirmTitle": "Odstrániť zálohy ({count})?",
"confirmDescription": "Tieto archívy záloh pre VM/CT {vmid} budú natrvalo odstránené. Chránené zálohy sú vylúčené. Hosť sa tým neodstráni.",
"deleting": "Čaká sa, kým Proxmox dokončí odstránenie vybraných archívov…",
"cancel": "Zrušiť",
"confirmDelete": "Odstrániť zálohy"
}, },
"updatesPanel": { "updatesPanel": {
"pendingTitle": "Čakajúce aktualizácie balíkov", "pendingTitle": "Čakajúce aktualizácie balíkov",
@@ -1668,6 +1684,16 @@
"searchApplications": "Hľadať aplikácie", "searchApplications": "Hľadať aplikácie",
"searchingApplications": "Hľadajú sa aplikácie…", "searchingApplications": "Hľadajú sa aplikácie…",
"noApplicationsDetected": "Neboli zistené žiadne aplikácie.", "noApplicationsDetected": "Neboli zistené žiadne aplikácie.",
"nativeEvidencePort": "Otvorený TCP port",
"nativeEvidenceFile": "Nainštalovaný súbor",
"nativeEvidenceProcess": "Spustený program",
"nativeEvidenceDirectory": "Pracovný priečinok",
"nativeEvidenceService": "Definícia služby",
"nativeScanFailed": "Vyhľadávanie aplikácií sa nepodarilo dokončiť. Predchádzajúce dôkazy môžu byť zastarané; skús to znova.",
"nativeScanPartial": "Vyhľadávanie aplikácií je neúplné. Skontroluj dostupné dôkazy a skús to znova.",
"nativeVersionUnknown": "Nainštalovaná verzia nie je známa",
"nativeEvidenceStale": "Dôkazy môžu byť zastarané alebo neúplné.",
"nativeRegisterOnly": "Registráciu potvrď v editore. Známa verzia je ručný údaj; aktualizátor nie je nastavený.",
"noNewApplicationsDetected": "Neboli zistené žiadne nové aplikácie.", "noNewApplicationsDetected": "Neboli zistené žiadne nové aplikácie.",
"oneNewApplicationDetected": "Bola zistená jedna nová aplikácia.", "oneNewApplicationDetected": "Bola zistená jedna nová aplikácia.",
"newApplicationsDetected": "Boli zistené {count} nové aplikácie.", "newApplicationsDetected": "Boli zistené {count} nové aplikácie.",
@@ -1816,7 +1842,8 @@
"logs": "Systémové záznamy", "logs": "Systémové záznamy",
"updates": "Aktualizácie systému", "updates": "Aktualizácie systému",
"security": "Bezpečnosť a certifikáty", "security": "Bezpečnosť a certifikáty",
"temperature": "Teplota" "temperature": "Teplota",
"pbsServices": "Služby PBS"
} }
}, },
"remoteStorage": { "remoteStorage": {
@@ -2110,7 +2137,13 @@
"inactiveFooter": "Zapnite notifikácie a dostávajte upozornenia o systéme, stave a bezpečnosti cez nastavené kanály.", "inactiveFooter": "Zapnite notifikácie a dostávajte upozornenia o systéme, stave a bezpečnosti cez nastavené kanály.",
"notificationLanguage": "Jazyk notifikácií", "notificationLanguage": "Jazyk notifikácií",
"selectNotificationLanguage": "Vyberte jazyk notifikácií", "selectNotificationLanguage": "Vyberte jazyk notifikácií",
"notificationLanguageHint": "Používa sa pre spoľahlivé runtime notifikácie aj vtedy, keď je AI vypnutá." "notificationLanguageHint": "Používa sa pre spoľahlivé runtime notifikácie aj vtedy, keď je AI vypnutá.",
"appriseServiceHelp": "Nastavenie Slack a Matrix",
"appriseSlackSteps": "V aplikácii Slack vytvorte prichádzajúci webhook pre cieľový kanál a vložte jeho úplnú HTTPS URL do poľa URL Apprise. Nie je potrebný žiadny prevod.",
"appriseSlackDocs": "Formáty URL a nastavenie Slack",
"appriseMatrixSteps": "Použite účet pre oznámenia, ktorý môže odosielať správy do miestnosti. Nahraďte vzorového používateľa, heslo, domovský server a úplné ID miestnosti; špeciálne znaky v prihlasovacích údajoch zakódujte pre URL. Schéma matrixs používa HTTPS. Podporu tokenov, SSO a šifrovaných miestností v nainštalovanej verzii Apprise nájdete v dokumentácii.",
"appriseMatrixDocs": "Formáty URL a nastavenie Matrix",
"appriseServiceFinish": "Povoľte Apprise, zadajte svoju URL, vyberte kategórie oznámení alebo jednotlivé udalosti, uložte a potom použite Odoslať test. Tieto príklady nemenia uloženú URL. Tento kanál Apprise má jeden cieľ; pre viac služieb naraz použite API Apprise."
}, },
"ai": { "ai": {
"title": "AI vylepšenie", "title": "AI vylepšenie",
@@ -4755,6 +4788,24 @@
"thursday": "štvrtok", "thursday": "štvrtok",
"tuesday": "utorok", "tuesday": "utorok",
"wednesday": "streda" "wednesday": "streda"
},
"storageRetention": {
"title": "Uchovávanie záloh úložiska",
"keepAll": "Ponechať všetky zálohy",
"description": "Predvolená politika uchovávania záloh uložená v Proxmoxe. Zálohovacie úlohy s vlastnou politikou majú pred týmto predvoleným nastavením prednosť.",
"refresh": "Obnoviť",
"saved": "Politika uchovávania záloh úložiska bola uložená. Prerezávanie sa nevykonalo.",
"choosePolicy": "Vyberte Ponechať všetky alebo aspoň jeden počet uchovávaných záloh väčší ako nula.",
"saveFailed": "Nepodarilo sa uložiť politiku uchovávania záloh úložiska. Ak sa konfigurácia zmenila, obnovte zoznam.",
"loadFailed": "Nepodarilo sa načítať politiku uchovávania záloh úložiska.",
"empty": "Nenašli sa žiadne úložiská záloh.",
"default": "Predvolené nastavenie Proxmoxu",
"edit": "Upraviť",
"editTitle": "Uchovávanie záloh pre {storage}",
"effect": "Platí pre budúce zálohovania, ktoré preberajú túto politiku úložiska. Uložením sa existujúce zálohy neodstránia. Chránené zálohy zostanú chránené.",
"cancel": "Zrušiť",
"save": "Uložiť uchovávanie záloh",
"useDefault": "Použite predvolené nastavenie Proxmox (žiadne pravidlá pre toto úložisko)"
} }
}, },
"healthStatus": { "healthStatus": {
@@ -4785,7 +4836,8 @@
"services": "Služby PVE", "services": "Služby PVE",
"logs": "Systémové záznamy", "logs": "Systémové záznamy",
"updates": "Aktualizácie systému", "updates": "Aktualizácie systému",
"security": "Bezpečnosť a certifikáty" "security": "Bezpečnosť a certifikáty",
"pbsServices": "Služby PBS"
}, },
"checks": { "checks": {
"cpu_usage": "Využitie CPU", "cpu_usage": "Využitie CPU",
@@ -7229,5 +7281,99 @@
"diagnosticsOmitted": "Ďalšie diagnostické riadky alebo text vynechaný: {count}. Pôvodná správa sa zachovala." "diagnosticsOmitted": "Ďalšie diagnostické riadky alebo text vynechaný: {count}. Pôvodná správa sa zachovala."
} }
} }
},
"pbs": {
"title": "Proxmox Backup Server",
"server": "Server PBS",
"chooseServer": "Vybrať server PBS",
"local": "Miestny PBS",
"refresh": "Obnoviť",
"manage": "Spravovať pripojenia PBS",
"loadFailed": "Nepodarilo sa načítať stav PBS.",
"loading": "Načítavajú sa údaje PBS…",
"noServer": "Pridajte pripojenie PBS na zobrazenie dátových úložísk, úloh a plánov.",
"permissionsHelp": "Pred uložením prihlasovacích údajov povoľte overovanie v Monitore. Použite vyhradený API token PBS s rolou Audit na / v ACL používateľa aj tokenu, so zapnutým dedením. Tento panel údaje PBS iba číta.",
"editConnection": "Pripojenie na úpravu",
"add": "Pridať nové pripojenie",
"name": "Názov",
"url": "HTTPS URL (pre pôvodný port PBS uveďte :8007)",
"token_id": "ID API tokenu (user@realm!token)",
"token_secret": "Tajný kľúč API tokenu (ponechajte prázdne, ak chcete zachovať uložený kľúč)",
"fingerprint": "Odtlačok SHA256 certifikátu (voliteľný)",
"tlsHelp": "Pre bežné overenie certifikačnej autority a názvu hostiteľa ponechajte odtlačok prázdny. Pri certifikáte s vlastným podpisom skopírujte jeho odtlačok SHA256 z konzoly PBS alebo iného dôveryhodného kanála. Odtlačok sa kontroluje pred odoslaním prihlasovacích údajov.",
"saved": "Pripojenie PBS bolo overené a uložené.",
"saveFailed": "Nepodarilo sa uložiť pripojenie PBS.",
"testing": "Overuje sa pripojenie…",
"save": "Overiť a uložiť",
"remove": "Odstrániť pripojenie",
"removeConfirm": "Odstrániť z Monitora toto uložené pripojenie PBS a jeho token? Údaje a úlohy PBS zostanú nezmenené.",
"sampled": "Údaje získané",
"datastores": "Dátové úložiská",
"total": "Kapacita súborového systému",
"used": "Použité",
"free": "Dostupné",
"noDatastores": "Žiadne dostupné dátové úložiská. Skontrolujte konfiguráciu úložísk a oprávnenia používateľa a tokenu.",
"tasks": "Nedávne úlohy",
"noTasks": "Neboli vrátené žiadne nedávne úlohy.",
"task": "Úloha",
"started": "Spustené",
"status": "Stav",
"running": "Prebieha",
"jobs": "Plány údržby",
"job": "Plánovaná úloha",
"schedule": "Plán",
"sync": "Synchronizácia",
"verify": "Overenie",
"prune": "Prerezávanie",
"overview": "Prehľad",
"logs": "Systémové záznamy",
"terminal": "Terminál",
"security": "Zabezpečenie",
"logout": "Odhlásiť sa",
"navigation": "Navigácia PBS",
"cpu": "CPU",
"memory": "Pamäť",
"uptime": "Čas prevádzky",
"capacityUnavailable": "Kapacita nie je k dispozícii.",
"datastoresUnavailable": "{count} nie je k dispozícii",
"datastoresOnline": "Všetky dostupné",
"moreDatastores": "+{count} ďalších dátových úložísk",
"estimatedFull": "Odhadovaný plný",
"estimatedFullUnknown": "Zatiaľ nie je dostatok údajov",
"duration": "Trvanie",
"noJobs": "Nie sú nakonfigurované žiadne plány synchronizácie, overovania ani odstraňovania."
},
"monitorLinks": {
"groups": {
"cluster": "Cluster",
"standalone": "Samostatné uzly",
"pbs": "Proxmox Backup Server"
},
"thisNode": "Tento uzol",
"checking": "Kontrolujú sa uzly…",
"loadFailed": "Nepodarilo sa načítať zoznam uzlov.",
"empty": "Zatiaľ žiadne ďalšie uzly. Pridajte jeden, aby ste odtiaľto otvorili jeho Monitor.",
"noResponse": "Žiadna odpoveď",
"pbsInterface": "rozhranie PBS",
"openNewTab": "Otvoriť na novej karte",
"manage": "Pridať alebo spravovať uzly…",
"dialogTitle": "Prepojené uzly",
"dialogDescription": "Uzly klastra a nakonfigurované Proxmox Backup Server sa zisťujú automaticky. Každý Monitor má vlastné prihlásenie.",
"addTitle": "Pridať uzol",
"name": "Meno",
"address": "Adresa monitora",
"type": "Typ",
"add": "Pridať",
"save": "Uložiť",
"remove": "Odstrániť",
"visible": "Viditeľné",
"autoAddress": "Automaticky",
"detected": "Zistené",
"added": "Pridané",
"saveFailed": "Nepodarilo sa uložiť zoznam uzlov.",
"backTo": "Späť na {name}",
"cameFromTitle": "Pridať uzol monitora?",
"cameFromBody": "Pridajte {name} do prepojených uzlov tohto monitora.",
"notNow": "Teraz nie"
} }
} }
+155 -9
View File
@@ -8,7 +8,8 @@
"unknown": "Okänd", "unknown": "Okänd",
"notAvailable": "N/A", "notAvailable": "N/A",
"serverOffline": "Servern är frånkopplad", "serverOffline": "Servern är frånkopplad",
"supportProject": "Stöd projektet och bidra" "supportProject": "Stöd projektet och bidra",
"descriptionPbs": "Proxmox Backup Server Dashboard"
}, },
"common": { "common": {
"notAvailable": "N/A", "notAvailable": "N/A",
@@ -165,7 +166,8 @@
}, },
"runningCountOne": "{count} körs", "runningCountOne": "{count} körs",
"runningCountFew": "{count} körs", "runningCountFew": "{count} körs",
"runningCountMany": "{count} körs" "runningCountMany": "{count} körs",
"pbsVersion": "PBS-version:"
}, },
"storage": { "storage": {
"loadingTitle": "Laddar lagringsdata...", "loadingTitle": "Laddar lagringsdata...",
@@ -1110,7 +1112,21 @@
"available": "Tillgängliga säkerhetskopior", "available": "Tillgängliga säkerhetskopior",
"loading": "Laddar säkerhetskopior...", "loading": "Laddar säkerhetskopior...",
"empty": "Inga säkerhetskopior hittades", "empty": "Inga säkerhetskopior hittades",
"emptyHint": "Skapa din första säkerhetskopia med knappen ovan" "emptyHint": "Skapa din första säkerhetskopia med knappen ovan",
"refresh": "Uppdatera",
"refreshFailed": "Det gick inte att uppdatera säkerhetskopiorna. Uppdatera igen för att bekräfta deras aktuella tillstånd.",
"deleteSelected": "Ta bort markerade ({count})",
"deletedCount": "{count} säkerhetskopior har tagits bort.",
"unconfirmedCount": "Borttagning av {count} säkerhetskopior har inte bekräftats. Uppdatera listan och kontrollera Proxmox uppgifter innan du försöker igen.",
"deleteFailed": "Borttagningen kunde inte bekräftas. Uppdatera listan och kontrollera Proxmox uppgifter innan du försöker igen.",
"selectHint": "Välj oskyddade säkerhetskopior (upp till 20 per omgång)",
"selectBackup": "Välj säkerhetskopia",
"protected": "Skyddad",
"confirmTitle": "Ta bort {count} säkerhetskopior?",
"confirmDescription": "Dessa säkerhetskopieringsarkiv för VM/CT {vmid} kommer att tas bort permanent. Skyddade säkerhetskopior är undantagna. Detta tar inte bort gästen.",
"deleting": "Väntar på att Proxmox ska slutföra borttagningen av de valda arkiven…",
"cancel": "Avbryt",
"confirmDelete": "Ta bort säkerhetskopior"
}, },
"updatesPanel": { "updatesPanel": {
"pendingTitle": "Väntande paketuppdateringar", "pendingTitle": "Väntande paketuppdateringar",
@@ -1721,7 +1737,17 @@
"portCategoryCustomPlaceholder": "Skriv en kategori och tryck på Enter (Esc för att avbryta)", "portCategoryCustomPlaceholder": "Skriv en kategori och tryck på Enter (Esc för att avbryta)",
"portCustomUrlPlaceholder": "Anpassad URL (t.ex. https://vault.example.com) - åsidosätter IP:port", "portCustomUrlPlaceholder": "Anpassad URL (t.ex. https://vault.example.com) - åsidosätter IP:port",
"stackMemberTitle": "En del av en multicontainerapplikation", "stackMemberTitle": "En del av en multicontainerapplikation",
"stackMemberBody": "Den här behållaren stöder en applikation vars huvudbehållare är {primary}. Applikationen, dess webblänkar och dess version visas där." "stackMemberBody": "Den här behållaren stöder en applikation vars huvudbehållare är {primary}. Applikationen, dess webblänkar och dess version visas där.",
"nativeEvidencePort": "Lyssnande TCP-port",
"nativeEvidenceFile": "Installerad fil",
"nativeEvidenceProcess": "Körande programfil",
"nativeEvidenceDirectory": "Arbetskatalog",
"nativeEvidenceService": "Tjänstdefinition",
"nativeScanFailed": "Programsökningen kunde inte slutföras. Tidigare resultat kan vara inaktuella; försök igen.",
"nativeScanPartial": "Programsökningen är ofullständig. Granska det som hittades och försök igen.",
"nativeVersionUnknown": "Installerad version okänd",
"nativeEvidenceStale": "Det som hittades kan vara inaktuellt eller ofullständigt.",
"nativeRegisterOnly": "Bekräfta registreringen i redigeraren. En känd version sparas som ett manuellt värde; ingen uppdatering konfigureras."
}, },
"statusFilter": { "statusFilter": {
"ariaLabel": "Filtrera virtuella maskiner och containrar", "ariaLabel": "Filtrera virtuella maskiner och containrar",
@@ -1817,7 +1843,8 @@
"logs": "Systemloggar", "logs": "Systemloggar",
"updates": "Systemuppdateringar", "updates": "Systemuppdateringar",
"security": "Säkerhet och certifikat", "security": "Säkerhet och certifikat",
"temperature": "Temperatur" "temperature": "Temperatur",
"pbsServices": "PBS Services"
} }
}, },
"remoteStorage": { "remoteStorage": {
@@ -2078,7 +2105,7 @@
"webhookUrl": "Webhook URL", "webhookUrl": "Webhook URL",
"enableEmail": "Aktivera e-post", "enableEmail": "Aktivera e-post",
"smtpHost": "SMTP-värd", "smtpHost": "SMTP-värd",
"port": "Hamn", "port": "Port",
"tlsMode": "TLS-läge", "tlsMode": "TLS-läge",
"noTls": "Ingen (port 25)", "noTls": "Ingen (port 25)",
"username": "Användarnamn", "username": "Användarnamn",
@@ -2111,7 +2138,13 @@
"inactiveFooter": "Aktivera aviseringar för att ta emot system-, hälso- och säkerhetsvarningar via de konfigurerade kanalerna.", "inactiveFooter": "Aktivera aviseringar för att ta emot system-, hälso- och säkerhetsvarningar via de konfigurerade kanalerna.",
"notificationLanguage": "Aviseringsspråk", "notificationLanguage": "Aviseringsspråk",
"selectNotificationLanguage": "Välj språk för meddelanden", "selectNotificationLanguage": "Välj språk för meddelanden",
"notificationLanguageHint": "Används för deterministiska körtidsmeddelanden även när AI är inaktiverat." "notificationLanguageHint": "Används för deterministiska körtidsmeddelanden även när AI är inaktiverat.",
"appriseServiceHelp": "Konfiguration av Slack och Matrix",
"appriseSlackSteps": "Skapa en inkommande webhook i din Slack-app för målkanalen och klistra sedan in dess fullständiga HTTPS-URL i fältet Apprise-URL. Ingen omvandling behövs.",
"appriseSlackDocs": "URL-format och konfiguration för Slack",
"appriseMatrixSteps": "Använd ett aviseringskonto som får skicka meddelanden till rummet. Ersätt exemplets användare, lösenord, hemserver och fullständiga rums-ID; URL-koda specialtecken i inloggningsuppgifterna. Schemat matrixs använder HTTPS. Se dokumentationen för stöd för token, SSO och krypterade rum i din installerade Apprise-version.",
"appriseMatrixDocs": "URL-format och konfiguration för Matrix",
"appriseServiceFinish": "Aktivera Apprise, ange din URL, välj aviseringskategorier eller enskilda händelser, spara och använd sedan Skicka test. Dessa exempel ändrar inte din sparade URL. Denna Apprise-kanal har ett mål; använd Apprise-API för flera tjänster samtidigt."
}, },
"ai": { "ai": {
"title": "AI-förbättring", "title": "AI-förbättring",
@@ -2690,7 +2723,7 @@
"interfaceOptional": "Gränssnitt (valfritt)", "interfaceOptional": "Gränssnitt (valfritt)",
"applyTo": "Ansök till", "applyTo": "Ansök till",
"commentOptional": "Kommentar (valfritt)", "commentOptional": "Kommentar (valfritt)",
"port": "Hamn", "port": "Port",
"source": "Källa", "source": "Källa",
"level": "Nivå", "level": "Nivå",
"interface": "Gränssnitt", "interface": "Gränssnitt",
@@ -4756,6 +4789,24 @@
"thursday": "torsdag", "thursday": "torsdag",
"tuesday": "tisdag", "tuesday": "tisdag",
"wednesday": "onsdag" "wednesday": "onsdag"
},
"storageRetention": {
"title": "Lagringens bevarandepolicy",
"keepAll": "Behåll alla säkerhetskopior",
"description": "Standardpolicy för bevarande av säkerhetskopior, sparad i Proxmox. Säkerhetskopieringsjobb med en uttrycklig policy åsidosätter denna standard.",
"refresh": "Uppdatera",
"saved": "Lagringens bevarandepolicy har sparats. Ingen gallring har körts.",
"choosePolicy": "Välj Behåll alla eller minst ett bevarandeantal som är större än noll.",
"saveFailed": "Det gick inte att spara lagringens bevarandepolicy. Uppdatera listan om konfigurationen har ändrats.",
"loadFailed": "Det gick inte att läsa in lagringens bevarandepolicy.",
"empty": "Ingen lagring för säkerhetskopior hittades.",
"default": "Proxmox standard",
"edit": "Redigera",
"editTitle": "Bevarande för {storage}",
"effect": "Gäller framtida säkerhetskopieringar som ärver denna lagringspolicy. Att spara tar inte bort befintliga säkerhetskopior. Skyddade säkerhetskopior förblir skyddade.",
"cancel": "Avbryt",
"save": "Spara bevarandepolicy",
"useDefault": "Använd Proxmox-standard (ingen policy för denna lagring)"
} }
}, },
"healthStatus": { "healthStatus": {
@@ -4786,7 +4837,8 @@
"services": "PVE-tjänster", "services": "PVE-tjänster",
"logs": "Systemloggar", "logs": "Systemloggar",
"updates": "Systemuppdateringar", "updates": "Systemuppdateringar",
"security": "Säkerhet & certifikat" "security": "Säkerhet & certifikat",
"pbsServices": "PBS-tjänster"
}, },
"checks": { "checks": {
"cpu_usage": "CPU-användning", "cpu_usage": "CPU-användning",
@@ -7230,5 +7282,99 @@
"diagnosticsOmitted": "Utelämnade diagnosrader eller text: {count}. Originalrapporten bevaras." "diagnosticsOmitted": "Utelämnade diagnosrader eller text: {count}. Originalrapporten bevaras."
} }
} }
},
"pbs": {
"title": "Proxmox Backup Server",
"server": "PBS-server",
"chooseServer": "Välj en PBS-server",
"local": "Lokal PBS",
"refresh": "Uppdatera",
"manage": "Hantera PBS-anslutningar",
"loadFailed": "Det gick inte att läsa in PBS-status.",
"loading": "Läser in PBS-data…",
"noServer": "Lägg till en PBS-anslutning för att visa datalager, uppgifter och scheman.",
"permissionsHelp": "Aktivera Monitor-autentisering innan du sparar inloggningsuppgifter. Använd en särskild PBS-API-token med rollen Audit på / i både användarens och tokenens ACL, med nedärvning. Denna panel läser endast PBS-data.",
"editConnection": "Anslutning att redigera",
"add": "Lägg till en ny anslutning",
"name": "Namn",
"url": "HTTPS-URL (ta med :8007 för PBS ordinarie port)",
"token_id": "API-token-ID (user@realm!token)",
"token_secret": "API-tokenhemlighet (lämna tomt för att behålla den sparade hemligheten)",
"fingerprint": "Certifikatets SHA256-fingeravtryck (valfritt)",
"tlsHelp": "Lämna fingeravtrycket tomt för vanlig kontroll av certifikatutfärdare och värdnamn. För ett självsignerat certifikat kopierar du dess SHA256-fingeravtryck från PBS-konsolen eller en annan betrodd kanal. Fingeravtrycket kontrolleras innan inloggningsuppgifter skickas.",
"saved": "PBS-anslutningen har verifierats och sparats.",
"saveFailed": "Det gick inte att spara PBS-anslutningen.",
"testing": "Verifierar anslutningen…",
"save": "Verifiera och spara",
"remove": "Ta bort anslutningen",
"removeConfirm": "Ta bort denna sparade PBS-anslutning och dess token från Monitor? PBS-data och jobb påverkas inte.",
"sampled": "Avläst",
"datastores": "Datalager",
"total": "Filsystemets kapacitet",
"used": "Använt",
"free": "Tillgängligt",
"noDatastores": "Inga tillgängliga datalager. Kontrollera datalagrets konfiguration samt användarens och tokenens behörigheter.",
"tasks": "Senaste uppgifter",
"noTasks": "Inga senaste uppgifter returnerades.",
"task": "Uppgift",
"started": "Startad",
"status": "Status",
"running": "Körs",
"jobs": "Underhållsscheman",
"job": "Jobb",
"schedule": "Schema",
"sync": "Synkronisering",
"verify": "Verifiering",
"prune": "Gallring",
"overview": "Översikt",
"logs": "Systemloggar",
"terminal": "Terminal",
"security": "Säkerhet",
"logout": "Logga ut",
"navigation": "PBS-navigering",
"cpu": "CPU",
"memory": "Minne",
"uptime": "Drifttid",
"capacityUnavailable": "Kapacitet inte tillgänglig.",
"datastoresUnavailable": "{count} inte tillgänglig",
"datastoresOnline": "Alla tillgängliga",
"moreDatastores": "+{count} fler databutiker",
"estimatedFull": "Beräknad full",
"estimatedFullUnknown": "Inte tillräckligt med data ännu",
"duration": "Varaktighet",
"noJobs": "Inga synkroniserings-, verifierings- eller beskärningsscheman är konfigurerade."
},
"monitorLinks": {
"groups": {
"cluster": "Kluster",
"standalone": "Fristående noder",
"pbs": "Proxmox Backup Server"
},
"thisNode": "Denna nod",
"checking": "Kontrollerar noder...",
"loadFailed": "Kunde inte ladda nodlistan.",
"empty": "Inga andra noder än. Lägg till en för att öppna dess Monitor härifrån.",
"noResponse": "Inget svar",
"pbsInterface": "PBS-gränssnitt",
"openNewTab": "Öppna i en ny flik",
"manage": "Lägg till eller hantera noder...",
"dialogTitle": "Länkade noder",
"dialogDescription": "Klusternoder och konfigurerade Proxmox Backup Server identifieras automatiskt. Varje Monitor behåller sin egen inloggning.",
"addTitle": "Lägg till en nod",
"name": "Namn",
"address": "Övervaka adress",
"type": "Typ",
"add": "Lägg till",
"save": "Spara",
"remove": "Ta bort",
"visible": "Synlig",
"autoAddress": "Automatisk",
"detected": "Upptäckt",
"added": "Tillagd",
"saveFailed": "Kunde inte spara nodlistan.",
"backTo": "Tillbaka till {name}",
"cameFromTitle": "Lägg till monitornod?",
"cameFromBody": "Lägg till {name} till de länkade noderna på denna monitor.",
"notNow": "Inte nu"
} }
} }
+3
View File
@@ -1115,6 +1115,9 @@ def _detect_proxmox_certificate_paths():
return PROXMOX_CUSTOM_CERT_PATH, PROXMOX_CUSTOM_KEY_PATH return PROXMOX_CUSTOM_CERT_PATH, PROXMOX_CUSTOM_KEY_PATH
if os.path.isfile(PROXMOX_CERT_PATH) and os.path.isfile(PROXMOX_KEY_PATH): if os.path.isfile(PROXMOX_CERT_PATH) and os.path.isfile(PROXMOX_KEY_PATH):
return PROXMOX_CERT_PATH, PROXMOX_KEY_PATH return PROXMOX_CERT_PATH, PROXMOX_KEY_PATH
pbs_cert, pbs_key = "/etc/proxmox-backup/proxy.pem", "/etc/proxmox-backup/proxy.key"
if os.path.isfile(pbs_cert) and os.path.isfile(pbs_key):
return pbs_cert, pbs_key
return "", "" return "", ""
+8
View File
@@ -141,6 +141,7 @@ cp "$SCRIPT_DIR/oci_console_logs.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "
cp "$SCRIPT_DIR/oci_instance_info.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ oci_instance_info.py not found" cp "$SCRIPT_DIR/oci_instance_info.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ oci_instance_info.py not found"
cp "$SCRIPT_DIR/oci_operations.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ oci_operations.py not found" cp "$SCRIPT_DIR/oci_operations.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ oci_operations.py not found"
cp "$SCRIPT_DIR/disk_temperature_history.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ disk_temperature_history.py not found" cp "$SCRIPT_DIR/disk_temperature_history.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ disk_temperature_history.py not found"
cp "$SCRIPT_DIR/lvm_device_scope.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/smartctl_resolver.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ smartctl_resolver.py not found" cp "$SCRIPT_DIR/smartctl_resolver.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ smartctl_resolver.py not found"
cp "$SCRIPT_DIR/disk_identity.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ disk_identity.py not found" cp "$SCRIPT_DIR/disk_identity.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ disk_identity.py not found"
cp "$SCRIPT_DIR/health_thresholds.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ health_thresholds.py not found" cp "$SCRIPT_DIR/health_thresholds.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ health_thresholds.py not found"
@@ -148,6 +149,7 @@ cp "$SCRIPT_DIR/managed_installs.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "
cp "$SCRIPT_DIR/lxc_update_observation.py" "$APP_DIR/usr/bin/" cp "$SCRIPT_DIR/lxc_update_observation.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/lxc_update_finalization.py" "$APP_DIR/usr/bin/" cp "$SCRIPT_DIR/lxc_update_finalization.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/lxc_check_job.py" "$APP_DIR/usr/bin/" cp "$SCRIPT_DIR/lxc_check_job.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/native_app_discovery.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/lxc_apps.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ lxc_apps.py not found" cp "$SCRIPT_DIR/lxc_apps.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ lxc_apps.py not found"
cp "$SCRIPT_DIR/custom_links.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ custom_links.py not found" cp "$SCRIPT_DIR/custom_links.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ custom_links.py not found"
cp "$SCRIPT_DIR/recreate_docker_container.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ recreate_docker_container.py not found" cp "$SCRIPT_DIR/recreate_docker_container.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ recreate_docker_container.py not found"
@@ -160,6 +162,11 @@ cp "$SCRIPT_DIR/flask_terminal_routes.py" "$APP_DIR/usr/bin/" 2>/dev/null || ech
cp "$SCRIPT_DIR/hardware_monitor.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ hardware_monitor.py not found" cp "$SCRIPT_DIR/hardware_monitor.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ hardware_monitor.py not found"
cp "$SCRIPT_DIR/temperature_sensor_resolver.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ temperature_sensor_resolver.py not found" cp "$SCRIPT_DIR/temperature_sensor_resolver.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ temperature_sensor_resolver.py not found"
cp "$SCRIPT_DIR/proxmox_storage_monitor.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ proxmox_storage_monitor.py not found" cp "$SCRIPT_DIR/proxmox_storage_monitor.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ proxmox_storage_monitor.py not found"
cp "$SCRIPT_DIR/pbs_manager.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/flask_pbs_routes.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/monitor_links.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/flask_monitor_links_routes.py" "$APP_DIR/usr/bin/"
cp "$APPIMAGE_ROOT/../scripts/global/proxmox_platform.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/flask_script_runner.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_script_runner.py not found" cp "$SCRIPT_DIR/flask_script_runner.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_script_runner.py not found"
cp "$SCRIPT_DIR/security_manager.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ security_manager.py not found" cp "$SCRIPT_DIR/security_manager.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ security_manager.py not found"
cp "$SCRIPT_DIR/flask_security_routes.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_security_routes.py not found" cp "$SCRIPT_DIR/flask_security_routes.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_security_routes.py not found"
@@ -181,6 +188,7 @@ done
cp "$SCRIPT_DIR/oci_manager.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ oci_manager.py not found" cp "$SCRIPT_DIR/oci_manager.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ oci_manager.py not found"
cp "$SCRIPT_DIR/flask_oci_routes.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_oci_routes.py not found" cp "$SCRIPT_DIR/flask_oci_routes.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_oci_routes.py not found"
cp "$SCRIPT_DIR/flask_vm_backup_routes.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_vm_backup_routes.py not found" cp "$SCRIPT_DIR/flask_vm_backup_routes.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_vm_backup_routes.py not found"
cp "$SCRIPT_DIR/guest_backup_archives.py" "$APP_DIR/usr/bin/"
cp "$SCRIPT_DIR/flask_audit_routes.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_audit_routes.py not found" cp "$SCRIPT_DIR/flask_audit_routes.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ flask_audit_routes.py not found"
cp "$SCRIPT_DIR/audit_store.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ audit_store.py not found" cp "$SCRIPT_DIR/audit_store.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ audit_store.py not found"
cp "$SCRIPT_DIR/audit_checks.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ audit_checks.py not found" cp "$SCRIPT_DIR/audit_checks.py" "$APP_DIR/usr/bin/" 2>/dev/null || echo "⚠️ audit_checks.py not found"
@@ -526,6 +526,16 @@ def disk_read_policy(disk_name: str, excluded: Optional[set] = None) -> str:
return IDLE return IDLE
def settle_own_reads(disk_names) -> None:
"""A reader that has just read these disks itself takes their counters as
the new baseline, so its own I/O never counts as activity."""
stats = _read_diskstats()
with _cache_lock:
for name in disk_names:
if name in _last_io and name in stats:
_last_io[name] = stats[name]
def is_disk_idle(disk_name: str) -> bool: def is_disk_idle(disk_name: str) -> bool:
"""True while the disk is being left alone for having no I/O.""" """True while the disk is being left alone for having no I/O."""
ts = _idle_state.get(disk_name) ts = _idle_state.get(disk_name)
+16 -5
View File
@@ -560,10 +560,12 @@ def totp_setup():
"""Initialize TOTP setup for a user""" """Initialize TOTP setup for a user"""
try: try:
token = request.headers.get('Authorization', '').replace('Bearer ', '') token = request.headers.get('Authorization', '').replace('Bearer ', '')
username = auth_manager.verify_token(token) username, scope = auth_manager.verify_token_full(token)
if not username: if not username:
return jsonify({"success": False, "message": "Unauthorized"}), 401 return jsonify({"success": False, "message": "Unauthorized"}), 401
if scope != 'full_admin':
return jsonify({"success": False, "message": "This action requires a full_admin token"}), 403
success, secret, qr_code, backup_codes, message = auth_manager.setup_totp(username) success, secret, qr_code, backup_codes, message = auth_manager.setup_totp(username)
@@ -586,10 +588,12 @@ def totp_enable():
"""Enable TOTP after verification""" """Enable TOTP after verification"""
try: try:
token = request.headers.get('Authorization', '').replace('Bearer ', '') token = request.headers.get('Authorization', '').replace('Bearer ', '')
username = auth_manager.verify_token(token) username, scope = auth_manager.verify_token_full(token)
if not username: if not username:
return jsonify({"success": False, "message": "Unauthorized"}), 401 return jsonify({"success": False, "message": "Unauthorized"}), 401
if scope != 'full_admin':
return jsonify({"success": False, "message": "This action requires a full_admin token"}), 403
data = request.json data = request.json
verification_token = data.get('token') verification_token = data.get('token')
@@ -612,10 +616,12 @@ def totp_disable():
"""Disable TOTP (requires password confirmation)""" """Disable TOTP (requires password confirmation)"""
try: try:
token = request.headers.get('Authorization', '').replace('Bearer ', '') token = request.headers.get('Authorization', '').replace('Bearer ', '')
username = auth_manager.verify_token(token) username, scope = auth_manager.verify_token_full(token)
if not username: if not username:
return jsonify({"success": False, "message": "Unauthorized"}), 401 return jsonify({"success": False, "message": "Unauthorized"}), 401
if scope != 'full_admin':
return jsonify({"success": False, "message": "This action requires a full_admin token"}), 403
data = request.json or {} data = request.json or {}
password = data.get('password') password = data.get('password')
@@ -653,10 +659,12 @@ def generate_api_token():
if not token: if not token:
return jsonify({"success": False, "message": "Unauthorized. Please log in first."}), 401 return jsonify({"success": False, "message": "Unauthorized. Please log in first."}), 401
username = auth_manager.verify_token(token) username, scope = auth_manager.verify_token_full(token)
if not username: if not username:
return jsonify({"success": False, "message": "Invalid or expired session. Please log in again."}), 401 return jsonify({"success": False, "message": "Invalid or expired session. Please log in again."}), 401
if scope != 'full_admin':
return jsonify({"success": False, "message": "This action requires a full_admin token"}), 403
data = request.json data = request.json
password = data.get('password') password = data.get('password')
@@ -749,8 +757,11 @@ def revoke_api_token_route(token_id):
return jsonify({"success": False, "message": "Authentication is not configured"}), 400 return jsonify({"success": False, "message": "Authentication is not configured"}), 400
token = request.headers.get('Authorization', '').replace('Bearer ', '') token = request.headers.get('Authorization', '').replace('Bearer ', '')
if not token or not auth_manager.verify_token(token): username, scope = auth_manager.verify_token_full(token) if token else (None, None)
if not username:
return jsonify({"success": False, "message": "Unauthorized"}), 401 return jsonify({"success": False, "message": "Unauthorized"}), 401
if scope != 'full_admin':
return jsonify({"success": False, "message": "This action requires a full_admin token"}), 403
success, message = auth_manager.revoke_api_token(token_id) success, message = auth_manager.revoke_api_token(token_id)
@@ -0,0 +1,58 @@
"""Links between ProxMenux Monitors for the node selector in the header."""
from flask import Blueprint, jsonify, request
import monitor_links
from jwt_middleware import require_auth, require_admin_scope
monitor_links_bp = Blueprint("monitor_links", __name__)
@monitor_links_bp.after_request
def no_cache(response):
response.headers["Cache-Control"] = "no-store"
return response
@monitor_links_bp.route("/api/monitor-links")
@require_auth
def list_links():
return jsonify(monitor_links.cached_links(include_hidden=request.args.get("all") == "1"))
@monitor_links_bp.route("/api/monitor-links", methods=["POST"])
@require_admin_scope
def add_link():
try:
key = monitor_links.add_link(request.get_json(silent=True))
monitor_links.refresh()
return jsonify(success=True, key=key)
except monitor_links.LinkError as exc:
return jsonify(error=str(exc)), 400
except OSError:
return jsonify(error="Could not save the node list."), 500
@monitor_links_bp.route("/api/monitor-links/<path:key>", methods=["PATCH"])
@require_admin_scope
def update_link(key):
try:
monitor_links.update_link(key, request.get_json(silent=True))
monitor_links.refresh()
return jsonify(success=True)
except monitor_links.LinkError as exc:
return jsonify(error=str(exc)), 400
except OSError:
return jsonify(error="Could not save the node list."), 500
@monitor_links_bp.route("/api/monitor-links/<path:key>", methods=["DELETE"])
@require_admin_scope
def delete_link(key):
try:
monitor_links.delete_link(key)
monitor_links.refresh()
return jsonify(success=True)
except monitor_links.LinkError as exc:
return jsonify(error=str(exc)), 400
except OSError:
return jsonify(error="Could not save the node list."), 500
@@ -1156,6 +1156,14 @@ def setup_pve_webhook_core() -> dict:
'fallback_commands': [], 'fallback_commands': [],
'error': None, 'error': None,
} }
try: # Proxmox Backup Server has no /etc/pve notification configuration
from pbs_manager import is_pbs_host
on_pbs = is_pbs_host()
except Exception:
on_pbs = False
if on_pbs:
result['not_applicable'] = True
return result
try: try:
if not _pve_cluster_fs_mounted(): if not _pve_cluster_fs_mounted():
@@ -1339,6 +1347,13 @@ def cleanup_pve_webhook_core() -> dict:
Only removes blocks named 'proxmenux-webhook' / 'proxmenux-default'. Only removes blocks named 'proxmenux-webhook' / 'proxmenux-default'.
""" """
result = {'cleaned': False, 'error': None} result = {'cleaned': False, 'error': None}
try: # Proxmox Backup Server has no /etc/pve notification configuration
from pbs_manager import is_pbs_host
on_pbs = is_pbs_host()
except Exception:
on_pbs = False
if on_pbs:
return {'cleaned': True, 'error': None, 'not_applicable': True}
try: try:
if not _pve_cluster_fs_mounted(): if not _pve_cluster_fs_mounted():
+71
View File
@@ -0,0 +1,71 @@
"""Read-only PBS status and administrative connection management."""
from flask import Blueprint, jsonify, request
import auth_manager
import pbs_manager as pbs
from jwt_middleware import require_auth, require_admin_scope
pbs_bp = Blueprint("pbs", __name__)
@pbs_bp.after_request
def no_cache(response):
response.headers["Cache-Control"] = "no-store"
return response
@pbs_bp.route("/api/platform")
@require_auth
def platform():
return jsonify(pbs.get_platform())
@pbs_bp.route("/api/pbs/servers")
@require_auth
def servers():
try:
return jsonify(servers=pbs.public_servers(), local_available=pbs.is_pbs_host())
except pbs.PBSError as exc:
return jsonify(error=str(exc)), 500
def _protected_configuration():
local = auth_manager.load_auth_config()
return local.get("enabled") and not local.get("declined") and local.get("password_hash")
@pbs_bp.route("/api/pbs/servers", methods=["POST"])
@require_admin_scope
def save_server():
if not _protected_configuration():
return jsonify(error="Enable local authentication before saving remote PBS credentials."), 409
try:
identifier = pbs.save_server(request.get_json(silent=True))
return jsonify(success=True, id=identifier, servers=pbs.public_servers())
except pbs.PBSError as exc:
return jsonify(error=str(exc)), 400
except OSError:
return jsonify(error="Could not save PBS connection."), 500
@pbs_bp.route("/api/pbs/servers/<identifier>", methods=["DELETE"])
@require_admin_scope
def delete_server(identifier):
if not _protected_configuration():
return jsonify(error="Enable local authentication before changing PBS connections."), 409
try:
pbs.delete_server(identifier)
return jsonify(success=True)
except pbs.PBSError as exc:
return jsonify(error=str(exc)), 400
except OSError:
return jsonify(error="Could not remove PBS connection."), 500
@pbs_bp.route("/api/pbs/status")
@require_auth
def status():
try:
return jsonify(pbs.get_status(request.args.get("server", "local")))
except pbs.PBSError as exc:
return jsonify(error=str(exc)), 502
+1 -1
View File
@@ -35,7 +35,7 @@ TOOL_METADATA = {
'system_utils': {'name': 'System Utilities', 'function': 'install_system_utils', 'version': '1.0'}, 'system_utils': {'name': 'System Utilities', 'function': 'install_system_utils', 'version': '1.0'},
'network_optimization': {'name': 'Network Optimizations', 'function': 'apply_network_optimizations', 'version': '1.0'}, 'network_optimization': {'name': 'Network Optimizations', 'function': 'apply_network_optimizations', 'version': '1.0'},
'bashrc_custom': {'name': 'Bashrc Customization', 'function': 'customize_bashrc', 'version': '1.0'}, 'bashrc_custom': {'name': 'Bashrc Customization', 'function': 'customize_bashrc', 'version': '1.0'},
'figurine': {'name': 'Figurine', 'function': 'configure_figurine', 'version': '1.0'}, 'figurine': {'name': 'Figurine', 'function': 'configure_figurine', 'version': '1.2'},
'fastfetch': {'name': 'Fastfetch', 'function': 'configure_fastfetch', 'version': '1.0'}, 'fastfetch': {'name': 'Fastfetch', 'function': 'configure_fastfetch', 'version': '1.0'},
'log2ram': {'name': 'Log2ram (SSD Protection)', 'function': 'configure_log2ram', 'version': '1.0'}, 'log2ram': {'name': 'Log2ram (SSD Protection)', 'function': 'configure_log2ram', 'version': '1.0'},
'zfs_autotrim': {'name': 'ZFS Autotrim', 'function': 'enable_zfs_autotrim', 'version': '1.0'}, 'zfs_autotrim': {'name': 'ZFS Autotrim', 'function': 'enable_zfs_autotrim', 'version': '1.0'},
+122 -60
View File
@@ -90,11 +90,16 @@ from flask_terminal_routes import ( # noqa: E402
from flask_health_routes import health_bp # noqa: E402 from flask_health_routes import health_bp # noqa: E402
from flask_auth_routes import auth_bp # noqa: E402 from flask_auth_routes import auth_bp # noqa: E402
from flask_proxmenux_routes import proxmenux_bp # noqa: E402 from flask_proxmenux_routes import proxmenux_bp # noqa: E402
from flask_pbs_routes import pbs_bp # noqa: E402
from flask_monitor_links_routes import monitor_links_bp # noqa: E402
from pbs_manager import get_platform, is_pbs_host # noqa: E402
from flask_security_routes import security_bp # noqa: E402 from flask_security_routes import security_bp # noqa: E402
from flask_notification_routes import notification_bp # noqa: E402 from flask_notification_routes import notification_bp # noqa: E402
from flask_oci_routes import oci_bp # noqa: E402 from flask_oci_routes import oci_bp # noqa: E402
from flask_audit_routes import audit_bp # noqa: E402 from flask_audit_routes import audit_bp # noqa: E402
from flask_vm_backup_routes import vm_backup_bp # noqa: E402 from flask_vm_backup_routes import vm_backup_bp # noqa: E402
from guest_backup_archives import (BackupArchiveError, delete_guest_backups,
guest_backup_deletion_status) # noqa: E402
from notification_manager import notification_manager, resolve_notification_hostname # noqa: E402 from notification_manager import notification_manager, resolve_notification_hostname # noqa: E402
import post_install_versions # noqa: E402 — Sprint 12A: detect post-install function updates import post_install_versions # noqa: E402 — Sprint 12A: detect post-install function updates
from jwt_middleware import require_auth, require_auth_or_ticket, require_admin_scope # noqa: E402 from jwt_middleware import require_auth, require_auth_or_ticket, require_admin_scope # noqa: E402
@@ -193,6 +198,8 @@ post_install_versions.scan_at_startup()
app.register_blueprint(auth_bp) app.register_blueprint(auth_bp)
app.register_blueprint(health_bp) app.register_blueprint(health_bp)
app.register_blueprint(proxmenux_bp) app.register_blueprint(proxmenux_bp)
app.register_blueprint(pbs_bp)
app.register_blueprint(monitor_links_bp)
app.register_blueprint(security_bp) app.register_blueprint(security_bp)
app.register_blueprint(notification_bp) app.register_blueprint(notification_bp)
app.register_blueprint(oci_bp) app.register_blueprint(oci_bp)
@@ -2271,6 +2278,8 @@ def _handle_guest_lifecycle(vmid: str, vm_type: str, action: str) -> None:
# or stops; drop the cached IP so the next /api/vms poll re-reads # or stops; drop the cached IP so the next /api/vms poll re-reads
# it via `lxc-info`. QEMU guests do not touch this cache. # it via `lxc-info`. QEMU guests do not touch this cache.
if guest_type == 'lxc': if guest_type == 'lxc':
import native_app_discovery
native_app_discovery.mark_stale(guest_id)
_invalidate_lxc_ip(guest_id) _invalidate_lxc_ip(guest_id)
if action in ('start', 'reboot'): if action in ('start', 'reboot'):
if guest_type == 'lxc': if guest_type == 'lxc':
@@ -3474,6 +3483,8 @@ def get_coral_info():
def get_proxmox_version(): def get_proxmox_version():
"""Get Proxmox version if available. Cached for 6 hours.""" """Get Proxmox version if available. Cached for 6 hours."""
global _system_info_cache global _system_info_cache
if is_pbs_host():
return get_platform().get('version')
now = time.time() now = time.time()
if _system_info_cache['proxmox_version'] is not None and \ if _system_info_cache['proxmox_version'] is not None and \
@@ -4186,13 +4197,17 @@ def get_system_disks():
pass pass
try: try:
# 4. Check LVM physical volumes. `--devices` (added when a disk # 4. Check LVM physical volumes. `--devices` keeps pvs off the disks
# is parked) keeps pvs from scanning — and waking — standby HDDs. # that are excluded, idle or asleep.
dev_args = _lvm_device_args()
result = subprocess.run( result = subprocess.run(
['pvs', *_lvm_device_args(), '--noheadings', '-o', 'pv_name,vg_name'], ['pvs', *dev_args, '--noheadings', '-o', 'pv_name,vg_name'],
capture_output=True, text=True, timeout=5 capture_output=True, text=True, timeout=5
) ) if dev_args is not None else None
if result.returncode == 0: if result is not None:
from lvm_device_scope import settle_scan_reads
settle_scan_reads()
if result is not None and result.returncode == 0:
for line in result.stdout.strip().split('\n'): for line in result.stdout.strip().split('\n'):
if line.strip(): if line.strip():
parts = line.split() parts = line.split()
@@ -4534,6 +4549,8 @@ def get_storage_info():
disk_info['used'] = round(partition_usage.used / (1024**3), 1) disk_info['used'] = round(partition_usage.used / (1024**3), 1)
disk_info['available'] = round(partition_usage.free / (1024**3), 1) disk_info['available'] = round(partition_usage.free / (1024**3), 1)
disk_info['usage_percent'] = round(partition_usage.percent, 1) disk_info['usage_percent'] = round(partition_usage.percent, 1)
disk_info['total_bytes'] = partition_usage.total
disk_info['used_bytes'] = partition_usage.used
except PermissionError: except PermissionError:
continue continue
@@ -5002,42 +5019,10 @@ def _hdd_in_standby(disk_name: str) -> bool:
return parked return parked
def _lvm_device_args() -> list: def _lvm_device_args():
"""Build the `--devices` argument that keeps an LVM tool (pvs/lvs/ """None means no automatic scan; [] permits LVM's normal scan."""
vgs) off parked disks. from lvm_device_scope import lvm_device_args
return lvm_device_args(_hdd_in_standby)
LVM tools scan every block device by default, which spins up a
standby HDD (issue #232). When any rotational disk is asleep, hand
LVM an explicit list of every block node EXCEPT those belonging to a
parked disk, so it never opens the sleeping drives. Each partition
must be listed too — `--devices /dev/sda` alone does not cover a PV
on /dev/sda3. Returns [] when nothing is parked, so hosts without
standby disks keep the plain pvs/lvs/vgs call unchanged.
"""
try:
r = subprocess.run(
['lsblk', '-rno', 'NAME,TYPE,PKNAME'],
capture_output=True, text=True, timeout=5)
if r.returncode != 0:
return []
nodes = []
for line in r.stdout.strip().split('\n'):
parts = line.split()
if len(parts) < 2 or parts[1] not in ('disk', 'part'):
continue
name = parts[0]
base = name if parts[1] == 'disk' else (parts[2] if len(parts) > 2 else name)
nodes.append((name, base))
standby_bases = {
name for name, base in nodes
if name == base and _hdd_in_standby(name)
}
if not standby_bases:
return []
devices = [f'/dev/{name}' for name, base in nodes if base not in standby_bases]
return ['--devices', ','.join(devices)] if devices else []
except (subprocess.SubprocessError, OSError):
return []
def get_smart_data(disk_name): def get_smart_data(disk_name):
@@ -9863,6 +9848,7 @@ def api_system():
'cpu_cores': cpu_cores, 'cpu_cores': cpu_cores,
'cpu_threads': cpu_threads, 'cpu_threads': cpu_threads,
'proxmox_version': proxmox_version, 'proxmox_version': proxmox_version,
'platform': get_platform(),
'kernel_version': kernel_version, 'kernel_version': kernel_version,
'available_updates': available_updates 'available_updates': available_updates
}) })
@@ -12234,6 +12220,17 @@ def _get_node_metrics_payload(timeframe, max_age=_NODE_METRICS_TTL):
lock.release() lock.release()
def _node_rrd_command(local_node, timeframe, cf=None):
"""Node RRD query; Proxmox Backup Server keeps the same fields under its own API."""
if is_pbs_host():
return ['proxmox-backup-debug', 'api', 'get', '/nodes/localhost/rrd',
'--timeframe', timeframe, '--cf', cf or 'AVERAGE', '--output-format', 'json']
command = ['pvesh', 'get', f'/nodes/{local_node}/rrddata', '--timeframe', timeframe]
if cf:
command += ['--cf', cf]
return command + ['--output-format', 'json']
def _compute_node_metrics_payload(timeframe: str) -> dict: def _compute_node_metrics_payload(timeframe: str) -> dict:
"""Fetch and shape one node RRD snapshot within a bounded query budget.""" """Fetch and shape one node RRD snapshot within a bounded query budget."""
valid_timeframes = ('hour', 'day', 'week', 'month', 'year') valid_timeframes = ('hour', 'day', 'week', 'month', 'year')
@@ -12257,8 +12254,7 @@ def _compute_node_metrics_payload(timeframe: str) -> dict:
try: try:
rrd_result = subprocess.run( rrd_result = subprocess.run(
['pvesh', 'get', f'/nodes/{local_node}/rrddata', _node_rrd_command(local_node, timeframe),
'--timeframe', timeframe, '--output-format', 'json'],
capture_output=True, text=True, timeout=_NODE_METRICS_TIMEOUT, capture_output=True, text=True, timeout=_NODE_METRICS_TIMEOUT,
) )
except subprocess.TimeoutExpired: except subprocess.TimeoutExpired:
@@ -12352,9 +12348,7 @@ def _compute_node_metrics_payload(timeframe: str) -> dict:
return None return None
try: try:
extra = subprocess.run( extra = subprocess.run(
['pvesh', 'get', f'/nodes/{local_node}/rrddata', _node_rrd_command(local_node, timeframe, cf),
'--timeframe', timeframe, '--cf', cf,
'--output-format', 'json'],
capture_output=True, text=True, timeout=remaining, capture_output=True, text=True, timeout=remaining,
) )
if extra.returncode == 0 and extra.stdout: if extra.returncode == 0 and extra.stdout:
@@ -13103,7 +13097,6 @@ def api_create_backup(vmid):
mode = data.get('mode', 'snapshot') # snapshot, suspend, stop mode = data.get('mode', 'snapshot') # snapshot, suspend, stop
compress = data.get('compress', 'zstd') # none, lzo, gzip, zstd compress = data.get('compress', 'zstd') # none, lzo, gzip, zstd
protected = data.get('protected', False) # True/False protected = data.get('protected', False) # True/False
notification = data.get('notification', 'auto') # always, failure, never, auto
notes = data.get('notes', '') # Backup notes/description notes = data.get('notes', '') # Backup notes/description
pbs_change_detection = data.get('pbs_change_detection', None) # default, legacy, data (for PBS + LXC) pbs_change_detection = data.get('pbs_change_detection', None) # default, legacy, data (for PBS + LXC)
@@ -13303,6 +13296,32 @@ def api_create_backup(vmid):
except Exception as e: except Exception as e:
return jsonify({'error': str(e)}), 500 return jsonify({'error': str(e)}), 500
@app.route('/api/vms/<int:vmid>/backups/delete', methods=['POST'])
@require_admin_scope
def api_delete_vm_backups(vmid):
"""Delete an explicitly confirmed selection, revalidated by Proxmox."""
try:
return jsonify(delete_guest_backups(vmid, request.get_json(silent=True)))
except BackupArchiveError as error:
return jsonify({'error': str(error)}), error.code
finally:
# Native deletion may have started even if the pvesh response was lost.
_vm_cache_invalidate(vmid, _vm_backups_cache)
@app.route('/api/vms/<int:vmid>/backups/delete-status', methods=['GET'])
@require_admin_scope
def api_vm_backup_deletion_status(vmid):
"""Only poll deletion tasks started here for this guest."""
try:
result = guest_backup_deletion_status(vmid, request.args.get('task'))
if result['status'] != 'pending':
_vm_cache_invalidate(vmid, _vm_backups_cache)
return jsonify(result)
except BackupArchiveError as error:
return jsonify({'error': str(error)}), error.code
@app.route('/api/vms/<int:vmid>/backups', methods=['GET']) @app.route('/api/vms/<int:vmid>/backups', methods=['GET'])
@require_auth @require_auth
def api_vm_backups(vmid): def api_vm_backups(vmid):
@@ -13383,6 +13402,7 @@ def api_vm_backups(vmid):
'timestamp': ctime, 'timestamp': ctime,
'date': datetime.fromtimestamp(ctime).strftime('%Y-%m-%d %H:%M') if ctime else '', 'date': datetime.fromtimestamp(ctime).strftime('%Y-%m-%d %H:%M') if ctime else '',
'notes': item.get('notes', ''), 'notes': item.get('notes', ''),
'protected': item.get('protected', False) not in (False, 0, '0', '', None),
}) })
except Exception: except Exception:
pass pass
@@ -13595,6 +13615,19 @@ def get_task_log(upid):
traceback.print_exc() traceback.print_exc()
return jsonify({'error': str(e)}), 500 return jsonify({'error': str(e)}), 500
def _installed_proxmenux_version():
"""Installed ProxMenux version; a beta install keeps beta_version.txt."""
for name in ('beta_version.txt', 'version.txt'):
try:
with open(f'/usr/local/share/proxmenux/{name}') as fh:
version = fh.read().strip()
if version:
return version
except OSError:
continue
return ''
@app.route('/api/health', methods=['GET']) @app.route('/api/health', methods=['GET'])
@require_auth @require_auth
def api_health(): def api_health():
@@ -13602,7 +13635,7 @@ def api_health():
return jsonify({ return jsonify({
'status': 'healthy', 'status': 'healthy',
'timestamp': datetime.now().isoformat(), 'timestamp': datetime.now().isoformat(),
'version': '1.2.2' 'version': _installed_proxmenux_version()
}) })
# ─── User-configurable health thresholds ───────────────────────────────────── # ─── User-configurable health thresholds ─────────────────────────────────────
@@ -14083,6 +14116,20 @@ def api_vm_apps_suggestions(vmid):
return jsonify({'error': str(e)}), 500 return jsonify({'error': str(e)}), 500
@app.route('/api/vms/<int:vmid>/apps/native-discovery', methods=['GET', 'POST'])
@require_auth
def api_vm_apps_native_discovery(vmid):
"""Only the Find applications button requests POST. GET never probes."""
import native_app_discovery
import lxc_apps
result = (native_app_discovery.discover(vmid) if request.method == 'POST'
else native_app_discovery.cached(vmid))
suggestions = _vm_cache_get(_vm_app_suggestions_cache, vmid, _VM_APP_SUGGESTIONS_TTL) or {}
sidecar = lxc_apps.load_sidecar(vmid) or {}
return jsonify(native_app_discovery.filter_candidates(
result, suggestions, sidecar.get('apps') or []))
@app.route('/api/vms/<int:vmid>/apps/adguard-setup', methods=['GET']) @app.route('/api/vms/<int:vmid>/apps/adguard-setup', methods=['GET'])
@require_auth @require_auth
def api_vm_apps_adguard_setup(vmid): def api_vm_apps_adguard_setup(vmid):
@@ -15014,11 +15061,11 @@ def api_prometheus():
disk_name = disk.get('name', 'unknown') disk_name = disk.get('name', 'unknown')
metrics.append(f'# HELP proxmox_disk_total_bytes Total disk space in bytes') metrics.append(f'# HELP proxmox_disk_total_bytes Total disk space in bytes')
metrics.append(f'# TYPE proxmox_disk_total_bytes gauge') metrics.append(f'# TYPE proxmox_disk_total_bytes gauge')
metrics.append(f'proxmox_disk_total_bytes{{node="{node}",disk="{disk_name}"}} {disk.get("total", 0)} {timestamp}') metrics.append(f'proxmox_disk_total_bytes{{node="{node}",disk="{disk_name}"}} {disk.get("total_bytes", 0)} {timestamp}')
metrics.append(f'# HELP proxmox_disk_used_bytes Used disk space in bytes') metrics.append(f'# HELP proxmox_disk_used_bytes Used disk space in bytes')
metrics.append(f'# TYPE proxmox_disk_used_bytes gauge') metrics.append(f'# TYPE proxmox_disk_used_bytes gauge')
metrics.append(f'proxmox_disk_used_bytes{{node="{node}",disk="{disk_name}"}} {disk.get("used", 0)} {timestamp}') metrics.append(f'proxmox_disk_used_bytes{{node="{node}",disk="{disk_name}"}} {disk.get("used_bytes", 0)} {timestamp}')
metrics.append(f'# HELP proxmox_disk_usage_percent Disk usage percentage') metrics.append(f'# HELP proxmox_disk_usage_percent Disk usage percentage')
metrics.append(f'# TYPE proxmox_disk_usage_percent gauge') metrics.append(f'# TYPE proxmox_disk_usage_percent gauge')
@@ -23186,9 +23233,11 @@ if __name__ == '__main__':
except Exception as e: except Exception as e:
print(f"[ProxMenux] Disk temperature history init failed: {e}") print(f"[ProxMenux] Disk temperature history init failed: {e}")
try: try:
import managed_installs # A PBS host has no guests whose applications could be tracked.
managed_installs.detect_and_register() if not is_pbs_host():
print("[ProxMenux] Managed-installs registry initialised") import managed_installs
managed_installs.detect_and_register()
print("[ProxMenux] Managed-installs registry initialised")
except Exception as e: except Exception as e:
print(f"[ProxMenux] managed_installs init failed: {e}") print(f"[ProxMenux] managed_installs init failed: {e}")
# Docker inventories are derived runtime data and intentionally # Docker inventories are derived runtime data and intentionally
@@ -23330,6 +23379,9 @@ if __name__ == '__main__':
# the 24 h PollingCollector cycle. Runs in a daemon thread with # the 24 h PollingCollector cycle. Runs in a daemon thread with
# a delay so it doesn't block startup — notification_manager needs # a delay so it doesn't block startup — notification_manager needs
# a moment to bind channels before we emit. # a moment to bind channels before we emit.
# The guest services below read VMs and LXCs, which do not exist on a
# Proxmox Backup Server.
pve_host = not is_pbs_host()
try: try:
def _startup_emit_pending_app_updates(): def _startup_emit_pending_app_updates():
time.sleep(15) # let channels bind + first metrics collect time.sleep(15) # let channels bind + first metrics collect
@@ -23339,8 +23391,9 @@ if __name__ == '__main__':
except Exception as e: except Exception as e:
print(f"[ProxMenux] startup emit_all_pending_updates failed: {e}", print(f"[ProxMenux] startup emit_all_pending_updates failed: {e}",
file=sys.stderr, flush=True) file=sys.stderr, flush=True)
threading.Thread(target=_startup_emit_pending_app_updates, daemon=True, if pve_host:
name='app-updates-startup-emit').start() threading.Thread(target=_startup_emit_pending_app_updates, daemon=True,
name='app-updates-startup-emit').start()
except Exception as e: except Exception as e:
print(f"[ProxMenux] app-updates startup emitter failed to arm: {e}") print(f"[ProxMenux] app-updates startup emitter failed to arm: {e}")
@@ -23352,6 +23405,13 @@ if __name__ == '__main__':
except Exception as e: except Exception as e:
print(f"[ProxMenux] Node-metrics prewarmer failed to start: {e}") print(f"[ProxMenux] Node-metrics prewarmer failed to start: {e}")
# Linked Monitors for the header node selector, renewed every 5 minutes.
try:
import monitor_links
monitor_links.start_prewarmer()
except Exception as e:
print(f"[ProxMenux] Monitor links prewarmer failed to start: {e}")
# ── VM/CT modal-cache prewarmer ── # ── VM/CT modal-cache prewarmer ──
# Keeps _vm_details_cache / _vm_backups_cache / app snapshots / # Keeps _vm_details_cache / _vm_backups_cache / app snapshots /
# _vm_schedule_cache warm from the backend so the "Loading # _vm_schedule_cache warm from the backend so the "Loading
@@ -23359,9 +23419,10 @@ if __name__ == '__main__':
# after the tab has been closed for minutes. The React-side # after the tab has been closed for minutes. The React-side
# prefetcher only ran while the page was open. # prefetcher only ran while the page was open.
try: try:
vm_modal_thread = threading.Thread(target=_vm_modal_prewarmer_loop, daemon=True, name='vm-modal-prewarmer') if pve_host:
vm_modal_thread.start() vm_modal_thread = threading.Thread(target=_vm_modal_prewarmer_loop, daemon=True, name='vm-modal-prewarmer')
print("[ProxMenux] VM-modal prewarmer started (one-shot warm-up; caches refreshed by event invalidation only)") vm_modal_thread.start()
print("[ProxMenux] VM-modal prewarmer started (one-shot warm-up; caches refreshed by event invalidation only)")
except Exception as e: except Exception as e:
print(f"[ProxMenux] VM-modal prewarmer failed to start: {e}") print(f"[ProxMenux] VM-modal prewarmer failed to start: {e}")
@@ -23385,8 +23446,9 @@ if __name__ == '__main__':
# identical (backup, restart, /usr/bin/update + custom command # identical (backup, restart, /usr/bin/update + custom command
# chain) between manual and scheduled invocations. # chain) between manual and scheduled invocations.
try: try:
scheduler_thread = threading.Thread(target=_scheduler_loop, daemon=True) if pve_host:
scheduler_thread.start() scheduler_thread = threading.Thread(target=_scheduler_loop, daemon=True)
scheduler_thread.start()
except Exception as e: except Exception as e:
print(f"[ProxMenux] LXC update scheduler failed to start: {e}") print(f"[ProxMenux] LXC update scheduler failed to start: {e}")
@@ -16,6 +16,9 @@ Endpoints, all under /api/vm-backup-jobs:
DELETE /<id> delete a job (its backups are kept) DELETE /<id> delete a job (its backups are kept)
POST /<id>/toggle enable / disable POST /<id>/toggle enable / disable
POST /<id>/run run it now, as a Proxmox task on each node POST /<id>/run run it now, as a Proxmox task on each node
Storage retention is read/edited under /api/vm-backup-storages. Saving a
policy only changes the native storage configuration; it never runs pruning.
""" """
import json import json
@@ -229,6 +232,58 @@ def options():
}) })
@vm_backup_bp.route("/api/vm-backup-storages", methods=["GET"])
@require_auth
def backup_storage_retention():
ok, storages = _pvesh_json(["get", "/storage"])
if not ok or not isinstance(storages, list):
return _err(storages if not ok else "Invalid storage inventory", 502)
return jsonify({"storages": [
{"id": storage["storage"], "type": storage.get("type", ""),
"prune_backups": _flat(storage.get("prune-backups") or ""),
"digest": storage.get("digest", "")}
for storage in storages
if isinstance(storage, dict) and storage.get("storage")
and "backup" in (storage.get("content") or "").split(",")
]})
@vm_backup_bp.route("/api/vm-backup-storages/<storage_id>", methods=["PUT"])
@require_admin_scope
def update_backup_storage_retention(storage_id):
if not STORAGE_RE.fullmatch(storage_id):
return _err("Invalid storage")
data = request.get_json(silent=True)
if not isinstance(data, dict):
return _err("A retention policy and current configuration digest are required")
digest, prune = data.get("digest"), data.get("prune_backups")
if not isinstance(digest, str) or not re.fullmatch(r"[a-fA-F0-9]{40}", digest):
return _err("Refresh the storage list before editing its retention")
# An empty policy removes the storage setting, so the Proxmox default applies.
if not isinstance(prune, str) or (prune and not PRUNE_RE.fullmatch(prune)):
return _err("Invalid retention policy")
pairs = [part.split("=", 1) for part in prune.split(",")] if prune else []
keys = [key for key, _ in pairs]
if (len(keys) != len(set(keys)) or
("keep-all" in keys and (len(keys) != 1 or pairs[0][1] != "1")) or
any(int(value) < 1 for _, value in pairs)):
return _err("Choose keep-all=1 or one or more positive retention counts")
# Check that this is still a backup storage, then let Proxmox check the
# digest under its storage lock. Other storage settings are never passed.
ok, storage = _pvesh_json(["get", f"/storage/{storage_id}"])
if not ok:
return _err(storage, 502)
if not isinstance(storage, dict) or "backup" not in (storage.get("content") or "").split(","):
return _err("This storage does not accept backups", 409)
if storage.get("digest") != digest:
return _err("Storage configuration changed; refresh before saving", 409)
change = ["--prune-backups", prune] if prune else ["--delete", "prune-backups"]
ok, out = _pvesh(["set", f"/storage/{storage_id}", "--digest", digest, *change])
if not ok:
return _err(out, 409)
return jsonify({"success": True})
@vm_backup_bp.route("/api/vm-backup-jobs", methods=["POST"]) @vm_backup_bp.route("/api/vm-backup-jobs", methods=["POST"])
@require_admin_scope @require_admin_scope
def create_job(): def create_job():
+172
View File
@@ -0,0 +1,172 @@
# ==========================================================
# ProxMenux Monitor - Guest backup archives
# Author : Jonatan
# GitHub : github.com/jcastro
# Maintainer : MacRimi
# Copyright : (c) 2026 MacRimi & contributors
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 16/10/2026
# ==========================================================
# Description:
# Deletes explicitly selected VM/CT backups through the native Proxmox
# storage API after a fresh inventory/protection check. Tracks native
# deletion tasks so an accepted operation is not mistaken for completion.
# No filesystem paths or cached inventory authorize deletion.
# ==========================================================
import json
import re
import socket
import subprocess
import threading
import time
from concurrent.futures import ThreadPoolExecutor
MAX_SELECTION = 20
REMOVED_RE = re.compile(r"Removed volume '[^'\n]+'")
STORAGE_RE = re.compile(r"[A-Za-z0-9][A-Za-z0-9._-]{0,63}")
NODE_RE = re.compile(r"[A-Za-z0-9][A-Za-z0-9-]{0,62}")
_tasks = {}
_lock = threading.Lock()
class BackupArchiveError(Exception):
def __init__(self, message, code=400):
super().__init__(message)
self.code = code
def _pvesh(args, timeout=20):
try:
result = subprocess.run(["pvesh", *args, "--output-format", "json"],
capture_output=True, text=True, timeout=timeout)
except (OSError, subprocess.TimeoutExpired) as error:
raise BackupArchiveError("Proxmox command unavailable or timed out; outcome unconfirmed", 502) from error
if result.returncode:
raise BackupArchiveError((result.stderr or "Proxmox command failed").strip()[:1000], 502)
output = result.stdout.strip()
try:
# pvesh can print no bytes for the API's successful null result.
return json.loads(output or "null")
except (ValueError, TypeError) as error:
# With --delay, a deletion that ends in time prints the line of its
# task log instead of JSON. Only that exact line is accepted.
if args[0] == "delete" and REMOVED_RE.fullmatch(output):
return output
raise BackupArchiveError("Invalid Proxmox response; outcome unconfirmed", 502) from error
def _node():
# Same local storage/node scope as the guest's existing backup list.
node = socket.gethostname().split(".")[0]
if not NODE_RE.fullmatch(node):
raise BackupArchiveError("Invalid local node name", 502)
return node
def _selection(vmid, data):
if not 100 <= vmid <= 999999999:
raise BackupArchiveError("Invalid VM/CT ID")
if not isinstance(data, dict) or data.get("confirm") is not True:
raise BackupArchiveError("Explicit confirmation is required")
selected = data.get("backups")
if not isinstance(selected, list) or not 1 <= len(selected) <= MAX_SELECTION:
raise BackupArchiveError(f"Select between 1 and {MAX_SELECTION} backups")
seen = set()
for item in selected:
if not isinstance(item, dict):
raise BackupArchiveError("Invalid backup selection")
storage, volid = item.get("storage"), item.get("volid")
if not isinstance(storage, str) or not STORAGE_RE.fullmatch(storage):
raise BackupArchiveError("Invalid backup storage")
if (not isinstance(volid, str) or not volid.startswith(storage + ":")
or len(volid) > 512 or not re.fullmatch(r"[A-Za-z0-9_:./+@-]+", volid)
or any(part in ("", ".", "..") for part in volid.split(":", 1)[1].split("/"))):
raise BackupArchiveError("Invalid backup volume ID")
if volid in seen:
raise BackupArchiveError("Duplicate backup selection")
seen.add(volid)
return [{"storage": item["storage"], "volid": item["volid"]} for item in selected]
def _protected(value):
# Fail closed for an unexpected protection value, including the text 'true'.
return value not in (None, False, 0, "0", "")
def _remember_task(task, vmid, node, volid):
# These are server-returned task IDs, never arbitrary client paths.
if (not isinstance(task, str) or not task.startswith(f"UPID:{node}:")
or ":imgdel:" not in task or "/" in task or len(task) > 1024):
raise BackupArchiveError("Unexpected deletion task; outcome unconfirmed", 502)
with _lock:
now = time.monotonic()
expired = [key for key, entry in _tasks.items() if now - entry["created"] > 3600]
for key in expired:
del _tasks[key]
# Bound even failed/unpolled jobs; expiry does not cancel a PVE task.
if len(_tasks) >= 512:
del _tasks[min(_tasks, key=lambda key: _tasks[key]["created"])]
_tasks[task] = {"vmid": vmid, "node": node, "volid": volid, "created": now}
def delete_guest_backups(vmid, data):
selected = _selection(vmid, data)
node = _node()
inventory = {}
# Preflight the entire batch before deleting anything. An unavailable
# storage or a protected/stale/cross-guest selection fails the whole batch.
for storage in sorted({item["storage"] for item in selected}):
entries = _pvesh(["get", f"/nodes/{node}/storage/{storage}/content",
"--content", "backup", "--vmid", str(vmid)])
if not isinstance(entries, list):
raise BackupArchiveError("Invalid backup inventory", 502)
for entry in entries:
if (isinstance(entry, dict) and entry.get("content") == "backup"
and isinstance(entry.get("volid"), str)):
inventory[(storage, entry.get("volid"))] = entry
for item in selected:
entry = inventory.get((item["storage"], item["volid"]))
if entry is None or str(entry.get("vmid")) != str(vmid):
raise BackupArchiveError("Backup no longer exists or does not belong to this VM/CT", 409)
if _protected(entry.get("protected")):
raise BackupArchiveError("Protected backups cannot be deleted", 409)
def remove(item):
try:
# Proxmox also enforces protection at deletion time. It removes
# associated notes/log files and returns null only after success.
task = _pvesh(["delete", f'/nodes/{node}/storage/{item["storage"]}/content/{item["volid"]}',
"--delay", "1"])
if isinstance(task, str) and task.startswith("UPID:"):
_remember_task(task, vmid, node, item["volid"])
return {**item, "status": "pending", "task": task}
if task is None or task == f"Removed volume '{item['volid']}'":
return {**item, "status": "deleted"}
return {**item, "status": "unconfirmed", "error": "Invalid Proxmox response; outcome unconfirmed"}
except BackupArchiveError as error:
return {**item, "status": "unconfirmed", "error": str(error)}
with ThreadPoolExecutor(max_workers=min(4, len(selected)), thread_name_prefix="backup-delete") as pool:
results = list(pool.map(remove, selected))
return {"results": results}
def guest_backup_deletion_status(vmid, task):
with _lock:
entry = _tasks.get(task) if isinstance(task, str) else None
if not entry or entry["vmid"] != vmid or time.monotonic() - entry["created"] > 3600:
raise BackupArchiveError("Deletion task unknown or expired; refresh the backup list", 409)
entry = entry.copy()
status = _pvesh(["get", f'/nodes/{entry["node"]}/tasks/{task}/status'])
if not isinstance(status, dict) or status.get("status") not in ("running", "stopped"):
raise BackupArchiveError("Invalid deletion task status", 502)
result = {"volid": entry["volid"], "status": "pending"}
if status["status"] == "stopped":
if status.get("exitstatus") == "OK":
result["status"] = "deleted"
else:
result.update(status="unconfirmed", error=status.get("exitstatus") or "Deletion outcome unconfirmed")
return result
+53 -41
View File
@@ -51,6 +51,7 @@ DEBUG_PERF = False
# ─── Startup Grace Period ──────────────────────────────────────────────────── # ─── Startup Grace Period ────────────────────────────────────────────────────
# Import centralized startup grace management for consistent behavior # Import centralized startup grace management for consistent behavior
import startup_grace import startup_grace
from pbs_manager import get_platform, PBS_SERVICES
def _is_startup_health_grace() -> bool: def _is_startup_health_grace() -> bool:
"""Check if we're within the startup health grace period (5 min). """Check if we're within the startup health grace period (5 min).
@@ -153,36 +154,10 @@ def _hdd_in_standby(disk_name: str) -> bool:
return parked return parked
def _lvm_device_args() -> list: def _lvm_device_args():
"""`--devices` list scoping pvs/lvs/vgs off parked disks so they """None means no automatic scan; [] permits LVM's normal scan."""
aren't spun up by the LVM scan (issue #232). Empty when nothing is from lvm_device_scope import lvm_device_args
parked, so the plain command runs unchanged. Each partition is return lvm_device_args(_hdd_in_standby)
listed too — a whole-disk path alone doesn't cover a PV on a
partition."""
try:
r = subprocess.run(
['lsblk', '-rno', 'NAME,TYPE,PKNAME'],
capture_output=True, text=True, timeout=5)
if r.returncode != 0:
return []
nodes = []
for line in r.stdout.strip().split('\n'):
parts = line.split()
if len(parts) < 2 or parts[1] not in ('disk', 'part'):
continue
name = parts[0]
base = name if parts[1] == 'disk' else (parts[2] if len(parts) > 2 else name)
nodes.append((name, base))
standby_bases = {
name for name, base in nodes
if name == base and _hdd_in_standby(name)
}
if not standby_bases:
return []
devices = [f'/dev/{name}' for name, base in nodes if base not in standby_bases]
return ['--devices', ','.join(devices)] if devices else []
except Exception:
return []
def _is_disk_removable(disk_name: str) -> bool: def _is_disk_removable(disk_name: str) -> bool:
@@ -466,6 +441,7 @@ class HealthMonitor:
def __init__(self): def __init__(self):
"""Initialize health monitor with state tracking""" """Initialize health monitor with state tracking"""
self.platform_type = get_platform().get("product")
self.state_history = defaultdict(list) self.state_history = defaultdict(list)
self.last_check_times = {} self.last_check_times = {}
self.cached_results = {} self.cached_results = {}
@@ -919,9 +895,9 @@ class HealthMonitor:
_perf_log("services", (time.time() - _t) * 1000) _perf_log("services", (time.time() - _t) * 1000)
details['services'] = services_status details['services'] = services_status
if services_status['status'] == 'CRITICAL': if services_status['status'] == 'CRITICAL':
critical_issues.append(f"PVE Services: {services_status.get('reason', 'Service failure')}") critical_issues.append(f"Proxmox Services: {services_status.get('reason', 'Service failure')}")
elif services_status['status'] == 'WARNING': elif services_status['status'] == 'WARNING':
warning_issues.append(f"PVE Services: {services_status.get('reason', 'Service issue')}") warning_issues.append(f"Proxmox Services: {services_status.get('reason', 'Service issue')}")
# Priority 1.5: Proxmox Storage Check (External Module) # Priority 1.5: Proxmox Storage Check (External Module)
_t = time.time() _t = time.time()
@@ -1661,7 +1637,7 @@ class HealthMonitor:
mem_critical_samples = [ mem_critical_samples = [
entry for entry in self.state_history[state_key] entry for entry in self.state_history[state_key]
if entry['mem_percent'] >= 90 and if entry['mem_percent'] >= self.MEMORY_CRITICAL and
current_time - entry['time'] <= self.MEMORY_DURATION current_time - entry['time'] <= self.MEMORY_DURATION
] ]
@@ -1695,7 +1671,7 @@ class HealthMonitor:
oldest = min(s['time'] for s in mem_critical_samples) oldest = min(s['time'] for s in mem_critical_samples)
actual_duration = int(current_time - oldest) actual_duration = int(current_time - oldest)
status = 'CRITICAL' status = 'CRITICAL'
reason = f'RAM >90% sustained for {actual_duration}s' reason = f'RAM >{self.MEMORY_CRITICAL}% sustained for {actual_duration}s'
elif swap_critical >= 2: elif swap_critical >= 2:
status = 'CRITICAL' status = 'CRITICAL'
reason = (f'Memory pressure: swap {swap_percent:.0f}% used ' reason = (f'Memory pressure: swap {swap_percent:.0f}% used '
@@ -1715,7 +1691,7 @@ class HealthMonitor:
swap_total_gb = round(swap.total / (1024**3), 2) swap_total_gb = round(swap.total / (1024**3), 2)
# Determine per-sub-check status # Determine per-sub-check status
ram_status = 'CRITICAL' if mem_percent >= 90 and mem_critical_count >= MEM_CRITICAL_MIN_SAMPLES else ('WARNING' if mem_percent >= self.MEMORY_WARNING and mem_warning_count >= MEM_WARNING_MIN_SAMPLES else 'OK') ram_status = 'CRITICAL' if mem_percent >= self.MEMORY_CRITICAL and mem_critical_count >= MEM_CRITICAL_MIN_SAMPLES else ('WARNING' if mem_percent >= self.MEMORY_WARNING and mem_warning_count >= MEM_WARNING_MIN_SAMPLES else 'OK')
swap_status = 'CRITICAL' if swap_critical >= 2 else 'OK' swap_status = 'CRITICAL' if swap_critical >= 2 else 'OK'
result = { result = {
@@ -2303,12 +2279,17 @@ class HealthMonitor:
# `--devices` keeps lvs from scanning parked HDDs (issue #232). # `--devices` keeps lvs from scanning parked HDDs (issue #232).
dev_args = _lvm_device_args() dev_args = _lvm_device_args()
if dev_args is None:
return {'status': 'INFO', 'skipped': True,
'reason': 'Automatic LVM scan skipped by disk read policy or unavailable inventory'}
result = subprocess.run( result = subprocess.run(
['lvs', *dev_args, '--noheadings', '--options', 'lv_name,vg_name,lv_attr'], ['lvs', *dev_args, '--noheadings', '--options', 'lv_name,vg_name,lv_attr'],
capture_output=True, capture_output=True,
text=True, text=True,
timeout=3 timeout=3
) )
from lvm_device_scope import settle_scan_reads
settle_scan_reads()
if result.returncode != 0: if result.returncode != 0:
return {'status': 'WARNING', 'reason': 'lvs command failed'} return {'status': 'WARNING', 'reason': 'lvs command failed'}
@@ -2333,6 +2314,7 @@ class HealthMonitor:
text=True, text=True,
timeout=3 timeout=3
) )
settle_scan_reads()
if vg_result.returncode == 0 and vg_result.stdout.strip(): if vg_result.returncode == 0 and vg_result.stdout.strip():
return {'status': 'WARNING', 'reason': 'No active LVM volumes detected'} return {'status': 'WARNING', 'reason': 'No active LVM volumes detected'}
else: else:
@@ -2347,6 +2329,8 @@ class HealthMonitor:
# The new _check_proxmox_storage function handles this logic better. # The new _check_proxmox_storage function handles this logic better.
def _check_proxmox_storages(self) -> Dict[str, Any]: def _check_proxmox_storages(self) -> Dict[str, Any]:
"""Check Proxmox-specific storages (only report problems)""" """Check Proxmox-specific storages (only report problems)"""
if getattr(self, "platform_type", "pve") == "pbs":
return {'status': 'OK', 'reason': 'Not applicable on PBS', 'checks': {}, 'not_applicable': True}
storages = {} storages = {}
try: try:
@@ -3515,6 +3499,8 @@ class HealthMonitor:
Optimized VM/CT check - detects qmp failures and startup errors from logs. Optimized VM/CT check - detects qmp failures and startup errors from logs.
Improved detection of container and VM errors from journalctl. Improved detection of container and VM errors from journalctl.
""" """
if getattr(self, "platform_type", "pve") == "pbs":
return {'status': 'OK', 'reason': 'Not applicable on PBS', 'checks': {}, 'not_applicable': True}
try: try:
# First: auto-resolve any persisted VM/CT errors where the guest # First: auto-resolve any persisted VM/CT errors where the guest
# is now running. This clears stale "Failed to start" / QMP # is now running. This clears stale "Failed to start" / QMP
@@ -3678,6 +3664,8 @@ class HealthMonitor:
Check VMs/CTs with persistent error tracking. Check VMs/CTs with persistent error tracking.
Errors persist until VM starts or 48h elapsed. Errors persist until VM starts or 48h elapsed.
""" """
if getattr(self, "platform_type", "pve") == "pbs":
return {'status': 'OK', 'reason': 'Not applicable on PBS', 'checks': {}, 'not_applicable': True}
try: try:
issues = [] issues = []
vm_details = {} vm_details = {}
@@ -3919,8 +3907,18 @@ class HealthMonitor:
""" """
try: try:
# Build service list: base PVE services + corosync if clustered # Build service list: base PVE services + corosync if clustered
services_to_check = list(self.PVE_SERVICES) pbs_host = getattr(self, "platform_type", "pve") == "pbs"
is_cluster = os.path.exists('/etc/corosync/corosync.conf') service_prefix = "pbs" if pbs_host else "pve"
product_name = "PBS" if pbs_host else "PVE"
services_to_check = list(PBS_SERVICES) if pbs_host else list(self.PVE_SERVICES)
if pbs_host:
# Old Monitor versions incorrectly treated absent PVE services
# as PBS failures; remove only those known inapplicable errors.
for service in self.PVE_SERVICES:
old_key = f'pve_service_{service}'
if health_persistence.is_error_active(old_key):
health_persistence.clear_error(old_key)
is_cluster = not pbs_host and os.path.exists('/etc/corosync/corosync.conf')
if is_cluster and 'corosync' not in services_to_check: if is_cluster and 'corosync' not in services_to_check:
services_to_check.append('corosync') services_to_check.append('corosync')
@@ -3952,14 +3950,14 @@ class HealthMonitor:
# cleanup loop). Audit Tier 5 — Health stack. # cleanup loop). Audit Tier 5 — Health stack.
for svc in services_to_check: for svc in services_to_check:
if svc not in failed_services: if svc not in failed_services:
error_key = f'pve_service_{svc}' error_key = f'{service_prefix}_service_{svc}'
if health_persistence.is_error_active(error_key): if health_persistence.is_error_active(error_key):
health_persistence.clear_error(error_key) health_persistence.clear_error(error_key)
# Build checks dict with status per service # Build checks dict with status per service
checks = {} checks = {}
for svc in services_to_check: for svc in services_to_check:
error_key = f'pve_service_{svc}' error_key = f'{service_prefix}_service_{svc}'
if svc in failed_services: if svc in failed_services:
state = service_details.get(svc, 'inactive') state = service_details.get(svc, 'inactive')
checks[svc] = { checks[svc] = {
@@ -3987,12 +3985,12 @@ class HealthMonitor:
# Record each failed service in persistence, respecting dismiss # Record each failed service in persistence, respecting dismiss
active_failed = [] active_failed = []
for svc in failed_services: for svc in failed_services:
error_key = f'pve_service_{svc}' error_key = f'{service_prefix}_service_{svc}'
rec_result = health_persistence.record_error( rec_result = health_persistence.record_error(
error_key=error_key, error_key=error_key,
category='pve_services', category='pve_services',
severity='CRITICAL', severity='CRITICAL',
reason=f'PVE service {svc} is {service_details.get(svc, "inactive")}', reason=f'{product_name} service {svc} is {service_details.get(svc, "inactive")}',
details={'service': svc, 'state': service_details.get(svc, 'inactive')} details={'service': svc, 'state': service_details.get(svc, 'inactive')}
) )
if rec_result and rec_result.get('type') == 'skipped_acknowledged': if rec_result and rec_result.get('type') == 'skipped_acknowledged':
@@ -5816,6 +5814,8 @@ class HealthMonitor:
- No persistent errors are recorded - No persistent errors are recorded
This prevents false positives when NFS/PBS/remote storage is still mounting. This prevents false positives when NFS/PBS/remote storage is still mounting.
""" """
if getattr(self, "platform_type", "pve") == "pbs":
return None
if not PROXMOX_STORAGE_AVAILABLE: if not PROXMOX_STORAGE_AVAILABLE:
return None return None
@@ -6076,6 +6076,8 @@ class HealthMonitor:
if status == 'stale': if status == 'stale':
checks[check_label] = { checks[check_label] = {
'status': 'CRITICAL', 'status': 'CRITICAL',
'error_key': error_key,
'dismissable': True,
'detail': f'{fstype} mount stale: {error or "unreachable"}', 'detail': f'{fstype} mount stale: {error or "unreachable"}',
'fstype': fstype, 'fstype': fstype,
'source': m.get('source', ''), 'source': m.get('source', ''),
@@ -6105,6 +6107,8 @@ class HealthMonitor:
elif status == 'readonly': elif status == 'readonly':
checks[check_label] = { checks[check_label] = {
'status': 'WARNING', 'status': 'WARNING',
'error_key': error_key,
'dismissable': True,
'detail': f'{fstype} mount unexpectedly read-only', 'detail': f'{fstype} mount unexpectedly read-only',
'fstype': fstype, 'fstype': fstype,
'source': m.get('source', ''), 'source': m.get('source', ''),
@@ -6205,6 +6209,8 @@ class HealthMonitor:
Notification cooldown is the standard 1h via the storage Notification cooldown is the standard 1h via the storage
category, so a CT teetering at 96% won't spam the channel. category, so a CT teetering at 96% won't spam the channel.
""" """
if getattr(self, "platform_type", "pve") == "pbs":
return None
# Cheap pre-check: bail out when there are no running LXCs. On # Cheap pre-check: bail out when there are no running LXCs. On
# nodes without CTs, the previous version still paid ~860ms per # nodes without CTs, the previous version still paid ~860ms per
# cycle for a `pvesh /cluster/resources` call that produced no # cycle for a `pvesh /cluster/resources` call that produced no
@@ -6365,6 +6371,8 @@ class HealthMonitor:
path, so a slow / dead guest agent can't stretch the health path, so a slow / dead guest agent can't stretch the health
cycle. cycle.
""" """
if getattr(self, "platform_type", "pve") == "pbs":
return None
try: try:
import flask_server # deferred — avoids circular import import flask_server # deferred — avoids circular import
resources = flask_server.get_cached_pvesh_cluster_resources_vm() or [] resources = flask_server.get_cached_pvesh_cluster_resources_vm() or []
@@ -6524,6 +6532,8 @@ class HealthMonitor:
check (already covered) because those are usually large data check (already covered) because those are usually large data
mounts whose fill rate is driven by user content, not log spam. mounts whose fill rate is driven by user content, not log spam.
""" """
if getattr(self, "platform_type", "pve") == "pbs":
return None
if not MOUNT_MONITOR_AVAILABLE: if not MOUNT_MONITOR_AVAILABLE:
return None return None
try: try:
@@ -6627,6 +6637,8 @@ class HealthMonitor:
check via _check_filesystem. Re-alerting on the same condition check via _check_filesystem. Re-alerting on the same condition
through two channels just clutters the inbox. through two channels just clutters the inbox.
""" """
if getattr(self, "platform_type", "pve") == "pbs":
return None
if not PROXMOX_STORAGE_AVAILABLE: if not PROXMOX_STORAGE_AVAILABLE:
return None return None
try: try:
+9 -1
View File
@@ -8,6 +8,8 @@ from flask import request, jsonify, g
from functools import wraps from functools import wraps
from auth_manager import load_auth_config, verify_token, verify_token_full from auth_manager import load_auth_config, verify_token, verify_token_full
_READ_METHODS = ('GET', 'HEAD', 'OPTIONS')
def require_auth(f): def require_auth(f):
""" """
@@ -64,13 +66,19 @@ def require_auth(f):
token = parts[1] token = parts[1]
# Verify token # Verify token
username = verify_token(token) username, scope = verify_token_full(token)
if not username: if not username:
return jsonify({ return jsonify({
"error": "Invalid or expired token", "error": "Invalid or expired token",
"message": "Please log in again" "message": "Please log in again"
}), 401 }), 401
# read_only tokens may only read; any method that can change
# state needs full_admin.
if request.method not in _READ_METHODS and scope != 'full_admin':
return jsonify({"error": "Insufficient scope",
"message": f"This action requires a full_admin token (your token: {scope})"}), 403
# Token is valid, allow access # Token is valid, allow access
return f(*args, **kwargs) return f(*args, **kwargs)
+116
View File
@@ -0,0 +1,116 @@
# ==========================================================
# ProxMenux Monitor - LVM disk read scope
# Author : Jonatan
# GitHub : github.com/jcastro
# Maintainer : MacRimi
# Copyright : (c) 2026 MacRimi & contributors
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 16/10/2026
# ==========================================================
# Description:
# Applies the automatic disk-read policy to LVM metadata scans in both
# Monitor readers. Excluded and idle disks receive no power-mode probe;
# every device built on them is excluded too, and the scan's own reads
# are not taken as activity. No eligible devices means no scan.
# ==========================================================
import json
import re
import subprocess
import threading
_NAME = re.compile(r"[A-Za-z0-9._+-]+")
_scan_lock = threading.Lock()
_scanned_disks = set()
def _topology():
"""Every block device by kernel name: its type, its parents and whether
it is an LVM physical volume. lsblk reads this from udev, not the disks.
A RAID or multipath device appears once per member."""
result = subprocess.run(["lsblk", "-J", "-l", "-o", "KNAME,TYPE,PKNAME,FSTYPE"],
capture_output=True, text=True, timeout=5)
if result.returncode:
return None
types, parents, volumes = {}, {}, set()
for device in json.loads(result.stdout).get("blockdevices") or []:
name, parent = device.get("kname"), device.get("pkname")
if not isinstance(name, str) or not _NAME.fullmatch(name):
return None
types[name] = device.get("type")
parents.setdefault(name, set())
if parent is not None:
if not isinstance(parent, str) or not _NAME.fullmatch(parent):
return None
parents[name].add(parent)
if device.get("fstype") == "LVM2_member" and device.get("type") != "lvm":
volumes.add(name)
return types, parents, volumes
def _disks_under(name, parents, seen=None):
"""The disks a device is built on: itself, or its parents' disks."""
seen = seen if seen is not None else set()
if name in seen:
return set()
seen.add(name)
if not parents.get(name):
return {name}
found = set()
for parent in parents[name]:
found |= _disks_under(parent, parents, seen)
return found
def lvm_device_args(standby_check):
"""Return [] for an unrestricted scan, --devices args, or None to skip.
A failed inventory must never fall back to an unrestricted scan. None
is distinct from []: if every disk is excluded/idle/asleep, callers
must not start pvs/lvs/vgs at all.
"""
try:
from disk_temperature_history import disk_read_policy, excluded_disk_names
topology = _topology()
if topology is None:
return None
types, parents, volumes = topology
disks = {name for name, kind in types.items() if kind == "disk"}
if not disks:
return None
excluded = excluded_disk_names()
blocked = set()
for disk in sorted(disks):
# Test policy first: even CHECK POWER MODE is forbidden for an
# excluded/idle disk. Some USB bridges do not honour -n standby.
if disk_read_policy(disk, excluded) != "read" or standby_check(disk):
blocked.add(disk)
allowed = sorted(f"/dev/{name}" for name in volumes if not (_disks_under(name, parents) & blocked))
with _scan_lock:
_scanned_disks.clear()
_scanned_disks.update(disks - blocked)
if not blocked:
return []
# An explicit list makes LVM open every device in it, so only the
# physical volumes are named: a RAID, multipath or encrypted volume
# built on a blocked disk would read that disk too and stays out.
if not allowed:
return None
return ["--devices", ",".join(allowed)]
except Exception:
# Unavailable exclusions, topology or policy are not permission to
# open devices that may have been deliberately excluded.
return None
def settle_scan_reads():
"""The LVM scan reads the disks it is allowed to. Those reads are its
own, so they must not make an unused disk look busy at the next check."""
try:
from disk_temperature_history import settle_own_reads
with _scan_lock:
disks = set(_scanned_disks)
settle_own_reads(disks)
except Exception:
pass
+7
View File
@@ -1179,6 +1179,13 @@ def validate_config(payload: dict) -> tuple[bool, Any]:
return _err("logo_url must be an http(s) URL (max 512 chars)") return _err("logo_url must be an http(s) URL (max 512 chars)")
conf["logo_url"] = logo conf["logo_url"] = logo
# Native discovery identity is separate from a curated helper/updater.
discovery_id = payload.get("discovery_id")
if discovery_id is not None:
if not isinstance(discovery_id, str) or not re.fullmatch(r"native-[0-9a-f]{24}", discovery_id):
return _err("invalid discovery_id")
conf["discovery_id"] = discovery_id
# Optional helper_slug — set by the frontend when the user picks # Optional helper_slug — set by the frontend when the user picks
# an auto-detected app (primary or extra). Persisted so we can # an auto-detected app (primary or extra). Persisted so we can
# filter the "also detected" chip list against apps already # filter the "also detected" chip list against apps already
+321
View File
@@ -0,0 +1,321 @@
"""Other ProxMenux Monitors reachable from this one: cluster nodes, PBS servers
and entries added by hand.
Only links are kept here. No credential is stored or sent: the browser opens
the other Monitor, which keeps its own sign-in.
"""
import ipaddress
import json
import os
import re
import secrets
import socket
import ssl
import subprocess
import threading
import time
import urllib.parse
import urllib.request
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import pbs_manager
LINKS_FILE = Path(os.environ.get("PROXMENUX_MONITOR_LINKS", "/usr/local/share/proxmenux/monitor_links.json"))
SSL_CONFIG_FILE = Path(os.environ.get("PROXMENUX_SSL_CONFIG", "/etc/proxmenux/ssl_config.json"))
STORAGE_CFG = Path("/etc/pve/storage.cfg")
MONITOR_PORT = 8008
PBS_UI_PORT = 8007
PROBE_TIMEOUT = 3
PROBE_TTL = 300
MAX_MANUAL = 64
PRODUCTS = ("pve", "pbs")
_lock = threading.Lock()
_probe_cache = {}
_snapshot = {"at": 0.0, "data": None}
_refresh_lock = threading.Lock()
class LinkError(ValueError):
pass
def _load():
try:
data = json.loads(LINKS_FILE.read_text())
except (OSError, ValueError):
return {"manual": [], "overrides": {}}
manual = data.get("manual") if isinstance(data.get("manual"), list) else []
overrides = data.get("overrides") if isinstance(data.get("overrides"), dict) else {}
return {"manual": [row for row in manual if isinstance(row, dict)], "overrides": overrides}
def _save(data):
LINKS_FILE.parent.mkdir(parents=True, exist_ok=True)
temporary = LINKS_FILE.with_suffix(".tmp")
temporary.write_text(json.dumps(data, indent=2))
os.chmod(temporary, 0o600)
os.replace(temporary, LINKS_FILE)
def normalize_url(value):
"""Return the origin of an http(s) address, or raise LinkError."""
if not isinstance(value, str) or not value.strip():
raise LinkError("Enter the address of the Monitor.")
text = value.strip()
if "://" not in text:
text = "https://" + text
parsed = urllib.parse.urlsplit(text)
if parsed.scheme not in ("http", "https") or not parsed.hostname:
raise LinkError("The address must start with http:// or https:// and name a host.")
if parsed.username or parsed.password:
raise LinkError("The address must not contain a user or password.")
try:
port = parsed.port
except ValueError as exc:
raise LinkError("The port of the address is not valid.") from exc
host = parsed.hostname
if ":" in host:
host = f"[{host}]"
return f"{parsed.scheme}://{host}" + (f":{port}" if port else "")
def _local_scheme():
try:
return "https" if json.loads(SSL_CONFIG_FILE.read_text()).get("enabled") else "http"
except (OSError, ValueError, AttributeError):
return "http"
def _host_url(host, port, scheme=None):
if ":" in host and not host.startswith("["):
host = f"[{host}]"
return f"{scheme or _local_scheme()}://{host}:{port}"
def _is_private(host):
"""Only addresses in private ranges are probed without being asked to."""
try:
addresses = {info[4][0] for info in socket.getaddrinfo(host, None)}
except OSError:
return False
try:
return bool(addresses) and all(ipaddress.ip_address(a.split("%")[0]).is_private for a in addresses)
except ValueError:
return False
def _cluster_nodes():
try:
result = subprocess.run(["pvesh", "get", "/cluster/status", "--output-format", "json"],
capture_output=True, text=True, timeout=8)
rows = json.loads(result.stdout) if result.returncode == 0 else []
except (OSError, subprocess.SubprocessError, ValueError):
return []
nodes = []
for row in rows if isinstance(rows, list) else []:
if isinstance(row, dict) and row.get("type") == "node" and not row.get("local") and row.get("name") and row.get("ip"):
nodes.append({"key": f"cluster:{row['name']}", "name": str(row["name"]), "product": "pve",
"group": "cluster", "host": str(row["ip"])})
return nodes
def _storage_pbs():
try:
text = STORAGE_CFG.read_text()
except OSError:
return []
servers, current = [], None
for line in text.splitlines():
header = re.match(r"^(\w+):\s*(\S+)", line)
if header:
current = {"name": header.group(2), "port": PBS_UI_PORT} if header.group(1) == "pbs" else None
if current is not None:
servers.append(current)
continue
option = re.match(r"^\s+(server|port)\s+(\S+)", line)
if current is not None and option:
current[option.group(1)] = option.group(2)
return [row for row in servers if row.get("server")]
def _pbs_servers():
found = {}
for row in _storage_pbs():
host = row["server"]
port = row.get("port") if str(row.get("port", "")).isdigit() else PBS_UI_PORT
found.setdefault(host, {"name": row["name"], "native_url": _host_url(host, port, "https")})
try:
for row in pbs_manager.public_servers():
host = urllib.parse.urlsplit(row.get("url", "")).hostname
if host:
found.setdefault(host, {"name": row.get("name") or host, "native_url": normalize_url(row["url"])})
except Exception:
pass
return [{"key": f"pbs:{host}", "name": info["name"], "product": "pbs", "group": "pbs",
"host": host, "native_url": info["native_url"]} for host, info in found.items()]
def _probe(url):
"""True when a ProxMenux Monitor answers at url; nothing is sent but the request."""
now = time.monotonic()
with _lock:
cached = _probe_cache.get(url)
if cached and cached[0] > now:
return cached[1]
context = ssl.create_default_context()
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE # liveness only: no credential or token is ever sent
online = False
try:
request = urllib.request.Request(url + "/api/auth/status", headers={"Accept": "application/json"})
with urllib.request.urlopen(request, timeout=PROBE_TIMEOUT, context=context) as response:
online = response.status == 200 and "auth_enabled" in json.loads(response.read(4096))
except Exception:
online = False
with _lock:
if len(_probe_cache) > 256:
_probe_cache.clear()
_probe_cache[url] = (now + PROBE_TTL, online)
return online
def _resolve(entry, override):
"""Pick the Monitor address and its state for one discovered or manual entry."""
if override.get("url"):
return override["url"], "online" if _probe(override["url"]) else "offline"
if entry.get("url"):
return entry["url"], "online" if _probe(entry["url"]) else "offline"
host = entry["host"]
if not _is_private(host):
return None, "unknown"
for scheme in (_local_scheme(), "https" if _local_scheme() == "http" else "http"):
url = _host_url(host, MONITOR_PORT, scheme)
if _probe(url):
return url, "online"
return _host_url(host, MONITOR_PORT), "offline"
def list_links(include_hidden=False):
platform = pbs_manager.get_platform()
data = _load()
entries = []
if platform.get("product") != "pbs":
entries += _cluster_nodes() + _pbs_servers()
for row in data["manual"]:
entries.append({"key": f"manual:{row.get('id')}", "name": row.get("name") or row.get("url"),
"product": row.get("product") if row.get("product") in PRODUCTS else "pve",
"group": "pbs" if row.get("product") == "pbs" else "standalone",
"url": row.get("url"), "manual": True})
visible = [e for e in entries if include_hidden or not data["overrides"].get(e["key"], {}).get("hidden")]
with ThreadPoolExecutor(max_workers=8) as executor:
resolved = list(executor.map(lambda e: _resolve(e, data["overrides"].get(e["key"], {})), visible))
links = []
for entry, (url, state) in zip(visible, resolved):
override = data["overrides"].get(entry["key"], {})
links.append({"key": entry["key"], "name": entry["name"], "product": entry["product"],
"group": entry["group"], "url": url, "status": state,
"native_url": entry.get("native_url"), "manual": bool(entry.get("manual")),
"hidden": bool(override.get("hidden")), "custom_url": bool(override.get("url"))})
return {"self": {"name": platform.get("hostname") or socket.gethostname(), "product": platform.get("product")},
"links": links}
def refresh():
"""Recompute the list with fresh probes and keep it for the header."""
with _refresh_lock:
with _lock:
_probe_cache.clear()
data = list_links(include_hidden=True)
_snapshot.update(at=time.monotonic(), data=data)
return data
def _refresh_quietly():
try:
refresh()
except Exception:
pass
def cached_links(include_hidden=False):
"""The last list at once; a stale one is renewed in the background."""
data = _snapshot["data"]
if data is None:
data = refresh()
elif time.monotonic() - _snapshot["at"] > PROBE_TTL and not _refresh_lock.locked():
threading.Thread(target=_refresh_quietly, daemon=True, name="monitor-links-refresh").start()
if include_hidden:
return data
return {**data, "links": [row for row in data["links"] if not row["hidden"]]}
def start_prewarmer():
def loop():
time.sleep(10)
while True:
_refresh_quietly()
time.sleep(PROBE_TTL)
threading.Thread(target=loop, daemon=True, name="monitor-links-prewarmer").start()
def add_link(payload):
if not isinstance(payload, dict):
raise LinkError("Invalid request.")
name = str(payload.get("name") or "").strip()[:64]
url = normalize_url(payload.get("url"))
product = payload.get("product") if payload.get("product") in PRODUCTS else "pve"
with _lock:
data = _load()
if len(data["manual"]) >= MAX_MANUAL:
raise LinkError(f"At most {MAX_MANUAL} nodes can be added.")
if any(row.get("url") == url for row in data["manual"]):
raise LinkError("This address is already in the list.")
identifier = secrets.token_hex(6)
data["manual"].append({"id": identifier, "name": name or urllib.parse.urlsplit(url).hostname,
"url": url, "product": product})
_save(data)
return f"manual:{identifier}"
def update_link(key, payload):
if not isinstance(payload, dict):
raise LinkError("Invalid request.")
with _lock:
data = _load()
if key.startswith("manual:"):
row = next((r for r in data["manual"] if f"manual:{r.get('id')}" == key), None)
if row is None:
raise LinkError("Node not found.")
if "name" in payload:
row["name"] = str(payload["name"] or "").strip()[:64] or row["name"]
if payload.get("url"):
row["url"] = normalize_url(payload["url"])
if payload.get("product") in PRODUCTS:
row["product"] = payload["product"]
elif re.fullmatch(r"(cluster|pbs):[\w.:\-\[\]]{1,253}", key):
override = data["overrides"].setdefault(key, {})
if "url" in payload:
if payload["url"]:
override["url"] = normalize_url(payload["url"])
else:
override.pop("url", None)
if "hidden" in payload:
override["hidden"] = bool(payload["hidden"])
if not override:
data["overrides"].pop(key, None)
else:
raise LinkError("Node not found.")
_save(data)
def delete_link(key):
with _lock:
data = _load()
remaining = [r for r in data["manual"] if f"manual:{r.get('id')}" != key]
if len(remaining) == len(data["manual"]):
raise LinkError("Only nodes added by hand can be removed; detected ones can be hidden.")
data["manual"] = remaining
_save(data)
+750
View File
@@ -0,0 +1,750 @@
#!/usr/bin/env python3
# ==========================================================
# ProxMenux - A menu-driven script for Proxmox VE management
# Author : VAIO73
# GitHub : github.com/Vaso73
# Maintainer : MacRimi
# Copyright : (c) 2026 MacRimi & contributors
# License : (GPL-3.0) (https://github.com/MacRimi/ProxMenux/blob/main/LICENSE)
# Version : 1.0
# Last Updated: 14/10/2026
# ==========================================================
# Description:
# On-demand native LXC discovery from service and installation metadata.
# Reads bounded data only; never executes discovered programs, starts guests,
# registers applications or infers their update mechanism.
# ==========================================================
"""Separate from catalog detection: callers must explicitly request a scan."""
import copy
from contextlib import contextmanager
import fcntl
import hashlib
import json
import os
from pathlib import Path
import re
import selectors
import signal
import stat
import subprocess
import threading
import time
TOTAL_SECONDS = 20
PROBE_SECONDS = 3
OUTPUT_BYTES = 64 * 1024
MAX_RECORDS = 256
MAX_CANDIDATES = 50
CACHE_SECONDS = 300
LOCAL_CONFIG = Path('/etc/pve/local/lxc')
OCI_ROOT = Path('/usr/local/share/proxmenux/oci/instances')
UPDATE_LOCK_ROOT = Path('/run/lock')
_cache = {}
_locks = {}
_generations = {}
_mutex = threading.Lock()
_PATH = re.compile(r'/[A-Za-z0-9_.+/@-]{1,510}\Z')
_NAME = re.compile(r'[A-Za-z0-9][A-Za-z0-9_.+@-]{0,63}\Z')
_VERSION = re.compile(r'v?\d+(?:\.\d+){1,4}(?:[-+._][A-Za-z0-9.-]+)?\Z')
_SYSTEM = {
'ssh', 'sshd', 'cron', 'crond', 'postfix', 'rsyslog', 'syslog', 'syslog-ng',
# Alpine/OpenRC system logging and service supervision are not apps.
'klogd', 'syslogd', 'openrc-user', 'runit', 'runsvdir', 'runsv', 'supervise-daemon',
'dbus', 'dbus-broker', 'systemd', 'udev', 'networking', 'network', 'networkmanager',
'dhcpcd', 'dhclient', 'chrony', 'chronyd', 'ntp', 'ntpd', 'openntpd', 'agetty',
'getty', 'login', 'polkit', 'polkitd', 'accounts-daemon', 'acpid', 'anacron',
'docker', 'containerd', 'containerd-shim', 'podman', 'qemu-guest-agent', 'rngd',
'auditd', 'logrotate', 'local', 'bootmisc', 'hostname', 'hwclock', 'modules', 'master',
'proxmox-boot-sync', 'pve-container', 'pve-firewall', 'pvefw-logger',
'pvedaemon', 'pvestatd', 'pveproxy', 'pvescheduler', 'spiceproxy', 'lxc',
'mount-ro', 'mdev', 'sysctl', 'sysfs', 'devfs', 'cgroups', 'rc', 'rcs', 'swap',
'sleep', 'true', 'false', 'echo', 'cat', 'head', 'tail', 'tee', 'logger',
'apt', 'apt-get', 'dpkg', 'dpkg-query', 'apk', 'unattended-upgrades',
'fstrim', 'fsck', 'fsckd', 'e2scrub', 'e2scrub-all', 'e2scrub-reap',
'irqbalance', 'smartd', 'smartmontools', 'rpcbind', 'nfs-common', 'nfs-server',
'rpc-statd', 'rpc-statd-notify', 'rpc-gssd', 'rpc-svcgssd', 'nftables', 'iptables',
'ip6tables', 'firewalld', 'ufw', 'resolvconf', 'wpa-supplicant', 'modemmanager',
'console-setup', 'keyboard-setup', 'plymouth', 'halt', 'reboot', 'shutdown',
'rc-local', 'ntpsec', 'networkd-dispatcher', 'busybox', 'atd', 'uuidd',
# Common packaged daemons are infrastructure, not a user-managed app.
'nginx', 'apache', 'apache2', 'httpd', 'lighttpd', 'haproxy', 'traefik',
'redis', 'redis-server', 'memcached', 'mysql', 'mysqld', 'mariadb', 'mariadbd',
'postgres', 'postgresql', 'mongod', 'mongodb', 'rabbitmq-server',
'elasticsearch', 'opensearch', 'fail2ban', 'named', 'bind9', 'dnsmasq',
'avahi-daemon', 'smbd', 'nmbd', 'samba', 'vsftpd', 'proftpd', 'cups', 'tor',
}
_RUNTIMES = re.compile(r'(?:python[0-9.]*|node(?:js)?|npm|pnpm|yarn|bun|sh|bash|dash|env|java|ruby|perl)\Z')
_PACKAGE_ROOT_RUNTIMES = {'node', 'nodejs', 'npm', 'pnpm', 'yarn', 'bun'}
_HELPER_ROLES = {
'backup', 'bootstrap', 'bridge', 'cleanup', 'housekeeping', 'maintenance',
'migrate', 'migration', 'restore', 'rotate', 'update', 'updater', 'upgrade',
'watch', 'watchdog', 'worker',
}
_HESTIA_NGINX = '/usr/local/hestia/nginx/sbin/hestia-nginx'
_HESTIA_ROOT = '/usr/local/hestia'
_HESTIA_PANEL_PORT = 8083
class ProbeError(Exception):
"""Safe error code only; guest output must never become an API error."""
def bounded_run(argv, deadline):
"""Cap combined stdout/stderr while reading, and kill/reap the process group."""
remaining = min(PROBE_SECONDS, deadline - time.monotonic())
if remaining <= 0:
raise ProbeError('deadline')
end = time.monotonic() + remaining
try:
proc = subprocess.Popen(argv, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
start_new_session=True)
except OSError:
raise ProbeError('unavailable') from None
outputs = [bytearray(), bytearray()]
try:
with selectors.DefaultSelector() as poll:
for index, stream in enumerate((proc.stdout, proc.stderr)):
os.set_blocking(stream.fileno(), False)
poll.register(stream, selectors.EVENT_READ, index)
while poll.get_map():
wait = end - time.monotonic()
if wait <= 0:
raise ProbeError('timeout')
for key, _ in poll.select(wait):
block = os.read(key.fileobj.fileno(), 4096)
if not block:
poll.unregister(key.fileobj)
continue
if sum(map(len, outputs)) + len(block) > OUTPUT_BYTES:
raise ProbeError('output_limit')
outputs[key.data].extend(block)
try:
proc.wait(timeout=max(0.001, end - time.monotonic()))
except subprocess.TimeoutExpired:
raise ProbeError('timeout') from None
if proc.returncode:
raise ProbeError('read_failed')
return outputs[0].decode('utf-8', errors='strict')
except UnicodeError:
raise ProbeError('invalid_data') from None
finally:
# Also clean up descendants that outlive a successfully exited wrapper.
try:
os.killpg(proc.pid, signal.SIGKILL)
except ProcessLookupError:
pass
proc.wait()
proc.stdout.close()
proc.stderr.close()
# These are fixed read-only scripts, not guest-supplied shell or init scripts.
# awk emits only executable/script paths and WorkingDirectory, never arguments
# or environment values. OpenRC files are parsed as data, never sourced.
SERVICE_SCRIPT = r'''
command -v awk >/dev/null && command -v head >/dev/null || exit 1
n=0
for dir in /etc/systemd/system /usr/lib/systemd/system /lib/systemd/system /etc/init.d; do
# With a merged /usr, /lib is /usr/lib: the same units must not count twice.
[ "$dir" = /lib/systemd/system ] && [ /lib/systemd/system -ef /usr/lib/systemd/system ] && continue
[ ! -d "$dir" ] || [ -r "$dir" ] || exit 1
for f in "$dir"/*; do
[ -f "$f" ] || continue
case "$dir:$f" in /etc/init.d:*) kind=openrc;; *:*.service) kind=systemd;; *) continue;; esac
n=$((n+1)); [ "$n" -le 256 ] || { printf 'LIMIT\n'; exit 0; }
[ -r "$f" ] || exit 1
content=$(timeout -s KILL 1 head -c 16385 "$f") || exit 1
[ "${#content}" -le 16384 ] || { printf 'LIMIT\n'; exit 0; }
printf '%s\n' "$content" | awk -v kind="$kind" -v file="$f" '
function clean(v) {gsub(/^[ \t]+|[ \t]+$/, "", v); gsub(/^["\047]|["\047]$/, "", v); return v}
/^ExecStart=/ {v=substr($0,11); sub(/^[-+!:@]+/, "", v); split(v,a,/[ \t]+/); exe=clean(a[1]); script=clean(a[2]); if(script !~ /^\//) script=""}
/^WorkingDirectory=/ {dir=clean(substr($0,18))}
/^command=/ {exe=clean(substr($0,9))}
/^directory=/ {dir=clean(substr($0,11))}
/^command_args=/ {v=clean(substr($0,14)); split(v,a,/[ \t]+/); script=clean(a[1]); if(script !~ /^\//) script=""}
END {printf "S\t%s\t%s\t%s\t%s\t%s\n", file, kind, exe, script, dir}'
done
done
printf 'END\n'
'''
PROCESS_SCRIPT = r'''
command -v readlink >/dev/null && command -v head >/dev/null || exit 1
n=0
for dir in /proc/[0-9]*; do
[ -d "$dir" ] || continue
n=$((n+1)); [ "$n" -le "$1" ] || { printf 'LIMIT\n'; exit 0; }
# Never inspect Docker/OCI child workloads or read cmdline/environ.
[ -d "$dir" ] || continue
[ -r "$dir/cgroup" ] || exit 1
group=$(timeout -s KILL 1 head -c 8193 "$dir/cgroup") || exit 1
[ "${#group}" -le 8192 ] || { printf 'LIMIT\n'; exit 0; }
case "$group" in *docker*|*libpod*|*kubepods*|*containerd*) continue;; esac
# Kernel threads and vanished processes have no application executable.
[ -L "$dir/exe" ] || continue
exe=$(timeout -s KILL 1 readlink "$dir/exe") || continue
cwd=$(timeout -s KILL 1 readlink "$dir/cwd") || continue
printf 'P\t%s\t%s\t%s\n' "${dir##*/}" "$exe" "$cwd"
done
printf 'END\n'
'''
PORT_SCRIPT = r'''
command -v awk >/dev/null || exit 1
if command -v ss >/dev/null; then
(timeout -s KILL 1 ss -H -lntp || printf 'FAILED\n') | awk -v cap="$1" '
/^FAILED$/ {failed=1; exit}
NR > cap {limited=1; exit}
{port=$4; sub(/^.*:/,"",port); rest=$0
# More than one owner is ambiguous; never choose the first arbitrarily.
if (gsub(/pid=/,"pid=",rest)!=1) next
if (match(rest,/pid=[0-9]+/)) {pid=substr(rest,RSTART+4,RLENGTH-4); printf "L\t%s\t%s\n",pid,port}}
END {print failed ? "FAILED" : (limited ? "LIMIT" : "END")}'
elif command -v netstat >/dev/null; then
(timeout -s KILL 1 netstat -lntp || printf 'FAILED\n') | awk -v cap="$1" '
/^FAILED$/ {failed=1; exit}
/^tcp/ {n++; if(n>cap) {limited=1; exit}; port=$4; sub(/^.*:/,"",port)
pid=$7; sub(/\/.*$/,"",pid); if(pid ~ /^[0-9]+$/) printf "L\t%s\t%s\n",pid,port}
END {print failed ? "FAILED" : (limited ? "LIMIT" : "END")}'
else
# Without ss or netstat there is no listener evidence; a retry would not change it.
printf 'END\n'
fi
'''
HESTIA_PANEL_SCRIPT = r'''
# The generic process collector intentionally stops after a bounded number of
# records and ignores shared listener ownership. Hestia's master/worker pair
# can therefore be outside that generic result. This narrow fallback never
# accepts a name alone: it requires Hestia's exact executable and its panel
# listener on the fixed local port.
[ -x /usr/local/hestia/nginx/sbin/hestia-nginx ] || { printf 'END\n'; exit 0; }
command -v pgrep >/dev/null && command -v readlink >/dev/null && command -v ss >/dev/null && command -v head >/dev/null || exit 1
n=0
for pid in $(pgrep -x hestia-nginx 2>/dev/null | head -n 9); do
case "$pid" in *[!0-9]*|'') continue;; esac
n=$((n+1)); [ "$n" -le 8 ] || { printf 'LIMIT\n'; exit 0; }
exe=$(timeout -s KILL 1 readlink "/proc/$pid/exe") || continue
[ "$exe" = /usr/local/hestia/nginx/sbin/hestia-nginx ] || continue
cwd=$(timeout -s KILL 1 readlink "/proc/$pid/cwd") || continue
if timeout -s KILL 1 ss -H -lntp | awk -v pid="$pid" '
{port=$4; sub(/^.*:/,"",port)
if (port == 8083 && index($0, "pid=" pid ",")) found=1}
END {exit(found ? 0 : 1)}'; then
printf 'H\t%s\t%s\t%s\t8083\n' "$pid" "$exe" "$cwd"
fi
done
printf 'END\n'
'''
FOOTPRINT_SCRIPT = r'''
[ -f "$1" ] && [ -r "$1" ] || exit 1
printf 'FILE\n'
if [ -f "$2/package.json" ]; then
[ -r "$2/package.json" ] || exit 1
timeout -s KILL 1 head -c 8192 "$2/package.json"
fi
'''
MANIFEST_FOOTPRINT_SCRIPT = r'''
command -v head >/dev/null && command -v readlink >/dev/null || exit 1
# Resolve only the operator-owned working directory and its declared parent.
# A release symlink may change the concrete version directory, but it must not
# escape the bounded custom application root supplied by the host.
workdir=$(timeout -s KILL 1 readlink -f "$1") || exit 1
anchor=$(timeout -s KILL 1 readlink -f "$2") || exit 1
parent=$(timeout -s KILL 1 readlink -f "$3") || exit 1
case "$anchor" in "$parent"/*) ;; *) printf 'OUTSIDE\n'; exit 0;; esac
case "$workdir" in "$anchor"|"$anchor"/*) ;; *) printf 'OUTSIDE\n'; exit 0;; esac
for relative in package.json server/package.json; do
candidate="$workdir/$relative"
[ -f "$candidate" ] && [ -r "$candidate" ] || continue
resolved=$(timeout -s KILL 1 readlink -f "$candidate") || exit 1
case "$resolved" in "$workdir"/*) ;; *) printf 'OUTSIDE\n'; exit 0;; esac
printf 'MANIFEST\t%s\n' "$relative"
timeout -s KILL 1 head -c 8192 "$resolved"
exit 0
done
printf 'MISSING\n'
'''
def _path(value):
return bool(isinstance(value, str) and _PATH.fullmatch(value)
and '..' not in value.split('/'))
def _system(name):
raw = name.lower().removesuffix('.service')
stem = raw.split('@')[0]
return (raw.startswith('user@') or stem in _SYSTEM
or (raw.startswith('php') and raw.endswith('-fpm'))
or stem.startswith((
'systemd-', 'dbus-', 'getty', 'containerd-', 'apt-', 'dpkg-', 'e2scrub',
'unattended-', 'ifup', 'ifdown', 'user@', 'btrfs-', 'lvm2-', 'lxc-')))
def _helper(name):
"""Reject service roles that support an application but are not one themselves."""
raw = name.lower().removesuffix('.service')
if raw.endswith('@'):
return True # A systemd template is not a concrete running application.
return any(part in _HELPER_ROLES for part in re.split(r'[._-]+', raw) if part)
def _hestia_panel_process(executable, ports):
"""Recognize only Hestia's own bound control-panel process.
Hestia's generated init unit does not expose an ExecStart that the generic
service parser may treat as an application. Its panel process and port are
stable, local runtime metadata, so this stays narrower than accepting
arbitrary vendor services or a generic web server.
"""
return executable == _HESTIA_NGINX and _HESTIA_PANEL_PORT in ports
def _custom(path):
if not _path(path):
return False
if path.startswith(('/opt/', '/srv/', '/usr/local/')):
return True
# Native apps commonly keep their runtime below /home/<service-user>/<app>.
# Do not accept a home directory itself: a second path component is required.
parts = path.split('/')
return len(parts) >= 4 and parts[1] == 'home' and bool(parts[2]) and bool(parts[3])
def _manifest_bounds(executable, directory):
"""Return a narrow, operator-owned boundary for a runtime manifest probe."""
try:
shared = os.path.commonpath((executable, directory))
except ValueError:
shared = ''
anchor = shared if _custom(shared) else directory
parts = anchor.split('/')
if anchor.startswith(('/opt/', '/srv/')):
parent = '/' + parts[1]
elif anchor.startswith('/usr/local/'):
parent = '/usr/local'
elif len(parts) >= 4 and parts[1] == 'home' and parts[2]:
parent = f'/home/{parts[2]}'
else:
return None
return anchor, parent
def _manifest_footprint(record, run, deadline):
"""Read one of two fixed Node manifest locations without following an escape."""
output = run(['pct', 'exec', str(record['vmid']), '--', 'timeout', '-s', 'KILL', '2',
'sh', '-c', MANIFEST_FOOTPRINT_SCRIPT, 'discovery',
record['root'], record['manifest_anchor'], record['manifest_parent']], deadline)
header, separator, metadata = output.partition('\n')
if header == 'MISSING':
if metadata:
raise ProbeError('invalid_data')
return None, None
if header == 'OUTSIDE':
if metadata:
raise ProbeError('invalid_data')
raise ProbeError('unsafe_manifest_root')
prefix = 'MANIFEST\t'
relative = header.removeprefix(prefix)
if not header.startswith(prefix) or relative not in ('package.json', 'server/package.json'):
raise ProbeError('invalid_data')
if not separator:
raise ProbeError('invalid_data')
candidate_record = dict(record)
candidate_record['path'] = str(Path(record['root']) / relative)
return candidate_record, 'FILE\n' + metadata
def parse_records(text, kind):
lines = text.splitlines()
if not lines or lines[-1] not in ('END', 'LIMIT'):
raise ProbeError('incomplete')
if len(lines) - 1 > MAX_RECORDS:
raise ProbeError('metadata_limit')
records = []
seen_services = set()
for line in lines[:-1]:
parts = line.split('\t')
if kind == 'S' and len(parts) == 6 and parts[0] == 'S':
_, source, manager, exe, script, directory = parts
if not _path(source) or manager not in ('systemd', 'openrc'):
raise ProbeError('invalid_data')
service = Path(source).name.removesuffix('.service')
if service in seen_services:
continue # /etc overrides vendor units; no duplicate installations.
seen_services.add(service)
if (not _NAME.fullmatch(service) or _system(service) or _helper(service)
or not _path(exe)):
continue
runtime_name = Path(exe).name
runtime = bool(_RUNTIMES.fullmatch(runtime_name))
manifest_bounds = None
if runtime and not _path(script):
# A Node/npm wrapper may keep its entry point relative to an
# operator-owned working directory. Only two fixed manifest
# locations can prove that root; no argv, environment, recursive
# search or guest-provided path is ever used.
if runtime_name not in _PACKAGE_ROOT_RUNTIMES or not _custom(directory):
continue
install = str(Path(directory) / 'package.json')
manifest_bounds = _manifest_bounds(exe, directory)
if manifest_bounds is None:
continue
else:
install = script if runtime else exe
if (not _path(install) or _system(Path(install).name)
or install.startswith(('/usr/lib/systemd/', '/lib/systemd/',
'/usr/lib/udev/', '/lib/udev/', '/usr/lib/apt/'))):
continue
# /etc units are operator-owned. Do not infer an application from an
# arbitrary packaged systemd unit: without a package-db probe it is
# impossible to distinguish vendor infrastructure from a user app.
if (manager == 'systemd'
and source.startswith(('/usr/lib/systemd/system/', '/lib/systemd/system/'))
and not _custom(install)):
continue
root = directory if _custom(directory) else str(Path(install).parent)
# Generic runtime without an absolute application script is ambiguous.
if runtime and not _custom(install):
continue
record = {'name': service, 'path': install, 'root': root,
'service': source, 'manager': manager, 'executable': exe}
if manifest_bounds:
record['manifest_anchor'], record['manifest_parent'] = manifest_bounds
records.append(record)
elif kind == 'P' and len(parts) == 4 and parts[0] == 'P':
_, pid, exe, directory = parts
# A process rooted at / is normally too ambiguous for generic
# discovery, but it is valid process metadata for the exact Hestia
# panel signature below. It never becomes a generic candidate.
if not pid.isdigit() or not _path(exe) or (directory != '/' and not _path(directory)):
continue
records.append({'pid': pid, 'executable': exe, 'root': directory})
elif kind == 'L' and len(parts) == 3 and parts[0] == 'L':
_, pid, port = parts
if pid.isdigit() and port.isdigit() and 0 < int(port) < 65536:
records.append({'pid': pid, 'port': int(port)})
else:
raise ProbeError('invalid_data')
elif kind == 'H' and len(parts) == 5 and parts[0] == 'H':
_, pid, exe, directory, port = parts
if (pid.isdigit() and exe == _HESTIA_NGINX
and (directory == '/' or _path(directory))
and port == str(_HESTIA_PANEL_PORT)):
records.append({'pid': pid, 'executable': exe, 'root': directory,
'port': _HESTIA_PANEL_PORT})
else:
raise ProbeError('invalid_data')
else:
raise ProbeError('invalid_data')
return records, lines[-1] == 'LIMIT', len(lines) - 1
def candidate(vmid, record, footprint):
if not footprint.startswith('FILE\n'):
raise ProbeError('invalid_data')
version, metadata_issue = None, None
metadata = footprint[5:].strip()
if metadata:
try:
manifest = json.loads(metadata)
except (ValueError, TypeError):
manifest = None
# Only the whitelisted version field leaves this function. Never retain
# manifest scripts, repository guesses, config or arbitrary extra fields.
if not isinstance(manifest, dict):
metadata_issue = 'invalid_metadata'
manifest = {}
value = manifest.get('version')
if (isinstance(value, str) and len(value) <= 64 and _VERSION.fullmatch(value)
and record['path'].startswith(record['root'].rstrip('/') + '/')):
version = value
identity = 'native-' + hashlib.sha256(
f'{int(vmid)}:{record["path"]}:{record["root"]}'.encode()).hexdigest()[:24]
evidence = [{'kind': 'installation_file', 'value': record['path']}]
if record.get('service'):
evidence.append({'kind': record['manager'], 'value': record['service']})
else:
evidence.extend([{'kind': 'process_executable', 'value': record['process']},
{'kind': 'working_directory',
'value': record.get('process_root', record['root'])}])
ports = sorted(set(record.get('listening_ports') or []))
# Hestia's control panel is a narrow, verified exception to generic port
# inference: its own executable serves the panel on fixed HTTPS port 8083.
# Keep this declaration on the exact runtime signature so an unrelated
# application merely listening on 8083 never receives an HTTPS guess.
hestia_panel = (record['name'] == 'Hestia'
and record['path'] == _HESTIA_NGINX
and record['root'] == _HESTIA_ROOT
and _HESTIA_PANEL_PORT in ports)
evidence.extend({'kind': 'tcp_listener', 'value': str(port)} for port in ports)
return {'slug': identity, 'discovery_id': identity, 'source': 'native_metadata',
'name': record['name'], 'installed_version': version, 'evidence': evidence,
'installation_path': record['path'],
'default_ports': [_HESTIA_PANEL_PORT] if hestia_panel else [],
'listening_ports': ports,
**({'scheme': 'https'} if hestia_panel else {}),
'metadata_issue': metadata_issue,
'tracking_suggestion': ({'installed_via': 'manual', 'installed_version': version,
'detected_version': version, 'detector_verified': True}
if version else None)}
def _guard(vmid, deadline, run):
# The local node path rejects QEMU, missing and remote CTs before pct exec.
path = LOCAL_CONFIG / f'{vmid}.conf'
if not path.is_file():
raise ProbeError('not_local_lxc')
# Presence, including a damaged/in-progress record, excludes generic probes.
try:
os.lstat(OCI_ROOT / str(vmid))
except FileNotFoundError:
pass
except OSError:
raise ProbeError('oci_unknown') from None
else:
raise ProbeError('managed_oci')
try:
with path.open('rb') as handle:
raw = handle.read(OUTPUT_BYTES + 1)
if len(raw) > OUTPUT_BYTES:
raise ProbeError('config_limit')
config = raw.decode('utf-8')
except (OSError, UnicodeError):
raise ProbeError('config_unavailable') from None
if re.search(r'^lock:', config, re.M):
raise ProbeError('guest_busy')
update_lock = UPDATE_LOCK_ROOT / f'proxmenux-lxc-update-{vmid}.lock'
try:
fd = os.open(update_lock, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK)
with os.fdopen(fd, 'rb') as handle:
info = os.fstat(handle.fileno())
if not stat.S_ISREG(info.st_mode):
raise ProbeError('lock_unavailable')
if info.st_size:
raise ProbeError('guest_busy')
try:
fcntl.flock(handle, fcntl.LOCK_SH | fcntl.LOCK_NB)
except BlockingIOError:
raise ProbeError('guest_busy') from None
finally:
fcntl.flock(handle, fcntl.LOCK_UN)
except FileNotFoundError:
pass
except OSError:
raise ProbeError('lock_unavailable') from None
if re.search(r'^ostype:\s*unmanaged\s*$', config, re.M):
raise ProbeError('unsupported_guest')
if not re.search(r'^ostype:\s*(?:debian|ubuntu|alpine|archlinux|centos|fedora|opensuse|gentoo)\s*$', config, re.M):
raise ProbeError('unsupported_guest')
status = run(['pct', 'status', str(vmid)], deadline).strip()
if status != 'status: running':
raise ProbeError('guest_stopped' if status == 'status: stopped' else 'status_unknown')
@contextmanager
def _scan_reservation(vmid):
"""Exclude cooperating updates/control tasks for this bounded scan only.
Keep an empty lock inode after first use, as the update runner does.
Never truncate a pending control-task reservation or unlink its inode.
"""
path = UPDATE_LOCK_ROOT / f'proxmenux-lxc-update-{vmid}.lock'
fd = None
try:
fd = os.open(path, os.O_RDWR | os.O_CREAT | os.O_NOFOLLOW | os.O_NONBLOCK, 0o600)
if not stat.S_ISREG(os.fstat(fd).st_mode):
raise ProbeError('lock_unavailable')
try:
fcntl.flock(fd, fcntl.LOCK_SH | fcntl.LOCK_NB)
except BlockingIOError:
raise ProbeError('guest_busy') from None
if os.fstat(fd).st_size:
raise ProbeError('guest_busy')
yield
except OSError:
raise ProbeError('lock_unavailable') from None
finally:
if fd is not None:
os.close(fd)
def scan(vmid, run=bounded_run):
deadline = time.monotonic() + TOTAL_SECONDS
_guard(vmid, deadline, run)
with _scan_reservation(vmid):
result = _scan_metadata(vmid, deadline, run)
# External PVE lifecycle operations do not share our flock. Fail closed
# if the guest stopped, changed ownership or became locked meanwhile.
_guard(vmid, deadline, run)
return result
def _scan_metadata(vmid, deadline, run):
records, issues, consumed, processes, listeners, hestia_panels = [], [], 0, [], [], []
for kind, script in (('S', SERVICE_SCRIPT), ('P', PROCESS_SCRIPT), ('L', PORT_SCRIPT)):
try:
if consumed >= MAX_RECORDS:
issues.append('metadata_limit')
break
parsed, limited, count = parse_records(run(
['pct', 'exec', str(vmid), '--', 'timeout', '-s', 'KILL', '2',
'sh', '-c', script, 'discovery', str(MAX_RECORDS - consumed)], deadline), kind)
if consumed + count > MAX_RECORDS:
issues.append('metadata_limit')
parsed = parsed[:MAX_RECORDS - consumed]
consumed = min(MAX_RECORDS, consumed + count)
# The entire collector, not each metadata source, has 256 records.
capacity = MAX_RECORDS - len(records)
if kind == 'S':
records.extend(parsed[:capacity])
elif kind == 'P':
processes = parsed[:capacity]
else:
listeners = parsed[:capacity]
if limited or len(parsed) > capacity:
issues.append('metadata_limit')
except ProbeError as exc:
issues.append(str(exc))
try:
# This is outside the generic record budget: it returns only an exact,
# fixed Hestia signature, not additional arbitrary process metadata.
hestia_panels, limited, _ = parse_records(run(
['pct', 'exec', str(vmid), '--', 'timeout', '-s', 'KILL', '2',
'sh', '-c', HESTIA_PANEL_SCRIPT, 'discovery'], deadline), 'H')
if limited:
issues.append('metadata_limit')
except ProbeError as exc:
issues.append(str(exc))
for process in processes:
exe, directory = process['executable'], process['root']
related = [record for record in records if record['executable'] == exe and (
not _RUNTIMES.fullmatch(Path(exe).name) or record['root'] == directory)]
ports = [entry['port'] for entry in listeners if entry['pid'] == process['pid']]
if len(related) == 1:
related[0].setdefault('listening_ports', []).extend(ports)
elif not related and _hestia_panel_process(exe, ports):
records.append({'name': 'Hestia', 'path': exe, 'root': _HESTIA_ROOT,
'process': exe, 'process_root': directory, 'executable': exe,
'listening_ports': ports})
elif not related and (_custom(exe) and _custom(directory)
and not _system(Path(exe).name)
and not _helper(Path(exe).name)
and not _RUNTIMES.fullmatch(Path(exe).name)
and exe.startswith(directory.rstrip('/') + '/')
and _NAME.fullmatch(Path(exe).name)):
records.append({'name': Path(exe).name, 'path': exe, 'root': directory,
'process': exe, 'executable': exe, 'listening_ports': ports})
for panel in hestia_panels:
records.append({'name': 'Hestia', 'path': panel['executable'], 'root': _HESTIA_ROOT,
'process': panel['executable'], 'process_root': panel['root'],
'executable': panel['executable'],
'listening_ports': [panel['port']]})
items, seen = [], set()
for record in records:
key = (record['path'], record['root'])
if key in seen:
continue
seen.add(key)
if len(items) >= MAX_CANDIDATES:
issues.append('candidate_limit')
break
try:
probe_record = dict(record)
probe_record['vmid'] = vmid
if 'manifest_anchor' in record:
probe_record, footprint = _manifest_footprint(probe_record, run, deadline)
if probe_record is None:
continue
else:
footprint = run(['pct', 'exec', str(vmid), '--', 'timeout', '-s', 'KILL', '2',
'sh', '-c', FOOTPRINT_SCRIPT,
'discovery', record['path'], record['root']], deadline)
item = candidate(vmid, probe_record, footprint)
items.append(item)
if item['metadata_issue']:
issues.append(item['metadata_issue'])
except ProbeError as exc:
issues.append(str(exc))
if str(exc) == 'deadline':
break
return {'vmid': vmid, 'status': 'partial' if issues else 'complete',
'candidates': items, 'issues': sorted(set(issues)), 'stale': False,
'scanned_at': time.time()}
def cached(vmid):
with _mutex:
value = copy.deepcopy(_cache.get(int(vmid)))
if value is None:
return {'vmid': int(vmid), 'status': 'not_scanned', 'candidates': [], 'stale': False}
if time.time() - value['scanned_at'] >= CACHE_SECONDS:
value['stale'] = True
return value
def mark_stale(vmid):
"""Lifecycle events retire freshness, never collect new guest metadata."""
with _mutex:
_generations[int(vmid)] = _generations.get(int(vmid), 0) + 1
if int(vmid) in _cache:
_cache[int(vmid)]['stale'] = True
def discover(vmid):
vmid = int(vmid)
with _mutex:
lock = _locks.setdefault(vmid, threading.Lock())
if not lock.acquire(blocking=False):
return {**cached(vmid), 'status': 'busy', 'stale': True}
try:
with _mutex:
generation = _generations.get(vmid, 0)
try:
result = scan(vmid)
except ProbeError as exc:
result = {**cached(vmid), 'status': 'error', 'issues': [str(exc)], 'stale': True}
result.setdefault('scanned_at', time.time())
# Comparing the generation and publishing must be atomic with lifecycle
# invalidation, including the first scan when no cache exists yet.
with _mutex:
previous = copy.deepcopy(_cache.get(vmid))
if _generations.get(vmid, 0) != generation:
result = {**(previous or {'vmid': vmid, 'candidates': [],
'scanned_at': time.time()}),
'status': 'error', 'issues': ['guest_changed'], 'stale': True}
elif result['status'] != 'complete' and previous and previous['candidates']:
# Partial output cannot prove that previously seen apps disappeared.
ids = {item['discovery_id'] for item in result['candidates']}
result['candidates'].extend(item for item in previous['candidates']
if item['discovery_id'] not in ids)
result['candidates'] = result['candidates'][:MAX_CANDIDATES]
result['stale'] = True
_cache[vmid] = copy.deepcopy(result)
return result
finally:
lock.release()
def filter_candidates(result, suggestions, apps):
"""Pure composition: never run catalog probes, load or write registries."""
known = [*apps, *(suggestions.get('extras') or []),
*(suggestions.get('docker_workloads') or [])]
if suggestions.get('helper_slug'):
known.append({'name': suggestions.get('name_suggestion'),
'helper_slug': suggestions['helper_slug'],
'tracking_suggestion': suggestions.get('tracking_suggestion')})
identities = {a.get('discovery_id') for a in known if a.get('discovery_id')}
names, paths = set(), set()
for app in known:
for field in ('name', 'slug', 'helper_slug', 'package'):
if app.get(field):
names.add(str(app[field]).casefold())
detector = app.get('tracking_suggestion') or app
for field in ('binary_path', 'file_path'):
if detector.get(field):
paths.add(detector[field])
items = [item for item in result['candidates']
if item['discovery_id'] not in identities
and item['name'].casefold() not in names
and item['installation_path'] not in paths]
return {**result, 'candidates': items}
+1 -1
View File
@@ -1564,7 +1564,7 @@ class AppriseChannel(NotificationChannel):
return apprise_mod.NotifyType.FAILURE return apprise_mod.NotifyType.FAILURE
if sev == 'WARNING': if sev == 'WARNING':
return apprise_mod.NotifyType.WARNING return apprise_mod.NotifyType.WARNING
if sev == 'SUCCESS': if sev in ('SUCCESS', 'OK'):
return apprise_mod.NotifyType.SUCCESS return apprise_mod.NotifyType.SUCCESS
return apprise_mod.NotifyType.INFO return apprise_mod.NotifyType.INFO
+7
View File
@@ -3850,6 +3850,13 @@ class PollingCollector:
return return
self._last_managed_check = now self._last_managed_check = now
try:
from pbs_manager import is_pbs_host
if is_pbs_host():
return # a Proxmox Backup Server has no guests or integrations to track
except Exception:
pass
try: try:
import managed_installs import managed_installs
except Exception: except Exception:
+3 -1
View File
@@ -3335,6 +3335,7 @@ def _ai_cache_key(title, body, ai_config, detail_level, use_emojis):
str(ai_config.get('ai_provider', '')), '\x1f', str(ai_config.get('ai_provider', '')), '\x1f',
str(ai_config.get('ai_model', '')), '\x1f', str(ai_config.get('ai_model', '')), '\x1f',
str(ai_config.get('ai_language', '')), '\x1f', str(ai_config.get('ai_language', '')), '\x1f',
str(ai_config.get('channel_type', '')).lower(), '\x1f',
detail_level, '\x1f', '1' if use_emojis else '0', detail_level, '\x1f', '1' if use_emojis else '0',
] ]
return _hash_ai_cache.sha256(''.join(parts).encode('utf-8', 'replace')).hexdigest() return _hash_ai_cache.sha256(''.join(parts).encode('utf-8', 'replace')).hexdigest()
@@ -3428,7 +3429,8 @@ def format_with_ai_full(title: str, body: str, severity: str,
channel_type = ai_config.get('channel_type', '').lower() channel_type = ai_config.get('channel_type', '').lower()
is_email = channel_type == 'email' is_email = channel_type == 'email'
if is_email and detail_level == 'detailed' and body and len(body) > 50: if (is_email and detail_level == 'detailed' and body and len(body) > 50
and result_body != body):
# Only append if original has substantial content # Only append if original has substantial content
result_body += "\n\n" + "-" * 40 + "\n" result_body += "\n\n" + "-" * 40 + "\n"
result_body += "Original message:\n" result_body += "Original message:\n"
+288
View File
@@ -0,0 +1,288 @@
"""Local PBS maintenance information and remote, read-only PBS monitoring."""
from concurrent.futures import ThreadPoolExecutor
import hashlib
import hmac
import http.client
import importlib.util
import json
import os
from pathlib import Path
import re
import secrets
import shutil
import ssl
import subprocess
import tempfile
import threading
import time
from urllib.parse import urlsplit
import auth_manager
try:
from proxmox_platform import detect_platform
except ModuleNotFoundError:
# Development checkout; AppImage bundles the canonical shared detector.
_path = Path(__file__).resolve().parents[2] / "scripts/global/proxmox_platform.py"
_spec = importlib.util.spec_from_file_location("proxmox_platform", _path)
_platform_module = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(_platform_module)
detect_platform = _platform_module.detect_platform
_lock = threading.RLock()
_platform_cache = (0, {})
_status_cache = {}
PBS_SERVICES = ("proxmox-backup", "proxmox-backup-proxy")
MAX_RESPONSE = 4 * 1024 * 1024
class PBSError(ValueError):
pass
def get_platform():
global _platform_cache
with _lock:
if _platform_cache[0] <= time.monotonic():
_platform_cache = (time.monotonic() + 60, detect_platform())
return dict(_platform_cache[1])
def is_pbs_host():
return get_platform().get("product") == "pbs"
def _servers_path():
return auth_manager.CONFIG_DIR / "pbs_servers.json"
def _load_servers():
try:
data = json.loads(_servers_path().read_text())
if not isinstance(data, list) or not all(isinstance(row, dict) for row in data):
raise ValueError()
return data
except FileNotFoundError:
return []
except (OSError, ValueError) as exc:
raise PBSError("Could not read the PBS server configuration.") from exc
def _save_servers(servers):
auth_manager.ensure_config_dir()
descriptor, temporary = tempfile.mkstemp(prefix=".pbs-servers-", dir=auth_manager.CONFIG_DIR)
try:
with os.fdopen(descriptor, "w") as handle:
os.fchmod(handle.fileno(), 0o600)
json.dump(servers, handle, indent=2)
os.replace(temporary, _servers_path())
finally:
if os.path.exists(temporary):
os.unlink(temporary)
_status_cache.clear()
def public_servers():
fields = ("id", "name", "url", "token_id", "fingerprint")
with _lock:
return [{**{key: row.get(key, "") for key in fields}, "has_token": bool(row.get("token_secret"))}
for row in _load_servers()]
def _validate_server(data, previous=None):
if not isinstance(data, dict):
raise PBSError("Invalid PBS server configuration.")
previous = previous or {}
row = {field: data.get(field, previous.get(field, ""))
for field in ("name", "url", "token_id", "token_secret", "fingerprint")}
if any(not isinstance(value, str) or len(value) > 4096 for value in row.values()):
raise PBSError("Invalid PBS connection field.")
row["name"] = row["name"].strip()
row["url"] = row["url"].strip().rstrip("/")
try:
parts = urlsplit(row["url"])
parts.port
if (parts.scheme != "https" or not parts.hostname or parts.username or parts.password
or parts.query or parts.fragment):
raise ValueError()
except ValueError as exc:
raise PBSError("Use an HTTPS PBS URL without embedded credentials, query or fragment.") from exc
if not row["name"] or len(row["name"]) > 128:
raise PBSError("A server name of at most 128 characters is required.")
row["token_id"] = row["token_id"].strip()
if not re.fullmatch(r"[^\s:!]+@[^\s:!]+![^\s:!]+", row["token_id"]):
raise PBSError("Enter a PBS API token ID in user@realm!token format.")
if (not row["token_secret"] or not row["token_secret"].isascii()
or any(char.isspace() or ord(char) < 32 for char in row["token_secret"])):
raise PBSError("A valid API token secret is required.")
if not row["token_id"].isascii():
raise PBSError("Invalid API token ID.")
fingerprint = row["fingerprint"].strip().lower().replace(":", "")
if fingerprint and not re.fullmatch("[a-f0-9]{64}", fingerprint):
raise PBSError("The certificate fingerprint must be SHA256 (64 hex characters).")
row["fingerprint"] = fingerprint
return row
def _remote_get(server, endpoint):
"""Validate the TLS peer BEFORE transmitting the PBS token (no redirects)."""
parts = urlsplit(server["url"])
fingerprint = server.get("fingerprint")
if fingerprint:
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE # Authenticated by the explicit pin below.
else:
context = ssl.create_default_context()
connection = http.client.HTTPSConnection(parts.hostname, parts.port or 443,
timeout=5, context=context)
try:
connection.connect()
if fingerprint:
actual = hashlib.sha256(connection.sock.getpeercert(binary_form=True)).hexdigest()
if not hmac.compare_digest(actual, fingerprint):
raise PBSError("PBS certificate fingerprint does not match. No credentials were sent.")
path = parts.path.rstrip("/") + "/api2/json" + endpoint
connection.request("GET", path, headers={
"Accept": "application/json",
"Authorization": "PBSAPIToken=" + server["token_id"] + ":" + server["token_secret"],
})
response = connection.getresponse()
content = response.read(MAX_RESPONSE + 1)
if response.status in (401, 403):
raise PBSError("PBS rejected the API token or its permissions. Check both the user and token ACLs.")
if response.status != 200 or len(content) > MAX_RESPONSE:
raise PBSError("PBS returned an invalid response. Redirects are not followed.")
document = json.loads(content)
if not isinstance(document, dict) or "data" not in document:
raise PBSError("PBS returned an invalid JSON document.")
return document["data"]
except PBSError:
raise
except (OSError, http.client.HTTPException, ValueError) as exc:
raise PBSError("Could not connect to PBS over verified HTTPS. Check the address, certificate and service.") from exc
finally:
connection.close()
def save_server(data):
identifier = data.get("id") if isinstance(data, dict) else None
if identifier is not None and not re.fullmatch(r"[A-Za-z0-9_-]{1,64}", str(identifier)):
raise PBSError("Invalid PBS server ID.")
with _lock:
previous = next((row for row in _load_servers() if row["id"] == identifier), None)
row = _validate_server(data, previous)
# /version may be public; validate credentials on the protected status API.
usage = _remote_get(row, "/status/datastore-usage")
if not isinstance(usage, list):
raise PBSError("PBS returned an invalid datastore list.")
row.update(id=identifier or "pbs-" + secrets.token_hex(6), revision=secrets.token_hex(16))
with _lock:
servers = _load_servers()
if previous is None and len(servers) >= 32:
raise PBSError("At most 32 PBS connections may be configured.")
servers = [existing for existing in servers if existing["id"] != row["id"]]
_save_servers([*servers, row])
return row["id"]
def delete_server(identifier):
with _lock:
servers = _load_servers()
remaining = [row for row in servers if row["id"] != identifier]
if len(remaining) == len(servers):
raise PBSError("PBS server not found.")
_save_servers(remaining)
def _cli(*arguments):
try:
result = subprocess.run(["proxmox-backup-manager", *arguments, "--output-format", "json"],
check=True, text=True, capture_output=True, timeout=8)
data = json.loads(result.stdout)
if not isinstance(data, list) or not all(isinstance(row, dict) for row in data):
raise ValueError()
return data
except (OSError, subprocess.SubprocessError, ValueError) as exc:
raise PBSError("Could not read PBS " + " ".join(arguments[:2]) + ". Check its service and permissions.") from exc
def _local_datastores():
stores = _cli("datastore", "list")
result = []
for store in stores:
row = {"name": store.get("name"), "path": store.get("path"),
"maintenance_mode": store.get("maintenance-mode"), "total": None, "used": None, "avail": None}
path = store.get("path")
# A removable datastore directory may survive unmounting. Do not report
# its underlying root filesystem as the missing datastore's capacity.
inactive = store.get("backing-device") and not (path and os.path.ismount(path))
if not path or inactive or store.get("maintenance-mode"):
row["error"] = "Datastore is offline, in maintenance or has no mounted backing device."
else:
try:
capacity = shutil.disk_usage(path)
row.update(total=capacity.total, used=capacity.used, avail=capacity.free)
except OSError:
row["error"] = "Datastore filesystem is not available."
result.append(row)
return result
def _local_services():
result = {}
for service in PBS_SERVICES:
try:
check = subprocess.run(["systemctl", "is-active", service], capture_output=True, text=True, timeout=3)
result[service] = check.stdout.strip() if check.returncode == 0 else "inactive"
except (OSError, subprocess.SubprocessError):
result[service] = "unknown"
return result
def get_status(identifier):
with _lock:
cached = _status_cache.get(identifier)
if cached and cached[0] > time.monotonic():
return cached[1]
errors = {}
if identifier == "local":
if not is_pbs_host():
raise PBSError("The local host is not a PBS server.")
name, version = "Local PBS", get_platform().get("version")
services = _local_services()
operations = {"datastores": _local_datastores,
"tasks": lambda: _cli("task", "list", "--all", "true", "--limit", "20"),
"sync": lambda: _cli("sync-job", "list"),
"verify": lambda: _cli("verify-job", "list"),
"prune": lambda: _cli("prune-job", "list")}
else:
with _lock:
server = next((row for row in _load_servers() if row["id"] == identifier), None)
if not server:
raise PBSError("PBS server not found.")
metadata = _remote_get(server, "/version")
name, version = server["name"], metadata.get("version") if isinstance(metadata, dict) else None
services = {}
endpoints = {"datastores": "/status/datastore-usage", "tasks": "/nodes/localhost/tasks?limit=20",
"sync": "/admin/sync", "verify": "/admin/verify", "prune": "/admin/prune"}
operations = {key: (lambda endpoint=path: _remote_get(server, endpoint)) for key, path in endpoints.items()}
result = {"server": identifier, "name": name, "version": version, "services": services,
"fetched_at": int(time.time()), "errors": errors}
with ThreadPoolExecutor(max_workers=5) as executor:
futures = {key: executor.submit(operation) for key, operation in operations.items()}
for key, future in futures.items():
try:
data = future.result()
if not isinstance(data, list) or not all(isinstance(row, dict) for row in data):
raise PBSError("PBS returned an invalid list.")
result[key] = [{**row, "name": row.get("store", row.get("name"))} for row in data] if key == "datastores" else data
except PBSError as exc:
result[key] = None # Unknown/forbidden is never presented as zero.
errors[key] = str(exc)
with _lock:
if len(_status_cache) >= 33:
_status_cache.clear()
_status_cache[identifier] = (time.monotonic() + 15, result)
return result
@@ -26,18 +26,18 @@ class FwbrUdevRuleOrderTests(unittest.TestCase):
def test_auto_post_install_uses_late_rule_and_removes_legacy_rule(self): def test_auto_post_install_uses_late_rule_and_removes_legacy_rule(self):
source = network_optimization_block(AUTO_POST_INSTALL) source = network_optimization_block(AUTO_POST_INSTALL)
self.assertIn('local FUNC_VERSION="1.2"', source) self.assertIn('local FUNC_VERSION="1.2"', source)
self.assertIn(f"rm -f /etc/udev/rules.d/{OLD_RULE}", source) self.assertIn(f"pmx_remove_file /etc/udev/rules.d/{OLD_RULE}", source)
self.assertIn(f"cat > /etc/udev/rules.d/{NEW_RULE}", source) self.assertIn(f"pmx_write_file /etc/udev/rules.d/{NEW_RULE}", source)
self.assertIn(f"chmod 0644 /etc/udev/rules.d/{NEW_RULE}", source) self.assertIn(f"chmod 0644 /etc/udev/rules.d/{NEW_RULE}", source)
self.assertNotIn(f"cat > /etc/udev/rules.d/{OLD_RULE}", source) self.assertNotIn(f"pmx_write_file /etc/udev/rules.d/{OLD_RULE}", source)
def test_customizable_post_install_uses_late_rule_and_removes_legacy_rule(self): def test_customizable_post_install_uses_late_rule_and_removes_legacy_rule(self):
source = network_optimization_block(CUSTOM_POST_INSTALL) source = network_optimization_block(CUSTOM_POST_INSTALL)
self.assertIn('local FUNC_VERSION="1.2"', source) self.assertIn('local FUNC_VERSION="1.2"', source)
self.assertIn(f"rm -f /etc/udev/rules.d/{OLD_RULE}", source) self.assertIn(f"pmx_remove_file /etc/udev/rules.d/{OLD_RULE}", source)
self.assertIn(f"cat > /etc/udev/rules.d/{NEW_RULE}", source) self.assertIn(f"pmx_write_file /etc/udev/rules.d/{NEW_RULE}", source)
self.assertIn(f"chmod 0644 /etc/udev/rules.d/{NEW_RULE}", source) self.assertIn(f"chmod 0644 /etc/udev/rules.d/{NEW_RULE}", source)
self.assertNotIn(f"cat > /etc/udev/rules.d/{OLD_RULE}", source) self.assertNotIn(f"pmx_write_file /etc/udev/rules.d/{OLD_RULE}", source)
def test_uninstall_removes_new_and_legacy_rule_names(self): def test_uninstall_removes_new_and_legacy_rule_names(self):
source = UNINSTALL_TOOLS.read_text() source = UNINSTALL_TOOLS.read_text()
@@ -0,0 +1,150 @@
import json
from pathlib import Path
import sys
import tempfile
from types import SimpleNamespace
import unittest
from unittest import mock
ROOT = Path(__file__).resolve().parents[3]
sys.path.insert(0, str(ROOT / "AppImage/scripts"))
import monitor_links as links
class MonitorLinksTests(unittest.TestCase):
def setUp(self):
temporary = tempfile.TemporaryDirectory()
self.addCleanup(temporary.cleanup)
self.root = Path(temporary.name)
storage = self.root / "storage.cfg"
storage.write_text("dir: local\n\tpath /var/lib/vz\n\npbs: Lab\n\tdatastore store1\n\tserver 10.0.0.49\n\n"
"pbs: Cloud\n\tdatastore x\n\tserver pbs.example.com\n\tport 8443\n")
patches = [
mock.patch.object(links, "LINKS_FILE", self.root / "links.json"),
mock.patch.object(links, "SSL_CONFIG_FILE", self.root / "ssl.json"),
mock.patch.object(links, "STORAGE_CFG", storage),
mock.patch.object(links.pbs_manager, "get_platform", return_value={"product": "pve", "hostname": "amd"}),
mock.patch.object(links.pbs_manager, "public_servers", return_value=[]),
]
for patch in patches:
patch.start()
self.addCleanup(patch.stop)
links._probe_cache.clear()
cluster = [{"type": "cluster", "name": "Casa"},
{"type": "node", "name": "amd", "ip": "10.0.0.55", "local": 1},
{"type": "node", "name": "constructor", "ip": "10.0.0.50", "local": 0}]
self.run_patch = mock.patch.object(links.subprocess, "run",
return_value=SimpleNamespace(returncode=0, stdout=json.dumps(cluster)))
self.run_patch.start()
self.addCleanup(self.run_patch.stop)
def test_normalize_url_keeps_only_the_origin_and_rejects_credentials(self):
self.assertEqual(links.normalize_url("192.168.0.49:8008/dashboard?x=1"), "https://192.168.0.49:8008")
self.assertEqual(links.normalize_url("http://pve.lan"), "http://pve.lan")
for bad in ("", "ftp://host", "https://user:pass@host", "https://host:99999", "javascript:alert(1)"):
with self.subTest(bad=bad), self.assertRaises(links.LinkError):
links.normalize_url(bad)
def test_discovers_other_cluster_nodes_and_pbs_without_the_local_node(self):
probed = []
with mock.patch.object(links, "_probe", side_effect=lambda url: probed.append(url) or url.endswith("10.0.0.50:8008")), \
mock.patch.object(links, "_is_private", side_effect=lambda host: host.startswith("10.")):
result = links.list_links()
by_key = {row["key"]: row for row in result["links"]}
self.assertEqual(result["self"]["name"], "amd")
self.assertNotIn("cluster:amd", by_key)
self.assertEqual(by_key["cluster:constructor"]["status"], "online")
self.assertEqual(by_key["pbs:10.0.0.49"]["native_url"], "https://10.0.0.49:8007")
self.assertEqual(by_key["pbs:10.0.0.49"]["status"], "offline")
# A public PBS is listed with its own interface but never probed unasked.
self.assertEqual(by_key["pbs:pbs.example.com"]["status"], "unknown")
self.assertEqual(by_key["pbs:pbs.example.com"]["native_url"], "https://pbs.example.com:8443")
self.assertFalse(any("example.com" in url for url in probed))
def test_pbs_host_lists_only_manual_entries(self):
links.pbs_manager.get_platform.return_value = {"product": "pbs", "hostname": "pbs-lab"}
links.add_link({"name": "amd", "url": "https://10.0.0.55:8008", "product": "pve"})
with mock.patch.object(links, "_probe", return_value=True):
result = links.list_links()
self.assertEqual([row["name"] for row in result["links"]], ["amd"])
self.links_run_not_called()
def links_run_not_called(self):
self.assertFalse(any(call.args and call.args[0][:1] == ["pvesh"] for call in links.subprocess.run.call_args_list))
def test_manual_entries_overrides_and_hidden_detected_nodes(self):
key = links.add_link({"name": "lab", "url": "10.0.0.46:8008", "product": "pve"})
with self.assertRaises(links.LinkError):
links.add_link({"url": "https://10.0.0.46:8008"})
links.update_link("cluster:constructor", {"url": "https://constructor.lan:8008"})
links.update_link("pbs:10.0.0.49", {"hidden": True})
with mock.patch.object(links, "_probe", return_value=False), \
mock.patch.object(links, "_is_private", return_value=True):
visible = {row["key"]: row for row in links.list_links()["links"]}
everything = {row["key"]: row for row in links.list_links(include_hidden=True)["links"]}
self.assertEqual(visible[key]["url"], "https://10.0.0.46:8008")
self.assertEqual(visible["cluster:constructor"]["url"], "https://constructor.lan:8008")
self.assertNotIn("pbs:10.0.0.49", visible)
self.assertTrue(everything["pbs:10.0.0.49"]["hidden"])
with self.assertRaises(links.LinkError):
links.delete_link("cluster:constructor")
links.delete_link(key)
self.assertEqual(json.loads((self.root / "links.json").read_text())["manual"], [])
self.assertEqual((self.root / "links.json").stat().st_mode & 0o777, 0o600)
def test_cached_list_is_served_without_new_probes_and_renewed_when_stale(self):
links._snapshot.update(at=0.0, data=None)
self.addCleanup(links._snapshot.update, at=0.0, data=None)
links.update_link("pbs:10.0.0.49", {"hidden": True})
with mock.patch.object(links, "_probe", return_value=True) as probe, \
mock.patch.object(links, "_is_private", return_value=True):
first = links.cached_links()
calls = probe.call_count
again = links.cached_links()
self.assertEqual(probe.call_count, calls)
self.assertEqual(first, again)
self.assertNotIn("pbs:10.0.0.49", [row["key"] for row in first["links"]])
self.assertIn("pbs:10.0.0.49", [row["key"] for row in links.cached_links(include_hidden=True)["links"]])
links._snapshot["at"] -= links.PROBE_TTL + 1
with mock.patch.object(links.threading, "Thread") as thread:
stale = links.cached_links()
self.assertEqual(stale, first)
thread.assert_called_once()
def test_unknown_keys_are_rejected(self):
for key in ("other:x", "cluster:../../etc", "manual:nope"):
with self.subTest(key=key), self.assertRaises(links.LinkError):
links.update_link(key, {"hidden": True})
def test_probe_sends_no_credentials_and_needs_a_monitor_answer(self):
seen = []
class Response:
status = 200
def __init__(self, body):
self.body = body
def read(self, _size):
return self.body
def __enter__(self):
return self
def __exit__(self, *_):
return False
def urlopen(request, timeout, context):
seen.append(request)
return Response(b'{"auth_enabled": true}' if "monitor" in request.full_url else b"<html>")
with mock.patch.object(links.urllib.request, "urlopen", side_effect=urlopen):
self.assertTrue(links._probe("https://monitor:8008"))
self.assertFalse(links._probe("https://other:8008"))
self.assertTrue(all(request.full_url.endswith("/api/auth/status") for request in seen))
self.assertTrue(all("Authorization" not in request.headers for request in seen))
if __name__ == "__main__":
unittest.main()
@@ -0,0 +1,785 @@
"""Host-independent discovery fixtures; no real pct, guest, auth or registry."""
import ast
import copy
import fcntl
from functools import wraps
import json
from pathlib import Path
import subprocess
import sys
import tempfile
import threading
import time
import unittest
from unittest.mock import Mock, patch
SCRIPTS = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(SCRIPTS))
import native_app_discovery as native
import lxc_apps
def service(name='handmade', exe='/opt/handmade/server', directory='/opt/handmade', manager='systemd', script='', source=None):
path = source or ('/etc/systemd/system/' + name + '.service' if manager == 'systemd'
else '/etc/init.d/' + name)
return '\t'.join(['S', path, manager, exe, script, directory]) + '\nEND\n'
class DiscoveryFixtures(unittest.TestCase):
def setUp(self):
self.temp = tempfile.TemporaryDirectory()
self.addCleanup(self.temp.cleanup)
self.root = Path(self.temp.name)
self.config = self.root / 'lxc'
self.config.mkdir()
(self.config / '101.conf').write_text('ostype: debian\n')
self.oci = self.root / 'oci'
self.oci.mkdir()
for context in (patch.object(native, 'LOCAL_CONFIG', self.config),
patch.object(native, 'OCI_ROOT', self.oci),
patch.object(native, 'UPDATE_LOCK_ROOT', self.root)):
context.start()
self.addCleanup(context.stop)
native._cache.clear()
native._locks.clear()
native._generations.clear()
self.calls = []
def runner(self, services=None, processes='END\n', footprint='FILE\n', manifest=None,
status='status: running', ports='END\n', hestia_panels='END\n'):
def run(argv, deadline):
self.calls.append(argv)
self.assertLessEqual(deadline - time.monotonic(), native.TOTAL_SECONDS)
if argv[1] == 'status':
return status
self.assertEqual(argv[:6], ['pct', 'exec', '101', '--', 'timeout', '-s'])
self.assertEqual(argv[6:9], ['KILL', '2', 'sh'])
script = argv[10]
if script == native.SERVICE_SCRIPT:
return services if services is not None else service()
if script == native.PROCESS_SCRIPT:
return processes
if script == native.PORT_SCRIPT:
return ports
if script == native.HESTIA_PANEL_SCRIPT:
return hestia_panels
if script == native.MANIFEST_FOOTPRINT_SCRIPT:
if manifest is not None:
return manifest
return 'MANIFEST\tpackage.json\n' + footprint.removeprefix('FILE\n')
if script == native.FOOTPRINT_SCRIPT:
return footprint
self.fail('Unexpected probe')
return run
def test_unknown_version_is_present_with_evidence_and_no_capabilities(self):
result = native.scan(101, self.runner())
self.assertEqual(result['status'], 'complete')
item, = result['candidates']
self.assertIsNone(item['installed_version'])
self.assertIsNone(item['tracking_suggestion'])
self.assertEqual(item['default_ports'], [])
self.assertEqual({e['kind'] for e in item['evidence']}, {'installation_file', 'systemd'})
for field in ('helper_slug', 'repo', 'update_command', 'binary_path', 'upstream_type'):
self.assertNotIn(field, item)
def test_openrc_node_script_known_version_is_manual_and_secret_free(self):
result = native.scan(101, self.runner(
service(exe='/usr/bin/node', script='/opt/handmade/server.js', manager='openrc'),
footprint='FILE\n' + json.dumps({'version': '1.2.3', 'scripts': {'start': 'SECRET'}, 'env': 'PASSWORD'})))
item, = result['candidates']
self.assertEqual(item['installed_version'], '1.2.3')
self.assertEqual(item['tracking_suggestion']['installed_via'], 'manual')
self.assertNotIn('SECRET', json.dumps(result))
self.assertNotIn('PASSWORD', json.dumps(result))
self.assertFalse(any('systemctl' in arg for call in self.calls for arg in call))
def test_inactive_service_and_packaged_application_are_not_hidden(self):
result = native.scan(101, self.runner(service(name='legit-app', exe='/usr/bin/legit-app', directory='')))
self.assertEqual(result['candidates'][0]['name'], 'legit-app')
def test_system_services_and_ambiguous_runtimes_have_zero_footprint_probes(self):
for name, exe in [('ssh', '/usr/sbin/sshd'), ('cron', '/usr/sbin/cron'),
('postfix', '/usr/lib/postfix/master'), ('rsyslog', '/usr/sbin/rsyslogd'),
('systemd-networkd', '/usr/lib/systemd/systemd-networkd'),
('ambiguous', '/usr/bin/python3')]:
self.calls.clear()
result = native.scan(101, self.runner(service(name=name, exe=exe)))
self.assertEqual(result['candidates'], [])
self.assertFalse(any(native.FOOTPRINT_SCRIPT in call for call in self.calls))
def test_common_system_daemons_and_vendor_units_have_zero_footprint_probes(self):
cases = [
('nginx', '/usr/sbin/nginx', 'openrc', None),
('apache2', '/usr/sbin/apache2', 'systemd', None),
('redis-server', '/usr/bin/redis-server', 'systemd', None),
('mariadb', '/usr/sbin/mariadbd', 'systemd', None),
('postgresql', '/usr/lib/postgresql/16/bin/postgres', 'systemd', None),
('php8.3-fpm', '/usr/sbin/php-fpm8.3', 'systemd', None),
('vendor-daemon', '/usr/bin/vendor-daemon', 'systemd',
'/usr/lib/systemd/system/vendor-daemon.service'),
]
for name, exe, manager, source in cases:
with self.subTest(name=name):
self.calls.clear()
result = native.scan(101, self.runner(
service(name=name, exe=exe, manager=manager, source=source)))
self.assertEqual(result['candidates'], [])
self.assertFalse(any(native.FOOTPRINT_SCRIPT in call for call in self.calls))
def test_alpine_openrc_infrastructure_has_zero_footprint_probes(self):
# Actual Alpine 3.24.1 CT217 init metadata: klogd, user and runsvdir.
# The runsvdir definition can exist without /usr/bin/runsvdir installed;
# rejecting it must precede probing, not turn the scan into partial.
for name, executable in (
('klogd', '/sbin/klogd'),
('user', '/usr/libexec/rc/bin/openrc-user'),
('runsvdir', '/usr/bin/runsvdir'),
('renamed-system-service', '/usr/libexec/rc/bin/openrc-user'),
):
with self.subTest(name=name):
self.calls.clear()
result = native.scan(101, self.runner(
service(name=name, exe=executable, directory='', manager='openrc')))
self.assertEqual(result['status'], 'complete')
self.assertEqual(result['candidates'], [])
self.assertFalse(any(native.FOOTPRINT_SCRIPT in call for call in self.calls))
def test_helper_services_and_templates_have_zero_footprint_probes(self):
cases = [
('netpulse-stable-update', '/bin/bash', '/opt/netpulse', '/opt/netpulse/stable-apply.sh'),
('rent-tool-backup', '/bin/bash', '/home/app/rent-tool', '/home/app/rent-tool/scripts/backup-db.sh'),
('keynest-update', '/bin/bash', '/opt/keynest', '/opt/keynest/keynest-update.sh'),
('cortendesk-bootstrap-worker', '/bin/bash', '/opt/cortendesk', '/opt/cortendesk/bootstrap-worker.sh'),
('cortendesk-source-worker', '/bin/bash', '/opt/cortendesk', '/opt/cortendesk/source-worker.sh'),
('cortendesk-managed-password@', '/bin/bash', '/opt/cortendesk', '/opt/cortendesk/password-bridge.sh'),
('miminails-watch', '/usr/local/bin/miminails-watch', '/usr/local/bin', ''),
]
for name, exe, directory, script in cases:
with self.subTest(name=name):
self.calls.clear()
result = native.scan(101, self.runner(service(
name=name, exe=exe, directory=directory, script=script)))
self.assertEqual(result['candidates'], [])
self.assertFalse(any(native.FOOTPRINT_SCRIPT in call for call in self.calls))
def test_hestia_panel_is_recognized_only_from_its_exact_runtime_and_port(self):
hestia = '/usr/local/hestia/nginx/sbin/hestia-nginx'
panel = f'H\t391\t{hestia}\t/\t8083\nEND\n'
result = native.scan(101, self.runner(
services='END\n', processes='LIMIT\n', ports='END\n', hestia_panels=panel))
item, = result['candidates']
self.assertEqual(item['name'], 'Hestia')
self.assertEqual(item['installation_path'], hestia)
self.assertEqual(item['listening_ports'], [8083])
self.assertEqual(item['default_ports'], [8083])
self.assertEqual(item['scheme'], 'https')
self.assertIn({'kind': 'working_directory', 'value': '/'}, item['evidence'])
self.assertIn('metadata_limit', result['issues'])
unrelated = native.candidate(101, {
'name': 'other-panel', 'path': '/opt/other-panel/server', 'root': '/opt/other-panel',
'process': '/opt/other-panel/server', 'listening_ports': [8083],
}, 'FILE\n')
self.assertEqual(unrelated['default_ports'], [])
self.assertNotIn('scheme', unrelated)
self.calls.clear()
result = native.scan(101, self.runner(
services='END\n', processes='LIMIT\n', ports='END\n'))
self.assertEqual(result['candidates'], [])
self.assertFalse(any(native.FOOTPRINT_SCRIPT in call for call in self.calls))
with self.assertRaises(native.ProbeError):
native.parse_records(f'H\t391\t{hestia}\t/\t8443\nEND\n', 'H')
def test_node_and_npm_wrappers_use_only_bounded_manifest_roots(self):
cases = [
('keynest', '/usr/bin/node', '/opt/keynest'),
('rent-tool', '/usr/local/bin/npm', '/home/app/rent-tool'),
]
for name, exe, directory in cases:
with self.subTest(name=name):
self.calls.clear()
result = native.scan(101, self.runner(
service(name=name, exe=exe, directory=directory),
footprint='FILE\n{"version":"1.2.3"}'))
item, = result['candidates']
self.assertEqual(item['name'], name)
self.assertEqual(item['installation_path'], directory + '/package.json')
self.assertEqual(item['installed_version'], '1.2.3')
footprint_calls = [call for call in self.calls if native.MANIFEST_FOOTPRINT_SCRIPT in call]
self.assertEqual(len(footprint_calls), 1)
if directory.startswith('/home/'):
parent = '/home/app'
else:
parent = '/opt'
self.assertEqual(footprint_calls[0][-3:], [directory, directory, parent])
self.calls.clear()
result = native.scan(101, self.runner(service(
name='ambiguous-home-runtime', exe='/usr/bin/node', directory='/home/app')))
self.assertEqual(result['candidates'], [])
self.assertFalse(any(native.MANIFEST_FOOTPRINT_SCRIPT in call for call in self.calls))
def test_node_release_layout_uses_only_server_manifest_under_shared_root(self):
self.calls.clear()
result = native.scan(101, self.runner(
service(name='keynest', exe='/opt/keynest/node/bin/node', directory='/opt/keynest/current'),
manifest='MANIFEST\tserver/package.json\n{"version":"1.5.63"}'))
item, = result['candidates']
self.assertEqual(item['name'], 'keynest')
self.assertEqual(item['installation_path'], '/opt/keynest/current/server/package.json')
self.assertEqual(item['installed_version'], '1.5.63')
manifest_calls = [call for call in self.calls if native.MANIFEST_FOOTPRINT_SCRIPT in call]
self.assertEqual(len(manifest_calls), 1)
self.assertEqual(manifest_calls[0][-3:],
['/opt/keynest/current', '/opt/keynest', '/opt'])
def test_node_release_layout_rejects_manifest_outside_operator_root(self):
self.calls.clear()
result = native.scan(101, self.runner(
service(name='outside-root', exe='/opt/outside/node/bin/node', directory='/opt/outside/current'),
manifest='OUTSIDE\n'))
self.assertEqual(result['candidates'], [])
self.assertIn('unsafe_manifest_root', result['issues'])
self.assertFalse(any(native.FOOTPRINT_SCRIPT in call for call in self.calls))
def test_actual_manifest_probe_accepts_release_and_rejects_symlink_escape(self):
parent = self.root / 'opt'
anchor = parent / 'keynest'
release = anchor / 'releases' / 'v1.5.63'
manifest = release / 'server' / 'package.json'
manifest.parent.mkdir(parents=True)
manifest.write_text('{"version":"1.5.63"}')
current = anchor / 'current'
current.symlink_to(release)
output = native.bounded_run(
['sh', '-c', native.MANIFEST_FOOTPRINT_SCRIPT, 'fixture',
str(current), str(anchor), str(parent)], time.monotonic() + 3)
self.assertEqual(output, 'MANIFEST\tserver/package.json\n{"version":"1.5.63"}')
outside = self.root / 'outside' / 'package.json'
outside.parent.mkdir()
outside.write_text('{"version":"not-readable"}')
manifest.unlink()
manifest.symlink_to(outside)
output = native.bounded_run(
['sh', '-c', native.MANIFEST_FOOTPRINT_SCRIPT, 'fixture',
str(current), str(anchor), str(parent)], time.monotonic() + 3)
self.assertEqual(output, 'OUTSIDE\n')
self.assertNotIn('not-readable', output)
def test_node_wrapper_with_no_fixed_manifest_is_not_an_error_or_candidate(self):
self.calls.clear()
result = native.scan(101, self.runner(
service(name='missing-manifest', exe='/opt/missing/node/bin/node', directory='/opt/missing/current'),
manifest='MISSING\n'))
self.assertEqual(result['status'], 'complete')
self.assertEqual(result['candidates'], [])
self.assertFalse(any(native.FOOTPRINT_SCRIPT in call for call in self.calls))
def test_helper_process_fallback_has_zero_footprint_probes(self):
self.calls.clear()
result = native.scan(101, self.runner(
services='END\n', processes='P\t123\t/opt/cortendesk/run-worker\t/opt/cortendesk\nEND\n'))
self.assertEqual(result['candidates'], [])
self.assertFalse(any(native.FOOTPRINT_SCRIPT in call for call in self.calls))
def test_missing_remote_qemu_stopped_busy_oci_guards_precede_exec(self):
scenarios = ['missing', 'stopped', 'busy', 'oci', 'invalid-oci', 'unmanaged']
for scenario in scenarios:
with self.subTest(scenario=scenario):
(self.config / '101.conf').write_text('ostype: debian\n')
if scenario == 'missing':
(self.config / '101.conf').unlink()
if scenario == 'busy':
(self.config / '101.conf').write_text('lock: backup\n')
if scenario == 'unmanaged':
(self.config / '101.conf').write_text('ostype: unmanaged\n')
if 'oci' in scenario:
(self.oci / '101').mkdir(exist_ok=True)
(self.oci / '101' / 'oci-compose.json').write_text('broken')
self.calls.clear()
with self.assertRaises(native.ProbeError):
native.scan(101, self.runner(status='status: stopped' if scenario == 'stopped' else 'status: running'))
self.assertFalse(any(call[1] == 'exec' for call in self.calls))
if (self.oci / '101').exists():
(self.oci / '101' / 'oci-compose.json').unlink()
(self.oci / '101').rmdir()
def test_failure_and_partial_output_are_not_successful_empty_results(self):
for text in ('', 'malformed\nEND\n', 'S\tpartial', 'LIMIT\n'):
result = native.scan(101, self.runner(services=text))
self.assertEqual(result['status'], 'partial')
run = self.runner()
def failed(argv, deadline):
if argv[1] == 'exec':
raise native.ProbeError('read_failed')
return run(argv, deadline)
self.assertEqual(native.scan(101, failed)['status'], 'partial')
def test_optional_invalid_version_metadata_keeps_proven_candidate(self):
result = native.scan(101, self.runner(footprint='FILE\n{broken'))
self.assertEqual(result['status'], 'partial')
item, = result['candidates']
self.assertIsNone(item['installed_version'])
self.assertIsNone(item['tracking_suggestion'])
def test_working_directory_manifest_does_not_version_an_unrelated_executable(self):
result = native.scan(101, self.runner(service(exe='/opt/other/server'),
footprint='FILE\n{"version":"1.2.3"}'))
self.assertIsNone(result['candidates'][0]['installed_version'])
def test_path_injection_and_port_only_data_do_not_become_candidates(self):
for text in (service(exe='/opt/app/$(id)'), service(exe='/opt/../server'),
service(name='app;id'), service(exe='/bin/sh')):
self.assertEqual(native.scan(101, self.runner(text))['candidates'], [])
with self.assertRaises(native.ProbeError):
native.parse_records('PORT\t8080\nEND\n', 'P')
def test_same_process_deduplicates_but_two_apps_using_node_do_not_merge(self):
services = service(exe='/usr/bin/node', script='/opt/handmade/server.js').removesuffix('END\n')
services += service(name='second', exe='/usr/bin/node', script='/opt/second/server.js', directory='/opt/second')
result = native.scan(101, self.runner(services))
self.assertEqual(len(result['candidates']), 2)
result = native.scan(101, self.runner(processes='P\t123\t/opt/handmade/server\t/opt/handmade\nEND\n'))
self.assertEqual(len(result['candidates']), 1)
other = native.candidate(102, native.parse_records(service(), 'S')[0][0], 'FILE\n')
self.assertNotEqual(other['discovery_id'], result['candidates'][0]['discovery_id'])
def test_record_and_candidate_caps(self):
too_many = 'P\t123\t/opt/a/app\t/opt/a\n' * 257 + 'END\n'
with self.assertRaises(native.ProbeError):
native.parse_records(too_many, 'P')
services = ''.join(service(name=f'app{i}', exe=f'/opt/app{i}/server', directory=f'/opt/app{i}').removesuffix('END\n') for i in range(51)) + 'END\n'
result = native.scan(101, self.runner(services))
self.assertEqual(len(result['candidates']), 50)
self.assertIn('candidate_limit', result['issues'])
def test_cached_ttl_never_runs_scan_and_error_preserves_last_known(self):
complete = native.scan(101, self.runner())
with patch.object(native, 'scan', return_value=complete) as scan:
native.discover(101)
for age, stale in ((299.9, False), (300, True), (300.1, True)):
with patch.object(native.time, 'time', return_value=complete['scanned_at'] + age):
self.assertEqual(native.cached(101)['stale'], stale)
self.assertEqual(scan.call_count, 1)
with patch.object(native, 'scan', side_effect=native.ProbeError('timeout')):
result = native.discover(101)
self.assertEqual(result['candidates'], complete['candidates'])
self.assertEqual(native.cached(101)['status'], 'error')
self.assertTrue(native.cached(101)['stale'])
partial = {**complete, 'status': 'partial', 'candidates': [], 'issues': ['read_failed']}
with patch.object(native, 'scan', return_value=partial):
self.assertEqual(native.discover(101)['candidates'], complete['candidates'])
with patch.object(native, 'scan', return_value={**complete, 'candidates': []}):
self.assertEqual(native.discover(101)['candidates'], [])
def test_lifecycle_during_first_or_cached_scan_cannot_publish_fresh_result(self):
complete = native.scan(101, self.runner())
for prior in (None, complete):
with self.subTest(cached=prior is not None):
native._cache.clear()
if prior:
native._cache[101] = copy.deepcopy(prior)
entered, release = threading.Event(), threading.Event()
results = []
def delayed(vmid):
entered.set()
if not release.wait(3):
raise AssertionError('scan release timed out')
return copy.deepcopy(complete)
with patch.object(native, 'scan', side_effect=delayed):
worker = threading.Thread(target=lambda: results.append(native.discover(101)))
worker.start()
try:
self.assertTrue(entered.wait(3))
native.mark_stale(101)
finally:
release.set()
worker.join(3)
self.assertFalse(worker.is_alive())
self.assertEqual(len(results), 1)
self.assertTrue(results[0]['stale'])
self.assertIn('guest_changed', results[0]['issues'])
self.assertEqual(results[0]['candidates'], prior['candidates'] if prior else [])
self.assertTrue(native.cached(101)['stale'])
with patch.object(native, 'scan', return_value=copy.deepcopy(complete)):
self.assertFalse(native.discover(101)['stale'])
with patch.object(native, 'scan', side_effect=lambda vmid: (
native.mark_stale(102), copy.deepcopy(complete))[1]):
self.assertFalse(native.discover(101)['stale'])
def test_scan_excludes_later_updater_and_releases_lock_after_success_or_failure(self):
path = self.root / 'proxmenux-lxc-update-101.lock'
for fail in (False, True):
with self.subTest(fail=fail):
runner = self.runner()
probes = []
def checked(argv, deadline):
if argv[1] == 'exec':
attempt = subprocess.run(['flock', '-n', str(path), 'true'])
self.assertEqual(attempt.returncode, 1)
probes.append(argv)
if fail:
raise native.ProbeError('timeout')
return runner(argv, deadline)
native.scan(101, checked)
self.assertTrue(probes)
self.assertEqual(subprocess.run(['flock', '-n', str(path), 'true']).returncode, 0)
self.assertEqual(path.read_bytes(), b'')
def test_pending_control_reservation_blocks_scan_without_touching_it(self):
path = self.root / 'proxmenux-lxc-update-101.lock'
original = b'{"task":"pending-control"}'
path.write_bytes(original)
with self.assertRaisesRegex(native.ProbeError, 'guest_busy'):
native.scan(101, self.runner())
self.assertFalse(any(call[1] == 'exec' for call in self.calls))
self.assertEqual(path.read_bytes(), original)
def test_guest_stop_during_scan_never_returns_complete(self):
runner = self.runner()
statuses = iter(['status: running', 'status: stopped'])
def stopped(argv, deadline):
return next(statuses) if argv[1] == 'status' else runner(argv, deadline)
with self.assertRaisesRegex(native.ProbeError, 'guest_stopped'):
native.scan(101, stopped)
self.assertTrue(all(call[1] == 'exec' for call in self.calls))
def test_scan_reservation_closes_race_after_guard_and_releases_after_exception(self):
import lxc_check_job
path = self.root / 'proxmenux-lxc-update-101.lock'
runner = self.runner()
handles = []
def starts_update(argv, deadline):
if argv[1] == 'status':
handles.append(lxc_check_job.acquire(101))
return runner(argv, deadline)
with patch.object(lxc_check_job, 'LOCK_DIR', self.root):
try:
with self.assertRaisesRegex(native.ProbeError, 'guest_busy'):
native.scan(101, starts_update)
self.assertFalse(any(call[1] == 'exec' for call in self.calls))
finally:
for handle in handles:
handle.close()
def crash(argv, deadline):
if argv[1] == 'exec':
with self.assertRaises(BlockingIOError):
lxc_check_job.acquire(101)
# Another CT must remain available.
lxc_check_job.acquire(102).close()
raise RuntimeError('unexpected probe failure')
return runner(argv, deadline)
with self.assertRaisesRegex(RuntimeError, 'unexpected'):
native.scan(101, crash)
lxc_check_job.acquire(101).close()
self.assertEqual(path.read_bytes(), b'')
def test_failed_scan_releases_discovery_lock_and_keeps_prior_evidence(self):
complete = native.scan(101, self.runner())
native._cache[101] = copy.deepcopy(complete)
with patch.object(native, 'scan', side_effect=RuntimeError('unexpected')):
with self.assertRaises(RuntimeError):
native.discover(101)
self.assertFalse(native._locks[101].locked())
self.assertEqual(native.cached(101)['candidates'], complete['candidates'])
def test_invalid_lock_inode_and_unreadable_config_fail_before_any_probe(self):
import os
path = self.root / 'proxmenux-lxc-update-101.lock'
target = self.root / 'unrelated'
target.write_bytes(b'keep')
for kind in ('symlink', 'fifo', 'directory'):
with self.subTest(kind=kind):
if kind == 'symlink':
path.symlink_to(target)
elif kind == 'fifo':
os.mkfifo(path)
else:
path.mkdir()
try:
with self.assertRaisesRegex(native.ProbeError, 'lock_unavailable'):
native.scan(101, self.runner())
self.assertEqual(self.calls, [])
self.assertEqual(target.read_bytes(), b'keep')
finally:
path.rmdir() if kind == 'directory' else path.unlink()
with patch.object(Path, 'open', side_effect=PermissionError):
with self.assertRaisesRegex(native.ProbeError, 'config_unavailable'):
native.scan(101, self.runner())
self.assertEqual(self.calls, [])
def test_same_ct_busy_and_different_ct_cache_isolation(self):
lock = threading.Lock()
lock.acquire()
native._locks[101] = lock
with patch.object(native, 'scan') as scanner:
self.assertEqual(native.discover(101)['status'], 'busy')
scanner.assert_not_called()
self.assertEqual(native.cached(102)['status'], 'not_scanned')
def test_filter_registered_curated_and_docker_children_without_mutation(self):
result = native.scan(101, self.runner())
original = copy.deepcopy(result)
for suggestions, apps in (({}, [{'discovery_id': result['candidates'][0]['discovery_id']}]),
({'extras': [{'name': 'Handmade'}]}, []),
({'docker_workloads': [{'name': 'Handmade'}]}, []),
({'helper_slug': 'handmade', 'name_suggestion': 'Handmade'}, [])):
self.assertEqual(native.filter_candidates(result, suggestions, apps)['candidates'], [])
self.assertEqual(result, original)
def test_actual_save_first_check_and_recheck_never_probe_or_configure_updater(self):
with patch.object(lxc_apps, '_APPS_DIR', str(self.root / 'apps')), \
patch.object(lxc_apps, '_pct_exec', side_effect=AssertionError('forbidden guest execution')) as execute, \
patch.object(lxc_apps, 'fetch_latest_upstream_details', side_effect=AssertionError('forbidden upstream')) as upstream:
for version in (None, '1.2.3'):
item = native.candidate(101, native.parse_records(service(), 'S')[0][0],
'FILE\n' + (json.dumps({'version': version}) if version else ''))
payload = {'name': item['name'], 'discovery_id': item['discovery_id'],
'update_method': 'none', **(item['tracking_suggestion'] or {})}
ok, sidecar = lxc_apps.add_app(101, payload)
self.assertTrue(ok, sidecar)
app = sidecar['apps'][-1]
self.assertEqual(app['state']['installed_version'], version)
self.assertIsNone(app['state']['update_available'])
self.assertEqual(app['update_method'], 'none')
for field in ('helper_slug', 'update_command', 'repo', 'upstream_type'):
self.assertNotIn(field, app)
lxc_apps.check_app(101, app['id'], force=True)
self.assertIsNone(lxc_apps.get_schedule(101))
self.assertIsNone(lxc_apps.get_bulk_update(101))
execute.assert_not_called()
upstream.assert_not_called()
def test_failed_save_has_no_first_check(self):
with patch.object(lxc_apps, '_write_sidecar', return_value=False), \
patch.object(lxc_apps, '_read_sidecar', return_value=None), \
patch.object(lxc_apps, 'check_app') as check:
self.assertFalse(lxc_apps.add_app(101, {'name': 'handmade'})[0])
check.assert_not_called()
def test_active_update_lock_blocks_all_guest_probes_without_writing(self):
path = self.root / 'proxmenux-lxc-update-101.lock'
path.write_bytes(b'unchanged')
with path.open('rb') as handle:
fcntl.flock(handle, fcntl.LOCK_EX)
with self.assertRaisesRegex(native.ProbeError, 'guest_busy'):
native.scan(101, self.runner())
self.assertEqual(path.read_bytes(), b'unchanged')
self.assertEqual(self.calls, [])
def test_actual_service_parser_reads_data_never_executes_or_sources_it(self):
for manager in ('systemd', 'openrc'):
directory = self.root / manager
directory.mkdir()
marker = self.root / ('forbidden-' + manager)
unit = directory / ('handmade.service' if manager == 'systemd' else 'handmade')
content = ('[Service]\nExecStart=/usr/bin/node /opt/handmade/server.js --password SECRET\nWorkingDirectory=/opt/handmade\n'
if manager == 'systemd' else 'command="/usr/bin/node"\ncommand_args="/opt/handmade/server.js --password SECRET"\ndirectory="/opt/handmade"\n')
unit.write_text(content + '\ntouch ' + str(marker) + '\n')
script = native.SERVICE_SCRIPT.replace('/etc/systemd/system /usr/lib/systemd/system /lib/systemd/system /etc/init.d', str(directory))
# Substitute only fixture paths, not the collector/parser behavior.
if manager == 'openrc':
script = script.replace('/etc/init.d:*)', str(directory) + ':*)')
output = native.bounded_run(['sh', '-c', script], time.monotonic() + 3)
record, = native.parse_records(output, 'S')[0]
self.assertEqual(record['path'], '/opt/handmade/server.js')
self.assertNotIn('SECRET', output)
self.assertFalse(marker.exists())
def test_merged_usr_units_are_counted_once(self):
usr = self.root / 'usr-units'
usr.mkdir()
(usr / 'handmade.service').write_text('[Service]\nExecStart=/opt/handmade/server\n')
lib = self.root / 'lib-units'
lib.symlink_to(usr)
check = '[ "$dir" = /lib/systemd/system ] && [ /lib/systemd/system -ef /usr/lib/systemd/system ]'
self.assertIn(check, native.SERVICE_SCRIPT)
script = (native.SERVICE_SCRIPT
.replace('/etc/systemd/system /usr/lib/systemd/system /lib/systemd/system /etc/init.d', f'{usr} {lib}')
.replace(check, f'[ "$dir" = {lib} ] && [ {lib} -ef {usr} ]'))
output = native.bounded_run(['sh', '-c', script], time.monotonic() + 3)
self.assertEqual(output.count('handmade.service'), 1)
def test_a_guest_without_ss_or_netstat_has_no_listener_evidence(self):
bin_dir = self.root / 'no-tools'
bin_dir.mkdir()
(bin_dir / 'awk').symlink_to('/usr/bin/awk')
output = native.bounded_run(['sh', '-c', 'PATH=' + str(bin_dir) + '\n' + native.PORT_SCRIPT, 'fixture', '256'],
time.monotonic() + 3)
self.assertEqual(native.parse_records(output, 'L'), ([], False, 0))
def test_actual_process_parser_skips_docker_and_never_reads_environ_cmdline(self):
proc = self.root / 'proc'
proc.mkdir()
for pid, group in ((1, '0::/\n'), (2, '0::/docker/child\n')):
directory = proc / str(pid)
directory.mkdir()
(directory / 'cgroup').write_text(group)
(directory / 'exe').symlink_to('/opt/handmade/server')
(directory / 'cwd').symlink_to('/opt/handmade')
(directory / 'cmdline').write_text('SECRET')
(directory / 'environ').write_text('PASSWORD')
script = native.PROCESS_SCRIPT.replace('/proc/[0-9]*', str(proc) + '/[0-9]*')
output = native.bounded_run(['sh', '-c', script, 'fixture', '256'], time.monotonic() + 3)
self.assertEqual(output, 'P\t1\t/opt/handmade/server\t/opt/handmade\nEND\n')
self.assertNotIn('"$dir/cmdline"', native.PROCESS_SCRIPT)
self.assertNotIn('"$dir/environ"', native.PROCESS_SCRIPT)
self.assertNotIn('SECRET', output)
def test_ports_require_exact_process_ownership_and_do_not_infer_http(self):
process = 'P\t123\t/opt/handmade/server\t/opt/handmade\nEND\n'
result = native.scan(101, self.runner(processes=process, ports='L\t123\t8080\nL\t999\t9000\nEND\n'))
item, = result['candidates']
self.assertEqual(item['listening_ports'], [8080])
self.assertEqual(item['default_ports'], [], 'TCP listener does not prove HTTP')
result = native.scan(101, self.runner(ports='L\t123\t8080\nEND\n'))
self.assertEqual(result['candidates'][0]['listening_ports'], [])
def test_shared_runtime_ports_are_assigned_only_by_working_directory(self):
services = service(exe='/usr/bin/node', script='/opt/handmade/server.js').removesuffix('END\n')
services += service(name='second', exe='/usr/bin/node', script='/opt/second/server.js', directory='/opt/second')
process = 'P\t123\t/usr/bin/node\t/opt/second\nEND\n'
result = native.scan(101, self.runner(services, processes=process, ports='L\t123\t8080\nEND\n'))
self.assertEqual(result['candidates'][0]['listening_ports'], [])
self.assertEqual(result['candidates'][1]['listening_ports'], [8080])
def test_ss_and_alpine_netstat_parser_are_bounded_and_secret_free(self):
for tool, line in (('ss', 'LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("SECRET",pid=123,fd=7))'),
('netstat', 'tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 123/SECRET')):
bin_dir = self.root / tool
bin_dir.mkdir()
executable = bin_dir / tool
executable.write_text('#!/bin/sh\nprintf \'%s\\n\' \'' + line + '\'\n')
executable.chmod(0o755)
(bin_dir / 'awk').symlink_to('/usr/bin/awk')
(bin_dir / 'timeout').symlink_to('/usr/bin/timeout')
script = 'PATH=' + str(bin_dir) + '\n' + native.PORT_SCRIPT
output = native.bounded_run(['sh', '-c', script, 'fixture', '256'], time.monotonic() + 3)
self.assertEqual(output, 'L\t123\t8080\nEND\n')
self.assertNotIn('SECRET', output)
executable.write_text('#!/bin/sh\nexit 1\n')
output = native.bounded_run(['sh', '-c', script, 'fixture', '256'], time.monotonic() + 3)
with self.assertRaises(native.ProbeError):
native.parse_records(output, 'L')
class BoundedRunner(unittest.TestCase):
def test_output_limit_is_enforced_during_read(self):
with self.assertRaisesRegex(native.ProbeError, 'output_limit'):
native.bounded_run([sys.executable, '-c', 'import os; os.write(2,b"x"*1000000)'], time.monotonic() + 3)
def test_pipe_holding_child_does_not_bypass_timeout(self):
start = time.monotonic()
with self.assertRaisesRegex(native.ProbeError, 'timeout'):
native.bounded_run([sys.executable, '-c', 'import os,time; os.fork(); time.sleep(10)'], start + .15)
self.assertLess(time.monotonic() - start, .8)
def test_failures_never_reveal_stderr_and_deadline_blocks_spawn(self):
with self.assertRaisesRegex(native.ProbeError, '^read_failed$'):
native.bounded_run([sys.executable, '-c', 'import sys; sys.stderr.write("SECRET"); sys.exit(1)'], time.monotonic() + 3)
with patch.object(native.subprocess, 'Popen') as launch:
with self.assertRaisesRegex(native.ProbeError, 'deadline'):
native.bounded_run(['forbidden'], time.monotonic() - 1)
launch.assert_not_called()
class Routes(unittest.TestCase):
"""Execute actual Flask route bodies; fixture auth, no server startup jobs."""
def setUp(self):
from flask import Flask, request, jsonify, g
self.app = Flask('native-fixture')
self.authorized = True
def require_auth(fn):
@wraps(fn)
def wrapped(*args, **kwargs):
if not self.authorized:
return jsonify({'error': 'auth_required'}), 401
return fn(*args, **kwargs)
return wrapped
self.globals = {'app': self.app, 'request': request, 'jsonify': jsonify,
'require_auth': require_auth, '_vm_app_suggestions_cache': {},
'_VM_APP_SUGGESTIONS_TTL': 300,
'_vm_cache_get': lambda cache, vmid, ttl: cache.get(vmid),
'_vm_cache_put': lambda cache, vmid, value: cache.update({vmid: value}),
'_pvesh_cache': {}, '_vm_disk_cache': {}, '_vm_schedule_cache': {}, '_vm_cache_invalidate': Mock(),
'_invalidate_lxc_ip': Mock(), '_schedule_started_guest_refresh': Mock(),
'_publish_guest_modal_cache_revision': Mock()}
tree = ast.parse((SCRIPTS / 'flask_server.py').read_text())
routes = [n for n in tree.body if isinstance(n, ast.FunctionDef) and n.name in (
'api_vm_apps_suggestions', 'api_vm_apps_native_discovery', 'api_vm_apps_add',
'_handle_guest_lifecycle')]
self.assertEqual(len(routes), 4)
exec(compile(ast.Module(body=routes, type_ignores=[]), str(SCRIPTS / 'flask_server.py'), 'exec'), self.globals)
self.client = self.app.test_client()
def test_explicit_post_only_get_and_existing_post_never_native_scan(self):
result = {'vmid': 101, 'status': 'complete', 'candidates': [], 'stale': False}
with patch.object(native, 'discover', return_value=result) as scan, \
patch.object(native, 'cached', return_value=result), \
patch.object(lxc_apps, 'get_suggestions', return_value={'extras': []}) as suggestions, \
patch.object(lxc_apps, 'load_sidecar', return_value={'apps': []}), \
patch.object(lxc_apps, '_write_sidecar') as write:
self.assertEqual(self.client.get('/api/vms/101/apps/native-discovery').status_code, 200)
self.client.get('/api/vms/101/apps/suggestions')
# OCI refresh and Find's existing catalog path retain their purpose.
self.client.post('/api/vms/101/apps/suggestions')
with self.app.test_request_context('/api/vms/101/apps/suggestions'):
from flask import g
g._internal_call = True
self.globals['_vm_app_suggestions_cache'].clear()
self.globals['api_vm_apps_suggestions'](101)
scan.assert_not_called()
self.client.post('/api/vms/101/apps/native-discovery')
scan.assert_called_once_with(101)
self.assertEqual(suggestions.call_count, 2)
write.assert_not_called()
def test_unauthorized_and_wrong_method_have_zero_scans(self):
with patch.object(native, 'discover') as scan, patch.object(lxc_apps, 'load_sidecar') as read:
self.authorized = False
self.assertEqual(self.client.post('/api/vms/101/apps/native-discovery').status_code, 401)
self.assertEqual(self.client.put('/api/vms/101/apps/native-discovery').status_code, 405)
scan.assert_not_called()
read.assert_not_called()
def test_actual_lifecycle_marks_last_known_stale_without_scanning(self):
native._cache[101] = {'vmid': 101, 'status': 'complete', 'stale': False,
'candidates': [], 'scanned_at': time.time()}
with patch.object(native, 'discover') as scan, \
patch.object(lxc_apps, 'clear_schedule_reboot_required') as clear:
for action in ('start', 'reboot', 'stop'):
self.globals['_handle_guest_lifecycle']('101', 'lxc', action)
self.assertTrue(native.cached(101)['stale'])
scan.assert_not_called()
self.assertEqual(clear.call_count, 2)
def test_actual_repeated_prewarmer_has_zero_native_scans(self):
tree = ast.parse((SCRIPTS / 'flask_server.py').read_text())
warmer = next(node for node in tree.body if isinstance(node, ast.FunctionDef)
and node.name == '_vm_modal_prewarmer_pass')
environment = dict(self.globals)
for name in ('_vm_details_cache', '_vm_backups_cache', '_vm_schedule_cache', '_vm_mounts_cache'):
environment[name] = {}
for name in ('_VM_DETAILS_TTL', '_VM_BACKUPS_TTL', '_VM_SCHEDULE_TTL', '_VM_MOUNTS_TTL'):
environment[name] = 300
for name in ('get_vm_config', 'api_vm_backups', 'api_vm_apps_schedule', 'api_lxc_mount_points'):
environment[name] = lambda vmid: None
environment.update({'get_cached_pvesh_cluster_resources_vm': lambda: [
{'vmid': 101, 'type': 'lxc', 'status': 'running'}], 'time': Mock(), 'sys': sys})
exec(compile(ast.Module(body=[warmer], type_ignores=[]), 'flask_server.py', 'exec'), environment)
with patch.object(native, 'discover') as scan, \
patch.object(lxc_apps, 'ensure_oci_registration'), \
patch.object(lxc_apps, 'load_sidecar', return_value={'apps': []}), \
patch.object(lxc_apps, 'get_suggestions', return_value={'extras': []}):
for _ in range(2):
environment['_vm_app_suggestions_cache'].clear()
self.assertEqual(environment['_vm_modal_prewarmer_pass'](), 1)
scan.assert_not_called()
if __name__ == '__main__':
unittest.main()
+289
View File
@@ -0,0 +1,289 @@
"""PBS platform, HTTPS protocol, authorization and isolated AppImage regressions."""
import ast
from datetime import datetime, timedelta, timezone
import hashlib
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
import ipaddress
import json
from pathlib import Path
import shlex
import ssl
import subprocess
import sys
import tempfile
import threading
from types import SimpleNamespace
import unittest
from unittest import mock
ROOT = Path(__file__).resolve().parents[3]
sys.path.insert(0, str(ROOT / "AppImage/scripts"))
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
from flask import Flask
import jwt
import auth_manager
import pbs_manager as pbs
from flask_pbs_routes import pbs_bp
class PlatformTests(unittest.TestCase):
def test_supported_platforms_and_partial_dpkg_failure(self):
module = pbs.detect_platform.__globals__
with tempfile.TemporaryDirectory() as directory:
release = Path(directory) / "os-release"
for package, version, suite, product in (
("pve-manager", "8.4.15", "bookworm", "pve"),
("pve-manager", "9.2.1", "trixie", "pve"),
("proxmox-backup-server", "3.4.0-1", "bookworm", "pbs"),
("proxmox-backup-server", "4.2.5-1", "trixie", "pbs"),
):
release.write_text(f'VERSION_CODENAME="{suite}"\n')
output = f"{package}\tinstall ok installed\t{version}\n"
with self.subTest(product=product, version=version), \
mock.patch.dict(module, {"OS_RELEASE": release}), \
mock.patch.object(subprocess, "run", return_value=SimpleNamespace(stdout=output, returncode=1)):
detected = pbs.detect_platform()
self.assertEqual(detected["product"], product)
self.assertEqual(detected["version"], version)
self.assertTrue(detected["supported"])
def test_client_only_and_removed_server_are_not_pbs(self):
for output in ("proxmox-backup-client\tinstall ok installed\t4.2.5\n",
"proxmox-backup-server\tdeinstall ok config-files\t4.2.5\n", ""):
with self.subTest(output=output), mock.patch.object(subprocess, "run", return_value=SimpleNamespace(stdout=output)):
self.assertEqual(pbs.detect_platform()["product"], "unknown")
def test_pbs_utilities_preserve_sources_and_never_call_pve_policy(self):
helper = ROOT / "scripts/global/repository-functions.sh"
for supported in (True, False):
harness = f'''
source {shlex.quote(str(helper))}
pmx_platform_field() {{ if [[ "$1" == product ]]; then echo pbs; else echo {str(supported).lower()}; fi; }}
msg_info() {{ :; }}
msg_error() {{ :; }}
translate() {{ printf '%s' "$1"; }}
pveversion() {{ echo 'FORBIDDEN: pveversion'; return 90; }}
repository_policy() {{ echo 'FORBIDDEN: repository mutation'; return 90; }}
apt-get() {{ echo 'FORBIDDEN: apt'; return 90; }}
ensure_repositories
'''
result = subprocess.run(["bash"], input=harness, text=True, capture_output=True)
self.assertNotIn("FORBIDDEN", result.stdout + result.stderr)
self.assertEqual(result.returncode, 0 if supported else 1)
class PBSManagerTests(unittest.TestCase):
def setUp(self):
temporary = tempfile.TemporaryDirectory()
self.addCleanup(temporary.cleanup)
self.root = Path(temporary.name)
patch = mock.patch.multiple(auth_manager, CONFIG_DIR=self.root, AUTH_CONFIG_FILE=self.root / "auth.json")
patch.start(); self.addCleanup(patch.stop)
auth_manager.save_auth_config({"enabled": True, "username": "admin", "password_hash": "configured",
"jwt_secret": "s" * 48, "api_tokens": [], "revoked_tokens": []})
auth_manager._invalidate_revoked_cache()
pbs._status_cache.clear()
self.server = {"name": "Backup", "url": "https://pbs.test:8007", "token_id": "audit@pbs!monitor",
"token_secret": "fixture-token-secret", "fingerprint": "a" * 64}
app = Flask(__name__); app.register_blueprint(pbs_bp)
self.client = app.test_client()
self.headers = {"Authorization": "Bearer " + auth_manager.generate_token("admin")}
def test_save_redacts_secret_and_probes_authenticated_endpoint(self):
with mock.patch.object(pbs, "_remote_get", return_value=[]) as request:
identifier = pbs.save_server(self.server)
request.assert_called_once()
self.assertEqual(request.call_args.args[1], "/status/datastore-usage")
self.assertEqual((self.root / "pbs_servers.json").stat().st_mode & 0o777, 0o600)
public = pbs.public_servers()
self.assertEqual(public[0]["id"], identifier)
self.assertNotIn("fixture-token-secret", json.dumps(public))
self.assertTrue(public[0]["has_token"])
with mock.patch.object(pbs, "_remote_get", return_value=[]):
pbs.save_server({"id": identifier, "name": "Renamed"})
self.assertEqual(pbs._load_servers()[0]["token_secret"], "fixture-token-secret")
pbs.delete_server(identifier)
self.assertEqual(pbs.public_servers(), [])
def test_failed_credentials_or_invalid_config_do_not_replace_saved_connection(self):
with mock.patch.object(pbs, "_remote_get", return_value=[]):
identifier = pbs.save_server(self.server)
original = (self.root / "pbs_servers.json").read_bytes()
with mock.patch.object(pbs, "_remote_get", side_effect=pbs.PBSError("Rejected token")):
with self.assertRaises(pbs.PBSError):
pbs.save_server({**self.server, "id": identifier, "token_secret": "wrong"})
for changes in ({"url": "http://pbs.test"}, {"url": "https://user:pass@pbs.test"},
{"fingerprint": "bad-pin"}, {"token_id": "not-a-token"},
{"token_secret": "line\nbreak"}):
with self.subTest(changes=changes), self.assertRaises(pbs.PBSError):
pbs.save_server({**self.server, **changes, "id": identifier})
self.assertEqual((self.root / "pbs_servers.json").read_bytes(), original)
def test_remote_partial_failure_is_unknown_instead_of_zero(self):
def remote(server, endpoint):
if endpoint == "/version": return {"version": "4.2.5"}
if endpoint == "/status/datastore-usage": return [{"store": "backup", "total": 100, "used": 30, "avail": 70}]
if endpoint == "/admin/verify": raise pbs.PBSError("Permission denied")
return []
with mock.patch.object(pbs, "_remote_get", side_effect=remote) as request:
identifier = pbs.save_server(self.server)
result = pbs.get_status(identifier)
count = request.call_count
self.assertEqual(pbs.get_status(identifier), result)
self.assertEqual(request.call_count, count)
self.assertEqual(result["datastores"][0]["name"], "backup")
self.assertIsNone(result["verify"])
self.assertIn("verify", result["errors"])
def test_unmounted_removable_datastore_has_no_fabricated_capacity(self):
with mock.patch.object(pbs, "_cli", return_value=[{"name": "removable", "path": "/not-mounted", "backing-device": "uuid"}]), \
mock.patch.object(pbs.os.path, "ismount", return_value=False), \
mock.patch.object(pbs.shutil, "disk_usage") as usage:
result = pbs._local_datastores()
usage.assert_not_called()
self.assertIsNone(result[0]["total"])
self.assertIn("error", result[0])
def test_local_status_reads_pbs_commands_and_filesystem_capacity(self):
def run(command, **kwargs):
self.assertNotIn("shell", kwargs)
self.assertEqual(command[0], "proxmox-backup-manager")
self.assertEqual(command[-2:], ["--output-format", "json"])
data = [{"name": "backup", "path": str(self.root)}] if command[1:3] == ["datastore", "list"] else []
return SimpleNamespace(stdout=json.dumps(data), returncode=0)
with mock.patch.object(pbs, "get_platform", return_value={"product": "pbs", "version": "4.2.5"}), \
mock.patch.object(pbs, "_local_services", return_value={key: "active" for key in pbs.PBS_SERVICES}), \
mock.patch.object(pbs.subprocess, "run", side_effect=run), \
mock.patch.object(pbs.shutil, "disk_usage", return_value=SimpleNamespace(total=100, used=30, free=70)):
result = pbs.get_status("local")
self.assertEqual(result["version"], "4.2.5")
self.assertEqual(result["errors"], {})
self.assertEqual(result["datastores"][0]["used"], 30)
self.assertEqual(set(result["services"]), set(pbs.PBS_SERVICES))
def test_connection_api_requires_full_admin_and_does_not_return_secret(self):
self.assertEqual(self.client.get("/api/pbs/servers").status_code, 401)
with mock.patch.object(pbs, "_remote_get", return_value=[]):
response = self.client.post("/api/pbs/servers", headers=self.headers, json=self.server)
self.assertEqual(response.status_code, 200)
self.assertNotIn("fixture-token-secret", response.text)
payload = jwt.decode(self.headers["Authorization"].split()[1], "s" * 48,
algorithms=["HS256"], audience=auth_manager.JWT_AUDIENCE)
payload["scope"] = "read_only"
read_only = {"Authorization": "Bearer " + jwt.encode(payload, "s" * 48, algorithm="HS256")}
self.assertEqual(self.client.post("/api/pbs/servers", headers=read_only, json=self.server).status_code, 403)
self.assertEqual(self.client.get("/api/pbs/servers", headers=read_only).status_code, 200)
auth_manager.save_auth_config({"enabled": False})
self.assertEqual(self.client.post("/api/pbs/servers", json=self.server).status_code, 409)
def test_health_uses_pbs_services_and_skips_pve_only_checks(self):
source = ast.parse((ROOT / "AppImage/scripts/health_monitor.py").read_text())
names = ("_check_pve_services", "_check_vms_cts_optimized", "_check_vms_cts_with_persistence",
"_check_proxmox_storages", "_check_proxmox_storage", "_check_lxc_disk_usage",
"_check_vm_disk_usage", "_check_lxc_mount_capacity", "_check_pve_storage_capacity")
functions = [node for node in ast.walk(source) if isinstance(node, ast.FunctionDef) and node.name in names]
persistence = mock.Mock(); persistence.is_error_active.return_value = False
run = mock.Mock(return_value=SimpleNamespace(returncode=0, stdout="active\n"))
namespace = {"Dict": dict, "Any": object, "Optional": __import__('typing').Optional,
"PBS_SERVICES": pbs.PBS_SERVICES, "health_persistence": persistence,
"subprocess": SimpleNamespace(run=run), "os": __import__('os')}
exec(compile(ast.Module(body=functions, type_ignores=[]), '<health-pbs-test>', 'exec'), namespace)
instance = SimpleNamespace(platform_type="pbs", PVE_SERVICES=["pveproxy", "pvedaemon", "pvestatd", "pve-cluster"])
services = namespace["_check_pve_services"](instance)
self.assertEqual(services["status"], "OK")
self.assertEqual([call.args[0][2] for call in run.call_args_list], list(pbs.PBS_SERVICES))
for name in names[1:]:
result = namespace[name](instance)
self.assertTrue(result is None or result.get("not_applicable"))
def test_appimage_copies_and_imports_pbs_dependencies_outside_source_tree(self):
builder = (ROOT / "AppImage/scripts/build_appimage.sh").read_text()
modules = ("pbs_manager", "flask_pbs_routes", "auth_manager", "jwt_middleware")
copies = []
for name in modules:
matches = [line for line in builder.splitlines() if line.startswith(f'cp "$SCRIPT_DIR/{name}.py"')]
self.assertEqual(len(matches), 1, name)
copies.extend(matches)
copies.extend(line for line in builder.splitlines() if line.startswith('cp "$APPIMAGE_ROOT/../scripts/global/proxmox_platform.py"'))
self.assertEqual(len(copies), len(modules) + 1)
app = self.root / 'AppDir'; (app / 'usr/bin').mkdir(parents=True)
q = shlex.quote
shell = f'APP_DIR={q(str(app))}\nSCRIPT_DIR={q(str(ROOT / "AppImage/scripts"))}\nAPPIMAGE_ROOT={q(str(ROOT / "AppImage"))}\n' + '\n'.join(copies)
subprocess.run(['bash', '-e', '-c', shell], check=True)
code = f'import sys; sys.path.insert(0, {str(app / "usr/bin")!r}); import pbs_manager, flask_pbs_routes, proxmox_platform'
subprocess.run([sys.executable, '-I', '-B', '-c', code], cwd=self.root, check=True, capture_output=True)
class TLSTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.temporary = tempfile.TemporaryDirectory()
root = Path(cls.temporary.name)
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
subject = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, "localhost")])
certificate = (x509.CertificateBuilder().subject_name(subject).issuer_name(subject).public_key(key.public_key())
.serial_number(x509.random_serial_number()).not_valid_before(datetime.now(timezone.utc) - timedelta(minutes=1))
.not_valid_after(datetime.now(timezone.utc) + timedelta(hours=1))
.add_extension(x509.SubjectAlternativeName([x509.IPAddress(ipaddress.ip_address("127.0.0.1"))]), critical=False)
.sign(key, hashes.SHA256()))
cert_path, key_path = root / 'server.pem', root / 'server.key'
cert_path.write_bytes(certificate.public_bytes(serialization.Encoding.PEM))
key_path.write_bytes(key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()))
cls.fingerprint = hashlib.sha256(certificate.public_bytes(serialization.Encoding.DER)).hexdigest()
cls.requests = []
cls.response_status = 200
class Handler(BaseHTTPRequestHandler):
def handle(self):
try:
super().handle()
except ConnectionResetError:
pass # A rejected TLS pin closes before any HTTP request.
def do_GET(self):
cls.requests.append((self.path, self.headers.get("Authorization")))
body = json.dumps({"data": []}).encode()
self.send_response(cls.response_status)
self.send_header('Content-Length', str(len(body)))
self.end_headers(); self.wfile.write(body)
def log_message(self, *args): pass
cls.server = ThreadingHTTPServer(('127.0.0.1', 0), Handler)
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER); context.load_cert_chain(cert_path, key_path)
cls.server.socket = context.wrap_socket(cls.server.socket, server_side=True)
cls.thread = threading.Thread(target=cls.server.serve_forever, daemon=True); cls.thread.start()
@classmethod
def tearDownClass(cls):
cls.server.shutdown(); cls.server.server_close(); cls.thread.join(timeout=5); cls.temporary.cleanup()
def setUp(self):
self.requests.clear(); type(self).response_status = 200
self.connection = {"url": f'https://127.0.0.1:{self.server.server_port}',
"fingerprint": self.fingerprint, "token_id": "audit@pbs!monitor", "token_secret": "fake-secret"}
def test_real_tls_matching_pin_sends_correct_pbs_authorization(self):
self.assertEqual(pbs._remote_get(self.connection, '/status/datastore-usage'), [])
self.assertEqual(self.requests, [('/api2/json/status/datastore-usage', 'PBSAPIToken=audit@pbs!monitor:fake-secret')])
def test_bad_pin_and_untrusted_ca_send_no_http_credentials(self):
for fingerprint in ('0' * 64, ''):
with self.subTest(fingerprint=fingerprint), self.assertRaises(pbs.PBSError):
pbs._remote_get({**self.connection, "fingerprint": fingerprint}, '/status/datastore-usage')
self.assertEqual(self.requests, [])
def test_bad_credentials_and_redirects_are_rejected(self):
for status in (401, 403, 302):
type(self).response_status = status
with self.subTest(status=status), self.assertRaises(pbs.PBSError):
pbs._remote_get(self.connection, '/status/datastore-usage')
if __name__ == '__main__':
unittest.main()
@@ -0,0 +1,67 @@
"""Remote mount checks carry the key their dismissal is saved under."""
import sys
import unittest
from pathlib import Path
from types import ModuleType, SimpleNamespace
from unittest.mock import patch
SCRIPTS_DIR = Path(__file__).resolve().parents[1]
if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
_health_persistence_module = ModuleType("health_persistence")
_health_persistence_module.health_persistence = SimpleNamespace(cleanup_old_errors=lambda: None)
_health_persistence_module.disk_base_name = lambda name: str(name).replace("/dev/", "")
sys.modules.setdefault("health_persistence", _health_persistence_module)
sys.modules.setdefault("psutil", ModuleType("psutil"))
sys.modules.setdefault("flask_server", SimpleNamespace(
get_proxmox_node_name=lambda: "fixture",
get_cached_pvesh_cluster_resources_vm=lambda: [],
get_cached_vm_disk=lambda _vmid: None,
))
import health_monitor # noqa: E402
class _Persistence:
def __init__(self):
self.recorded = []
def record_error(self, **kwargs):
self.recorded.append(kwargs)
def get_active_errors(self, *args, **kwargs):
return []
def clear_error(self, key):
pass
class RemoteMountDismissal(unittest.TestCase):
def check(self, host, lxc=()):
persistence = _Persistence()
mounts = SimpleNamespace(scan_remote_mounts=lambda force: list(host),
scan_lxc_mounts=lambda force: list(lxc))
with patch.object(health_monitor, "MOUNT_MONITOR_AVAILABLE", True), \
patch.object(health_monitor, "mount_monitor", mounts, create=True), \
patch.object(health_monitor, "health_persistence", persistence):
result = health_monitor.HealthMonitor._check_remote_mounts(SimpleNamespace())
return result, persistence
def test_stale_and_read_only_mounts_carry_their_saved_key(self):
result, persistence = self.check(
[{"target": "/mnt/pve/nas", "status": "stale", "fstype": "nfs4"},
{"target": "/mnt/share", "status": "ok", "fstype": "cifs"}],
[{"target": "/media", "status": "readonly", "fstype": "nfs4", "lxc_id": "120", "lxc_name": "app"}])
checks = result["checks"]
self.assertEqual(checks["/mnt/pve/nas"]["error_key"], "mount_stale_/mnt/pve/nas")
self.assertEqual(checks["CT 120 (app) /media"]["error_key"], "mount_readonly_ct120:/media")
self.assertTrue(checks["/mnt/pve/nas"]["dismissable"])
self.assertTrue(checks["CT 120 (app) /media"]["dismissable"])
self.assertNotIn("error_key", checks["/mnt/share"])
self.assertEqual({entry["error_key"] for entry in persistence.recorded},
{"mount_stale_/mnt/pve/nas", "mount_readonly_ct120:/media"})
if __name__ == "__main__":
unittest.main()
+69
View File
@@ -0,0 +1,69 @@
const assert = require('node:assert/strict')
const fs = require('node:fs')
const path = require('node:path')
const vm = require('node:vm')
const ts = require('typescript')
const source = fs.readFileSync(path.join(__dirname, '../components/proxmox-dashboard.tsx'), 'utf8')
const tree = ts.createSourceFile('dashboard.tsx', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX)
const wrapper = tree.statements.find(node => ts.isFunctionDeclaration(node) && node.name?.text === 'ProxmoxDashboard')
assert.ok(wrapper, 'platform gate must remain separate from PVE hooks/prewarmers')
const code = ts.transpileModule(ts.createPrinter().printNode(ts.EmitHint.Unspecified, wrapper, tree), {
compilerOptions: { module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.ReactJSX },
}).outputText
const PBS = () => {}, PVE = () => {}
for (const [data, error, expected] of [
[{ product: 'pbs' }, null, PBS], [{ product: 'pve' }, null, PVE],
[undefined, new Error('older API'), PVE], [undefined, null, 'p'],
]) {
const sandbox = {
exports: {}, PBSDashboard: PBS, PVEDashboard: PVE,
useT: () => key => key, useSWR: () => ({ data, error }),
require: () => ({ jsx: (type, props) => ({ type, props }) }),
}
vm.runInNewContext(code, sandbox)
assert.equal(sandbox.exports.ProxmoxDashboard().type, expected)
}
console.log('PASS: PBS dashboard routing and loading gate do not mount PVE prewarmers')
// The PVE Backup tab keeps native guest retention alongside jobs when adding
// its PBS connection view. Exercise the shipped JSX for all three selections.
const backupSource = fs.readFileSync(path.join(__dirname, '../components/backup-tab.tsx'), 'utf8')
const backupTree = ts.createSourceFile('backup-tab.tsx', backupSource, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX)
const backupTab = backupTree.statements.find(node => ts.isFunctionDeclaration(node) && node.name?.text === 'BackupTab')
assert.ok(backupTab, 'Backup tab must keep a platform-specific view switch')
const backupCode = ts.transpileModule(ts.createPrinter().printNode(ts.EmitHint.Unspecified, backupTab, backupTree), {
compilerOptions: { module: ts.ModuleKind.CommonJS, jsx: ts.JsxEmit.ReactJSX },
}).outputText
const HostBackup = () => {}, VmBackupJobs = () => {}, BackupStorageRetention = () => {}, PBSPanel = () => {}
for (const [view, expected] of [
['host', [HostBackup]], ['guests', [VmBackupJobs, BackupStorageRetention]], ['pbs', [PBSPanel]],
]) {
const selected = []
const sandbox = {
exports: {}, HostBackup, VmBackupJobs, BackupStorageRetention, PBSPanel, DatabaseBackup: () => {},
useT: () => key => key, useState: () => [view, value => selected.push(value)],
require: () => ({ jsx: (type, props) => ({ type, props }), jsxs: (type, props) => ({ type, props }), Fragment: Symbol('Fragment') }),
}
vm.runInNewContext(backupCode, sandbox)
const visible = [], mountedHosts = [], buttons = []
const components = new Set([HostBackup, VmBackupJobs, BackupStorageRetention, PBSPanel])
function walk(node, hidden = false) {
if (Array.isArray(node)) { node.forEach(item => walk(item, hidden)); return }
if (!node || typeof node !== 'object') return
hidden ||= node.props?.className === 'hidden'
if (node.type === HostBackup) mountedHosts.push(node)
if (components.has(node.type) && !hidden) visible.push(node.type)
if (node.type === 'button') buttons.push(node)
walk(node.props?.children, hidden)
}
walk(sandbox.exports.BackupTab())
assert.deepEqual(visible, expected, `visible backup panels for ${view}`)
assert.equal(mountedHosts.length, 1, 'host backup progress must stay mounted across view changes')
assert.equal(buttons.length, 3)
assert.equal(buttons.filter(button => button.props['aria-pressed']).length, 1)
const pbsButton = buttons.find(button => button.props.children === 'pbs.title')
assert.ok(pbsButton, 'PBS navigation button must exist')
pbsButton.props.onClick()
assert.deepEqual(selected, ['pbs'])
}
console.log('PASS: host progress stays mounted, guest jobs and retention stay together, and PBS navigation remains separate')
+17 -4
View File
@@ -818,7 +818,7 @@ Note that the dev server has no Flask backend on the same host by default: the a
### Testing ### Testing
#### Offline i18n script tests #### Offline script regressions (including i18n)
From the repository root, run the complete script suite: From the repository root, run the complete script suite:
@@ -836,6 +836,14 @@ It does not launch React or require `npm install`, `pip install`, a Proxmox host
API keys, or a translation provider. Generator tests use isolated temporary API keys, or a translation provider. Generator tests use isolated temporary
catalogs and fake/forbidden providers; repository catalogs are not regenerated. catalogs and fake/forbidden providers; repository catalogs are not regenerated.
The command-description recovery regression first completes disposable copies
of the shipped catalogs with the real generator and a deterministic offline
provider, then requires a second run to make zero provider calls and zero
writes. It checks that all existing nonempty translations and the frozen
command-description arrays survive unchanged. A synthetic EN-only feature tests
new nested strings, arrays, placeholders and non-text values. New message keys
do not need to be added to a test-specific translation seed list.
Scheduler fixtures discover every shipped `lang/*.json` cache and run the actual Scheduler fixtures discover every shipped `lang/*.json` cache and run the actual
`translate` lookup at both empty-job consumers for PBS and local archive backends. `translate` lookup at both empty-job consumers for PBS and local archive backends.
They accept English fallback for missing/empty entries; complete translations are They accept English fallback for missing/empty entries; complete translations are
@@ -859,12 +867,17 @@ than silently skipping the consumer assertions. `.github/workflows/test-i18n.yml
provisions Python and Node and runs this same command on relevant pull requests provisions Python and Node and runs this same command on relevant pull requests
and main/develop pushes (or manually). It is separate from the translation and main/develop pushes (or manually). It is separate from the translation
publication workflow and has read-only repository permissions. publication workflow and has read-only repository permissions.
Its Actions name is **Offline script regressions** because it also covers menus,
downloads, scheduler behavior and other scripts. The `offline-i18n` check ID is
retained for compatibility with existing branch protection rules; a failure of
that check is not necessarily a translation failure. Read its failing test name
and traceback before changing catalogs or translation-provider settings.
#### Qualified offline `tests/` CI lanes #### Qualified offline `tests/` CI lanes
`.github/workflows/test-offline-qualified.yml` runs **only** the explicitly listed `.github/workflows/test-offline-qualified.yml` runs **only** the explicitly listed
nine Python files (including the inert active-notification payload fixture and ten Python files (including the native discovery fixtures, the inert active-notification payload fixture and
the focused health recurrence tests) and eleven the focused health recurrence tests) and twelve
Node files in `.github/scripts/run_offline_qualified.py`. Node files in `.github/scripts/run_offline_qualified.py`.
The Python runner gives each test file a private temporary directory through The Python runner gives each test file a private temporary directory through
`TMPDIR`. It removes that directory after the test file finishes. `TMPDIR`. It removes that directory after the test file finishes.
@@ -887,7 +900,7 @@ dangling symlink) rather than overwrite or reuse it. In Bash:
) )
``` ```
Requires Python 3.11, Node 22.14, npm and Bash/coreutils for the bounded shell Requires Python 3.11, Flask 3.x for the native route fixtures, Node 22.14, npm and Bash/coreutils for the bounded shell
fixtures. The separate lockfile installs only TypeScript, React, React DOM, fixtures. The separate lockfile installs only TypeScript, React, React DOM,
Lucide icons and the locked scheduler dependency; it does not build the Monitor Lucide icons and the locked scheduler dependency; it does not build the Monitor
or run npm lifecycle scripts. `--legacy-peer-deps` admits the existing Lucide or run npm lifecycle scripts. `--legacy-peer-deps` admits the existing Lucide
+66
View File
@@ -0,0 +1,66 @@
# Proxmox Backup Server support
ProxMenux detects PBS from the installed **proxmox-backup-server** package.
Installing proxmox-backup-client on PVE or another Debian system does not turn
that host into PBS. Supported maintenance platforms are PBS 3/Debian 12 and
PBS 4/Debian 13; PVE 8/9 detection remains separate. If both server products
are co-installed, PVE takes precedence.
## On a PBS host
The main CLI opens a PBS menu with package/version information, datastores,
recent tasks, the existing system-utility installer, settings and a PBS package
update. Utility installation preserves the operator's APT sources instead of
trying to find a PVE version or enabling PVE/Ceph repositories. Configure a
working subscription or no-subscription PBS repository in PBS itself if APT
reports an error.
The update refreshes configured indexes, displays the simulated full-upgrade
plan and requires confirmation before applying it. It does not run PVE repository
cleanup, major-version migration, kernel cleanup or guest scripts. The automatic
and customizable PVE post-install entry points reject PBS before adjustments.
Monitor opens a PBS dashboard with CPU, RAM, uptime, local PBS status,
datastores, recent tasks and maintenance schedules, plus logs, terminal and
Monitor authentication/HTTPS settings. PVE guest prewarmers, firewall controls
and guest-storage checks are not part of this dashboard. Health checks monitor
proxmox-backup and proxmox-backup-proxy rather than reporting absent PVE daemons.
Monitor can reuse the native PBS proxy.pem/proxy.key pair, including renewal.
Local data is read with proxmox-backup-manager JSON commands. Datastore capacity
is filesystem capacity, not the logical sum of backups; datastores sharing one
filesystem can show the same capacity. An unmounted removable datastore or a
failed check has unknown capacity, not zero/free space.
## Monitor a remote PBS from PVE
Open **Backup → Proxmox Backup Server → Manage PBS connections**. Enable local
Monitor authentication first, then add a named HTTPS endpoint (for example
https://pbs.example:8007), a dedicated API token ID (`user@pbs!monitor`) and its
secret. Give both the user and token the PBS **Audit** role on `/`, with
propagation, to view system tasks and datastore status. Token ACLs cannot exceed
the user's effective permissions. A restricted token may show only its permitted
data; denied sections are displayed as unavailable.
Use a CA-trusted certificate or copy the certificate's SHA256 fingerprint from
the PBS console/a trusted channel. With a pin, Monitor checks the peer certificate
before sending the token. With no pin, the normal system CA and hostname checks
apply. Connections go directly to the configured HTTPS endpoint and do not follow
redirects. A reverse-proxy prefix is supported; omit the port for HTTPS 443 or
include the native PBS port explicitly.
Saving tests an authenticated datastore-status endpoint. Secrets are stored
atomically with mode 0600 in `~/.config/proxmenux-monitor/pbs_servers.json`, never
returned by the API, and retained when the edit form's secret is left empty.
Connection changes require full administrator access; read-only Monitor API tokens
can read monitoring data but cannot change connections. Removing a connection
removes its saved credentials only. Monitoring does not start prune, garbage
collection, verification or sync jobs and does not modify PBS backups.
Status is cached for 15 seconds and the panel refreshes every 30 seconds. Task or
schedule permission failures are shown independently, without hiding successful
datastore data or presenting failed queries as empty healthy results.
References: [PBS command syntax](https://pbs.proxmox.com/docs/command-syntax.html),
[PBS API](https://pbs.proxmox.com/docs/api-viewer/index.html),
[PBS users, tokens and ACLs](https://pbs.proxmox.com/docs/user-management.html).
+3 -2
View File
@@ -21,7 +21,7 @@
# • whiptail (interactive terminal menus) # • whiptail (interactive terminal menus)
# • curl (downloads and connectivity checks) # • curl (downloads and connectivity checks)
# • jq (JSON parsing) # • jq (JSON parsing)
# • Python 3 + venv (for translation support) # - Installs pre-generated JSON language catalogs (no translation runtime).
# - Creates the ProxMenux base directories and configuration files: # - Creates the ProxMenux base directories and configuration files:
# • $BASE_DIR/config.json # • $BASE_DIR/config.json
# • $BASE_DIR/cache.json # • $BASE_DIR/cache.json
@@ -396,7 +396,8 @@ select_language() {
"de" "German" \ "de" "German" \
"it" "Italian" \ "it" "Italian" \
"pt" "Portuguese" \ "pt" "Portuguese" \
"sk" "Slovenčina" 3>&1 1>&2 2>&3) "sk" "Slovenčina" \
"sv" "Svenska" 3>&1 1>&2 2>&3)
if [ -z "$LANGUAGE" ]; then if [ -z "$LANGUAGE" ]; then
msg_error "No language selected. Exiting." msg_error "No language selected. Exiting."
+2 -1
View File
@@ -594,7 +594,8 @@ select_language() {
"de" "German" \ "de" "German" \
"it" "Italian" \ "it" "Italian" \
"pt" "Portuguese" \ "pt" "Portuguese" \
"sk" "Slovenčina" 3>&1 1>&2 2>&3) "sk" "Slovenčina" \
"sv" "Svenska" 3>&1 1>&2 2>&3)
if [ -z "$LANGUAGE" ]; then if [ -z "$LANGUAGE" ]; then
msg_error "No language selected. Exiting." msg_error "No language selected. Exiting."
+99 -23
View File
@@ -37,6 +37,7 @@
"/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (Proxmox-Standard-vzdump-Pfad)", "/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (Proxmox-Standard-vzdump-Pfad)",
"/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (Proxmox-Standard)", "/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (Proxmox-Standard)",
"1777 = sticky bit + rwx for all. No shared group needed.": "1777 = Sticky Bit + RWX für alle. Keine gemeinsame Gruppe erforderlich.", "1777 = sticky bit + rwx for all. No shared group needed.": "1777 = Sticky Bit + RWX für alle. Keine gemeinsame Gruppe erforderlich.",
"====== PBS UPDATE COMPLETED ======": "====== PBS-UPDATE ABGESCHLOSSEN ======",
"====== PVE UPDATE COMPLETED ======": "====== PVE-UPDATE ABGESCHLOSSEN ======", "====== PVE UPDATE COMPLETED ======": "====== PVE-UPDATE ABGESCHLOSSEN ======",
">> Create a new backup job <<": ">> Einen neuen Sicherungsauftrag erstellen <<", ">> Create a new backup job <<": ">> Einen neuen Sicherungsauftrag erstellen <<",
">> Save and return <<": ">> Speichern und zurück <<", ">> Save and return <<": ">> Speichern und zurück <<",
@@ -87,6 +88,7 @@
"A multi-container application is not recreated: its containers are updated together.": "Eine Multi-Container-Anwendung wird nicht neu erstellt: Ihre Container werden zusammen aktualisiert.", "A multi-container application is not recreated: its containers are updated together.": "Eine Multi-Container-Anwendung wird nicht neu erstellt: Ihre Container werden zusammen aktualisiert.",
"A new ProxMenux version is available:": "Eine neue ProxMenux-Version ist verfügbar:", "A new ProxMenux version is available:": "Eine neue ProxMenux-Version ist verfügbar:",
"A new kernel is staged for the next boot:": "Ein neuer Kernel wird für den nächsten Start bereitgestellt:", "A new kernel is staged for the next boot:": "Ein neuer Kernel wird für den nächsten Start bereitgestellt:",
"A newer kernel is installed. Reboot before the next backup window to use it.": "Ein neuerer Kernel ist installiert. Starten Sie vor dem nächsten Backup-Fenster einen Neustart, um es zu verwenden.",
"A newer version is available:": "Eine neuere Version ist verfügbar:", "A newer version is available:": "Eine neuere Version ist verfügbar:",
"A pending operation exists for": "Eine ausstehende operation existiert für", "A pending operation exists for": "Eine ausstehende operation existiert für",
"A pending stack assembly already exists; it is not overwritten": "Eine ausstehende Stapelanordnung existiert bereits; sie wird nicht überschrieben", "A pending stack assembly already exists; it is not overwritten": "Eine ausstehende Stapelanordnung existiert bereits; sie wird nicht überschrieben",
@@ -169,6 +171,8 @@
"APT language configuration updated": "APT-Sprachkonfiguration aktualisiert", "APT language configuration updated": "APT-Sprachkonfiguration aktualisiert",
"APT language downloads restored": "APT-Sprachdownloads wiederhergestellt", "APT language downloads restored": "APT-Sprachdownloads wiederhergestellt",
"APT package lists updated": "APT-Paketlisten aktualisiert", "APT package lists updated": "APT-Paketlisten aktualisiert",
"APT refresh failed. Check the PBS subscription and repositories in Administration > Repositories. Log:": "APT-Aktualisierung fehlgeschlagen. Überprüfen Sie das PBS-Abonnement und die Repositorys unter Administration > Repositorys. Protokoll:",
"APT refresh failed. Check the PBS subscription and repositories in the native PBS interface; no source was changed.": "APT-Aktualisierung fehlgeschlagen. Prüfen Sie das PBS-Abonnement und die Repositories in der nativen PBS-Oberfläche; es wurden keine Paketquellen geändert.",
"APT update failed. Check repository access; NVIDIA installation stopped.": "APT-Update fehlgeschlagen. Überprüfen Sie den Repository-Zugriff. Die Installation von NVIDIA wurde gestoppt.", "APT update failed. Check repository access; NVIDIA installation stopped.": "APT-Update fehlgeschlagen. Überprüfen Sie den Repository-Zugriff. Die Installation von NVIDIA wurde gestoppt.",
"Aborted": "Abgebrochen", "Aborted": "Abgebrochen",
"About to restore": "Wird gerade wiederhergestellt", "About to restore": "Wird gerade wiederhergestellt",
@@ -425,6 +429,7 @@
"Apply": "Anwenden", "Apply": "Anwenden",
"Apply AMD CPU fixes": "AMD-CPU-Korrekturen anwenden", "Apply AMD CPU fixes": "AMD-CPU-Korrekturen anwenden",
"Apply Available Updates": "Verfügbare Updates anwenden", "Apply Available Updates": "Verfügbare Updates anwenden",
"Apply PBS package upgrade": "PBS-Paketaktualisierung anwenden",
"Apply and restart services:": "Dienste anwenden und neu starten:", "Apply and restart services:": "Dienste anwenden und neu starten:",
"Apply available updates": "Verfügbare Updates anwenden", "Apply available updates": "Verfügbare Updates anwenden",
"Apply boot/initramfs changes": "Übernehmen Sie Boot-/Initramfs-Änderungen", "Apply boot/initramfs changes": "Übernehmen Sie Boot-/Initramfs-Änderungen",
@@ -438,12 +443,15 @@
"Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Optionen aus der aktuellen Katalogvorlage anwenden? Es können neue erforderliche Pfade und Einstellungen angefordert werden. Überprüfen Sie die resultierende Konfiguration, bevor Sie CT neu erstellen.", "Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Optionen aus der aktuellen Katalogvorlage anwenden? Es können neue erforderliche Pfade und Einstellungen angefordert werden. Überprüfen Sie die resultierende Konfiguration, bevor Sie CT neu erstellen.",
"Apply read+write access for 'others' on the host directory?": "Lese- und Schreibzugriff für „Andere“ auf das Hostverzeichnis anwenden?", "Apply read+write access for 'others' on the host directory?": "Lese- und Schreibzugriff für „Andere“ auf das Hostverzeichnis anwenden?",
"Apply the options from the current catalog template? Your data and configuration are kept.": "Wenden Sie die Optionen aus der aktuellen Katalogvorlage an? Ihre Daten und Konfiguration werden gespeichert.", "Apply the options from the current catalog template? Your data and configuration are kept.": "Wenden Sie die Optionen aus der aktuellen Katalogvorlage an? Ihre Daten und Konfiguration werden gespeichert.",
"Apply the reviewed package upgrade now? Services may restart and packages listed for removal will be removed.": "Die geprüfte Paketaktualisierung jetzt anwenden? Dienste können neu starten und zur Entfernung aufgeführte Pakete werden entfernt.",
"Apply these changes?": "Diese Änderungen anwenden?", "Apply these changes?": "Diese Änderungen anwenden?",
"Applying AMD-specific fixes...": "Anwenden von AMD-spezifischen Korrekturen...", "Applying AMD-specific fixes...": "Anwenden von AMD-spezifischen Korrekturen...",
"Applying CPU and RAM changes without rebuilding the container...": "CPU- und RAM-Änderungen werden ohne Neuerstellung des Containers angewendet...",
"Applying Changes": "Anwenden von Änderungen", "Applying Changes": "Anwenden von Änderungen",
"Applying Controller/NVMe passthrough to VM": "Anwenden von Controller/NVMe-Passthrough auf die VM", "Applying Controller/NVMe passthrough to VM": "Anwenden von Controller/NVMe-Passthrough auf die VM",
"Applying balanced memory optimization settings...": "Ausgewogene Einstellungen zur Speicheroptimierung werden angewendet...", "Applying balanced memory optimization settings...": "Ausgewogene Einstellungen zur Speicheroptimierung werden angewendet...",
"Applying changes safely...": "Änderungen sicher anwenden...", "Applying changes safely...": "Änderungen sicher anwenden...",
"Applying container settings without rebuilding the container...": "Containereinstellungen werden ohne Neuerstellung des Containers angewendet...",
"Applying custom restore": "Benutzerdefinierte Wiederherstellung anwenden", "Applying custom restore": "Benutzerdefinierte Wiederherstellung anwenden",
"Applying default VM configuration": "Anwenden der Standard-VM-Konfiguration", "Applying default VM configuration": "Anwenden der Standard-VM-Konfiguration",
"Applying host permissions for unprivileged LXC bind-mounts...": "Anwenden von Hostberechtigungen für nicht privilegierte LXC-Bind-Mounts ...", "Applying host permissions for unprivileged LXC bind-mounts...": "Anwenden von Hostberechtigungen für nicht privilegierte LXC-Bind-Mounts ...",
@@ -535,6 +543,7 @@
"Available Borg targets:": "Verfügbare Borg-Ziele:", "Available Borg targets:": "Verfügbare Borg-Ziele:",
"Available Disks on Host": "Verfügbare Festplatten auf dem Host", "Available Disks on Host": "Verfügbare Festplatten auf dem Host",
"Available ISO Images": "Verfügbare ISO-Images", "Available ISO Images": "Verfügbare ISO-Images",
"Available PBS Version": "Verfügbare PBS-Version",
"Available PBS repositories:": "Verfügbare PBS-Repositories:", "Available PBS repositories:": "Verfügbare PBS-Repositories:",
"Available PVE Version": "Verfügbare PVE-Version", "Available PVE Version": "Verfügbare PVE-Version",
"Available Shares:": "Verfügbare Aktien:", "Available Shares:": "Verfügbare Aktien:",
@@ -746,6 +755,7 @@
"Cannot verify the reused disk:": "Kann die wiederverwendete Festplatte nicht überprüfen:", "Cannot verify the reused disk:": "Kann die wiederverwendete Festplatte nicht überprüfen:",
"Cannot write the image archive to {storage}. Check storage access and mount status.": "Das Bildarchiv kann nicht in {storage} geschrieben werden. Überprüfen Sie den Speicherzugriff und den Bereitstellungsstatus.", "Cannot write the image archive to {storage}. Check storage access and mount status.": "Das Bildarchiv kann nicht in {storage} geschrieben werden. Überprüfen Sie den Speicherzugriff und den Bereitstellungsstatus.",
"Capabilities cannot be kept and all dropped at the same time": "Fähigkeiten können nicht gehalten werden und alle gleichzeitig fallen gelassen werden", "Capabilities cannot be kept and all dropped at the same time": "Fähigkeiten können nicht gehalten werden und alle gleichzeitig fallen gelassen werden",
"Catalog options": "Katalogoptionen",
"Category": "Kategorie", "Category": "Kategorie",
"Caution: Maximum mode generates more heat.": "Achtung: Im Maximalmodus entsteht mehr Wärme.", "Caution: Maximum mode generates more heat.": "Achtung: Im Maximalmodus entsteht mehr Wärme.",
"Ceph check skipped by user flag (--ignore-ceph-check)": "Ceph-Prüfung durch Benutzerflag übersprungen (--ignore-ceph-check)", "Ceph check skipped by user flag (--ignore-ceph-check)": "Ceph-Prüfung durch Benutzerflag übersprungen (--ignore-ceph-check)",
@@ -880,6 +890,7 @@
"Choose a schedule (systemd calendar-event syntax):": "Wählen Sie einen Zeitplan (systemd-Kalender-Ereignis-Syntax):", "Choose a schedule (systemd calendar-event syntax):": "Wählen Sie einen Zeitplan (systemd-Kalender-Ereignis-Syntax):",
"Choose a script to execute:": "Wählen Sie ein auszuführendes Skript aus:", "Choose a script to execute:": "Wählen Sie ein auszuführendes Skript aus:",
"Choose a server:": "Wählen Sie einen Server:", "Choose a server:": "Wählen Sie einen Server:",
"Choose a setting. Changes are applied only after the final review.": "Wähle eine Einstellung. Änderungen werden erst nach der abschließenden Prüfung angewendet.",
"Choose a share to mount:": "Wählen Sie eine Freigabe zum Mounten aus:", "Choose a share to mount:": "Wählen Sie eine Freigabe zum Mounten aus:",
"Choose a specific supported GPU or USB node": "Wählen Sie einen bestimmten unterstützten GPU- oder USB-Knoten aus", "Choose a specific supported GPU or USB node": "Wählen Sie einen bestimmten unterstützten GPU- oder USB-Knoten aus",
"Choose action for this controller/NVMe:": "Wählen Sie eine Aktion für diesen Controller/NVMe:", "Choose action for this controller/NVMe:": "Wählen Sie eine Aktion für diesen Controller/NVMe:",
@@ -960,6 +971,7 @@
"Code-server is VS Code running on a remote server, accessible through the browser.": "Code-Server ist VS Code, der auf einem entfernten Server läuft und über den Browser zugänglich ist.", "Code-server is VS Code running on a remote server, accessible through the browser.": "Code-Server ist VS Code, der auf einem entfernten Server läuft und über den Browser zugänglich ist.",
"CodeProject.AI Server": "CodeProject. AI Server", "CodeProject.AI Server": "CodeProject. AI Server",
"Command": "Befehl", "Command": "Befehl",
"Command failed": "Befehl fehlgeschlagen",
"Command override for an unknown service:": "Command Override für einen unbekannten Dienst:", "Command override for an unknown service:": "Command Override für einen unbekannten Dienst:",
"Commenting any residual Bookworm lines in *.list...": "Kommentieren aller verbleibenden Bookworm-Zeilen in *.list...", "Commenting any residual Bookworm lines in *.list...": "Kommentieren aller verbleibenden Bookworm-Zeilen in *.list...",
"Commenting legacy PVE 8 repository .list files (if any)...": "Kommentieren älterer PVE 8-Repository-.list-Dateien (falls vorhanden) ...", "Commenting legacy PVE 8 repository .list files (if any)...": "Kommentieren älterer PVE 8-Repository-.list-Dateien (falls vorhanden) ...",
@@ -1516,6 +1528,7 @@
"Current NVIDIA inventory resolved: a refresh is required": "Aktuelles NVIDIA-Inventar behoben: ein Refresh ist required", "Current NVIDIA inventory resolved: a refresh is required": "Aktuelles NVIDIA-Inventar behoben: ein Refresh ist required",
"Current NVIDIA inventory resolved: no refresh is required": "Aktuelles NVIDIA-Inventar behoben: kein Refresh ist required", "Current NVIDIA inventory resolved: no refresh is required": "Aktuelles NVIDIA-Inventar behoben: kein Refresh ist required",
"Current Network Configuration": "Aktuelle Netzwerkkonfiguration", "Current Network Configuration": "Aktuelle Netzwerkkonfiguration",
"Current PBS Version": "Aktuelle PBS-Version",
"Current PVE Version": "Aktuelle PVE-Version", "Current PVE Version": "Aktuelle PVE-Version",
"Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Aktuelle ProxMenux-Hostskripte registrieren Remote-Freigaben mithilfe von pvesm als Proxmox-Speicher.", "Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Aktuelle ProxMenux-Hostskripte registrieren Remote-Freigaben mithilfe von pvesm als Proxmox-Speicher.",
"Current ProxMenux version:": "Aktuelle ProxMenux-Version:", "Current ProxMenux version:": "Aktuelle ProxMenux-Version:",
@@ -1630,8 +1643,8 @@
"Default Proxmox logo applied": "Standardmäßiges Proxmox-Logo angewendet", "Default Proxmox logo applied": "Standardmäßiges Proxmox-Logo angewendet",
"Default is none (video-only passthrough). Use SPACE to toggle selections.": "Der Standardwert ist „Keine“ (nur Video-Passthrough). Verwenden Sie die LEERTASTE, um die Auswahl umzuschalten.", "Default is none (video-only passthrough). Use SPACE to toggle selections.": "Der Standardwert ist „Keine“ (nur Video-Passthrough). Verwenden Sie die LEERTASTE, um die Auswahl umzuschalten.",
"Default journald configuration restored": "Standard-Journald-Konfiguration wiederhergestellt", "Default journald configuration restored": "Standard-Journald-Konfiguration wiederhergestellt",
"Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "Der Standardspeicherort ist /mnt/<Name>. Die Freigabe wird hier auf dem Host mit offenen Berechtigungen gemountet, sodass ein unprivilegierter LXC sie binden, mounten und darauf schreiben kann. Für den LXC-Zugriff müssen Sie diesen Pfad mit dem LXC Mount Manager binden.", "Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "Der Standardspeicherort ist /mnt/<name>. Die Freigabe wird hier auf dem Host mit offenen Berechtigungen eingehängt, sodass ein unprivilegierter LXC sie per Bind-Mount einbinden und darauf schreiben kann. Verwenden Sie dafür den LXC Mount Manager.",
"Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "Der Standardspeicherort ist /mnt/<Name>. Die Freigabe wird hier auf dem Host gemountet. Verwenden Sie diesen Pfad in /etc/fstab. Für den LXC-Zugriff müssen Sie diesen Pfad mit dem LXC Mount Manager binden.", "Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "Der Standardspeicherort ist /mnt/<name>. Die Freigabe wird hier auf dem Host eingehängt. Verwenden Sie diesen Pfad in /etc/fstab. Für den Zugriff aus einem LXC binden Sie ihn mit dem LXC Mount Manager als Bind-Mount ein.",
"Default login": "Standard-Login", "Default login": "Standard-Login",
"Default options": "Standardoptionen", "Default options": "Standardoptionen",
"Default options read/write": "Standardoptionen Lesen/Schreiben", "Default options read/write": "Standardoptionen Lesen/Schreiben",
@@ -1681,6 +1694,7 @@
"Detected Coral hardware:": "Erkannte Coral-Hardware:", "Detected Coral hardware:": "Erkannte Coral-Hardware:",
"Detected GPU(s):": "Erkannte GPU(s):", "Detected GPU(s):": "Erkannte GPU(s):",
"Detected Intel CPU": "Intel-CPU erkannt", "Detected Intel CPU": "Intel-CPU erkannt",
"Detected Proxmox Backup Server": "Proxmox Backup Server erkannt",
"Detected Proxmox VE": "Proxmox VE erkannt", "Detected Proxmox VE": "Proxmox VE erkannt",
"Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Proxmox VE 8.x erkannt – Ceph Squid für Debian installieren", "Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Proxmox VE 8.x erkannt – Ceph Squid für Debian installieren",
"Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Proxmox VE 9.x erkannt – Ceph Squid für Debian Trixie installieren", "Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Proxmox VE 9.x erkannt – Ceph Squid für Debian Trixie installieren",
@@ -1762,6 +1776,9 @@
"Disabling and stopping rpcbind service and socket...": "Rpcbind-Dienst und Socket deaktivieren und stoppen ...", "Disabling and stopping rpcbind service and socket...": "Rpcbind-Dienst und Socket deaktivieren und stoppen ...",
"Disabling non-free firmware warnings...": "Warnungen zu nicht kostenloser Firmware werden deaktiviert...", "Disabling non-free firmware warnings...": "Warnungen zu nicht kostenloser Firmware werden deaktiviert...",
"Disabling portmapper/rpcbind for security...": "Portmapper/rpcbind wird aus Sicherheitsgründen deaktiviert...", "Disabling portmapper/rpcbind for security...": "Portmapper/rpcbind wird aus Sicherheitsgründen deaktiviert...",
"Discard changes": "Änderungen verwerfen",
"Discard unapplied changes?": "Noch nicht angewendete Änderungen verwerfen?",
"Discover NFS exports on a server": "NFS-Exporte eines Servers ermitteln",
"Discover targets on portal:": "Entdecken Sie Ziele auf dem Portal:", "Discover targets on portal:": "Entdecken Sie Ziele auf dem Portal:",
"Discovering iSCSI targets...": "iSCSI-Ziele werden ermittelt...", "Discovering iSCSI targets...": "iSCSI-Ziele werden ermittelt...",
"Discovery successful": "Entdeckung erfolgreich", "Discovery successful": "Entdeckung erfolgreich",
@@ -1841,6 +1858,7 @@
"Do you want to proceed with the system update?": "Möchten Sie mit der Systemaktualisierung fortfahren?", "Do you want to proceed with the system update?": "Möchten Sie mit der Systemaktualisierung fortfahren?",
"Do you want to proceed?": "Möchten Sie fortfahren?", "Do you want to proceed?": "Möchten Sie fortfahren?",
"Do you want to reboot now?": "Möchten Sie jetzt neu starten?", "Do you want to reboot now?": "Möchten Sie jetzt neu starten?",
"Do you want to remove the Proxmox subscription banner from the Proxmox Backup Server web interface?": "Möchten Sie das Proxmox-Abonnementbanner von der Proxmox Backup Server-Weboberfläche entfernen?",
"Do you want to remove the stale entry from vfio.conf and continue?": "Möchten Sie den veralteten Eintrag aus vfio.conf entfernen und fortfahren?", "Do you want to remove the stale entry from vfio.conf and continue?": "Möchten Sie den veralteten Eintrag aus vfio.conf entfernen und fortfahren?",
"Do you want to remove the stale entry from vfio.conf?": "Möchten Sie den veralteten Eintrag aus vfio.conf entfernen?", "Do you want to remove the stale entry from vfio.conf?": "Möchten Sie den veralteten Eintrag aus vfio.conf entfernen?",
"Do you want to restart the network service now to apply changes?": "Möchten Sie den Netzwerkdienst jetzt neu starten, um die Änderungen zu übernehmen?", "Do you want to restart the network service now to apply changes?": "Möchten Sie den Netzwerkdienst jetzt neu starten, um die Änderungen zu übernehmen?",
@@ -2018,7 +2036,7 @@
"Enter VM ID:": "Geben Sie die VM-ID ein:", "Enter VM ID:": "Geben Sie die VM-ID ein:",
"Enter VM/CT ID:": "Geben Sie die VM/CT-ID ein:", "Enter VM/CT ID:": "Geben Sie die VM/CT-ID ein:",
"Enter ZFS pool name for the selected disk:": "Geben Sie den ZFS-Poolnamen für die ausgewählte Festplatte ein:", "Enter ZFS pool name for the selected disk:": "Geben Sie den ZFS-Poolnamen für die ausgewählte Festplatte ein:",
"Enter a name for the mount point (used as /mnt/<name>):": "Geben Sie einen Namen für den Mount-Punkt ein (verwendet als /mnt/<Name>):", "Enter a name for the mount point (used as /mnt/<name>):": "Geben Sie einen Namen für den Mountpoint ein (wird als /mnt/<name> verwendet):",
"Enter a name for the new virtual machine:": "Geben Sie einen Namen für die neue virtuelle Maschine ein:", "Enter a name for the new virtual machine:": "Geben Sie einen Namen für die neue virtuelle Maschine ein:",
"Enter a non-empty password or access token on a single line.": "Geben Sie in einer einzelnen Zeile ein nicht leeres Passwort oder Zugriffstoken ein.", "Enter a non-empty password or access token on a single line.": "Geben Sie in einer einzelnen Zeile ein nicht leeres Passwort oder Zugriffstoken ein.",
"Enter a number, or write or paste a command:": "Geben Sie eine Zahl ein oder schreiben oder fügen Sie einen Befehl ein:", "Enter a number, or write or paste a command:": "Geben Sie eine Zahl ein oder schreiben oder fügen Sie einen Befehl ein:",
@@ -2166,7 +2184,7 @@
"Example: pct stop 110": "Beispiel: pct stop 110", "Example: pct stop 110": "Beispiel: pct stop 110",
"Example: pct stop 114": "Beispiel: pct stop 114", "Example: pct stop 114": "Beispiel: pct stop 114",
"Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Beispiel: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup", "Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Beispiel: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup",
"Example: pvesm path local-lvm:vm-114-disk-0": "Beispiel: pvesm-Pfad local-lvm:vm-114-disk-0", "Example: pvesm path local-lvm:vm-114-disk-0": "Beispiel: pvesm path local-lvm:vm-114-disk-0",
"Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Beispiel: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf", "Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Beispiel: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf",
"Examples:": "Beispiele:", "Examples:": "Beispiele:",
"Execute destructive rollback?": "Destruktives Rollback ausführen?", "Execute destructive rollback?": "Destruktives Rollback ausführen?",
@@ -2266,6 +2284,7 @@
"Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Das Klonen des log2ram-Repositorys ist fehlgeschlagen. Überprüfen Sie /tmp/log2ram_install.log", "Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Das Klonen des log2ram-Repositorys ist fehlgeschlagen. Überprüfen Sie /tmp/log2ram_install.log",
"Failed to configure EFI disk": "Die EFI-Festplatte konnte nicht konfiguriert werden", "Failed to configure EFI disk": "Die EFI-Festplatte konnte nicht konfiguriert werden",
"Failed to configure IOMMU automatically.": "IOMMU konnte nicht automatisch konfiguriert werden.", "Failed to configure IOMMU automatically.": "IOMMU konnte nicht automatisch konfiguriert werden.",
"Failed to configure Proxmox Backup Server repositories": "Proxmox Backup Server-Repositorys konnten nicht konfiguriert werden",
"Failed to configure Proxmox repositories": "Proxmox-Repositorys konnten nicht konfiguriert werden", "Failed to configure Proxmox repositories": "Proxmox-Repositorys konnten nicht konfiguriert werden",
"Failed to configure TPM device in VM": "Das TPM-Gerät konnte in der VM nicht konfiguriert werden", "Failed to configure TPM device in VM": "Das TPM-Gerät konnte in der VM nicht konfiguriert werden",
"Failed to configure repositories.": "Die Repositorys konnten nicht konfiguriert werden.", "Failed to configure repositories.": "Die Repositorys konnten nicht konfiguriert werden.",
@@ -2477,7 +2496,7 @@
"For a consistent export, the VM should be stopped.": "Für einen konsistenten Export sollte die VM gestoppt werden.", "For a consistent export, the VM should be stopped.": "Für einen konsistenten Export sollte die VM gestoppt werden.",
"For any question, issue or bug report, please use the official repository": "Für Fragen, Probleme oder Fehlerberichte nutzen Sie bitte das offizielle Repository", "For any question, issue or bug report, please use the official repository": "Für Fragen, Probleme oder Fehlerberichte nutzen Sie bitte das offizielle Repository",
"For dedicated GPUs, FLR is required by this policy to reduce VM": "Für dedizierte GPUs ist gemäß dieser Richtlinie FLR erforderlich, um VM zu reduzieren", "For dedicated GPUs, FLR is required by this policy to reduce VM": "Für dedizierte GPUs ist gemäß dieser Richtlinie FLR erforderlich, um VM zu reduzieren",
"For example: pct enter 101": "Beispiel: pct Geben Sie 101 ein", "For example: pct enter 101": "Zum Beispiel: pct enter 101",
"For guest shares add: --options guest": "Für Gastfreigaben hinzufügen: --options Guest", "For guest shares add: --options guest": "Für Gastfreigaben hinzufügen: --options Guest",
"For security reasons, the system cannot format it.": "Aus Sicherheitsgründen kann das System es nicht formatieren.", "For security reasons, the system cannot format it.": "Aus Sicherheitsgründen kann das System es nicht formatieren.",
"For unprivileged containers, use instead:": "Verwenden Sie für nichtprivilegierte Container stattdessen:", "For unprivileged containers, use instead:": "Verwenden Sie für nichtprivilegierte Container stattdessen:",
@@ -2889,8 +2908,8 @@
"If notifications are enabled (Telegram/Discord/ntfy/...), you will receive a \"Host restore finished\" message when all background tasks complete.": "Wenn Benachrichtigungen aktiviert sind (Telegram/Discord/ntfy/...), erhalten Sie die Meldung „Host-Wiederherstellung abgeschlossen“, wenn alle Hintergrundaufgaben abgeschlossen sind.", "If notifications are enabled (Telegram/Discord/ntfy/...), you will receive a \"Host restore finished\" message when all background tasks complete.": "Wenn Benachrichtigungen aktiviert sind (Telegram/Discord/ntfy/...), erhalten Sie die Meldung „Host-Wiederherstellung abgeschlossen“, wenn alle Hintergrundaufgaben abgeschlossen sind.",
"If passthrough fails on Windows: install RadeonResetBugFix.": "Wenn Passthrough unter Windows fehlschlägt: Installieren Sie RadeonResetBugFix.", "If passthrough fails on Windows: install RadeonResetBugFix.": "Wenn Passthrough unter Windows fehlschlägt: Installieren Sie RadeonResetBugFix.",
"If path not accessible (ZFS/BTRFS):": "Wenn der Pfad nicht zugänglich ist (ZFS/BTRFS):", "If path not accessible (ZFS/BTRFS):": "Wenn der Pfad nicht zugänglich ist (ZFS/BTRFS):",
"If pvesm path returned a DEVICE (LVM):": "Wenn der pvesm-Pfad ein GERÄT (LVM) zurückgegeben hat:", "If pvesm path returned a DEVICE (LVM):": "Wenn pvesm path ein GERÄT (LVM) zurückgegeben hat:",
"If pvesm path returned a DIRECTORY (ZFS/BTRFS):": "Wenn der pvesm-Pfad ein VERZEICHNIS (ZFS/BTRFS) zurückgegeben hat:", "If pvesm path returned a DIRECTORY (ZFS/BTRFS):": "Wenn pvesm path ein VERZEICHNIS (ZFS/BTRFS) zurückgegeben hat:",
"If repositories error:": "Bei Repository-Fehler:", "If repositories error:": "Bei Repository-Fehler:",
"If the container had already been changed, its previous native backup is restored. Shared host directories are not reverted. The disks of the failed attempt are removed when the cleanup succeeds.": "Wenn der Container bereits geändert wurde, wird sein vorheriges natives Backup wiederhergestellt. Freigegebene Hostverzeichnisse werden nicht zurückgesetzt. Die Festplatten des fehlgeschlagenen Versuchs werden entfernt, wenn die Bereinigung erfolgreich ist.", "If the container had already been changed, its previous native backup is restored. Shared host directories are not reverted. The disks of the failed attempt are removed when the cleanup succeeds.": "Wenn der Container bereits geändert wurde, wird sein vorheriges natives Backup wiederhergestellt. Freigegebene Hostverzeichnisse werden nicht zurückgesetzt. Die Festplatten des fehlgeschlagenen Versuchs werden entfernt, wenn die Bereinigung erfolgreich ist.",
"If the host freezes, remove hostpci entries from": "Wenn der Host einfriert, entfernen Sie die Hostpci-Einträge aus", "If the host freezes, remove hostpci entries from": "Wenn der Host einfriert, entfernen Sie die Hostpci-Einträge aus",
@@ -2973,6 +2992,7 @@
"Inaccessible device during VM startup": "Während des VM-Starts ist kein Zugriff auf das Gerät möglich", "Inaccessible device during VM startup": "Während des VM-Starts ist kein Zugriff auf das Gerät möglich",
"Inactive": "Inaktiv", "Inactive": "Inaktiv",
"Inactive (entry in fstab, not currently mounted)": "Inaktiv (Eintrag in fstab, derzeit nicht gemountet)", "Inactive (entry in fstab, not currently mounted)": "Inaktiv (Eintrag in fstab, derzeit nicht gemountet)",
"Include these changes in oci-compose?": "Diese Änderungen in oci-compose einschließen?",
"Include these resources in oci-compose?": "Diese Ressourcen in oci-compose einschließen?", "Include these resources in oci-compose?": "Diese Ressourcen in oci-compose einschließen?",
"Includes /etc/network (may drop SSH immediately)": "Enthält /etc/network (kann SSH sofort löschen)", "Includes /etc/network (may drop SSH immediately)": "Enthält /etc/network (kann SSH sofort löschen)",
"Includes /etc/zfs": "Enthält /etc/zfs", "Includes /etc/zfs": "Enthält /etc/zfs",
@@ -3206,9 +3226,11 @@
"Interrupted operation:": "Unterbrochene operation:", "Interrupted operation:": "Unterbrochene operation:",
"Interrupted stack operation found": "Unterbrochener Stapel operation gefunden", "Interrupted stack operation found": "Unterbrochener Stapel operation gefunden",
"Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.": "Ungültiger „proxmox-ve“-Kandidat (nicht 9.x oder keiner). Bitte überprüfen Sie Ihre Repository-Konfiguration und Ihr Netzwerk und versuchen Sie es dann erneut.", "Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.": "Ungültiger „proxmox-ve“-Kandidat (nicht 9.x oder keiner). Bitte überprüfen Sie Ihre Repository-Konfiguration und Ihr Netzwerk und versuchen Sie es dann erneut.",
"Invalid ALLOWED_HOSTS value": "Wert für ungültig ALLOWED HOSTs", "Invalid ALLOWED_HOSTS value": "Ungültiger Wert für ALLOWED_HOSTS",
"Invalid Home Assistant OS check timeout": "Invalid Home Assistant OS Check Timeout", "Invalid Home Assistant OS check timeout": "Invalid Home Assistant OS Check Timeout",
"Invalid ID": "Ungültige ID", "Invalid ID": "Ungültige ID",
"Invalid IPv4 address": "Ungültige IPv4-Adresse",
"Invalid IPv4 gateway": "Ungültiges IPv4-Gateway",
"Invalid Intel render path": "Ungültiger Intel-Renderpfad", "Invalid Intel render path": "Ungültiger Intel-Renderpfad",
"Invalid Jellyfin path:": "Ungültige Jellyfin-Strecke:", "Invalid Jellyfin path:": "Ungültige Jellyfin-Strecke:",
"Invalid MAC address:": "Ungültige MAC-Adresse:", "Invalid MAC address:": "Ungültige MAC-Adresse:",
@@ -3241,6 +3263,7 @@
"Invalid administrator email": "E-Mail des ungültigen Administrators", "Invalid administrator email": "E-Mail des ungültigen Administrators",
"Invalid administrator user name": "Ungültiger Administrator Benutzername", "Invalid administrator user name": "Ungültiger Administrator Benutzername",
"Invalid base VMID": "Ungültige Basis VMID", "Invalid base VMID": "Ungültige Basis VMID",
"Invalid bridge": "Ungültige Bridge",
"Invalid check package:": "Ungültiges Kontrollpaket:", "Invalid check package:": "Ungültiges Kontrollpaket:",
"Invalid configuration path:": "Ungültiger Konfigurationspfad:", "Invalid configuration path:": "Ungültiger Konfigurationspfad:",
"Invalid consume/export volumes": "Ungültige Verbrauchs-/Ausfuhrmengen", "Invalid consume/export volumes": "Ungültige Verbrauchs-/Ausfuhrmengen",
@@ -3304,6 +3327,7 @@
"Invalid remote name": "Name der ungültigen Fernbedienung", "Invalid remote name": "Name der ungültigen Fernbedienung",
"Invalid remote path": "Ungültiger Fernpfad", "Invalid remote path": "Ungültiger Fernpfad",
"Invalid repair version:": "Ungültige Reparaturversion:", "Invalid repair version:": "Ungültige Reparaturversion:",
"Invalid resource value": "Ungültiger Ressourcenwert",
"Invalid resources": "Ungültige Mittel", "Invalid resources": "Ungültige Mittel",
"Invalid restored volume path": "Ungültiger wiederhergestellter Volumenpfad", "Invalid restored volume path": "Ungültiger wiederhergestellter Volumenpfad",
"Invalid running state": "Invaliditätsstatus", "Invalid running state": "Invaliditätsstatus",
@@ -3322,6 +3346,7 @@
"Invalid stack members": "Ungültige Stapelmitglieder", "Invalid stack members": "Ungültige Stapelmitglieder",
"Invalid stack name": "Name des ungültigen Stacks", "Invalid stack name": "Name des ungültigen Stacks",
"Invalid stack operation": "Ungültiger Stapel operation", "Invalid stack operation": "Ungültiger Stapel operation",
"Invalid storage": "Ungültiger Speicher",
"Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "Ungültige Speicher-ID. Verwenden Sie nur Buchstaben, Zahlen, Bindestriche und Unterstriche.", "Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "Ungültige Speicher-ID. Verwenden Sie nur Buchstaben, Zahlen, Bindestriche und Unterstriche.",
"Invalid storage name": "Ungültiger Speichername", "Invalid storage name": "Ungültiger Speichername",
"Invalid suite application": "Anwendung ungültiger Suiten", "Invalid suite application": "Anwendung ungültiger Suiten",
@@ -3436,7 +3461,7 @@
"Keyfile removed.": "Schlüsseldatei entfernt.", "Keyfile removed.": "Schlüsseldatei entfernt.",
"Keyrings method failed; trying apt-key fallback": "Keyrings-Methode fehlgeschlagen; Ich versuche einen Apt-Key-Fallback", "Keyrings method failed; trying apt-key fallback": "Keyrings-Methode fehlgeschlagen; Ich versuche einen Apt-Key-Fallback",
"KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - Eine Cross-Plattform und Open Source Electronics Design Automation Suite.", "KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - Eine Cross-Plattform und Open Source Electronics Design Automation Suite.",
"Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai hat kein Standardkonto. Geben Sie den Container ein mit: pct enter {main vmid}", "Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai hat kein Standardkonto. Öffnen Sie eine Shell im Container mit: pct enter {main_vmid}",
"Kimai is a professional grade time-tracking application, free and open-source.": "Kimai ist eine professionelle Time-Tracking-Anwendung, kostenlos und Open-Source.", "Kimai is a professional grade time-tracking application, free and open-source.": "Kimai ist eine professionelle Time-Tracking-Anwendung, kostenlos und Open-Source.",
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa ist ein leistungsstarkes Tool, das Ihnen die vollständige Kontrolle über Ihre Medienbibliotheken gibt. Mit Kometa können Sie Ihre Anpassung auf die nächste Ebene bringen, mit granularer Kontrolle über Metadaten, Sammlungen, Overlays und vielem mehr.", "Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa ist ein leistungsstarkes Tool, das Ihnen die vollständige Kontrolle über Ihre Medienbibliotheken gibt. Mit Kometa können Sie Ihre Anpassung auf die nächste Ebene bringen, mit granularer Kontrolle über Metadaten, Sammlungen, Overlays und vielem mehr.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa liest seine Konfiguration aus /config/config.yml und der Container liefert nur /config/config.yml.template. Kopieren Sie die Vorlage auf config.yml, füllen Sie die required Plex und TMDb-Verbindungen aus und starten Sie dann den Container neu.", "Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa liest seine Konfiguration aus /config/config.yml und der Container liefert nur /config/config.yml.template. Kopieren Sie die Vorlage auf config.yml, füllen Sie die required Plex und TMDb-Verbindungen aus und starten Sie dann den Container neu.",
@@ -3507,7 +3532,7 @@
"Limiting size and optimizing journald": "Begrenzung der Größe und Optimierung des Journals", "Limiting size and optimizing journald": "Begrenzung der Größe und Optimierung des Journals",
"Limiting size and optimizing journald...": "Größe begrenzen und Journal optimieren...", "Limiting size and optimizing journald...": "Größe begrenzen und Journal optimieren...",
"Limnoria A robust, full-featured, and user/programmer-friendly Python IRC bot, with many existing plugins. Successor of the well-known Supybot.": "Limnoria Ein robuster, voll ausgestatteter und benutzerfreundlicher Python IRC-Bot mit vielen vorhandenen Plugins. Nachfolger des bekannten Supybot.", "Limnoria A robust, full-featured, and user/programmer-friendly Python IRC bot, with many existing plugins. Successor of the well-known Supybot.": "Limnoria Ein robuster, voll ausgestatteter und benutzerfreundlicher Python IRC-Bot mit vielen vorhandenen Plugins. Nachfolger des bekannten Supybot.",
"Limnoria joins no IRC network until its configuration file exists. Create it with the setup wizard from the Proxmox host: pct exec <CTID> -- bash -c 'cd /config && limnoria-wizard'": "Limnoria schließt sich keinem IRC-Netzwerk an, bis seine Konfigurationsdatei existiert. Erstellen Sie es mit dem Setup-Assistenten aus dem Proxmox-Host: pct exec <CTID> - bash -c 'cd /config & & limnoria-Wizard'", "Limnoria joins no IRC network until its configuration file exists. Create it with the setup wizard from the Proxmox host: pct exec <CTID> -- bash -c 'cd /config && limnoria-wizard'": "Limnoria verbindet sich erst mit einem IRC-Netzwerk, wenn die Konfigurationsdatei existiert. Erstellen Sie sie mit dem Einrichtungsassistenten auf dem Proxmox-Host: pct exec <CTID> -- bash -c 'cd /config && limnoria-wizard'",
"Line to paste (single line, including \"command=...\" prefix):": "Einzufügende Zeile (einzelne Zeile, einschließlich „command=…“-Präfix):", "Line to paste (single line, including \"command=...\" prefix):": "Einzufügende Zeile (einzelne Zeile, einschließlich „command=…“-Präfix):",
"Linux Installation Options": "Linux-Installationsoptionen", "Linux Installation Options": "Linux-Installationsoptionen",
"Linux/Mac path:": "Linux/Mac-Pfad:", "Linux/Mac path:": "Linux/Mac-Pfad:",
@@ -4153,6 +4178,7 @@
"No active Proxmox storage accepts OCI template archives.": "Kein aktiver Proxmox-Speicher akzeptiert OCI-Vorlagenarchive.", "No active Proxmox storage accepts OCI template archives.": "Kein aktiver Proxmox-Speicher akzeptiert OCI-Vorlagenarchive.",
"No active exports or showmount not available": "Keine aktiven Exporte oder Showmount nicht verfügbar", "No active exports or showmount not available": "Keine aktiven Exporte oder Showmount nicht verfügbar",
"No active session": "Keine aktive Sitzung", "No active session": "Keine aktive Sitzung",
"No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.": "Kein aktives Abonnement und kein verwendbares PBS-Repository. Im nicht interaktiven Modus können APT-Quellen nicht geändert werden. Konfigurieren Sie sie unter Administration > Repositorys.",
"No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Kein aktives Abonnement und kein nutzbares PVE-Repository. Im nicht interaktiven Modus können APT-Quellen nicht geändert werden. Konfigurieren Sie sie unter Knoten > Updates > Repositorys.", "No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Kein aktives Abonnement und kein nutzbares PVE-Repository. Im nicht interaktiven Modus können APT-Quellen nicht geändert werden. Konfigurieren Sie sie unter Knoten > Updates > Repositorys.",
"No additional GPU can be added.": "Es kann keine zusätzliche GPU hinzugefügt werden.", "No additional GPU can be added.": "Es kann keine zusätzliche GPU hinzugefügt werden.",
"No additional device needs to be added.": "Es muss kein zusätzliches Gerät hinzugefügt werden.", "No additional device needs to be added.": "Es muss kein zusätzliches Gerät hinzugefügt werden.",
@@ -4238,7 +4264,7 @@
"No jobs": "Keine Jobs", "No jobs": "Keine Jobs",
"No language selected.": "Keine Sprache ausgewählt.", "No language selected.": "Keine Sprache ausgewählt.",
"No local disk storage configured.": "Kein lokaler Festplattenspeicher konfiguriert.", "No local disk storage configured.": "Kein lokaler Festplattenspeicher konfiguriert.",
"No main sources.list present (skipped)": "Keine Hauptquellenliste vorhanden (übersprungen)", "No main sources.list present (skipped)": "Keine zentrale sources.list vorhanden (übersprungen)",
"No matches for:": "Keine Treffer für:", "No matches for:": "Keine Treffer für:",
"No matching PVE vzdump jobs were found for the PBS backend.": "Für das PBS-Backend wurden keine passenden PVE-vzdump-Jobs gefunden.", "No matching PVE vzdump jobs were found for the PBS backend.": "Für das PBS-Backend wurden keine passenden PVE-vzdump-Jobs gefunden.",
"No matching PVE vzdump jobs were found for the local archive backend.": "Für das lokale Archiv-Backend wurden keine passenden PVE-vzdump-Jobs gefunden.", "No matching PVE vzdump jobs were found for the local archive backend.": "Für das lokale Archiv-Backend wurden keine passenden PVE-vzdump-Jobs gefunden.",
@@ -4257,6 +4283,7 @@
"No password provided.": "Kein Passwort angegeben.", "No password provided.": "Kein Passwort angegeben.",
"No paths available": "Keine Pfade verfügbar", "No paths available": "Keine Pfade verfügbar",
"No paths selected. Select at least one path.": "Keine Pfade ausgewählt. Wählen Sie mindestens einen Pfad aus.", "No paths selected. Select at least one path.": "Keine Pfade ausgewählt. Wählen Sie mindestens einen Pfad aus.",
"No pending changes": "Keine ausstehenden Änderungen",
"No pending paths to schedule for reboot.": "Keine ausstehenden Pfade zum Planen eines Neustarts.", "No pending paths to schedule for reboot.": "Keine ausstehenden Pfade zum Planen eines Neustarts.",
"No permanent NFS mounts configured.": "Keine permanenten NFS-Mounts konfiguriert.", "No permanent NFS mounts configured.": "Keine permanenten NFS-Mounts konfiguriert.",
"No permanent Samba mounts configured.": "Keine permanenten Samba-Mounts konfiguriert.", "No permanent Samba mounts configured.": "Keine permanenten Samba-Mounts konfiguriert.",
@@ -4398,6 +4425,7 @@
"OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM entfernt (Marionettenartefakte müssen möglicherweise manuell bereinigt werden)", "OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM entfernt (Marionettenartefakte müssen möglicherweise manuell bereinigt werden)",
"OVH server detected": "OVH-Server erkannt", "OVH server detected": "OVH-Server erkannt",
"OVH server detection and RTM installation process completed": "OVH-Servererkennung und RTM-Installationsprozess abgeschlossen", "OVH server detection and RTM installation process completed": "OVH-Servererkennung und RTM-Installationsprozess abgeschlossen",
"Object detector": "Objekterkennung",
"Object detector for Frigate": "Objektdetektor für Frigate", "Object detector for Frigate": "Objektdetektor für Frigate",
"Observer is not responding on port 4357": "Beobachter reagiert nicht auf Port 4357", "Observer is not responding on port 4357": "Beobachter reagiert nicht auf Port 4357",
"Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian ist eine Notiz-App, mit der Sie Ihre Notizen auf Ihrem Gerät erstellen, verknüpfen und organisieren können, mit Hunderten von Plugins und Themes, um Ihren Workflow anzupassen. Sie können Ihre Notizen auch online veröffentlichen, offline darauf zugreifen und sie sicher mit einer End-to-End-Verschlüsselung synchronisieren.", "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian ist eine Notiz-App, mit der Sie Ihre Notizen auf Ihrem Gerät erstellen, verknüpfen und organisieren können, mit Hunderten von Plugins und Themes, um Ihren Workflow anzupassen. Sie können Ihre Notizen auch online veröffentlichen, offline darauf zugreifen und sie sicher mit einer End-to-End-Verschlüsselung synchronisieren.",
@@ -4523,11 +4551,17 @@
"PBS backup error log": "PBS-Backup-Fehlerprotokoll", "PBS backup error log": "PBS-Backup-Fehlerprotokoll",
"PBS backup failed.": "PBS-Sicherung fehlgeschlagen.", "PBS backup failed.": "PBS-Sicherung fehlgeschlagen.",
"PBS certificate": "PBS-Zertifikat", "PBS certificate": "PBS-Zertifikat",
"PBS datastores": "PBS-Datenspeicher",
"PBS encryption keyfile (show / replace / remove)": "PBS-Verschlüsselungsschlüsseldatei (anzeigen/ersetzen/entfernen)", "PBS encryption keyfile (show / replace / remove)": "PBS-Verschlüsselungsschlüsseldatei (anzeigen/ersetzen/entfernen)",
"PBS encryption keyfile — detailed info": "PBS-Verschlüsselungsschlüsseldatei – detaillierte Informationen", "PBS encryption keyfile — detailed info": "PBS-Verschlüsselungsschlüsseldatei – detaillierte Informationen",
"PBS extraction failed": "Die PBS-Extraktion ist fehlgeschlagen", "PBS extraction failed": "Die PBS-Extraktion ist fehlgeschlagen",
"PBS host or IP address:": "PBS-Host oder IP-Adresse:", "PBS host or IP address:": "PBS-Host oder IP-Adresse:",
"PBS package update": "PBS-Paketaktualisierung",
"PBS package upgrade completed. Check whether a reboot is required before your next backup window.": "PBS-Paketaktualisierung abgeschlossen. Prüfen Sie vor dem nächsten Sicherungszeitfenster, ob ein Neustart erforderlich ist.",
"PBS package upgrade failed. Review the APT output before retrying.": "PBS-Paketaktualisierung fehlgeschlagen. Prüfen Sie vor einem erneuten Versuch die APT-Ausgabe.",
"PBS upload": "PBS-Upload", "PBS upload": "PBS-Upload",
"PBS version and packages": "PBS-Version und Pakete",
"PBS: using the configured APT repositories without changing sources.": "PBS: Die konfigurierten APT-Repositories werden verwendet, ohne die Paketquellen zu ändern.",
"PCI Address": "PCI-Adresse", "PCI Address": "PCI-Adresse",
"PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "PCI-Passthrough, TPM-Status, Cloud-Init-Konfiguration, Proxmox-Hooks", "PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "PCI-Passthrough, TPM-Status, Cloud-Init-Konfiguration, Proxmox-Hooks",
"PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "PCI-Passthrough, TPM-Status, Cloud-Init, Snapshots, Proxmox-spezifische Hooks", "PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "PCI-Passthrough, TPM-Status, Cloud-Init, Snapshots, Proxmox-spezifische Hooks",
@@ -4864,12 +4898,17 @@
"Proxmology logo applied": "Proxmology-Logo angewendet", "Proxmology logo applied": "Proxmology-Logo angewendet",
"Proxmox 9 system update allready": "Proxmox 9 Systemupdate bereits", "Proxmox 9 system update allready": "Proxmox 9 Systemupdate bereits",
"Proxmox APT repositories configured": "Proxmox APT-Repositorys konfiguriert", "Proxmox APT repositories configured": "Proxmox APT-Repositorys konfiguriert",
"Proxmox Backup Server": "Proxmox Backup Server",
"Proxmox Backup Server (PBS) destinations": "Proxmox Backup Server (PBS)-Ziele", "Proxmox Backup Server (PBS) destinations": "Proxmox Backup Server (PBS)-Ziele",
"Proxmox Backup Server APT repositories configured": "Proxmox Backup Server APT-Repositorys konfiguriert",
"Proxmox Backup Server Update": "Aktualisierung von Proxmox Backup Server",
"Proxmox Backup Server update completed.": "Aktualisierung von Proxmox Backup Server abgeschlossen.",
"Proxmox CIFS Storage Status:": "Proxmox CIFS-Speicherstatus:", "Proxmox CIFS Storage Status:": "Proxmox CIFS-Speicherstatus:",
"Proxmox NFS Storage Status:": "Proxmox NFS-Speicherstatus:", "Proxmox NFS Storage Status:": "Proxmox NFS-Speicherstatus:",
"Proxmox System Update": "Proxmox-Systemupdate", "Proxmox System Update": "Proxmox-Systemupdate",
"Proxmox Update": "Proxmox-Update", "Proxmox Update": "Proxmox-Update",
"Proxmox VE": "Proxmox VE", "Proxmox VE": "Proxmox VE",
"Proxmox VE 8 package upgrade failed. Review the APT output before retrying.": "Die Paketaktualisierung von Proxmox VE 8 ist fehlgeschlagen. Prüfen Sie die APT-Ausgabe, bevor Sie es erneut versuchen.",
"Proxmox VE 8 system update completed successfully": "Proxmox VE 8 Systemupdate erfolgreich abgeschlossen", "Proxmox VE 8 system update completed successfully": "Proxmox VE 8 Systemupdate erfolgreich abgeschlossen",
"Proxmox VE 8 to 9 Manual Upgrade Guide": "Manuelles Upgrade-Handbuch für Proxmox VE 8 auf 9", "Proxmox VE 8 to 9 Manual Upgrade Guide": "Manuelles Upgrade-Handbuch für Proxmox VE 8 auf 9",
"Proxmox VE 9.x no-subscription repository created": "Proxmox VE 9.x No-Subscription-Repository erstellt", "Proxmox VE 9.x no-subscription repository created": "Proxmox VE 9.x No-Subscription-Repository erstellt",
@@ -4877,6 +4916,7 @@
"Proxmox VE configuration completed.": "Proxmox VE-Konfiguration abgeschlossen.", "Proxmox VE configuration completed.": "Proxmox VE-Konfiguration abgeschlossen.",
"Proxmox VE safe update completed": "Sicheres Proxmox VE-Update abgeschlossen", "Proxmox VE safe update completed": "Sicheres Proxmox VE-Update abgeschlossen",
"Proxmox auth logger service created and started": "Proxmox-Auth-Logger-Dienst erstellt und gestartet", "Proxmox auth logger service created and started": "Proxmox-Auth-Logger-Dienst erstellt und gestartet",
"Proxmox could not apply the container settings": "Proxmox konnte die Containereinstellungen nicht anwenden",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Das Proxmox-Enterprise-Repository fehlt möglicherweise oder ist nicht zugänglich. Ich versuche, auf kein Abonnement umzustellen...", "Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Das Proxmox-Enterprise-Repository fehlt möglicherweise oder ist nicht zugänglich. Ich versuche, auf kein Abonnement umzustellen...",
"Proxmox filter configured": "Proxmox-Filter konfiguriert", "Proxmox filter configured": "Proxmox-Filter konfiguriert",
"Proxmox iSCSI Storage Status:": "Proxmox iSCSI-Speicherstatus:", "Proxmox iSCSI Storage Status:": "Proxmox iSCSI-Speicherstatus:",
@@ -4993,6 +5033,7 @@
"Rebuilding initramfs to apply nouveau blacklist before installation...": "Initramfs neu erstellen, um vor der Installation die Nouveau-Blacklist anzuwenden ...", "Rebuilding initramfs to apply nouveau blacklist before installation...": "Initramfs neu erstellen, um vor der Installation die Nouveau-Blacklist anzuwenden ...",
"Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "Die Neuerstellung des Containers und die Wiederherstellung aus seinem Backup werden auf demselben Image getestet. Das erste Update auf ein neueres Image steht noch aus.", "Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "Die Neuerstellung des Containers und die Wiederherstellung aus seinem Backup werden auf demselben Image getestet. Das erste Update auf ein neueres Image steht noch aus.",
"Recent": "Jüngste", "Recent": "Jüngste",
"Recent PBS tasks": "Letzte PBS-Aufgaben",
"Recent Samba Servers": "Aktuelle Samba-Server", "Recent Samba Servers": "Aktuelle Samba-Server",
"Recent Samba server": "Aktueller Samba-Server", "Recent Samba server": "Aktueller Samba-Server",
"Recent logs:": "Aktuelle Protokolle:", "Recent logs:": "Aktuelle Protokolle:",
@@ -5052,6 +5093,7 @@
"Recreation completed. Data kept.": "Erholung abgeschlossen. Vorgehaltene Daten.", "Recreation completed. Data kept.": "Erholung abgeschlossen. Vorgehaltene Daten.",
"Recreation prepared": "Erholung vorbereitet", "Recreation prepared": "Erholung vorbereitet",
"Refresh APT index and verify repositories:": "APT-Index aktualisieren und Repositorys überprüfen:", "Refresh APT index and verify repositories:": "APT-Index aktualisieren und Repositorys überprüfen:",
"Refresh the configured APT repositories and review the PBS package upgrade? Repository files will be preserved.": "Die konfigurierten APT-Repositories aktualisieren und die PBS-Paketaktualisierung prüfen? Repository-Dateien bleiben erhalten.",
"Refresh token of the connector, from the Twingate admin console": "Aktualisierungstoken des Connectors aus der Twingate-Verwaltungskonsole", "Refresh token of the connector, from the Twingate admin console": "Aktualisierungstoken des Connectors aus der Twingate-Verwaltungskonsole",
"Refresh your browser (Ctrl+Shift+R) to see changes": "Aktualisieren Sie Ihren Browser (Strg+Umschalt+R), um die Änderungen anzuzeigen", "Refresh your browser (Ctrl+Shift+R) to see changes": "Aktualisieren Sie Ihren Browser (Strg+Umschalt+R), um die Änderungen anzuzeigen",
"Refresh your browser to see changes (server restart may be required)": "Aktualisieren Sie Ihren Browser, um Änderungen zu sehen (möglicherweise ist ein Neustart des Servers erforderlich).", "Refresh your browser to see changes (server restart may be required)": "Aktualisieren Sie Ihren Browser, um Änderungen zu sehen (möglicherweise ist ein Neustart des Servers erforderlich).",
@@ -5141,6 +5183,8 @@
"Remove saved credentials": "Gespeicherte Anmeldeinformationen entfernen", "Remove saved credentials": "Gespeicherte Anmeldeinformationen entfernen",
"Remove subscription banner": "Abonnementbanner entfernen", "Remove subscription banner": "Abonnementbanner entfernen",
"Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Anwendung entfernen? Seine Containerfestplatten werden gelöscht und können nur durch ein Backup wiederhergestellt werden.", "Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Anwendung entfernen? Seine Containerfestplatten werden gelöscht und können nur durch ein Backup wiederhergestellt werden.",
"Remove the lock left on a VM by an interrupted task": "Die Sperre entfernen, die eine unterbrochene Aufgabe auf einer VM hinterlassen hat",
"Remove the lock left on a container by an interrupted task": "Die Sperre entfernen, die eine unterbrochene Aufgabe auf einem Container hinterlassen hat",
"Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Die gespeicherten Anmeldeinformationen für {registry} entfernen?\nLaufende Apps funktionieren weiterhin.", "Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Die gespeicherten Anmeldeinformationen für {registry} entfernen?\nLaufende Apps funktionieren weiterhin.",
"Remove the unprivileged flag from configuration:": "Entfernen Sie das Unprivileged-Flag aus der Konfiguration:", "Remove the unprivileged flag from configuration:": "Entfernen Sie das Unprivileged-Flag aus der Konfiguration:",
"Remove unused packages and their config": "Entfernen Sie nicht verwendete Pakete und deren Konfiguration", "Remove unused packages and their config": "Entfernen Sie nicht verwendete Pakete und deren Konfiguration",
@@ -5222,6 +5266,7 @@
"Repository helper unavailable. NVIDIA installation stopped.": "Repository-Helfer nicht verfügbar. Die Installation von NVIDIA wurde gestoppt.", "Repository helper unavailable. NVIDIA installation stopped.": "Repository-Helfer nicht verfügbar. Die Installation von NVIDIA wurde gestoppt.",
"Repository policy returned an unexpected result.": "Die Repository-Richtlinie hat ein unerwartetes Ergebnis zurückgegeben.", "Repository policy returned an unexpected result.": "Die Repository-Richtlinie hat ein unerwartetes Ergebnis zurückgegeben.",
"Repository ready.": "Repository bereit.", "Repository ready.": "Repository bereit.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.": "Repository-Quellen geändert, aber die Aktualisierung der APT-Paketliste ist fehlgeschlagen. Betrieb gestoppt;Quellen wurden nicht zurückgesetzt. Überprüfen Sie „Administration > Repositorys“ und wiederholen Sie apt-get update, bevor Sie fortfahren.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Die Repository-Quellen wurden geändert, aber die Aktualisierung der APT-Paketliste ist fehlgeschlagen. Vorgang gestoppt; die Quellen wurden nicht zurückgesetzt. Überprüfen Sie Knoten > Updates > Repositorys und wiederholen Sie apt-get update, bevor Sie fortfahren.", "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Die Repository-Quellen wurden geändert, aber die Aktualisierung der APT-Paketliste ist fehlgeschlagen. Vorgang gestoppt; die Quellen wurden nicht zurückgesetzt. Überprüfen Sie Knoten > Updates > Repositorys und wiederholen Sie apt-get update, bevor Sie fortfahren.",
"Repository switch declined; no APT source changed.": "Repository-Wechsel abgelehnt; es wurde keine APT-Quelle geändert.", "Repository switch declined; no APT source changed.": "Repository-Wechsel abgelehnt; es wurde keine APT-Quelle geändert.",
"Repository:": "Repository:", "Repository:": "Repository:",
@@ -5316,7 +5361,9 @@
"Reverting ZFS ARC tuning...": "ZFS ARC-Tuning wird zurückgesetzt...", "Reverting ZFS ARC tuning...": "ZFS ARC-Tuning wird zurückgesetzt...",
"Reverting pigz wrapper...": "Pigz-Wrapper wird zurückgesetzt...", "Reverting pigz wrapper...": "Pigz-Wrapper wird zurückgesetzt...",
"Reverting vzdump speed tuning...": "vzdump-Geschwindigkeitsoptimierung wird zurückgesetzt...", "Reverting vzdump speed tuning...": "vzdump-Geschwindigkeitsoptimierung wird zurückgesetzt...",
"Review and apply": "Prüfen und anwenden",
"Review external OCI changes": "Überprüfen Sie externe OCI-Änderungen", "Review external OCI changes": "Überprüfen Sie externe OCI-Änderungen",
"Review package upgrade": "Paketaktualisierung prüfen",
"Review passthrough config files": "Überprüfen Sie die Passthrough-Konfigurationsdateien", "Review passthrough config files": "Überprüfen Sie die Passthrough-Konfigurationsdateien",
"Review what will be removed": "Überprüfen Sie, was entfernt wird", "Review what will be removed": "Überprüfen Sie, was entfernt wird",
"Rip DVD and Blu-ray media from a browser": "Rip DVD und Blu-ray Medien aus einem Browser", "Rip DVD and Blu-ray media from a browser": "Rip DVD und Blu-ray Medien aus einem Browser",
@@ -5359,6 +5406,7 @@
"Run the following inside the VM:": "Führen Sie Folgendes in der VM aus:", "Run the following inside the VM:": "Führen Sie Folgendes in der VM aus:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "Führen Sie den Release-Channel-Schalter von einer SSH-Sitzung oder der Proxmox-Hostkonsole aus mit:", "Run the release-channel switch from an SSH session or the Proxmox host console with:": "Führen Sie den Release-Channel-Schalter von einer SSH-Sitzung oder der Proxmox-Hostkonsole aus mit:",
"Run the update from an SSH session or the Proxmox host console with:": "Führen Sie das Update über eine SSH-Sitzung oder die Proxmox-Hostkonsole aus mit:", "Run the update from an SSH session or the Proxmox host console with:": "Führen Sie das Update über eine SSH-Sitzung oder die Proxmox-Hostkonsole aus mit:",
"Run this operation as root.": "Führen Sie diesen Vorgang als root aus.",
"Run upgrade checklist script:": "Führen Sie das Upgrade-Checklisten-Skript aus:", "Run upgrade checklist script:": "Führen Sie das Upgrade-Checklisten-Skript aus:",
"Running": "Läuft", "Running": "Läuft",
"Running Lynis security audit...": "Lynis-Sicherheitsaudit wird ausgeführt...", "Running Lynis security audit...": "Lynis-Sicherheitsaudit wird ausgeführt...",
@@ -5773,6 +5821,7 @@
"ShareCoveX panel": "ShareCoveX-Panel", "ShareCoveX panel": "ShareCoveX-Panel",
"Shared Directory Ready:": "Bereit für das freigegebene Verzeichnis:", "Shared Directory Ready:": "Bereit für das freigegebene Verzeichnis:",
"Shared Group": "Geteilte Gruppe", "Shared Group": "Geteilte Gruppe",
"Shared directories": "Freigegebene Verzeichnisse",
"Shared directory created:": "Gemeinsames Verzeichnis erstellt:", "Shared directory created:": "Gemeinsames Verzeichnis erstellt:",
"Shared directory for consume and export": "Gemeinsames Verzeichnis für Konsum und Export", "Shared directory for consume and export": "Gemeinsames Verzeichnis für Konsum und Export",
"Shared directory for copy/sync operations": "Gemeinsames Verzeichnis für Copy/Sync operations", "Shared directory for copy/sync operations": "Gemeinsames Verzeichnis für Copy/Sync operations",
@@ -5802,6 +5851,7 @@
"Should show pve-manager/9.x.x": "Sollte pve-manager/9.x.x anzeigen", "Should show pve-manager/9.x.x": "Sollte pve-manager/9.x.x anzeigen",
"Shoutrrr addresses for the notifications (optional)": "Shoutrrr-Adressen für die Benachrichtigungen (optional)", "Shoutrrr addresses for the notifications (optional)": "Shoutrrr-Adressen für die Benachrichtigungen (optional)",
"Show All Scripts": "Alle Skripte anzeigen", "Show All Scripts": "Alle Skripte anzeigen",
"Show CT users and their UID/GID on the host": "CT-Benutzer und UID/GID auf dem Host anzeigen",
"Show CT users in table format": "CT-Benutzer im Tabellenformat anzeigen", "Show CT users in table format": "CT-Benutzer im Tabellenformat anzeigen",
"Show Container Privilege Status": "Container-Berechtigungsstatus anzeigen", "Show Container Privilege Status": "Container-Berechtigungsstatus anzeigen",
"Show DNS resolution status": "DNS-Auflösungsstatus anzeigen", "Show DNS resolution status": "DNS-Auflösungsstatus anzeigen",
@@ -5963,6 +6013,7 @@
"Start the VM to begin Windows installation from the mounted ISO.": "Starten Sie die VM, um die Windows-Installation von der gemounteten ISO aus zu starten.", "Start the VM to begin Windows installation from the mounted ISO.": "Starten Sie die VM, um die Windows-Installation von der gemounteten ISO aus zu starten.",
"Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Die Anwendungen starten, sobald sie registriert sind? Antworten Sie mit „Nein“, wenn die ursprünglichen Container noch auf einem anderen Host laufen: Beide würden die gleichen Adressen verwenden.", "Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Die Anwendungen starten, sobald sie registriert sind? Antworten Sie mit „Nein“, wenn die ursprünglichen Container noch auf einem anderen Host laufen: Beide würden die gleichen Adressen verwenden.",
"Start the converted container:": "Starten Sie den konvertierten Container:", "Start the converted container:": "Starten Sie den konvertierten Container:",
"Start the interactive Proxmox package upgrade": "Interaktive Paketaktualisierung von Proxmox starten",
"Start the main system upgrade:": "Starten Sie das Hauptsystem-Upgrade:", "Start the main system upgrade:": "Starten Sie das Hauptsystem-Upgrade:",
"Start the stack with Proxmox": "Starten Sie den Stack mit Proxmox", "Start the stack with Proxmox": "Starten Sie den Stack mit Proxmox",
"Start uploading to PBS — sets a recovery passphrase": "Hochladen auf PBS starten – legt eine Wiederherstellungspassphrase fest", "Start uploading to PBS — sets a recovery passphrase": "Hochladen auf PBS starten – legt eine Wiederherstellungspassphrase fest",
@@ -6242,6 +6293,7 @@
"The Paperless persistent volumes need at least 8 GB": "Die papierlosen persistenten Volumes benötigen mindestens 8 GB", "The Paperless persistent volumes need at least 8 GB": "Die papierlosen persistenten Volumes benötigen mindestens 8 GB",
"The PostgreSQL volume needs at least 4 GB": "Das PostgreSQL-Volume benötigt mindestens 4 GB", "The PostgreSQL volume needs at least 4 GB": "Das PostgreSQL-Volume benötigt mindestens 4 GB",
"The PostgreSQL volume needs at least 8 GB": "Das PostgreSQL-Volume benötigt mindestens 8 GB", "The PostgreSQL volume needs at least 8 GB": "Das PostgreSQL-Volume benötigt mindestens 8 GB",
"The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "Die Aktualisierung von Proxmox VE 8 wurde nicht abgeschlossen. Das Upgrade auf Proxmox VE 9 wurde nicht gestartet.",
"The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Der Archivschlüsselbund Proxmox fehlt;Die Ceph-Installation kann nicht sicher fortgesetzt werden", "The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Der Archivschlüsselbund Proxmox fehlt;Die Ceph-Installation kann nicht sicher fortgesetzt werden",
"The Proxmox inventory and the local configurations differ": "Das Proxmox-Inventar und die lokalen Konfigurationen unterscheiden sich", "The Proxmox inventory and the local configurations differ": "Das Proxmox-Inventar und die lokalen Konfigurationen unterscheiden sich",
"The ROCm image has no support for the AMD GPU of this host:": "Das ROCm-Image unterstützt die AMD-GPU dieses Hosts nicht:", "The ROCm image has no support for the AMD GPU of this host:": "Das ROCm-Image unterstützt die AMD-GPU dieses Hosts nicht:",
@@ -6312,6 +6364,7 @@
"The build could not be checked beforehand; continuing without that check.": "Der Build konnte vorher nicht überprüft werden;ohne diese Prüfung weitermachen.", "The build could not be checked beforehand; continuing without that check.": "Der Build konnte vorher nicht überprüft werden;ohne diese Prüfung weitermachen.",
"The cached image does not match the current digest": "Das zwischengespeicherte Bild entspricht nicht dem aktuellen Digest", "The cached image does not match the current digest": "Das zwischengespeicherte Bild entspricht nicht dem aktuellen Digest",
"The cached image is damaged; it will be downloaded again.": "Das zwischengespeicherte Bild ist beschädigt; es wird erneut heruntergeladen.", "The cached image is damaged; it will be downloaded again.": "Das zwischengespeicherte Bild ist beschädigt; es wird erneut heruntergeladen.",
"The changes need the application installer": "Die Änderungen erfordern das Installationsprogramm der Anwendung",
"The character device does not exist:": "Das Zeichengerät existiert nicht:", "The character device does not exist:": "Das Zeichengerät existiert nicht:",
"The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Der Befehl fragt nach einem Passwort, das nicht wiederholt wird. Nach dem Erstellen der Benutzer fragt die Weboberfläche nach einem Benutzernamen und einem Passwort.", "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Der Befehl fragt nach einem Passwort, das nicht wiederholt wird. Nach dem Erstellen der Benutzer fragt die Weboberfläche nach einem Benutzernamen und einem Passwort.",
"The command does not name an image": "Der Befehl benennt kein Bild", "The command does not name an image": "Der Befehl benennt kein Bild",
@@ -6431,8 +6484,10 @@
"The host bind source does not exist:": "Die Host-Bind-Quelle existiert nicht:", "The host bind source does not exist:": "Die Host-Bind-Quelle existiert nicht:",
"The host bind source is not a regular file or directory:": "Die Host-Bind-Quelle ist keine reguläre Datei oder ein Verzeichnis:", "The host bind source is not a regular file or directory:": "Die Host-Bind-Quelle ist keine reguläre Datei oder ein Verzeichnis:",
"The host directory changed before it was mounted": "Das Host-Verzeichnis wurde geändert, bevor es gemountet wurde", "The host directory changed before it was mounted": "Das Host-Verzeichnis wurde geändert, bevor es gemountet wurde",
"The host directory does not exist": "Das Verzeichnis auf dem Host ist nicht vorhanden",
"The host directory may not be accessible from an unprivileged container.": "Auf das Hostverzeichnis kann von einem unprivilegierten Container aus möglicherweise nicht zugegriffen werden.", "The host directory may not be accessible from an unprivileged container.": "Auf das Hostverzeichnis kann von einem unprivilegierten Container aus möglicherweise nicht zugegriffen werden.",
"The host has no IPv4 address on the selected bridge": "Der Host hat keine IPv4-Adresse auf der ausgewählten Brücke", "The host has no IPv4 address on the selected bridge": "Der Host hat keine IPv4-Adresse auf der ausgewählten Brücke",
"The host loses access to them. If the group includes a network or storage controller the host needs, cancel at the summary.": "Der Host verliert den Zugriff darauf. Wenn die Gruppe einen Netzwerk- oder Speichercontroller enthält, den der Host benötigt, brechen Sie in der Zusammenfassung ab.",
"The host monitor does not see the real host memory": "Der Host-Monitor sieht den echten Host-Speicher nicht", "The host monitor does not see the real host memory": "Der Host-Monitor sieht den echten Host-Speicher nicht",
"The host monitor does not share this host namespace:": "Der Host-Monitor teilt diesen Host-Namespace nicht:", "The host monitor does not share this host namespace:": "Der Host-Monitor teilt diesen Host-Namespace nicht:",
"The host monitor include is unknown or differs from the saved record": "Das Host-Monitor-Include ist unbekannt oder weicht vom gespeicherten Datensatz ab", "The host monitor include is unknown or differs from the saved record": "Das Host-Monitor-Include ist unbekannt oder weicht vom gespeicherten Datensatz ab",
@@ -6622,6 +6677,7 @@
"The repair must preserve the image dependencies:": "Die Reparatur muss die Bildabhängigkeiten bewahren:", "The repair must preserve the image dependencies:": "Die Reparatur muss die Bildabhängigkeiten bewahren:",
"The requested VMID block is already in use": "Der angeforderte VMID-Block wird bereits verwendet", "The requested VMID block is already in use": "Der angeforderte VMID-Block wird bereits verwendet",
"The requested machine learning GPU profile is not working; it is not replaced by CPU": "Das angeforderte Machine Learning-GPU-Profil funktioniert nicht; es wird nicht durch CPU ersetzt", "The requested machine learning GPU profile is not working; it is not replaced by CPU": "Das angeforderte Machine Learning-GPU-Profil funktioniert nicht; es wird nicht durch CPU ersetzt",
"The resource edit could not be fully rolled back. Open Manage again to review and import the current Proxmox settings.": "Die Ressourcenänderung konnte nicht vollständig rückgängig gemacht werden. Öffne die Verwaltung erneut, um die aktuellen Proxmox-Einstellungen zu prüfen und zu übernehmen.",
"The restored service did not pass its health check": "Der wiederhergestellte Service bestand seinen Gesundheitscheck nicht", "The restored service did not pass its health check": "Der wiederhergestellte Service bestand seinen Gesundheitscheck nicht",
"The restored service stopped; the recovery is not confirmed": "Der wiederhergestellte Dienst wurde gestoppt; die Wiederherstellung wird nicht bestätigt", "The restored service stopped; the recovery is not confirmed": "Der wiederhergestellte Dienst wurde gestoppt; die Wiederherstellung wird nicht bestätigt",
"The reviewed Tandoor stack does not require a privileged LXC.": "Der überprüfte Tandoor-Stack requi ist kein privilegierter LXC.", "The reviewed Tandoor stack does not require a privileged LXC.": "Der überprüfte Tandoor-Stack requi ist kein privilegierter LXC.",
@@ -6667,6 +6723,7 @@
"The selected VM": "Die ausgewählte VM", "The selected VM": "Die ausgewählte VM",
"The selected VM is running.": "Die ausgewählte VM läuft.", "The selected VM is running.": "Die ausgewählte VM läuft.",
"The selected base folder does not exist and could not be created:": "Der ausgewählte Basisordner existiert nicht und konnte nicht erstellt werden:", "The selected base folder does not exist and could not be created:": "Der ausgewählte Basisordner existiert nicht und konnte nicht erstellt werden:",
"The selected bridge does not exist": "Die ausgewählte Bridge ist nicht vorhanden",
"The selected bridge has no IPv4 subnet for the host-monitor firewall": "Die ausgewählte Bridge verfügt über kein IPv4-Subnetz für die Host-Monitor-Firewall", "The selected bridge has no IPv4 subnet for the host-monitor firewall": "Die ausgewählte Bridge verfügt über kein IPv4-Subnetz für die Host-Monitor-Firewall",
"The selected configuration needs to start the LXC during the installation": "Die ausgewählte Konfiguration muss den LXC während der Installation starten", "The selected configuration needs to start the LXC during the installation": "Die ausgewählte Konfiguration muss den LXC während der Installation starten",
"The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Der ausgewählte Container ist nicht privilegiert. Für den direkten Geräte-Passthrough ist ein privilegierter Container erforderlich.", "The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Der ausgewählte Container ist nicht privilegiert. Für den direkten Geräte-Passthrough ist ein privilegierter Container erforderlich.",
@@ -6689,6 +6746,7 @@
"The service builds its own image; only a published image can be installed": "Der Dienst erstellt ein eigenes Image; nur ein veröffentlichtes Image kann installiert werden", "The service builds its own image; only a published image can be installed": "Der Dienst erstellt ein eigenes Image; nur ein veröffentlichtes Image kann installiert werden",
"The service declares no image:": "Der Dienst erklärt kein Bild:", "The service declares no image:": "Der Dienst erklärt kein Bild:",
"The setting has no final value:": "Die Einstellung hat keinen Endwert:", "The setting has no final value:": "Die Einstellung hat keinen Endwert:",
"The settings could not be fully rolled back. Review the container and its saved modification before continuing.": "Die Einstellungen konnten nicht vollständig zurückgesetzt werden. Prüfe den Container und die gespeicherte Änderung, bevor du fortfährst.",
"The share already exists in smb.conf:": "Die Freigabe existiert bereits in smb.conf:", "The share already exists in smb.conf:": "Die Freigabe existiert bereits in smb.conf:",
"The shared destination is not a directory": "Das gemeinsame Ziel ist kein Verzeichnis", "The shared destination is not a directory": "Das gemeinsame Ziel ist kein Verzeichnis",
"The shared directory points to a protected host path": "Das Shared Directory zeigt auf einen geschützten Hostpfad", "The shared directory points to a protected host path": "Das Shared Directory zeigt auf einen geschützten Hostpfad",
@@ -6756,19 +6814,24 @@
"These Proxmox resources were added outside ProxMenux:": "Diese Proxmox-Ressourcen wurden außerhalb von ProxMenux hinzugefügt:", "These Proxmox resources were added outside ProxMenux:": "Diese Proxmox-Ressourcen wurden außerhalb von ProxMenux hinzugefügt:",
"These VMIDs are not free:": "Diese VMIDs sind nicht kostenlos:", "These VMIDs are not free:": "Diese VMIDs sind nicht kostenlos:",
"These are the changes that will be made": "Dies sind die Änderungen, die vorgenommen werden", "These are the changes that will be made": "Dies sind die Änderungen, die vorgenommen werden",
"These changes were made in Proxmox outside ProxMenux:": "Diese Änderungen wurden in Proxmox außerhalb von ProxMenux vorgenommen:",
"These container mount paths have an extension-like suffix:": "Diese Einhängepfade im Container haben eine Endung, die einer Dateierweiterung ähnelt:", "These container mount paths have an extension-like suffix:": "Diese Einhängepfade im Container haben eine Endung, die einer Dateierweiterung ähnelt:",
"These containers were restored from a backup and this host has no record of their OCI application:": "Diese Container wurden aus einem Backup wiederhergestellt und dieser Host hat keinen Datensatz ihrer OCI-Anwendung:", "These containers were restored from a backup and this host has no record of their OCI application:": "Diese Container wurden aus einem Backup wiederhergestellt und dieser Host hat keinen Datensatz ihrer OCI-Anwendung:",
"These devices share the IOMMU group of the selected GPU and will be assigned to the VM with it:": "Diese Geräte teilen sich die IOMMU-Gruppe der ausgewählten GPU und werden zusammen mit ihr der VM zugewiesen:",
"These host backups run with this job and expect its storage:": "Diese Host-Backups werden mit diesem Job ausgeführt und erwarten dessen Speicherung:", "These host backups run with this job and expect its storage:": "Diese Host-Backups werden mit diesem Job ausgeführt und erwarten dessen Speicherung:",
"These host backups run with this job and will stop running:": "Diese Host-Backups werden mit diesem Job ausgeführt und werden nicht mehr ausgeführt:", "These host backups run with this job and will stop running:": "Diese Host-Backups werden mit diesem Job ausgeführt und werden nicht mehr ausgeführt:",
"These interface configurations will be removed": "Diese Schnittstellenkonfigurationen werden entfernt", "These interface configurations will be removed": "Diese Schnittstellenkonfigurationen werden entfernt",
"These manual changes cannot be adopted safely:": "Diese manuellen Änderungen können nicht sicher übernommen werden:", "These manual changes cannot be adopted safely:": "Diese manuellen Änderungen können nicht sicher übernommen werden:",
"These paths will not be restored live and will be extracted for manual recovery.": "Diese Pfade werden nicht live wiederhergestellt und zur manuellen Wiederherstellung extrahiert.", "These paths will not be restored live and will be extracted for manual recovery.": "Diese Pfade werden nicht live wiederhergestellt und zur manuellen Wiederherstellung extrahiert.",
"These settings require an application-specific adaptation. The image was not downloaded and the container was not recreated.": "Diese Einstellungen erfordern eine anwendungsspezifische Anpassung. Das Image wurde nicht heruntergeladen und der Container nicht neu erstellt.",
"These values are asked during the installation:": "Diese Werte werden während der Installation abgefragt:", "These values are asked during the installation:": "Diese Werte werden während der Installation abgefragt:",
"They are described in the image documentation:": "Sie sind in der Dokumentation des Images beschrieben:", "They are described in the image documentation:": "Sie sind in der Dokumentation des Images beschrieben:",
"They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Sie werden zu oci-compose hinzugefügt, bevor Sie fortfahren. Nicht unterstützte oder geänderte Ressourcen werden nicht importiert.", "They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Sie werden zu oci-compose hinzugefügt, bevor Sie fortfahren. Nicht unterstützte oder geänderte Ressourcen werden nicht importiert.",
"They will be saved in oci-compose before continuing.": "Sie werden in oci-compose gespeichert, bevor Sie fortfahren.",
"This CIFS share is mounted with restrictive permissions.": "Diese CIFS-Freigabe wird mit restriktiven Berechtigungen gemountet.", "This CIFS share is mounted with restrictive permissions.": "Diese CIFS-Freigabe wird mit restriktiven Berechtigungen gemountet.",
"This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Diese GPU gilt als inkompatibel mit GPU-Passthrough zu einer VM in ProxMenux.", "This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Diese GPU gilt als inkompatibel mit GPU-Passthrough zu einer VM in ProxMenux.",
"This NFS share is fully restricted — even the host root cannot write to it.": "Diese NFS-Freigabe ist vollständig eingeschränkt – selbst der Host-Root kann nicht darauf schreiben.", "This NFS share is fully restricted — even the host root cannot write to it.": "Diese NFS-Freigabe ist vollständig eingeschränkt – selbst der Host-Root kann nicht darauf schreiben.",
"This Proxmox Backup Server has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PBS source will be disabled.": "Dieser Proxmox Backup Server hat kein aktives Abonnement. Zum Repository ohne Abonnement wechseln? Die nicht erreichbare Enterprise-PBS-Quelle wird deaktiviert.",
"This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Für diese VM sind zusätzliche Installationsschritte erforderlich. Weitere Informationen finden Sie im Installationshandbuch unter:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144", "This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Für diese VM sind zusätzliche Installationsschritte erforderlich. Weitere Informationen finden Sie im Installationshandbuch unter:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144",
"This account is for {registry}. Enter an image from that registry.": "Dieses Konto ist für {registry}. Geben Sie ein Bild aus dieser Registrierung ein.", "This account is for {registry}. Enter an image from that registry.": "Dieses Konto ist für {registry}. Geben Sie ein Bild aus dieser Registrierung ein.",
"This action will:": "Diese Aktion wird:", "This action will:": "Diese Aktion wird:",
@@ -6830,10 +6893,13 @@
"This may take several minutes...": "Dies kann einige Minuten dauern...", "This may take several minutes...": "Dies kann einige Minuten dauern...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Das bedeutet, dass Proxmox den Mount-Lebenszyklus nativ verwaltet (für NFS/CIFS-Hostspeicher ist kein manuelles /etc/fstab erforderlich).", "This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Das bedeutet, dass Proxmox den Mount-Lebenszyklus nativ verwaltet (für NFS/CIFS-Hostspeicher ist kein manuelles /etc/fstab erforderlich).",
"This means the credentials are incorrect.": "Dies bedeutet, dass die Anmeldeinformationen falsch sind.", "This means the credentials are incorrect.": "Dies bedeutet, dass die Anmeldeinformationen falsch sind.",
"This menu requires Proxmox Backup Server.": "Dieses Menü erfordert Proxmox Backup Server.",
"This might indicate network connectivity issues.": "Dies könnte auf Probleme mit der Netzwerkverbindung hinweisen.", "This might indicate network connectivity issues.": "Dies könnte auf Probleme mit der Netzwerkverbindung hinweisen.",
"This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Dieser Monitor verwendet einen privilegierten LXC, teilt Prozesse und Netzwerke mit Proxmox und deaktiviert AppArmor im CT. Es verwendet die IP-Adresse und Firewall des Hosts. Ein kompromittiertes Bild könnte den Host beeinflussen; setzen Sie seine Web-Benutzeroberfläche nicht dem Internet aus.", "This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Dieser Monitor verwendet einen privilegierten LXC, teilt Prozesse und Netzwerke mit Proxmox und deaktiviert AppArmor im CT. Es verwendet die IP-Adresse und Firewall des Hosts. Ein kompromittiertes Bild könnte den Host beeinflussen; setzen Sie seine Web-Benutzeroberfläche nicht dem Internet aus.",
"This only removes the schedule. Existing backup archives are NOT deleted.": "Dadurch wird nur der Zeitplan entfernt. Vorhandene Backup-Archive werden NICHT gelöscht.", "This only removes the schedule. Existing backup archives are NOT deleted.": "Dadurch wird nur der Zeitplan entfernt. Vorhandene Backup-Archive werden NICHT gelöscht.",
"This operation may take several minutes and requires internet connectivity.": "Dieser Vorgang kann mehrere Minuten dauern und erfordert eine Internetverbindung.", "This operation may take several minutes and requires internet connectivity.": "Dieser Vorgang kann mehrere Minuten dauern und erfordert eine Internetverbindung.",
"This operation requires a Proxmox VE host. Use the PBS menu on Proxmox Backup Server.": "Dieser Vorgang erfordert einen Proxmox-VE-Host. Verwenden Sie auf Proxmox Backup Server das PBS-Menü.",
"This operation supports PBS 3 on Debian 12 and PBS 4 on Debian 13.": "Dieser Vorgang unterstützt PBS 3 unter Debian 12 und PBS 4 unter Debian 13.",
"This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Dieses Paket wurde von älteren Versionen des ProxMenux Coral-Installationsprogramms installiert, das den M.2-Kernel-Treiber auf jedem System platzierte, einschließlich reiner USB-Setups. Es ist nicht für Coral USB-Geräte erforderlich, die nur libedgetpu1-std / libedgetpu1-max verwenden.", "This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Dieses Paket wurde von älteren Versionen des ProxMenux Coral-Installationsprogramms installiert, das den M.2-Kernel-Treiber auf jedem System platzierte, einschließlich reiner USB-Setups. Es ist nicht für Coral USB-Geräte erforderlich, die nur libedgetpu1-std / libedgetpu1-max verwenden.",
"This passphrase is the ONLY way to access encrypted Borg backups.": "Diese Passphrase ist die EINZIGE Möglichkeit, auf verschlüsselte Borg-Backups zuzugreifen.", "This passphrase is the ONLY way to access encrypted Borg backups.": "Diese Passphrase ist die EINZIGE Möglichkeit, auf verschlüsselte Borg-Backups zuzugreifen.",
"This path is already used as a mount point in this container.": "Dieser Pfad wird in diesem Container bereits als Mountpunkt verwendet.", "This path is already used as a mount point in this container.": "Dieser Pfad wird in diesem Container bereits als Mountpunkt verwendet.",
@@ -6849,6 +6915,7 @@
"This script cannot be executed from the Proxmox web terminal.": "Dieses Skript kann nicht über das Proxmox-Webterminal ausgeführt werden.", "This script cannot be executed from the Proxmox web terminal.": "Dieses Skript kann nicht über das Proxmox-Webterminal ausgeführt werden.",
"This script must be run as root.": "Dieses Skript muss als Root ausgeführt werden.", "This script must be run as root.": "Dieses Skript muss als Root ausgeführt werden.",
"This script must be run on a Proxmox host.": "Dieses Skript muss auf einem Proxmox-Host ausgeführt werden.", "This script must be run on a Proxmox host.": "Dieses Skript muss auf einem Proxmox-Host ausgeführt werden.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox Backup Server": "Dieses Skript wendet die folgenden Optimierungen und erweiterten Anpassungen auf Ihren Proxmox Backup Server an",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Dieses Skript wendet die folgenden Optimierungen und erweiterten Anpassungen auf Ihren Proxmox VE-Server an", "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Dieses Skript wendet die folgenden Optimierungen und erweiterten Anpassungen auf Ihren Proxmox VE-Server an",
"This script will update your Proxmox VE system with advanced options:": "Dieses Skript aktualisiert Ihr Proxmox VE-System mit erweiterten Optionen:", "This script will update your Proxmox VE system with advanced options:": "Dieses Skript aktualisiert Ihr Proxmox VE-System mit erweiterten Optionen:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Diese Sitzung wird im Monitor-Terminal ausgeführt. Wenn Sie es von hier aus ausführen, wird die Verbindung während der Installation unterbrochen und der Switch bleibt in einem defekten Zustand.", "This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Diese Sitzung wird im Monitor-Terminal ausgeführt. Wenn Sie es von hier aus ausführen, wird die Verbindung während der Installation unterbrochen und der Switch bleibt in einem defekten Zustand.",
@@ -6935,7 +7002,7 @@
"Transaction log:": "Transaktionsprotokoll:", "Transaction log:": "Transaktionsprotokoll:",
"Translation files:": "Übersetzungsdateien:", "Translation files:": "Übersetzungsdateien:",
"Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission wurde für eine einfache und leistungsstarke Nutzung entwickelt. Transmission verfügt über die Funktionen, die Sie von einem BitTorrent-Client erwarten: Verschlüsselung, eine Web-Schnittstelle, Peer-Exchange, Magnet-Links, DHT, μTP, UPnP und NAT-PMP Port-Weiterleitung, Webseed-Unterstützung, Watch-Verzeichnisse, Tracker-Bearbeitung, globale und per-Torrent-Geschwindigkeitsbeschränkungen und mehr.", "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission wurde für eine einfache und leistungsstarke Nutzung entwickelt. Transmission verfügt über die Funktionen, die Sie von einem BitTorrent-Client erwarten: Verschlüsselung, eine Web-Schnittstelle, Peer-Exchange, Magnet-Links, DHT, μTP, UPnP und NAT-PMP Port-Weiterleitung, Webseed-Unterstützung, Watch-Verzeichnisse, Tracker-Bearbeitung, globale und per-Torrent-Geschwindigkeitsbeschränkungen und mehr.",
"Tried pvesm path and manual detection methods": "Versuchte den Pvesm-Pfad und manuelle Erkennungsmethoden", "Tried pvesm path and manual detection methods": "pvesm path und manuelle Erkennungsmethoden wurden versucht",
"Trust this certificate and save it for scheduled backups?": "Diesem Zertifikat vertrauen und es für geplante Sicherungen speichern?", "Trust this certificate and save it for scheduled backups?": "Diesem Zertifikat vertrauen und es für geplante Sicherungen speichern?",
"Try Again": "Versuchen Sie es erneut", "Try Again": "Versuchen Sie es erneut",
"Try a different search term.": "Versuchen Sie es mit einem anderen Suchbegriff.", "Try a different search term.": "Versuchen Sie es mit einem anderen Suchbegriff.",
@@ -6980,6 +7047,7 @@
"Unable to identify the root filesystem. Aborting.": "Das Root-Dateisystem konnte nicht identifiziert werden. Abbruch.", "Unable to identify the root filesystem. Aborting.": "Das Root-Dateisystem konnte nicht identifiziert werden. Abbruch.",
"Unable to resolve system ZFS pool disks. Aborting.": "System-Pool-Festplatten ZFS können nicht aufgelöst werden. Abbruch.", "Unable to resolve system ZFS pool disks. Aborting.": "System-Pool-Festplatten ZFS können nicht aufgelöst werden. Abbruch.",
"Unable to resolve system disk topology. Aborting.": "Die Topologie der Systemfestplatte kann nicht aufgelöst werden. Abbruch.", "Unable to resolve system disk topology. Aborting.": "Die Topologie der Systemfestplatte kann nicht aufgelöst werden. Abbruch.",
"Unapplied changes": "Noch nicht angewendete Änderungen",
"Understand the security implications of privileged containers": "Verstehen Sie die Sicherheitsauswirkungen privilegierter Container", "Understand the security implications of privileged containers": "Verstehen Sie die Sicherheitsauswirkungen privilegierter Container",
"Unexpected Proxmox inventory; recovery blocked": "Unerwartetes Proxmox-Inventar; Erholung blockiert", "Unexpected Proxmox inventory; recovery blocked": "Unerwartetes Proxmox-Inventar; Erholung blockiert",
"Unexpected formatting directory in the new Valkey volume": "Unerwartetes Formatierungsverzeichnis im neuen Valkey-Volume", "Unexpected formatting directory in the new Valkey volume": "Unerwartetes Formatierungsverzeichnis im neuen Valkey-Volume",
@@ -7079,6 +7147,7 @@
"Unsupported OCI-LXC AppArmor profile:": "Nicht unterstütztes OCI-LXC AppArmor-Profil:", "Unsupported OCI-LXC AppArmor profile:": "Nicht unterstütztes OCI-LXC AppArmor-Profil:",
"Unsupported OCI-LXC seccomp profile:": "Nicht unterstütztes OCI-LXC seccomp Profil:", "Unsupported OCI-LXC seccomp profile:": "Nicht unterstütztes OCI-LXC seccomp Profil:",
"Unsupported Terminal": "Nicht unterstütztes Terminal", "Unsupported Terminal": "Nicht unterstütztes Terminal",
"Unsupported architecture for Figurine:": "Nicht unterstützte Architektur für Figurine:",
"Unsupported architecture:": "Nicht unterstützte Architektur:", "Unsupported architecture:": "Nicht unterstützte Architektur:",
"Unsupported backup compression": "Nicht unterstützte Backup-Komprimierung", "Unsupported backup compression": "Nicht unterstützte Backup-Komprimierung",
"Unsupported credential pattern:": "Nicht unterstütztes credential-Muster:", "Unsupported credential pattern:": "Nicht unterstütztes credential-Muster:",
@@ -7094,6 +7163,7 @@
"Unsupported mount type": "Typ der nicht unterstützten Halterung", "Unsupported mount type": "Typ der nicht unterstützten Halterung",
"Unsupported mount type:": "Typ der nicht unterstützten Halterung:", "Unsupported mount type:": "Typ der nicht unterstützten Halterung:",
"Unsupported native device type:": "Nicht unterstützter nativer Gerätetyp:", "Unsupported native device type:": "Nicht unterstützter nativer Gerätetyp:",
"Unsupported network settings": "Nicht unterstützte Netzwerkeinstellungen",
"Unsupported operation": "Nicht unterstützte operation", "Unsupported operation": "Nicht unterstützte operation",
"Unsupported or unknown Proxmox version; no repository changed.": "Nicht unterstützte oder unbekannte Proxmox-Version; es wurde kein Repository geändert.", "Unsupported or unknown Proxmox version; no repository changed.": "Nicht unterstützte oder unbekannte Proxmox-Version; es wurde kein Repository geändert.",
"Unsupported output format:": "Nicht unterstütztes Ausgabeformat:", "Unsupported output format:": "Nicht unterstütztes Ausgabeformat:",
@@ -7121,6 +7191,7 @@
"Update NVIDIA in LXC Containers": "Aktualisieren Sie NVIDIA in LXC-Containern", "Update NVIDIA in LXC Containers": "Aktualisieren Sie NVIDIA in LXC-Containern",
"Update OCI": "Aktualisierung OCI", "Update OCI": "Aktualisierung OCI",
"Update OCI stack": "Aktualisierung des OCI-Stacks", "Update OCI stack": "Aktualisierung des OCI-Stacks",
"Update PBS using configured repositories": "PBS mit den konfigurierten Repositories aktualisieren",
"Update PVE enterprise repository (Only if using enterprise):": "PVE-Enterprise-Repository aktualisieren (nur bei Verwendung von Enterprise):", "Update PVE enterprise repository (Only if using enterprise):": "PVE-Enterprise-Repository aktualisieren (nur bei Verwendung von Enterprise):",
"Update Proxmox VE Appliance Manager": "Proxmox VE Appliance Manager aktualisieren", "Update Proxmox VE Appliance Manager": "Proxmox VE Appliance Manager aktualisieren",
"Update Proxmox package lists": "Proxmox-Paketlisten aktualisieren", "Update Proxmox package lists": "Proxmox-Paketlisten aktualisieren",
@@ -7173,6 +7244,7 @@
"Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Upgrade-Assistent: Proxmox VE 8 → 9 (Trixie)", "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Upgrade-Assistent: Proxmox VE 8 → 9 (Trixie)",
"Upgrade canceled by user": "Upgrade vom Benutzer abgebrochen", "Upgrade canceled by user": "Upgrade vom Benutzer abgebrochen",
"Upgrade one node at a time": "Aktualisieren Sie jeweils einen Knoten", "Upgrade one node at a time": "Aktualisieren Sie jeweils einen Knoten",
"Upgrade the system, offering the": "Aktualisieren Sie das System und bieten Sie das an",
"Upgrade to Proxmox VE 9": "Upgrade auf Proxmox VE 9", "Upgrade to Proxmox VE 9": "Upgrade auf Proxmox VE 9",
"Upgrading NVIDIA utils (Arch)...": "NVIDIA-Dienstprogramme werden aktualisiert (Arch)...", "Upgrading NVIDIA utils (Arch)...": "NVIDIA-Dienstprogramme werden aktualisiert (Arch)...",
"Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Eine verschlüsselte Kopie des Schlüssels auf PBS hochladen, damit Sie ihn auf einem neu installierten Host mit nur einer Passphrase wiederherstellen können?", "Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Eine verschlüsselte Kopie des Schlüssels auf PBS hochladen, damit Sie ihn auf einem neu installierten Host mit nur einer Passphrase wiederherstellen können?",
@@ -7186,7 +7258,7 @@
"Usage:": "Verwendung:", "Usage:": "Verwendung:",
"Use \"Check test progress\" to see results.": "Verwenden Sie „Testfortschritt prüfen“, um die Ergebnisse anzuzeigen.", "Use \"Check test progress\" to see results.": "Verwenden Sie „Testfortschritt prüfen“, um die Ergebnisse anzuzeigen.",
"Use 'Export to file' to save it and inspect manually.": "Verwenden Sie „In Datei exportieren“, um es zu speichern und manuell zu überprüfen.", "Use 'Export to file' to save it and inspect manually.": "Verwenden Sie „In Datei exportieren“, um es zu speichern und manuell zu überprüfen.",
"Use 'pct restore' / 'qmrestore' to recover their disks from your VM backups.": "Verwenden Sie „pct restart“ / „qmrestore“, um ihre Festplatten aus Ihren VM-Backups wiederherzustellen.", "Use 'pct restore' / 'qmrestore' to recover their disks from your VM backups.": "Verwenden Sie 'pct restore' / 'qmrestore', um die Datenträger aus den VM-Sicherungen wiederherzustellen.",
"Use Custom backup and uncheck the conflicting path from the list": "Verwenden Sie die benutzerdefinierte Sicherung und deaktivieren Sie den in Konflikt stehenden Pfad aus der Liste", "Use Custom backup and uncheck the conflicting path from the list": "Verwenden Sie die benutzerdefinierte Sicherung und deaktivieren Sie den in Konflikt stehenden Pfad aus der Liste",
"Use Default Settings?": "Standardeinstellungen verwenden?", "Use Default Settings?": "Standardeinstellungen verwenden?",
"Use Download first if you want to save a copy of the current key. Continue?": "Verwenden Sie zuerst „Herunterladen“, wenn Sie eine Kopie des aktuellen Schlüssels speichern möchten. Weitermachen?", "Use Download first if you want to save a copy of the current key. Continue?": "Verwenden Sie zuerst „Herunterladen“, wenn Sie eine Kopie des aktuellen Schlüssels speichern möchten. Weitermachen?",
@@ -7610,6 +7682,7 @@
"active VF(s)": "aktive VF(s)", "active VF(s)": "aktive VF(s)",
"active VFs": "aktive VFs", "active VFs": "aktive VFs",
"active Virtual Functions. Changing its driver binding would destroy every VF.": "aktive virtuelle Funktionen. Eine Änderung der Treiberbindung würde jedes VF zerstören.", "active Virtual Functions. Changing its driver binding would destroy every VF.": "aktive virtuelle Funktionen. Eine Änderung der Treiberbindung würde jedes VF zerstören.",
"added in Proxmox": "in Proxmox hinzugefügt",
"after copy; source tree is incomplete.": "nach Kopie; Der Quellbaum ist unvollständig.", "after copy; source tree is incomplete.": "nach Kopie; Der Quellbaum ist unvollständig.",
"after update.": "nach dem Update.", "after update.": "nach dem Update.",
"all guests": "alle Gäste", "all guests": "alle Gäste",
@@ -7930,8 +8003,8 @@
"partition(s). Partition table preserved.": "Partition(en). Partitionstabelle bleibt erhalten.", "partition(s). Partition table preserved.": "Partition(en). Partitionstabelle bleibt erhalten.",
"pasted Compose file": "Einfügen der Compose-Datei", "pasted Compose file": "Einfügen der Compose-Datei",
"paths for next boot (/etc/pve, guests, drivers, ...)": "Pfade für den nächsten Start (/etc/pve, Gäste, Treiber, ...)", "paths for next boot (/etc/pve, guests, drivers, ...)": "Pfade für den nächsten Start (/etc/pve, Gäste, Treiber, ...)",
"pct exec authorization failed": "PCT-Exec-Autorisierung fehlgeschlagen", "pct exec authorization failed": "Autorisierung für pct exec fehlgeschlagen",
"pct push failed. Check log:": "PCT-Push fehlgeschlagen. Protokoll prüfen:", "pct push failed. Check log:": "pct push fehlgeschlagen. Protokoll prüfen:",
"pending (reboot required to enumerate full group)": "ausstehend (Neustart erforderlich, um die vollständige Gruppe aufzulisten)", "pending (reboot required to enumerate full group)": "ausstehend (Neustart erforderlich, um die vollständige Gruppe aufzulisten)",
"phpMyAdmin is installed with arbitrary server connections enabled: the login page has a Server field where the address of the MySQL or MariaDB server is entered, together with its user and password.": "phpMyAdmin wird mit aktivierten beliebigen Serververbindungen installiert: Die Anmeldeseite hat ein Serverfeld, in dem die Adresse des MySQL- oder MariaDB-Servers zusammen mit seinem Benutzer und seinem Passwort eingegeben wird.", "phpMyAdmin is installed with arbitrary server connections enabled: the login page has a Server field where the address of the MySQL or MariaDB server is entered, together with its user and password.": "phpMyAdmin wird mit aktivierten beliebigen Serververbindungen installiert: Die Anmeldeseite hat ein Serverfeld, in dem die Adresse des MySQL- oder MariaDB-Servers zusammen mit seinem Benutzer und seinem Passwort eingegeben wird.",
"pigz configuration completed": "Pigz-Konfiguration abgeschlossen", "pigz configuration completed": "Pigz-Konfiguration abgeschlossen",
@@ -7954,8 +8027,8 @@
"pvesm command not found. This should not happen on Proxmox.": "pvesm-Befehl nicht gefunden. Dies sollte auf Proxmox nicht passieren.", "pvesm command not found. This should not happen on Proxmox.": "pvesm-Befehl nicht gefunden. Dies sollte auf Proxmox nicht passieren.",
"pvesm not available.": "pvesm nicht verfügbar.", "pvesm not available.": "pvesm nicht verfügbar.",
"pvesm not found.": "pvesm nicht gefunden.", "pvesm not found.": "pvesm nicht gefunden.",
"pvesm path failed, trying manual detection...": "pvesm-Pfad fehlgeschlagen, manuelle Erkennung versucht ...", "pvesm path failed, trying manual detection...": "pvesm path fehlgeschlagen, manuelle Erkennung wird versucht...",
"pvesm status failed": "pvesm-Status fehlgeschlagen", "pvesm status failed": "pvesm status fehlgeschlagen",
"pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad ist ein Free- und Open Source-Download-Manager, der in Python geschrieben ist und extrem leicht, leicht erweiterbar und vollständig über das Web zu verwalten ist.", "pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad ist ein Free- und Open Source-Download-Manager, der in Python geschrieben ist und extrem leicht, leicht erweiterbar und vollständig über das Web zu verwalten ist.",
"pyLoad web interface": "pyLoad Web Interface", "pyLoad web interface": "pyLoad Web Interface",
"qBittorrent WebUI password (user: admin)": "qBittorrent WebUI Passwort (Benutzer: admin)", "qBittorrent WebUI password (user: admin)": "qBittorrent WebUI Passwort (Benutzer: admin)",
@@ -7968,8 +8041,8 @@
"qBittorrent: invalid login response or missing session cookie": "qBittorrent: ungültige Anmeldeantwort oder fehlendes Sitzungscookie", "qBittorrent: invalid login response or missing session cookie": "qBittorrent: ungültige Anmeldeantwort oder fehlendes Sitzungscookie",
"raw USB disk — no filesystem (will be FORMATTED)": "Raw-USB-Festplatte – kein Dateisystem (wird FORMATTIERT)", "raw USB disk — no filesystem (will be FORMATTED)": "Raw-USB-Festplatte – kein Dateisystem (wird FORMATTIERT)",
"read-only": "Read-Only", "read-only": "Read-Only",
"reboot-quick alias added": "reboot-quick-Alias ​​hinzugefügt", "reboot-quick alias added": "reboot-quick-Alias hinzugefügt",
"reboot-quick alias is already configured": "reboot-quick Alias ​​ist bereits konfiguriert", "reboot-quick alias is already configured": "reboot-quick Alias ist bereits konfiguriert",
"recognition": "Erkennung", "recognition": "Erkennung",
"recognition only": "Nur Erkennung", "recognition only": "Nur Erkennung",
"recommended": "empfohlen", "recommended": "empfohlen",
@@ -7977,6 +8050,7 @@
"remapped users": "Benutzer neu zugeordnet", "remapped users": "Benutzer neu zugeordnet",
"remove the (now-empty) directory if possible": "Entfernen Sie nach Möglichkeit das (jetzt leere) Verzeichnis", "remove the (now-empty) directory if possible": "Entfernen Sie nach Möglichkeit das (jetzt leere) Verzeichnis",
"removed from Proxmox": "aus Proxmox entfernt", "removed from Proxmox": "aus Proxmox entfernt",
"removed in Proxmox": "in Proxmox entfernt",
"removed successfully from Proxmox.": "erfolgreich aus Proxmox entfernt.", "removed successfully from Proxmox.": "erfolgreich aus Proxmox entfernt.",
"requires running the official installer, which restarts the Monitor service.": "erfordert die Ausführung des offiziellen Installationsprogramms, das den Monitor-Dienst neu startet.", "requires running the official installer, which restarts the Monitor service.": "erfordert die Ausführung des offiziellen Installationsprogramms, das den Monitor-Dienst neu startet.",
"requires the package": "erfordert das Paket", "requires the package": "erfordert das Paket",
@@ -8013,8 +8087,8 @@
"skipped (already exist)": "übersprungen (existiert bereits)", "skipped (already exist)": "übersprungen (existiert bereits)",
"smbclient command is not working properly.": "Der smbclient-Befehl funktioniert nicht ordnungsgemäß.", "smbclient command is not working properly.": "Der smbclient-Befehl funktioniert nicht ordnungsgemäß.",
"smbclient command not found after installation.": "Der Befehl smbclient wurde nach der Installation nicht gefunden.", "smbclient command not found after installation.": "Der Befehl smbclient wurde nach der Installation nicht gefunden.",
"sources.list update skipped (no change)": "Aktualisierung der Quellenliste übersprungen (keine Änderung)", "sources.list update skipped (no change)": "Aktualisierung von sources.list übersprungen (keine Änderung)",
"sources.list updated to Trixie": "Quellen. Liste auf Trixie aktualisiert", "sources.list updated to Trixie": "sources.list auf Trixie aktualisiert",
"ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen ist fehlgeschlagen. Es kann kein neuer SSH-Schlüssel erstellt werden.", "ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen ist fehlgeschlagen. Es kann kein neuer SSH-Schlüssel erstellt werden.",
"stale entry/entries for interfaces no longer present": "veralteter Eintrag/Einträge für Schnittstellen, die nicht mehr vorhanden sind", "stale entry/entries for interfaces no longer present": "veralteter Eintrag/Einträge für Schnittstellen, die nicht mehr vorhanden sind",
"standard performance": "Standardleistung", "standard performance": "Standardleistung",
@@ -8033,7 +8107,7 @@
"switch_gpu_mode.sh was not found.": "switch_gpu_mode.sh wurde nicht gefunden.", "switch_gpu_mode.sh was not found.": "switch_gpu_mode.sh wurde nicht gefunden.",
"sysfs ROM dump failed — trying ACPI VFCT table...": "sysfs-ROM-Dump fehlgeschlagen – Versuch der ACPI-VFCT-Tabelle ...", "sysfs ROM dump failed — trying ACPI VFCT table...": "sysfs-ROM-Dump fehlgeschlagen – Versuch der ACPI-VFCT-Tabelle ...",
"syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "syslog-ng ermöglicht es Ihnen, Protokolle aus Ihrer gesamten Infrastruktur flexibel zu sammeln, zu analysieren, zu klassifizieren, neu zu schreiben und zu korrelieren und sie zu Protokollanalyse-Tools zu speichern oder zu leiten.", "syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "syslog-ng ermöglicht es Ihnen, Protokolle aus Ihrer gesamten Infrastruktur flexibel zu sammeln, zu analysieren, zu klassifizieren, neu zu schreiben und zu korrelieren und sie zu Protokollanalyse-Tools zu speichern oder zu leiten.",
"systemctl restart networking failed:": "systemctl-Neustart des Netzwerks fehlgeschlagen:", "systemctl restart networking failed:": "systemctl restart networking fehlgeschlagen:",
"systemd OnCalendar expression": "systemd OnCalendar-Ausdruck", "systemd OnCalendar expression": "systemd OnCalendar-Ausdruck",
"the API key was not generated on the first start": "Der API-Schlüssel wurde beim ersten Start nicht generiert", "the API key was not generated on the first start": "Der API-Schlüssel wurde beim ersten Start nicht generiert",
"the NVIDIA hook of the container is not available:": "der NVIDIA-Hook des Containers ist nicht verfügbar:", "the NVIDIA hook of the container is not available:": "der NVIDIA-Hook des Containers ist nicht verfügbar:",
@@ -8080,18 +8154,20 @@
"using": "verwenden", "using": "verwenden",
"using direct path (not guaranteed to survive reboots).": "Verwenden des direkten Pfads (keine Garantie, dass Neustarts überlebt werden).", "using direct path (not guaranteed to survive reboots).": "Verwenden des direkten Pfads (keine Garantie, dass Neustarts überlebt werden).",
"vCPUs:": "vCPUs:", "vCPUs:": "vCPUs:",
"value changed in Proxmox": "Wert in Proxmox geändert",
"vfio-pci IDs configured": "vfio-pci-IDs konfiguriert", "vfio-pci IDs configured": "vfio-pci-IDs konfiguriert",
"vfio-pci IDs in /etc/modprobe.d/vfio.conf": "vfio-pci-IDs in /etc/modprobe.d/vfio.conf", "vfio-pci IDs in /etc/modprobe.d/vfio.conf": "vfio-pci-IDs in /etc/modprobe.d/vfio.conf",
"video": "Video", "video": "Video",
"video + recognition": "Video + Erkennung", "video + recognition": "Video + Erkennung",
"video only": "nur Video", "video only": "nur Video",
"vzdump backup speed optimization completed": "vzdump-Backup-Geschwindigkeitsoptimierung abgeschlossen", "vzdump backup speed optimization completed": "vzdump-Backup-Geschwindigkeitsoptimierung abgeschlossen",
"wallabag builds its links from the address given during the installation. If it does not match the address of the container, edit lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME in /etc/pve/lxc/<CTID>.conf with the container stopped, and start it again.": "wallabag baut seine Links aus der während der Installation angegebenen Adresse auf. Wenn es nicht mit der Adresse des Containers übereinstimmt, bearbeiten Sie lxc.environment. Laufzeit: SYMFONY ENV DOMAIN NAME in /etc/pve/lxc/<CTID>.conf mit angehaltenem Container und erneut starten.", "wallabag builds its links from the address given during the installation. If it does not match the address of the container, edit lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME in /etc/pve/lxc/<CTID>.conf with the container stopped, and start it again.": "wallabag erzeugt seine Links aus der bei der Installation angegebenen Adresse. Passt diese nicht zur Container-Adresse, stoppen Sie den Container, bearbeiten Sie lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME in /etc/pve/lxc/<CTID>.conf und starten Sie ihn erneut.",
"wallabag listens on port 80 of the container and stores its data in SQLite.": "wallabag hört auf Port 80 des Containers und speichert seine Daten in SQLite.", "wallabag listens on port 80 of the container and stores its data in SQLite.": "wallabag hört auf Port 80 des Containers und speichert seine Daten in SQLite.",
"wallabag web interface": "wallabag Web-Schnittstelle", "wallabag web interface": "wallabag Web-Schnittstelle",
"was": "war", "was": "war",
"was installed, but the kernel reports:": "wurde installiert, aber der Kernel meldet:", "was installed, but the kernel reports:": "wurde installiert, aber der Kernel meldet:",
"when finished": "wenn fertig", "when finished": "wenn fertig",
"when the server has no subscription (you will be asked)": "Wenn der Server kein Abonnement hat (Sie werden gefragt)",
"will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "Bindet die GPU beim nächsten Neustart erneut an vfio-pci, wodurch der zu installierende Treiber beschädigt wird.", "will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "Bindet die GPU beim nächsten Neustart erneut an vfio-pci, wodurch der zu installierende Treiber beschädigt wird.",
"wipefs failed on": "Wipefs sind fehlgeschlagen", "wipefs failed on": "Wipefs sind fehlgeschlagen",
"with": "mit", "with": "mit",
+165 -89
View File
@@ -37,6 +37,7 @@
"/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (ruta por defecto de vzdump)", "/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (ruta por defecto de vzdump)",
"/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (por defecto en Proxmox)", "/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (por defecto en Proxmox)",
"1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx para todos. No se necesita un grupo compartido.", "1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx para todos. No se necesita un grupo compartido.",
"====== PBS UPDATE COMPLETED ======": "====== ACTUALIZACIÓN PBS COMPLETADA ======",
"====== PVE UPDATE COMPLETED ======": "====== ACTUALIZACIÓN PVE COMPLETADA ======", "====== PVE UPDATE COMPLETED ======": "====== ACTUALIZACIÓN PVE COMPLETADA ======",
">> Create a new backup job <<": ">> Crear un trabajo de backup nuevo <<", ">> Create a new backup job <<": ">> Crear un trabajo de backup nuevo <<",
">> Save and return <<": ">> Guardar y volver <<", ">> Save and return <<": ">> Guardar y volver <<",
@@ -86,6 +87,7 @@
"A modern wiki and knowledge base for teams": "Una wiki y base de conocimiento moderna para equipos", "A modern wiki and knowledge base for teams": "Una wiki y base de conocimiento moderna para equipos",
"A new ProxMenux version is available:": "Una nueva versión de ProxMenux está disponible:", "A new ProxMenux version is available:": "Una nueva versión de ProxMenux está disponible:",
"A new kernel is staged for the next boot:": "Se prepara un nuevo kernel para el siguiente arranque:", "A new kernel is staged for the next boot:": "Se prepara un nuevo kernel para el siguiente arranque:",
"A newer kernel is installed. Reboot before the next backup window to use it.": "Hay un kernel más reciente instalado. Reinicie antes de la próxima ventana de backups para usarlo.",
"A newer version is available:": "Hay una versión más nueva disponible:", "A newer version is available:": "Hay una versión más nueva disponible:",
"A pending operation exists for": "Existe una operación pendiente", "A pending operation exists for": "Existe una operación pendiente",
"A pending stack assembly already exists; it is not overwritten": "Ya existe un ensamblado de pila pendiente; no se sobrescribe", "A pending stack assembly already exists; it is not overwritten": "Ya existe un ensamblado de pila pendiente; no se sobrescribe",
@@ -168,6 +170,8 @@
"APT language configuration updated": "Configuración del idioma APT actualizada", "APT language configuration updated": "Configuración del idioma APT actualizada",
"APT language downloads restored": "Descargas del idioma APT restauradas", "APT language downloads restored": "Descargas del idioma APT restauradas",
"APT package lists updated": "Listas de paquetes APT actualizadas", "APT package lists updated": "Listas de paquetes APT actualizadas",
"APT refresh failed. Check the PBS subscription and repositories in Administration > Repositories. Log:": "La actualización de APT ha fallado. Revise la suscripción y los repositorios de PBS en Administration > Repositories. Log:",
"APT refresh failed. Check the PBS subscription and repositories in the native PBS interface; no source was changed.": "No se pudieron actualizar los índices de APT. Comprueba la suscripción y los repositorios de PBS en su interfaz nativa; no se ha cambiado ninguna fuente.",
"APT update failed. Check repository access; NVIDIA installation stopped.": "La actualización de APT ha fallado. Verifica el acceso a los repositorios; la instalación de NVIDIA se ha detenido.", "APT update failed. Check repository access; NVIDIA installation stopped.": "La actualización de APT ha fallado. Verifica el acceso a los repositorios; la instalación de NVIDIA se ha detenido.",
"Aborted": "Abortado", "Aborted": "Abortado",
"About to restore": "A punto de restaurar", "About to restore": "A punto de restaurar",
@@ -255,7 +259,7 @@
"Add new PVE 9 enterprise repository (deb822 format) (Only if using enterprise):": "Agregue un nuevo repositorio empresarial PVE 9 (formato deb822) (solo si usa Enterprise):", "Add new PVE 9 enterprise repository (deb822 format) (Only if using enterprise):": "Agregue un nuevo repositorio empresarial PVE 9 (formato deb822) (solo si usa Enterprise):",
"Add or change a device": "Agregar o cambiar un dispositivo", "Add or change a device": "Agregar o cambiar un dispositivo",
"Add or remove extra paths and devices": "Añadir o eliminar rutas y dispositivos extra", "Add or remove extra paths and devices": "Añadir o eliminar rutas y dispositivos extra",
"Add physical disk to VM via": "Agregue el disco físico a la VM mediante", "Add physical disk to VM via": "Añadir un disco físico a la VM mediante",
"Add share block in /etc/samba/smb.conf:": "Agregue el bloque para compartir en /etc/samba/smb.conf:", "Add share block in /etc/samba/smb.conf:": "Agregue el bloque para compartir en /etc/samba/smb.conf:",
"Add unprivileged flag to container configuration:": "Agregar la opción de contenedor sin privilegios a su configuración:", "Add unprivileged flag to container configuration:": "Agregar la opción de contenedor sin privilegios a su configuración:",
"Add virtual disk": "Agregar disco virtual", "Add virtual disk": "Agregar disco virtual",
@@ -316,7 +320,7 @@
"After confirming, you will be asked to choose the NVIDIA driver version to install.": "Después de confirmar, se le pedirá que elija la versión del controlador NVIDIA para instalar.", "After confirming, you will be asked to choose the NVIDIA driver version to install.": "Después de confirmar, se le pedirá que elija la versión del controlador NVIDIA para instalar.",
"After detailed analysis, no changes are needed.": "Después de un análisis detallado, no se necesitan cambios.", "After detailed analysis, no changes are needed.": "Después de un análisis detallado, no se necesitan cambios.",
"After detailed analysis, no cleanup is needed.": "Después de un análisis detallado, no es necesaria ninguna limpieza.", "After detailed analysis, no cleanup is needed.": "Después de un análisis detallado, no es necesaria ninguna limpieza.",
"After logging in, run: ip a to obtain the IP address.\nThen, enter that IP address in your web browser like this:\n http://IP_ADDRESS\n\nThis will open the Umbral OS dashboard.": "Después de iniciar sesión, ejecute: ip a para obtener la dirección IP.\nLuego, ingrese esa dirección IP en su navegador web de esta manera:\n http://DIRECCIÓN_IP\n\nEsto abrirá el panel de Umbral OS.", "After logging in, run: ip a to obtain the IP address.\nThen, enter that IP address in your web browser like this:\n http://IP_ADDRESS\n\nThis will open the Umbral OS dashboard.": "Después de iniciar sesión, ejecuta ip a para obtener la dirección IP.\nIntroduce esa dirección en el navegador de esta forma:\n http://IP_ADDRESS\n\nSe abrirá el panel de Umbral OS.",
"After pasting on the server, ensure the file is chmod 600 and owned by": "Después de pegarlo en el servidor, asegúrese de que el archivo sea chmod 600 y sea propiedad de", "After pasting on the server, ensure the file is chmod 600 and owned by": "Después de pegarlo en el servidor, asegúrese de que el archivo sea chmod 600 y sea propiedad de",
"After reboot the system will be fully accessible (SSH, web UI, login), but the following components will be reinstalled in BACKGROUND — until they finish, commands like nvidia-smi may not yet be available:": "Después de reiniciar, el sistema será completamente accesible (SSH, interfaz web e inicio de sesión), pero los siguientes componentes se reinstalarán EN SEGUNDO PLANO; hasta que finalicen, es posible que comandos como nvidia-smi aún no estén disponibles:", "After reboot the system will be fully accessible (SSH, web UI, login), but the following components will be reinstalled in BACKGROUND — until they finish, commands like nvidia-smi may not yet be available:": "Después de reiniciar, el sistema será completamente accesible (SSH, interfaz web e inicio de sesión), pero los siguientes componentes se reinstalarán EN SEGUNDO PLANO; hasta que finalicen, es posible que comandos como nvidia-smi aún no estén disponibles:",
"After reboot, these components will reinstall in background:": "Después de reiniciar, estos componentes se reinstalarán en segundo plano:", "After reboot, these components will reinstall in background:": "Después de reiniciar, estos componentes se reinstalarán en segundo plano:",
@@ -328,7 +332,7 @@
"After this LXC → VM switch, reboot the host so the new binding state is applied cleanly.": "Después de este cambio LXC → VM, reinicie el host para que el nuevo estado de enlace se aplique limpiamente.", "After this LXC → VM switch, reboot the host so the new binding state is applied cleanly.": "Después de este cambio LXC → VM, reinicie el host para que el nuevo estado de enlace se aplique limpiamente.",
"Airsonic Advanced web interface": "Interfaz web de Airsonic Advanced", "Airsonic Advanced web interface": "Interfaz web de Airsonic Advanced",
"Airsonic-advanced is a free, web-based media streamer, providing ubiquitious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.": "Airsonic-advanced es un servidor de streaming multimedia gratuito y basado en web que da acceso a tu música desde cualquier lugar. Úsalo para compartir tu música con amigos o para escuchar la tuya en el trabajo. Puede transmitir a varios reproductores a la vez, por ejemplo a uno en la cocina y a otro en el salón.", "Airsonic-advanced is a free, web-based media streamer, providing ubiquitious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.": "Airsonic-advanced es un servidor de streaming multimedia gratuito y basado en web que da acceso a tu música desde cualquier lugar. Úsalo para compartir tu música con amigos o para escuchar la tuya en el trabajo. Puede transmitir a varios reproductores a la vez, por ejemplo a uno en la cocina y a otro en el salón.",
"Aliases added to .bashrc": "Alias ​​agregados a .bashrc", "Aliases added to .bashrc": "Alias agregados a .bashrc",
"Alist Sync web interface": "Interfaz web de Alist Sync", "Alist Sync web interface": "Interfaz web de Alist Sync",
"All": "Todos", "All": "Todos",
"All Available Scripts": "Todos los scripts disponibles", "All Available Scripts": "Todos los scripts disponibles",
@@ -422,6 +426,7 @@
"Apply": "Aplicar", "Apply": "Aplicar",
"Apply AMD CPU fixes": "Aplicar correcciones para CPU AMD", "Apply AMD CPU fixes": "Aplicar correcciones para CPU AMD",
"Apply Available Updates": "Aplicar actualizaciones disponibles", "Apply Available Updates": "Aplicar actualizaciones disponibles",
"Apply PBS package upgrade": "Aplicar la actualización de paquetes de PBS",
"Apply and restart services:": "Aplicar y reiniciar servicios:", "Apply and restart services:": "Aplicar y reiniciar servicios:",
"Apply available updates": "Aplicar actualizaciones disponibles", "Apply available updates": "Aplicar actualizaciones disponibles",
"Apply boot/initramfs changes": "Aplicar cambios de arranque/initramfs", "Apply boot/initramfs changes": "Aplicar cambios de arranque/initramfs",
@@ -435,12 +440,15 @@
"Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "¿Aplicar las opciones de la plantilla actual del catálogo? Puede que se pidan rutas y ajustes nuevos obligatorios. Revisa la configuración resultante antes de recrear el CT.", "Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "¿Aplicar las opciones de la plantilla actual del catálogo? Puede que se pidan rutas y ajustes nuevos obligatorios. Revisa la configuración resultante antes de recrear el CT.",
"Apply read+write access for 'others' on the host directory?": "¿Aplicar acceso de lectura+escritura para 'otros' en el directorio de host?", "Apply read+write access for 'others' on the host directory?": "¿Aplicar acceso de lectura+escritura para 'otros' en el directorio de host?",
"Apply the options from the current catalog template? Your data and configuration are kept.": "¿Aplicar las opciones de la plantilla actual del catálogo? Sus datos y su configuración se mantienen.", "Apply the options from the current catalog template? Your data and configuration are kept.": "¿Aplicar las opciones de la plantilla actual del catálogo? Sus datos y su configuración se mantienen.",
"Apply the reviewed package upgrade now? Services may restart and packages listed for removal will be removed.": "¿Aplicar ahora la actualización de paquetes revisada? Los servicios podrían reiniciarse y se eliminarán los paquetes indicados para su eliminación.",
"Apply these changes?": "¿Aplicar estos cambios?", "Apply these changes?": "¿Aplicar estos cambios?",
"Applying AMD-specific fixes...": "Aplicando correcciones específicas de AMD...", "Applying AMD-specific fixes...": "Aplicando correcciones específicas de AMD...",
"Applying CPU and RAM changes without rebuilding the container...": "Aplicando los cambios de CPU y RAM sin recrear el contenedor...",
"Applying Changes": "Aplicando cambios", "Applying Changes": "Aplicando cambios",
"Applying Controller/NVMe passthrough to VM": "Aplicando el passthrough de controlador/NVMe a la VM", "Applying Controller/NVMe passthrough to VM": "Aplicando el passthrough de controlador/NVMe a la VM",
"Applying balanced memory optimization settings...": "Aplicando configuraciones equilibradas de optimización de memoria...", "Applying balanced memory optimization settings...": "Aplicando configuraciones equilibradas de optimización de memoria...",
"Applying changes safely...": "Aplicando cambios de forma segura...", "Applying changes safely...": "Aplicando cambios de forma segura...",
"Applying container settings without rebuilding the container...": "Aplicando los ajustes sin recrear el contenedor...",
"Applying custom restore": "Aplicar restauración personalizada", "Applying custom restore": "Aplicar restauración personalizada",
"Applying default VM configuration": "Aplicar la configuración predeterminada de VM", "Applying default VM configuration": "Aplicar la configuración predeterminada de VM",
"Applying host permissions for unprivileged LXC bind-mounts...": "Aplicando permisos de host para montajes de enlace LXC sin privilegios...", "Applying host permissions for unprivileged LXC bind-mounts...": "Aplicando permisos de host para montajes de enlace LXC sin privilegios...",
@@ -533,6 +541,7 @@
"Available Borg targets:": "Destinos Borg disponibles:", "Available Borg targets:": "Destinos Borg disponibles:",
"Available Disks on Host": "Discos disponibles en el host", "Available Disks on Host": "Discos disponibles en el host",
"Available ISO Images": "Imágenes ISO disponibles", "Available ISO Images": "Imágenes ISO disponibles",
"Available PBS Version": "Versión PBS disponible",
"Available PBS repositories:": "Repositorios de PBS disponibles:", "Available PBS repositories:": "Repositorios de PBS disponibles:",
"Available PVE Version": "Versión PVE disponible", "Available PVE Version": "Versión PVE disponible",
"Available Shares:": "Recursos compartidos disponibles:", "Available Shares:": "Recursos compartidos disponibles:",
@@ -560,7 +569,7 @@
"Babybuddy is a buddy for babies! Helps caregivers track sleep, feedings, diaper changes, tummy time and more to learn about and predict baby's needs without (as much) guess work.": "¡Babybuddy es un compañero para bebés! Ayuda a quienes los cuidan a registrar el sueño, las tomas, los cambios de pañal, el tiempo boca abajo y más, para conocer y prever las necesidades del bebé sin (tantas) conjeturas.", "Babybuddy is a buddy for babies! Helps caregivers track sleep, feedings, diaper changes, tummy time and more to learn about and predict baby's needs without (as much) guess work.": "¡Babybuddy es un compañero para bebés! Ayuda a quienes los cuidan a registrar el sueño, las tomas, los cambios de pañal, el tiempo boca abajo y más, para conocer y prever las necesidades del bebé sin (tantas) conjeturas.",
"Back": "Volver", "Back": "Volver",
"Back to job list": "Volver a la lista de trabajos", "Back to job list": "Volver a la lista de trabajos",
"Back to previous menu or Esc + Enter": "Volver al menú anterior o Esc + Enter", "Back to previous menu or Esc + Enter": "Volver al menú anterior o Esc + Intro",
"Backed up and cleared": "Guardado en backup y vaciado", "Backed up and cleared": "Guardado en backup y vaciado",
"Backend": "Backend", "Backend": "Backend",
"Backend:": "Parte trasera:", "Backend:": "Parte trasera:",
@@ -745,6 +754,7 @@
"Cannot verify the reused disk:": "No se puede verificar el disco reutilizado:", "Cannot verify the reused disk:": "No se puede verificar el disco reutilizado:",
"Cannot write the image archive to {storage}. Check storage access and mount status.": "No se puede escribir el archivo de la imagen en {storage}. Comprueba el acceso al almacenamiento y que esté montado.", "Cannot write the image archive to {storage}. Check storage access and mount status.": "No se puede escribir el archivo de la imagen en {storage}. Comprueba el acceso al almacenamiento y que esté montado.",
"Capabilities cannot be kept and all dropped at the same time": "No se pueden mantener las capacidades y todas se disminuyen al mismo tiempo", "Capabilities cannot be kept and all dropped at the same time": "No se pueden mantener las capacidades y todas se disminuyen al mismo tiempo",
"Catalog options": "Opciones del catálogo",
"Category": "Categoría", "Category": "Categoría",
"Caution: Maximum mode generates more heat.": "Precaución: el modo máximo genera más calor.", "Caution: Maximum mode generates more heat.": "Precaución: el modo máximo genera más calor.",
"Ceph check skipped by user flag (--ignore-ceph-check)": "Comprobación de Ceph omitida por el indicador del usuario (--ignore-ceph-check)", "Ceph check skipped by user flag (--ignore-ceph-check)": "Comprobación de Ceph omitida por el indicador del usuario (--ignore-ceph-check)",
@@ -776,7 +786,7 @@
"Certificate provider: empty for Let's Encrypt, zerossl for ZeroSSL": "Proveedor de certificados: vacío para Let's Encrypt, zerossl para ZeroSSL", "Certificate provider: empty for Let's Encrypt, zerossl for ZeroSSL": "Proveedor de certificados: vacío para Let's Encrypt, zerossl para ZeroSSL",
"Change GPU acceleration?": "¿Cambiar la aceleración de GPU?", "Change GPU acceleration?": "¿Cambiar la aceleración de GPU?",
"Change Language": "Cambiar idioma", "Change Language": "Cambiar idioma",
"Change Release Channel": "Cambiar canal de release", "Change Release Channel": "Cambiar canal de versiones",
"Change it after the first login.": "Cámbialo después del primer inicio de sesión.", "Change it after the first login.": "Cámbialo después del primer inicio de sesión.",
"Change or add an environment variable?": "¿Cambiar o añadir una variable de entorno?", "Change or add an environment variable?": "¿Cambiar o añadir una variable de entorno?",
"Change the access network?": "¿Cambiar la red de acceso?", "Change the access network?": "¿Cambiar la red de acceso?",
@@ -788,7 +798,7 @@
"Changes have been applied to the configuration file.": "Se han aplicado cambios al archivo de configuración.", "Changes have been applied to the configuration file.": "Se han aplicado cambios al archivo de configuración.",
"Changes prepared": "Cambios preparados", "Changes prepared": "Cambios preparados",
"Changes will apply after reboot.": "Los cambios se aplicarán después del reinicio.", "Changes will apply after reboot.": "Los cambios se aplicarán después del reinicio.",
"Changing Release Channel": "Cambio de canal de release", "Changing Release Channel": "Cambio de canal de versiones",
"Changing between a privileged and an unprivileged container, or the host monitor mode, requires installing the application again": "Cambiar entre contenedor privilegiado y sin privilegios, o el modo de monitor del host, requiere instalar la aplicación de nuevo", "Changing between a privileged and an unprivileged container, or the host monitor mode, requires installing the application again": "Cambiar entre contenedor privilegiado y sin privilegios, o el modo de monitor del host, requiere instalar la aplicación de nuevo",
"Changing the machine type on an existing installed VM is not safe: it changes the chipset and PCI slot layout, which typically prevents the guest OS from booting.": "Cambiar el tipo de máquina de una VM ya instalada no es seguro: cambia el chipset y la disposición de las ranuras PCI, lo que normalmente impide que arranque el sistema operativo de la VM.", "Changing the machine type on an existing installed VM is not safe: it changes the chipset and PCI slot layout, which typically prevents the guest OS from booting.": "Cambiar el tipo de máquina de una VM ya instalada no es seguro: cambia el chipset y la disposición de las ranuras PCI, lo que normalmente impide que arranque el sistema operativo de la VM.",
"Changing the rootfs or its storage requires a separate migration": "Cambiar los rootfs o su almacenamiento requiere una migración separada", "Changing the rootfs or its storage requires a separate migration": "Cambiar los rootfs o su almacenamiento requiere una migración separada",
@@ -797,20 +807,20 @@
"Check": "Controlar", "Check": "Controlar",
"Check BIOS/UEFI in Hardware > BIOS — must match what the original VM used": "Verifique BIOS/UEFI en Hardware > BIOS: debe coincidir con lo que usó la VM original", "Check BIOS/UEFI in Hardware > BIOS — must match what the original VM used": "Verifique BIOS/UEFI en Hardware > BIOS: debe coincidir con lo que usó la VM original",
"Check Coral USB/M.2 detection": "Comprobar la detección de Coral USB/M.2", "Check Coral USB/M.2 detection": "Comprobar la detección de Coral USB/M.2",
"Check LXC GPU/TPU mapping": "Verifique el mapeo LXC GPU/TPU", "Check LXC GPU/TPU mapping": "Comprobar el mapeo de GPU/TPU de los LXC",
"Check NFS Status": "Verificar el estado de NFS", "Check NFS Status": "Verificar el estado de NFS",
"Check NVIDIA driver and devices": "Verifique el controlador y los dispositivos NVIDIA", "Check NVIDIA driver and devices": "Comprobar el controlador y los dispositivos NVIDIA",
"Check Proxmox Web UI status": "Verifique el estado de la interfaz de usuario web de Proxmox", "Check Proxmox Web UI status": "Comprobar el estado de la interfaz web de Proxmox",
"Check Proxmox firewall status": "Comprobar el estado del cortafuegos de Proxmox", "Check Proxmox firewall status": "Comprobar el estado del cortafuegos de Proxmox",
"Check Samba Status": "Verificar el estado de Samba", "Check Samba Status": "Verificar el estado de Samba",
"Check VM disk/PCI slots": "Verifique el disco VM/ranuras PCI", "Check VM disk/PCI slots": "Verifique el disco VM/ranuras PCI",
"Check VM passthrough settings": "Revise la configuración de passthrough de la VM", "Check VM passthrough settings": "Revisar la configuración de passthrough de la VM",
"Check access to an image": "Comprobar el acceso a una imagen", "Check access to an image": "Comprobar el acceso a una imagen",
"Check connectivity with another host": "Verificar la conectividad con otro host", "Check connectivity with another host": "Verificar la conectividad con otro host",
"Check container mount points": "Verifique los puntos de montaje del contenedor", "Check container mount points": "Verifique los puntos de montaje del contenedor",
"Check if container is unprivileged:": "Compruebe si el contenedor no tiene privilegios:", "Check if container is unprivileged:": "Compruebe si el contenedor no tiene privilegios:",
"Check kernel params (IOMMU flags)": "Verifique los parámetros del kernel (indicadores IOMMU)", "Check kernel params (IOMMU flags)": "Comprobar los parámetros del kernel (opciones IOMMU)",
"Check loaded GPU/TPU modules": "Verifique los módulos GPU/TPU cargados", "Check loaded GPU/TPU modules": "Comprobar los módulos de GPU/TPU cargados",
"Check log:": "Revise el log:", "Check log:": "Revise el log:",
"Check resulting config:": "Verifique la configuración resultante:", "Check resulting config:": "Verifique la configuración resultante:",
"Check script not found": "Comprobar script no encontrado", "Check script not found": "Comprobar script no encontrado",
@@ -879,6 +889,7 @@
"Choose a schedule (systemd calendar-event syntax):": "Elija una programación (sintaxis de eventos de calendario de systemd):", "Choose a schedule (systemd calendar-event syntax):": "Elija una programación (sintaxis de eventos de calendario de systemd):",
"Choose a script to execute:": "Elige un script para ejecutar:", "Choose a script to execute:": "Elige un script para ejecutar:",
"Choose a server:": "Elige un servidor:", "Choose a server:": "Elige un servidor:",
"Choose a setting. Changes are applied only after the final review.": "Elige qué configurar. Los cambios solo se aplican tras la revisión final.",
"Choose a share to mount:": "Elige un recurso compartido para montar:", "Choose a share to mount:": "Elige un recurso compartido para montar:",
"Choose a specific supported GPU or USB node": "Elija un nodo concreto de GPU o USB compatible", "Choose a specific supported GPU or USB node": "Elija un nodo concreto de GPU o USB compatible",
"Choose action for this controller/NVMe:": "Elige la acción para este controlador/NVMe:", "Choose action for this controller/NVMe:": "Elige la acción para este controlador/NVMe:",
@@ -908,7 +919,7 @@
"Choose storage for the downloaded image.": "Elige el almacenamiento para la imagen descargada.", "Choose storage for the downloaded image.": "Elige el almacenamiento para la imagen descargada.",
"Choose the driver version for Coral M.2:": "Elige la versión del controlador para Coral M.2:", "Choose the driver version for Coral M.2:": "Elige la versión del controlador para Coral M.2:",
"Choose the filesystem for the new partition:": "Elige el sistema de archivos para la nueva partición:", "Choose the filesystem for the new partition:": "Elige el sistema de archivos para la nueva partición:",
"Choose the release channel to use:": "Elige el canal de release que quieres usar:", "Choose the release channel to use:": "Elige el canal de versiones que quieres usar:",
"Choose the storage for the EFI disk (4MB):": "Elija el almacenamiento para el disco EFI (4 MB):", "Choose the storage for the EFI disk (4MB):": "Elija el almacenamiento para el disco EFI (4 MB):",
"Choose the storage for the TPM disk (4MB):": "Elija el almacenamiento para el disco TPM (4 MB):", "Choose the storage for the TPM disk (4MB):": "Elija el almacenamiento para el disco TPM (4 MB):",
"Choose the storage for the loader disk:": "Elija el almacenamiento para el disco del cargador:", "Choose the storage for the loader disk:": "Elija el almacenamiento para el disco del cargador:",
@@ -959,6 +970,7 @@
"Code-server is VS Code running on a remote server, accessible through the browser.": "Code-server es VS Code ejecutándose en un servidor remoto, accesible desde el navegador.", "Code-server is VS Code running on a remote server, accessible through the browser.": "Code-server es VS Code ejecutándose en un servidor remoto, accesible desde el navegador.",
"CodeProject.AI Server": "CodeProject.AI Server", "CodeProject.AI Server": "CodeProject.AI Server",
"Command": "Comando", "Command": "Comando",
"Command failed": "El comando ha fallado",
"Command override for an unknown service:": "Anulación de comando para un servicio desconocido:", "Command override for an unknown service:": "Anulación de comando para un servicio desconocido:",
"Commenting any residual Bookworm lines in *.list...": "Comentando cualquier línea residual de Bookworm en *.list...", "Commenting any residual Bookworm lines in *.list...": "Comentando cualquier línea residual de Bookworm en *.list...",
"Commenting legacy PVE 8 repository .list files (if any)...": "Comentando archivos .list del repositorio PVE 8 heredado (si los hay)...", "Commenting legacy PVE 8 repository .list files (if any)...": "Comentando archivos .list del repositorio PVE 8 heredado (si los hay)...",
@@ -1514,6 +1526,7 @@
"Current NVIDIA inventory resolved: a refresh is required": "Actual inventario NVIDIA resuelto: un refresco es necesario", "Current NVIDIA inventory resolved: a refresh is required": "Actual inventario NVIDIA resuelto: un refresco es necesario",
"Current NVIDIA inventory resolved: no refresh is required": "Inventario NVIDIA actual resuelto: ningún refresco es necesario", "Current NVIDIA inventory resolved: no refresh is required": "Inventario NVIDIA actual resuelto: ningún refresco es necesario",
"Current Network Configuration": "Configuración de red actual", "Current Network Configuration": "Configuración de red actual",
"Current PBS Version": "Versión PBS actual",
"Current PVE Version": "Versión PVE actual", "Current PVE Version": "Versión PVE actual",
"Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Los scripts de host ProxMenux actuales registran recursos compartidos remotos como almacenamientos en Proxmox utilizando pvesm.", "Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Los scripts de host ProxMenux actuales registran recursos compartidos remotos como almacenamientos en Proxmox utilizando pvesm.",
"Current ProxMenux version:": "Versión actual de ProxMenux:", "Current ProxMenux version:": "Versión actual de ProxMenux:",
@@ -1628,8 +1641,8 @@
"Default Proxmox logo applied": "Logotipo predeterminado de Proxmox aplicado", "Default Proxmox logo applied": "Logotipo predeterminado de Proxmox aplicado",
"Default is none (video-only passthrough). Use SPACE to toggle selections.": "Por defecto, ninguno (passthrough solo de vídeo). Use ESPACIO para marcar o desmarcar.", "Default is none (video-only passthrough). Use SPACE to toggle selections.": "Por defecto, ninguno (passthrough solo de vídeo). Use ESPACIO para marcar o desmarcar.",
"Default journald configuration restored": "Configuración de diario predeterminada restaurada", "Default journald configuration restored": "Configuración de diario predeterminada restaurada",
"Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "La ubicación predeterminada es /mnt/<nombre>. El recurso compartido se montará aquí en el host con permisos abiertos para que un LXC sin privilegios pueda vincularlo, montarlo y escribir en él. Para acceder a LXC, vincule esta ruta con LXC Mount Manager.", "Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "La ubicación predeterminada es /mnt/<name>. El recurso compartido se montará aquí, en el host, con permisos abiertos para que un LXC sin privilegios pueda montarlo mediante un bind mount y escribir en él. Usa LXC Mount Manager para crear ese montaje en el LXC.",
"Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "La ubicación predeterminada es /mnt/<nombre>. El recurso compartido se montará aquí en el host. Utilice esta ruta en /etc/fstab. Para acceder a LXC, vincule esta ruta con LXC Mount Manager.", "Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "La ubicación predeterminada es /mnt/<name>. El recurso compartido se montará aquí, en el host. Usa esta ruta en /etc/fstab. Para acceder desde un LXC, crea un bind mount de esta ruta con LXC Mount Manager.",
"Default login": "Acceso predeterminado", "Default login": "Acceso predeterminado",
"Default options": "Opciones predeterminadas", "Default options": "Opciones predeterminadas",
"Default options read/write": "Opciones predeterminadas lectura/escritura", "Default options read/write": "Opciones predeterminadas lectura/escritura",
@@ -1641,8 +1654,8 @@
"Delete Borg target": "Eliminar destino Borg", "Delete Borg target": "Eliminar destino Borg",
"Delete Export": "Eliminar Exportar", "Delete Export": "Eliminar Exportar",
"Delete Share": "Eliminar Compartir", "Delete Share": "Eliminar Compartir",
"Delete a CT (irreversible). Use the correct <ctid>": "Eliminar un CT (irreversible). Utilice el <ctid> correcto", "Delete a CT (irreversible). Use the correct <ctid>": "Eliminar un CT (irreversible). Usa el <ctid> correcto",
"Delete a VM (irreversible). Use the correct <vmid>": "Eliminar una VM (irreversible). Utilice el <vmid> correcto", "Delete a VM (irreversible). Use the correct <vmid>": "Eliminar una VM (irreversible). Usa el <vmid> correcto",
"Delete archive": "Eliminar archivo", "Delete archive": "Eliminar archivo",
"Delete backup job": "¿Eliminar el trabajo de backup", "Delete backup job": "¿Eliminar el trabajo de backup",
"Delete it?": "¿Eliminarla?", "Delete it?": "¿Eliminarla?",
@@ -1679,6 +1692,7 @@
"Detected Coral hardware:": "Hardware Coral detectado:", "Detected Coral hardware:": "Hardware Coral detectado:",
"Detected GPU(s):": "GPU detectadas:", "Detected GPU(s):": "GPU detectadas:",
"Detected Intel CPU": "CPU Intel detectada", "Detected Intel CPU": "CPU Intel detectada",
"Detected Proxmox Backup Server": "Proxmox Backup Server detectado",
"Detected Proxmox VE": "Proxmox VE detectado", "Detected Proxmox VE": "Proxmox VE detectado",
"Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Proxmox VE 8.x detectado: instalación de Ceph Squid para Debian", "Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Proxmox VE 8.x detectado: instalación de Ceph Squid para Debian",
"Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Detectado Proxmox VE 9.x - Instalación de Ceph Squid para Debian Trixie", "Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Detectado Proxmox VE 9.x - Instalación de Ceph Squid para Debian Trixie",
@@ -1760,6 +1774,9 @@
"Disabling and stopping rpcbind service and socket...": "Deshabilitar y detener el servicio y el socket rpcbind...", "Disabling and stopping rpcbind service and socket...": "Deshabilitar y detener el servicio y el socket rpcbind...",
"Disabling non-free firmware warnings...": "Deshabilitando las advertencias de firmware no libre...", "Disabling non-free firmware warnings...": "Deshabilitando las advertencias de firmware no libre...",
"Disabling portmapper/rpcbind for security...": "Deshabilitando portmapper/rpcbind por seguridad...", "Disabling portmapper/rpcbind for security...": "Deshabilitando portmapper/rpcbind por seguridad...",
"Discard changes": "Descartar cambios",
"Discard unapplied changes?": "¿Descartar los cambios sin aplicar?",
"Discover NFS exports on a server": "Descubrir los exports NFS de un servidor",
"Discover targets on portal:": "Detectar destinos en el portal:", "Discover targets on portal:": "Detectar destinos en el portal:",
"Discovering iSCSI targets...": "Descubriendo destinos iSCSI...", "Discovering iSCSI targets...": "Descubriendo destinos iSCSI...",
"Discovery successful": "Detección correcta", "Discovery successful": "Detección correcta",
@@ -1839,6 +1856,7 @@
"Do you want to proceed with the system update?": "¿Quieres continuar con la actualización del sistema?", "Do you want to proceed with the system update?": "¿Quieres continuar con la actualización del sistema?",
"Do you want to proceed?": "¿Quieres continuar?", "Do you want to proceed?": "¿Quieres continuar?",
"Do you want to reboot now?": "¿Quieres reiniciar ahora?", "Do you want to reboot now?": "¿Quieres reiniciar ahora?",
"Do you want to remove the Proxmox subscription banner from the Proxmox Backup Server web interface?": "¿Desea eliminar el banner de suscripción de Proxmox de la interfaz web de Proxmox Backup Server?",
"Do you want to remove the stale entry from vfio.conf and continue?": "¿Quiere eliminar la entrada obsoleta de vfio.conf y continuar?", "Do you want to remove the stale entry from vfio.conf and continue?": "¿Quiere eliminar la entrada obsoleta de vfio.conf y continuar?",
"Do you want to remove the stale entry from vfio.conf?": "¿Quiere eliminar la entrada obsoleta de vfio.conf?", "Do you want to remove the stale entry from vfio.conf?": "¿Quiere eliminar la entrada obsoleta de vfio.conf?",
"Do you want to restart the network service now to apply changes?": "¿Quieres reiniciar el servicio de red ahora para aplicar los cambios?", "Do you want to restart the network service now to apply changes?": "¿Quieres reiniciar el servicio de red ahora para aplicar los cambios?",
@@ -1953,7 +1971,7 @@
"Enable": "Habilitar", "Enable": "Habilitar",
"Enable / disable job timer": "Activar/desactivar el temporizador de copias", "Enable / disable job timer": "Activar/desactivar el temporizador de copias",
"Enable High Availability services": "Activar servicios de alta disponibilidad", "Enable High Availability services": "Activar servicios de alta disponibilidad",
"Enable IOMMU in GRUB or ZFS boot": "Habilite IOMMU en el arranque GRUB o ZFS", "Enable IOMMU in GRUB or ZFS boot": "Activar IOMMU en el arranque GRUB o ZFS",
"Enable IOMMU support if not enabled": "Habilite la compatibilidad con IOMMU si no está habilitado", "Enable IOMMU support if not enabled": "Habilite la compatibilidad con IOMMU si no está habilitado",
"Enable IOMMU, reboot the host, and try again.": "Habilite IOMMU, reinicie el host y vuelva a intentarlo.", "Enable IOMMU, reboot the host, and try again.": "Habilite IOMMU, reinicie el host y vuelva a intentarlo.",
"Enable Intel/AMD VA-API video acceleration": "Activar la aceleración de vídeo VA-API de Intel/AMD", "Enable Intel/AMD VA-API video acceleration": "Activar la aceleración de vídeo VA-API de Intel/AMD",
@@ -2002,30 +2020,30 @@
"Ensuring proxmox.sources (PVE 9, no-subscription, deb822) is present...": "Garantizar que proxmox.sources (PVE 9, sin suscripción, deb822) esté presente...", "Ensuring proxmox.sources (PVE 9, no-subscription, deb822) is present...": "Garantizar que proxmox.sources (PVE 9, sin suscripción, deb822) esté presente...",
"Ensuring pve-enterprise.sources (PVE 9, deb822) is present...": "Garantizar que pve-enterprise.sources (PVE 9, deb822) esté presente...", "Ensuring pve-enterprise.sources (PVE 9, deb822) is present...": "Garantizar que pve-enterprise.sources (PVE 9, deb822) esté presente...",
"Enter": "Ingresar", "Enter": "Ingresar",
"Enter CT ID:": "Ingrese el ID de CT:", "Enter CT ID:": "Introduce el ID del CT:",
"Enter MSR register (e.g. 0x10):": "Ingrese el registro MSR (por ejemplo, 0x10):", "Enter MSR register (e.g. 0x10):": "Introduce el registro MSR (por ejemplo, 0x10):",
"Enter NFS export path (e.g., /mnt/shared):": "Ingrese la ruta de exportación NFS (por ejemplo, /mnt/shared):", "Enter NFS export path (e.g., /mnt/shared):": "Ingrese la ruta de exportación NFS (por ejemplo, /mnt/shared):",
"Enter NFS server IP or hostname:": "Ingrese la IP o el nombre de host del servidor NFS:", "Enter NFS server IP or hostname:": "Introduce la IP o el nombre de host del servidor NFS:",
"Enter NVMe device (e.g., nvme0):": "Ingrese el dispositivo NVMe (por ejemplo, nvme0):", "Enter NVMe device (e.g., nvme0):": "Introduce el dispositivo NVMe (por ejemplo, nvme0):",
"Enter PCI BDF (e.g. 0000:01:00.0):": "Ingrese PCI BDF (por ejemplo, 0000:01:00.0):", "Enter PCI BDF (e.g. 0000:01:00.0):": "Introduce el BDF PCI (por ejemplo, 0000:01:00.0):",
"Enter PCI BDF (e.g. 0000:04:00.0):": "Ingrese PCI BDF (por ejemplo, 0000:04:00.0):", "Enter PCI BDF (e.g. 0000:04:00.0):": "Ingrese PCI BDF (por ejemplo, 0000:04:00.0):",
"Enter Path": "Ingresar ruta", "Enter Path": "Ingresar ruta",
"Enter Samba server IP:": "Ingrese la IP del servidor Samba:", "Enter Samba server IP:": "Ingrese la IP del servidor Samba:",
"Enter Samba share name:": "Ingrese el nombre del recurso compartido de Samba:", "Enter Samba share name:": "Ingrese el nombre del recurso compartido de Samba:",
"Enter Tailscale Auth Key": "Ingrese la clave de autenticación de Tailscale", "Enter Tailscale Auth Key": "Ingrese la clave de autenticación de Tailscale",
"Enter VM ID:": "Introduzca el ID de la máquina virtual:", "Enter VM ID:": "Introduce el ID de la VM:",
"Enter VM/CT ID:": "Introduzca el ID de VM/CT:", "Enter VM/CT ID:": "Introduce el ID de la VM o del CT:",
"Enter ZFS pool name for the selected disk:": "Ingrese el nombre del grupo ZFS para el disco seleccionado:", "Enter ZFS pool name for the selected disk:": "Ingrese el nombre del grupo ZFS para el disco seleccionado:",
"Enter a name for the mount point (used as /mnt/<name>):": "Ingrese un nombre para el punto de montaje (usado como /mnt/<nombre>):", "Enter a name for the mount point (used as /mnt/<name>):": "Introduce un nombre para el punto de montaje (se usará como /mnt/<name>):",
"Enter a name for the new virtual machine:": "Ingrese un nombre para la nueva máquina virtual:", "Enter a name for the new virtual machine:": "Ingrese un nombre para la nueva máquina virtual:",
"Enter a non-empty password or access token on a single line.": "Escribe una contraseña o un token de acceso, en una sola línea.", "Enter a non-empty password or access token on a single line.": "Escribe una contraseña o un token de acceso, en una sola línea.",
"Enter a number, or write or paste a command:": "Ingrese un número, o escriba o pegue un comando:", "Enter a number, or write or paste a command:": "Introduce un número, o escribe o pega un comando:",
"Enter a systemd calendar event, e.g.:": "Introduzca un evento de calendario de systemd, por ejemplo:", "Enter a systemd calendar event, e.g.:": "Introduzca un evento de calendario de systemd, por ejemplo:",
"Enter a usable IPv4 address with its prefix, for example": "Introduzca una dirección IPv4 usable con su prefijo, por ejemplo", "Enter a usable IPv4 address with its prefix, for example": "Introduzca una dirección IPv4 usable con su prefijo, por ejemplo",
"Enter a valid registry username.": "Escribe un usuario válido para el registro.", "Enter a valid registry username.": "Escribe un usuario válido para el registro.",
"Enter backup file (.zst):": "Ingrese el archivo de backup (.zst):", "Enter backup file (.zst):": "Introduce el archivo de backup (.zst):",
"Enter backup path (.tar.zst):": "Ingrese la ruta de backup (.tar.zst):", "Enter backup path (.tar.zst):": "Introduce la ruta del backup (.tar.zst):",
"Enter backup path (.vma.zst):": "Ingrese la ruta de backup (.vma.zst):", "Enter backup path (.vma.zst):": "Introduce la ruta del backup (.vma.zst):",
"Enter base folder for temporary files and converter (default:": "Ingrese la carpeta base para archivos temporales y convertidor (predeterminado:", "Enter base folder for temporary files and converter (default:": "Ingrese la carpeta base para archivos temporales y convertidor (predeterminado:",
"Enter boot target (e.g. scsi0, sata0, ide0):": "Ingrese el destino de inicio (por ejemplo, scsi0, sata0, ide0):", "Enter boot target (e.g. scsi0, sata0, ide0):": "Ingrese el destino de inicio (por ejemplo, scsi0, sata0, ide0):",
"Enter content types (comma-separated):": "Introduzca los tipos de contenido (separados por comas):", "Enter content types (comma-separated):": "Introduzca los tipos de contenido (separados por comas):",
@@ -2038,30 +2056,30 @@
"Enter custom pathr": "Ingrese una ruta personalizada", "Enter custom pathr": "Ingrese una ruta personalizada",
"Enter destination directory for exported file(s):": "Ingrese el directorio de destino para los archivos exportados:", "Enter destination directory for exported file(s):": "Ingrese el directorio de destino para los archivos exportados:",
"Enter destination path for ISO file": "Ingrese la ruta de destino para el archivo ISO", "Enter destination path for ISO file": "Ingrese la ruta de destino para el archivo ISO",
"Enter destination path:": "Introduzca la ruta de destino:", "Enter destination path:": "Introduce la ruta de destino:",
"Enter device (e.g., sda or nvme0):": "Ingrese el dispositivo (por ejemplo, sda o nvme0):", "Enter device (e.g., sda or nvme0):": "Introduce el dispositivo (por ejemplo, sda o nvme0):",
"Enter directory containing OVA/OVF files:": "Ingrese al directorio que contiene los archivos OVA/OVF:", "Enter directory containing OVA/OVF files:": "Ingrese al directorio que contiene los archivos OVA/OVF:",
"Enter directory path:": "Introduzca la ruta del directorio:", "Enter directory path:": "Introduzca la ruta del directorio:",
"Enter disk image path:": "Ingrese la ruta de la imagen del disco:", "Enter disk image path:": "Introduce la ruta de la imagen de disco:",
"Enter disk or partition path (prefer /dev/disk/by-id/...):": "Ingrese la ruta del disco o la partición (prefiera /dev/disk/by-id/...):", "Enter disk or partition path (prefer /dev/disk/by-id/...):": "Ingrese la ruta del disco o la partición (prefiera /dev/disk/by-id/...):",
"Enter domain name:": "Introduzca el nombre de dominio:", "Enter domain name:": "Introduce el nombre de dominio:",
"Enter domain:": "Introduzca el dominio:", "Enter domain:": "Introduce el dominio:",
"Enter filesystem (ext4/xfs/btrfs):": "Ingrese al sistema de archivos (ext4/xfs/btrfs):", "Enter filesystem (ext4/xfs/btrfs):": "Ingrese al sistema de archivos (ext4/xfs/btrfs):",
"Enter folder name for /mnt:": "Ingrese el nombre de la carpeta para /mnt:", "Enter folder name for /mnt:": "Ingrese el nombre de la carpeta para /mnt:",
"Enter full disk path as shown above (starting with /dev/disk/by-id/xxx...):": "Ingrese la ruta completa del disco como se muestra arriba (comenzando con /dev/disk/by-id/xxx...):", "Enter full disk path as shown above (starting with /dev/disk/by-id/xxx...):": "Introduce la ruta completa del disco tal como aparece arriba (empieza por /dev/disk/by-id/xxx...):",
"Enter full path for mount point:": "Ingrese la ruta completa para el punto de montaje:", "Enter full path for mount point:": "Ingrese la ruta completa para el punto de montaje:",
"Enter full path to the disk image (e.g., /var/lib/vz/images/xyz.img):": "Ingrese la ruta completa a la imagen del disco (por ejemplo, /var/lib/vz/images/xyz.img):", "Enter full path to the disk image (e.g., /var/lib/vz/images/xyz.img):": "Introduce la ruta completa de la imagen de disco (por ejemplo, /var/lib/vz/images/xyz.img):",
"Enter full path to the input image (e.g., /var/lib/vz/images/disk.vmdk):": "Ingrese la ruta completa a la imagen de entrada (por ejemplo, /var/lib/vz/images/disk.vmdk):", "Enter full path to the input image (e.g., /var/lib/vz/images/disk.vmdk):": "Introduce la ruta completa de la imagen de entrada (por ejemplo, /var/lib/vz/images/disk.vmdk):",
"Enter full path:": "Ingrese la ruta completa:", "Enter full path:": "Ingrese la ruta completa:",
"Enter host IP (e.g., 192.168.0.100):": "Ingrese la IP del host (por ejemplo, 192.168.0.100):", "Enter host IP (e.g., 192.168.0.100):": "Ingrese la IP del host (por ejemplo, 192.168.0.100):",
"Enter host or IP:": "Ingrese el host o IP:", "Enter host or IP:": "Introduce el host o la IP:",
"Enter hostpci slot (e.g. 0):": "Ingrese la ranura hostpci (por ejemplo, 0):", "Enter hostpci slot (e.g. 0):": "Introduce el slot hostpci (por ejemplo, 0):",
"Enter hostpci slot number (e.g. 0):": "Ingrese el número de ranura hostpci (por ejemplo, 0):", "Enter hostpci slot number (e.g. 0):": "Ingrese el número de ranura hostpci (por ejemplo, 0):",
"Enter iSCSI target portal IP or hostname:": "Ingrese la IP o el nombre de host del portal de destino iSCSI:", "Enter iSCSI target portal IP or hostname:": "Ingrese la IP o el nombre de host del portal de destino iSCSI:",
"Enter image full path:": "Ingrese la ruta completa de la imagen:", "Enter image full path:": "Ingrese la ruta completa de la imagen:",
"Enter imported disk reference (e.g. local-lvm:vm-100-disk-0):": "Ingrese la referencia del disco importado (por ejemplo, local-lvm:vm-100-disk-0):", "Enter imported disk reference (e.g. local-lvm:vm-100-disk-0):": "Ingrese la referencia del disco importado (por ejemplo, local-lvm:vm-100-disk-0):",
"Enter interface (sata/scsi/virtio/ide):": "Ingrese a la interfaz (sata/scsi/virtio/ide):", "Enter interface (sata/scsi/virtio/ide):": "Ingrese a la interfaz (sata/scsi/virtio/ide):",
"Enter interface name (e.g. eth0):": "Ingrese el nombre de la interfaz (por ejemplo, eth0):", "Enter interface name (e.g. eth0):": "Introduce el nombre de la interfaz (por ejemplo, eth0):",
"Enter mount path on host:": "Ingrese la ruta de montaje en el host:", "Enter mount path on host:": "Ingrese la ruta de montaje en el host:",
"Enter mount point in CT (e.g. /mnt/data):": "Ingrese el punto de montaje en CT (por ejemplo, /mnt/data):", "Enter mount point in CT (e.g. /mnt/data):": "Ingrese el punto de montaje en CT (por ejemplo, /mnt/data):",
"Enter mp slot number (e.g. 0):": "Ingrese el número de ranura mp (por ejemplo, 0):", "Enter mp slot number (e.g. 0):": "Ingrese el número de ranura mp (por ejemplo, 0):",
@@ -2073,26 +2091,26 @@
"Enter password for": "Introduzca la contraseña para", "Enter password for": "Introduzca la contraseña para",
"Enter password:": "Introduzca la contraseña:", "Enter password:": "Introduzca la contraseña:",
"Enter path manually": "Ingrese la ruta manualmente", "Enter path manually": "Ingrese la ruta manualmente",
"Enter path to analyze:": "Ingrese la ruta a analizar:", "Enter path to analyze:": "Introduce la ruta que quieres analizar:",
"Enter path:": "Introduzca la ruta:", "Enter path:": "Introduzca la ruta:",
"Enter pool name to clear errors:": "Ingrese el nombre del grupo para borrar errores:", "Enter pool name to clear errors:": "Introduce el nombre del pool cuyos errores quieres borrar:",
"Enter pool name to export:": "Ingrese el nombre del grupo para exportar:", "Enter pool name to export:": "Introduce el nombre del pool que quieres exportar:",
"Enter pool name to import:": "Ingrese el nombre del grupo para importar:", "Enter pool name to import:": "Introduce el nombre del pool que quieres importar:",
"Enter pool name:": "Introduzca el nombre del grupo:", "Enter pool name:": "Introduce el nombre del pool:",
"Enter pool/dataset name:": "Ingrese el nombre del grupo/conjunto de datos:", "Enter pool/dataset name:": "Introduce el nombre del pool o dataset:",
"Enter pool/dataset to destroy:": "Ingrese el grupo/conjunto de datos para destruir:", "Enter pool/dataset to destroy:": "Introduce el pool o dataset que quieres destruir:",
"Enter pool/dataset to mount:": "Ingrese el grupo/conjunto de datos para montar:", "Enter pool/dataset to mount:": "Introduce el pool o dataset que quieres montar:",
"Enter search term (leave empty to show all scripts):": "Ingrese el término de búsqueda (déjelo vacío para mostrar todos los scripts):", "Enter search term (leave empty to show all scripts):": "Ingrese el término de búsqueda (déjelo vacío para mostrar todos los scripts):",
"Enter server IP": "Ingrese la IP del servidor", "Enter server IP": "Ingrese la IP del servidor",
"Enter server IP/hostname manually": "Ingrese la IP/nombre de host del servidor manualmente", "Enter server IP/hostname manually": "Ingrese la IP/nombre de host del servidor manualmente",
"Enter server IP:": "Ingrese la IP del servidor:", "Enter server IP:": "Introduce la IP del servidor:",
"Enter slot number (e.g. 0):": "Ingrese el número de ranura (por ejemplo, 0):", "Enter slot number (e.g. 0):": "Ingrese el número de ranura (por ejemplo, 0):",
"Enter storage ID for Proxmox:": "Ingrese el ID de almacenamiento para Proxmox:", "Enter storage ID for Proxmox:": "Ingrese el ID de almacenamiento para Proxmox:",
"Enter storage ID:": "Ingrese el ID de almacenamiento:", "Enter storage ID:": "Introduce el ID del almacenamiento:",
"Enter storage name:": "Introduzca el nombre del almacenamiento:", "Enter storage name:": "Introduce el nombre del almacenamiento:",
"Enter subnet (e.g., 192.168.0.0/24):": "Ingrese la subred (por ejemplo, 192.168.0.0/24):", "Enter subnet (e.g., 192.168.0.0/24):": "Ingrese la subred (por ejemplo, 192.168.0.0/24):",
"Enter target disk (e.g. /dev/sdX):": "Ingrese el disco de destino (por ejemplo, /dev/sdX):", "Enter target disk (e.g. /dev/sdX):": "Ingrese el disco de destino (por ejemplo, /dev/sdX):",
"Enter target storage name (e.g., local-lvm):": "Ingrese el nombre del almacenamiento de destino (por ejemplo, local-lvm):", "Enter target storage name (e.g., local-lvm):": "Introduce el nombre del almacenamiento de destino (por ejemplo, local-lvm):",
"Enter target storage:": "Ingrese el almacenamiento de destino:", "Enter target storage:": "Ingrese el almacenamiento de destino:",
"Enter the BORG REPOKEY passphrase for target:": "Ingrese la frase de contraseña de BORG REPOKEY para el destino:", "Enter the BORG REPOKEY passphrase for target:": "Ingrese la frase de contraseña de BORG REPOKEY para el destino:",
"Enter the Samba username:": "Ingrese el nombre de usuario de Samba:", "Enter the Samba username:": "Ingrese el nombre de usuario de Samba:",
@@ -2101,14 +2119,14 @@
"Enter the full path to the directory containing disk images:": "Ingrese la ruta completa al directorio que contiene las imágenes del disco:", "Enter the full path to the directory containing disk images:": "Ingrese la ruta completa al directorio que contiene las imágenes del disco:",
"Enter the full path to the host directory:": "Ingrese la ruta completa al directorio del host:", "Enter the full path to the host directory:": "Ingrese la ruta completa al directorio del host:",
"Enter the full path to the host folder:": "Ingrese la ruta completa a la carpeta del host:", "Enter the full path to the host folder:": "Ingrese la ruta completa a la carpeta del host:",
"Enter the full path to the output image (e.g., /var/lib/vz/images/output.qcow2):": "Ingrese la ruta completa a la imagen de salida (por ejemplo, /var/lib/vz/images/output.qcow2):", "Enter the full path to the output image (e.g., /var/lib/vz/images/output.qcow2):": "Introduce la ruta completa de la imagen de salida (por ejemplo, /var/lib/vz/images/output.qcow2):",
"Enter the host mount path:": "Ingrese la ruta de montaje del host:", "Enter the host mount path:": "Ingrese la ruta de montaje del host:",
"Enter the iperf3 server IP or hostname:": "Ingrese la IP o el nombre de host del servidor iperf3:", "Enter the iperf3 server IP or hostname:": "Ingrese la IP o el nombre de host del servidor iperf3:",
"Enter the maximum size (in MB) to allocate for /var/log in RAM (e.g. 128, 256, 512):": "Ingrese el tamaño máximo (en MB) para asignar para /var/log en la RAM (por ejemplo, 128, 256, 512):", "Enter the maximum size (in MB) to allocate for /var/log in RAM (e.g. 128, 256, 512):": "Ingrese el tamaño máximo (en MB) para asignar para /var/log en la RAM (por ejemplo, 128, 256, 512):",
"Enter the mount point for the NFS export (e.g., /mnt/mynfs):": "Ingrese el punto de montaje para la exportación NFS (por ejemplo, /mnt/mynfs):", "Enter the mount point for the NFS export (e.g., /mnt/mynfs):": "Ingrese el punto de montaje para la exportación NFS (por ejemplo, /mnt/mynfs):",
"Enter the mount point for the shared folder (e.g., /mnt/myshare):": "Ingrese el punto de montaje para la carpeta compartida (por ejemplo, /mnt/myshare):", "Enter the mount point for the shared folder (e.g., /mnt/myshare):": "Ingrese el punto de montaje para la carpeta compartida (por ejemplo, /mnt/myshare):",
"Enter the mount point inside the CT for": "Introduzca el punto de montaje dentro del CT para", "Enter the mount point inside the CT for": "Introduzca el punto de montaje dentro del CT para",
"Enter the number or type the interface name:": "Ingrese el número o escriba el nombre de la interfaz:", "Enter the number or type the interface name:": "Introduce el número o escribe el nombre de la interfaz:",
"Enter the password for Samba user:": "Ingrese la contraseña para el usuario de Samba:", "Enter the password for Samba user:": "Ingrese la contraseña para el usuario de Samba:",
"Enter the recovery passphrase set when the keyfile was created:": "Ingrese la frase de contraseña de recuperación establecida cuando se creó el archivo de claves:", "Enter the recovery passphrase set when the keyfile was created:": "Ingrese la frase de contraseña de recuperación establecida cuando se creó el archivo de claves:",
"Enter the size in whole GB, for example": "Introduzca el tamaño en GB enteros, por ejemplo", "Enter the size in whole GB, for example": "Introduzca el tamaño en GB enteros, por ejemplo",
@@ -2159,11 +2177,11 @@
"Example: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak": "Ejemplo: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak", "Example: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak": "Ejemplo: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak",
"Example: daily or Mon..Fri 03:00": "Ejemplo: diario o lunes a viernes 03:00", "Example: daily or Mon..Fri 03:00": "Ejemplo: diario o lunes a viernes 03:00",
"Example: mkdir -p /tmp/lxc_convert_114": "Ejemplo: mkdir -p /tmp/lxc_convert_114", "Example: mkdir -p /tmp/lxc_convert_114": "Ejemplo: mkdir -p /tmp/lxc_convert_114",
"Example: pct config 110 | grep unprivileged": "Ejemplo: configuración pct 110 | grep sin privilegios", "Example: pct config 110 | grep unprivileged": "Ejemplo: pct config 110 | grep unprivileged",
"Example: pct stop 110": "Ejemplo: parada pct 110", "Example: pct stop 110": "Ejemplo: pct stop 110",
"Example: pct stop 114": "Ejemplo: parada pct 114", "Example: pct stop 114": "Ejemplo: pct stop 114",
"Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Ejemplo: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup", "Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Ejemplo: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup",
"Example: pvesm path local-lvm:vm-114-disk-0": "Ejemplo: ruta pvesm local-lvm:vm-114-disk-0", "Example: pvesm path local-lvm:vm-114-disk-0": "Ejemplo: pvesm path local-lvm:vm-114-disk-0",
"Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Ejemplo: sed -i '/^sin privilegios: 1/d' /etc/pve/lxc/110.conf", "Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Ejemplo: sed -i '/^sin privilegios: 1/d' /etc/pve/lxc/110.conf",
"Examples:": "Ejemplos:", "Examples:": "Ejemplos:",
"Execute destructive rollback?": "¿Ejecutar el rollback destructivo?", "Execute destructive rollback?": "¿Ejecutar el rollback destructivo?",
@@ -2263,6 +2281,7 @@
"Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "No se pudo clonar el repositorio log2ram. Compruebe /tmp/log2ram_install.log", "Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "No se pudo clonar el repositorio log2ram. Compruebe /tmp/log2ram_install.log",
"Failed to configure EFI disk": "No se pudo configurar el disco EFI", "Failed to configure EFI disk": "No se pudo configurar el disco EFI",
"Failed to configure IOMMU automatically.": "No se pudo configurar IOMMU automáticamente.", "Failed to configure IOMMU automatically.": "No se pudo configurar IOMMU automáticamente.",
"Failed to configure Proxmox Backup Server repositories": "No se han podido configurar los repositorios de Proxmox Backup Server",
"Failed to configure Proxmox repositories": "No se pudieron configurar los repositorios de Proxmox", "Failed to configure Proxmox repositories": "No se pudieron configurar los repositorios de Proxmox",
"Failed to configure TPM device in VM": "No se pudo configurar el dispositivo TPM en VM", "Failed to configure TPM device in VM": "No se pudo configurar el dispositivo TPM en VM",
"Failed to configure repositories.": "No se pudieron configurar los repositorios.", "Failed to configure repositories.": "No se pudieron configurar los repositorios.",
@@ -2409,13 +2428,13 @@
"Fastfetch installed successfully": "Fastfetch se instaló correctamente", "Fastfetch installed successfully": "Fastfetch se instaló correctamente",
"Fastfetch is not installed correctly.": "Fastfetch no está instalado correctamente.", "Fastfetch is not installed correctly.": "Fastfetch no está instalado correctamente.",
"Fastfetch is not installed.": "Fastfetch no está instalado.", "Fastfetch is not installed.": "Fastfetch no está instalado.",
"Fastfetch is not installed. Run: apt install fastfetch": "Fastfetch no está instalado. Ejecute: apt install fastfetch", "Fastfetch is not installed. Run: apt install fastfetch": "Fastfetch no está instalado. Ejecuta: apt install fastfetch",
"Fastfetch now displays: System optimised by: ProxMenux": "Fastfetch ahora muestra: Sistema optimizado por: ProxMenux", "Fastfetch now displays: System optimised by: ProxMenux": "Fastfetch ahora muestra: Sistema optimizado por: ProxMenux",
"Fastfetch removed from system": "Fastfetch eliminado del sistema", "Fastfetch removed from system": "Fastfetch eliminado del sistema",
"Fastfetch will start automatically in the console": "Fastfetch se iniciará automáticamente en la consola", "Fastfetch will start automatically in the console": "Fastfetch se iniciará automáticamente en la consola",
"Ferdium is a desktop app that helps you organize how you use your favourite apps by combining them into one application.": "Ferdium es una aplicación de escritorio que te ayuda a organizar el uso de tus aplicaciones favoritas reuniéndolas en una sola.", "Ferdium is a desktop app that helps you organize how you use your favourite apps by combining them into one application.": "Ferdium es una aplicación de escritorio que te ayuda a organizar el uso de tus aplicaciones favoritas reuniéndolas en una sola.",
"Fetching NVIDIA driver versions supported by your GPU...": "Obteniendo versiones del controlador NVIDIA compatibles con su GPU...", "Fetching NVIDIA driver versions supported by your GPU...": "Obteniendo versiones del controlador NVIDIA compatibles con su GPU...",
"Figurine installation and configuration completed successfully.": "La instalación y configuración deFigurine se completó con éxito.", "Figurine installation and configuration completed successfully.": "La instalación y configuración de Figurine se completó correctamente.",
"Figurine is not installed.": "Figurine no está instalado.", "Figurine is not installed.": "Figurine no está instalado.",
"Figurine removed from system": "Figurine desinstalado del sistema", "Figurine removed from system": "Figurine desinstalado del sistema",
"File bind mounts do not support spaces:": "Los bind mounts de archivo no admiten espacios:", "File bind mounts do not support spaces:": "Los bind mounts de archivo no admiten espacios:",
@@ -2474,7 +2493,7 @@
"For a consistent export, the VM should be stopped.": "Para una exportación consistente, se debe detener la VM.", "For a consistent export, the VM should be stopped.": "Para una exportación consistente, se debe detener la VM.",
"For any question, issue or bug report, please use the official repository": "Para cualquier pregunta, problema o informe de error, utilice el repositorio oficial", "For any question, issue or bug report, please use the official repository": "Para cualquier pregunta, problema o informe de error, utilice el repositorio oficial",
"For dedicated GPUs, FLR is required by this policy to reduce VM": "Para GPU dedicadas, esta política requiere FLR para reducir la VM", "For dedicated GPUs, FLR is required by this policy to reduce VM": "Para GPU dedicadas, esta política requiere FLR para reducir la VM",
"For example: pct enter 101": "Por ejemplo: pct introduzca 101", "For example: pct enter 101": "Por ejemplo: pct enter 101",
"For guest shares add: --options guest": "Para recursos compartidos de invitado añada: --options guest", "For guest shares add: --options guest": "Para recursos compartidos de invitado añada: --options guest",
"For security reasons, the system cannot format it.": "Por razones de seguridad, el sistema no puede formatearlo.", "For security reasons, the system cannot format it.": "Por razones de seguridad, el sistema no puede formatearlo.",
"For unprivileged containers, use instead:": "Para contenedores sin privilegios, utilice en su lugar:", "For unprivileged containers, use instead:": "Para contenedores sin privilegios, utilice en su lugar:",
@@ -2483,8 +2502,8 @@
"Force NFSv3:": "Forzar NFSv3:", "Force NFSv3:": "Forzar NFSv3:",
"Force NFSv4:": "Forzar NFSv4:", "Force NFSv4:": "Forzar NFSv4:",
"Force stop VM now?": "¿Forzar la detención de VM ahora?", "Force stop VM now?": "¿Forzar la detención de VM ahora?",
"Force stop a container. Use the correct <ctid>": "Forzar la parada de un contenedor. Utilice el <ctid> correcto", "Force stop a container. Use the correct <ctid>": "Forzar la parada de un contenedor. Usa el <ctid> correcto",
"Force stop a virtual machine. Use the correct <vmid>": "Forzar la detención de una máquina virtual. Utilice el <vmid> correcto", "Force stop a virtual machine. Use the correct <vmid>": "Forzar la parada de una VM. Usa el <vmid> correcto",
"Forcing removal...": "Forzando la eliminación...", "Forcing removal...": "Forzando la eliminación...",
"Format / Wipe Physical Disk (Safe)": "Formatear/borrar disco físico (seguro)", "Format / Wipe Physical Disk (Safe)": "Formatear/borrar disco físico (seguro)",
"Format Mode": "Modo de formato", "Format Mode": "Modo de formato",
@@ -2970,6 +2989,7 @@
"Inaccessible device during VM startup": "Dispositivo inaccesible durante el inicio de la VM", "Inaccessible device during VM startup": "Dispositivo inaccesible durante el inicio de la VM",
"Inactive": "Inactivo", "Inactive": "Inactivo",
"Inactive (entry in fstab, not currently mounted)": "Inactivo (entrada en fstab, no montado actualmente)", "Inactive (entry in fstab, not currently mounted)": "Inactivo (entrada en fstab, no montado actualmente)",
"Include these changes in oci-compose?": "¿Incluir estos cambios en el oci-compose?",
"Include these resources in oci-compose?": "¿Incorporar estos recursos al oci-compose?", "Include these resources in oci-compose?": "¿Incorporar estos recursos al oci-compose?",
"Includes /etc/network (may drop SSH immediately)": "Incluye /etc/network (puede eliminar SSH inmediatamente)", "Includes /etc/network (may drop SSH immediately)": "Incluye /etc/network (puede eliminar SSH inmediatamente)",
"Includes /etc/zfs": "Incluye /etc/zfs", "Includes /etc/zfs": "Incluye /etc/zfs",
@@ -3206,6 +3226,8 @@
"Invalid ALLOWED_HOSTS value": "Valor inválido ALLOWED_HOSTS", "Invalid ALLOWED_HOSTS value": "Valor inválido ALLOWED_HOSTS",
"Invalid Home Assistant OS check timeout": "Inválido Home Assistant OS check timeout", "Invalid Home Assistant OS check timeout": "Inválido Home Assistant OS check timeout",
"Invalid ID": "ID no válida", "Invalid ID": "ID no válida",
"Invalid IPv4 address": "Dirección IPv4 no válida",
"Invalid IPv4 gateway": "Puerta de enlace IPv4 no válida",
"Invalid Intel render path": "Ruta de renderización Intel no válida", "Invalid Intel render path": "Ruta de renderización Intel no válida",
"Invalid Jellyfin path:": "Camino inválido Jellyfin:", "Invalid Jellyfin path:": "Camino inválido Jellyfin:",
"Invalid MAC address:": "Dirección MAC inválida:", "Invalid MAC address:": "Dirección MAC inválida:",
@@ -3238,6 +3260,7 @@
"Invalid administrator email": "Correo electrónico del administrador inválido", "Invalid administrator email": "Correo electrónico del administrador inválido",
"Invalid administrator user name": "Nombre del usuario del administrador inválido", "Invalid administrator user name": "Nombre del usuario del administrador inválido",
"Invalid base VMID": "Base inválida VMID", "Invalid base VMID": "Base inválida VMID",
"Invalid bridge": "Puente no válido",
"Invalid check package:": "Paquete de comprobación inválido:", "Invalid check package:": "Paquete de comprobación inválido:",
"Invalid configuration path:": "Vía de configuración inválida:", "Invalid configuration path:": "Vía de configuración inválida:",
"Invalid consume/export volumes": "Volumen de consumo/exportación inválidos", "Invalid consume/export volumes": "Volumen de consumo/exportación inválidos",
@@ -3301,6 +3324,7 @@
"Invalid remote name": "Nombre remoto inválido", "Invalid remote name": "Nombre remoto inválido",
"Invalid remote path": "Camino remoto inválido", "Invalid remote path": "Camino remoto inválido",
"Invalid repair version:": "Versión de reparación inválida:", "Invalid repair version:": "Versión de reparación inválida:",
"Invalid resource value": "Valor de recursos no válido",
"Invalid resources": "Recursos inválidos", "Invalid resources": "Recursos inválidos",
"Invalid restored volume path": "Camino de volumen restaurado inválido", "Invalid restored volume path": "Camino de volumen restaurado inválido",
"Invalid running state": "Estado de funcionamiento inválido", "Invalid running state": "Estado de funcionamiento inválido",
@@ -3319,6 +3343,7 @@
"Invalid stack members": "Miembros de pila no válidos", "Invalid stack members": "Miembros de pila no válidos",
"Invalid stack name": "Nombre de pila no válido", "Invalid stack name": "Nombre de pila no válido",
"Invalid stack operation": "Operación de pila no válida", "Invalid stack operation": "Operación de pila no válida",
"Invalid storage": "Almacenamiento no válido",
"Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "ID de almacenamiento no válido. Utilice únicamente letras, números, guiones y guiones bajos.", "Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "ID de almacenamiento no válido. Utilice únicamente letras, números, guiones y guiones bajos.",
"Invalid storage name": "Nombre de almacenamiento no válido", "Invalid storage name": "Nombre de almacenamiento no válido",
"Invalid suite application": "Aplicación de suite inválida", "Invalid suite application": "Aplicación de suite inválida",
@@ -3590,7 +3615,7 @@
"Lynis is a security auditing tool that performs comprehensive system scans including:": "Lynis es una herramienta de auditoría de seguridad que realiza análisis completos del sistema que incluyen:", "Lynis is a security auditing tool that performs comprehensive system scans including:": "Lynis es una herramienta de auditoría de seguridad que realiza análisis completos del sistema que incluyen:",
"Lynis is currently installed.": "Lynis está instalado actualmente.", "Lynis is currently installed.": "Lynis está instalado actualmente.",
"Lynis is not installed on this system.": "Lynis no está instalado en este sistema.", "Lynis is not installed on this system.": "Lynis no está instalado en este sistema.",
"Lynis is not installed. Run: apt install lynis": "Lynis no está instalado. Ejecute: apt install lynis", "Lynis is not installed. Run: apt install lynis": "Lynis no está instalado. Ejecuta: apt install lynis",
"Lynis is ready to use": "Lynis está listo para usar", "Lynis is ready to use": "Lynis está listo para usar",
"Lynis updated to version:": "Lynis actualizado a la versión:", "Lynis updated to version:": "Lynis actualizado a la versión:",
"Lynis version:": "Versión Lynis:", "Lynis version:": "Versión Lynis:",
@@ -4148,6 +4173,7 @@
"No active Proxmox storage accepts OCI template archives.": "Ningún almacenamiento activo de Proxmox acepta archivos de plantilla OCI.", "No active Proxmox storage accepts OCI template archives.": "Ningún almacenamiento activo de Proxmox acepta archivos de plantilla OCI.",
"No active exports or showmount not available": "No hay exportaciones activas o showmount no disponible", "No active exports or showmount not available": "No hay exportaciones activas o showmount no disponible",
"No active session": "Ninguna sesión activa", "No active session": "Ninguna sesión activa",
"No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.": "No hay suscripción activa ni un repositorio de PBS utilizable. El modo no interactivo no puede cambiar las fuentes de APT; configúrelas en Administration > Repositories.",
"No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "No hay suscripción activa ni un repositorio de PVE utilizable. El modo no interactivo no puede cambiar las fuentes de APT; configúralas en Nodo > Actualizaciones > Repositorios.", "No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "No hay suscripción activa ni un repositorio de PVE utilizable. El modo no interactivo no puede cambiar las fuentes de APT; configúralas en Nodo > Actualizaciones > Repositorios.",
"No additional GPU can be added.": "No se puede agregar ninguna GPU adicional.", "No additional GPU can be added.": "No se puede agregar ninguna GPU adicional.",
"No additional device needs to be added.": "No es necesario agregar ningún dispositivo adicional.", "No additional device needs to be added.": "No es necesario agregar ningún dispositivo adicional.",
@@ -4252,6 +4278,7 @@
"No password provided.": "No se proporcionó contraseña.", "No password provided.": "No se proporcionó contraseña.",
"No paths available": "No hay rutas disponibles", "No paths available": "No hay rutas disponibles",
"No paths selected. Select at least one path.": "No hay rutas seleccionadas. Seleccione al menos una ruta.", "No paths selected. Select at least one path.": "No hay rutas seleccionadas. Seleccione al menos una ruta.",
"No pending changes": "No hay cambios pendientes",
"No pending paths to schedule for reboot.": "No hay rutas pendientes para programar el reinicio.", "No pending paths to schedule for reboot.": "No hay rutas pendientes para programar el reinicio.",
"No permanent NFS mounts configured.": "No se han configurado montajes NFS permanentes.", "No permanent NFS mounts configured.": "No se han configurado montajes NFS permanentes.",
"No permanent Samba mounts configured.": "No hay montajes Samba permanentes configurados.", "No permanent Samba mounts configured.": "No hay montajes Samba permanentes configurados.",
@@ -4390,6 +4417,7 @@
"OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM eliminado (los artefactos de las marionetas pueden necesitar una limpieza manual)", "OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM eliminado (los artefactos de las marionetas pueden necesitar una limpieza manual)",
"OVH server detected": "Servidor OVH detectado", "OVH server detected": "Servidor OVH detectado",
"OVH server detection and RTM installation process completed": "Finalizado el proceso de detección del servidor OVH e instalación de RTM", "OVH server detection and RTM installation process completed": "Finalizado el proceso de detección del servidor OVH e instalación de RTM",
"Object detector": "Detector de objetos",
"Object detector for Frigate": "Detector de objetos para Frigate", "Object detector for Frigate": "Detector de objetos para Frigate",
"Observer is not responding on port 4357": "El observador no responde en el puerto 4357", "Observer is not responding on port 4357": "El observador no responde en el puerto 4357",
"Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian es una aplicación de notas que te permite crear, enlazar y organizar tus notas en tu dispositivo, con cientos de plugins y temas para personalizar tu flujo de trabajo. También puedes publicar tus notas en línea, acceder a ellas sin conexión y sincronizarlas de forma segura con cifrado de extremo a extremo.", "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian es una aplicación de notas que te permite crear, enlazar y organizar tus notas en tu dispositivo, con cientos de plugins y temas para personalizar tu flujo de trabajo. También puedes publicar tus notas en línea, acceder a ellas sin conexión y sincronizarlas de forma segura con cifrado de extremo a extremo.",
@@ -4515,11 +4543,17 @@
"PBS backup error log": "Log de errores de backup de PBS", "PBS backup error log": "Log de errores de backup de PBS",
"PBS backup failed.": "El backup de PBS falló.", "PBS backup failed.": "El backup de PBS falló.",
"PBS certificate": "Certificado PBS", "PBS certificate": "Certificado PBS",
"PBS datastores": "Datastores de PBS",
"PBS encryption keyfile (show / replace / remove)": "Archivo de claves de cifrado PBS (mostrar/reemplazar/eliminar)", "PBS encryption keyfile (show / replace / remove)": "Archivo de claves de cifrado PBS (mostrar/reemplazar/eliminar)",
"PBS encryption keyfile — detailed info": "Archivo de claves de cifrado PBS: información detallada", "PBS encryption keyfile — detailed info": "Archivo de claves de cifrado PBS: información detallada",
"PBS extraction failed": "Error en la extracción de PBS", "PBS extraction failed": "Error en la extracción de PBS",
"PBS host or IP address:": "Host de PBS o dirección IP:", "PBS host or IP address:": "Host de PBS o dirección IP:",
"PBS package update": "Actualización de paquetes de PBS",
"PBS package upgrade completed. Check whether a reboot is required before your next backup window.": "Actualización de paquetes de PBS completada. Comprueba si hace falta reiniciar antes de la próxima ventana de backups.",
"PBS package upgrade failed. Review the APT output before retrying.": "La actualización de paquetes de PBS ha fallado. Revisa la salida de APT antes de volver a intentarlo.",
"PBS upload": "Subida a PBS", "PBS upload": "Subida a PBS",
"PBS version and packages": "Versión y paquetes de PBS",
"PBS: using the configured APT repositories without changing sources.": "PBS: se utilizan los repositorios de APT configurados sin cambiar las fuentes.",
"PCI Address": "Dirección PCI", "PCI Address": "Dirección PCI",
"PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "Passthrough PCI, estado de TPM, configuración de cloud-init, hooks de Proxmox", "PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "Passthrough PCI, estado de TPM, configuración de cloud-init, hooks de Proxmox",
"PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "Passthrough PCI, estado de TPM, cloud-init, instantáneas, hooks específicos de Proxmox", "PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "Passthrough PCI, estado de TPM, cloud-init, instantáneas, hooks específicos de Proxmox",
@@ -4753,9 +4787,9 @@
"Preserving logs to /var/log.hdd before unmounting...": "Preservando logs en /var/log.hdd antes de desmontar...", "Preserving logs to /var/log.hdd before unmounting...": "Preservando logs en /var/log.hdd antes de desmontar...",
"Press 'q' to exit": "Presione 'q' para salir", "Press 'q' to exit": "Presione 'q' para salir",
"Press Ctrl+C to stop the server and return to menu.": "Presione Ctrl+C para detener el servidor y regresar al menú.", "Press Ctrl+C to stop the server and return to menu.": "Presione Ctrl+C para detener el servidor y regresar al menú.",
"Press ENTER to continue...": "Presione ENTER para continuar...", "Press ENTER to continue...": "Pulsa Intro para continuar...",
"Press ENTER to return to the menu...": "Presione ENTER para regresar al menú...", "Press ENTER to return to the menu...": "Pulsa Intro para volver al menú...",
"Press ENTER to return...": "Presione ENTER para regresar...", "Press ENTER to return...": "Pulsa Intro para volver...",
"Press Enter to close...": "Presione Entrar para cerrar...", "Press Enter to close...": "Presione Entrar para cerrar...",
"Press Enter to continue": "Presione Entrar para continuar", "Press Enter to continue": "Presione Entrar para continuar",
"Press Enter to continue...": "Presione Entrar para continuar...", "Press Enter to continue...": "Presione Entrar para continuar...",
@@ -4855,12 +4889,17 @@
"Proxmology logo applied": "Logotipo de Proxmology aplicado", "Proxmology logo applied": "Logotipo de Proxmology aplicado",
"Proxmox 9 system update allready": "Actualización del sistema Proxmox 9 ya", "Proxmox 9 system update allready": "Actualización del sistema Proxmox 9 ya",
"Proxmox APT repositories configured": "Repositorios Proxmox APT configurados", "Proxmox APT repositories configured": "Repositorios Proxmox APT configurados",
"Proxmox Backup Server": "Proxmox Backup Server",
"Proxmox Backup Server (PBS) destinations": "Destinos de Proxmox Backup Server (PBS)", "Proxmox Backup Server (PBS) destinations": "Destinos de Proxmox Backup Server (PBS)",
"Proxmox Backup Server APT repositories configured": "Repositorios APT de Proxmox Backup Server configurados",
"Proxmox Backup Server Update": "Actualización de Proxmox Backup Server",
"Proxmox Backup Server update completed.": "Actualización de Proxmox Backup Server completada.",
"Proxmox CIFS Storage Status:": "Estado de almacenamiento CIFS de Proxmox:", "Proxmox CIFS Storage Status:": "Estado de almacenamiento CIFS de Proxmox:",
"Proxmox NFS Storage Status:": "Estado de almacenamiento NFS de Proxmox:", "Proxmox NFS Storage Status:": "Estado de almacenamiento NFS de Proxmox:",
"Proxmox System Update": "Actualización del sistema Proxmox", "Proxmox System Update": "Actualización del sistema Proxmox",
"Proxmox Update": "Actualización de Proxmox", "Proxmox Update": "Actualización de Proxmox",
"Proxmox VE": "Proxmox VE", "Proxmox VE": "Proxmox VE",
"Proxmox VE 8 package upgrade failed. Review the APT output before retrying.": "La actualización de paquetes de Proxmox VE 8 ha fallado. Revisa la salida de APT antes de volver a intentarlo.",
"Proxmox VE 8 system update completed successfully": "La actualización del sistema Proxmox VE 8 se completó con éxito", "Proxmox VE 8 system update completed successfully": "La actualización del sistema Proxmox VE 8 se completó con éxito",
"Proxmox VE 8 to 9 Manual Upgrade Guide": "Guía de actualización manual de Proxmox VE 8 a 9", "Proxmox VE 8 to 9 Manual Upgrade Guide": "Guía de actualización manual de Proxmox VE 8 a 9",
"Proxmox VE 9.x no-subscription repository created": "Se creó el repositorio sin suscripción Proxmox VE 9.x", "Proxmox VE 9.x no-subscription repository created": "Se creó el repositorio sin suscripción Proxmox VE 9.x",
@@ -4868,6 +4907,7 @@
"Proxmox VE configuration completed.": "Configuración de Proxmox VE completada.", "Proxmox VE configuration completed.": "Configuración de Proxmox VE completada.",
"Proxmox VE safe update completed": "Actualización segura de Proxmox VE completada", "Proxmox VE safe update completed": "Actualización segura de Proxmox VE completada",
"Proxmox auth logger service created and started": "Servicio de registro de autenticación Proxmox creado e iniciado", "Proxmox auth logger service created and started": "Servicio de registro de autenticación Proxmox creado e iniciado",
"Proxmox could not apply the container settings": "Proxmox no ha podido aplicar los ajustes del contenedor",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Es posible que falte el repositorio empresarial de Proxmox o que no se pueda acceder a él. Intentando cambiar a sin suscripción...", "Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Es posible que falte el repositorio empresarial de Proxmox o que no se pueda acceder a él. Intentando cambiar a sin suscripción...",
"Proxmox filter configured": "Filtro Proxmox configurado", "Proxmox filter configured": "Filtro Proxmox configurado",
"Proxmox iSCSI Storage Status:": "Estado de almacenamiento Proxmox iSCSI:", "Proxmox iSCSI Storage Status:": "Estado de almacenamiento Proxmox iSCSI:",
@@ -4984,6 +5024,7 @@
"Rebuilding initramfs to apply nouveau blacklist before installation...": "Reconstruyendo initramfs para aplicar la lista negra de nouveau antes de la instalación...", "Rebuilding initramfs to apply nouveau blacklist before installation...": "Reconstruyendo initramfs para aplicar la lista negra de nouveau antes de la instalación...",
"Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "La reconstrucción del contenedor y su recuperación desde el backup están probadas con la misma imagen; la primera actualización a una imagen más nueva está aún por observar.", "Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "La reconstrucción del contenedor y su recuperación desde el backup están probadas con la misma imagen; la primera actualización a una imagen más nueva está aún por observar.",
"Recent": "Reciente", "Recent": "Reciente",
"Recent PBS tasks": "Tareas recientes de PBS",
"Recent Samba Servers": "Servidores Samba recientes", "Recent Samba Servers": "Servidores Samba recientes",
"Recent Samba server": "Servidor Samba reciente", "Recent Samba server": "Servidor Samba reciente",
"Recent logs:": "Logs recientes:", "Recent logs:": "Logs recientes:",
@@ -5036,6 +5077,7 @@
"Recreating the container...": "Recreando el contenedor...", "Recreating the container...": "Recreando el contenedor...",
"Recreating the container:": "Recreando el contenedor:", "Recreating the container:": "Recreando el contenedor:",
"Refresh APT index and verify repositories:": "Actualizar el índice APT y verificar los repositorios:", "Refresh APT index and verify repositories:": "Actualizar el índice APT y verificar los repositorios:",
"Refresh the configured APT repositories and review the PBS package upgrade? Repository files will be preserved.": "¿Actualizar los índices de los repositorios de APT configurados y revisar la actualización de paquetes de PBS? Se conservarán los archivos de los repositorios.",
"Refresh token of the connector, from the Twingate admin console": "Token de renovación del conector, de la consola de administración de Twingate", "Refresh token of the connector, from the Twingate admin console": "Token de renovación del conector, de la consola de administración de Twingate",
"Refresh your browser (Ctrl+Shift+R) to see changes": "Actualiza tu navegador (Ctrl+Shift+R) para ver los cambios", "Refresh your browser (Ctrl+Shift+R) to see changes": "Actualiza tu navegador (Ctrl+Shift+R) para ver los cambios",
"Refresh your browser to see changes (server restart may be required)": "Actualice su navegador para ver los cambios (es posible que sea necesario reiniciar el servidor)", "Refresh your browser to see changes (server restart may be required)": "Actualice su navegador para ver los cambios (es posible que sea necesario reiniciar el servidor)",
@@ -5061,10 +5103,10 @@
"Reinstalling": "Reinstalando", "Reinstalling": "Reinstalando",
"Reinstalling core Proxmox packages...": "Reinstalando los paquetes principales de Proxmox...", "Reinstalling core Proxmox packages...": "Reinstalando los paquetes principales de Proxmox...",
"Relative CPU priority (cpuunits)": "Prioridad relativa de la CPU (cpuunits)", "Relative CPU priority (cpuunits)": "Prioridad relativa de la CPU (cpuunits)",
"Release Channel": "Canal de release", "Release Channel": "Canal de versiones",
"Release channel set to Beta.": "Canal de release establecido en Beta.", "Release channel set to Beta.": "Canal de versiones establecido en Beta.",
"Release channel set to Stable.": "Canal de release establecido en Estable.", "Release channel set to Stable.": "Canal de versiones establecido en Estable.",
"Release channel:": "Canal de release:", "Release channel:": "Canal de versiones:",
"Reload Proxmox firewall rules": "Recargar las reglas del cortafuegos de Proxmox", "Reload Proxmox firewall rules": "Recargar las reglas del cortafuegos de Proxmox",
"Reload network configuration (ifupdown2)": "Recargar la configuración de red (ifupdown2)", "Reload network configuration (ifupdown2)": "Recargar la configuración de red (ifupdown2)",
"Remapped Users:": "Usuarios reasignados:", "Remapped Users:": "Usuarios reasignados:",
@@ -5125,6 +5167,8 @@
"Remove saved credentials": "Eliminar las credenciales guardadas", "Remove saved credentials": "Eliminar las credenciales guardadas",
"Remove subscription banner": "Eliminar banner de suscripción", "Remove subscription banner": "Eliminar banner de suscripción",
"Remove the application? Its container disks are deleted, and only a backup can bring them back.": "¿Eliminar la aplicación? Los discos de sus contenedores se eliminan y solo un backup puede recuperarlos.", "Remove the application? Its container disks are deleted, and only a backup can bring them back.": "¿Eliminar la aplicación? Los discos de sus contenedores se eliminan y solo un backup puede recuperarlos.",
"Remove the lock left on a VM by an interrupted task": "Quitar el bloqueo que deja en una VM una tarea interrumpida",
"Remove the lock left on a container by an interrupted task": "Quitar el bloqueo que deja en un contenedor una tarea interrumpida",
"Remove the saved credentials for {registry}?\nRunning apps will keep working.": "¿Eliminar las credenciales guardadas de {registry}?\nLas aplicaciones en marcha seguirán funcionando.", "Remove the saved credentials for {registry}?\nRunning apps will keep working.": "¿Eliminar las credenciales guardadas de {registry}?\nLas aplicaciones en marcha seguirán funcionando.",
"Remove the unprivileged flag from configuration:": "Elimine el flag unprivileged de la configuración:", "Remove the unprivileged flag from configuration:": "Elimine el flag unprivileged de la configuración:",
"Remove unused packages and their config": "Eliminar paquetes no utilizados y su configuración", "Remove unused packages and their config": "Eliminar paquetes no utilizados y su configuración",
@@ -5194,7 +5238,7 @@
"Replace": "Reemplazar", "Replace": "Reemplazar",
"Replace <container-id> with the actual ID.": "Reemplace <container-id> con el ID real.", "Replace <container-id> with the actual ID.": "Reemplace <container-id> con el ID real.",
"Replace <container-id> with your actual container ID": "Reemplace <container-id> con su ID de contenedor real", "Replace <container-id> with your actual container ID": "Reemplace <container-id> con su ID de contenedor real",
"Replace <storage-identifier> with the value from step 4": "Reemplace <identificador-de-almacenamiento> con el valor del paso 4", "Replace <storage-identifier> with the value from step 4": "Sustituye <storage-identifier> por el valor del paso 4",
"Replace invalid port(s)": "Reemplazar puerto(s) no válido(s)", "Replace invalid port(s)": "Reemplazar puerto(s) no válido(s)",
"Replace it?": "¿Reemplazarlo?", "Replace it?": "¿Reemplazarlo?",
"Replace paths with your actual values from step 5": "Reemplace las rutas con sus valores reales del paso 5", "Replace paths with your actual values from step 5": "Reemplace las rutas con sus valores reales del paso 5",
@@ -5206,6 +5250,7 @@
"Repository helper unavailable. NVIDIA installation stopped.": "El asistente de repositorios no está disponible. La instalación de NVIDIA se ha detenido.", "Repository helper unavailable. NVIDIA installation stopped.": "El asistente de repositorios no está disponible. La instalación de NVIDIA se ha detenido.",
"Repository policy returned an unexpected result.": "La política de repositorios ha devuelto un resultado inesperado.", "Repository policy returned an unexpected result.": "La política de repositorios ha devuelto un resultado inesperado.",
"Repository ready.": "Repositorio listo.", "Repository ready.": "Repositorio listo.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.": "Las fuentes de repositorio han cambiado, pero la actualización de la lista de paquetes de APT ha fallado. Operación detenida; las fuentes no se han revertido. Revise Administration > Repositories y vuelva a ejecutar apt-get update antes de continuar.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Las fuentes de los repositorios han cambiado, pero la actualización de la lista de paquetes de APT ha fallado. La operación se ha detenido; las fuentes no se han revertido. Revisa Nodo > Actualizaciones > Repositorios y vuelve a ejecutar apt-get update antes de continuar.", "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Las fuentes de los repositorios han cambiado, pero la actualización de la lista de paquetes de APT ha fallado. La operación se ha detenido; las fuentes no se han revertido. Revisa Nodo > Actualizaciones > Repositorios y vuelve a ejecutar apt-get update antes de continuar.",
"Repository switch declined; no APT source changed.": "Cambio de repositorio rechazado; no se ha modificado ninguna fuente de APT.", "Repository switch declined; no APT source changed.": "Cambio de repositorio rechazado; no se ha modificado ninguna fuente de APT.",
"Repository:": "Repositorio:", "Repository:": "Repositorio:",
@@ -5232,7 +5277,7 @@
"Resources": "Recursos", "Resources": "Recursos",
"Restart Network": "Reiniciar la red", "Restart Network": "Reiniciar la red",
"Restart Network Service": "Reiniciar el servicio de red", "Restart Network Service": "Reiniciar el servicio de red",
"Restart Web UI proxy": "Reinicie el proxy de la interfaz de usuario web", "Restart Web UI proxy": "Reiniciar el proxy de la interfaz web",
"Restart aMule automatically on a schedule (true or false)": "Reiniciar aMule automáticamente según una programación (true o false)", "Restart aMule automatically on a schedule (true or false)": "Reiniciar aMule automáticamente según una programación (true o false)",
"Restart and enable Samba:": "Reinicie y habilite Samba:", "Restart and enable Samba:": "Reinicie y habilite Samba:",
"Restart container to activate mount?": "¿Reiniciar el contenedor para activar el montaje?", "Restart container to activate mount?": "¿Reiniciar el contenedor para activar el montaje?",
@@ -5300,7 +5345,9 @@
"Reverting ZFS ARC tuning...": "Revirtiendo el ajuste ZFS ARC...", "Reverting ZFS ARC tuning...": "Revirtiendo el ajuste ZFS ARC...",
"Reverting pigz wrapper...": "Revertir la envoltura de pigz...", "Reverting pigz wrapper...": "Revertir la envoltura de pigz...",
"Reverting vzdump speed tuning...": "Revertir el ajuste de velocidad de vzdump...", "Reverting vzdump speed tuning...": "Revertir el ajuste de velocidad de vzdump...",
"Review and apply": "Revisar y aplicar",
"Review external OCI changes": "Revisar cambios externos de OCI", "Review external OCI changes": "Revisar cambios externos de OCI",
"Review package upgrade": "Revisar la actualización de paquetes",
"Review passthrough config files": "Revisar los archivos de configuración de passthrough", "Review passthrough config files": "Revisar los archivos de configuración de passthrough",
"Review what will be removed": "Revisa lo que se eliminará", "Review what will be removed": "Revisa lo que se eliminará",
"Rip DVD and Blu-ray media from a browser": "Extrae DVD y Blu-ray desde un navegador", "Rip DVD and Blu-ray media from a browser": "Extrae DVD y Blu-ray desde un navegador",
@@ -5323,9 +5370,9 @@
"Run GGUF LLMs locally with GPU acceleration": "Ejecuta LLM en formato GGUF en local con aceleración por GPU", "Run GGUF LLMs locally with GPU acceleration": "Ejecuta LLM en formato GGUF en local con aceleración por GPU",
"Run Now": "Ejecutar ahora", "Run Now": "Ejecutar ahora",
"Run PVE 8 to 9": "Ejecuta PVE 8 a 9", "Run PVE 8 to 9": "Ejecuta PVE 8 a 9",
"Run PVE 8 to 9 check": "Ejecute la verificación PVE 8 a 9", "Run PVE 8 to 9 check": "Ejecutar la comprobación de PVE 8 a 9",
"Run \\\"Install NVIDIA Drivers on Host\\\" first so the installer is cached.": "Primero ejecute \\\"Instalar controladores NVIDIA en el host\\\" para que el instalador se almacene en caché.", "Run \\\"Install NVIDIA Drivers on Host\\\" first so the installer is cached.": "Primero ejecute \\\"Instalar controladores NVIDIA en el host\\\" para que el instalador se almacene en caché.",
"Run a full security audit": "Ejecute una auditoría de seguridad completa", "Run a full security audit": "Ejecutar una auditoría de seguridad completa",
"Run a job now": "Ejecute un trabajo ahora", "Run a job now": "Ejecute un trabajo ahora",
"Run apt-get install -f to complete any pending package configurations": "Ejecutar apt-get install -f para completar las configuraciones de paquetes pendientes", "Run apt-get install -f to complete any pending package configurations": "Ejecutar apt-get install -f para completar las configuraciones de paquetes pendientes",
"Run as root on the Proxmox node; the registry contains private data": "Ejecutar como root en el nodo Proxmox; el registro contiene datos privados", "Run as root on the Proxmox node; the registry contains private data": "Ejecutar como root en el nodo Proxmox; el registro contiene datos privados",
@@ -5341,8 +5388,9 @@
"Run now": "Ejecutar ahora", "Run now": "Ejecutar ahora",
"Run security audit now": "Ejecute la auditoría de seguridad ahora", "Run security audit now": "Ejecute la auditoría de seguridad ahora",
"Run the following inside the VM:": "Ejecute lo siguiente dentro de la VM:", "Run the following inside the VM:": "Ejecute lo siguiente dentro de la VM:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "Ejecute el cambio de canal de release desde una sesión SSH o la consola del host Proxmox con:", "Run the release-channel switch from an SSH session or the Proxmox host console with:": "Ejecuta el cambio de canal de versiones desde una sesión SSH o la consola del host Proxmox con:",
"Run the update from an SSH session or the Proxmox host console with:": "Ejecute la actualización desde una sesión SSH o la consola del host Proxmox con:", "Run the update from an SSH session or the Proxmox host console with:": "Ejecute la actualización desde una sesión SSH o la consola del host Proxmox con:",
"Run this operation as root.": "Ejecuta esta operación como root.",
"Run upgrade checklist script:": "Ejecute el script de la lista de verificación de actualización:", "Run upgrade checklist script:": "Ejecute el script de la lista de verificación de actualización:",
"Running": "En ejecución", "Running": "En ejecución",
"Running Lynis security audit...": "Ejecutando auditoría de seguridad de Lynis...", "Running Lynis security audit...": "Ejecutando auditoría de seguridad de Lynis...",
@@ -5535,7 +5583,7 @@
"Select Virtual Machine": "Seleccionar máquina virtual", "Select Virtual Machine": "Seleccionar máquina virtual",
"Select a Custom Logo": "Seleccione un logotipo personalizado", "Select a Custom Logo": "Seleccione un logotipo personalizado",
"Select a VirtIO ISO to use:": "Seleccione una ISO de VirtIO para usar:", "Select a VirtIO ISO to use:": "Seleccione una ISO de VirtIO para usar:",
"Select a category of useful commands:": "Seleccione una categoría de comandos útiles:", "Select a category of useful commands:": "Elige una categoría de comandos útiles:",
"Select a category or search for applications:": "Seleccione una categoría o busque aplicaciones:", "Select a category or search for applications:": "Seleccione una categoría o busque aplicaciones:",
"Select a category or search for scripts:": "Seleccione una categoría o busque scripts:", "Select a category or search for scripts:": "Seleccione una categoría o busque scripts:",
"Select a custom ISO to use:": "Seleccione una ISO personalizada para usar:", "Select a custom ISO to use:": "Seleccione una ISO personalizada para usar:",
@@ -5577,7 +5625,7 @@
"Select conversion guide:": "Seleccione la guía de conversión:", "Select conversion guide:": "Seleccione la guía de conversión:",
"Select conversion option:": "Seleccione la opción de conversión:", "Select conversion option:": "Seleccione la opción de conversión:",
"Select destination": "Seleccionar destino", "Select destination": "Seleccionar destino",
"Select disk interface type:": "Seleccione el tipo de interfaz de disco:", "Select disk interface type:": "Elige el tipo de interfaz del disco:",
"Select driver version": "Seleccione la versión del controlador", "Select driver version": "Seleccione la versión del controlador",
"Select export format:": "Seleccione el formato de exportación:", "Select export format:": "Seleccione el formato de exportación:",
"Select export permissions:": "Seleccione permisos de exportación:", "Select export permissions:": "Seleccione permisos de exportación:",
@@ -5757,6 +5805,7 @@
"ShareCoveX panel": "Panel de ShareCoveX", "ShareCoveX panel": "Panel de ShareCoveX",
"Shared Directory Ready:": "Directorio compartido listo:", "Shared Directory Ready:": "Directorio compartido listo:",
"Shared Group": "Grupo compartido", "Shared Group": "Grupo compartido",
"Shared directories": "Directorios compartidos",
"Shared directory created:": "Directorio compartido creado:", "Shared directory created:": "Directorio compartido creado:",
"Shared directory for consume and export": "Directorio compartido para consumo y exportación", "Shared directory for consume and export": "Directorio compartido para consumo y exportación",
"Shared directory for copy/sync operations": "Directorio compartido para operaciones de copia/sincronización", "Shared directory for copy/sync operations": "Directorio compartido para operaciones de copia/sincronización",
@@ -5786,6 +5835,7 @@
"Should show pve-manager/9.x.x": "Debería mostrar pve-manager/9.x.x", "Should show pve-manager/9.x.x": "Debería mostrar pve-manager/9.x.x",
"Shoutrrr addresses for the notifications (optional)": "Direcciones de Shoutrrr para las notificaciones (opcional)", "Shoutrrr addresses for the notifications (optional)": "Direcciones de Shoutrrr para las notificaciones (opcional)",
"Show All Scripts": "Mostrar todos los scripts", "Show All Scripts": "Mostrar todos los scripts",
"Show CT users and their UID/GID on the host": "Ver usuarios del CT y su UID/GID en el host",
"Show CT users in table format": "Mostrar usuarios de CT en formato de tabla", "Show CT users in table format": "Mostrar usuarios de CT en formato de tabla",
"Show Container Privilege Status": "Mostrar estado de privilegios de los contenedores", "Show Container Privilege Status": "Mostrar estado de privilegios de los contenedores",
"Show DNS resolution status": "Mostrar estado de resolución DNS", "Show DNS resolution status": "Mostrar estado de resolución DNS",
@@ -5799,14 +5849,14 @@
"Show Proxmox version": "Mostrar versión de Proxmox", "Show Proxmox version": "Mostrar versión de Proxmox",
"Show Routing Table": "Mostrar tabla de enrutamiento", "Show Routing Table": "Mostrar tabla de enrutamiento",
"Show UUID and filesystem type of block devices": "Mostrar UUID y tipo de sistema de archivos de dispositivos de bloque", "Show UUID and filesystem type of block devices": "Mostrar UUID y tipo de sistema de archivos de dispositivos de bloque",
"Show VM configuration. Use the correct <vmid>": "Mostrar la configuración de la máquina virtual. Utilice el <vmid> correcto", "Show VM configuration. Use the correct <vmid>": "Mostrar la configuración de la VM. Usa el <vmid> correcto",
"Show Version Information": "Mostrar información de versión", "Show Version Information": "Mostrar información de versión",
"Show ZFS pool status": "Mostrar el estado del grupo ZFS", "Show ZFS pool status": "Mostrar el estado del grupo ZFS",
"Show active firewall rules (iptables)": "Mostrar las reglas activas del cortafuegos (iptables)", "Show active firewall rules (iptables)": "Mostrar las reglas activas del cortafuegos (iptables)",
"Show all active mounts from the kernel": "Mostrar todos los montajes activos del kernel", "Show all active mounts from the kernel": "Mostrar todos los montajes activos del kernel",
"Show available Proxmox upgrades": "Mostrar actualizaciones de Proxmox disponibles", "Show available Proxmox upgrades": "Mostrar actualizaciones de Proxmox disponibles",
"Show configured network bridges": "Mostrar puentes de red configurados", "Show configured network bridges": "Mostrar puentes de red configurados",
"Show container configuration. Use the correct <ctid>": "Mostrar la configuración del contenedor. Utilice el <ctid> correcto", "Show container configuration. Use the correct <ctid>": "Mostrar la configuración del contenedor. Usa el <ctid> correcto",
"Show detailed keyfile info": "Mostrar información detallada del archivo de claves", "Show detailed keyfile info": "Mostrar información detallada del archivo de claves",
"Show disk usage by mount point": "Mostrar uso del disco por punto de montaje", "Show disk usage by mount point": "Mostrar uso del disco por punto de montaje",
"Show jobs and last run status": "Mostrar tareas y estado de la última ejecución", "Show jobs and last run status": "Mostrar tareas y estado de la última ejecución",
@@ -5928,8 +5978,8 @@
"Start VM": "Iniciar máquina virtual", "Start VM": "Iniciar máquina virtual",
"Start VM after creation": "Iniciar VM después de la creación", "Start VM after creation": "Iniciar VM después de la creación",
"Start VM after creation?": "¿Iniciar VM después de la creación?", "Start VM after creation?": "¿Iniciar VM después de la creación?",
"Start a container. Use the correct <ctid>": "Inicie un contenedor. Utilice el <ctid> correcto", "Start a container. Use the correct <ctid>": "Iniciar un contenedor. Usa el <ctid> correcto",
"Start a virtual machine. Use the correct <vmid>": "Inicie una máquina virtual. Utilice el <vmid> correcto", "Start a virtual machine. Use the correct <vmid>": "Iniciar una VM. Usa el <vmid> correcto",
"Start each LXC with Proxmox (no coordinated startup)": "Iniciar cada LXC con Proxmox (sin arranque coordinado)", "Start each LXC with Proxmox (no coordinated startup)": "Iniciar cada LXC con Proxmox (sin arranque coordinado)",
"Start long self-test (hours)": "Iniciar autotest largo (horas)", "Start long self-test (hours)": "Iniciar autotest largo (horas)",
"Start long test now?": "¿Iniciar una prueba larga ahora?", "Start long test now?": "¿Iniciar una prueba larga ahora?",
@@ -5948,6 +5998,7 @@
"Start the VM to begin Windows installation from the mounted ISO.": "Inicie la VM para comenzar la instalación de Windows desde la ISO montada.", "Start the VM to begin Windows installation from the mounted ISO.": "Inicie la VM para comenzar la instalación de Windows desde la ISO montada.",
"Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "¿Iniciar las aplicaciones una vez registradas? Responde No si los contenedores originales siguen en marcha en otro host: los dos usarían las mismas direcciones.", "Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "¿Iniciar las aplicaciones una vez registradas? Responde No si los contenedores originales siguen en marcha en otro host: los dos usarían las mismas direcciones.",
"Start the converted container:": "Inicie el contenedor convertido:", "Start the converted container:": "Inicie el contenedor convertido:",
"Start the interactive Proxmox package upgrade": "Iniciar la actualización interactiva de paquetes de Proxmox",
"Start the main system upgrade:": "Inicie la actualización principal del sistema:", "Start the main system upgrade:": "Inicie la actualización principal del sistema:",
"Start the stack with Proxmox": "Iniciar la pila con Proxmox", "Start the stack with Proxmox": "Iniciar la pila con Proxmox",
"Start uploading to PBS — sets a recovery passphrase": "Empezar a subir a PBS — establece una frase de contraseña de recuperación", "Start uploading to PBS — sets a recovery passphrase": "Empezar a subir a PBS — establece una frase de contraseña de recuperación",
@@ -6227,6 +6278,7 @@
"The Paperless persistent volumes need at least 8 GB": "Los volúmenes persistentes de Paperless necesitan al menos 8 GB", "The Paperless persistent volumes need at least 8 GB": "Los volúmenes persistentes de Paperless necesitan al menos 8 GB",
"The PostgreSQL volume needs at least 4 GB": "El volumen de PostgreSQL necesita al menos 4 GB", "The PostgreSQL volume needs at least 4 GB": "El volumen de PostgreSQL necesita al menos 4 GB",
"The PostgreSQL volume needs at least 8 GB": "El volumen de PostgreSQL necesita al menos 8 GB", "The PostgreSQL volume needs at least 8 GB": "El volumen de PostgreSQL necesita al menos 8 GB",
"The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "La actualización de Proxmox VE 8 no ha terminado. No se ha iniciado la actualización a Proxmox VE 9.",
"The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Falta el conjunto de claves de archivo Proxmox; La instalación de Ceph no puede continuar de forma segura", "The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Falta el conjunto de claves de archivo Proxmox; La instalación de Ceph no puede continuar de forma segura",
"The Proxmox inventory and the local configurations differ": "El inventario de Proxmox y las configuraciones locales difieren", "The Proxmox inventory and the local configurations differ": "El inventario de Proxmox y las configuraciones locales difieren",
"The ROCm image has no support for the AMD GPU of this host:": "La imagen de ROCm no tiene soporte para la GPU AMD de este host:", "The ROCm image has no support for the AMD GPU of this host:": "La imagen de ROCm no tiene soporte para la GPU AMD de este host:",
@@ -6295,6 +6347,7 @@
"The build could not be checked beforehand; continuing without that check.": "La compilación no se pudo comprobar de antemano; se continúa sin esa comprobación.", "The build could not be checked beforehand; continuing without that check.": "La compilación no se pudo comprobar de antemano; se continúa sin esa comprobación.",
"The cached image does not match the current digest": "La imagen en caché no coincide con el digest actual", "The cached image does not match the current digest": "La imagen en caché no coincide con el digest actual",
"The cached image is damaged; it will be downloaded again.": "La imagen en caché está dañada; se descargará de nuevo.", "The cached image is damaged; it will be downloaded again.": "La imagen en caché está dañada; se descargará de nuevo.",
"The changes need the application installer": "Los cambios requieren el instalador de la aplicación",
"The character device does not exist:": "El dispositivo de carácter no existe:", "The character device does not exist:": "El dispositivo de carácter no existe:",
"The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "El comando pide una contraseña que no se muestra al escribirla. Tras crear los usuarios, la interfaz web pide un nombre de usuario y una contraseña.", "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "El comando pide una contraseña que no se muestra al escribirla. Tras crear los usuarios, la interfaz web pide un nombre de usuario y una contraseña.",
"The command does not name an image": "El comando no nombra ninguna imagen", "The command does not name an image": "El comando no nombra ninguna imagen",
@@ -6413,8 +6466,10 @@
"The host bind source does not exist:": "La fuente de host bind no existe:", "The host bind source does not exist:": "La fuente de host bind no existe:",
"The host bind source is not a regular file or directory:": "La fuente host bind no es un archivo o directorio regular:", "The host bind source is not a regular file or directory:": "La fuente host bind no es un archivo o directorio regular:",
"The host directory changed before it was mounted": "El directorio host cambió antes de montarlo", "The host directory changed before it was mounted": "El directorio host cambió antes de montarlo",
"The host directory does not exist": "El directorio del host no existe",
"The host directory may not be accessible from an unprivileged container.": "Es posible que no se pueda acceder al directorio del host desde un contenedor sin privilegios.", "The host directory may not be accessible from an unprivileged container.": "Es posible que no se pueda acceder al directorio del host desde un contenedor sin privilegios.",
"The host has no IPv4 address on the selected bridge": "El host no tiene dirección IPv4 en el puente seleccionado", "The host has no IPv4 address on the selected bridge": "El host no tiene dirección IPv4 en el puente seleccionado",
"The host loses access to them. If the group includes a network or storage controller the host needs, cancel at the summary.": "El host pierde el acceso a ellos. Si el grupo incluye un controlador de red o de almacenamiento que el host necesita, cancela en el resumen.",
"The host monitor does not see the real host memory": "El monitor host no ve la memoria real del host", "The host monitor does not see the real host memory": "El monitor host no ve la memoria real del host",
"The host monitor does not share this host namespace:": "El monitor host no comparte este espacio de nombres de host:", "The host monitor does not share this host namespace:": "El monitor host no comparte este espacio de nombres de host:",
"The host monitor include is unknown or differs from the saved record": "El include del monitor del host es desconocido o difiere del registro guardado", "The host monitor include is unknown or differs from the saved record": "El include del monitor del host es desconocido o difiere del registro guardado",
@@ -6598,6 +6653,7 @@
"The repair must preserve the image dependencies:": "La reparación debe preservar las dependencias de la imagen:", "The repair must preserve the image dependencies:": "La reparación debe preservar las dependencias de la imagen:",
"The requested VMID block is already in use": "El bloque VMID solicitado ya está en uso", "The requested VMID block is already in use": "El bloque VMID solicitado ya está en uso",
"The requested machine learning GPU profile is not working; it is not replaced by CPU": "El perfil de GPU solicitado para Machine learning no funciona; no se sustituye por CPU", "The requested machine learning GPU profile is not working; it is not replaced by CPU": "El perfil de GPU solicitado para Machine learning no funciona; no se sustituye por CPU",
"The resource edit could not be fully rolled back. Open Manage again to review and import the current Proxmox settings.": "El cambio de recursos no se ha podido revertir por completo. Vuelve al menú de gestión para revisar e incorporar la configuración actual de Proxmox.",
"The restored service did not pass its health check": "El servicio restaurado no superó su comprobación de estado", "The restored service did not pass its health check": "El servicio restaurado no superó su comprobación de estado",
"The restored service stopped; the recovery is not confirmed": "El servicio restaurado se detuvo; la recuperación no se confirma", "The restored service stopped; the recovery is not confirmed": "El servicio restaurado se detuvo; la recuperación no se confirma",
"The reviewed Tandoor stack does not require a privileged LXC.": "La pila de Tandoor revisada no necesita un LXC privilegiado.", "The reviewed Tandoor stack does not require a privileged LXC.": "La pila de Tandoor revisada no necesita un LXC privilegiado.",
@@ -6643,6 +6699,7 @@
"The selected VM": "La máquina virtual seleccionada", "The selected VM": "La máquina virtual seleccionada",
"The selected VM is running.": "La VM seleccionada se está ejecutando.", "The selected VM is running.": "La VM seleccionada se está ejecutando.",
"The selected base folder does not exist and could not be created:": "La carpeta base seleccionada no existe y no se pudo crear:", "The selected base folder does not exist and could not be created:": "La carpeta base seleccionada no existe y no se pudo crear:",
"The selected bridge does not exist": "El puente seleccionado no existe",
"The selected bridge has no IPv4 subnet for the host-monitor firewall": "El bridge seleccionado no tiene subred IPv4 para el cortafuegos del monitor del host", "The selected bridge has no IPv4 subnet for the host-monitor firewall": "El bridge seleccionado no tiene subred IPv4 para el cortafuegos del monitor del host",
"The selected configuration needs to start the LXC during the installation": "La configuración seleccionada debe iniciar el LXC durante la instalación", "The selected configuration needs to start the LXC during the installation": "La configuración seleccionada debe iniciar el LXC durante la instalación",
"The selected container is unprivileged. A privileged container is required for direct device passthrough.": "El contenedor seleccionado no tiene privilegios. Se requiere un contenedor privilegiado para el passthrough directo de dispositivos.", "The selected container is unprivileged. A privileged container is required for direct device passthrough.": "El contenedor seleccionado no tiene privilegios. Se requiere un contenedor privilegiado para el passthrough directo de dispositivos.",
@@ -6665,6 +6722,7 @@
"The service builds its own image; only a published image can be installed": "El servicio construye su propia imagen; solo se puede instalar una imagen publicada", "The service builds its own image; only a published image can be installed": "El servicio construye su propia imagen; solo se puede instalar una imagen publicada",
"The service declares no image:": "El servicio no declara imagen:", "The service declares no image:": "El servicio no declara imagen:",
"The setting has no final value:": "El ajuste no tiene valor final:", "The setting has no final value:": "El ajuste no tiene valor final:",
"The settings could not be fully rolled back. Review the container and its saved modification before continuing.": "No se han podido revertir todos los ajustes. Revisa el contenedor y la modificación guardada antes de continuar.",
"The share already exists in smb.conf:": "El recurso compartido ya existe en smb.conf:", "The share already exists in smb.conf:": "El recurso compartido ya existe en smb.conf:",
"The shared destination is not a directory": "El destino compartido no es un directorio", "The shared destination is not a directory": "El destino compartido no es un directorio",
"The shared directory points to a protected host path": "El directorio compartido apunta a una ruta de host protegida", "The shared directory points to a protected host path": "El directorio compartido apunta a una ruta de host protegida",
@@ -6732,18 +6790,23 @@
"These Proxmox resources were added outside ProxMenux:": "Se añadieron estos recursos en Proxmox fuera de ProxMenux:", "These Proxmox resources were added outside ProxMenux:": "Se añadieron estos recursos en Proxmox fuera de ProxMenux:",
"These VMIDs are not free:": "Estos VMID no están libres:", "These VMIDs are not free:": "Estos VMID no están libres:",
"These are the changes that will be made": "Estos son los cambios que se harán", "These are the changes that will be made": "Estos son los cambios que se harán",
"These changes were made in Proxmox outside ProxMenux:": "Estos cambios se hicieron en Proxmox fuera de ProxMenux:",
"These container mount paths have an extension-like suffix:": "Estas rutas de montaje del contenedor tienen un sufijo parecido a una extensión:", "These container mount paths have an extension-like suffix:": "Estas rutas de montaje del contenedor tienen un sufijo parecido a una extensión:",
"These containers were restored from a backup and this host has no record of their OCI application:": "Estos contenedores se restauraron desde un backup y este host no tiene el registro de su aplicación OCI:", "These containers were restored from a backup and this host has no record of their OCI application:": "Estos contenedores se restauraron desde un backup y este host no tiene el registro de su aplicación OCI:",
"These devices share the IOMMU group of the selected GPU and will be assigned to the VM with it:": "Estos dispositivos comparten el grupo IOMMU de la GPU seleccionada y se asignarán a la VM junto con ella:",
"These host backups run with this job and expect its storage:": "Estos backups del host se ejecutan con este trabajo y esperan su almacenamiento:", "These host backups run with this job and expect its storage:": "Estos backups del host se ejecutan con este trabajo y esperan su almacenamiento:",
"These interface configurations will be removed": "Estas configuraciones de interfaz serán eliminadas", "These interface configurations will be removed": "Estas configuraciones de interfaz serán eliminadas",
"These manual changes cannot be adopted safely:": "Estos cambios manuales no se pueden incorporar con seguridad:", "These manual changes cannot be adopted safely:": "Estos cambios manuales no se pueden incorporar con seguridad:",
"These paths will not be restored live and will be extracted for manual recovery.": "Estas rutas no se restaurarán en vivo y se extraerán para su recuperación manual.", "These paths will not be restored live and will be extracted for manual recovery.": "Estas rutas no se restaurarán en vivo y se extraerán para su recuperación manual.",
"These settings require an application-specific adaptation. The image was not downloaded and the container was not recreated.": "Estos ajustes requieren una adaptación específica de la aplicación. No se ha descargado la imagen ni se ha recreado el contenedor.",
"These values are asked during the installation:": "Estos valores se preguntan durante la instalación:", "These values are asked during the installation:": "Estos valores se preguntan durante la instalación:",
"They are described in the image documentation:": "Están descritos en la documentación de la imagen:", "They are described in the image documentation:": "Están descritos en la documentación de la imagen:",
"They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Se añadirán al oci-compose antes de continuar. No se importarán recursos incompatibles o modificados.", "They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Se añadirán al oci-compose antes de continuar. No se importarán recursos incompatibles o modificados.",
"They will be saved in oci-compose before continuing.": "Se guardarán en el oci-compose antes de continuar.",
"This CIFS share is mounted with restrictive permissions.": "Este recurso compartido CIFS está montado con permisos restrictivos.", "This CIFS share is mounted with restrictive permissions.": "Este recurso compartido CIFS está montado con permisos restrictivos.",
"This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Esta GPU se considera incompatible con el passthrough de GPU a una VM en ProxMenux.", "This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Esta GPU se considera incompatible con el passthrough de GPU a una VM en ProxMenux.",
"This NFS share is fully restricted — even the host root cannot write to it.": "Este recurso compartido NFS está completamente restringido: ni siquiera la raíz del host puede escribir en él.", "This NFS share is fully restricted — even the host root cannot write to it.": "Este recurso compartido NFS está completamente restringido: ni siquiera la raíz del host puede escribir en él.",
"This Proxmox Backup Server has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PBS source will be disabled.": "Este Proxmox Backup Server no tiene una suscripción activa, ¿cambiar al repositorio sin suscripción? Se deshabilitará la fuente Enterprise de PBS, que no es accesible.",
"This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Esta máquina virtual requiere pasos de instalación adicionales; consulte la guía de instalación en:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144", "This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Esta máquina virtual requiere pasos de instalación adicionales; consulte la guía de instalación en:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144",
"This account is for {registry}. Enter an image from that registry.": "Esta cuenta es de {registry}. Escribe una imagen de ese registro.", "This account is for {registry}. Enter an image from that registry.": "Esta cuenta es de {registry}. Escribe una imagen de ese registro.",
"This action will:": "Esta acción:", "This action will:": "Esta acción:",
@@ -6805,10 +6868,13 @@
"This may take several minutes...": "Esto puede tardar varios minutos...", "This may take several minutes...": "Esto puede tardar varios minutos...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Esto significa que Proxmox maneja el ciclo de vida del montaje de forma nativa (no se necesita /etc/fstab manual para almacenamientos de host NFS/CIFS).", "This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Esto significa que Proxmox maneja el ciclo de vida del montaje de forma nativa (no se necesita /etc/fstab manual para almacenamientos de host NFS/CIFS).",
"This means the credentials are incorrect.": "Esto significa que las credenciales son incorrectas.", "This means the credentials are incorrect.": "Esto significa que las credenciales son incorrectas.",
"This menu requires Proxmox Backup Server.": "Este menú requiere Proxmox Backup Server.",
"This might indicate network connectivity issues.": "Esto podría indicar problemas de conectividad de red.", "This might indicate network connectivity issues.": "Esto podría indicar problemas de conectividad de red.",
"This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Este monitor utiliza un LXC privilegiado, comparte procesos y red con Proxmox y deshabilita AppArmor en el CT. Utiliza la dirección IP y el cortafuegos del host. Una imagen comprometida podría afectar al host; no exponga su interfaz de usuario web a Internet.", "This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Este monitor utiliza un LXC privilegiado, comparte procesos y red con Proxmox y deshabilita AppArmor en el CT. Utiliza la dirección IP y el cortafuegos del host. Una imagen comprometida podría afectar al host; no exponga su interfaz de usuario web a Internet.",
"This only removes the schedule. Existing backup archives are NOT deleted.": "Solo se elimina la programación. Los backups ya hechos NO se eliminan.", "This only removes the schedule. Existing backup archives are NOT deleted.": "Solo se elimina la programación. Los backups ya hechos NO se eliminan.",
"This operation may take several minutes and requires internet connectivity.": "Esta operación puede tardar varios minutos y requiere conexión a Internet.", "This operation may take several minutes and requires internet connectivity.": "Esta operación puede tardar varios minutos y requiere conexión a Internet.",
"This operation requires a Proxmox VE host. Use the PBS menu on Proxmox Backup Server.": "Esta operación requiere un host de Proxmox VE. Utiliza el menú de PBS en Proxmox Backup Server.",
"This operation supports PBS 3 on Debian 12 and PBS 4 on Debian 13.": "Esta operación admite PBS 3 en Debian 12 y PBS 4 en Debian 13.",
"This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Este paquete fue instalado por versiones antiguas del instalador de Coral de ProxMenux, que instalaban el controlador del kernel para Coral M.2 en todos los sistemas, incluidos aquellos que solo usaban Coral USB. No es necesario para los dispositivos Coral USB, que únicamente utilizan libedgetpu1-std o libedgetpu1-max.", "This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Este paquete fue instalado por versiones antiguas del instalador de Coral de ProxMenux, que instalaban el controlador del kernel para Coral M.2 en todos los sistemas, incluidos aquellos que solo usaban Coral USB. No es necesario para los dispositivos Coral USB, que únicamente utilizan libedgetpu1-std o libedgetpu1-max.",
"This passphrase is the ONLY way to access encrypted Borg backups.": "Esta frase de contraseña es la ÚNICA forma de acceder a los backups cifrados de Borg.", "This passphrase is the ONLY way to access encrypted Borg backups.": "Esta frase de contraseña es la ÚNICA forma de acceder a los backups cifrados de Borg.",
"This path is already used as a mount point in this container.": "Esta ruta ya se utiliza como punto de montaje en este contenedor.", "This path is already used as a mount point in this container.": "Esta ruta ya se utiliza como punto de montaje en este contenedor.",
@@ -6816,7 +6882,7 @@
"This process changes file ownership inside the container": "Este proceso cambia la propiedad del archivo dentro del contenedor", "This process changes file ownership inside the container": "Este proceso cambia la propiedad del archivo dentro del contenedor",
"This profile only supports directory bind mounts": "Este perfil solo admite bind mounts de directorio", "This profile only supports directory bind mounts": "Este perfil solo admite bind mounts de directorio",
"This profile requires a privileged LXC, which reduces isolation from the host.": "Este perfil requiere un LXC privilegiado, lo que reduce el aislamiento respecto al host.", "This profile requires a privileged LXC, which reduces isolation from the host.": "Este perfil requiere un LXC privilegiado, lo que reduce el aislamiento respecto al host.",
"This release channel is already active.": "Este canal de release ya está activo.", "This release channel is already active.": "Este canal de versiones ya está activo.",
"This removes the 'unprivileged: 1' line from the config": "Esto elimina la línea 'sin privilegios: 1' de la configuración", "This removes the 'unprivileged: 1' line from the config": "Esto elimina la línea 'sin privilegios: 1' de la configuración",
"This removes the storage from Proxmox. The iSCSI target is not affected.": "Esto elimina el almacenamiento de Proxmox. El destino iSCSI no se ve afectado.", "This removes the storage from Proxmox. The iSCSI target is not affected.": "Esto elimina el almacenamiento de Proxmox. El destino iSCSI no se ve afectado.",
"This replaces the currently installed keyfile.": "Esto reemplaza el archivo de claves instalado actualmente.", "This replaces the currently installed keyfile.": "Esto reemplaza el archivo de claves instalado actualmente.",
@@ -6824,6 +6890,7 @@
"This script cannot be executed from the Proxmox web terminal.": "Este script no se puede ejecutar desde la terminal web de Proxmox.", "This script cannot be executed from the Proxmox web terminal.": "Este script no se puede ejecutar desde la terminal web de Proxmox.",
"This script must be run as root.": "Este script debe ejecutarse como root.", "This script must be run as root.": "Este script debe ejecutarse como root.",
"This script must be run on a Proxmox host.": "Este script debe ejecutarse en un host Proxmox.", "This script must be run on a Proxmox host.": "Este script debe ejecutarse en un host Proxmox.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox Backup Server": "Este script aplicará las siguientes optimizaciones y ajustes avanzados a su Proxmox Backup Server",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Este script aplicará las siguientes optimizaciones y ajustes avanzados a su servidor Proxmox VE", "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Este script aplicará las siguientes optimizaciones y ajustes avanzados a su servidor Proxmox VE",
"This script will update your Proxmox VE system with advanced options:": "Este script actualizará su sistema Proxmox VE con opciones avanzadas:", "This script will update your Proxmox VE system with advanced options:": "Este script actualizará su sistema Proxmox VE con opciones avanzadas:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Esta sesión se ejecuta en el terminal del Monitor. Ejecutarlo desde aquí cortaría la conexión a mitad de la instalación y dejaría el cambio en un estado roto.", "This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Esta sesión se ejecuta en el terminal del Monitor. Ejecutarlo desde aquí cortaría la conexión a mitad de la instalación y dejaría el cambio en un estado roto.",
@@ -6955,6 +7022,7 @@
"Unable to identify the root filesystem. Aborting.": "No se pudo identificar el sistema de archivos raíz. Se cancela.", "Unable to identify the root filesystem. Aborting.": "No se pudo identificar el sistema de archivos raíz. Se cancela.",
"Unable to resolve system ZFS pool disks. Aborting.": "No se pudieron resolver los discos del pool ZFS del sistema. Se cancela.", "Unable to resolve system ZFS pool disks. Aborting.": "No se pudieron resolver los discos del pool ZFS del sistema. Se cancela.",
"Unable to resolve system disk topology. Aborting.": "No se pudo resolver la topología de discos del sistema. Se cancela.", "Unable to resolve system disk topology. Aborting.": "No se pudo resolver la topología de discos del sistema. Se cancela.",
"Unapplied changes": "Cambios sin aplicar",
"Understand the security implications of privileged containers": "Comprender las implicaciones de seguridad de los contenedores privilegiados", "Understand the security implications of privileged containers": "Comprender las implicaciones de seguridad de los contenedores privilegiados",
"Unexpected Proxmox inventory; recovery blocked": "Inventario Proxmox inesperado; recuperación bloqueada", "Unexpected Proxmox inventory; recovery blocked": "Inventario Proxmox inesperado; recuperación bloqueada",
"Unexpected formatting directory in the new Valkey volume": "Directorio de formato inesperado en el nuevo volumen Valkey", "Unexpected formatting directory in the new Valkey volume": "Directorio de formato inesperado en el nuevo volumen Valkey",
@@ -7054,6 +7122,7 @@
"Unsupported OCI-LXC AppArmor profile:": "Perfil OCI-LXC sin soporte:", "Unsupported OCI-LXC AppArmor profile:": "Perfil OCI-LXC sin soporte:",
"Unsupported OCI-LXC seccomp profile:": "Perfil sin soporte OCI-LXC seccomp:", "Unsupported OCI-LXC seccomp profile:": "Perfil sin soporte OCI-LXC seccomp:",
"Unsupported Terminal": "Terminal no compatible", "Unsupported Terminal": "Terminal no compatible",
"Unsupported architecture for Figurine:": "Arquitectura no compatible con Figurine:",
"Unsupported architecture:": "Arquitectura sin soporte:", "Unsupported architecture:": "Arquitectura sin soporte:",
"Unsupported backup compression": "Compresión de backup sin soporte", "Unsupported backup compression": "Compresión de backup sin soporte",
"Unsupported credential pattern:": "Patrón credential sin soporte:", "Unsupported credential pattern:": "Patrón credential sin soporte:",
@@ -7069,6 +7138,7 @@
"Unsupported mount type": "Tipo de montaje no compatible", "Unsupported mount type": "Tipo de montaje no compatible",
"Unsupported mount type:": "Tipo de montaje no compatible:", "Unsupported mount type:": "Tipo de montaje no compatible:",
"Unsupported native device type:": "Tipo de dispositivo nativo sin soporte:", "Unsupported native device type:": "Tipo de dispositivo nativo sin soporte:",
"Unsupported network settings": "Ajustes de red no compatibles",
"Unsupported operation": "Operación sin soporte", "Unsupported operation": "Operación sin soporte",
"Unsupported or unknown Proxmox version; no repository changed.": "Versión de Proxmox no compatible o desconocida; no se ha modificado ningún repositorio.", "Unsupported or unknown Proxmox version; no repository changed.": "Versión de Proxmox no compatible o desconocida; no se ha modificado ningún repositorio.",
"Unsupported output format:": "Formato de salida no admitido:", "Unsupported output format:": "Formato de salida no admitido:",
@@ -7094,6 +7164,7 @@
"Update NVIDIA in LXC Containers": "Actualizar NVIDIA en contenedores LXC", "Update NVIDIA in LXC Containers": "Actualizar NVIDIA en contenedores LXC",
"Update OCI": "Actualizar OCI", "Update OCI": "Actualizar OCI",
"Update OCI stack": "Actualizar la pila OCI", "Update OCI stack": "Actualizar la pila OCI",
"Update PBS using configured repositories": "Actualizar PBS con los repositorios configurados",
"Update PVE enterprise repository (Only if using enterprise):": "Actualice el repositorio empresarial PVE (solo si usa Enterprise):", "Update PVE enterprise repository (Only if using enterprise):": "Actualice el repositorio empresarial PVE (solo si usa Enterprise):",
"Update Proxmox VE Appliance Manager": "Actualizar Proxmox VE Appliance Manager", "Update Proxmox VE Appliance Manager": "Actualizar Proxmox VE Appliance Manager",
"Update Proxmox package lists": "Actualizar las listas de paquetes de Proxmox", "Update Proxmox package lists": "Actualizar las listas de paquetes de Proxmox",
@@ -7139,13 +7210,14 @@
"Updating kernel panic configuration...": "Actualizando la configuración de pánico del kernel...", "Updating kernel panic configuration...": "Actualizando la configuración de pánico del kernel...",
"Updating package lists...": "Actualizando listas de paquetes...", "Updating package lists...": "Actualizando listas de paquetes...",
"Updating pve-enterprise.list (if present) to Trixie...": "Actualizando pve-enterprise.list (si está presente) a Trixie...", "Updating pve-enterprise.list (if present) to Trixie...": "Actualizando pve-enterprise.list (si está presente) a Trixie...",
"Updating release channel configuration...": "Actualizando la configuración del canal de release...", "Updating release channel configuration...": "Actualizando la configuración del canal de versiones...",
"Updating the copy of the record...": "Actualizando la copia del registro...", "Updating the copy of the record...": "Actualizando la copia del registro...",
"Upgrade Ceph to compatible version": "Actualice Ceph a una versión compatible", "Upgrade Ceph to compatible version": "Actualice Ceph a una versión compatible",
"Upgrade PVE 8 to PVE 9": "Actualizar PVE 8 a PVE 9", "Upgrade PVE 8 to PVE 9": "Actualizar PVE 8 a PVE 9",
"Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Asistente de actualización: Proxmox VE 8 → 9 (Trixie)", "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Asistente de actualización: Proxmox VE 8 → 9 (Trixie)",
"Upgrade canceled by user": "Actualización cancelada por el usuario", "Upgrade canceled by user": "Actualización cancelada por el usuario",
"Upgrade one node at a time": "Actualice un nodo a la vez", "Upgrade one node at a time": "Actualice un nodo a la vez",
"Upgrade the system, offering the": "Actualizar el sistema, ofreciendo el",
"Upgrade to Proxmox VE 9": "Actualice a Proxmox VE 9", "Upgrade to Proxmox VE 9": "Actualice a Proxmox VE 9",
"Upgrading NVIDIA utils (Arch)...": "Actualizando las utilidades de NVIDIA (Arch)...", "Upgrading NVIDIA utils (Arch)...": "Actualizando las utilidades de NVIDIA (Arch)...",
"Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "¿Cargar una copia cifrada de la clave a PBS para poder recuperarla en un host reinstalado con solo una frase de contraseña?", "Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "¿Cargar una copia cifrada de la clave a PBS para poder recuperarla en un host reinstalado con solo una frase de contraseña?",
@@ -7584,6 +7656,7 @@
"active VF(s)": "FV activas", "active VF(s)": "FV activas",
"active VFs": "FV activas", "active VFs": "FV activas",
"active Virtual Functions. Changing its driver binding would destroy every VF.": "Funciones virtuales activas. Cambiar la vinculación del controlador destruiría todos los VF.", "active Virtual Functions. Changing its driver binding would destroy every VF.": "Funciones virtuales activas. Cambiar la vinculación del controlador destruiría todos los VF.",
"added in Proxmox": "añadida en Proxmox",
"after copy; source tree is incomplete.": "después de la copia; El árbol fuente está incompleto.", "after copy; source tree is incomplete.": "después de la copia; El árbol fuente está incompleto.",
"after update.": "después de la actualización.", "after update.": "después de la actualización.",
"all guests": "todas las VM y CT", "all guests": "todas las VM y CT",
@@ -7903,7 +7976,7 @@
"partition(s). Partition table preserved.": "partición(es). Se conserva la tabla de particiones.", "partition(s). Partition table preserved.": "partición(es). Se conserva la tabla de particiones.",
"pasted Compose file": "archivo Compose pegado", "pasted Compose file": "archivo Compose pegado",
"paths for next boot (/etc/pve, guests, drivers, ...)": "rutas para el próximo arranque (/etc/pve, VM y CT, controladores, ...)", "paths for next boot (/etc/pve, guests, drivers, ...)": "rutas para el próximo arranque (/etc/pve, VM y CT, controladores, ...)",
"pct exec authorization failed": "error en la autorización ejecutiva de PCT", "pct exec authorization failed": "Ha fallado la autorización de pct exec",
"pct push failed. Check log:": "pct push falló. Revise el log:", "pct push failed. Check log:": "pct push falló. Revise el log:",
"pending (reboot required to enumerate full group)": "pendiente (es necesario reiniciar para enumerar el grupo completo)", "pending (reboot required to enumerate full group)": "pendiente (es necesario reiniciar para enumerar el grupo completo)",
"phpMyAdmin is installed with arbitrary server connections enabled: the login page has a Server field where the address of the MySQL or MariaDB server is entered, together with its user and password.": "phpMyAdmin se instala con conexiones de servidor arbitrarias habilitadas: la página de inicio de sesión tiene un campo Server donde se introduce la dirección del servidor MySQL o MariaDB, junto con su usuario y contraseña.", "phpMyAdmin is installed with arbitrary server connections enabled: the login page has a Server field where the address of the MySQL or MariaDB server is entered, together with its user and password.": "phpMyAdmin se instala con conexiones de servidor arbitrarias habilitadas: la página de inicio de sesión tiene un campo Server donde se introduce la dirección del servidor MySQL o MariaDB, junto con su usuario y contraseña.",
@@ -7927,8 +8000,8 @@
"pvesm command not found. This should not happen on Proxmox.": "Comando pvesm no encontrado. Esto no debería suceder en Proxmox.", "pvesm command not found. This should not happen on Proxmox.": "Comando pvesm no encontrado. Esto no debería suceder en Proxmox.",
"pvesm not available.": "pvesm no disponible.", "pvesm not available.": "pvesm no disponible.",
"pvesm not found.": "pvesm no encontrado.", "pvesm not found.": "pvesm no encontrado.",
"pvesm path failed, trying manual detection...": "La ruta pvesm falló, intentando la detección manual...", "pvesm path failed, trying manual detection...": "Ha fallado pvesm path; intentando la detección manual...",
"pvesm status failed": "el estado de pvesm falló", "pvesm status failed": "Ha fallado pvesm status",
"pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad es un gestor de descargas libre y de código abierto escrito en Python, diseñado para ser muy ligero, fácil de ampliar y totalmente gestionable por web.", "pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad es un gestor de descargas libre y de código abierto escrito en Python, diseñado para ser muy ligero, fácil de ampliar y totalmente gestionable por web.",
"pyLoad web interface": "Interfaz web de pyLoad", "pyLoad web interface": "Interfaz web de pyLoad",
"qBittorrent WebUI password (user: admin)": "contraseña de qBittorrent WebUI (usuario: admin)", "qBittorrent WebUI password (user: admin)": "contraseña de qBittorrent WebUI (usuario: admin)",
@@ -7950,6 +8023,7 @@
"remapped users": "usuarios reasignados", "remapped users": "usuarios reasignados",
"remove the (now-empty) directory if possible": "elimine el directorio (ahora vacío) si es posible", "remove the (now-empty) directory if possible": "elimine el directorio (ahora vacío) si es posible",
"removed from Proxmox": "eliminado de Proxmox", "removed from Proxmox": "eliminado de Proxmox",
"removed in Proxmox": "eliminada en Proxmox",
"removed successfully from Proxmox.": "eliminado correctamente de Proxmox.", "removed successfully from Proxmox.": "eliminado correctamente de Proxmox.",
"requires running the official installer, which restarts the Monitor service.": "requiere ejecutar el instalador oficial, que reinicia el servicio del Monitor.", "requires running the official installer, which restarts the Monitor service.": "requiere ejecutar el instalador oficial, que reinicia el servicio del Monitor.",
"requires the package": "requiere el paquete", "requires the package": "requiere el paquete",
@@ -7987,7 +8061,7 @@
"smbclient command is not working properly.": "El comando smbclient no funciona correctamente.", "smbclient command is not working properly.": "El comando smbclient no funciona correctamente.",
"smbclient command not found after installation.": "El comando smbclient no se encuentra después de la instalación.", "smbclient command not found after installation.": "El comando smbclient no se encuentra después de la instalación.",
"sources.list update skipped (no change)": "Actualización de sources.list omitida (sin cambios)", "sources.list update skipped (no change)": "Actualización de sources.list omitida (sin cambios)",
"sources.list updated to Trixie": "fuentes.lista actualizada a Trixie", "sources.list updated to Trixie": "sources.list actualizado a Trixie",
"ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen falló. No se puede crear una nueva clave SSH.", "ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen falló. No se puede crear una nueva clave SSH.",
"stale entry/entries for interfaces no longer present": "entrada(s) obsoleta(s) de interfaces que ya no existen", "stale entry/entries for interfaces no longer present": "entrada(s) obsoleta(s) de interfaces que ya no existen",
"standard performance": "rendimiento estándar", "standard performance": "rendimiento estándar",
@@ -8053,6 +8127,7 @@
"using": "usando", "using": "usando",
"using direct path (not guaranteed to survive reboots).": "utilizando una ruta directa (no se garantiza que sobreviva a los reinicios).", "using direct path (not guaranteed to survive reboots).": "utilizando una ruta directa (no se garantiza que sobreviva a los reinicios).",
"vCPUs:": "vCPU:", "vCPUs:": "vCPU:",
"value changed in Proxmox": "valor cambiado en Proxmox",
"vfio-pci IDs configured": "ID de vfio-pci configurados", "vfio-pci IDs configured": "ID de vfio-pci configurados",
"vfio-pci IDs in /etc/modprobe.d/vfio.conf": "ID de vfio-pci en /etc/modprobe.d/vfio.conf", "vfio-pci IDs in /etc/modprobe.d/vfio.conf": "ID de vfio-pci en /etc/modprobe.d/vfio.conf",
"video": "vídeo", "video": "vídeo",
@@ -8065,6 +8140,7 @@
"was": "era", "was": "era",
"was installed, but the kernel reports:": "se instaló, pero el kernel informa:", "was installed, but the kernel reports:": "se instaló, pero el kernel informa:",
"when finished": "al terminar", "when finished": "al terminar",
"when the server has no subscription (you will be asked)": "cuando el servidor no tiene suscripción (se le preguntará)",
"will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "volverá a vincular la GPU a vfio-pci en el próximo reinicio, rompiendo el controlador que está a punto de instalarse.", "will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "volverá a vincular la GPU a vfio-pci en el próximo reinicio, rompiendo el controlador que está a punto de instalarse.",
"wipefs failed on": "los borrados fallaron", "wipefs failed on": "los borrados fallaron",
"with": "con", "with": "con",
+104 -28
View File
@@ -37,6 +37,7 @@
"/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (chemin vzdump par défaut de Proxmox)", "/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (chemin vzdump par défaut de Proxmox)",
"/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (Proxmox par défaut)", "/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (Proxmox par défaut)",
"1777 = sticky bit + rwx for all. No shared group needed.": "1777 = bit collant + rwx pour tous. Aucun groupe partagé n’est nécessaire.", "1777 = sticky bit + rwx for all. No shared group needed.": "1777 = bit collant + rwx pour tous. Aucun groupe partagé n’est nécessaire.",
"====== PBS UPDATE COMPLETED ======": "====== MISE À JOUR PBS TERMINÉE ======",
"====== PVE UPDATE COMPLETED ======": "====== MISE À JOUR PVE TERMINÉE ======", "====== PVE UPDATE COMPLETED ======": "====== MISE À JOUR PVE TERMINÉE ======",
">> Create a new backup job <<": ">> Créer une nouvelle tâche de sauvegarde <<", ">> Create a new backup job <<": ">> Créer une nouvelle tâche de sauvegarde <<",
">> Save and return <<": ">> Enregistrer et retourner <<", ">> Save and return <<": ">> Enregistrer et retourner <<",
@@ -87,6 +88,7 @@
"A multi-container application is not recreated: its containers are updated together.": "Une application multi-conteneurs n'est pas recréée : ses conteneurs sont mis à jour ensemble.", "A multi-container application is not recreated: its containers are updated together.": "Une application multi-conteneurs n'est pas recréée : ses conteneurs sont mis à jour ensemble.",
"A new ProxMenux version is available:": "Une nouvelle version de ProxMenux est disponible :", "A new ProxMenux version is available:": "Une nouvelle version de ProxMenux est disponible :",
"A new kernel is staged for the next boot:": "Un nouveau noyau est préparé pour le prochain démarrage :", "A new kernel is staged for the next boot:": "Un nouveau noyau est préparé pour le prochain démarrage :",
"A newer kernel is installed. Reboot before the next backup window to use it.": "Un noyau plus récent est installé. Redémarrez avant la prochaine fenêtre de sauvegarde pour l'utiliser.",
"A newer version is available:": "Une version plus récente est disponible :", "A newer version is available:": "Une version plus récente est disponible :",
"A pending operation exists for": "Un opera en attente existe pour", "A pending operation exists for": "Un opera en attente existe pour",
"A pending stack assembly already exists; it is not overwritten": "Une pile en attente existe déjà; elle n'est pas écrasée", "A pending stack assembly already exists; it is not overwritten": "Une pile en attente existe déjà; elle n'est pas écrasée",
@@ -169,6 +171,8 @@
"APT language configuration updated": "Configuration du langage APT mise à jour", "APT language configuration updated": "Configuration du langage APT mise à jour",
"APT language downloads restored": "Téléchargements de langues APT restaurés", "APT language downloads restored": "Téléchargements de langues APT restaurés",
"APT package lists updated": "Listes de packages APT mises à jour", "APT package lists updated": "Listes de packages APT mises à jour",
"APT refresh failed. Check the PBS subscription and repositories in Administration > Repositories. Log:": "L’actualisation APT a échoué. Vérifiez l'abonnement PBS et les référentiels dans Administration > Référentiels. Enregistrer:",
"APT refresh failed. Check the PBS subscription and repositories in the native PBS interface; no source was changed.": "Échec de l'actualisation APT. Vérifiez l'abonnement et les dépôts de PBS dans son interface native ; aucune source n'a été modifiée.",
"APT update failed. Check repository access; NVIDIA installation stopped.": "La mise à jour APT a échoué. Vérifiez l'accès au référentiel ; l'installation de NVIDIA s'est arrêtée.", "APT update failed. Check repository access; NVIDIA installation stopped.": "La mise à jour APT a échoué. Vérifiez l'accès au référentiel ; l'installation de NVIDIA s'est arrêtée.",
"Aborted": "Avorté", "Aborted": "Avorté",
"About to restore": "Sur le point de restaurer", "About to restore": "Sur le point de restaurer",
@@ -297,7 +301,7 @@
"Address of the database server (mysql and pgsql only)": "Adresse du serveur de base de données (mysql et pgsql uniquement)", "Address of the database server (mysql and pgsql only)": "Adresse du serveur de base de données (mysql et pgsql uniquement)",
"Address of the oci-compose file": "Adresse du fichier oci-compose", "Address of the oci-compose file": "Adresse du fichier oci-compose",
"Address to reach Pydio Cells (https://domain or https://IP:8080)": "Adresse pour atteindre Pydio Cells (https://domaine ou https://IP:8080)", "Address to reach Pydio Cells (https://domain or https://IP:8080)": "Adresse pour atteindre Pydio Cells (https://domaine ou https://IP:8080)",
"Address used to reach wallabag (http://IP or https://wallabag.example.com)": "Adresse utilisée pour atteindre wallabag (http://IP ou https://wallabag.exemple.com)", "Address used to reach wallabag (http://IP or https://wallabag.example.com)": "Adresse d’accès à wallabag (http://IP ou https://wallabag.example.com)",
"Addresses to update: ipv4, ipv6 or both (uses an external service)": "Adresses à mettre à jour: ipv4, ipv6 ou les deux (utilise un service externe)", "Addresses to update: ipv4, ipv6 or both (uses an external service)": "Adresses à mettre à jour: ipv4, ipv6 ou les deux (utilise un service externe)",
"Adguardhome Sync web interface": "Interface web Adguardhome Sync", "Adguardhome Sync web interface": "Interface web Adguardhome Sync",
"Adguardhome-sync is a tool to synchronize AdGuardHome config to replica instances.": "Adguardhome-sync est un outil pour synchroniser la configuration AdGuardHome pour reproduire des instances.", "Adguardhome-sync is a tool to synchronize AdGuardHome config to replica instances.": "Adguardhome-sync est un outil pour synchroniser la configuration AdGuardHome pour reproduire des instances.",
@@ -318,7 +322,7 @@
"After confirming, you will be asked to choose the NVIDIA driver version to install.": "Après confirmation, il vous sera demandé de choisir la version du pilote NVIDIA à installer.", "After confirming, you will be asked to choose the NVIDIA driver version to install.": "Après confirmation, il vous sera demandé de choisir la version du pilote NVIDIA à installer.",
"After detailed analysis, no changes are needed.": "Après une analyse détaillée, aucune modification n’est nécessaire.", "After detailed analysis, no changes are needed.": "Après une analyse détaillée, aucune modification n’est nécessaire.",
"After detailed analysis, no cleanup is needed.": "Après une analyse détaillée, aucun nettoyage n’est nécessaire.", "After detailed analysis, no cleanup is needed.": "Après une analyse détaillée, aucun nettoyage n’est nécessaire.",
"After logging in, run: ip a to obtain the IP address.\nThen, enter that IP address in your web browser like this:\n http://IP_ADDRESS\n\nThis will open the Umbral OS dashboard.": "Après vous être connecté, exécutez : ip a pour obtenir l'adresse IP.\nEnsuite, saisissez cette adresse IP dans votre navigateur Web comme ceci :\n http://ADRESSE_IP\n\nCela ouvrira le tableau de bord Umbral OS.", "After logging in, run: ip a to obtain the IP address.\nThen, enter that IP address in your web browser like this:\n http://IP_ADDRESS\n\nThis will open the Umbral OS dashboard.": "Après la connexion, exécutez ip a pour obtenir l’adresse IP.\nSaisissez ensuite cette adresse dans votre navigateur sous cette forme :\n http://IP_ADDRESS\n\nLe tableau de bord Umbral OS s’ouvrira.",
"After pasting on the server, ensure the file is chmod 600 and owned by": "Après avoir collé sur le serveur, assurez-vous que le fichier est chmod 600 et appartient à", "After pasting on the server, ensure the file is chmod 600 and owned by": "Après avoir collé sur le serveur, assurez-vous que le fichier est chmod 600 et appartient à",
"After reboot the system will be fully accessible (SSH, web UI, login), but the following components will be reinstalled in BACKGROUND — until they finish, commands like nvidia-smi may not yet be available:": "Après le redémarrage, le système sera entièrement accessible (SSH, interface utilisateur Web, connexion), mais les composants suivants seront réinstallés en ARRIÈRE-PLAN — jusqu'à ce qu'ils soient terminés, des commandes comme nvidia-smi peuvent ne pas encore être disponibles :", "After reboot the system will be fully accessible (SSH, web UI, login), but the following components will be reinstalled in BACKGROUND — until they finish, commands like nvidia-smi may not yet be available:": "Après le redémarrage, le système sera entièrement accessible (SSH, interface utilisateur Web, connexion), mais les composants suivants seront réinstallés en ARRIÈRE-PLAN — jusqu'à ce qu'ils soient terminés, des commandes comme nvidia-smi peuvent ne pas encore être disponibles :",
"After reboot, these components will reinstall in background:": "Après le redémarrage, ces composants seront réinstallés en arrière-plan :", "After reboot, these components will reinstall in background:": "Après le redémarrage, ces composants seront réinstallés en arrière-plan :",
@@ -330,7 +334,7 @@
"After this LXC → VM switch, reboot the host so the new binding state is applied cleanly.": "Après ce commutateur LXC → VM, redémarrez l'hôte afin que le nouvel état de liaison soit appliqué proprement.", "After this LXC → VM switch, reboot the host so the new binding state is applied cleanly.": "Après ce commutateur LXC → VM, redémarrez l'hôte afin que le nouvel état de liaison soit appliqué proprement.",
"Airsonic Advanced web interface": "Interface web Airsonic Advanced", "Airsonic Advanced web interface": "Interface web Airsonic Advanced",
"Airsonic-advanced is a free, web-based media streamer, providing ubiquitious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.": "Airsonic-Advanced est un flux multimédia gratuit, basé sur le web, offrant un accès ubiquitious à votre musique. Utilisez-le pour partager votre musique avec des amis, ou pour écouter votre propre musique au travail. Vous pouvez diffuser simultanément vers plusieurs joueurs, par exemple vers un joueur dans votre cuisine et un autre dans votre salon.", "Airsonic-advanced is a free, web-based media streamer, providing ubiquitious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.": "Airsonic-Advanced est un flux multimédia gratuit, basé sur le web, offrant un accès ubiquitious à votre musique. Utilisez-le pour partager votre musique avec des amis, ou pour écouter votre propre musique au travail. Vous pouvez diffuser simultanément vers plusieurs joueurs, par exemple vers un joueur dans votre cuisine et un autre dans votre salon.",
"Aliases added to .bashrc": "Alias ​​ajoutés à .bashrc", "Aliases added to .bashrc": "Alias ajoutés à .bashrc",
"Alist Sync web interface": "Alist Interface web Sync", "Alist Sync web interface": "Alist Interface web Sync",
"All": "Tous", "All": "Tous",
"All Available Scripts": "Tous les scripts disponibles", "All Available Scripts": "Tous les scripts disponibles",
@@ -425,6 +429,7 @@
"Apply": "Appliquer", "Apply": "Appliquer",
"Apply AMD CPU fixes": "appliquer les correctifs du processeur AMD", "Apply AMD CPU fixes": "appliquer les correctifs du processeur AMD",
"Apply Available Updates": "Appliquer les mises à jour disponibles", "Apply Available Updates": "Appliquer les mises à jour disponibles",
"Apply PBS package upgrade": "Appliquer la mise à niveau des paquets PBS",
"Apply and restart services:": "Appliquer et redémarrer les services :", "Apply and restart services:": "Appliquer et redémarrer les services :",
"Apply available updates": "appliquer les mises à jour disponibles", "Apply available updates": "appliquer les mises à jour disponibles",
"Apply boot/initramfs changes": "Appliquer les modifications de démarrage/initramfs", "Apply boot/initramfs changes": "Appliquer les modifications de démarrage/initramfs",
@@ -438,12 +443,15 @@
"Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "appliquer les options du modèle de catalogue actuel ?De nouveaux chemins et paramètres requis peuvent être demandés. Passez en revue la configuration résultante avant de recréer le CT.", "Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "appliquer les options du modèle de catalogue actuel ?De nouveaux chemins et paramètres requis peuvent être demandés. Passez en revue la configuration résultante avant de recréer le CT.",
"Apply read+write access for 'others' on the host directory?": "Appliquer un accès en lecture et en écriture aux « autres » sur le répertoire hôte ?", "Apply read+write access for 'others' on the host directory?": "Appliquer un accès en lecture et en écriture aux « autres » sur le répertoire hôte ?",
"Apply the options from the current catalog template? Your data and configuration are kept.": "Appliquer les options du modèle de catalogue actuel? Vos données et configurations sont conservées.", "Apply the options from the current catalog template? Your data and configuration are kept.": "Appliquer les options du modèle de catalogue actuel? Vos données et configurations sont conservées.",
"Apply the reviewed package upgrade now? Services may restart and packages listed for removal will be removed.": "Appliquer maintenant la mise à niveau des paquets examinée ? Des services peuvent redémarrer et les paquets indiqués pour suppression seront supprimés.",
"Apply these changes?": "Appliquer ces modifications ?", "Apply these changes?": "Appliquer ces modifications ?",
"Applying AMD-specific fixes...": "Application de correctifs spécifiques à AMD...", "Applying AMD-specific fixes...": "Application de correctifs spécifiques à AMD...",
"Applying CPU and RAM changes without rebuilding the container...": "Application des modifications de CPU et de RAM sans recréer le conteneur...",
"Applying Changes": "Application des modifications", "Applying Changes": "Application des modifications",
"Applying Controller/NVMe passthrough to VM": "Application du relais Controller/NVMe à la VM", "Applying Controller/NVMe passthrough to VM": "Application du relais Controller/NVMe à la VM",
"Applying balanced memory optimization settings...": "Application des paramètres d'optimisation de la mémoire équilibrée...", "Applying balanced memory optimization settings...": "Application des paramètres d'optimisation de la mémoire équilibrée...",
"Applying changes safely...": "Appliquer les modifications en toute sécurité...", "Applying changes safely...": "Appliquer les modifications en toute sécurité...",
"Applying container settings without rebuilding the container...": "Application des paramètres sans recréer le conteneur...",
"Applying custom restore": "Application d'une restauration personnalisée", "Applying custom restore": "Application d'une restauration personnalisée",
"Applying default VM configuration": "Application de la configuration de VM par défaut", "Applying default VM configuration": "Application de la configuration de VM par défaut",
"Applying host permissions for unprivileged LXC bind-mounts...": "Application des autorisations d'hôte pour les montages de liaison LXC non privilégiés...", "Applying host permissions for unprivileged LXC bind-mounts...": "Application des autorisations d'hôte pour les montages de liaison LXC non privilégiés...",
@@ -535,6 +543,7 @@
"Available Borg targets:": "Cibles Borg disponibles :", "Available Borg targets:": "Cibles Borg disponibles :",
"Available Disks on Host": "Disques disponibles sur l'hôte", "Available Disks on Host": "Disques disponibles sur l'hôte",
"Available ISO Images": "Images ISO disponibles", "Available ISO Images": "Images ISO disponibles",
"Available PBS Version": "version PBS disponible",
"Available PBS repositories:": "Dépôts PBS disponibles :", "Available PBS repositories:": "Dépôts PBS disponibles :",
"Available PVE Version": "Version PVE disponible", "Available PVE Version": "Version PVE disponible",
"Available Shares:": "Actions disponibles :", "Available Shares:": "Actions disponibles :",
@@ -746,6 +755,7 @@
"Cannot verify the reused disk:": "Impossible de vérifier le disque réutilisé :", "Cannot verify the reused disk:": "Impossible de vérifier le disque réutilisé :",
"Cannot write the image archive to {storage}. Check storage access and mount status.": "Impossible d'écrire l'archive d'images sur {storage}. Vérifiez l'accès au stockage et l'état du montage.", "Cannot write the image archive to {storage}. Check storage access and mount status.": "Impossible d'écrire l'archive d'images sur {storage}. Vérifiez l'accès au stockage et l'état du montage.",
"Capabilities cannot be kept and all dropped at the same time": "Les capacités ne peuvent être conservées et toutes abandonnées en même temps", "Capabilities cannot be kept and all dropped at the same time": "Les capacités ne peuvent être conservées et toutes abandonnées en même temps",
"Catalog options": "Options du catalogue",
"Category": "Catégorie", "Category": "Catégorie",
"Caution: Maximum mode generates more heat.": "Attention : le mode maximum génère plus de chaleur.", "Caution: Maximum mode generates more heat.": "Attention : le mode maximum génère plus de chaleur.",
"Ceph check skipped by user flag (--ignore-ceph-check)": "Vérification Ceph ignorée par l'indicateur utilisateur (--ignore-ceph-check)", "Ceph check skipped by user flag (--ignore-ceph-check)": "Vérification Ceph ignorée par l'indicateur utilisateur (--ignore-ceph-check)",
@@ -880,6 +890,7 @@
"Choose a schedule (systemd calendar-event syntax):": "Choisissez un calendrier (syntaxe d'événement de calendrier systemd) :", "Choose a schedule (systemd calendar-event syntax):": "Choisissez un calendrier (syntaxe d'événement de calendrier systemd) :",
"Choose a script to execute:": "Choisissez un script à exécuter :", "Choose a script to execute:": "Choisissez un script à exécuter :",
"Choose a server:": "Choisissez un serveur :", "Choose a server:": "Choisissez un serveur :",
"Choose a setting. Changes are applied only after the final review.": "Choisissez un réglage. Les modifications ne sont appliquées qu’après la vérification finale.",
"Choose a share to mount:": "Choisissez un partage à monter :", "Choose a share to mount:": "Choisissez un partage à monter :",
"Choose a specific supported GPU or USB node": "Choisissez un GPU ou un nœud USB pris en charge spécifique", "Choose a specific supported GPU or USB node": "Choisissez un GPU ou un nœud USB pris en charge spécifique",
"Choose action for this controller/NVMe:": "Choisissez une action pour ce contrôleur/NVMe :", "Choose action for this controller/NVMe:": "Choisissez une action pour ce contrôleur/NVMe :",
@@ -960,6 +971,7 @@
"Code-server is VS Code running on a remote server, accessible through the browser.": "Code-serveur est VS Code fonctionnant sur un serveur distant, accessible par le navigateur.", "Code-server is VS Code running on a remote server, accessible through the browser.": "Code-serveur est VS Code fonctionnant sur un serveur distant, accessible par le navigateur.",
"CodeProject.AI Server": "CodeProject. AI Server", "CodeProject.AI Server": "CodeProject. AI Server",
"Command": "Commande", "Command": "Commande",
"Command failed": "Échec de la commande",
"Command override for an unknown service:": "Dépassement de commande pour un service inconnu :", "Command override for an unknown service:": "Dépassement de commande pour un service inconnu :",
"Commenting any residual Bookworm lines in *.list...": "Commenter les lignes résiduelles de Bookworm dans *.list...", "Commenting any residual Bookworm lines in *.list...": "Commenter les lignes résiduelles de Bookworm dans *.list...",
"Commenting legacy PVE 8 repository .list files (if any)...": "Commentaire des anciens fichiers .list du référentiel PVE 8 (le cas échéant)...", "Commenting legacy PVE 8 repository .list files (if any)...": "Commentaire des anciens fichiers .list du référentiel PVE 8 (le cas échéant)...",
@@ -1516,6 +1528,7 @@
"Current NVIDIA inventory resolved: a refresh is required": "L'inventaire actuel de NVIDIA résolu: un rafraîchissement est required", "Current NVIDIA inventory resolved: a refresh is required": "L'inventaire actuel de NVIDIA résolu: un rafraîchissement est required",
"Current NVIDIA inventory resolved: no refresh is required": "L'inventaire actuel de NVIDIA résolu: aucun rafraîchissement n'est required", "Current NVIDIA inventory resolved: no refresh is required": "L'inventaire actuel de NVIDIA résolu: aucun rafraîchissement n'est required",
"Current Network Configuration": "Configuration réseau actuelle", "Current Network Configuration": "Configuration réseau actuelle",
"Current PBS Version": "version actuelle de PBS",
"Current PVE Version": "Version PVE actuelle", "Current PVE Version": "Version PVE actuelle",
"Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Les scripts hôtes ProxMenux actuels enregistrent les partages distants en tant que stockages Proxmox à l'aide de pvesm.", "Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Les scripts hôtes ProxMenux actuels enregistrent les partages distants en tant que stockages Proxmox à l'aide de pvesm.",
"Current ProxMenux version:": "Version actuelle de ProxMenux :", "Current ProxMenux version:": "Version actuelle de ProxMenux :",
@@ -1630,8 +1643,8 @@
"Default Proxmox logo applied": "Logo Proxmox par défaut appliqué", "Default Proxmox logo applied": "Logo Proxmox par défaut appliqué",
"Default is none (video-only passthrough). Use SPACE to toggle selections.": "La valeur par défaut est aucune (passthrough vidéo uniquement). Utilisez ESPACE pour basculer les sélections.", "Default is none (video-only passthrough). Use SPACE to toggle selections.": "La valeur par défaut est aucune (passthrough vidéo uniquement). Utilisez ESPACE pour basculer les sélections.",
"Default journald configuration restored": "Configuration journald par défaut restaurée", "Default journald configuration restored": "Configuration journald par défaut restaurée",
"Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "L'emplacement par défaut est /mnt/<nom>. Le partage sera monté ici sur l'hôte avec des autorisations ouvertes afin qu'un LXC non privilégié puisse y effectuer un montage en liaison et y écrire. Pour l'accès LXC, montez en liaison ce chemin avec le gestionnaire de montage LXC.", "Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "L’emplacement par défaut est /mnt/<name>. Le partage sera monté ici sur l’hôte avec des permissions ouvertes, afin qu’un LXC non privilégié puisse le monter par liaison et y écrire. Utilisez LXC Mount Manager pour créer ce montage dans le LXC.",
"Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "L'emplacement par défaut est /mnt/<nom>. Le partage sera monté ici sur l'hôte. Utilisez ce chemin dans /etc/fstab. Pour l'accès LXC, montez en liaison ce chemin avec le gestionnaire de montage LXC.", "Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "L’emplacement par défaut est /mnt/<name>. Le partage sera monté ici sur l’hôte. Utilisez ce chemin dans /etc/fstab. Pour y accéder depuis un LXC, créez un montage lié de ce chemin avec LXC Mount Manager.",
"Default login": "Connexion par défaut", "Default login": "Connexion par défaut",
"Default options": "Options par défaut", "Default options": "Options par défaut",
"Default options read/write": "Options par défaut lecture/écriture", "Default options read/write": "Options par défaut lecture/écriture",
@@ -1681,6 +1694,7 @@
"Detected Coral hardware:": "Matériel Coral détecté :", "Detected Coral hardware:": "Matériel Coral détecté :",
"Detected GPU(s):": "GPU(s) détecté(s) :", "Detected GPU(s):": "GPU(s) détecté(s) :",
"Detected Intel CPU": "Processeur Intel détecté", "Detected Intel CPU": "Processeur Intel détecté",
"Detected Proxmox Backup Server": "Proxmox Backup Server détecté",
"Detected Proxmox VE": "Proxmox VE détecté", "Detected Proxmox VE": "Proxmox VE détecté",
"Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Proxmox VE 8.x détecté - Installation de Ceph Squid pour Debian", "Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Proxmox VE 8.x détecté - Installation de Ceph Squid pour Debian",
"Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Proxmox VE 9.x détecté - Installation de Ceph Squid pour Debian Trixie", "Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Proxmox VE 9.x détecté - Installation de Ceph Squid pour Debian Trixie",
@@ -1762,6 +1776,9 @@
"Disabling and stopping rpcbind service and socket...": "Désactivation et arrêt du service et du socket rpcbind...", "Disabling and stopping rpcbind service and socket...": "Désactivation et arrêt du service et du socket rpcbind...",
"Disabling non-free firmware warnings...": "Désactivation des avertissements du firmware non libre...", "Disabling non-free firmware warnings...": "Désactivation des avertissements du firmware non libre...",
"Disabling portmapper/rpcbind for security...": "Désactivation de portmapper/rpcbind pour la sécurité...", "Disabling portmapper/rpcbind for security...": "Désactivation de portmapper/rpcbind pour la sécurité...",
"Discard changes": "Abandonner les modifications",
"Discard unapplied changes?": "Abandonner les modifications non appliquées ?",
"Discover NFS exports on a server": "Découvrir les exports NFS d’un serveur",
"Discover targets on portal:": "Découvrez les cibles sur le portail :", "Discover targets on portal:": "Découvrez les cibles sur le portail :",
"Discovering iSCSI targets...": "Découverte des cibles iSCSI...", "Discovering iSCSI targets...": "Découverte des cibles iSCSI...",
"Discovery successful": "Découverte réussie", "Discovery successful": "Découverte réussie",
@@ -1841,6 +1858,7 @@
"Do you want to proceed with the system update?": "Voulez-vous procéder à la mise à jour du système ?", "Do you want to proceed with the system update?": "Voulez-vous procéder à la mise à jour du système ?",
"Do you want to proceed?": "Voulez-vous continuer ?", "Do you want to proceed?": "Voulez-vous continuer ?",
"Do you want to reboot now?": "Voulez-vous redémarrer maintenant ?", "Do you want to reboot now?": "Voulez-vous redémarrer maintenant ?",
"Do you want to remove the Proxmox subscription banner from the Proxmox Backup Server web interface?": "Voulez-vous supprimer la bannière d'abonnement Proxmox de l'interface web de Proxmox Backup Server ?",
"Do you want to remove the stale entry from vfio.conf and continue?": "Voulez-vous supprimer l'entrée obsolète de vfio.conf et continuer ?", "Do you want to remove the stale entry from vfio.conf and continue?": "Voulez-vous supprimer l'entrée obsolète de vfio.conf et continuer ?",
"Do you want to remove the stale entry from vfio.conf?": "Voulez-vous supprimer l'entrée obsolète de vfio.conf ?", "Do you want to remove the stale entry from vfio.conf?": "Voulez-vous supprimer l'entrée obsolète de vfio.conf ?",
"Do you want to restart the network service now to apply changes?": "Voulez-vous redémarrer le service réseau maintenant pour appliquer les modifications ?", "Do you want to restart the network service now to apply changes?": "Voulez-vous redémarrer le service réseau maintenant pour appliquer les modifications ?",
@@ -1903,7 +1921,7 @@
"Dry run completed; the container and the mounts were not changed.": "Le parcours à sec est terminé; le conteneur et les supports n'ont pas été changés.", "Dry run completed; the container and the mounts were not changed.": "Le parcours à sec est terminé; le conteneur et les supports n'ont pas été changés.",
"DuckDNS subdomain without .duckdns.org (comma separated for several)": "DuckDNS sous-domaine sans .duckdns.org (comma séparé pour plusieurs)", "DuckDNS subdomain without .duckdns.org (comma separated for several)": "DuckDNS sous-domaine sans .duckdns.org (comma séparé pour plusieurs)",
"DuckDNS token from your account at duckdns.org": "Jeton DuckDNS de votre compte sur duckdns.org", "DuckDNS token from your account at duckdns.org": "Jeton DuckDNS de votre compte sur duckdns.org",
"DuckDNS updates the subdomain every 5 minutes. Without UPDATE_IP, DuckDNS itself detects the public IPv4 address of the request.": "DuckDNS met à jour le sous-domaine toutes les 5 minutes. Sans mise à jour, DuckDNS détecte lui-même l'adresse IPv4 publique de la requête.", "DuckDNS updates the subdomain every 5 minutes. Without UPDATE_IP, DuckDNS itself detects the public IPv4 address of the request.": "DuckDNS met à jour le sous-domaine toutes les 5 minutes. Sans UPDATE_IP, DuckDNS détecte lui-même l’adresse IPv4 publique de la requête.",
"DuckStation is a PS1 Emulator aiming for the best accuracy and game support.": "DuckStation est un émulateur PS1 visant le meilleur accuracy et le meilleur support de jeu.", "DuckStation is a PS1 Emulator aiming for the best accuracy and game support.": "DuckStation est un émulateur PS1 visant le meilleur accuracy et le meilleur support de jeu.",
"Duckdns is a free service which will point a DNS (sub domains of duckdns.org) to an IP of your choice. The service is completely free, and doesn't require reactivation or forum posts to maintain its existence.": "Duckdns est un service gratuit qui dirigera un DNS (sous-domaines de duckdns.org) vers une IP de votre choix. Le service est entièrement gratuit, et ne require réactivation ou forum posts pour maintenir son existence.", "Duckdns is a free service which will point a DNS (sub domains of duckdns.org) to an IP of your choice. The service is completely free, and doesn't require reactivation or forum posts to maintain its existence.": "Duckdns est un service gratuit qui dirigera un DNS (sous-domaines de duckdns.org) vers une IP de votre choix. Le service est entièrement gratuit, et ne require réactivation ou forum posts pour maintenir son existence.",
"Dumping AMD GPU ROM BIOS via sysfs...": "Vidage du BIOS ROM GPU AMD via sysfs...", "Dumping AMD GPU ROM BIOS via sysfs...": "Vidage du BIOS ROM GPU AMD via sysfs...",
@@ -2162,11 +2180,11 @@
"Example: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak": "Exemple : cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak", "Example: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak": "Exemple : cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak",
"Example: daily or Mon..Fri 03:00": "Exemple : tous les jours ou du lundi au vendredi à 03h00", "Example: daily or Mon..Fri 03:00": "Exemple : tous les jours ou du lundi au vendredi à 03h00",
"Example: mkdir -p /tmp/lxc_convert_114": "Exemple : mkdir -p /tmp/lxc_convert_114", "Example: mkdir -p /tmp/lxc_convert_114": "Exemple : mkdir -p /tmp/lxc_convert_114",
"Example: pct config 110 | grep unprivileged": "Exemple : configuration PCT 110 | grep sans privilège", "Example: pct config 110 | grep unprivileged": "Exemple : pct config 110 | grep unprivileged",
"Example: pct stop 110": "Exemple : PCT stop 110", "Example: pct stop 110": "Exemple : pct stop 110",
"Example: pct stop 114": "Exemple : PCT arrêt 114", "Example: pct stop 114": "Exemple : pct stop 114",
"Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Exemple : pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup", "Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Exemple : pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup",
"Example: pvesm path local-lvm:vm-114-disk-0": "Exemple : chemin pvesm local-lvm:vm-114-disk-0", "Example: pvesm path local-lvm:vm-114-disk-0": "Exemple : pvesm path local-lvm:vm-114-disk-0",
"Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Exemple : sed -i '/^unprivileged : 1/d' /etc/pve/lxc/110.conf", "Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Exemple : sed -i '/^unprivileged : 1/d' /etc/pve/lxc/110.conf",
"Examples:": "Exemples :", "Examples:": "Exemples :",
"Execute destructive rollback?": "Exécuter une restauration destructrice ?", "Execute destructive rollback?": "Exécuter une restauration destructrice ?",
@@ -2266,6 +2284,7 @@
"Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Échec du clonage du référentiel log2ram. Vérifiez /tmp/log2ram_install.log", "Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Échec du clonage du référentiel log2ram. Vérifiez /tmp/log2ram_install.log",
"Failed to configure EFI disk": "Échec de la configuration du disque EFI", "Failed to configure EFI disk": "Échec de la configuration du disque EFI",
"Failed to configure IOMMU automatically.": "Échec de la configuration automatique d'IOMMU.", "Failed to configure IOMMU automatically.": "Échec de la configuration automatique d'IOMMU.",
"Failed to configure Proxmox Backup Server repositories": "Échec de la configuration des dépôts de Proxmox Backup Server",
"Failed to configure Proxmox repositories": "Échec de la configuration des référentiels Proxmox", "Failed to configure Proxmox repositories": "Échec de la configuration des référentiels Proxmox",
"Failed to configure TPM device in VM": "Échec de la configuration du périphérique TPM dans la VM", "Failed to configure TPM device in VM": "Échec de la configuration du périphérique TPM dans la VM",
"Failed to configure repositories.": "Échec de la configuration des référentiels.", "Failed to configure repositories.": "Échec de la configuration des référentiels.",
@@ -2477,7 +2496,7 @@
"For a consistent export, the VM should be stopped.": "Pour une exportation cohérente, la VM doit être arrêtée.", "For a consistent export, the VM should be stopped.": "Pour une exportation cohérente, la VM doit être arrêtée.",
"For any question, issue or bug report, please use the official repository": "Pour toute question, problème ou rapport de bug, veuillez utiliser le référentiel officiel", "For any question, issue or bug report, please use the official repository": "Pour toute question, problème ou rapport de bug, veuillez utiliser le référentiel officiel",
"For dedicated GPUs, FLR is required by this policy to reduce VM": "Pour les GPU dédiés, FLR est requis par cette stratégie pour réduire les VM", "For dedicated GPUs, FLR is required by this policy to reduce VM": "Pour les GPU dédiés, FLR est requis par cette stratégie pour réduire les VM",
"For example: pct enter 101": "Par exemple : PCT, entrez 101", "For example: pct enter 101": "Par exemple : pct enter 101",
"For guest shares add: --options guest": "Pour les partages invités, ajoutez : --options invité", "For guest shares add: --options guest": "Pour les partages invités, ajoutez : --options invité",
"For security reasons, the system cannot format it.": "Pour des raisons de sécurité, le système ne peut pas le formater.", "For security reasons, the system cannot format it.": "Pour des raisons de sécurité, le système ne peut pas le formater.",
"For unprivileged containers, use instead:": "Pour les conteneurs non privilégiés, utilisez plutôt :", "For unprivileged containers, use instead:": "Pour les conteneurs non privilégiés, utilisez plutôt :",
@@ -2802,7 +2821,7 @@
"How do you want to add it?": "Comment veux-tu l’ajouter ?", "How do you want to add it?": "Comment veux-tu l’ajouter ?",
"How do you want to authenticate this backup target?": "Comment souhaitez-vous authentifier cette cible de sauvegarde ?", "How do you want to authenticate this backup target?": "Comment souhaitez-vous authentifier cette cible de sauvegarde ?",
"How do you want to select the": "Comment voulez-vous sélectionner le", "How do you want to select the": "Comment voulez-vous sélectionner le",
"How do you want to select the HOST folder to mount?": "Comment souhaitez-vous sélectionner le dossier HOST à ​​monter ?", "How do you want to select the HOST folder to mount?": "Comment souhaitez-vous sélectionner le dossier HOST à monter ?",
"How do you want to select the NFS server?": "Comment voulez-vous sélectionner le serveur NFS ?", "How do you want to select the NFS server?": "Comment voulez-vous sélectionner le serveur NFS ?",
"How do you want to select the Samba server?": "Comment voulez-vous sélectionner le serveur Samba ?", "How do you want to select the Samba server?": "Comment voulez-vous sélectionner le serveur Samba ?",
"How do you want to select the folder to export?": "Comment voulez-vous sélectionner le dossier à exporter ?", "How do you want to select the folder to export?": "Comment voulez-vous sélectionner le dossier à exporter ?",
@@ -2889,8 +2908,8 @@
"If notifications are enabled (Telegram/Discord/ntfy/...), you will receive a \"Host restore finished\" message when all background tasks complete.": "Si les notifications sont activées (Telegram/Discord/ntfy/...), vous recevrez un message « Restauration de l'hôte terminée » lorsque toutes les tâches en arrière-plan seront terminées.", "If notifications are enabled (Telegram/Discord/ntfy/...), you will receive a \"Host restore finished\" message when all background tasks complete.": "Si les notifications sont activées (Telegram/Discord/ntfy/...), vous recevrez un message « Restauration de l'hôte terminée » lorsque toutes les tâches en arrière-plan seront terminées.",
"If passthrough fails on Windows: install RadeonResetBugFix.": "Si le relais échoue sous Windows : installez RadeonResetBugFix.", "If passthrough fails on Windows: install RadeonResetBugFix.": "Si le relais échoue sous Windows : installez RadeonResetBugFix.",
"If path not accessible (ZFS/BTRFS):": "Si chemin non accessible (ZFS/BTRFS) :", "If path not accessible (ZFS/BTRFS):": "Si chemin non accessible (ZFS/BTRFS) :",
"If pvesm path returned a DEVICE (LVM):": "Si le chemin pvesm a renvoyé un DEVICE (LVM) :", "If pvesm path returned a DEVICE (LVM):": "Si pvesm path a renvoyé un PÉRIPHÉRIQUE (LVM) :",
"If pvesm path returned a DIRECTORY (ZFS/BTRFS):": "Si le chemin pvesm renvoie un RÉPERTOIRE (ZFS/BTRFS) :", "If pvesm path returned a DIRECTORY (ZFS/BTRFS):": "Si pvesm path a renvoyé un RÉPERTOIRE (ZFS/BTRFS) :",
"If repositories error:": "Si erreur de référentiels :", "If repositories error:": "Si erreur de référentiels :",
"If the container had already been changed, its previous native backup is restored. Shared host directories are not reverted. The disks of the failed attempt are removed when the cleanup succeeds.": "Si le conteneur a déjà été modifié, sa sauvegarde native précédente est restaurée. Les répertoires hôtes partagés ne sont pas rétablis. Les disques de la tentative ayant échoué sont supprimés une fois le nettoyage réussi.", "If the container had already been changed, its previous native backup is restored. Shared host directories are not reverted. The disks of the failed attempt are removed when the cleanup succeeds.": "Si le conteneur a déjà été modifié, sa sauvegarde native précédente est restaurée. Les répertoires hôtes partagés ne sont pas rétablis. Les disques de la tentative ayant échoué sont supprimés une fois le nettoyage réussi.",
"If the host freezes, remove hostpci entries from": "Si l'hôte se bloque, supprimez les entrées hostpci de", "If the host freezes, remove hostpci entries from": "Si l'hôte se bloque, supprimez les entrées hostpci de",
@@ -2973,6 +2992,7 @@
"Inaccessible device during VM startup": "Périphérique inaccessible lors du démarrage de la VM", "Inaccessible device during VM startup": "Périphérique inaccessible lors du démarrage de la VM",
"Inactive": "Inactif", "Inactive": "Inactif",
"Inactive (entry in fstab, not currently mounted)": "Inactif (entrée dans fstab, non monté actuellement)", "Inactive (entry in fstab, not currently mounted)": "Inactif (entrée dans fstab, non monté actuellement)",
"Include these changes in oci-compose?": "Inclure ces modifications dans oci-compose ?",
"Include these resources in oci-compose?": "inclure ces ressources dans oci-compose ?", "Include these resources in oci-compose?": "inclure ces ressources dans oci-compose ?",
"Includes /etc/network (may drop SSH immediately)": "Inclut /etc/network (peut supprimer SSH immédiatement)", "Includes /etc/network (may drop SSH immediately)": "Inclut /etc/network (peut supprimer SSH immédiatement)",
"Includes /etc/zfs": "Inclut /etc/zfs", "Includes /etc/zfs": "Inclut /etc/zfs",
@@ -3029,7 +3049,7 @@
"Initiator IQN is authorised on the target": "L'IQN initiateur est autorisé sur la cible", "Initiator IQN is authorised on the target": "L'IQN initiateur est autorisé sur la cible",
"Initiator IQN:": "IQN initiateur :", "Initiator IQN:": "IQN initiateur :",
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape est un logiciel graphique vectoriel de qualité professionnelle qui fonctionne sur les ordinateurs de bureau Linux, Mac OS X et Windows.", "Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape est un logiciel graphique vectoriel de qualité professionnelle qui fonctionne sur les ordinateurs de bureau Linux, Mac OS X et Windows.",
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "À l'intérieur du LXC, créez l'administrateur: console kimai:utilisateur:créez VOTRE USERNAME VOTRE ROLE_SUPER_ADMIN", "Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Dans le LXC, créez l’administrateur : console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
"Inspect disks before any action": "Inspecter les disques avant toute action", "Inspect disks before any action": "Inspecter les disques avant toute action",
"Inspect host device nodes": "Inspecter les nœuds du périphérique hôte", "Inspect host device nodes": "Inspecter les nœuds du périphérique hôte",
"Inspect passthrough/kernel events": "Inspecter les événements de relais/noyau", "Inspect passthrough/kernel events": "Inspecter les événements de relais/noyau",
@@ -3206,9 +3226,11 @@
"Interrupted operation:": "Interruption du opera:", "Interrupted operation:": "Interruption du opera:",
"Interrupted stack operation found": "Pile interrompue operation trouvée", "Interrupted stack operation found": "Pile interrompue operation trouvée",
"Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.": "Candidat 'proxmox-ve' invalide (pas 9.x ou aucun). Veuillez vérifier la configuration et le réseau de votre référentiel, puis réessayez.", "Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.": "Candidat 'proxmox-ve' invalide (pas 9.x ou aucun). Veuillez vérifier la configuration et le réseau de votre référentiel, puis réessayez.",
"Invalid ALLOWED_HOSTS value": "Valeur HÔTES INDÉSIRABLES", "Invalid ALLOWED_HOSTS value": "Valeur ALLOWED_HOSTS invalide",
"Invalid Home Assistant OS check timeout": "Non valide Home Assistant OS check timeout", "Invalid Home Assistant OS check timeout": "Non valide Home Assistant OS check timeout",
"Invalid ID": "Pièce d'identité invalide", "Invalid ID": "Pièce d'identité invalide",
"Invalid IPv4 address": "Adresse IPv4 non valide",
"Invalid IPv4 gateway": "Passerelle IPv4 non valide",
"Invalid Intel render path": "Chemin de rendu Intel incorrect", "Invalid Intel render path": "Chemin de rendu Intel incorrect",
"Invalid Jellyfin path:": "Chemin Jellyfin non valide:", "Invalid Jellyfin path:": "Chemin Jellyfin non valide:",
"Invalid MAC address:": "Adresse MAC non valide:", "Invalid MAC address:": "Adresse MAC non valide:",
@@ -3241,6 +3263,7 @@
"Invalid administrator email": "Courriel administrateur non valide", "Invalid administrator email": "Courriel administrateur non valide",
"Invalid administrator user name": "Nom d'utilisateur non valide", "Invalid administrator user name": "Nom d'utilisateur non valide",
"Invalid base VMID": "VMID de base non valide", "Invalid base VMID": "VMID de base non valide",
"Invalid bridge": "Pont réseau non valide",
"Invalid check package:": "Dossier de contrôle non valide:", "Invalid check package:": "Dossier de contrôle non valide:",
"Invalid configuration path:": "Chemin de configuration non valide & #160;:", "Invalid configuration path:": "Chemin de configuration non valide & #160;:",
"Invalid consume/export volumes": "Volumes de consommation/d'exportation non valides", "Invalid consume/export volumes": "Volumes de consommation/d'exportation non valides",
@@ -3304,6 +3327,7 @@
"Invalid remote name": "Nom distant non valide", "Invalid remote name": "Nom distant non valide",
"Invalid remote path": "Voie distante non valide", "Invalid remote path": "Voie distante non valide",
"Invalid repair version:": "Version de réparation non valide:", "Invalid repair version:": "Version de réparation non valide:",
"Invalid resource value": "Valeur de ressource non valide",
"Invalid resources": "Ressources non valides", "Invalid resources": "Ressources non valides",
"Invalid restored volume path": "Voie de volume restaurée non valide", "Invalid restored volume path": "Voie de volume restaurée non valide",
"Invalid running state": "État de fonctionnement non valide", "Invalid running state": "État de fonctionnement non valide",
@@ -3322,6 +3346,7 @@
"Invalid stack members": "Membres de pile non valides", "Invalid stack members": "Membres de pile non valides",
"Invalid stack name": "Nom de la pile non valide", "Invalid stack name": "Nom de la pile non valide",
"Invalid stack operation": "Pile non valide operation", "Invalid stack operation": "Pile non valide operation",
"Invalid storage": "Stockage non valide",
"Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "ID de stockage invalide. Utilisez uniquement des lettres, des chiffres, des traits d'union et des traits de soulignement.", "Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "ID de stockage invalide. Utilisez uniquement des lettres, des chiffres, des traits d'union et des traits de soulignement.",
"Invalid storage name": "Nom de stockage invalide", "Invalid storage name": "Nom de stockage invalide",
"Invalid suite application": "Demande de suite non valide", "Invalid suite application": "Demande de suite non valide",
@@ -3436,7 +3461,7 @@
"Keyfile removed.": "fichier clé supprimé.", "Keyfile removed.": "fichier clé supprimé.",
"Keyrings method failed; trying apt-key fallback": "La méthode des porte-clés a échoué ; essayer la solution de secours apt-key", "Keyrings method failed; trying apt-key fallback": "La méthode des porte-clés a échoué ; essayer la solution de secours apt-key",
"KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - Une plateforme croisée et une suite d'automatisation de conception électronique ouverte.", "KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - Une plateforme croisée et une suite d'automatisation de conception électronique ouverte.",
"Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai n'a pas de compte par défaut. Saisissez le conteneur avec : pct entre {main vmid}", "Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai n’a pas de compte par défaut. Ouvrez un shell dans le conteneur avec : pct enter {main_vmid}",
"Kimai is a professional grade time-tracking application, free and open-source.": "Kimai est une application professionnelle de suivi du temps, libre et open-source.", "Kimai is a professional grade time-tracking application, free and open-source.": "Kimai est une application professionnelle de suivi du temps, libre et open-source.",
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa est un outil puissant conçu pour vous donner un contrôle complet sur vos bibliothèques multimédias. Avec Kometa, vous pouvez porter votre personnalisation au niveau suivant, avec un contrôle granulaire sur les métadonnées, les collections, les superpositions et bien plus encore.", "Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa est un outil puissant conçu pour vous donner un contrôle complet sur vos bibliothèques multimédias. Avec Kometa, vous pouvez porter votre personnalisation au niveau suivant, avec un contrôle granulaire sur les métadonnées, les collections, les superpositions et bien plus encore.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa lit sa configuration à partir de /config/config.yml et le conteneur ne livre que /config/config.yml.template. Copiez le modèle pour config.yml, remplissez les connexions required Plex et TMDb, puis redémarrez le conteneur.", "Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa lit sa configuration à partir de /config/config.yml et le conteneur ne livre que /config/config.yml.template. Copiez le modèle pour config.yml, remplissez les connexions required Plex et TMDb, puis redémarrez le conteneur.",
@@ -3488,7 +3513,7 @@
"Leave empty to keep every backup (not recommended).": "Laissez vide pour conserver chaque sauvegarde (non recommandé).", "Leave empty to keep every backup (not recommended).": "Laissez vide pour conserver chaque sauvegarde (non recommandé).",
"Leave empty to use the retention of the storage.": "Laisser vide pour utiliser la rétention du stockage.", "Leave empty to use the retention of the storage.": "Laisser vide pour utiliser la rétention du stockage.",
"Legacy PVE 8 .list files commented or not present": "Fichiers .list hérités PVE 8 commentés ou non présents", "Legacy PVE 8 .list files commented or not present": "Fichiers .list hérités PVE 8 commentés ou non présents",
"Legacy ceph.list commented or not present": "Ceph.list hérité commenté ou non présent", "Legacy ceph.list commented or not present": "Ancien fichier ceph.list commenté ou absent",
"Legacy gasket-dkms cleanup could not be verified as complete.": "Impossible de confirmer que le nettoyage de l'ancien paquet gasket-dkms est terminé.", "Legacy gasket-dkms cleanup could not be verified as complete.": "Impossible de confirmer que le nettoyage de l'ancien paquet gasket-dkms est terminé.",
"Legacy gasket-dkms detected": "Ancien paquet gasket-dkms détecté", "Legacy gasket-dkms detected": "Ancien paquet gasket-dkms détecté",
"Legacy network tools (e.g., ifconfig)": "Outils réseau hérités (par exemple, ifconfig)", "Legacy network tools (e.g., ifconfig)": "Outils réseau hérités (par exemple, ifconfig)",
@@ -4153,6 +4178,7 @@
"No active Proxmox storage accepts OCI template archives.": "Aucun stockage Proxmox actif n'accepte les archives de modèles OCI.", "No active Proxmox storage accepts OCI template archives.": "Aucun stockage Proxmox actif n'accepte les archives de modèles OCI.",
"No active exports or showmount not available": "Aucune exportation active ou showmount non disponible", "No active exports or showmount not available": "Aucune exportation active ou showmount non disponible",
"No active session": "Aucune session active", "No active session": "Aucune session active",
"No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.": "Aucun abonnement actif et aucun référentiel PBS utilisable. Le mode non interactif ne peut pas modifier les sources APT ;configurez-les dans Administration > Dépôts.",
"No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Aucun abonnement actif et aucun référentiel PVE utilisable. Le mode non interactif ne peut pas modifier les sources APT ; configurez-les dans Nœud > Mises à jour > Dépôts.", "No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Aucun abonnement actif et aucun référentiel PVE utilisable. Le mode non interactif ne peut pas modifier les sources APT ; configurez-les dans Nœud > Mises à jour > Dépôts.",
"No additional GPU can be added.": "Aucun GPU supplémentaire ne peut être ajouté.", "No additional GPU can be added.": "Aucun GPU supplémentaire ne peut être ajouté.",
"No additional device needs to be added.": "Aucun appareil supplémentaire ne doit être ajouté.", "No additional device needs to be added.": "Aucun appareil supplémentaire ne doit être ajouté.",
@@ -4238,7 +4264,7 @@
"No jobs": "Aucun emploi", "No jobs": "Aucun emploi",
"No language selected.": "Aucune langue sélectionnée.", "No language selected.": "Aucune langue sélectionnée.",
"No local disk storage configured.": "Aucun stockage sur disque local configuré.", "No local disk storage configured.": "Aucun stockage sur disque local configuré.",
"No main sources.list present (skipped)": "Aucune liste de sources principale présente (ignorée)", "No main sources.list present (skipped)": "Aucun fichier sources.list principal présent (ignoré)",
"No matches for:": "Aucune correspondance pour :", "No matches for:": "Aucune correspondance pour :",
"No matching PVE vzdump jobs were found for the PBS backend.": "Aucune tâche vzdump PVE correspondante n'a été trouvée pour le backend PBS.", "No matching PVE vzdump jobs were found for the PBS backend.": "Aucune tâche vzdump PVE correspondante n'a été trouvée pour le backend PBS.",
"No matching PVE vzdump jobs were found for the local archive backend.": "Aucune tâche vzdump PVE correspondante n'a été trouvée pour le backend d'archive local.", "No matching PVE vzdump jobs were found for the local archive backend.": "Aucune tâche vzdump PVE correspondante n'a été trouvée pour le backend d'archive local.",
@@ -4257,6 +4283,7 @@
"No password provided.": "Aucun mot de passe fourni.", "No password provided.": "Aucun mot de passe fourni.",
"No paths available": "Aucun chemin disponible", "No paths available": "Aucun chemin disponible",
"No paths selected. Select at least one path.": "Aucun chemin sélectionné. Sélectionnez au moins un chemin.", "No paths selected. Select at least one path.": "Aucun chemin sélectionné. Sélectionnez au moins un chemin.",
"No pending changes": "Aucune modification en attente",
"No pending paths to schedule for reboot.": "Aucun chemin en attente pour planifier le redémarrage.", "No pending paths to schedule for reboot.": "Aucun chemin en attente pour planifier le redémarrage.",
"No permanent NFS mounts configured.": "Aucun montage NFS permanent configuré.", "No permanent NFS mounts configured.": "Aucun montage NFS permanent configuré.",
"No permanent Samba mounts configured.": "Aucun montage Samba permanent configuré.", "No permanent Samba mounts configured.": "Aucun montage Samba permanent configuré.",
@@ -4334,7 +4361,7 @@
"Not all shared directories were verified": "Tous les répertoires partagés n'ont pas été vérifiés", "Not all shared directories were verified": "Tous les répertoires partagés n'ont pas été vérifiés",
"Not an OVH server, skipping RTM installation": "Pas un serveur OVH, sautant l'installation RTM", "Not an OVH server, skipping RTM installation": "Pas un serveur OVH, sautant l'installation RTM",
"Not currently mounted": "Non monté actuellement", "Not currently mounted": "Non monté actuellement",
"Not currently mounted — skipping umount.": "Non monté actuellement – ​​ignorer umount.", "Not currently mounted — skipping umount.": "Non monté actuellement – ignorer umount.",
"Not declared": "Non déclaré", "Not declared": "Non déclaré",
"Not enough free space for the backup": "Pas assez d'espace libre pour la sauvegarde", "Not enough free space for the backup": "Pas assez d'espace libre pour la sauvegarde",
"Not enough free space on {storage}. Free some space or select another storage.": "Pas assez d’espace libre sur {storage}. Libérez de l'espace ou sélectionnez un autre stockage.", "Not enough free space on {storage}. Free some space or select another storage.": "Pas assez d’espace libre sur {storage}. Libérez de l'espace ou sélectionnez un autre stockage.",
@@ -4398,6 +4425,7 @@
"OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM supprimé (les artefacts de marionnettes peuvent nécessiter un nettoyage manuel)", "OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM supprimé (les artefacts de marionnettes peuvent nécessiter un nettoyage manuel)",
"OVH server detected": "Serveur OVH détecté", "OVH server detected": "Serveur OVH détecté",
"OVH server detection and RTM installation process completed": "Processus de détection du serveur OVH et d'installation de RTM terminé", "OVH server detection and RTM installation process completed": "Processus de détection du serveur OVH et d'installation de RTM terminé",
"Object detector": "Détecteur d’objets",
"Object detector for Frigate": "Détecteur d'objets pour Frigate", "Object detector for Frigate": "Détecteur d'objets pour Frigate",
"Observer is not responding on port 4357": "L'observateur ne répond pas au port 4357", "Observer is not responding on port 4357": "L'observateur ne répond pas au port 4357",
"Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian est une application de prise de notes qui vous permet de créer, de lier et d'organiser vos notes sur votre appareil, avec des centaines de plugins et de thèmes pour personnaliser votre workflow. Vous pouvez également publier vos notes en ligne, les accéder hors ligne et les synchroniser en toute sécurité avec le chiffrement de bout en bout.", "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian est une application de prise de notes qui vous permet de créer, de lier et d'organiser vos notes sur votre appareil, avec des centaines de plugins et de thèmes pour personnaliser votre workflow. Vous pouvez également publier vos notes en ligne, les accéder hors ligne et les synchroniser en toute sécurité avec le chiffrement de bout en bout.",
@@ -4523,11 +4551,17 @@
"PBS backup error log": "Journal des erreurs de sauvegarde PBS", "PBS backup error log": "Journal des erreurs de sauvegarde PBS",
"PBS backup failed.": "La sauvegarde PBS a échoué.", "PBS backup failed.": "La sauvegarde PBS a échoué.",
"PBS certificate": "certificat PBS", "PBS certificate": "certificat PBS",
"PBS datastores": "Magasins de données PBS",
"PBS encryption keyfile (show / replace / remove)": "fichier de clé de chiffrement PBS (afficher/remplacer/supprimer)", "PBS encryption keyfile (show / replace / remove)": "fichier de clé de chiffrement PBS (afficher/remplacer/supprimer)",
"PBS encryption keyfile — detailed info": "Fichier de clé de chiffrement PBS – informations détaillées", "PBS encryption keyfile — detailed info": "Fichier de clé de chiffrement PBS – informations détaillées",
"PBS extraction failed": "L'extraction PBS a échoué", "PBS extraction failed": "L'extraction PBS a échoué",
"PBS host or IP address:": "Hôte PBS ou adresse IP :", "PBS host or IP address:": "Hôte PBS ou adresse IP :",
"PBS package update": "Mise à jour des paquets PBS",
"PBS package upgrade completed. Check whether a reboot is required before your next backup window.": "Mise à niveau des paquets PBS terminée. Vérifiez si un redémarrage est nécessaire avant votre prochaine plage de sauvegarde.",
"PBS package upgrade failed. Review the APT output before retrying.": "Échec de la mise à niveau des paquets PBS. Consultez la sortie APT avant de réessayer.",
"PBS upload": "Téléchargement PBS", "PBS upload": "Téléchargement PBS",
"PBS version and packages": "Version et paquets PBS",
"PBS: using the configured APT repositories without changing sources.": "PBS : utilisation des dépôts APT configurés sans modifier les sources.",
"PCI Address": "Adresse PCI", "PCI Address": "Adresse PCI",
"PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "Passthrough PCI, état TPM, configuration cloud-init, hooks Proxmox", "PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "Passthrough PCI, état TPM, configuration cloud-init, hooks Proxmox",
"PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "Passthrough PCI, état TPM, cloud-init, instantanés, hooks spécifiques à Proxmox", "PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "Passthrough PCI, état TPM, cloud-init, instantanés, hooks spécifiques à Proxmox",
@@ -4864,12 +4898,17 @@
"Proxmology logo applied": "Logo Proxmologie appliqué", "Proxmology logo applied": "Logo Proxmologie appliqué",
"Proxmox 9 system update allready": "La mise à jour du système Proxmox 9 est déjà terminée", "Proxmox 9 system update allready": "La mise à jour du système Proxmox 9 est déjà terminée",
"Proxmox APT repositories configured": "Dépôts Proxmox APT configurés", "Proxmox APT repositories configured": "Dépôts Proxmox APT configurés",
"Proxmox Backup Server": "Proxmox Backup Server",
"Proxmox Backup Server (PBS) destinations": "Destinations Proxmox Backup Server (PBS)", "Proxmox Backup Server (PBS) destinations": "Destinations Proxmox Backup Server (PBS)",
"Proxmox Backup Server APT repositories configured": "Dépôts APT de Proxmox Backup Server configurés",
"Proxmox Backup Server Update": "Mise à jour de Proxmox Backup Server",
"Proxmox Backup Server update completed.": "Mise à jour de Proxmox Backup Server terminée.",
"Proxmox CIFS Storage Status:": "État de stockage Proxmox CIFS :", "Proxmox CIFS Storage Status:": "État de stockage Proxmox CIFS :",
"Proxmox NFS Storage Status:": "Statut de stockage Proxmox NFS :", "Proxmox NFS Storage Status:": "Statut de stockage Proxmox NFS :",
"Proxmox System Update": "Mise à jour du système Proxmox", "Proxmox System Update": "Mise à jour du système Proxmox",
"Proxmox Update": "Mise à jour Proxmox", "Proxmox Update": "Mise à jour Proxmox",
"Proxmox VE": "Proxmox VE", "Proxmox VE": "Proxmox VE",
"Proxmox VE 8 package upgrade failed. Review the APT output before retrying.": "La mise à jour des paquets de Proxmox VE 8 a échoué. Vérifiez la sortie d’APT avant de réessayer.",
"Proxmox VE 8 system update completed successfully": "La mise à jour du système Proxmox VE 8 s'est terminée avec succès", "Proxmox VE 8 system update completed successfully": "La mise à jour du système Proxmox VE 8 s'est terminée avec succès",
"Proxmox VE 8 to 9 Manual Upgrade Guide": "Guide de mise à niveau manuelle Proxmox VE 8 vers 9", "Proxmox VE 8 to 9 Manual Upgrade Guide": "Guide de mise à niveau manuelle Proxmox VE 8 vers 9",
"Proxmox VE 9.x no-subscription repository created": "Création d'un référentiel sans abonnement Proxmox VE 9.x", "Proxmox VE 9.x no-subscription repository created": "Création d'un référentiel sans abonnement Proxmox VE 9.x",
@@ -4877,6 +4916,7 @@
"Proxmox VE configuration completed.": "Configuration de Proxmox VE terminée.", "Proxmox VE configuration completed.": "Configuration de Proxmox VE terminée.",
"Proxmox VE safe update completed": "Mise à jour sécurisée de Proxmox VE terminée", "Proxmox VE safe update completed": "Mise à jour sécurisée de Proxmox VE terminée",
"Proxmox auth logger service created and started": "Service d'enregistrement d'authentification Proxmox créé et démarré", "Proxmox auth logger service created and started": "Service d'enregistrement d'authentification Proxmox créé et démarré",
"Proxmox could not apply the container settings": "Proxmox n’a pas pu appliquer les paramètres du conteneur",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Le référentiel d'entreprise Proxmox peut être manquant ou inaccessible. J'essaie de passer au sans abonnement...", "Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Le référentiel d'entreprise Proxmox peut être manquant ou inaccessible. J'essaie de passer au sans abonnement...",
"Proxmox filter configured": "Filtre Proxmox configuré", "Proxmox filter configured": "Filtre Proxmox configuré",
"Proxmox iSCSI Storage Status:": "État du stockage Proxmox iSCSI :", "Proxmox iSCSI Storage Status:": "État du stockage Proxmox iSCSI :",
@@ -4993,6 +5033,7 @@
"Rebuilding initramfs to apply nouveau blacklist before installation...": "Reconstruction d'initramfs pour appliquer la liste noire de nouveau avant l'installation...", "Rebuilding initramfs to apply nouveau blacklist before installation...": "Reconstruction d'initramfs pour appliquer la liste noire de nouveau avant l'installation...",
"Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "La reconstruction du conteneur et sa récupération à partir de sa sauvegarde sont testées sur la même image ; la première mise à jour vers une image plus récente reste à observer.", "Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "La reconstruction du conteneur et sa récupération à partir de sa sauvegarde sont testées sur la même image ; la première mise à jour vers une image plus récente reste à observer.",
"Recent": "Récent", "Recent": "Récent",
"Recent PBS tasks": "Tâches PBS récentes",
"Recent Samba Servers": "Serveurs Samba récents", "Recent Samba Servers": "Serveurs Samba récents",
"Recent Samba server": "Serveur Samba récent", "Recent Samba server": "Serveur Samba récent",
"Recent logs:": "Journaux récents :", "Recent logs:": "Journaux récents :",
@@ -5052,6 +5093,7 @@
"Recreation completed. Data kept.": "Loisirs terminés. Données conservées.", "Recreation completed. Data kept.": "Loisirs terminés. Données conservées.",
"Recreation prepared": "Loisirs préparés", "Recreation prepared": "Loisirs préparés",
"Refresh APT index and verify repositories:": "Actualisez l'index APT et vérifiez les référentiels :", "Refresh APT index and verify repositories:": "Actualisez l'index APT et vérifiez les référentiels :",
"Refresh the configured APT repositories and review the PBS package upgrade? Repository files will be preserved.": "Actualiser les dépôts APT configurés et examiner la mise à niveau des paquets PBS ? Les fichiers des dépôts seront conservés.",
"Refresh token of the connector, from the Twingate admin console": "Jeton d'actualisation du connecteur, depuis la console d'administration Twingate", "Refresh token of the connector, from the Twingate admin console": "Jeton d'actualisation du connecteur, depuis la console d'administration Twingate",
"Refresh your browser (Ctrl+Shift+R) to see changes": "Actualisez votre navigateur (Ctrl+Shift+R) pour voir les modifications", "Refresh your browser (Ctrl+Shift+R) to see changes": "Actualisez votre navigateur (Ctrl+Shift+R) pour voir les modifications",
"Refresh your browser to see changes (server restart may be required)": "Actualisez votre navigateur pour voir les modifications (un redémarrage du serveur peut être nécessaire)", "Refresh your browser to see changes (server restart may be required)": "Actualisez votre navigateur pour voir les modifications (un redémarrage du serveur peut être nécessaire)",
@@ -5141,6 +5183,8 @@
"Remove saved credentials": "Supprimer les informations d'identification enregistrées", "Remove saved credentials": "Supprimer les informations d'identification enregistrées",
"Remove subscription banner": "Supprimer la bannière d'abonnement", "Remove subscription banner": "Supprimer la bannière d'abonnement",
"Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Supprimer l'application ?Ses disques conteneurs sont supprimés et seule une sauvegarde peut les restaurer.", "Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Supprimer l'application ?Ses disques conteneurs sont supprimés et seule une sauvegarde peut les restaurer.",
"Remove the lock left on a VM by an interrupted task": "Supprimer le verrou laissé sur une VM par une tâche interrompue",
"Remove the lock left on a container by an interrupted task": "Supprimer le verrou laissé sur un conteneur par une tâche interrompue",
"Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Supprimer les informations d'identification enregistrées pour {registry} ?\nLes applications en cours d’exécution continueront de fonctionner.", "Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Supprimer les informations d'identification enregistrées pour {registry} ?\nLes applications en cours d’exécution continueront de fonctionner.",
"Remove the unprivileged flag from configuration:": "Supprimez l'indicateur sans privilèges de la configuration :", "Remove the unprivileged flag from configuration:": "Supprimez l'indicateur sans privilèges de la configuration :",
"Remove unused packages and their config": "Supprimer les packages inutilisés et leur configuration", "Remove unused packages and their config": "Supprimer les packages inutilisés et leur configuration",
@@ -5222,6 +5266,7 @@
"Repository helper unavailable. NVIDIA installation stopped.": "Assistant du référentiel indisponible. L'installation de NVIDIA s'est arrêtée.", "Repository helper unavailable. NVIDIA installation stopped.": "Assistant du référentiel indisponible. L'installation de NVIDIA s'est arrêtée.",
"Repository policy returned an unexpected result.": "La stratégie du référentiel a renvoyé un résultat inattendu.", "Repository policy returned an unexpected result.": "La stratégie du référentiel a renvoyé un résultat inattendu.",
"Repository ready.": "Référentiel prêt.", "Repository ready.": "Référentiel prêt.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.": "les sources du référentiel ont été modifiées, mais l'actualisation de la liste des packages APT a échoué. Opération arrêtée ;les sources n’ont pas été annulées. Inspectez Administration > Référentiels et réessayez apt-get update avant de continuer.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Les sources du référentiel ont été modifiées, mais l'actualisation de la liste des paquets APT a échoué. Opération arrêtée ; les sources n'ont pas été restaurées. Vérifiez Nœud > Mises à jour > Dépôts et relancez apt-get update avant de continuer.", "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Les sources du référentiel ont été modifiées, mais l'actualisation de la liste des paquets APT a échoué. Opération arrêtée ; les sources n'ont pas été restaurées. Vérifiez Nœud > Mises à jour > Dépôts et relancez apt-get update avant de continuer.",
"Repository switch declined; no APT source changed.": "Le changement de référentiel a été refusé ; aucune source APT n'a été modifiée.", "Repository switch declined; no APT source changed.": "Le changement de référentiel a été refusé ; aucune source APT n'a été modifiée.",
"Repository:": "Dépôt:", "Repository:": "Dépôt:",
@@ -5316,7 +5361,9 @@
"Reverting ZFS ARC tuning...": "Annulation du réglage ZFS ARC...", "Reverting ZFS ARC tuning...": "Annulation du réglage ZFS ARC...",
"Reverting pigz wrapper...": "Rétablissement du wrapper pigz...", "Reverting pigz wrapper...": "Rétablissement du wrapper pigz...",
"Reverting vzdump speed tuning...": "Annulation du réglage de la vitesse de vzdump...", "Reverting vzdump speed tuning...": "Annulation du réglage de la vitesse de vzdump...",
"Review and apply": "Vérifier et appliquer",
"Review external OCI changes": "Examiner les modifications externes de OCI", "Review external OCI changes": "Examiner les modifications externes de OCI",
"Review package upgrade": "Examiner la mise à niveau des paquets",
"Review passthrough config files": "Examiner les fichiers de configuration relais", "Review passthrough config files": "Examiner les fichiers de configuration relais",
"Review what will be removed": "Vérifiez ce qui sera supprimé", "Review what will be removed": "Vérifiez ce qui sera supprimé",
"Rip DVD and Blu-ray media from a browser": "Rip DVD et Blu-ray médias à partir d'un navigateur", "Rip DVD and Blu-ray media from a browser": "Rip DVD et Blu-ray médias à partir d'un navigateur",
@@ -5359,6 +5406,7 @@
"Run the following inside the VM:": "Exécutez la commande suivante dans la VM :", "Run the following inside the VM:": "Exécutez la commande suivante dans la VM :",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "Exécutez le commutateur de canal de version à partir d'une session SSH ou de la console hôte Proxmox avec :", "Run the release-channel switch from an SSH session or the Proxmox host console with:": "Exécutez le commutateur de canal de version à partir d'une session SSH ou de la console hôte Proxmox avec :",
"Run the update from an SSH session or the Proxmox host console with:": "Exécutez la mise à jour depuis une session SSH ou la console hôte Proxmox avec :", "Run the update from an SSH session or the Proxmox host console with:": "Exécutez la mise à jour depuis une session SSH ou la console hôte Proxmox avec :",
"Run this operation as root.": "Exécutez cette opération en tant que root.",
"Run upgrade checklist script:": "Exécutez le script de liste de contrôle de mise à niveau :", "Run upgrade checklist script:": "Exécutez le script de liste de contrôle de mise à niveau :",
"Running": "En cours d'exécution", "Running": "En cours d'exécution",
"Running Lynis security audit...": "Exécution de l'audit de sécurité Lynis...", "Running Lynis security audit...": "Exécution de l'audit de sécurité Lynis...",
@@ -5773,6 +5821,7 @@
"ShareCoveX panel": "Panneau ShareCoveX", "ShareCoveX panel": "Panneau ShareCoveX",
"Shared Directory Ready:": "Répertoire partagé prêt :", "Shared Directory Ready:": "Répertoire partagé prêt :",
"Shared Group": "Groupe partagé", "Shared Group": "Groupe partagé",
"Shared directories": "Dossiers partagés",
"Shared directory created:": "Répertoire partagé créé & #160;:", "Shared directory created:": "Répertoire partagé créé & #160;:",
"Shared directory for consume and export": "Répertoire partagé pour la consommation et l'exportation", "Shared directory for consume and export": "Répertoire partagé pour la consommation et l'exportation",
"Shared directory for copy/sync operations": "Répertoire partagé pour copier/sync operations", "Shared directory for copy/sync operations": "Répertoire partagé pour copier/sync operations",
@@ -5802,6 +5851,7 @@
"Should show pve-manager/9.x.x": "Devrait afficher pve-manager/9.x.x", "Should show pve-manager/9.x.x": "Devrait afficher pve-manager/9.x.x",
"Shoutrrr addresses for the notifications (optional)": "Adresses Shoutrrr pour les notifications (facultatif)", "Shoutrrr addresses for the notifications (optional)": "Adresses Shoutrrr pour les notifications (facultatif)",
"Show All Scripts": "Afficher tous les scripts", "Show All Scripts": "Afficher tous les scripts",
"Show CT users and their UID/GID on the host": "Utilisateurs du CT et leurs UID/GID sur l’hôte",
"Show CT users in table format": "Afficher les utilisateurs CT sous forme de tableau", "Show CT users in table format": "Afficher les utilisateurs CT sous forme de tableau",
"Show Container Privilege Status": "Afficher l'état des privilèges du conteneur", "Show Container Privilege Status": "Afficher l'état des privilèges du conteneur",
"Show DNS resolution status": "Afficher l'état de la résolution DNS", "Show DNS resolution status": "Afficher l'état de la résolution DNS",
@@ -5963,6 +6013,7 @@
"Start the VM to begin Windows installation from the mounted ISO.": "Démarrez la VM pour commencer l'installation de Windows à partir de l'ISO montée.", "Start the VM to begin Windows installation from the mounted ISO.": "Démarrez la VM pour commencer l'installation de Windows à partir de l'ISO montée.",
"Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Lancer les applications une fois enregistrées ? Répondez Non si les conteneurs d'origine s'exécutent toujours sur un autre hôte : les deux utiliseraient les mêmes adresses.", "Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Lancer les applications une fois enregistrées ? Répondez Non si les conteneurs d'origine s'exécutent toujours sur un autre hôte : les deux utiliseraient les mêmes adresses.",
"Start the converted container:": "Démarrez le conteneur converti :", "Start the converted container:": "Démarrez le conteneur converti :",
"Start the interactive Proxmox package upgrade": "Lancer la mise à niveau interactive des paquets Proxmox",
"Start the main system upgrade:": "Démarrez la mise à niveau principale du système :", "Start the main system upgrade:": "Démarrez la mise à niveau principale du système :",
"Start the stack with Proxmox": "Démarrer la pile avec Proxmox", "Start the stack with Proxmox": "Démarrer la pile avec Proxmox",
"Start uploading to PBS — sets a recovery passphrase": "Commencer le téléchargement sur PBS – définit une phrase secrète de récupération", "Start uploading to PBS — sets a recovery passphrase": "Commencer le téléchargement sur PBS – définit une phrase secrète de récupération",
@@ -6241,6 +6292,7 @@
"The Paperless persistent volumes need at least 8 GB": "Les volumes persistants sans papier nécessitent au moins 8 Go", "The Paperless persistent volumes need at least 8 GB": "Les volumes persistants sans papier nécessitent au moins 8 Go",
"The PostgreSQL volume needs at least 4 GB": "Le volume PostgreSQL nécessite au moins 4 Go", "The PostgreSQL volume needs at least 4 GB": "Le volume PostgreSQL nécessite au moins 4 Go",
"The PostgreSQL volume needs at least 8 GB": "Le volume PostgreSQL nécessite au moins 8 Go", "The PostgreSQL volume needs at least 8 GB": "Le volume PostgreSQL nécessite au moins 8 Go",
"The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "La mise à jour de Proxmox VE 8 n’a pas abouti. La mise à niveau vers Proxmox VE 9 n’a pas été lancée.",
"The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Le trousseau de clés d'archive Proxmox est manquant ;L'installation de Ceph ne peut pas continuer en toute sécurité", "The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Le trousseau de clés d'archive Proxmox est manquant ;L'installation de Ceph ne peut pas continuer en toute sécurité",
"The Proxmox inventory and the local configurations differ": "L'inventaire Proxmox et les configurations locales diffèrent", "The Proxmox inventory and the local configurations differ": "L'inventaire Proxmox et les configurations locales diffèrent",
"The ROCm image has no support for the AMD GPU of this host:": "L'image ROCm ne prend pas en charge le GPU AMD de cet hôte :", "The ROCm image has no support for the AMD GPU of this host:": "L'image ROCm ne prend pas en charge le GPU AMD de cet hôte :",
@@ -6311,6 +6363,7 @@
"The build could not be checked beforehand; continuing without that check.": "La build n'a pas pu être vérifiée au préalable ;continuer sans cette vérification.", "The build could not be checked beforehand; continuing without that check.": "La build n'a pas pu être vérifiée au préalable ;continuer sans cette vérification.",
"The cached image does not match the current digest": "L'image mise en cache ne correspond pas au digest actuel", "The cached image does not match the current digest": "L'image mise en cache ne correspond pas au digest actuel",
"The cached image is damaged; it will be downloaded again.": "L'image mise en cache est endommagée ; elle sera à nouveau téléchargée.", "The cached image is damaged; it will be downloaded again.": "L'image mise en cache est endommagée ; elle sera à nouveau téléchargée.",
"The changes need the application installer": "Ces modifications nécessitent le programme d’installation de l’application",
"The character device does not exist:": "Le dispositif de caractère n'existe pas :", "The character device does not exist:": "Le dispositif de caractère n'existe pas :",
"The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "La commande demande un mot de passe qui n'est pas repris. Après la création des utilisateurs, l'interface web demande un nom d'utilisateur et un mot de passe.", "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "La commande demande un mot de passe qui n'est pas repris. Après la création des utilisateurs, l'interface web demande un nom d'utilisateur et un mot de passe.",
"The command does not name an image": "La commande ne nomme pas une image", "The command does not name an image": "La commande ne nomme pas une image",
@@ -6430,8 +6483,10 @@
"The host bind source does not exist:": "La source de liaison hôte n'existe pas:", "The host bind source does not exist:": "La source de liaison hôte n'existe pas:",
"The host bind source is not a regular file or directory:": "La source de liaison hôte n'est pas un fichier ou un répertoire régulier :", "The host bind source is not a regular file or directory:": "La source de liaison hôte n'est pas un fichier ou un répertoire régulier :",
"The host directory changed before it was mounted": "Le répertoire hôte a changé avant son montage", "The host directory changed before it was mounted": "Le répertoire hôte a changé avant son montage",
"The host directory does not exist": "Le répertoire de l’hôte n’existe pas",
"The host directory may not be accessible from an unprivileged container.": "Le répertoire hôte peut ne pas être accessible à partir d'un conteneur non privilégié.", "The host directory may not be accessible from an unprivileged container.": "Le répertoire hôte peut ne pas être accessible à partir d'un conteneur non privilégié.",
"The host has no IPv4 address on the selected bridge": "L'hôte n'a pas d'adresse IPv4 sur le pont sélectionné", "The host has no IPv4 address on the selected bridge": "L'hôte n'a pas d'adresse IPv4 sur le pont sélectionné",
"The host loses access to them. If the group includes a network or storage controller the host needs, cancel at the summary.": "L’hôte perd l’accès à ces périphériques. Si le groupe contient un contrôleur réseau ou de stockage dont l’hôte a besoin, annulez au résumé.",
"The host monitor does not see the real host memory": "Le moniteur hôte ne voit pas la mémoire de l'hôte réel", "The host monitor does not see the real host memory": "Le moniteur hôte ne voit pas la mémoire de l'hôte réel",
"The host monitor does not share this host namespace:": "L'écran hôte ne partage pas cet espace de noms d'hôte :", "The host monitor does not share this host namespace:": "L'écran hôte ne partage pas cet espace de noms d'hôte :",
"The host monitor include is unknown or differs from the saved record": "L'include du moniteur d'hôte est inconnu ou diffère de l'enregistrement sauvegardé", "The host monitor include is unknown or differs from the saved record": "L'include du moniteur d'hôte est inconnu ou diffère de l'enregistrement sauvegardé",
@@ -6621,6 +6676,7 @@
"The repair must preserve the image dependencies:": "La réparation doit préserver les dépendances de l'image:", "The repair must preserve the image dependencies:": "La réparation doit préserver les dépendances de l'image:",
"The requested VMID block is already in use": "Le bloc VMID demandé est déjà utilisé", "The requested VMID block is already in use": "Le bloc VMID demandé est déjà utilisé",
"The requested machine learning GPU profile is not working; it is not replaced by CPU": "Le profil GPU d'apprentissage automatique demandé ne fonctionne pas; il n'est pas remplacé par CPU", "The requested machine learning GPU profile is not working; it is not replaced by CPU": "Le profil GPU d'apprentissage automatique demandé ne fonctionne pas; il n'est pas remplacé par CPU",
"The resource edit could not be fully rolled back. Open Manage again to review and import the current Proxmox settings.": "Impossible d’annuler entièrement la modification des ressources. Rouvrez le menu de gestion pour vérifier et importer les réglages actuels de Proxmox.",
"The restored service did not pass its health check": "Le service restauré n'a pas réussi son bilan de santé", "The restored service did not pass its health check": "Le service restauré n'a pas réussi son bilan de santé",
"The restored service stopped; the recovery is not confirmed": "Le service restauré s'est arrêté; la récupération n'est pas confirmée", "The restored service stopped; the recovery is not confirmed": "Le service restauré s'est arrêté; la récupération n'est pas confirmée",
"The reviewed Tandoor stack does not require a privileged LXC.": "La pile Tandoor revue ne require un LXC privilégié.", "The reviewed Tandoor stack does not require a privileged LXC.": "La pile Tandoor revue ne require un LXC privilégié.",
@@ -6666,6 +6722,7 @@
"The selected VM": "La VM sélectionnée", "The selected VM": "La VM sélectionnée",
"The selected VM is running.": "La VM sélectionnée est en cours d'exécution.", "The selected VM is running.": "La VM sélectionnée est en cours d'exécution.",
"The selected base folder does not exist and could not be created:": "Le dossier de base sélectionné n'existe pas et n'a pas pu être créé :", "The selected base folder does not exist and could not be created:": "Le dossier de base sélectionné n'existe pas et n'a pas pu être créé :",
"The selected bridge does not exist": "Le pont réseau sélectionné n’existe pas",
"The selected bridge has no IPv4 subnet for the host-monitor firewall": "Le pont sélectionné n'a pas de sous-réseau IPv4 pour le pare-feu du moniteur hôte", "The selected bridge has no IPv4 subnet for the host-monitor firewall": "Le pont sélectionné n'a pas de sous-réseau IPv4 pour le pare-feu du moniteur hôte",
"The selected configuration needs to start the LXC during the installation": "La configuration sélectionnée doit démarrer le LXC pendant l'installation", "The selected configuration needs to start the LXC during the installation": "La configuration sélectionnée doit démarrer le LXC pendant l'installation",
"The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Le conteneur sélectionné n'est pas privilégié. Un conteneur privilégié est requis pour le relais direct du périphérique.", "The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Le conteneur sélectionné n'est pas privilégié. Un conteneur privilégié est requis pour le relais direct du périphérique.",
@@ -6688,6 +6745,7 @@
"The service builds its own image; only a published image can be installed": "Le service construit sa propre image; seule une image publiée peut être installée", "The service builds its own image; only a published image can be installed": "Le service construit sa propre image; seule une image publiée peut être installée",
"The service declares no image:": "Le service ne déclare aucune image:", "The service declares no image:": "Le service ne déclare aucune image:",
"The setting has no final value:": "Le réglage n'a pas de valeur finale:", "The setting has no final value:": "Le réglage n'a pas de valeur finale:",
"The settings could not be fully rolled back. Review the container and its saved modification before continuing.": "Les paramètres n’ont pas pu être entièrement rétablis. Vérifiez le conteneur et la modification enregistrée avant de continuer.",
"The share already exists in smb.conf:": "Le partage existe déjà dans smb.conf :", "The share already exists in smb.conf:": "Le partage existe déjà dans smb.conf :",
"The shared destination is not a directory": "La destination partagée n'est pas un répertoire", "The shared destination is not a directory": "La destination partagée n'est pas un répertoire",
"The shared directory points to a protected host path": "Le répertoire partagé indique un chemin d'hôte protégé", "The shared directory points to a protected host path": "Le répertoire partagé indique un chemin d'hôte protégé",
@@ -6755,19 +6813,24 @@
"These Proxmox resources were added outside ProxMenux:": "Ces ressources Proxmox ont été ajoutées en dehors de ProxMenux :", "These Proxmox resources were added outside ProxMenux:": "Ces ressources Proxmox ont été ajoutées en dehors de ProxMenux :",
"These VMIDs are not free:": "Ces VMID ne sont pas libres:", "These VMIDs are not free:": "Ces VMID ne sont pas libres:",
"These are the changes that will be made": "Ce sont les changements qui seront apportés", "These are the changes that will be made": "Ce sont les changements qui seront apportés",
"These changes were made in Proxmox outside ProxMenux:": "Ces modifications ont été apportées dans Proxmox en dehors de ProxMenux :",
"These container mount paths have an extension-like suffix:": "Ces chemins de montage du conteneur ont un suffixe qui ressemble à une extension :", "These container mount paths have an extension-like suffix:": "Ces chemins de montage du conteneur ont un suffixe qui ressemble à une extension :",
"These containers were restored from a backup and this host has no record of their OCI application:": "Ces conteneurs ont été restaurés à partir d'une sauvegarde et cet hôte n'a aucun enregistrement de leur application OCI :", "These containers were restored from a backup and this host has no record of their OCI application:": "Ces conteneurs ont été restaurés à partir d'une sauvegarde et cet hôte n'a aucun enregistrement de leur application OCI :",
"These devices share the IOMMU group of the selected GPU and will be assigned to the VM with it:": "Ces périphériques partagent le groupe IOMMU du GPU sélectionné et seront attribués à la VM avec lui :",
"These host backups run with this job and expect its storage:": "Ces sauvegardes d'hôte s'exécutent avec ce travail et attendent son stockage :", "These host backups run with this job and expect its storage:": "Ces sauvegardes d'hôte s'exécutent avec ce travail et attendent son stockage :",
"These host backups run with this job and will stop running:": "Ces sauvegardes d'hôte s'exécutent avec ce travail et cesseront de s'exécuter :", "These host backups run with this job and will stop running:": "Ces sauvegardes d'hôte s'exécutent avec ce travail et cesseront de s'exécuter :",
"These interface configurations will be removed": "Ces configurations d'interface seront supprimées", "These interface configurations will be removed": "Ces configurations d'interface seront supprimées",
"These manual changes cannot be adopted safely:": "Ces modifications manuelles ne peuvent pas être adoptées en toute sécurité :", "These manual changes cannot be adopted safely:": "Ces modifications manuelles ne peuvent pas être adoptées en toute sécurité :",
"These paths will not be restored live and will be extracted for manual recovery.": "Ces chemins ne seront pas restaurés en direct et seront extraits pour une récupération manuelle.", "These paths will not be restored live and will be extracted for manual recovery.": "Ces chemins ne seront pas restaurés en direct et seront extraits pour une récupération manuelle.",
"These settings require an application-specific adaptation. The image was not downloaded and the container was not recreated.": "Ces paramètres nécessitent une adaptation propre à l’application. L’image n’a pas été téléchargée et le conteneur n’a pas été recréé.",
"These values are asked during the installation:": "Ces valeurs sont demandées lors de l'installation:", "These values are asked during the installation:": "Ces valeurs sont demandées lors de l'installation:",
"They are described in the image documentation:": "Ils sont décrits dans la documentation de l'image :", "They are described in the image documentation:": "Ils sont décrits dans la documentation de l'image :",
"They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Ils seront ajoutés à oci-compose avant de continuer. Les ressources non prises en charge ou modifiées ne sont pas importées.", "They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Ils seront ajoutés à oci-compose avant de continuer. Les ressources non prises en charge ou modifiées ne sont pas importées.",
"They will be saved in oci-compose before continuing.": "Elles seront enregistrées dans oci-compose avant de continuer.",
"This CIFS share is mounted with restrictive permissions.": "Ce partage CIFS est monté avec des autorisations restrictives.", "This CIFS share is mounted with restrictive permissions.": "Ce partage CIFS est monté avec des autorisations restrictives.",
"This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Ce GPU est considéré comme incompatible avec le relais GPU vers une VM dans ProxMenux.", "This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Ce GPU est considéré comme incompatible avec le relais GPU vers une VM dans ProxMenux.",
"This NFS share is fully restricted — even the host root cannot write to it.": "Ce partage NFS est entièrement restreint : même la racine de l'hôte ne peut pas y écrire.", "This NFS share is fully restricted — even the host root cannot write to it.": "Ce partage NFS est entièrement restreint : même la racine de l'hôte ne peut pas y écrire.",
"This Proxmox Backup Server has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PBS source will be disabled.": "Ce Proxmox Backup Server n'a pas d'abonnement actif. Passer au dépôt sans abonnement ? La source Enterprise PBS inaccessible sera désactivée.",
"This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Cette VM nécessite des étapes d'installation supplémentaires. Consultez le guide d'installation à l'adresse :\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144", "This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Cette VM nécessite des étapes d'installation supplémentaires. Consultez le guide d'installation à l'adresse :\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144",
"This account is for {registry}. Enter an image from that registry.": "Ce compte concerne {registry}. Entrez une image de ce registre.", "This account is for {registry}. Enter an image from that registry.": "Ce compte concerne {registry}. Entrez une image de ce registre.",
"This action will:": "Cette action va :", "This action will:": "Cette action va :",
@@ -6829,10 +6892,13 @@
"This may take several minutes...": "Cela peut prendre plusieurs minutes...", "This may take several minutes...": "Cela peut prendre plusieurs minutes...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Cela signifie que Proxmox gère le cycle de vie du montage de manière native (aucun /etc/fstab manuel n'est nécessaire pour les stockages hôtes NFS/CIFS).", "This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Cela signifie que Proxmox gère le cycle de vie du montage de manière native (aucun /etc/fstab manuel n'est nécessaire pour les stockages hôtes NFS/CIFS).",
"This means the credentials are incorrect.": "Cela signifie que les informations d'identification sont incorrectes.", "This means the credentials are incorrect.": "Cela signifie que les informations d'identification sont incorrectes.",
"This menu requires Proxmox Backup Server.": "Ce menu nécessite Proxmox Backup Server.",
"This might indicate network connectivity issues.": "Cela peut indiquer des problèmes de connectivité réseau.", "This might indicate network connectivity issues.": "Cela peut indiquer des problèmes de connectivité réseau.",
"This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Ce moniteur utilise un LXC privilégié, partage les processus et le réseau avec Proxmox et désactive AppArmor dans le CT. Il utilise l'adresse IP et le pare-feu de l'hôte. Une image compromise pourrait affecter l'hôte; ne pas exposer son interface utilisateur Web à Internet.", "This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Ce moniteur utilise un LXC privilégié, partage les processus et le réseau avec Proxmox et désactive AppArmor dans le CT. Il utilise l'adresse IP et le pare-feu de l'hôte. Une image compromise pourrait affecter l'hôte; ne pas exposer son interface utilisateur Web à Internet.",
"This only removes the schedule. Existing backup archives are NOT deleted.": "Cela supprime uniquement le calendrier. Les archives de sauvegarde existantes ne sont PAS supprimées.", "This only removes the schedule. Existing backup archives are NOT deleted.": "Cela supprime uniquement le calendrier. Les archives de sauvegarde existantes ne sont PAS supprimées.",
"This operation may take several minutes and requires internet connectivity.": "Cette opération peut prendre plusieurs minutes et nécessite une connexion Internet.", "This operation may take several minutes and requires internet connectivity.": "Cette opération peut prendre plusieurs minutes et nécessite une connexion Internet.",
"This operation requires a Proxmox VE host. Use the PBS menu on Proxmox Backup Server.": "Cette opération nécessite un hôte Proxmox VE. Utilisez le menu PBS sur Proxmox Backup Server.",
"This operation supports PBS 3 on Debian 12 and PBS 4 on Debian 13.": "Cette opération prend en charge PBS 3 sur Debian 12 et PBS 4 sur Debian 13.",
"This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Ce package a été installé par les anciennes versions du programme d'installation de ProxMenux Coral qui plaçaient le pilote du noyau M.2 sur chaque système, y compris les configurations USB uniquement. Il n'est pas nécessaire pour les périphériques USB Coral, qui utilisent uniquement libedgetpu1-std / libedgetpu1-max.", "This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Ce package a été installé par les anciennes versions du programme d'installation de ProxMenux Coral qui plaçaient le pilote du noyau M.2 sur chaque système, y compris les configurations USB uniquement. Il n'est pas nécessaire pour les périphériques USB Coral, qui utilisent uniquement libedgetpu1-std / libedgetpu1-max.",
"This passphrase is the ONLY way to access encrypted Borg backups.": "Cette phrase secrète est le SEUL moyen d'accéder aux sauvegardes Borg cryptées.", "This passphrase is the ONLY way to access encrypted Borg backups.": "Cette phrase secrète est le SEUL moyen d'accéder aux sauvegardes Borg cryptées.",
"This path is already used as a mount point in this container.": "Ce chemin est déjà utilisé comme point de montage dans ce conteneur.", "This path is already used as a mount point in this container.": "Ce chemin est déjà utilisé comme point de montage dans ce conteneur.",
@@ -6848,6 +6914,7 @@
"This script cannot be executed from the Proxmox web terminal.": "Ce script ne peut pas être exécuté depuis le terminal web Proxmox.", "This script cannot be executed from the Proxmox web terminal.": "Ce script ne peut pas être exécuté depuis le terminal web Proxmox.",
"This script must be run as root.": "Ce script doit être exécuté en tant que root.", "This script must be run as root.": "Ce script doit être exécuté en tant que root.",
"This script must be run on a Proxmox host.": "Ce script doit être exécuté sur un hôte Proxmox.", "This script must be run on a Proxmox host.": "Ce script doit être exécuté sur un hôte Proxmox.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox Backup Server": "Ce script appliquera les optimisations et réglages avancés suivants à votre Proxmox Backup Server",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Ce script appliquera les optimisations et ajustements avancés suivants à votre serveur Proxmox VE", "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Ce script appliquera les optimisations et ajustements avancés suivants à votre serveur Proxmox VE",
"This script will update your Proxmox VE system with advanced options:": "Ce script mettra à jour votre système Proxmox VE avec des options avancées :", "This script will update your Proxmox VE system with advanced options:": "Ce script mettra à jour votre système Proxmox VE avec des options avancées :",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Cette session est en cours d'exécution dans le terminal Monitor. L’exécuter à partir d’ici couperait la connexion en cours d’installation et laisserait le commutateur dans un état cassé.", "This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Cette session est en cours d'exécution dans le terminal Monitor. L’exécuter à partir d’ici couperait la connexion en cours d’installation et laisserait le commutateur dans un état cassé.",
@@ -6934,7 +7001,7 @@
"Transaction log:": "Registre des transactions & #160;:", "Transaction log:": "Registre des transactions & #160;:",
"Translation files:": "Fichiers de traduction :", "Translation files:": "Fichiers de traduction :",
"Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission est conçu pour une utilisation facile et puissante. Transmission possède les fonctionnalités que vous souhaitez d'un client BitTorrent : cryptage, interface web, échange entre pairs, liens magnétiques, DHT, μTP, UPnP et le transfert de port NAT-PMP, support de webseed, répertoires de veille, édition de tracker, limites de vitesse globales et per-torrent, et plus encore.", "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission est conçu pour une utilisation facile et puissante. Transmission possède les fonctionnalités que vous souhaitez d'un client BitTorrent : cryptage, interface web, échange entre pairs, liens magnétiques, DHT, μTP, UPnP et le transfert de port NAT-PMP, support de webseed, répertoires de veille, édition de tracker, limites de vitesse globales et per-torrent, et plus encore.",
"Tried pvesm path and manual detection methods": "Chemin pvesm essayé et méthodes de détection manuelle", "Tried pvesm path and manual detection methods": "pvesm path et les méthodes de détection manuelle ont été essayés",
"Trust this certificate and save it for scheduled backups?": "Faire confiance à ce certificat et l'enregistrer pour les sauvegardes planifiées ?", "Trust this certificate and save it for scheduled backups?": "Faire confiance à ce certificat et l'enregistrer pour les sauvegardes planifiées ?",
"Try Again": "Essayer à nouveau", "Try Again": "Essayer à nouveau",
"Try a different search term.": "Essayez un autre terme de recherche.", "Try a different search term.": "Essayez un autre terme de recherche.",
@@ -6979,6 +7046,7 @@
"Unable to identify the root filesystem. Aborting.": "Impossible d'identifier le système de fichiers racine. Avorter.", "Unable to identify the root filesystem. Aborting.": "Impossible d'identifier le système de fichiers racine. Avorter.",
"Unable to resolve system ZFS pool disks. Aborting.": "Impossible de résoudre les disques du pool système ZFS.Avorter.", "Unable to resolve system ZFS pool disks. Aborting.": "Impossible de résoudre les disques du pool système ZFS.Avorter.",
"Unable to resolve system disk topology. Aborting.": "Impossible de résoudre la topologie du disque système. Avorter.", "Unable to resolve system disk topology. Aborting.": "Impossible de résoudre la topologie du disque système. Avorter.",
"Unapplied changes": "Modifications non appliquées",
"Understand the security implications of privileged containers": "Comprendre les implications de sécurité des conteneurs privilégiés", "Understand the security implications of privileged containers": "Comprendre les implications de sécurité des conteneurs privilégiés",
"Unexpected Proxmox inventory; recovery blocked": "stocks imprévus Proxmox; récupération bloquée", "Unexpected Proxmox inventory; recovery blocked": "stocks imprévus Proxmox; récupération bloquée",
"Unexpected formatting directory in the new Valkey volume": "Répertoire de formatage inattendu dans le nouveau volume Valkey", "Unexpected formatting directory in the new Valkey volume": "Répertoire de formatage inattendu dans le nouveau volume Valkey",
@@ -7078,6 +7146,7 @@
"Unsupported OCI-LXC AppArmor profile:": "Profil OCI-LXC AppArmor non soutenu :", "Unsupported OCI-LXC AppArmor profile:": "Profil OCI-LXC AppArmor non soutenu :",
"Unsupported OCI-LXC seccomp profile:": "Profil de seccomp OCI-LXC non pris en charge:", "Unsupported OCI-LXC seccomp profile:": "Profil de seccomp OCI-LXC non pris en charge:",
"Unsupported Terminal": "Terminal non pris en charge", "Unsupported Terminal": "Terminal non pris en charge",
"Unsupported architecture for Figurine:": "Architecture non prise en charge pour Figurine :",
"Unsupported architecture:": "Architecture non supportée :", "Unsupported architecture:": "Architecture non supportée :",
"Unsupported backup compression": "compression de sauvegarde non prise en charge", "Unsupported backup compression": "compression de sauvegarde non prise en charge",
"Unsupported credential pattern:": "Modèle credential non pris en charge:", "Unsupported credential pattern:": "Modèle credential non pris en charge:",
@@ -7093,6 +7162,7 @@
"Unsupported mount type": "Type de montage non supporté", "Unsupported mount type": "Type de montage non supporté",
"Unsupported mount type:": "Type de montage non pris en charge:", "Unsupported mount type:": "Type de montage non pris en charge:",
"Unsupported native device type:": "Type de périphérique natif non pris en charge:", "Unsupported native device type:": "Type de périphérique natif non pris en charge:",
"Unsupported network settings": "Paramètres réseau non pris en charge",
"Unsupported operation": "operation non prise en charge", "Unsupported operation": "operation non prise en charge",
"Unsupported or unknown Proxmox version; no repository changed.": "Version Proxmox non prise en charge ou inconnue ; aucun référentiel n'a été modifié.", "Unsupported or unknown Proxmox version; no repository changed.": "Version Proxmox non prise en charge ou inconnue ; aucun référentiel n'a été modifié.",
"Unsupported output format:": "Format de sortie non pris en charge :", "Unsupported output format:": "Format de sortie non pris en charge :",
@@ -7120,6 +7190,7 @@
"Update NVIDIA in LXC Containers": "Mettre à jour NVIDIA dans les conteneurs LXC", "Update NVIDIA in LXC Containers": "Mettre à jour NVIDIA dans les conteneurs LXC",
"Update OCI": "Mettre à jour OCI", "Update OCI": "Mettre à jour OCI",
"Update OCI stack": "Mettre à jour la pile OCI", "Update OCI stack": "Mettre à jour la pile OCI",
"Update PBS using configured repositories": "Mettre à jour PBS avec les dépôts configurés",
"Update PVE enterprise repository (Only if using enterprise):": "Mettre à jour le référentiel d'entreprise PVE (uniquement si vous utilisez l'entreprise) :", "Update PVE enterprise repository (Only if using enterprise):": "Mettre à jour le référentiel d'entreprise PVE (uniquement si vous utilisez l'entreprise) :",
"Update Proxmox VE Appliance Manager": "Mettre à jour le gestionnaire d'appliances Proxmox VE", "Update Proxmox VE Appliance Manager": "Mettre à jour le gestionnaire d'appliances Proxmox VE",
"Update Proxmox package lists": "Mettre à jour les listes de packages Proxmox", "Update Proxmox package lists": "Mettre à jour les listes de packages Proxmox",
@@ -7172,6 +7243,7 @@
"Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Assistant de mise à niveau : Proxmox VE 8 → 9 (Trixie)", "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Assistant de mise à niveau : Proxmox VE 8 → 9 (Trixie)",
"Upgrade canceled by user": "Mise à niveau annulée par l'utilisateur", "Upgrade canceled by user": "Mise à niveau annulée par l'utilisateur",
"Upgrade one node at a time": "Mettre à niveau un nœud à la fois", "Upgrade one node at a time": "Mettre à niveau un nœud à la fois",
"Upgrade the system, offering the": "mettre à niveau le système, en offrant le",
"Upgrade to Proxmox VE 9": "Mise à niveau vers Proxmox VE 9", "Upgrade to Proxmox VE 9": "Mise à niveau vers Proxmox VE 9",
"Upgrading NVIDIA utils (Arch)...": "Mise à niveau des utilitaires NVIDIA (Arch)...", "Upgrading NVIDIA utils (Arch)...": "Mise à niveau des utilitaires NVIDIA (Arch)...",
"Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Télécharger une copie cryptée de la clé sur PBS afin de pouvoir la récupérer sur un hôte réinstallé avec juste une phrase secrète ?", "Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Télécharger une copie cryptée de la clé sur PBS afin de pouvoir la récupérer sur un hôte réinstallé avec juste une phrase secrète ?",
@@ -7185,7 +7257,7 @@
"Usage:": "Utilisation :", "Usage:": "Utilisation :",
"Use \"Check test progress\" to see results.": "Utilisez « Vérifier la progression du test » pour voir les résultats.", "Use \"Check test progress\" to see results.": "Utilisez « Vérifier la progression du test » pour voir les résultats.",
"Use 'Export to file' to save it and inspect manually.": "Utilisez « Exporter vers un fichier » pour l'enregistrer et l'inspecter manuellement.", "Use 'Export to file' to save it and inspect manually.": "Utilisez « Exporter vers un fichier » pour l'enregistrer et l'inspecter manuellement.",
"Use 'pct restore' / 'qmrestore' to recover their disks from your VM backups.": "Utilisez « PCT Restore » / « qmrestore » pour récupérer leurs disques à partir des sauvegardes de votre VM.", "Use 'pct restore' / 'qmrestore' to recover their disks from your VM backups.": "Utilisez 'pct restore' / 'qmrestore' pour récupérer leurs disques depuis les sauvegardes des VM.",
"Use Custom backup and uncheck the conflicting path from the list": "Utilisez la sauvegarde personnalisée et décochez le chemin en conflit dans la liste", "Use Custom backup and uncheck the conflicting path from the list": "Utilisez la sauvegarde personnalisée et décochez le chemin en conflit dans la liste",
"Use Default Settings?": "Utiliser les paramètres par défaut ?", "Use Default Settings?": "Utiliser les paramètres par défaut ?",
"Use Download first if you want to save a copy of the current key. Continue?": "utilisez d'abord Télécharger si vous souhaitez enregistrer une copie de la clé actuelle. Continuer?", "Use Download first if you want to save a copy of the current key. Continue?": "utilisez d'abord Télécharger si vous souhaitez enregistrer une copie de la clé actuelle. Continuer?",
@@ -7609,6 +7681,7 @@
"active VF(s)": "FV active(s)", "active VF(s)": "FV active(s)",
"active VFs": "VF actives", "active VFs": "VF actives",
"active Virtual Functions. Changing its driver binding would destroy every VF.": "Fonctions virtuelles actives. Changer la liaison de son pilote détruirait chaque VF.", "active Virtual Functions. Changing its driver binding would destroy every VF.": "Fonctions virtuelles actives. Changer la liaison de son pilote détruirait chaque VF.",
"added in Proxmox": "ajoutée dans Proxmox",
"after copy; source tree is incomplete.": "après copie ; l'arborescence des sources est incomplète.", "after copy; source tree is incomplete.": "après copie ; l'arborescence des sources est incomplète.",
"after update.": "après mise à jour.", "after update.": "après mise à jour.",
"all guests": "tous les invités", "all guests": "tous les invités",
@@ -7929,8 +8002,8 @@
"partition(s). Partition table preserved.": "partition(s). Table de partition conservée.", "partition(s). Partition table preserved.": "partition(s). Table de partition conservée.",
"pasted Compose file": "fichier Compose collé", "pasted Compose file": "fichier Compose collé",
"paths for next boot (/etc/pve, guests, drivers, ...)": "chemins pour le prochain démarrage (/etc/pve, invités, pilotes, ...)", "paths for next boot (/etc/pve, guests, drivers, ...)": "chemins pour le prochain démarrage (/etc/pve, invités, pilotes, ...)",
"pct exec authorization failed": "échec de l'autorisation d'exécution PCT", "pct exec authorization failed": "Échec de l’autorisation pour pct exec",
"pct push failed. Check log:": "La poussée PCT a échoué. Journal de vérification :", "pct push failed. Check log:": "Échec de pct push. Consultez le journal :",
"pending (reboot required to enumerate full group)": "en attente (redémarrage requis pour énumérer le groupe complet)", "pending (reboot required to enumerate full group)": "en attente (redémarrage requis pour énumérer le groupe complet)",
"phpMyAdmin is installed with arbitrary server connections enabled: the login page has a Server field where the address of the MySQL or MariaDB server is entered, together with its user and password.": "phpMyAdmin est installé avec des connexions de serveur arbitraires activées : la page de connexion a un champ Serveur où l'adresse du serveur MySQL ou MariaDB est saisie, ainsi que son utilisateur et son mot de passe.", "phpMyAdmin is installed with arbitrary server connections enabled: the login page has a Server field where the address of the MySQL or MariaDB server is entered, together with its user and password.": "phpMyAdmin est installé avec des connexions de serveur arbitraires activées : la page de connexion a un champ Serveur où l'adresse du serveur MySQL ou MariaDB est saisie, ainsi que son utilisateur et son mot de passe.",
"pigz configuration completed": "configuration pigz terminée", "pigz configuration completed": "configuration pigz terminée",
@@ -7953,8 +8026,8 @@
"pvesm command not found. This should not happen on Proxmox.": "Commande pvesm introuvable. Cela ne devrait pas arriver sur Proxmox.", "pvesm command not found. This should not happen on Proxmox.": "Commande pvesm introuvable. Cela ne devrait pas arriver sur Proxmox.",
"pvesm not available.": "pvesm non disponible.", "pvesm not available.": "pvesm non disponible.",
"pvesm not found.": "pvesm introuvable.", "pvesm not found.": "pvesm introuvable.",
"pvesm path failed, trying manual detection...": "Échec du chemin pvesm, tentative de détection manuelle...", "pvesm path failed, trying manual detection...": "Échec de pvesm path, tentative de détection manuelle...",
"pvesm status failed": "le statut pvesm a échoué", "pvesm status failed": "Échec de pvesm status",
"pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad est un gestionnaire de téléchargement gratuit et Open Source écrit en Python et conçu pour être extrêmement léger, facilement extensible et entièrement gérable via le web.", "pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad est un gestionnaire de téléchargement gratuit et Open Source écrit en Python et conçu pour être extrêmement léger, facilement extensible et entièrement gérable via le web.",
"pyLoad web interface": "PyLoad interface web", "pyLoad web interface": "PyLoad interface web",
"qBittorrent WebUI password (user: admin)": "qBittorrent Mot de passe WebUI (utilisateur: admin)", "qBittorrent WebUI password (user: admin)": "qBittorrent Mot de passe WebUI (utilisateur: admin)",
@@ -7976,6 +8049,7 @@
"remapped users": "utilisateurs remappés", "remapped users": "utilisateurs remappés",
"remove the (now-empty) directory if possible": "supprimez le répertoire (maintenant vide) si possible", "remove the (now-empty) directory if possible": "supprimez le répertoire (maintenant vide) si possible",
"removed from Proxmox": "supprimé de Proxmox", "removed from Proxmox": "supprimé de Proxmox",
"removed in Proxmox": "supprimée dans Proxmox",
"removed successfully from Proxmox.": "supprimé avec succès de Proxmox.", "removed successfully from Proxmox.": "supprimé avec succès de Proxmox.",
"requires running the official installer, which restarts the Monitor service.": "nécessite l'exécution du programme d'installation officiel, qui redémarre le service Monitor.", "requires running the official installer, which restarts the Monitor service.": "nécessite l'exécution du programme d'installation officiel, qui redémarre le service Monitor.",
"requires the package": "nécessite le paquet", "requires the package": "nécessite le paquet",
@@ -8032,7 +8106,7 @@
"switch_gpu_mode.sh was not found.": "switch_gpu_mode.sh n'a pas été trouvé.", "switch_gpu_mode.sh was not found.": "switch_gpu_mode.sh n'a pas été trouvé.",
"sysfs ROM dump failed — trying ACPI VFCT table...": "Échec du vidage de la ROM sysfs - tentative de la table ACPI VFCT...", "sysfs ROM dump failed — trying ACPI VFCT table...": "Échec du vidage de la ROM sysfs - tentative de la table ACPI VFCT...",
"syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "syslog-ng vous permet de collecter, d'analyser, de classer, de réécrire et de corréler les logs de toute votre infrastructure et de les stocker ou de les acheminer vers des outils d'analyse de log.", "syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "syslog-ng vous permet de collecter, d'analyser, de classer, de réécrire et de corréler les logs de toute votre infrastructure et de les stocker ou de les acheminer vers des outils d'analyse de log.",
"systemctl restart networking failed:": "échec du redémarrage du réseau systemctl :", "systemctl restart networking failed:": "Échec de systemctl restart networking :",
"systemd OnCalendar expression": "expression systemd OnCalendar", "systemd OnCalendar expression": "expression systemd OnCalendar",
"the API key was not generated on the first start": "la clé API n'a pas été générée au premier démarrage", "the API key was not generated on the first start": "la clé API n'a pas été générée au premier démarrage",
"the NVIDIA hook of the container is not available:": "le hook NVIDIA du conteneur n'est pas disponible :", "the NVIDIA hook of the container is not available:": "le hook NVIDIA du conteneur n'est pas disponible :",
@@ -8079,18 +8153,20 @@
"using": "en utilisant", "using": "en utilisant",
"using direct path (not guaranteed to survive reboots).": "en utilisant un chemin direct (il n'est pas garanti de survivre aux redémarrages).", "using direct path (not guaranteed to survive reboots).": "en utilisant un chemin direct (il n'est pas garanti de survivre aux redémarrages).",
"vCPUs:": "Processeurs virtuels :", "vCPUs:": "Processeurs virtuels :",
"value changed in Proxmox": "valeur modifiée dans Proxmox",
"vfio-pci IDs configured": "ID vfio-pci configurés", "vfio-pci IDs configured": "ID vfio-pci configurés",
"vfio-pci IDs in /etc/modprobe.d/vfio.conf": "ID vfio-pci dans /etc/modprobe.d/vfio.conf", "vfio-pci IDs in /etc/modprobe.d/vfio.conf": "ID vfio-pci dans /etc/modprobe.d/vfio.conf",
"video": "vidéo", "video": "vidéo",
"video + recognition": "vidéo + reconnaissance", "video + recognition": "vidéo + reconnaissance",
"video only": "vidéo uniquement", "video only": "vidéo uniquement",
"vzdump backup speed optimization completed": "Optimisation de la vitesse de sauvegarde de vzdump terminée", "vzdump backup speed optimization completed": "Optimisation de la vitesse de sauvegarde de vzdump terminée",
"wallabag builds its links from the address given during the installation. If it does not match the address of the container, edit lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME in /etc/pve/lxc/<CTID>.conf with the container stopped, and start it again.": "wallabag construit ses liens à partir de l'adresse donnée pendant l'installation. S'il ne correspond pas à l'adresse du conteneur, modifier lxc.environnement. temps d'exécution: SYMFONY ENV DOMAIN NAME dans /etc/pve/lxc/<CTID>.conf avec le conteneur arrêté, et recommencer.", "wallabag builds its links from the address given during the installation. If it does not match the address of the container, edit lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME in /etc/pve/lxc/<CTID>.conf with the container stopped, and start it again.": "wallabag construit ses liens avec l’adresse indiquée à l’installation. Si elle ne correspond pas à celle du conteneur, arrêtez-le, modifiez lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME dans /etc/pve/lxc/<CTID>.conf, puis redémarrez-le.",
"wallabag listens on port 80 of the container and stores its data in SQLite.": "wallabag écoute sur le port 80 du conteneur et stocke ses données dans SQLite.", "wallabag listens on port 80 of the container and stores its data in SQLite.": "wallabag écoute sur le port 80 du conteneur et stocke ses données dans SQLite.",
"wallabag web interface": "Interface web wallabag", "wallabag web interface": "Interface web wallabag",
"was": "était", "was": "était",
"was installed, but the kernel reports:": "a été installé, mais le noyau rapporte :", "was installed, but the kernel reports:": "a été installé, mais le noyau rapporte :",
"when finished": "à la fin", "when finished": "à la fin",
"when the server has no subscription (you will be asked)": "lorsque le serveur n'a pas d'abonnement (il vous sera demandé)",
"will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "reliera le GPU à vfio-pci au prochain redémarrage, cassant ainsi le pilote qui est sur le point d'être installé.", "will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "reliera le GPU à vfio-pci au prochain redémarrage, cassant ainsi le pilote qui est sur le point d'être installé.",
"wipefs failed on": "les wipefs ont échoué", "wipefs failed on": "les wipefs ont échoué",
"with": "avec", "with": "avec",
+76
View File
@@ -37,6 +37,7 @@
"/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (percorso vzdump predefinito di Proxmox)", "/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (percorso vzdump predefinito di Proxmox)",
"/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (predefinito di Proxmox)", "/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (predefinito di Proxmox)",
"1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx per tutti. Non è necessario alcun gruppo condiviso.", "1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx per tutti. Non è necessario alcun gruppo condiviso.",
"====== PBS UPDATE COMPLETED ======": "====== AGGIORNAMENTO PBS COMPLETATO ======",
"====== PVE UPDATE COMPLETED ======": "====== AGGIORNAMENTO PVE COMPLETATO ======", "====== PVE UPDATE COMPLETED ======": "====== AGGIORNAMENTO PVE COMPLETATO ======",
">> Create a new backup job <<": ">> Crea un nuovo job di backup <<", ">> Create a new backup job <<": ">> Crea un nuovo job di backup <<",
">> Save and return <<": ">> Salva e torna indietro <<", ">> Save and return <<": ">> Salva e torna indietro <<",
@@ -87,6 +88,7 @@
"A multi-container application is not recreated: its containers are updated together.": "Un’applicazione con più container non viene ricreata: i suoi container vengono aggiornati insieme.", "A multi-container application is not recreated: its containers are updated together.": "Un’applicazione con più container non viene ricreata: i suoi container vengono aggiornati insieme.",
"A new ProxMenux version is available:": "È disponibile una nuova versione di ProxMenux:", "A new ProxMenux version is available:": "È disponibile una nuova versione di ProxMenux:",
"A new kernel is staged for the next boot:": "Un nuovo kernel è predisposto per il prossimo avvio:", "A new kernel is staged for the next boot:": "Un nuovo kernel è predisposto per il prossimo avvio:",
"A newer kernel is installed. Reboot before the next backup window to use it.": "è installato un kernel più recente. Riavviare prima della finestra di backup successiva per utilizzarlo.",
"A newer version is available:": "È disponibile una versione più recente:", "A newer version is available:": "È disponibile una versione più recente:",
"A pending operation exists for": "Operazione in sospeso per il", "A pending operation exists for": "Operazione in sospeso per il",
"A pending stack assembly already exists; it is not overwritten": "Esiste già un assemblaggio dello stack in sospeso; non verrà sovrascritto", "A pending stack assembly already exists; it is not overwritten": "Esiste già un assemblaggio dello stack in sospeso; non verrà sovrascritto",
@@ -169,6 +171,8 @@
"APT language configuration updated": "Configurazione lingue APT aggiornata", "APT language configuration updated": "Configurazione lingue APT aggiornata",
"APT language downloads restored": "Download dei file lingua APT ripristinati", "APT language downloads restored": "Download dei file lingua APT ripristinati",
"APT package lists updated": "Elenchi pacchetti APT aggiornati", "APT package lists updated": "Elenchi pacchetti APT aggiornati",
"APT refresh failed. Check the PBS subscription and repositories in Administration > Repositories. Log:": "aggiornamento APT non riuscito. Controllare l'abbonamento e i repository PBS in Amministrazione > Repository. Tronco d'albero:",
"APT refresh failed. Check the PBS subscription and repositories in the native PBS interface; no source was changed.": "Aggiornamento degli indici APT non riuscito. Controlla l'abbonamento e i repository PBS nell'interfaccia nativa di PBS; nessuna sorgente è stata modificata.",
"APT update failed. Check repository access; NVIDIA installation stopped.": "Aggiornamento APT non riuscito. Controlla l’accesso al repository; installazione NVIDIA interrotta.", "APT update failed. Check repository access; NVIDIA installation stopped.": "Aggiornamento APT non riuscito. Controlla l’accesso al repository; installazione NVIDIA interrotta.",
"Aborted": "Interrotto", "Aborted": "Interrotto",
"About to restore": "Ripristino imminente di", "About to restore": "Ripristino imminente di",
@@ -425,6 +429,7 @@
"Apply": "Applica", "Apply": "Applica",
"Apply AMD CPU fixes": "Applica correzioni CPU AMD", "Apply AMD CPU fixes": "Applica correzioni CPU AMD",
"Apply Available Updates": "Applica aggiornamenti disponibili", "Apply Available Updates": "Applica aggiornamenti disponibili",
"Apply PBS package upgrade": "Applica l'aggiornamento dei pacchetti PBS",
"Apply and restart services:": "Applicare e riavviare i servizi:", "Apply and restart services:": "Applicare e riavviare i servizi:",
"Apply available updates": "Applica aggiornamenti disponibili", "Apply available updates": "Applica aggiornamenti disponibili",
"Apply boot/initramfs changes": "Applica modifiche boot/initramfs", "Apply boot/initramfs changes": "Applica modifiche boot/initramfs",
@@ -438,12 +443,15 @@
"Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Applicare le opzioni dal modello di catalogo corrente? Potrebbero essere richiesti nuovi percorsi e impostazioni obbligatori. Controlla la configurazione risultante prima di ricreare il CT.", "Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Applicare le opzioni dal modello di catalogo corrente? Potrebbero essere richiesti nuovi percorsi e impostazioni obbligatori. Controlla la configurazione risultante prima di ricreare il CT.",
"Apply read+write access for 'others' on the host directory?": "Applicare l'accesso in lettura+scrittura per \"altri\" nella directory host?", "Apply read+write access for 'others' on the host directory?": "Applicare l'accesso in lettura+scrittura per \"altri\" nella directory host?",
"Apply the options from the current catalog template? Your data and configuration are kept.": "Applicare le opzioni dal modello di catalogo corrente? I dati e la configurazione sono conservati.", "Apply the options from the current catalog template? Your data and configuration are kept.": "Applicare le opzioni dal modello di catalogo corrente? I dati e la configurazione sono conservati.",
"Apply the reviewed package upgrade now? Services may restart and packages listed for removal will be removed.": "Applicare ora l'aggiornamento dei pacchetti esaminato? I servizi potrebbero riavviarsi e i pacchetti indicati per la rimozione verranno rimossi.",
"Apply these changes?": "Applicare queste modifiche?", "Apply these changes?": "Applicare queste modifiche?",
"Applying AMD-specific fixes...": "Applicazione delle correzioni specifiche AMD...", "Applying AMD-specific fixes...": "Applicazione delle correzioni specifiche AMD...",
"Applying CPU and RAM changes without rebuilding the container...": "Applicazione delle modifiche a CPU e RAM senza ricreare il container...",
"Applying Changes": "Applicazione delle modifiche", "Applying Changes": "Applicazione delle modifiche",
"Applying Controller/NVMe passthrough to VM": "Applicazione del passthrough Controller/NVMe alla VM", "Applying Controller/NVMe passthrough to VM": "Applicazione del passthrough Controller/NVMe alla VM",
"Applying balanced memory optimization settings...": "Applicazione delle impostazioni bilanciate di ottimizzazione della memoria...", "Applying balanced memory optimization settings...": "Applicazione delle impostazioni bilanciate di ottimizzazione della memoria...",
"Applying changes safely...": "Applicazione sicura delle modifiche...", "Applying changes safely...": "Applicazione sicura delle modifiche...",
"Applying container settings without rebuilding the container...": "Applicazione delle impostazioni senza ricreare il container...",
"Applying custom restore": "Applicazione del ripristino personalizzato", "Applying custom restore": "Applicazione del ripristino personalizzato",
"Applying default VM configuration": "Applicazione della configurazione predefinita della VM", "Applying default VM configuration": "Applicazione della configurazione predefinita della VM",
"Applying host permissions for unprivileged LXC bind-mounts...": "Applicazione delle autorizzazioni sull’host per i bind mount dei container LXC non privilegiati...", "Applying host permissions for unprivileged LXC bind-mounts...": "Applicazione delle autorizzazioni sull’host per i bind mount dei container LXC non privilegiati...",
@@ -535,6 +543,7 @@
"Available Borg targets:": "Destinazioni Borg disponibili:", "Available Borg targets:": "Destinazioni Borg disponibili:",
"Available Disks on Host": "Dischi disponibili sull'host", "Available Disks on Host": "Dischi disponibili sull'host",
"Available ISO Images": "Immagini ISO disponibili", "Available ISO Images": "Immagini ISO disponibili",
"Available PBS Version": "versione PBS disponibile",
"Available PBS repositories:": "Repository PBS disponibili:", "Available PBS repositories:": "Repository PBS disponibili:",
"Available PVE Version": "Versione PVE disponibile", "Available PVE Version": "Versione PVE disponibile",
"Available Shares:": "Condivisioni disponibili:", "Available Shares:": "Condivisioni disponibili:",
@@ -746,6 +755,7 @@
"Cannot verify the reused disk:": "Impossibile verificare il disco riutilizzato:", "Cannot verify the reused disk:": "Impossibile verificare il disco riutilizzato:",
"Cannot write the image archive to {storage}. Check storage access and mount status.": "Impossibile scrivere l'archivio immagini su {storage}. Controlla l'accesso allo spazio di archiviazione e lo stato di montaggio.", "Cannot write the image archive to {storage}. Check storage access and mount status.": "Impossibile scrivere l'archivio immagini su {storage}. Controlla l'accesso allo spazio di archiviazione e lo stato di montaggio.",
"Capabilities cannot be kept and all dropped at the same time": "Non è possibile conservare alcune capability e rimuoverle tutte contemporaneamente", "Capabilities cannot be kept and all dropped at the same time": "Non è possibile conservare alcune capability e rimuoverle tutte contemporaneamente",
"Catalog options": "Opzioni del catalogo",
"Category": "Categoria", "Category": "Categoria",
"Caution: Maximum mode generates more heat.": "Attenzione: la modalità massima genera più calore.", "Caution: Maximum mode generates more heat.": "Attenzione: la modalità massima genera più calore.",
"Ceph check skipped by user flag (--ignore-ceph-check)": "Controllo Ceph saltato dal flag utente (--ignore-ceph-check)", "Ceph check skipped by user flag (--ignore-ceph-check)": "Controllo Ceph saltato dal flag utente (--ignore-ceph-check)",
@@ -880,6 +890,7 @@
"Choose a schedule (systemd calendar-event syntax):": "Scegli una pianificazione (sintassi calendar-event di systemd):", "Choose a schedule (systemd calendar-event syntax):": "Scegli una pianificazione (sintassi calendar-event di systemd):",
"Choose a script to execute:": "Scegli uno script da eseguire:", "Choose a script to execute:": "Scegli uno script da eseguire:",
"Choose a server:": "Scegli un server:", "Choose a server:": "Scegli un server:",
"Choose a setting. Changes are applied only after the final review.": "Scegli cosa configurare. Le modifiche vengono applicate solo dopo la verifica finale.",
"Choose a share to mount:": "Scegli una condivisione da montare:", "Choose a share to mount:": "Scegli una condivisione da montare:",
"Choose a specific supported GPU or USB node": "Scegli uno specifico nodo GPU o USB supportato", "Choose a specific supported GPU or USB node": "Scegli uno specifico nodo GPU o USB supportato",
"Choose action for this controller/NVMe:": "Scegli l'azione per questo controller/NVMe:", "Choose action for this controller/NVMe:": "Scegli l'azione per questo controller/NVMe:",
@@ -960,6 +971,7 @@
"Code-server is VS Code running on a remote server, accessible through the browser.": "Code-server è VS Code in esecuzione su un server remoto, accessibile tramite il browser.", "Code-server is VS Code running on a remote server, accessible through the browser.": "Code-server è VS Code in esecuzione su un server remoto, accessibile tramite il browser.",
"CodeProject.AI Server": "CodeProject.AI Server", "CodeProject.AI Server": "CodeProject.AI Server",
"Command": "Comando", "Command": "Comando",
"Command failed": "Comando non riuscito",
"Command override for an unknown service:": "Override del comando per un servizio sconosciuto:", "Command override for an unknown service:": "Override del comando per un servizio sconosciuto:",
"Commenting any residual Bookworm lines in *.list...": "Disattivazione tramite commento delle eventuali righe residue di Bookworm in *.list…", "Commenting any residual Bookworm lines in *.list...": "Disattivazione tramite commento delle eventuali righe residue di Bookworm in *.list…",
"Commenting legacy PVE 8 repository .list files (if any)...": "Commento dei file .list del repository PVE 8 legacy (se presenti)...", "Commenting legacy PVE 8 repository .list files (if any)...": "Commento dei file .list del repository PVE 8 legacy (se presenti)...",
@@ -1516,6 +1528,7 @@
"Current NVIDIA inventory resolved: a refresh is required": "Inventario NVIDIA attuale acquisito: è necessario un aggiornamento", "Current NVIDIA inventory resolved: a refresh is required": "Inventario NVIDIA attuale acquisito: è necessario un aggiornamento",
"Current NVIDIA inventory resolved: no refresh is required": "Inventario NVIDIA corrente acquisito: non serve aggiornarlo", "Current NVIDIA inventory resolved: no refresh is required": "Inventario NVIDIA corrente acquisito: non serve aggiornarlo",
"Current Network Configuration": "Configurazione di rete corrente", "Current Network Configuration": "Configurazione di rete corrente",
"Current PBS Version": "versione attuale di PBS",
"Current PVE Version": "Versione PVE attuale", "Current PVE Version": "Versione PVE attuale",
"Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Gli attuali script host ProxMenux registrano le condivisioni remote come storage Proxmox con pvesm.", "Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Gli attuali script host ProxMenux registrano le condivisioni remote come storage Proxmox con pvesm.",
"Current ProxMenux version:": "Versione attuale di ProxMenux:", "Current ProxMenux version:": "Versione attuale di ProxMenux:",
@@ -1681,6 +1694,7 @@
"Detected Coral hardware:": "Hardware Coral rilevato:", "Detected Coral hardware:": "Hardware Coral rilevato:",
"Detected GPU(s):": "GPU rilevate:", "Detected GPU(s):": "GPU rilevate:",
"Detected Intel CPU": "Rilevata CPU Intel", "Detected Intel CPU": "Rilevata CPU Intel",
"Detected Proxmox Backup Server": "Proxmox Backup Server rilevato",
"Detected Proxmox VE": "Rilevato Proxmox VE", "Detected Proxmox VE": "Rilevato Proxmox VE",
"Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Rilevato Proxmox VE 8.x - Installazione di Ceph Squid per Debian", "Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Rilevato Proxmox VE 8.x - Installazione di Ceph Squid per Debian",
"Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Rilevato Proxmox VE 9.x - Installazione di Ceph Squid per Debian Trixie", "Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Rilevato Proxmox VE 9.x - Installazione di Ceph Squid per Debian Trixie",
@@ -1762,6 +1776,9 @@
"Disabling and stopping rpcbind service and socket...": "Disabilitazione e arresto del servizio e del socket rpcbind...", "Disabling and stopping rpcbind service and socket...": "Disabilitazione e arresto del servizio e del socket rpcbind...",
"Disabling non-free firmware warnings...": "Disabilitazione degli avvisi sul firmware non libero...", "Disabling non-free firmware warnings...": "Disabilitazione degli avvisi sul firmware non libero...",
"Disabling portmapper/rpcbind for security...": "Disabilitazione di portmapper/rpcbind per sicurezza...", "Disabling portmapper/rpcbind for security...": "Disabilitazione di portmapper/rpcbind per sicurezza...",
"Discard changes": "Scarta le modifiche",
"Discard unapplied changes?": "Scartare le modifiche non applicate?",
"Discover NFS exports on a server": "Individuare le esportazioni NFS di un server",
"Discover targets on portal:": "Scopri target sul portale:", "Discover targets on portal:": "Scopri target sul portale:",
"Discovering iSCSI targets...": "Individuazione dei target iSCSI...", "Discovering iSCSI targets...": "Individuazione dei target iSCSI...",
"Discovery successful": "Scoperta riuscita", "Discovery successful": "Scoperta riuscita",
@@ -1841,6 +1858,7 @@
"Do you want to proceed with the system update?": "Vuoi procedere con l'aggiornamento del sistema?", "Do you want to proceed with the system update?": "Vuoi procedere con l'aggiornamento del sistema?",
"Do you want to proceed?": "Vuoi procedere?", "Do you want to proceed?": "Vuoi procedere?",
"Do you want to reboot now?": "Vuoi riavviare ora?", "Do you want to reboot now?": "Vuoi riavviare ora?",
"Do you want to remove the Proxmox subscription banner from the Proxmox Backup Server web interface?": "Vuoi rimuovere il banner dell'abbonamento Proxmox dall'interfaccia web di Proxmox Backup Server?",
"Do you want to remove the stale entry from vfio.conf and continue?": "Vuoi rimuovere la voce obsoleta da vfio.conf e continuare?", "Do you want to remove the stale entry from vfio.conf and continue?": "Vuoi rimuovere la voce obsoleta da vfio.conf e continuare?",
"Do you want to remove the stale entry from vfio.conf?": "Vuoi rimuovere la voce obsoleta da vfio.conf?", "Do you want to remove the stale entry from vfio.conf?": "Vuoi rimuovere la voce obsoleta da vfio.conf?",
"Do you want to restart the network service now to apply changes?": "Riavviare ora il servizio di rete per applicare le modifiche?", "Do you want to restart the network service now to apply changes?": "Riavviare ora il servizio di rete per applicare le modifiche?",
@@ -2266,6 +2284,7 @@
"Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Impossibile clonare il repository log2ram. Controlla /tmp/log2ram_install.log", "Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Impossibile clonare il repository log2ram. Controlla /tmp/log2ram_install.log",
"Failed to configure EFI disk": "Impossibile configurare il disco EFI", "Failed to configure EFI disk": "Impossibile configurare il disco EFI",
"Failed to configure IOMMU automatically.": "Impossibile configurare IOMMU automaticamente.", "Failed to configure IOMMU automatically.": "Impossibile configurare IOMMU automaticamente.",
"Failed to configure Proxmox Backup Server repositories": "Impossibile configurare i repository di Proxmox Backup Server",
"Failed to configure Proxmox repositories": "Impossibile configurare i repository Proxmox", "Failed to configure Proxmox repositories": "Impossibile configurare i repository Proxmox",
"Failed to configure TPM device in VM": "Impossibile configurare il dispositivo TPM nella VM", "Failed to configure TPM device in VM": "Impossibile configurare il dispositivo TPM nella VM",
"Failed to configure repositories.": "Impossibile configurare i repository.", "Failed to configure repositories.": "Impossibile configurare i repository.",
@@ -2973,6 +2992,7 @@
"Inaccessible device during VM startup": "Dispositivo inaccessibile durante l'avvio della VM", "Inaccessible device during VM startup": "Dispositivo inaccessibile durante l'avvio della VM",
"Inactive": "Inattivo", "Inactive": "Inattivo",
"Inactive (entry in fstab, not currently mounted)": "Inattivo (presente in fstab, attualmente non montato)", "Inactive (entry in fstab, not currently mounted)": "Inattivo (presente in fstab, attualmente non montato)",
"Include these changes in oci-compose?": "Includere queste modifiche in oci-compose?",
"Include these resources in oci-compose?": "Includere queste risorse in oci-compose?", "Include these resources in oci-compose?": "Includere queste risorse in oci-compose?",
"Includes /etc/network (may drop SSH immediately)": "Include /etc/network (potrebbe interrompere immediatamente la connessione SSH)", "Includes /etc/network (may drop SSH immediately)": "Include /etc/network (potrebbe interrompere immediatamente la connessione SSH)",
"Includes /etc/zfs": "Include /etc/zfs", "Includes /etc/zfs": "Include /etc/zfs",
@@ -3209,6 +3229,8 @@
"Invalid ALLOWED_HOSTS value": "Valore ALLOWED_HOSTS non valido", "Invalid ALLOWED_HOSTS value": "Valore ALLOWED_HOSTS non valido",
"Invalid Home Assistant OS check timeout": "Timeout controllo Home Assistant OS non valido", "Invalid Home Assistant OS check timeout": "Timeout controllo Home Assistant OS non valido",
"Invalid ID": "ID non valido", "Invalid ID": "ID non valido",
"Invalid IPv4 address": "Indirizzo IPv4 non valido",
"Invalid IPv4 gateway": "Gateway IPv4 non valido",
"Invalid Intel render path": "Percorso di rendering Intel non valido", "Invalid Intel render path": "Percorso di rendering Intel non valido",
"Invalid Jellyfin path:": "Percorso Jellyfin non valido:", "Invalid Jellyfin path:": "Percorso Jellyfin non valido:",
"Invalid MAC address:": "Indirizzo MAC non valido:", "Invalid MAC address:": "Indirizzo MAC non valido:",
@@ -3241,6 +3263,7 @@
"Invalid administrator email": "Email amministratore non valida", "Invalid administrator email": "Email amministratore non valida",
"Invalid administrator user name": "Nome utente amministratore non valido", "Invalid administrator user name": "Nome utente amministratore non valido",
"Invalid base VMID": "VMID di base non valido", "Invalid base VMID": "VMID di base non valido",
"Invalid bridge": "Bridge non valido",
"Invalid check package:": "Pacchetto controllo non valido:", "Invalid check package:": "Pacchetto controllo non valido:",
"Invalid configuration path:": "Percorso configurazione non valido:", "Invalid configuration path:": "Percorso configurazione non valido:",
"Invalid consume/export volumes": "Volumi di acquisizione/esportazione non validi", "Invalid consume/export volumes": "Volumi di acquisizione/esportazione non validi",
@@ -3304,6 +3327,7 @@
"Invalid remote name": "Nome del remote non valido", "Invalid remote name": "Nome del remote non valido",
"Invalid remote path": "Percorso remoto non valido", "Invalid remote path": "Percorso remoto non valido",
"Invalid repair version:": "Versione di riparazione non valida:", "Invalid repair version:": "Versione di riparazione non valida:",
"Invalid resource value": "Valore della risorsa non valido",
"Invalid resources": "Risorse non valide", "Invalid resources": "Risorse non valide",
"Invalid restored volume path": "Percorso volume ripristinato non valido", "Invalid restored volume path": "Percorso volume ripristinato non valido",
"Invalid running state": "Stato di esecuzione non valido", "Invalid running state": "Stato di esecuzione non valido",
@@ -3322,6 +3346,7 @@
"Invalid stack members": "Membri stack non validi", "Invalid stack members": "Membri stack non validi",
"Invalid stack name": "Nome stack non valido", "Invalid stack name": "Nome stack non valido",
"Invalid stack operation": "Operazione stack non valida", "Invalid stack operation": "Operazione stack non valida",
"Invalid storage": "Archiviazione non valida",
"Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "ID storage non valido. Usa solo lettere, numeri, trattini e trattini bassi.", "Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "ID storage non valido. Usa solo lettere, numeri, trattini e trattini bassi.",
"Invalid storage name": "Nome storage non valido", "Invalid storage name": "Nome storage non valido",
"Invalid suite application": "Applicazione suite non valida", "Invalid suite application": "Applicazione suite non valida",
@@ -4153,6 +4178,7 @@
"No active Proxmox storage accepts OCI template archives.": "Nessuno spazio di archiviazione Proxmox attivo accetta archivi modello OCI.", "No active Proxmox storage accepts OCI template archives.": "Nessuno spazio di archiviazione Proxmox attivo accetta archivi modello OCI.",
"No active exports or showmount not available": "Nessuna esportazione attiva o showmount non disponibile", "No active exports or showmount not available": "Nessuna esportazione attiva o showmount non disponibile",
"No active session": "Nessuna sessione attiva", "No active session": "Nessuna sessione attiva",
"No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.": "nessun abbonamento attivo e nessun repository PBS utilizzabile. La modalità non interattiva non può modificare le origini APT;configurarli in Amministrazione > Repository.",
"No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Nessun abbonamento attivo e nessun repository PVE utilizzabile. La modalità non interattiva non può modificare le origini APT; configurarle in Nodo > Aggiornamenti > Repository.", "No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Nessun abbonamento attivo e nessun repository PVE utilizzabile. La modalità non interattiva non può modificare le origini APT; configurarle in Nodo > Aggiornamenti > Repository.",
"No additional GPU can be added.": "Non è possibile aggiungere alcuna GPU aggiuntiva.", "No additional GPU can be added.": "Non è possibile aggiungere alcuna GPU aggiuntiva.",
"No additional device needs to be added.": "Non è necessario aggiungere alcun dispositivo aggiuntivo.", "No additional device needs to be added.": "Non è necessario aggiungere alcun dispositivo aggiuntivo.",
@@ -4257,6 +4283,7 @@
"No password provided.": "Nessuna password fornita.", "No password provided.": "Nessuna password fornita.",
"No paths available": "Nessun percorso disponibile", "No paths available": "Nessun percorso disponibile",
"No paths selected. Select at least one path.": "Nessun percorso selezionato. Seleziona almeno un percorso.", "No paths selected. Select at least one path.": "Nessun percorso selezionato. Seleziona almeno un percorso.",
"No pending changes": "Nessuna modifica in sospeso",
"No pending paths to schedule for reboot.": "Nessun percorso in sospeso da pianificare per il riavvio.", "No pending paths to schedule for reboot.": "Nessun percorso in sospeso da pianificare per il riavvio.",
"No permanent NFS mounts configured.": "Nessun montaggio NFS permanente configurato.", "No permanent NFS mounts configured.": "Nessun montaggio NFS permanente configurato.",
"No permanent Samba mounts configured.": "Nessun montaggio Samba permanente configurato.", "No permanent Samba mounts configured.": "Nessun montaggio Samba permanente configurato.",
@@ -4398,6 +4425,7 @@
"OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM rimosso (gli artefatti di Puppet potrebbero richiedere una pulizia manuale)", "OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM rimosso (gli artefatti di Puppet potrebbero richiedere una pulizia manuale)",
"OVH server detected": "Rilevato server OVH", "OVH server detected": "Rilevato server OVH",
"OVH server detection and RTM installation process completed": "Processo di rilevamento dei server OVH e installazione RTM completato", "OVH server detection and RTM installation process completed": "Processo di rilevamento dei server OVH e installazione RTM completato",
"Object detector": "Rilevatore di oggetti",
"Object detector for Frigate": "Rilevatore di oggetti per Frigate", "Object detector for Frigate": "Rilevatore di oggetti per Frigate",
"Observer is not responding on port 4357": "Observer non risponde sulla porta 4357", "Observer is not responding on port 4357": "Observer non risponde sulla porta 4357",
"Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian è un’app per creare, collegare e organizzare le note sul tuo dispositivo, con centinaia di plugin e temi per personalizzare il flusso di lavoro. Puoi pubblicare le note online, consultarle offline e sincronizzarle in modo sicuro con crittografia end-to-end.", "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian è un’app per creare, collegare e organizzare le note sul tuo dispositivo, con centinaia di plugin e temi per personalizzare il flusso di lavoro. Puoi pubblicare le note online, consultarle offline e sincronizzarle in modo sicuro con crittografia end-to-end.",
@@ -4523,11 +4551,17 @@
"PBS backup error log": "Registro errori backup PBS", "PBS backup error log": "Registro errori backup PBS",
"PBS backup failed.": "Backup PBS non riuscito.", "PBS backup failed.": "Backup PBS non riuscito.",
"PBS certificate": "Certificato PBS", "PBS certificate": "Certificato PBS",
"PBS datastores": "Datastore PBS",
"PBS encryption keyfile (show / replace / remove)": "File chiave di crittografia PBS (mostra/sostituisci/rimuovi)", "PBS encryption keyfile (show / replace / remove)": "File chiave di crittografia PBS (mostra/sostituisci/rimuovi)",
"PBS encryption keyfile — detailed info": "File chiave di crittografia PBS: dettagli", "PBS encryption keyfile — detailed info": "File chiave di crittografia PBS: dettagli",
"PBS extraction failed": "Estrazione PBS non riuscita", "PBS extraction failed": "Estrazione PBS non riuscita",
"PBS host or IP address:": "Host PBS o indirizzo IP:", "PBS host or IP address:": "Host PBS o indirizzo IP:",
"PBS package update": "Aggiornamento dei pacchetti PBS",
"PBS package upgrade completed. Check whether a reboot is required before your next backup window.": "Aggiornamento dei pacchetti PBS completato. Verifica se è necessario un riavvio prima della prossima finestra di backup.",
"PBS package upgrade failed. Review the APT output before retrying.": "Aggiornamento dei pacchetti PBS non riuscito. Esamina l'output di APT prima di riprovare.",
"PBS upload": "Caricamento PBS", "PBS upload": "Caricamento PBS",
"PBS version and packages": "Versione e pacchetti PBS",
"PBS: using the configured APT repositories without changing sources.": "PBS: vengono usati i repository APT configurati senza modificare le sorgenti.",
"PCI Address": "Indirizzo PCI", "PCI Address": "Indirizzo PCI",
"PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "Passthrough PCI, stato TPM, configurazione cloud-init, hook Proxmox", "PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "Passthrough PCI, stato TPM, configurazione cloud-init, hook Proxmox",
"PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "Passthrough PCI, stato TPM, cloud-init, snapshot, hook specifici di Proxmox", "PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "Passthrough PCI, stato TPM, cloud-init, snapshot, hook specifici di Proxmox",
@@ -4864,12 +4898,17 @@
"Proxmology logo applied": "Logo Proxmology applicato", "Proxmology logo applied": "Logo Proxmology applicato",
"Proxmox 9 system update allready": "Il sistema è già aggiornato a Proxmox 9", "Proxmox 9 system update allready": "Il sistema è già aggiornato a Proxmox 9",
"Proxmox APT repositories configured": "Repository APT Proxmox configurati", "Proxmox APT repositories configured": "Repository APT Proxmox configurati",
"Proxmox Backup Server": "Proxmox Backup Server",
"Proxmox Backup Server (PBS) destinations": "Destinazioni Proxmox Backup Server (PBS)", "Proxmox Backup Server (PBS) destinations": "Destinazioni Proxmox Backup Server (PBS)",
"Proxmox Backup Server APT repositories configured": "Repository APT di Proxmox Backup Server configurati",
"Proxmox Backup Server Update": "Aggiornamento di Proxmox Backup Server",
"Proxmox Backup Server update completed.": "Aggiornamento di Proxmox Backup Server completato.",
"Proxmox CIFS Storage Status:": "Stato storage CIFS Proxmox:", "Proxmox CIFS Storage Status:": "Stato storage CIFS Proxmox:",
"Proxmox NFS Storage Status:": "Stato storage NFS Proxmox:", "Proxmox NFS Storage Status:": "Stato storage NFS Proxmox:",
"Proxmox System Update": "Aggiornamento sistema Proxmox", "Proxmox System Update": "Aggiornamento sistema Proxmox",
"Proxmox Update": "Aggiornamento Proxmox", "Proxmox Update": "Aggiornamento Proxmox",
"Proxmox VE": "Proxmox VE", "Proxmox VE": "Proxmox VE",
"Proxmox VE 8 package upgrade failed. Review the APT output before retrying.": "L’aggiornamento dei pacchetti di Proxmox VE 8 non è riuscito. Controlla l’output di APT prima di riprovare.",
"Proxmox VE 8 system update completed successfully": "Aggiornamento sistema Proxmox VE 8 completato", "Proxmox VE 8 system update completed successfully": "Aggiornamento sistema Proxmox VE 8 completato",
"Proxmox VE 8 to 9 Manual Upgrade Guide": "Guida aggiornamento manuale Proxmox VE da 8 a 9", "Proxmox VE 8 to 9 Manual Upgrade Guide": "Guida aggiornamento manuale Proxmox VE da 8 a 9",
"Proxmox VE 9.x no-subscription repository created": "Repository senza abbonamento Proxmox VE 9.x creato", "Proxmox VE 9.x no-subscription repository created": "Repository senza abbonamento Proxmox VE 9.x creato",
@@ -4877,6 +4916,7 @@
"Proxmox VE configuration completed.": "Configurazione Proxmox VE completata.", "Proxmox VE configuration completed.": "Configurazione Proxmox VE completata.",
"Proxmox VE safe update completed": "Aggiornamento sicuro di Proxmox VE completato", "Proxmox VE safe update completed": "Aggiornamento sicuro di Proxmox VE completato",
"Proxmox auth logger service created and started": "Servizio log autenticazione Proxmox creato e avviato", "Proxmox auth logger service created and started": "Servizio log autenticazione Proxmox creato e avviato",
"Proxmox could not apply the container settings": "Proxmox non è riuscito ad applicare le impostazioni del container",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Il repository enterprise Proxmox potrebbe essere mancante o inaccessibile. Tentativo di passare al repository no-subscription...", "Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Il repository enterprise Proxmox potrebbe essere mancante o inaccessibile. Tentativo di passare al repository no-subscription...",
"Proxmox filter configured": "Filtro Proxmox configurato", "Proxmox filter configured": "Filtro Proxmox configurato",
"Proxmox iSCSI Storage Status:": "Stato storage iSCSI Proxmox:", "Proxmox iSCSI Storage Status:": "Stato storage iSCSI Proxmox:",
@@ -4993,6 +5033,7 @@
"Rebuilding initramfs to apply nouveau blacklist before installation...": "Ricostruzione di initramfs per applicare la blacklist di nouveau prima dell'installazione...", "Rebuilding initramfs to apply nouveau blacklist before installation...": "Ricostruzione di initramfs per applicare la blacklist di nouveau prima dell'installazione...",
"Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "La ricostruzione del container e il ripristino dal backup sono stati testati con la stessa immagine; il primo aggiornamento a un'immagine più recente resta da verificare.", "Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "La ricostruzione del container e il ripristino dal backup sono stati testati con la stessa immagine; il primo aggiornamento a un'immagine più recente resta da verificare.",
"Recent": "Recente", "Recent": "Recente",
"Recent PBS tasks": "Attività PBS recenti",
"Recent Samba Servers": "Server Samba recenti", "Recent Samba Servers": "Server Samba recenti",
"Recent Samba server": "Server Samba recente", "Recent Samba server": "Server Samba recente",
"Recent logs:": "Registri recenti:", "Recent logs:": "Registri recenti:",
@@ -5052,6 +5093,7 @@
"Recreation completed. Data kept.": "Ricreazione completata. Dati conservati.", "Recreation completed. Data kept.": "Ricreazione completata. Dati conservati.",
"Recreation prepared": "Ricreazione preparata", "Recreation prepared": "Ricreazione preparata",
"Refresh APT index and verify repositories:": "Aggiorna l'indice APT e verifica i repository:", "Refresh APT index and verify repositories:": "Aggiorna l'indice APT e verifica i repository:",
"Refresh the configured APT repositories and review the PBS package upgrade? Repository files will be preserved.": "Aggiornare gli indici dei repository APT configurati ed esaminare l'aggiornamento dei pacchetti PBS? I file dei repository verranno conservati.",
"Refresh token of the connector, from the Twingate admin console": "Token di refresh del connettore, dalla console di amministrazione Twingate", "Refresh token of the connector, from the Twingate admin console": "Token di refresh del connettore, dalla console di amministrazione Twingate",
"Refresh your browser (Ctrl+Shift+R) to see changes": "Aggiorna il browser (Ctrl+Shift+R) per vedere le modifiche", "Refresh your browser (Ctrl+Shift+R) to see changes": "Aggiorna il browser (Ctrl+Shift+R) per vedere le modifiche",
"Refresh your browser to see changes (server restart may be required)": "Aggiorna il browser per vedere le modifiche (potrebbe essere necessario il riavvio del server)", "Refresh your browser to see changes (server restart may be required)": "Aggiorna il browser per vedere le modifiche (potrebbe essere necessario il riavvio del server)",
@@ -5141,6 +5183,8 @@
"Remove saved credentials": "Rimuovi le credenziali salvate", "Remove saved credentials": "Rimuovi le credenziali salvate",
"Remove subscription banner": "Rimuovi il banner di abbonamento", "Remove subscription banner": "Rimuovi il banner di abbonamento",
"Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Rimuovere l’applicazione? I dischi dei suoi container vengono eliminati e solo un backup può ripristinarli.", "Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Rimuovere l’applicazione? I dischi dei suoi container vengono eliminati e solo un backup può ripristinarli.",
"Remove the lock left on a VM by an interrupted task": "Rimuovere il blocco lasciato su una VM da un’attività interrotta",
"Remove the lock left on a container by an interrupted task": "Rimuovere il blocco lasciato su un container da un’attività interrotta",
"Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Rimuovere le credenziali salvate per {registry}?\nLe app in esecuzione continueranno a funzionare.", "Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Rimuovere le credenziali salvate per {registry}?\nLe app in esecuzione continueranno a funzionare.",
"Remove the unprivileged flag from configuration:": "Rimuovi il flag unprivileged dalla configurazione:", "Remove the unprivileged flag from configuration:": "Rimuovi il flag unprivileged dalla configurazione:",
"Remove unused packages and their config": "Rimuovi i pacchetti non utilizzati e la loro configurazione", "Remove unused packages and their config": "Rimuovi i pacchetti non utilizzati e la loro configurazione",
@@ -5222,6 +5266,7 @@
"Repository helper unavailable. NVIDIA installation stopped.": "Funzione di gestione dei repository non disponibile. L’installazione di NVIDIA è stata interrotta.", "Repository helper unavailable. NVIDIA installation stopped.": "Funzione di gestione dei repository non disponibile. L’installazione di NVIDIA è stata interrotta.",
"Repository policy returned an unexpected result.": "La policy del repository ha restituito un risultato imprevisto.", "Repository policy returned an unexpected result.": "La policy del repository ha restituito un risultato imprevisto.",
"Repository ready.": "Repository pronto.", "Repository ready.": "Repository pronto.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.": "Le sorgenti dei repository sono cambiate, ma l'aggiornamento dell'elenco dei pacchetti APT non è riuscito. Operazione interrotta; le sorgenti non sono state ripristinate. Controlla Administration > Repositories e riesegui apt-get update prima di continuare.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Le origini del repository sono cambiate, ma l'aggiornamento dell'elenco dei pacchetti APT non è riuscito. Operazione interrotta; le origini non sono state ripristinate. Controlla Nodo > Aggiornamenti > Repository e riprova apt-get update prima di continuare.", "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Le origini del repository sono cambiate, ma l'aggiornamento dell'elenco dei pacchetti APT non è riuscito. Operazione interrotta; le origini non sono state ripristinate. Controlla Nodo > Aggiornamenti > Repository e riprova apt-get update prima di continuare.",
"Repository switch declined; no APT source changed.": "Cambio di repository rifiutato; nessuna origine APT è stata modificata.", "Repository switch declined; no APT source changed.": "Cambio di repository rifiutato; nessuna origine APT è stata modificata.",
"Repository:": "Repository:", "Repository:": "Repository:",
@@ -5316,7 +5361,9 @@
"Reverting ZFS ARC tuning...": "Rimozione delle ottimizzazioni di ZFS ARC…", "Reverting ZFS ARC tuning...": "Rimozione delle ottimizzazioni di ZFS ARC…",
"Reverting pigz wrapper...": "Rimozione del wrapper pigz e ripristino della configurazione precedente...", "Reverting pigz wrapper...": "Rimozione del wrapper pigz e ripristino della configurazione precedente...",
"Reverting vzdump speed tuning...": "Rimozione ottimizzazioni velocità vzdump…", "Reverting vzdump speed tuning...": "Rimozione ottimizzazioni velocità vzdump…",
"Review and apply": "Verifica e applica",
"Review external OCI changes": "Esamina le modifiche OCI esterne", "Review external OCI changes": "Esamina le modifiche OCI esterne",
"Review package upgrade": "Esamina l'aggiornamento dei pacchetti",
"Review passthrough config files": "Esamina i file di configurazione passthrough", "Review passthrough config files": "Esamina i file di configurazione passthrough",
"Review what will be removed": "Controlla cosa verrà rimosso", "Review what will be removed": "Controlla cosa verrà rimosso",
"Rip DVD and Blu-ray media from a browser": "Estrai contenuti da DVD e Blu-ray tramite browser", "Rip DVD and Blu-ray media from a browser": "Estrai contenuti da DVD e Blu-ray tramite browser",
@@ -5359,6 +5406,7 @@
"Run the following inside the VM:": "Esegui quanto segue nella VM:", "Run the following inside the VM:": "Esegui quanto segue nella VM:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "Esegui il cambio del canale di rilascio da una sessione SSH o dalla console dell’host Proxmox con:", "Run the release-channel switch from an SSH session or the Proxmox host console with:": "Esegui il cambio del canale di rilascio da una sessione SSH o dalla console dell’host Proxmox con:",
"Run the update from an SSH session or the Proxmox host console with:": "Esegui l’aggiornamento da una sessione SSH o dalla console dell’host Proxmox con:", "Run the update from an SSH session or the Proxmox host console with:": "Esegui l’aggiornamento da una sessione SSH o dalla console dell’host Proxmox con:",
"Run this operation as root.": "Esegui questa operazione come root.",
"Run upgrade checklist script:": "Esegui lo script di controllo dell’aggiornamento:", "Run upgrade checklist script:": "Esegui lo script di controllo dell’aggiornamento:",
"Running": "In esecuzione", "Running": "In esecuzione",
"Running Lynis security audit...": "Esecuzione controllo di sicurezza Lynis...", "Running Lynis security audit...": "Esecuzione controllo di sicurezza Lynis...",
@@ -5773,6 +5821,7 @@
"ShareCoveX panel": "Pannello ShareCoveX", "ShareCoveX panel": "Pannello ShareCoveX",
"Shared Directory Ready:": "Directory condivisa pronta:", "Shared Directory Ready:": "Directory condivisa pronta:",
"Shared Group": "Gruppo condiviso", "Shared Group": "Gruppo condiviso",
"Shared directories": "Cartelle condivise",
"Shared directory created:": "Directory condivisa creata:", "Shared directory created:": "Directory condivisa creata:",
"Shared directory for consume and export": "Directory condivisa per consume ed export", "Shared directory for consume and export": "Directory condivisa per consume ed export",
"Shared directory for copy/sync operations": "Directory condivisa per copia e sincronizzazione", "Shared directory for copy/sync operations": "Directory condivisa per copia e sincronizzazione",
@@ -5802,6 +5851,7 @@
"Should show pve-manager/9.x.x": "Dovrebbe mostrare pve-manager/9.x.x", "Should show pve-manager/9.x.x": "Dovrebbe mostrare pve-manager/9.x.x",
"Shoutrrr addresses for the notifications (optional)": "Indirizzi Shoutrrr per le notifiche (facoltativi)", "Shoutrrr addresses for the notifications (optional)": "Indirizzi Shoutrrr per le notifiche (facoltativi)",
"Show All Scripts": "Mostra tutti gli script", "Show All Scripts": "Mostra tutti gli script",
"Show CT users and their UID/GID on the host": "Utenti del CT e loro UID/GID sull’host",
"Show CT users in table format": "Mostra utenti CT in tabella", "Show CT users in table format": "Mostra utenti CT in tabella",
"Show Container Privilege Status": "Mostra stato privilegi container", "Show Container Privilege Status": "Mostra stato privilegi container",
"Show DNS resolution status": "Mostra stato risoluzione DNS", "Show DNS resolution status": "Mostra stato risoluzione DNS",
@@ -5963,6 +6013,7 @@
"Start the VM to begin Windows installation from the mounted ISO.": "Avvia la VM per iniziare l’installazione di Windows dalla ISO montata.", "Start the VM to begin Windows installation from the mounted ISO.": "Avvia la VM per iniziare l’installazione di Windows dalla ISO montata.",
"Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Avviare le applicazioni dopo la registrazione? Rispondi No se i container originali sono ancora in esecuzione su un altro host: entrambi userebbero gli stessi indirizzi.", "Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Avviare le applicazioni dopo la registrazione? Rispondi No se i container originali sono ancora in esecuzione su un altro host: entrambi userebbero gli stessi indirizzi.",
"Start the converted container:": "Avvia il container convertito:", "Start the converted container:": "Avvia il container convertito:",
"Start the interactive Proxmox package upgrade": "Avviare l’aggiornamento interattivo dei pacchetti Proxmox",
"Start the main system upgrade:": "Avvia l’aggiornamento principale del sistema:", "Start the main system upgrade:": "Avvia l’aggiornamento principale del sistema:",
"Start the stack with Proxmox": "Avvia lo stack con Proxmox", "Start the stack with Proxmox": "Avvia lo stack con Proxmox",
"Start uploading to PBS — sets a recovery passphrase": "Avvia il caricamento su PBS — viene impostata una passphrase di ripristino", "Start uploading to PBS — sets a recovery passphrase": "Avvia il caricamento su PBS — viene impostata una passphrase di ripristino",
@@ -6242,6 +6293,7 @@
"The Paperless persistent volumes need at least 8 GB": "I volumi persistenti di Paperless richiedono almeno 8 GB", "The Paperless persistent volumes need at least 8 GB": "I volumi persistenti di Paperless richiedono almeno 8 GB",
"The PostgreSQL volume needs at least 4 GB": "Il volume PostgreSQL richiede almeno 4 GB", "The PostgreSQL volume needs at least 4 GB": "Il volume PostgreSQL richiede almeno 4 GB",
"The PostgreSQL volume needs at least 8 GB": "Il volume PostgreSQL richiede almeno 8 GB", "The PostgreSQL volume needs at least 8 GB": "Il volume PostgreSQL richiede almeno 8 GB",
"The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "L’aggiornamento di Proxmox VE 8 non è stato completato. L’aggiornamento a Proxmox VE 9 non è stato avviato.",
"The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Manca il portachiavi dell’archivio Proxmox; l’installazione di Ceph non può continuare in sicurezza", "The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Manca il portachiavi dell’archivio Proxmox; l’installazione di Ceph non può continuare in sicurezza",
"The Proxmox inventory and the local configurations differ": "L'inventario Proxmox e le configurazioni locali differiscono", "The Proxmox inventory and the local configurations differ": "L'inventario Proxmox e le configurazioni locali differiscono",
"The ROCm image has no support for the AMD GPU of this host:": "L’immagine ROCm non supporta la GPU AMD di questo host:", "The ROCm image has no support for the AMD GPU of this host:": "L’immagine ROCm non supporta la GPU AMD di questo host:",
@@ -6312,6 +6364,7 @@
"The build could not be checked beforehand; continuing without that check.": "Non è stato possibile verificare la build in anticipo; si prosegue senza tale verifica.", "The build could not be checked beforehand; continuing without that check.": "Non è stato possibile verificare la build in anticipo; si prosegue senza tale verifica.",
"The cached image does not match the current digest": "L'immagine nella cache non corrisponde al digest corrente", "The cached image does not match the current digest": "L'immagine nella cache non corrisponde al digest corrente",
"The cached image is damaged; it will be downloaded again.": "L'immagine nella cache è danneggiata; verrà scaricata nuovamente.", "The cached image is damaged; it will be downloaded again.": "L'immagine nella cache è danneggiata; verrà scaricata nuovamente.",
"The changes need the application installer": "Le modifiche richiedono il programma di installazione dell’applicazione",
"The character device does not exist:": "Il dispositivo a caratteri non esiste:", "The character device does not exist:": "Il dispositivo a caratteri non esiste:",
"The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Il comando richiede una password non visualizzata durante la digitazione. Dopo la creazione degli utenti, l’interfaccia web richiede nome utente e password.", "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Il comando richiede una password non visualizzata durante la digitazione. Dopo la creazione degli utenti, l’interfaccia web richiede nome utente e password.",
"The command does not name an image": "Il comando non specifica un’immagine", "The command does not name an image": "Il comando non specifica un’immagine",
@@ -6431,8 +6484,10 @@
"The host bind source does not exist:": "La sorgente del bind mount sull’host non esiste:", "The host bind source does not exist:": "La sorgente del bind mount sull’host non esiste:",
"The host bind source is not a regular file or directory:": "La sorgente del bind mount sull'host non è un file regolare né una directory:", "The host bind source is not a regular file or directory:": "La sorgente del bind mount sull'host non è un file regolare né una directory:",
"The host directory changed before it was mounted": "La directory dell'host è cambiata prima del montaggio", "The host directory changed before it was mounted": "La directory dell'host è cambiata prima del montaggio",
"The host directory does not exist": "La directory dell’host non esiste",
"The host directory may not be accessible from an unprivileged container.": "La directory dell’host potrebbe non essere accessibile da un container non privilegiato.", "The host directory may not be accessible from an unprivileged container.": "La directory dell’host potrebbe non essere accessibile da un container non privilegiato.",
"The host has no IPv4 address on the selected bridge": "L'host non ha un indirizzo IPv4 sul bridge selezionato", "The host has no IPv4 address on the selected bridge": "L'host non ha un indirizzo IPv4 sul bridge selezionato",
"The host loses access to them. If the group includes a network or storage controller the host needs, cancel at the summary.": "L’host perde l’accesso a questi dispositivi. Se il gruppo include un controller di rete o di archiviazione necessario all’host, annulla nel riepilogo.",
"The host monitor does not see the real host memory": "Il monitoraggio dell'host non rileva la memoria reale dell'host", "The host monitor does not see the real host memory": "Il monitoraggio dell'host non rileva la memoria reale dell'host",
"The host monitor does not share this host namespace:": "Il monitor dell'host non condivide questo namespace dell'host:", "The host monitor does not share this host namespace:": "Il monitor dell'host non condivide questo namespace dell'host:",
"The host monitor include is unknown or differs from the saved record": "L'include del monitor host è sconosciuto o diverso dal record salvato", "The host monitor include is unknown or differs from the saved record": "L'include del monitor host è sconosciuto o diverso dal record salvato",
@@ -6622,6 +6677,7 @@
"The repair must preserve the image dependencies:": "La riparazione deve conservare le dipendenze dell'immagine:", "The repair must preserve the image dependencies:": "La riparazione deve conservare le dipendenze dell'immagine:",
"The requested VMID block is already in use": "Il blocco di VMID richiesto è già in uso", "The requested VMID block is already in use": "Il blocco di VMID richiesto è già in uso",
"The requested machine learning GPU profile is not working; it is not replaced by CPU": "Il profilo GPU richiesto per il machine learning non funziona; non viene sostituito dalla CPU", "The requested machine learning GPU profile is not working; it is not replaced by CPU": "Il profilo GPU richiesto per il machine learning non funziona; non viene sostituito dalla CPU",
"The resource edit could not be fully rolled back. Open Manage again to review and import the current Proxmox settings.": "Non è stato possibile annullare completamente la modifica delle risorse. Riapri il menu di gestione per verificare e importare le impostazioni attuali di Proxmox.",
"The restored service did not pass its health check": "Il servizio ripristinato non ha superato il controllo dello stato", "The restored service did not pass its health check": "Il servizio ripristinato non ha superato il controllo dello stato",
"The restored service stopped; the recovery is not confirmed": "Il servizio ripristinato si è arrestato; il recupero non è confermato", "The restored service stopped; the recovery is not confirmed": "Il servizio ripristinato si è arrestato; il recupero non è confermato",
"The reviewed Tandoor stack does not require a privileged LXC.": "Lo stack Tandoor esaminato non richiede un container LXC privilegiato.", "The reviewed Tandoor stack does not require a privileged LXC.": "Lo stack Tandoor esaminato non richiede un container LXC privilegiato.",
@@ -6667,6 +6723,7 @@
"The selected VM": "La VM selezionata", "The selected VM": "La VM selezionata",
"The selected VM is running.": "La VM selezionata è in esecuzione.", "The selected VM is running.": "La VM selezionata è in esecuzione.",
"The selected base folder does not exist and could not be created:": "La cartella di base selezionata non esiste e non è stato possibile crearla:", "The selected base folder does not exist and could not be created:": "La cartella di base selezionata non esiste e non è stato possibile crearla:",
"The selected bridge does not exist": "Il bridge selezionato non esiste",
"The selected bridge has no IPv4 subnet for the host-monitor firewall": "Il bridge selezionato non ha una sottorete IPv4 per il firewall del monitoraggio dell’host", "The selected bridge has no IPv4 subnet for the host-monitor firewall": "Il bridge selezionato non ha una sottorete IPv4 per il firewall del monitoraggio dell’host",
"The selected configuration needs to start the LXC during the installation": "La configurazione selezionata richiede l'avvio del container LXC durante l'installazione", "The selected configuration needs to start the LXC during the installation": "La configurazione selezionata richiede l'avvio del container LXC durante l'installazione",
"The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Il container selezionato non è privilegiato. È richiesto un container privilegiato per il passthrough diretto del dispositivo.", "The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Il container selezionato non è privilegiato. È richiesto un container privilegiato per il passthrough diretto del dispositivo.",
@@ -6689,6 +6746,7 @@
"The service builds its own image; only a published image can be installed": "Il servizio crea la propria immagine; si possono installare solo immagini pubblicate", "The service builds its own image; only a published image can be installed": "Il servizio crea la propria immagine; si possono installare solo immagini pubblicate",
"The service declares no image:": "Il servizio non dichiara alcuna immagine:", "The service declares no image:": "Il servizio non dichiara alcuna immagine:",
"The setting has no final value:": "L'impostazione non ha un valore finale:", "The setting has no final value:": "L'impostazione non ha un valore finale:",
"The settings could not be fully rolled back. Review the container and its saved modification before continuing.": "Non è stato possibile ripristinare tutte le impostazioni. Controlla il container e la modifica salvata prima di continuare.",
"The share already exists in smb.conf:": "La condivisione esiste già in smb.conf:", "The share already exists in smb.conf:": "La condivisione esiste già in smb.conf:",
"The shared destination is not a directory": "La destinazione condivisa non è una directory", "The shared destination is not a directory": "La destinazione condivisa non è una directory",
"The shared directory points to a protected host path": "La directory condivisa punta a un percorso protetto dell'host", "The shared directory points to a protected host path": "La directory condivisa punta a un percorso protetto dell'host",
@@ -6756,19 +6814,24 @@
"These Proxmox resources were added outside ProxMenux:": "Queste risorse Proxmox sono state aggiunte fuori da ProxMenux:", "These Proxmox resources were added outside ProxMenux:": "Queste risorse Proxmox sono state aggiunte fuori da ProxMenux:",
"These VMIDs are not free:": "Questi VMID non sono disponibili:", "These VMIDs are not free:": "Questi VMID non sono disponibili:",
"These are the changes that will be made": "Verranno apportate queste modifiche", "These are the changes that will be made": "Verranno apportate queste modifiche",
"These changes were made in Proxmox outside ProxMenux:": "Queste modifiche sono state apportate in Proxmox al di fuori di ProxMenux:",
"These container mount paths have an extension-like suffix:": "Questi percorsi di mount del container hanno un suffisso simile a un'estensione:", "These container mount paths have an extension-like suffix:": "Questi percorsi di mount del container hanno un suffisso simile a un'estensione:",
"These containers were restored from a backup and this host has no record of their OCI application:": "Questi container sono stati ripristinati da un backup e questo host non ha il record della loro applicazione OCI:", "These containers were restored from a backup and this host has no record of their OCI application:": "Questi container sono stati ripristinati da un backup e questo host non ha il record della loro applicazione OCI:",
"These devices share the IOMMU group of the selected GPU and will be assigned to the VM with it:": "Questi dispositivi condividono il gruppo IOMMU della GPU selezionata e verranno assegnati alla VM insieme a essa:",
"These host backups run with this job and expect its storage:": "Questi backup dell’host vengono eseguiti con questo job e richiedono il suo storage:", "These host backups run with this job and expect its storage:": "Questi backup dell’host vengono eseguiti con questo job e richiedono il suo storage:",
"These host backups run with this job and will stop running:": "Questi backup dell'host vengono eseguiti con questo lavoro e interromperanno l'esecuzione:", "These host backups run with this job and will stop running:": "Questi backup dell'host vengono eseguiti con questo lavoro e interromperanno l'esecuzione:",
"These interface configurations will be removed": "Queste configurazioni dell'interfaccia verranno rimosse", "These interface configurations will be removed": "Queste configurazioni dell'interfaccia verranno rimosse",
"These manual changes cannot be adopted safely:": "Queste modifiche manuali non possono essere acquisite in modo sicuro:", "These manual changes cannot be adopted safely:": "Queste modifiche manuali non possono essere acquisite in modo sicuro:",
"These paths will not be restored live and will be extracted for manual recovery.": "Questi percorsi non verranno ripristinati sul sistema in esecuzione e verranno estratti per il ripristino manuale.", "These paths will not be restored live and will be extracted for manual recovery.": "Questi percorsi non verranno ripristinati sul sistema in esecuzione e verranno estratti per il ripristino manuale.",
"These settings require an application-specific adaptation. The image was not downloaded and the container was not recreated.": "Queste impostazioni richiedono un adattamento specifico per l’applicazione. L’immagine non è stata scaricata e il container non è stato ricreato.",
"These values are asked during the installation:": "Questi valori verranno richiesti durante l’installazione:", "These values are asked during the installation:": "Questi valori verranno richiesti durante l’installazione:",
"They are described in the image documentation:": "Sono descritti nella documentazione dell'immagine:", "They are described in the image documentation:": "Sono descritti nella documentazione dell'immagine:",
"They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Se confermi e la verifica finale riesce, queste risorse verranno registrate in oci-compose prima di proseguire. Le risorse non supportate o modificate non vengono acquisite.", "They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Se confermi e la verifica finale riesce, queste risorse verranno registrate in oci-compose prima di proseguire. Le risorse non supportate o modificate non vengono acquisite.",
"They will be saved in oci-compose before continuing.": "Verranno salvate in oci-compose prima di continuare.",
"This CIFS share is mounted with restrictive permissions.": "Questa condivisione CIFS è montata con autorizzazioni restrittive.", "This CIFS share is mounted with restrictive permissions.": "Questa condivisione CIFS è montata con autorizzazioni restrittive.",
"This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Questa GPU è considerata incompatibile con il passthrough GPU a una VM in ProxMenux.", "This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Questa GPU è considerata incompatibile con il passthrough GPU a una VM in ProxMenux.",
"This NFS share is fully restricted — even the host root cannot write to it.": "Questa condivisione NFS è completamente limitata: nemmeno l'utente root dell'host può scrivervi.", "This NFS share is fully restricted — even the host root cannot write to it.": "Questa condivisione NFS è completamente limitata: nemmeno l'utente root dell'host può scrivervi.",
"This Proxmox Backup Server has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PBS source will be disabled.": "Questo Proxmox Backup Server non ha un abbonamento attivo. Passare al repository senza abbonamento? La sorgente Enterprise PBS non accessibile verrà disattivata.",
"This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Questa VM richiede ulteriori passaggi di installazione; vedere la guida:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144", "This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Questa VM richiede ulteriori passaggi di installazione; vedere la guida:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144",
"This account is for {registry}. Enter an image from that registry.": "Questo account è per {registry}. Inserisci un'immagine da quel registro.", "This account is for {registry}. Enter an image from that registry.": "Questo account è per {registry}. Inserisci un'immagine da quel registro.",
"This action will:": "Questa azione:", "This action will:": "Questa azione:",
@@ -6830,10 +6893,13 @@
"This may take several minutes...": "L'operazione potrebbe richiedere diversi minuti...", "This may take several minutes...": "L'operazione potrebbe richiedere diversi minuti...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Proxmox gestisce il montaggio nativamente (non occorre modificare manualmente /etc/fstab per gli storage host NFS/CIFS).", "This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Proxmox gestisce il montaggio nativamente (non occorre modificare manualmente /etc/fstab per gli storage host NFS/CIFS).",
"This means the credentials are incorrect.": "Ciò significa che le credenziali non sono corrette.", "This means the credentials are incorrect.": "Ciò significa che le credenziali non sono corrette.",
"This menu requires Proxmox Backup Server.": "Questo menu richiede Proxmox Backup Server.",
"This might indicate network connectivity issues.": "Ciò potrebbe indicare problemi di connettività di rete.", "This might indicate network connectivity issues.": "Ciò potrebbe indicare problemi di connettività di rete.",
"This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Questo monitor usa un LXC privilegiato, condivide processi e rete con Proxmox e disabilita AppArmor nel CT. Usa IP e firewall dell’host. Un’immagine compromessa potrebbe compromettere l’host; non esporre l’interfaccia web a Internet.", "This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Questo monitor usa un LXC privilegiato, condivide processi e rete con Proxmox e disabilita AppArmor nel CT. Usa IP e firewall dell’host. Un’immagine compromessa potrebbe compromettere l’host; non esporre l’interfaccia web a Internet.",
"This only removes the schedule. Existing backup archives are NOT deleted.": "Viene rimossa solo la pianificazione. Gli archivi di backup esistenti NON vengono eliminati.", "This only removes the schedule. Existing backup archives are NOT deleted.": "Viene rimossa solo la pianificazione. Gli archivi di backup esistenti NON vengono eliminati.",
"This operation may take several minutes and requires internet connectivity.": "L’operazione può richiedere diversi minuti e necessita di connettività Internet.", "This operation may take several minutes and requires internet connectivity.": "L’operazione può richiedere diversi minuti e necessita di connettività Internet.",
"This operation requires a Proxmox VE host. Use the PBS menu on Proxmox Backup Server.": "Questa operazione richiede un host Proxmox VE. Usa il menu PBS su Proxmox Backup Server.",
"This operation supports PBS 3 on Debian 12 and PBS 4 on Debian 13.": "Questa operazione supporta PBS 3 su Debian 12 e PBS 4 su Debian 13.",
"This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Questo pacchetto è stato installato dalle versioni precedenti del programma di installazione ProxMenux Coral, che installavano il driver del kernel M.2 su ogni sistema, comprese le configurazioni solo USB. Non è necessario per i dispositivi USB Coral, che utilizzano solo libedgetpu1-std / libedgetpu1-max.", "This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Questo pacchetto è stato installato dalle versioni precedenti del programma di installazione ProxMenux Coral, che installavano il driver del kernel M.2 su ogni sistema, comprese le configurazioni solo USB. Non è necessario per i dispositivi USB Coral, che utilizzano solo libedgetpu1-std / libedgetpu1-max.",
"This passphrase is the ONLY way to access encrypted Borg backups.": "Questa passphrase è l'UNICO modo per accedere ai backup Borg crittografati.", "This passphrase is the ONLY way to access encrypted Borg backups.": "Questa passphrase è l'UNICO modo per accedere ai backup Borg crittografati.",
"This path is already used as a mount point in this container.": "Questo percorso è già utilizzato come punto di montaggio in questo container.", "This path is already used as a mount point in this container.": "Questo percorso è già utilizzato come punto di montaggio in questo container.",
@@ -6849,6 +6915,7 @@
"This script cannot be executed from the Proxmox web terminal.": "Questo script non può essere eseguito dal terminale web Proxmox.", "This script cannot be executed from the Proxmox web terminal.": "Questo script non può essere eseguito dal terminale web Proxmox.",
"This script must be run as root.": "Questo script deve essere eseguito come root.", "This script must be run as root.": "Questo script deve essere eseguito come root.",
"This script must be run on a Proxmox host.": "Questo script deve essere eseguito su un host Proxmox.", "This script must be run on a Proxmox host.": "Questo script deve essere eseguito su un host Proxmox.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox Backup Server": "Questo script applicherà le seguenti ottimizzazioni e regolazioni avanzate al tuo Proxmox Backup Server",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Questo script applicherà le seguenti ottimizzazioni e regolazioni avanzate al server Proxmox VE", "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Questo script applicherà le seguenti ottimizzazioni e regolazioni avanzate al server Proxmox VE",
"This script will update your Proxmox VE system with advanced options:": "Questo script aggiornerà il sistema Proxmox VE con opzioni avanzate:", "This script will update your Proxmox VE system with advanced options:": "Questo script aggiornerà il sistema Proxmox VE con opzioni avanzate:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Questa sessione è in esecuzione nel terminale Monitor. Eseguirlo da qui interromperebbe la connessione a metà installazione e lascerebbe il cambio di canale incompleto.", "This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Questa sessione è in esecuzione nel terminale Monitor. Eseguirlo da qui interromperebbe la connessione a metà installazione e lascerebbe il cambio di canale incompleto.",
@@ -6980,6 +7047,7 @@
"Unable to identify the root filesystem. Aborting.": "Impossibile identificare il filesystem root. Interruzione.", "Unable to identify the root filesystem. Aborting.": "Impossibile identificare il filesystem root. Interruzione.",
"Unable to resolve system ZFS pool disks. Aborting.": "Impossibile individuare i dischi del pool ZFS di sistema. Interruzione.", "Unable to resolve system ZFS pool disks. Aborting.": "Impossibile individuare i dischi del pool ZFS di sistema. Interruzione.",
"Unable to resolve system disk topology. Aborting.": "Impossibile determinare la topologia del disco di sistema. Interruzione.", "Unable to resolve system disk topology. Aborting.": "Impossibile determinare la topologia del disco di sistema. Interruzione.",
"Unapplied changes": "Modifiche non applicate",
"Understand the security implications of privileged containers": "Comprendere le implicazioni sulla sicurezza dei container privilegiati", "Understand the security implications of privileged containers": "Comprendere le implicazioni sulla sicurezza dei container privilegiati",
"Unexpected Proxmox inventory; recovery blocked": "Inventario Proxmox inatteso; ripristino bloccato", "Unexpected Proxmox inventory; recovery blocked": "Inventario Proxmox inatteso; ripristino bloccato",
"Unexpected formatting directory in the new Valkey volume": "Directory di formattazione imprevista nel nuovo volume Valkey", "Unexpected formatting directory in the new Valkey volume": "Directory di formattazione imprevista nel nuovo volume Valkey",
@@ -7079,6 +7147,7 @@
"Unsupported OCI-LXC AppArmor profile:": "Profilo OCI-LXC AppArmor non supportato:", "Unsupported OCI-LXC AppArmor profile:": "Profilo OCI-LXC AppArmor non supportato:",
"Unsupported OCI-LXC seccomp profile:": "Profilo seccomp OCI-LXC non supportato:", "Unsupported OCI-LXC seccomp profile:": "Profilo seccomp OCI-LXC non supportato:",
"Unsupported Terminal": "Terminale non supportato", "Unsupported Terminal": "Terminale non supportato",
"Unsupported architecture for Figurine:": "Architettura non supportata da Figurine:",
"Unsupported architecture:": "Architettura non supportata:", "Unsupported architecture:": "Architettura non supportata:",
"Unsupported backup compression": "Compressione backup non supportata", "Unsupported backup compression": "Compressione backup non supportata",
"Unsupported credential pattern:": "Schema credenziali non supportato:", "Unsupported credential pattern:": "Schema credenziali non supportato:",
@@ -7094,6 +7163,7 @@
"Unsupported mount type": "Tipo mount non supportato", "Unsupported mount type": "Tipo mount non supportato",
"Unsupported mount type:": "Tipo mount non supportato:", "Unsupported mount type:": "Tipo mount non supportato:",
"Unsupported native device type:": "Tipo dispositivo nativo non supportato:", "Unsupported native device type:": "Tipo dispositivo nativo non supportato:",
"Unsupported network settings": "Impostazioni di rete non supportate",
"Unsupported operation": "Operazione non supportata", "Unsupported operation": "Operazione non supportata",
"Unsupported or unknown Proxmox version; no repository changed.": "Versione Proxmox non supportata o sconosciuta; nessun repository è stato modificato.", "Unsupported or unknown Proxmox version; no repository changed.": "Versione Proxmox non supportata o sconosciuta; nessun repository è stato modificato.",
"Unsupported output format:": "Formato output non supportato:", "Unsupported output format:": "Formato output non supportato:",
@@ -7121,6 +7191,7 @@
"Update NVIDIA in LXC Containers": "Aggiorna NVIDIA nei container LXC", "Update NVIDIA in LXC Containers": "Aggiorna NVIDIA nei container LXC",
"Update OCI": "Aggiorna OCI", "Update OCI": "Aggiorna OCI",
"Update OCI stack": "Aggiorna stack OCI", "Update OCI stack": "Aggiorna stack OCI",
"Update PBS using configured repositories": "Aggiorna PBS usando i repository configurati",
"Update PVE enterprise repository (Only if using enterprise):": "Aggiorna repository enterprise PVE (solo se si utilizza enterprise):", "Update PVE enterprise repository (Only if using enterprise):": "Aggiorna repository enterprise PVE (solo se si utilizza enterprise):",
"Update Proxmox VE Appliance Manager": "Aggiorna Proxmox VE Appliance Manager", "Update Proxmox VE Appliance Manager": "Aggiorna Proxmox VE Appliance Manager",
"Update Proxmox package lists": "Aggiorna elenchi pacchetti Proxmox", "Update Proxmox package lists": "Aggiorna elenchi pacchetti Proxmox",
@@ -7173,6 +7244,7 @@
"Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Assistente aggiornamento: Proxmox VE 8 → 9 (Trixie)", "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Assistente aggiornamento: Proxmox VE 8 → 9 (Trixie)",
"Upgrade canceled by user": "Aggiornamento annullato dall'utente", "Upgrade canceled by user": "Aggiornamento annullato dall'utente",
"Upgrade one node at a time": "Aggiorna un nodo alla volta", "Upgrade one node at a time": "Aggiorna un nodo alla volta",
"Upgrade the system, offering the": "aggiorna il sistema, offrendo il",
"Upgrade to Proxmox VE 9": "Aggiorna a Proxmox VE 9", "Upgrade to Proxmox VE 9": "Aggiorna a Proxmox VE 9",
"Upgrading NVIDIA utils (Arch)...": "Aggiornamento utilità NVIDIA (Arch)...", "Upgrading NVIDIA utils (Arch)...": "Aggiornamento utilità NVIDIA (Arch)...",
"Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Caricare una copia crittografata della chiave su PBS per recuperarla su un host reinstallato con la sola passphrase?", "Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Caricare una copia crittografata della chiave su PBS per recuperarla su un host reinstallato con la sola passphrase?",
@@ -7610,6 +7682,7 @@
"active VF(s)": "VF attive", "active VF(s)": "VF attive",
"active VFs": "VF attive", "active VFs": "VF attive",
"active Virtual Functions. Changing its driver binding would destroy every VF.": "funzioni virtuali attive. La modifica del collegamento del driver distruggerebbe ogni VF.", "active Virtual Functions. Changing its driver binding would destroy every VF.": "funzioni virtuali attive. La modifica del collegamento del driver distruggerebbe ogni VF.",
"added in Proxmox": "aggiunta in Proxmox",
"after copy; source tree is incomplete.": "dopo la copia; l'albero dei sorgenti è incompleto.", "after copy; source tree is incomplete.": "dopo la copia; l'albero dei sorgenti è incompleto.",
"after update.": "dopo l'aggiornamento.", "after update.": "dopo l'aggiornamento.",
"all guests": "tutti i guest", "all guests": "tutti i guest",
@@ -7977,6 +8050,7 @@
"remapped users": "utenti rimappati", "remapped users": "utenti rimappati",
"remove the (now-empty) directory if possible": "rimuovere la directory (ora vuota) se possibile", "remove the (now-empty) directory if possible": "rimuovere la directory (ora vuota) se possibile",
"removed from Proxmox": "rimosso da Proxmox", "removed from Proxmox": "rimosso da Proxmox",
"removed in Proxmox": "rimossa in Proxmox",
"removed successfully from Proxmox.": "rimosso con successo da Proxmox.", "removed successfully from Proxmox.": "rimosso con successo da Proxmox.",
"requires running the official installer, which restarts the Monitor service.": "richiede l'esecuzione del programma di installazione ufficiale, che riavvia il servizio Monitor.", "requires running the official installer, which restarts the Monitor service.": "richiede l'esecuzione del programma di installazione ufficiale, che riavvia il servizio Monitor.",
"requires the package": "richiede il pacchetto", "requires the package": "richiede il pacchetto",
@@ -8080,6 +8154,7 @@
"using": "utilizzando", "using": "utilizzando",
"using direct path (not guaranteed to survive reboots).": "utilizzando il percorso diretto (non è garantito che sopravviva ai riavvii).", "using direct path (not guaranteed to survive reboots).": "utilizzando il percorso diretto (non è garantito che sopravviva ai riavvii).",
"vCPUs:": "vCPU:", "vCPUs:": "vCPU:",
"value changed in Proxmox": "valore modificato in Proxmox",
"vfio-pci IDs configured": "ID vfio-pci configurati", "vfio-pci IDs configured": "ID vfio-pci configurati",
"vfio-pci IDs in /etc/modprobe.d/vfio.conf": "ID vfio-pci in /etc/modprobe.d/vfio.conf", "vfio-pci IDs in /etc/modprobe.d/vfio.conf": "ID vfio-pci in /etc/modprobe.d/vfio.conf",
"video": "video", "video": "video",
@@ -8092,6 +8167,7 @@
"was": "era", "was": "era",
"was installed, but the kernel reports:": "è stato installato, ma il kernel segnala:", "was installed, but the kernel reports:": "è stato installato, ma il kernel segnala:",
"when finished": "al termine", "when finished": "al termine",
"when the server has no subscription (you will be asked)": "quando il server non ha abbonamento (ti verrà chiesto)",
"will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "riassocerà la GPU a vfio-pci al prossimo riavvio, impedendo il funzionamento del driver che sta per essere installato.", "will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "riassocerà la GPU a vfio-pci al prossimo riavvio, impedendo il funzionamento del driver che sta per essere installato.",
"wipefs failed on": "wipefs non è riuscito su", "wipefs failed on": "wipefs non è riuscito su",
"with": "con", "with": "con",
+100 -24
View File
@@ -37,6 +37,7 @@
"/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (caminho vzdump padrão do Proxmox)", "/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (caminho vzdump padrão do Proxmox)",
"/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (padrão Proxmox)", "/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (padrão Proxmox)",
"1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx para todos. Nenhum grupo compartilhado é necessário.", "1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx para todos. Nenhum grupo compartilhado é necessário.",
"====== PBS UPDATE COMPLETED ======": "====== ATUALIZAÇÃO PBS CONCLUÍDA ======",
"====== PVE UPDATE COMPLETED ======": "====== ATUALIZAÇÃO PVE CONCLUÍDA ======", "====== PVE UPDATE COMPLETED ======": "====== ATUALIZAÇÃO PVE CONCLUÍDA ======",
">> Create a new backup job <<": ">> Crie uma nova tarefa de backup <<", ">> Create a new backup job <<": ">> Crie uma nova tarefa de backup <<",
">> Save and return <<": ">> Salvar e retornar <<", ">> Save and return <<": ">> Salvar e retornar <<",
@@ -87,6 +88,7 @@
"A multi-container application is not recreated: its containers are updated together.": "Um aplicativo com vários contêineres não é recriado: seus contêineres são atualizados juntos.", "A multi-container application is not recreated: its containers are updated together.": "Um aplicativo com vários contêineres não é recriado: seus contêineres são atualizados juntos.",
"A new ProxMenux version is available:": "Uma nova versão do ProxMenux está disponível:", "A new ProxMenux version is available:": "Uma nova versão do ProxMenux está disponível:",
"A new kernel is staged for the next boot:": "Um novo kernel está preparado para a próxima inicialização:", "A new kernel is staged for the next boot:": "Um novo kernel está preparado para a próxima inicialização:",
"A newer kernel is installed. Reboot before the next backup window to use it.": "Um kernel mais recente está instalado. Reinicie antes da próxima janela de backup para usá-lo.",
"A newer version is available:": "Uma versão mais recente está disponível:", "A newer version is available:": "Uma versão mais recente está disponível:",
"A pending operation exists for": "Existe uma operation pendente para", "A pending operation exists for": "Existe uma operation pendente para",
"A pending stack assembly already exists; it is not overwritten": "Já existe um conjunto de pilha pendente; não é substituído", "A pending stack assembly already exists; it is not overwritten": "Já existe um conjunto de pilha pendente; não é substituído",
@@ -136,7 +138,7 @@
"ALL DATA ON": "TODOS OS DADOS EM", "ALL DATA ON": "TODOS OS DADOS EM",
"ALL DATA ON THIS DISK WILL BE PERMANENTLY LOST!": "TODOS OS DADOS NESTE DISCO SERÃO PERMANENTEMENTE PERDIDOS!", "ALL DATA ON THIS DISK WILL BE PERMANENTLY LOST!": "TODOS OS DADOS NESTE DISCO SERÃO PERMANENTEMENTE PERDIDOS!",
"ALL Utilities": "TODOS os utilitários", "ALL Utilities": "TODOS os utilitários",
"ALLOWED_HOSTS cannot contain line breaks": "Allowed hosts não pode conter quebras de linha", "ALLOWED_HOSTS cannot contain line breaks": "ALLOWED_HOSTS não pode conter quebras de linha",
"AList initial login": "Login inicial do AList", "AList initial login": "Login inicial do AList",
"AMD (ROCm)": "AMD (ROCm)", "AMD (ROCm)": "AMD (ROCm)",
"AMD (VA-API and ROCm detection)": "AMD (detecção VA-API e ROCm)", "AMD (VA-API and ROCm detection)": "AMD (detecção VA-API e ROCm)",
@@ -169,6 +171,8 @@
"APT language configuration updated": "Configuração de idioma APT atualizada", "APT language configuration updated": "Configuração de idioma APT atualizada",
"APT language downloads restored": "Downloads de idiomas APT restaurados", "APT language downloads restored": "Downloads de idiomas APT restaurados",
"APT package lists updated": "Listas de pacotes APT atualizadas", "APT package lists updated": "Listas de pacotes APT atualizadas",
"APT refresh failed. Check the PBS subscription and repositories in Administration > Repositories. Log:": "falha na atualização do APT.Verifique a assinatura e os repositórios do PBS em Administração > Repositórios. Registro:",
"APT refresh failed. Check the PBS subscription and repositories in the native PBS interface; no source was changed.": "Falha ao atualizar os índices APT. Verifique a subscrição e os repositórios PBS na interface nativa do PBS; nenhuma fonte foi alterada.",
"APT update failed. Check repository access; NVIDIA installation stopped.": "Falha na atualização do APT. Verifique o acesso ao repositório; a instalação do NVIDIA foi interrompida.", "APT update failed. Check repository access; NVIDIA installation stopped.": "Falha na atualização do APT. Verifique o acesso ao repositório; a instalação do NVIDIA foi interrompida.",
"Aborted": "Abortado", "Aborted": "Abortado",
"About to restore": "Prestes a restaurar", "About to restore": "Prestes a restaurar",
@@ -425,6 +429,7 @@
"Apply": "Aplicar", "Apply": "Aplicar",
"Apply AMD CPU fixes": "Aplicar correções de CPU AMD", "Apply AMD CPU fixes": "Aplicar correções de CPU AMD",
"Apply Available Updates": "Aplicar atualizações disponíveis", "Apply Available Updates": "Aplicar atualizações disponíveis",
"Apply PBS package upgrade": "Aplicar a atualização de pacotes PBS",
"Apply and restart services:": "Aplique e reinicie os serviços:", "Apply and restart services:": "Aplique e reinicie os serviços:",
"Apply available updates": "Aplicar atualizações disponíveis", "Apply available updates": "Aplicar atualizações disponíveis",
"Apply boot/initramfs changes": "Aplicar alterações de boot/initramfs", "Apply boot/initramfs changes": "Aplicar alterações de boot/initramfs",
@@ -438,12 +443,15 @@
"Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Aplicar opções do modelo de catálogo atual? Novos caminhos e configurações necessários podem ser solicitados. Revise a configuração resultante antes de recriar o CT.", "Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Aplicar opções do modelo de catálogo atual? Novos caminhos e configurações necessários podem ser solicitados. Revise a configuração resultante antes de recriar o CT.",
"Apply read+write access for 'others' on the host directory?": "Aplicar acesso de leitura + gravação para 'outros' no diretório host?", "Apply read+write access for 'others' on the host directory?": "Aplicar acesso de leitura + gravação para 'outros' no diretório host?",
"Apply the options from the current catalog template? Your data and configuration are kept.": "Aplicar as opções do modelo de catálogo atual? Seus dados e configuração são mantidos.", "Apply the options from the current catalog template? Your data and configuration are kept.": "Aplicar as opções do modelo de catálogo atual? Seus dados e configuração são mantidos.",
"Apply the reviewed package upgrade now? Services may restart and packages listed for removal will be removed.": "Aplicar agora a atualização de pacotes revista? Os serviços podem reiniciar e os pacotes indicados para remoção serão removidos.",
"Apply these changes?": "Aplicar essas alterações?", "Apply these changes?": "Aplicar essas alterações?",
"Applying AMD-specific fixes...": "Aplicando correções específicas da AMD...", "Applying AMD-specific fixes...": "Aplicando correções específicas da AMD...",
"Applying CPU and RAM changes without rebuilding the container...": "A aplicar as alterações de CPU e RAM sem recriar o contentor...",
"Applying Changes": "Aplicando alterações", "Applying Changes": "Aplicando alterações",
"Applying Controller/NVMe passthrough to VM": "Aplicando passagem de controlador/NVMe à VM", "Applying Controller/NVMe passthrough to VM": "Aplicando passagem de controlador/NVMe à VM",
"Applying balanced memory optimization settings...": "Aplicando configurações de otimização de memória balanceadas...", "Applying balanced memory optimization settings...": "Aplicando configurações de otimização de memória balanceadas...",
"Applying changes safely...": "Aplicando alterações com segurança...", "Applying changes safely...": "Aplicando alterações com segurança...",
"Applying container settings without rebuilding the container...": "A aplicar as definições sem recriar o contentor...",
"Applying custom restore": "Aplicando restauração personalizada", "Applying custom restore": "Aplicando restauração personalizada",
"Applying default VM configuration": "Aplicando configuração de VM padrão", "Applying default VM configuration": "Aplicando configuração de VM padrão",
"Applying host permissions for unprivileged LXC bind-mounts...": "Aplicando permissões de host para montagens de ligação LXC sem privilégios...", "Applying host permissions for unprivileged LXC bind-mounts...": "Aplicando permissões de host para montagens de ligação LXC sem privilégios...",
@@ -535,6 +543,7 @@
"Available Borg targets:": "Alvos Borg disponíveis:", "Available Borg targets:": "Alvos Borg disponíveis:",
"Available Disks on Host": "Discos disponíveis no host", "Available Disks on Host": "Discos disponíveis no host",
"Available ISO Images": "Imagens ISO disponíveis", "Available ISO Images": "Imagens ISO disponíveis",
"Available PBS Version": "versão PBS disponível",
"Available PBS repositories:": "Repositórios PBS disponíveis:", "Available PBS repositories:": "Repositórios PBS disponíveis:",
"Available PVE Version": "Versão PVE disponível", "Available PVE Version": "Versão PVE disponível",
"Available Shares:": "Ações disponíveis:", "Available Shares:": "Ações disponíveis:",
@@ -746,6 +755,7 @@
"Cannot verify the reused disk:": "Não foi possível verificar o disco reutilizado:", "Cannot verify the reused disk:": "Não foi possível verificar o disco reutilizado:",
"Cannot write the image archive to {storage}. Check storage access and mount status.": "Não é possível gravar o arquivo de imagem em {storage}. Verifique o acesso ao armazenamento e o status de montagem.", "Cannot write the image archive to {storage}. Check storage access and mount status.": "Não é possível gravar o arquivo de imagem em {storage}. Verifique o acesso ao armazenamento e o status de montagem.",
"Capabilities cannot be kept and all dropped at the same time": "Capacidades não podem ser mantidas e todas retiradas ao mesmo tempo", "Capabilities cannot be kept and all dropped at the same time": "Capacidades não podem ser mantidas e todas retiradas ao mesmo tempo",
"Catalog options": "Opções do catálogo",
"Category": "Categoria", "Category": "Categoria",
"Caution: Maximum mode generates more heat.": "Cuidado: O modo máximo gera mais calor.", "Caution: Maximum mode generates more heat.": "Cuidado: O modo máximo gera mais calor.",
"Ceph check skipped by user flag (--ignore-ceph-check)": "Verificação do Ceph ignorada pelo sinalizador do usuário (--ignore-ceph-check)", "Ceph check skipped by user flag (--ignore-ceph-check)": "Verificação do Ceph ignorada pelo sinalizador do usuário (--ignore-ceph-check)",
@@ -880,6 +890,7 @@
"Choose a schedule (systemd calendar-event syntax):": "Escolha uma programação (sintaxe de evento de calendário do systemd):", "Choose a schedule (systemd calendar-event syntax):": "Escolha uma programação (sintaxe de evento de calendário do systemd):",
"Choose a script to execute:": "Escolha um script para executar:", "Choose a script to execute:": "Escolha um script para executar:",
"Choose a server:": "Escolha um servidor:", "Choose a server:": "Escolha um servidor:",
"Choose a setting. Changes are applied only after the final review.": "Escolha o que configurar. As alterações só são aplicadas após a revisão final.",
"Choose a share to mount:": "Escolha um compartilhamento para montar:", "Choose a share to mount:": "Escolha um compartilhamento para montar:",
"Choose a specific supported GPU or USB node": "Escolha um GPU ou nó USB específico compatível", "Choose a specific supported GPU or USB node": "Escolha um GPU ou nó USB específico compatível",
"Choose action for this controller/NVMe:": "Escolha a ação para este controlador/NVMe:", "Choose action for this controller/NVMe:": "Escolha a ação para este controlador/NVMe:",
@@ -960,6 +971,7 @@
"Code-server is VS Code running on a remote server, accessible through the browser.": "O servidor de código é VS Code em execução em um servidor remoto, acessível através do navegador.", "Code-server is VS Code running on a remote server, accessible through the browser.": "O servidor de código é VS Code em execução em um servidor remoto, acessível através do navegador.",
"CodeProject.AI Server": "CodeProject. AI Server", "CodeProject.AI Server": "CodeProject. AI Server",
"Command": "Comando", "Command": "Comando",
"Command failed": "O comando falhou",
"Command override for an unknown service:": "Sobreposição de comandos para um serviço desconhecido:", "Command override for an unknown service:": "Sobreposição de comandos para um serviço desconhecido:",
"Commenting any residual Bookworm lines in *.list...": "Comentando quaisquer linhas residuais do Bookworm em *.list...", "Commenting any residual Bookworm lines in *.list...": "Comentando quaisquer linhas residuais do Bookworm em *.list...",
"Commenting legacy PVE 8 repository .list files (if any)...": "Comentando arquivos .list do repositório PVE 8 legado (se houver)...", "Commenting legacy PVE 8 repository .list files (if any)...": "Comentando arquivos .list do repositório PVE 8 legado (se houver)...",
@@ -1516,6 +1528,7 @@
"Current NVIDIA inventory resolved: a refresh is required": "Inventário NVIDIA atual resolvido: uma atualização é required", "Current NVIDIA inventory resolved: a refresh is required": "Inventário NVIDIA atual resolvido: uma atualização é required",
"Current NVIDIA inventory resolved: no refresh is required": "Inventário NVIDIA atual resolvido: nenhuma atualização é required", "Current NVIDIA inventory resolved: no refresh is required": "Inventário NVIDIA atual resolvido: nenhuma atualização é required",
"Current Network Configuration": "Configuração de rede atual", "Current Network Configuration": "Configuração de rede atual",
"Current PBS Version": "versão atual do PBS",
"Current PVE Version": "Versão atual do PVE", "Current PVE Version": "Versão atual do PVE",
"Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Os scripts atuais do host ProxMenux registram compartilhamentos remotos como armazenamentos Proxmox usando pvesm.", "Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Os scripts atuais do host ProxMenux registram compartilhamentos remotos como armazenamentos Proxmox usando pvesm.",
"Current ProxMenux version:": "Versão atual do ProxMenux:", "Current ProxMenux version:": "Versão atual do ProxMenux:",
@@ -1630,8 +1643,8 @@
"Default Proxmox logo applied": "Logotipo padrão do Proxmox aplicado", "Default Proxmox logo applied": "Logotipo padrão do Proxmox aplicado",
"Default is none (video-only passthrough). Use SPACE to toggle selections.": "O padrão é nenhum (passagem somente de vídeo). Use ESPAÇO para alternar as seleções.", "Default is none (video-only passthrough). Use SPACE to toggle selections.": "O padrão é nenhum (passagem somente de vídeo). Use ESPAÇO para alternar as seleções.",
"Default journald configuration restored": "Configuração padrão do diário restaurada", "Default journald configuration restored": "Configuração padrão do diário restaurada",
"Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "O local padrão é /mnt/<nome>. O compartilhamento será montado aqui no host com permissões abertas para que um LXC sem privilégios possa montar e gravar nele. Para acesso LXC, monte este caminho com o LXC Mount Manager.", "Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "A localização predefinida é /mnt/<name>. A partilha será montada aqui no host com permissões abertas, para que um LXC sem privilégios a possa montar por bind mount e nela escrever. Utilize o LXC Mount Manager para criar essa montagem no LXC.",
"Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "O local padrão é /mnt/<nome>. O compartilhamento será montado aqui no host. Use este caminho em /etc/fstab. Para acesso LXC, monte este caminho com o LXC Mount Manager.", "Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "A localização predefinida é /mnt/<name>. A partilha será montada aqui no host. Utilize este caminho em /etc/fstab. Para aceder a partir de um LXC, crie um bind mount deste caminho com o LXC Mount Manager.",
"Default login": "Utilizador por omissão", "Default login": "Utilizador por omissão",
"Default options": "Opções padrão", "Default options": "Opções padrão",
"Default options read/write": "Opções padrão leitura/gravação", "Default options read/write": "Opções padrão leitura/gravação",
@@ -1681,6 +1694,7 @@
"Detected Coral hardware:": "Hardware Coral detectado:", "Detected Coral hardware:": "Hardware Coral detectado:",
"Detected GPU(s):": "GPU(s) detectada(s):", "Detected GPU(s):": "GPU(s) detectada(s):",
"Detected Intel CPU": "CPU Intel detectada", "Detected Intel CPU": "CPU Intel detectada",
"Detected Proxmox Backup Server": "Proxmox Backup Server detectado",
"Detected Proxmox VE": "Proxmox VE detectado", "Detected Proxmox VE": "Proxmox VE detectado",
"Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Detectado Proxmox VE 8.x - Instalando Ceph Squid para Debian", "Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Detectado Proxmox VE 8.x - Instalando Ceph Squid para Debian",
"Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Detectado Proxmox VE 9.x - Instalando Ceph Squid para Debian Trixie", "Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Detectado Proxmox VE 9.x - Instalando Ceph Squid para Debian Trixie",
@@ -1762,6 +1776,9 @@
"Disabling and stopping rpcbind service and socket...": "Desativando e parando o serviço e soquete rpcbind...", "Disabling and stopping rpcbind service and socket...": "Desativando e parando o serviço e soquete rpcbind...",
"Disabling non-free firmware warnings...": "Desativando avisos de firmware não-livre...", "Disabling non-free firmware warnings...": "Desativando avisos de firmware não-livre...",
"Disabling portmapper/rpcbind for security...": "Desativando portmapper/rpcbind por segurança...", "Disabling portmapper/rpcbind for security...": "Desativando portmapper/rpcbind por segurança...",
"Discard changes": "Descartar alterações",
"Discard unapplied changes?": "Descartar as alterações por aplicar?",
"Discover NFS exports on a server": "Descobrir as exportações NFS de um servidor",
"Discover targets on portal:": "Descubra alvos no portal:", "Discover targets on portal:": "Descubra alvos no portal:",
"Discovering iSCSI targets...": "Descobrindo alvos iSCSI...", "Discovering iSCSI targets...": "Descobrindo alvos iSCSI...",
"Discovery successful": "Descoberta bem-sucedida", "Discovery successful": "Descoberta bem-sucedida",
@@ -1841,6 +1858,7 @@
"Do you want to proceed with the system update?": "Deseja prosseguir com a atualização do sistema?", "Do you want to proceed with the system update?": "Deseja prosseguir com a atualização do sistema?",
"Do you want to proceed?": "Você quer prosseguir?", "Do you want to proceed?": "Você quer prosseguir?",
"Do you want to reboot now?": "Você quer reiniciar agora?", "Do you want to reboot now?": "Você quer reiniciar agora?",
"Do you want to remove the Proxmox subscription banner from the Proxmox Backup Server web interface?": "Deseja remover o banner de assinatura do Proxmox da interface web do Proxmox Backup Server?",
"Do you want to remove the stale entry from vfio.conf and continue?": "Deseja remover a entrada obsoleta do vfio.conf e continuar?", "Do you want to remove the stale entry from vfio.conf and continue?": "Deseja remover a entrada obsoleta do vfio.conf e continuar?",
"Do you want to remove the stale entry from vfio.conf?": "Você deseja remover a entrada obsoleta do vfio.conf?", "Do you want to remove the stale entry from vfio.conf?": "Você deseja remover a entrada obsoleta do vfio.conf?",
"Do you want to restart the network service now to apply changes?": "Deseja reiniciar o serviço de rede agora para aplicar as alterações?", "Do you want to restart the network service now to apply changes?": "Deseja reiniciar o serviço de rede agora para aplicar as alterações?",
@@ -2018,7 +2036,7 @@
"Enter VM ID:": "Insira o ID da VM:", "Enter VM ID:": "Insira o ID da VM:",
"Enter VM/CT ID:": "Insira o ID da VM/CT:", "Enter VM/CT ID:": "Insira o ID da VM/CT:",
"Enter ZFS pool name for the selected disk:": "Insira o nome do pool ZFS para o disco selecionado:", "Enter ZFS pool name for the selected disk:": "Insira o nome do pool ZFS para o disco selecionado:",
"Enter a name for the mount point (used as /mnt/<name>):": "Insira um nome para o ponto de montagem (usado como /mnt/<nome>):", "Enter a name for the mount point (used as /mnt/<name>):": "Introduza um nome para o ponto de montagem (será usado como /mnt/<name>):",
"Enter a name for the new virtual machine:": "Insira um nome para a nova máquina virtual:", "Enter a name for the new virtual machine:": "Insira um nome para a nova máquina virtual:",
"Enter a non-empty password or access token on a single line.": "Insira uma senha ou token de acesso não vazio em uma única linha.", "Enter a non-empty password or access token on a single line.": "Insira uma senha ou token de acesso não vazio em uma única linha.",
"Enter a number, or write or paste a command:": "Digite um número ou escreva ou cole um comando:", "Enter a number, or write or paste a command:": "Digite um número ou escreva ou cole um comando:",
@@ -2162,11 +2180,11 @@
"Example: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak": "Exemplo: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak", "Example: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak": "Exemplo: cp /etc/pve/lxc/114.conf /etc/pve/lxc/114.conf.bak",
"Example: daily or Mon..Fri 03:00": "Exemplo: diariamente ou Seg..Sex 03:00", "Example: daily or Mon..Fri 03:00": "Exemplo: diariamente ou Seg..Sex 03:00",
"Example: mkdir -p /tmp/lxc_convert_114": "Exemplo: mkdir -p /tmp/lxc_convert_114", "Example: mkdir -p /tmp/lxc_convert_114": "Exemplo: mkdir -p /tmp/lxc_convert_114",
"Example: pct config 110 | grep unprivileged": "Exemplo: configuração pct 110 | grep sem privilégios", "Example: pct config 110 | grep unprivileged": "Exemplo: pct config 110 | grep unprivileged",
"Example: pct stop 110": "Exemplo: pct stop 110", "Example: pct stop 110": "Exemplo: pct stop 110",
"Example: pct stop 114": "Exemplo: parada pct 114", "Example: pct stop 114": "Exemplo: pct stop 114",
"Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Exemplo: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup", "Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Exemplo: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup",
"Example: pvesm path local-lvm:vm-114-disk-0": "Exemplo: caminho pvesm local-lvm:vm-114-disk-0", "Example: pvesm path local-lvm:vm-114-disk-0": "Exemplo: pvesm path local-lvm:vm-114-disk-0",
"Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Exemplo: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf", "Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Exemplo: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf",
"Examples:": "Exemplos:", "Examples:": "Exemplos:",
"Execute destructive rollback?": "Executar reversão destrutiva?", "Execute destructive rollback?": "Executar reversão destrutiva?",
@@ -2266,6 +2284,7 @@
"Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Falha ao clonar o repositório log2ram. Verifique /tmp/log2ram_install.log", "Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Falha ao clonar o repositório log2ram. Verifique /tmp/log2ram_install.log",
"Failed to configure EFI disk": "Falha ao configurar o disco EFI", "Failed to configure EFI disk": "Falha ao configurar o disco EFI",
"Failed to configure IOMMU automatically.": "Falha ao configurar o IOMMU automaticamente.", "Failed to configure IOMMU automatically.": "Falha ao configurar o IOMMU automaticamente.",
"Failed to configure Proxmox Backup Server repositories": "Falha ao configurar os repositórios do Proxmox Backup Server",
"Failed to configure Proxmox repositories": "Falha ao configurar repositórios Proxmox", "Failed to configure Proxmox repositories": "Falha ao configurar repositórios Proxmox",
"Failed to configure TPM device in VM": "Falha ao configurar o dispositivo TPM na VM", "Failed to configure TPM device in VM": "Falha ao configurar o dispositivo TPM na VM",
"Failed to configure repositories.": "Falha ao configurar repositórios.", "Failed to configure repositories.": "Falha ao configurar repositórios.",
@@ -2477,7 +2496,7 @@
"For a consistent export, the VM should be stopped.": "Para uma exportação consistente, a VM deve ser interrompida.", "For a consistent export, the VM should be stopped.": "Para uma exportação consistente, a VM deve ser interrompida.",
"For any question, issue or bug report, please use the official repository": "Para qualquer dúvida, problema ou relatório de bug, use o repositório oficial", "For any question, issue or bug report, please use the official repository": "Para qualquer dúvida, problema ou relatório de bug, use o repositório oficial",
"For dedicated GPUs, FLR is required by this policy to reduce VM": "Para GPUs dedicadas, o FLR é exigido por esta política para reduzir VM", "For dedicated GPUs, FLR is required by this policy to reduce VM": "Para GPUs dedicadas, o FLR é exigido por esta política para reduzir VM",
"For example: pct enter 101": "Por exemplo: pct digite 101", "For example: pct enter 101": "Por exemplo: pct enter 101",
"For guest shares add: --options guest": "Para compartilhamentos de convidados, adicione: --options guest", "For guest shares add: --options guest": "Para compartilhamentos de convidados, adicione: --options guest",
"For security reasons, the system cannot format it.": "Por motivos de segurança, o sistema não pode formatá-lo.", "For security reasons, the system cannot format it.": "Por motivos de segurança, o sistema não pode formatá-lo.",
"For unprivileged containers, use instead:": "Para contêineres sem privilégios, use:", "For unprivileged containers, use instead:": "Para contêineres sem privilégios, use:",
@@ -2889,8 +2908,8 @@
"If notifications are enabled (Telegram/Discord/ntfy/...), you will receive a \"Host restore finished\" message when all background tasks complete.": "Se as notificações estiverem habilitadas (Telegram/Discord/ntfy/...), você receberá uma mensagem \"Restauração do host concluída\" quando todas as tarefas em segundo plano forem concluídas.", "If notifications are enabled (Telegram/Discord/ntfy/...), you will receive a \"Host restore finished\" message when all background tasks complete.": "Se as notificações estiverem habilitadas (Telegram/Discord/ntfy/...), você receberá uma mensagem \"Restauração do host concluída\" quando todas as tarefas em segundo plano forem concluídas.",
"If passthrough fails on Windows: install RadeonResetBugFix.": "Se a passagem falhar no Windows: instale RadeonResetBugFix.", "If passthrough fails on Windows: install RadeonResetBugFix.": "Se a passagem falhar no Windows: instale RadeonResetBugFix.",
"If path not accessible (ZFS/BTRFS):": "Se o caminho não estiver acessível (ZFS/BTRFS):", "If path not accessible (ZFS/BTRFS):": "Se o caminho não estiver acessível (ZFS/BTRFS):",
"If pvesm path returned a DEVICE (LVM):": "Se o caminho pvesm retornou um DEVICE (LVM):", "If pvesm path returned a DEVICE (LVM):": "Se pvesm path devolveu um DISPOSITIVO (LVM):",
"If pvesm path returned a DIRECTORY (ZFS/BTRFS):": "Se o caminho pvesm retornou um DIRETÓRIO (ZFS/BTRFS):", "If pvesm path returned a DIRECTORY (ZFS/BTRFS):": "Se pvesm path devolveu um DIRETÓRIO (ZFS/BTRFS):",
"If repositories error:": "Se houver erro nos repositórios:", "If repositories error:": "Se houver erro nos repositórios:",
"If the container had already been changed, its previous native backup is restored. Shared host directories are not reverted. The disks of the failed attempt are removed when the cleanup succeeds.": "Se o contêiner já tiver sido alterado, seu backup nativo anterior será restaurado. Os diretórios de host compartilhados não são revertidos. Os discos da tentativa fracassada serão removidos quando a limpeza for bem-sucedida.", "If the container had already been changed, its previous native backup is restored. Shared host directories are not reverted. The disks of the failed attempt are removed when the cleanup succeeds.": "Se o contêiner já tiver sido alterado, seu backup nativo anterior será restaurado. Os diretórios de host compartilhados não são revertidos. Os discos da tentativa fracassada serão removidos quando a limpeza for bem-sucedida.",
"If the host freezes, remove hostpci entries from": "Se o host congelar, remova as entradas hostpci de", "If the host freezes, remove hostpci entries from": "Se o host congelar, remova as entradas hostpci de",
@@ -2973,6 +2992,7 @@
"Inaccessible device during VM startup": "Dispositivo inacessível durante a inicialização da VM", "Inaccessible device during VM startup": "Dispositivo inacessível durante a inicialização da VM",
"Inactive": "Inativo", "Inactive": "Inativo",
"Inactive (entry in fstab, not currently mounted)": "Inativo (entrada no fstab, não montada no momento)", "Inactive (entry in fstab, not currently mounted)": "Inativo (entrada no fstab, não montada no momento)",
"Include these changes in oci-compose?": "Incluir essas alterações no oci-compose?",
"Include these resources in oci-compose?": "Incluir esses recursos no oci-compose?", "Include these resources in oci-compose?": "Incluir esses recursos no oci-compose?",
"Includes /etc/network (may drop SSH immediately)": "Inclui /etc/network (pode descartar o SSH imediatamente)", "Includes /etc/network (may drop SSH immediately)": "Inclui /etc/network (pode descartar o SSH imediatamente)",
"Includes /etc/zfs": "Inclui /etc/zfs", "Includes /etc/zfs": "Inclui /etc/zfs",
@@ -3029,7 +3049,7 @@
"Initiator IQN is authorised on the target": "O iniciador IQN está autorizado no alvo", "Initiator IQN is authorised on the target": "O iniciador IQN está autorizado no alvo",
"Initiator IQN:": "IQN do iniciador:", "Initiator IQN:": "IQN do iniciador:",
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape é um software gráfico vetorial de qualidade profissional que funciona em computadores desktop Linux, Mac OS X e Windows.", "Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape é um software gráfico vetorial de qualidade profissional que funciona em computadores desktop Linux, Mac OS X e Windows.",
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Dentro do LXC, crie o administrador: console kimai:user:create Your USERNAME Your EMAIL ROLE_SUPER_ADMIN", "Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Dentro do LXC, crie o administrador: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
"Inspect disks before any action": "Inspecione os discos antes de qualquer ação", "Inspect disks before any action": "Inspecione os discos antes de qualquer ação",
"Inspect host device nodes": "Inspecione os nós do dispositivo host", "Inspect host device nodes": "Inspecione os nós do dispositivo host",
"Inspect passthrough/kernel events": "Inspecionar eventos de passagem/kernel", "Inspect passthrough/kernel events": "Inspecionar eventos de passagem/kernel",
@@ -3206,9 +3226,11 @@
"Interrupted operation:": "operation interrompida:", "Interrupted operation:": "operation interrompida:",
"Interrupted stack operation found": "Pilha interrompida operation encontrada", "Interrupted stack operation found": "Pilha interrompida operation encontrada",
"Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.": "Candidato 'proxmox-ve' inválido (não 9.x ou nenhum). Verifique a configuração e a rede do seu repositório e tente novamente.", "Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.": "Candidato 'proxmox-ve' inválido (não 9.x ou nenhum). Verifique a configuração e a rede do seu repositório e tente novamente.",
"Invalid ALLOWED_HOSTS value": "Valor Allowed hosts inválido", "Invalid ALLOWED_HOSTS value": "Valor de ALLOWED_HOSTS inválido",
"Invalid Home Assistant OS check timeout": "Tempo de verificação do sistema operacional Home Assistant inválido", "Invalid Home Assistant OS check timeout": "Tempo de verificação do sistema operacional Home Assistant inválido",
"Invalid ID": "ID inválido", "Invalid ID": "ID inválido",
"Invalid IPv4 address": "Endereço IPv4 inválido",
"Invalid IPv4 gateway": "Gateway IPv4 inválido",
"Invalid Intel render path": "Caminho de renderização Intel inválido", "Invalid Intel render path": "Caminho de renderização Intel inválido",
"Invalid Jellyfin path:": "Caminho Jellyfin inválido:", "Invalid Jellyfin path:": "Caminho Jellyfin inválido:",
"Invalid MAC address:": "Endereço MAC inválido:", "Invalid MAC address:": "Endereço MAC inválido:",
@@ -3241,6 +3263,7 @@
"Invalid administrator email": "E- mail de administrador inválido", "Invalid administrator email": "E- mail de administrador inválido",
"Invalid administrator user name": "Nome de usuário do administrador inválido", "Invalid administrator user name": "Nome de usuário do administrador inválido",
"Invalid base VMID": "VMID base inválida", "Invalid base VMID": "VMID base inválida",
"Invalid bridge": "Bridge inválida",
"Invalid check package:": "Pacote de verificação inválido:", "Invalid check package:": "Pacote de verificação inválido:",
"Invalid configuration path:": "Caminho de configuração inválido:", "Invalid configuration path:": "Caminho de configuração inválido:",
"Invalid consume/export volumes": "Volumes de consumo/exportação inválidos", "Invalid consume/export volumes": "Volumes de consumo/exportação inválidos",
@@ -3304,6 +3327,7 @@
"Invalid remote name": "Nome remoto inválido", "Invalid remote name": "Nome remoto inválido",
"Invalid remote path": "Caminho remoto inválido", "Invalid remote path": "Caminho remoto inválido",
"Invalid repair version:": "Versão de reparação inválida:", "Invalid repair version:": "Versão de reparação inválida:",
"Invalid resource value": "Valor de recurso inválido",
"Invalid resources": "Recursos inválidos", "Invalid resources": "Recursos inválidos",
"Invalid restored volume path": "Caminho de volume restaurado inválido", "Invalid restored volume path": "Caminho de volume restaurado inválido",
"Invalid running state": "Estado de execução inválido", "Invalid running state": "Estado de execução inválido",
@@ -3322,6 +3346,7 @@
"Invalid stack members": "Membros inválidos da pilha", "Invalid stack members": "Membros inválidos da pilha",
"Invalid stack name": "Nome da pilha inválido", "Invalid stack name": "Nome da pilha inválido",
"Invalid stack operation": "Pilha inválida operation", "Invalid stack operation": "Pilha inválida operation",
"Invalid storage": "Armazenamento inválido",
"Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "ID de armazenamento inválido. Use apenas letras, números, hífens e sublinhados.", "Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "ID de armazenamento inválido. Use apenas letras, números, hífens e sublinhados.",
"Invalid storage name": "Nome de armazenamento inválido", "Invalid storage name": "Nome de armazenamento inválido",
"Invalid suite application": "Aplicativo de suite inválido", "Invalid suite application": "Aplicativo de suite inválido",
@@ -3436,7 +3461,7 @@
"Keyfile removed.": "arquivo-chave removido.", "Keyfile removed.": "arquivo-chave removido.",
"Keyrings method failed; trying apt-key fallback": "O método dos chaveiros falhou; tentando substituto do apt-key", "Keyrings method failed; trying apt-key fallback": "O método dos chaveiros falhou; tentando substituto do apt-key",
"KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - Uma plataforma cruzada e Open Source Electronics Design Automation Suite.", "KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - Uma plataforma cruzada e Open Source Electronics Design Automation Suite.",
"Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai não tem conta padrão. Digite o recipiente com: pct enter {main vmid}", "Kimai has no default account. Enter the container with: pct enter {main_vmid}": "O Kimai não tem uma conta predefinida. Aceda ao contentor com: pct enter {main_vmid}",
"Kimai is a professional grade time-tracking application, free and open-source.": "Kimai é um aplicativo de rastreamento de tempo profissional, gratuito e de código aberto.", "Kimai is a professional grade time-tracking application, free and open-source.": "Kimai é um aplicativo de rastreamento de tempo profissional, gratuito e de código aberto.",
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa é uma ferramenta poderosa projetada para lhe dar controle completo sobre suas bibliotecas de mídia. Com o Kometa, você pode levar sua personalização para o próximo nível, com controle granular sobre metadados, coleções, sobreposições e muito mais.", "Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa é uma ferramenta poderosa projetada para lhe dar controle completo sobre suas bibliotecas de mídia. Com o Kometa, você pode levar sua personalização para o próximo nível, com controle granular sobre metadados, coleções, sobreposições e muito mais.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa lê a sua configuração a partir de /config/config.yml e o recipiente apenas navios /config/config.yml.template. Copie o modelo para config.yml, preencha o requi vermelho Plex e conexões TMDb, em seguida, reinicie o recipiente.", "Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa lê a sua configuração a partir de /config/config.yml e o recipiente apenas navios /config/config.yml.template. Copie o modelo para config.yml, preencha o requi vermelho Plex e conexões TMDb, em seguida, reinicie o recipiente.",
@@ -3488,7 +3513,7 @@
"Leave empty to keep every backup (not recommended).": "Deixe em branco para manter todos os backups (não recomendado).", "Leave empty to keep every backup (not recommended).": "Deixe em branco para manter todos os backups (não recomendado).",
"Leave empty to use the retention of the storage.": "Deixe em branco para usar a retenção do armazenamento.", "Leave empty to use the retention of the storage.": "Deixe em branco para usar a retenção do armazenamento.",
"Legacy PVE 8 .list files commented or not present": "Arquivos .list PVE 8 legados comentados ou não presentes", "Legacy PVE 8 .list files commented or not present": "Arquivos .list PVE 8 legados comentados ou não presentes",
"Legacy ceph.list commented or not present": "Ceph.list legado comentado ou não presente", "Legacy ceph.list commented or not present": "ceph.list antigo comentado ou inexistente",
"Legacy gasket-dkms cleanup could not be verified as complete.": "Não foi possível confirmar a conclusão da limpeza do pacote gasket-dkms legado.", "Legacy gasket-dkms cleanup could not be verified as complete.": "Não foi possível confirmar a conclusão da limpeza do pacote gasket-dkms legado.",
"Legacy gasket-dkms detected": "gasket-dkms legado detetado", "Legacy gasket-dkms detected": "gasket-dkms legado detetado",
"Legacy network tools (e.g., ifconfig)": "Ferramentas de rede legadas (por exemplo, ifconfig)", "Legacy network tools (e.g., ifconfig)": "Ferramentas de rede legadas (por exemplo, ifconfig)",
@@ -4153,6 +4178,7 @@
"No active Proxmox storage accepts OCI template archives.": "Nenhum armazenamento Proxmox ativo aceita arquivos de modelo OCI.", "No active Proxmox storage accepts OCI template archives.": "Nenhum armazenamento Proxmox ativo aceita arquivos de modelo OCI.",
"No active exports or showmount not available": "Nenhuma exportação ativa ou showmount não disponível", "No active exports or showmount not available": "Nenhuma exportação ativa ou showmount não disponível",
"No active session": "Nenhuma sessão ativa", "No active session": "Nenhuma sessão ativa",
"No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.": "Nenhuma assinatura ativa e nenhum repositório PBS utilizável. O modo não interativo não pode alterar fontes APT;configure-os em Administração > Repositórios.",
"No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Nenhuma assinatura ativa e nenhum repositório PVE utilizável. O modo não interativo não pode alterar fontes APT; configure-as em Nó > Atualizações > Repositórios.", "No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Nenhuma assinatura ativa e nenhum repositório PVE utilizável. O modo não interativo não pode alterar fontes APT; configure-as em Nó > Atualizações > Repositórios.",
"No additional GPU can be added.": "Nenhuma GPU adicional pode ser adicionada.", "No additional GPU can be added.": "Nenhuma GPU adicional pode ser adicionada.",
"No additional device needs to be added.": "Nenhum dispositivo adicional precisa ser adicionado.", "No additional device needs to be added.": "Nenhum dispositivo adicional precisa ser adicionado.",
@@ -4215,7 +4241,7 @@
"No format-safe disks are available.": "Nenhum disco de formato seguro está disponível.", "No format-safe disks are available.": "Nenhum disco de formato seguro está disponível.",
"No free ProxMenux private /24 network is available": "Nenhuma rede privada ProxMenux livre 24 está disponível", "No free ProxMenux private /24 network is available": "Nenhuma rede privada ProxMenux livre 24 está disponível",
"No gasket DKMS registrations remain.": "Não restam registos DKMS de gasket.", "No gasket DKMS registrations remain.": "Não restam registos DKMS de gasket.",
"No group creation required — uses world-writable sticky bit permissions.": "Não é necessária a criação de grupos — usa permissões de sticky bit graváveis ​​mundialmente.", "No group creation required — uses world-writable sticky bit permissions.": "Não é necessária a criação de grupos — usa permissões de sticky bit graváveis mundialmente.",
"No guest selected.": "Nenhum convidado selecionado.", "No guest selected.": "Nenhum convidado selecionado.",
"No host VFIO reconfiguration expected": "Nenhuma reconfiguração VFIO do host é esperada", "No host VFIO reconfiguration expected": "Nenhuma reconfiguração VFIO do host é esperada",
"No host VFIO/native binding changes were required.": "Nenhuma alteração de ligação VFIO/nativa do host foi necessária.", "No host VFIO/native binding changes were required.": "Nenhuma alteração de ligação VFIO/nativa do host foi necessária.",
@@ -4238,7 +4264,7 @@
"No jobs": "Sem empregos", "No jobs": "Sem empregos",
"No language selected.": "Nenhum idioma selecionado.", "No language selected.": "Nenhum idioma selecionado.",
"No local disk storage configured.": "Nenhum armazenamento em disco local configurado.", "No local disk storage configured.": "Nenhum armazenamento em disco local configurado.",
"No main sources.list present (skipped)": "Nenhuma fonte principal.list presente (ignorada)", "No main sources.list present (skipped)": "Não existe um sources.list principal (ignorado)",
"No matches for:": "Nenhuma correspondência para:", "No matches for:": "Nenhuma correspondência para:",
"No matching PVE vzdump jobs were found for the PBS backend.": "Nenhum trabalho PVE vzdump correspondente foi encontrado para o backend do PBS.", "No matching PVE vzdump jobs were found for the PBS backend.": "Nenhum trabalho PVE vzdump correspondente foi encontrado para o backend do PBS.",
"No matching PVE vzdump jobs were found for the local archive backend.": "Nenhum trabalho PVE vzdump correspondente foi encontrado para o back-end do arquivo local.", "No matching PVE vzdump jobs were found for the local archive backend.": "Nenhum trabalho PVE vzdump correspondente foi encontrado para o back-end do arquivo local.",
@@ -4257,6 +4283,7 @@
"No password provided.": "Nenhuma senha fornecida.", "No password provided.": "Nenhuma senha fornecida.",
"No paths available": "Nenhum caminho disponível", "No paths available": "Nenhum caminho disponível",
"No paths selected. Select at least one path.": "Nenhum caminho selecionado. Selecione pelo menos um caminho.", "No paths selected. Select at least one path.": "Nenhum caminho selecionado. Selecione pelo menos um caminho.",
"No pending changes": "Sem alterações pendentes",
"No pending paths to schedule for reboot.": "Não há caminhos pendentes para agendar a reinicialização.", "No pending paths to schedule for reboot.": "Não há caminhos pendentes para agendar a reinicialização.",
"No permanent NFS mounts configured.": "Nenhuma montagem NFS permanente configurada.", "No permanent NFS mounts configured.": "Nenhuma montagem NFS permanente configurada.",
"No permanent Samba mounts configured.": "Nenhuma montagem permanente do Samba configurada.", "No permanent Samba mounts configured.": "Nenhuma montagem permanente do Samba configurada.",
@@ -4398,6 +4425,7 @@
"OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM removido (artefatos de marionetes podem precisar de limpeza manual)", "OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM removido (artefatos de marionetes podem precisar de limpeza manual)",
"OVH server detected": "Servidor OVH detectado", "OVH server detected": "Servidor OVH detectado",
"OVH server detection and RTM installation process completed": "Processo de detecção do servidor OVH e instalação RTM concluído", "OVH server detection and RTM installation process completed": "Processo de detecção do servidor OVH e instalação RTM concluído",
"Object detector": "Detetor de objetos",
"Object detector for Frigate": "Detector de objetos para Frigate", "Object detector for Frigate": "Detector de objetos para Frigate",
"Observer is not responding on port 4357": "Observador não está respondendo na porta 4357", "Observer is not responding on port 4357": "Observador não está respondendo na porta 4357",
"Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian é um aplicativo de anotações que permite criar, vincular e organizar suas notas em seu dispositivo, com centenas de plugins e temas para personalizar seu fluxo de trabalho. Você também pode publicar suas notas online, acessá-las offline e sincronizá-las com segurança com criptografia de ponta a ponta.", "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian é um aplicativo de anotações que permite criar, vincular e organizar suas notas em seu dispositivo, com centenas de plugins e temas para personalizar seu fluxo de trabalho. Você também pode publicar suas notas online, acessá-las offline e sincronizá-las com segurança com criptografia de ponta a ponta.",
@@ -4432,7 +4460,7 @@
"Only an installed standalone OCI instance can adopt external changes": "Somente uma instância OCI autônoma instalada pode adotar alterações externas", "Only an installed standalone OCI instance can adopt external changes": "Somente uma instância OCI autônoma instalada pode adotar alterações externas",
"Only backed-up Proxmox volumes can be adopted": "Somente volumes Proxmox com backup podem ser adotados", "Only backed-up Proxmox volumes can be adopted": "Somente volumes Proxmox com backup podem ser adotados",
"Only convert to privileged if absolutely necessary for your use case.": "Converta para privilegiado apenas se for absolutamente necessário para o seu caso de uso.", "Only convert to privileged if absolutely necessary for your use case.": "Converta para privilegiado apenas se for absolutamente necessário para o seu caso de uso.",
"Only fully free disks are shown (not system-used and not referenced by VM/LXC).": "Somente discos totalmente livres são mostrados (não usados ​​pelo sistema e não referenciados pelo VM/LXC).", "Only fully free disks are shown (not system-used and not referenced by VM/LXC).": "Somente discos totalmente livres são mostrados (não usados pelo sistema e não referenciados pelo VM/LXC).",
"Only if using enterprise subscription": "Somente se estiver usando assinatura corporativa", "Only if using enterprise subscription": "Somente se estiver usando assinatura corporativa",
"Only if using no-subscription repository": "Somente se estiver usando repositório sem assinatura", "Only if using no-subscription repository": "Somente se estiver usando repositório sem assinatura",
"Only needed if you mounted the filesystem in step 6b": "Necessário apenas se você montou o sistema de arquivos na etapa 6b", "Only needed if you mounted the filesystem in step 6b": "Necessário apenas se você montou o sistema de arquivos na etapa 6b",
@@ -4523,11 +4551,17 @@
"PBS backup error log": "Log de erros de backup do PBS", "PBS backup error log": "Log de erros de backup do PBS",
"PBS backup failed.": "Falha no backup do PBS.", "PBS backup failed.": "Falha no backup do PBS.",
"PBS certificate": "certificado PBS", "PBS certificate": "certificado PBS",
"PBS datastores": "Armazenamentos de dados PBS",
"PBS encryption keyfile (show / replace / remove)": "arquivo-chave de criptografia PBS (mostrar/substituir/remover)", "PBS encryption keyfile (show / replace / remove)": "arquivo-chave de criptografia PBS (mostrar/substituir/remover)",
"PBS encryption keyfile — detailed info": "arquivo-chave de criptografia PBS – informações detalhadas", "PBS encryption keyfile — detailed info": "arquivo-chave de criptografia PBS – informações detalhadas",
"PBS extraction failed": "Falha na extração de PBS", "PBS extraction failed": "Falha na extração de PBS",
"PBS host or IP address:": "Host PBS ou endereço IP:", "PBS host or IP address:": "Host PBS ou endereço IP:",
"PBS package update": "Atualização de pacotes PBS",
"PBS package upgrade completed. Check whether a reboot is required before your next backup window.": "Atualização de pacotes PBS concluída. Verifique se é necessário reiniciar antes do próximo período de cópias de segurança.",
"PBS package upgrade failed. Review the APT output before retrying.": "A atualização de pacotes PBS falhou. Reveja a saída do APT antes de tentar novamente.",
"PBS upload": "upload PBS", "PBS upload": "upload PBS",
"PBS version and packages": "Versão e pacotes PBS",
"PBS: using the configured APT repositories without changing sources.": "PBS: a usar os repositórios APT configurados sem alterar as fontes.",
"PCI Address": "Endereço PCI", "PCI Address": "Endereço PCI",
"PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "Passagem PCI, estado TPM, configuração de inicialização em nuvem, ganchos Proxmox", "PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "Passagem PCI, estado TPM, configuração de inicialização em nuvem, ganchos Proxmox",
"PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "Passagem PCI, estado TPM, inicialização em nuvem, snapshots, ganchos específicos do Proxmox", "PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "Passagem PCI, estado TPM, inicialização em nuvem, snapshots, ganchos específicos do Proxmox",
@@ -4864,12 +4898,17 @@
"Proxmology logo applied": "Logotipo da Proxmologia aplicado", "Proxmology logo applied": "Logotipo da Proxmologia aplicado",
"Proxmox 9 system update allready": "Atualização do sistema Proxmox 9 já", "Proxmox 9 system update allready": "Atualização do sistema Proxmox 9 já",
"Proxmox APT repositories configured": "repositórios Proxmox APT configurados", "Proxmox APT repositories configured": "repositórios Proxmox APT configurados",
"Proxmox Backup Server": "Proxmox Backup Server",
"Proxmox Backup Server (PBS) destinations": "Destinos Proxmox Backup Server (PBS)", "Proxmox Backup Server (PBS) destinations": "Destinos Proxmox Backup Server (PBS)",
"Proxmox Backup Server APT repositories configured": "Repositórios APT do Proxmox Backup Server configurados",
"Proxmox Backup Server Update": "Atualização do Proxmox Backup Server",
"Proxmox Backup Server update completed.": "Atualização do Proxmox Backup Server concluída.",
"Proxmox CIFS Storage Status:": "Status de armazenamento CIFS do Proxmox:", "Proxmox CIFS Storage Status:": "Status de armazenamento CIFS do Proxmox:",
"Proxmox NFS Storage Status:": "Status de armazenamento NFS do Proxmox:", "Proxmox NFS Storage Status:": "Status de armazenamento NFS do Proxmox:",
"Proxmox System Update": "Atualização do sistema Proxmox", "Proxmox System Update": "Atualização do sistema Proxmox",
"Proxmox Update": "Atualização Proxmox", "Proxmox Update": "Atualização Proxmox",
"Proxmox VE": "Proxmox VE", "Proxmox VE": "Proxmox VE",
"Proxmox VE 8 package upgrade failed. Review the APT output before retrying.": "A atualização de pacotes do Proxmox VE 8 falhou. Reveja a saída do APT antes de tentar novamente.",
"Proxmox VE 8 system update completed successfully": "Atualização do sistema Proxmox VE 8 concluída com sucesso", "Proxmox VE 8 system update completed successfully": "Atualização do sistema Proxmox VE 8 concluída com sucesso",
"Proxmox VE 8 to 9 Manual Upgrade Guide": "Guia de atualização manual do Proxmox VE 8 a 9", "Proxmox VE 8 to 9 Manual Upgrade Guide": "Guia de atualização manual do Proxmox VE 8 a 9",
"Proxmox VE 9.x no-subscription repository created": "Repositório sem assinatura Proxmox VE 9.x criado", "Proxmox VE 9.x no-subscription repository created": "Repositório sem assinatura Proxmox VE 9.x criado",
@@ -4877,6 +4916,7 @@
"Proxmox VE configuration completed.": "Configuração do Proxmox VE concluída.", "Proxmox VE configuration completed.": "Configuração do Proxmox VE concluída.",
"Proxmox VE safe update completed": "Atualização segura do Proxmox VE concluída", "Proxmox VE safe update completed": "Atualização segura do Proxmox VE concluída",
"Proxmox auth logger service created and started": "Serviço de registrador de autenticação Proxmox criado e iniciado", "Proxmox auth logger service created and started": "Serviço de registrador de autenticação Proxmox criado e iniciado",
"Proxmox could not apply the container settings": "O Proxmox não conseguiu aplicar as definições do contentor",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "O repositório corporativo Proxmox pode estar ausente ou inacessível. Tentando mudar para sem assinatura...", "Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "O repositório corporativo Proxmox pode estar ausente ou inacessível. Tentando mudar para sem assinatura...",
"Proxmox filter configured": "Filtro Proxmox configurado", "Proxmox filter configured": "Filtro Proxmox configurado",
"Proxmox iSCSI Storage Status:": "Status de armazenamento iSCSI do Proxmox:", "Proxmox iSCSI Storage Status:": "Status de armazenamento iSCSI do Proxmox:",
@@ -4993,6 +5033,7 @@
"Rebuilding initramfs to apply nouveau blacklist before installation...": "Reconstruindo o initramfs para aplicar a lista negra do nouveau antes da instalação...", "Rebuilding initramfs to apply nouveau blacklist before installation...": "Reconstruindo o initramfs para aplicar a lista negra do nouveau antes da instalação...",
"Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "A reconstrução do contêiner e a recuperação do backup são testadas na mesma imagem; a primeira atualização para uma imagem mais recente ainda está para ser observada.", "Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "A reconstrução do contêiner e a recuperação do backup são testadas na mesma imagem; a primeira atualização para uma imagem mais recente ainda está para ser observada.",
"Recent": "Recente", "Recent": "Recente",
"Recent PBS tasks": "Tarefas PBS recentes",
"Recent Samba Servers": "Servidores Samba recentes", "Recent Samba Servers": "Servidores Samba recentes",
"Recent Samba server": "Servidor Samba recente", "Recent Samba server": "Servidor Samba recente",
"Recent logs:": "Registros recentes:", "Recent logs:": "Registros recentes:",
@@ -5052,6 +5093,7 @@
"Recreation completed. Data kept.": "Recreação concluída. Dados guardados.", "Recreation completed. Data kept.": "Recreação concluída. Dados guardados.",
"Recreation prepared": "Recreação preparada", "Recreation prepared": "Recreação preparada",
"Refresh APT index and verify repositories:": "Atualize o índice APT e verifique os repositórios:", "Refresh APT index and verify repositories:": "Atualize o índice APT e verifique os repositórios:",
"Refresh the configured APT repositories and review the PBS package upgrade? Repository files will be preserved.": "Atualizar os índices dos repositórios APT configurados e rever a atualização de pacotes PBS? Os ficheiros dos repositórios serão preservados.",
"Refresh token of the connector, from the Twingate admin console": "Token de atualização do conector, no console administrativo Twingate", "Refresh token of the connector, from the Twingate admin console": "Token de atualização do conector, no console administrativo Twingate",
"Refresh your browser (Ctrl+Shift+R) to see changes": "Atualize seu navegador (Ctrl+Shift+R) para ver as alterações", "Refresh your browser (Ctrl+Shift+R) to see changes": "Atualize seu navegador (Ctrl+Shift+R) para ver as alterações",
"Refresh your browser to see changes (server restart may be required)": "Atualize seu navegador para ver as alterações (pode ser necessário reiniciar o servidor)", "Refresh your browser to see changes (server restart may be required)": "Atualize seu navegador para ver as alterações (pode ser necessário reiniciar o servidor)",
@@ -5141,6 +5183,8 @@
"Remove saved credentials": "Remover credenciais salvas", "Remove saved credentials": "Remover credenciais salvas",
"Remove subscription banner": "Remover banner de assinatura", "Remove subscription banner": "Remover banner de assinatura",
"Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Remover o aplicativo? Seus discos contêineres são excluídos e somente um backup pode recuperá-los.", "Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Remover o aplicativo? Seus discos contêineres são excluídos e somente um backup pode recuperá-los.",
"Remove the lock left on a VM by an interrupted task": "Remover o bloqueio deixado numa VM por uma tarefa interrompida",
"Remove the lock left on a container by an interrupted task": "Remover o bloqueio deixado num contentor por uma tarefa interrompida",
"Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Remover as credenciais salvas para {registry}?\nOs aplicativos em execução continuarão funcionando.", "Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Remover as credenciais salvas para {registry}?\nOs aplicativos em execução continuarão funcionando.",
"Remove the unprivileged flag from configuration:": "Remova o sinalizador sem privilégios da configuração:", "Remove the unprivileged flag from configuration:": "Remova o sinalizador sem privilégios da configuração:",
"Remove unused packages and their config": "Remova pacotes não utilizados e suas configurações", "Remove unused packages and their config": "Remova pacotes não utilizados e suas configurações",
@@ -5222,6 +5266,7 @@
"Repository helper unavailable. NVIDIA installation stopped.": "Auxiliar de repositório indisponível. A instalação do NVIDIA foi interrompida.", "Repository helper unavailable. NVIDIA installation stopped.": "Auxiliar de repositório indisponível. A instalação do NVIDIA foi interrompida.",
"Repository policy returned an unexpected result.": "A política do repositório retornou um resultado inesperado.", "Repository policy returned an unexpected result.": "A política do repositório retornou um resultado inesperado.",
"Repository ready.": "Repositório pronto.", "Repository ready.": "Repositório pronto.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.": "as fontes do repositório foram alteradas, mas a atualização da lista de pacotes APT falhou. Operação interrompida;fontes não foram revertidas. Inspecione Administração > Repositórios e tente apt-get update novamente antes de continuar.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "As fontes do repositório foram alteradas, mas a atualização da lista de pacotes APT falhou. Operação interrompida; as fontes não foram revertidas. Verifique Nó > Atualizações > Repositórios e tente apt-get update novamente antes de continuar.", "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "As fontes do repositório foram alteradas, mas a atualização da lista de pacotes APT falhou. Operação interrompida; as fontes não foram revertidas. Verifique Nó > Atualizações > Repositórios e tente apt-get update novamente antes de continuar.",
"Repository switch declined; no APT source changed.": "Troca de repositório recusada; nenhuma fonte APT foi alterada.", "Repository switch declined; no APT source changed.": "Troca de repositório recusada; nenhuma fonte APT foi alterada.",
"Repository:": "Repositório:", "Repository:": "Repositório:",
@@ -5316,7 +5361,9 @@
"Reverting ZFS ARC tuning...": "Revertendo ajuste do ZFS ARC...", "Reverting ZFS ARC tuning...": "Revertendo ajuste do ZFS ARC...",
"Reverting pigz wrapper...": "Revertendo o wrapper pigz...", "Reverting pigz wrapper...": "Revertendo o wrapper pigz...",
"Reverting vzdump speed tuning...": "Revertendo o ajuste de velocidade do vzdump...", "Reverting vzdump speed tuning...": "Revertendo o ajuste de velocidade do vzdump...",
"Review and apply": "Rever e aplicar",
"Review external OCI changes": "revisar alterações externas do OCI", "Review external OCI changes": "revisar alterações externas do OCI",
"Review package upgrade": "Rever a atualização de pacotes",
"Review passthrough config files": "Revise os arquivos de configuração de passagem", "Review passthrough config files": "Revise os arquivos de configuração de passagem",
"Review what will be removed": "Revise o que será removido", "Review what will be removed": "Revise o que será removido",
"Rip DVD and Blu-ray media from a browser": "Extrair mídia de DVD e Blu-ray de um navegador", "Rip DVD and Blu-ray media from a browser": "Extrair mídia de DVD e Blu-ray de um navegador",
@@ -5359,6 +5406,7 @@
"Run the following inside the VM:": "Execute o seguinte dentro da VM:", "Run the following inside the VM:": "Execute o seguinte dentro da VM:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "execute a opção de canal de lançamento a partir de uma sessão SSH ou do console do host Proxmox com:", "Run the release-channel switch from an SSH session or the Proxmox host console with:": "execute a opção de canal de lançamento a partir de uma sessão SSH ou do console do host Proxmox com:",
"Run the update from an SSH session or the Proxmox host console with:": "execute a atualização a partir de uma sessão SSH ou do console do host Proxmox com:", "Run the update from an SSH session or the Proxmox host console with:": "execute a atualização a partir de uma sessão SSH ou do console do host Proxmox com:",
"Run this operation as root.": "Execute esta operação como root.",
"Run upgrade checklist script:": "Execute o script da lista de verificação de atualização:", "Run upgrade checklist script:": "Execute o script da lista de verificação de atualização:",
"Running": "Correndo", "Running": "Correndo",
"Running Lynis security audit...": "Executando auditoria de segurança do Lynis...", "Running Lynis security audit...": "Executando auditoria de segurança do Lynis...",
@@ -5773,6 +5821,7 @@
"ShareCoveX panel": "Painel ShareCoveX", "ShareCoveX panel": "Painel ShareCoveX",
"Shared Directory Ready:": "Diretório compartilhado pronto:", "Shared Directory Ready:": "Diretório compartilhado pronto:",
"Shared Group": "Grupo Compartilhado", "Shared Group": "Grupo Compartilhado",
"Shared directories": "Diretórios partilhados",
"Shared directory created:": "Directório partilhado criado:", "Shared directory created:": "Directório partilhado criado:",
"Shared directory for consume and export": "Diretório compartilhado para consumir e exportar", "Shared directory for consume and export": "Diretório compartilhado para consumir e exportar",
"Shared directory for copy/sync operations": "Diretório compartilhado para copiar/sincronizar operations", "Shared directory for copy/sync operations": "Diretório compartilhado para copiar/sincronizar operations",
@@ -5802,6 +5851,7 @@
"Should show pve-manager/9.x.x": "Deve mostrar pve-manager/9.x.x", "Should show pve-manager/9.x.x": "Deve mostrar pve-manager/9.x.x",
"Shoutrrr addresses for the notifications (optional)": "Endereços Shoutrrr para as notificações (opcional)", "Shoutrrr addresses for the notifications (optional)": "Endereços Shoutrrr para as notificações (opcional)",
"Show All Scripts": "Mostrar todos os scripts", "Show All Scripts": "Mostrar todos os scripts",
"Show CT users and their UID/GID on the host": "Utilizadores do CT e UID/GID no host",
"Show CT users in table format": "Mostrar usuários do CT em formato de tabela", "Show CT users in table format": "Mostrar usuários do CT em formato de tabela",
"Show Container Privilege Status": "Mostrar status de privilégio do contêiner", "Show Container Privilege Status": "Mostrar status de privilégio do contêiner",
"Show DNS resolution status": "Mostrar status de resolução DNS", "Show DNS resolution status": "Mostrar status de resolução DNS",
@@ -5963,6 +6013,7 @@
"Start the VM to begin Windows installation from the mounted ISO.": "Inicie a VM para iniciar a instalação do Windows a partir do ISO montado.", "Start the VM to begin Windows installation from the mounted ISO.": "Inicie a VM para iniciar a instalação do Windows a partir do ISO montado.",
"Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Iniciar os aplicativos assim que estiverem registrados? Responda Não se os contêineres originais ainda estiverem em execução em outro host: ambos usariam os mesmos endereços.", "Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Iniciar os aplicativos assim que estiverem registrados? Responda Não se os contêineres originais ainda estiverem em execução em outro host: ambos usariam os mesmos endereços.",
"Start the converted container:": "Inicie o contêiner convertido:", "Start the converted container:": "Inicie o contêiner convertido:",
"Start the interactive Proxmox package upgrade": "Iniciar a atualização interativa de pacotes do Proxmox",
"Start the main system upgrade:": "Inicie a atualização principal do sistema:", "Start the main system upgrade:": "Inicie a atualização principal do sistema:",
"Start the stack with Proxmox": "Iniciar a pilha com Proxmox", "Start the stack with Proxmox": "Iniciar a pilha com Proxmox",
"Start uploading to PBS — sets a recovery passphrase": "comece a enviar para o PBS – define uma senha de recuperação", "Start uploading to PBS — sets a recovery passphrase": "comece a enviar para o PBS – define uma senha de recuperação",
@@ -6242,6 +6293,7 @@
"The Paperless persistent volumes need at least 8 GB": "Os volumes persistentes sem papel precisam de pelo menos 8 GB", "The Paperless persistent volumes need at least 8 GB": "Os volumes persistentes sem papel precisam de pelo menos 8 GB",
"The PostgreSQL volume needs at least 4 GB": "O volume PostgreSQL precisa de pelo menos 4 GB", "The PostgreSQL volume needs at least 4 GB": "O volume PostgreSQL precisa de pelo menos 4 GB",
"The PostgreSQL volume needs at least 8 GB": "O volume PostgreSQL precisa de pelo menos 8 GB", "The PostgreSQL volume needs at least 8 GB": "O volume PostgreSQL precisa de pelo menos 8 GB",
"The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "A atualização do Proxmox VE 8 não terminou. A atualização para o Proxmox VE 9 não foi iniciada.",
"The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "O chaveiro de arquivo Proxmox está faltando;A instalação do Ceph não pode continuar com segurança", "The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "O chaveiro de arquivo Proxmox está faltando;A instalação do Ceph não pode continuar com segurança",
"The Proxmox inventory and the local configurations differ": "O inventário Proxmox e as configurações locais diferem", "The Proxmox inventory and the local configurations differ": "O inventário Proxmox e as configurações locais diferem",
"The ROCm image has no support for the AMD GPU of this host:": "A imagem ROCm não tem suporte para a GPU AMD deste host:", "The ROCm image has no support for the AMD GPU of this host:": "A imagem ROCm não tem suporte para a GPU AMD deste host:",
@@ -6312,6 +6364,7 @@
"The build could not be checked beforehand; continuing without that check.": "A compilação não pôde ser verificada antecipadamente;continuando sem essa verificação.", "The build could not be checked beforehand; continuing without that check.": "A compilação não pôde ser verificada antecipadamente;continuando sem essa verificação.",
"The cached image does not match the current digest": "A imagem em cache não corresponde à digest atual", "The cached image does not match the current digest": "A imagem em cache não corresponde à digest atual",
"The cached image is damaged; it will be downloaded again.": "A imagem em cache está danificada; será transferida novamente.", "The cached image is damaged; it will be downloaded again.": "A imagem em cache está danificada; será transferida novamente.",
"The changes need the application installer": "As alterações requerem o instalador da aplicação",
"The character device does not exist:": "O dispositivo de caracteres não existe:", "The character device does not exist:": "O dispositivo de caracteres não existe:",
"The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "O comando pede uma senha que não seja ecoada. Depois de criar os usuários, a interface web pede um nome de usuário e uma senha.", "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "O comando pede uma senha que não seja ecoada. Depois de criar os usuários, a interface web pede um nome de usuário e uma senha.",
"The command does not name an image": "O comando não nomeia uma imagem", "The command does not name an image": "O comando não nomeia uma imagem",
@@ -6431,8 +6484,10 @@
"The host bind source does not exist:": "O código de ligação da máquina não existe:", "The host bind source does not exist:": "O código de ligação da máquina não existe:",
"The host bind source is not a regular file or directory:": "O código de ligação da máquina não é um ficheiro ou directório regular:", "The host bind source is not a regular file or directory:": "O código de ligação da máquina não é um ficheiro ou directório regular:",
"The host directory changed before it was mounted": "A pasta da máquina mudou antes de ser montada", "The host directory changed before it was mounted": "A pasta da máquina mudou antes de ser montada",
"The host directory does not exist": "O diretório do anfitrião não existe",
"The host directory may not be accessible from an unprivileged container.": "O diretório host pode não estar acessível a partir de um contêiner sem privilégios.", "The host directory may not be accessible from an unprivileged container.": "O diretório host pode não estar acessível a partir de um contêiner sem privilégios.",
"The host has no IPv4 address on the selected bridge": "A máquina não tem endereço IPv4 na ponte seleccionada", "The host has no IPv4 address on the selected bridge": "A máquina não tem endereço IPv4 na ponte seleccionada",
"The host loses access to them. If the group includes a network or storage controller the host needs, cancel at the summary.": "O host perde o acesso a eles. Se o grupo incluir um controlador de rede ou de armazenamento de que o host precisa, cancele no resumo.",
"The host monitor does not see the real host memory": "O monitor da máquina não vê a memória verdadeira da máquina", "The host monitor does not see the real host memory": "O monitor da máquina não vê a memória verdadeira da máquina",
"The host monitor does not share this host namespace:": "O monitor da máquina não compartilha este espaço de nomes da máquina:", "The host monitor does not share this host namespace:": "O monitor da máquina não compartilha este espaço de nomes da máquina:",
"The host monitor include is unknown or differs from the saved record": "O include do monitor do host é desconhecido ou difere do registro salvo", "The host monitor include is unknown or differs from the saved record": "O include do monitor do host é desconhecido ou difere do registro salvo",
@@ -6622,6 +6677,7 @@
"The repair must preserve the image dependencies:": "O reparo deve preservar as dependências da imagem:", "The repair must preserve the image dependencies:": "O reparo deve preservar as dependências da imagem:",
"The requested VMID block is already in use": "O bloco VMID solicitado já está em uso", "The requested VMID block is already in use": "O bloco VMID solicitado já está em uso",
"The requested machine learning GPU profile is not working; it is not replaced by CPU": "O perfil de aprendizado de máquina solicitado GPU não está funcionando; não é substituído por CPU", "The requested machine learning GPU profile is not working; it is not replaced by CPU": "O perfil de aprendizado de máquina solicitado GPU não está funcionando; não é substituído por CPU",
"The resource edit could not be fully rolled back. Open Manage again to review and import the current Proxmox settings.": "Não foi possível reverter totalmente a alteração dos recursos. Volte a abrir o menu de gestão para rever e importar as definições atuais do Proxmox.",
"The restored service did not pass its health check": "O serviço restaurado não passou no seu exame de saúde", "The restored service did not pass its health check": "O serviço restaurado não passou no seu exame de saúde",
"The restored service stopped; the recovery is not confirmed": "O serviço restaurado parou; a recuperação não é confirmada", "The restored service stopped; the recovery is not confirmed": "O serviço restaurado parou; a recuperação não é confirmada",
"The reviewed Tandoor stack does not require a privileged LXC.": "A pilha Tandoor revisada não require um LXC privilegiado.", "The reviewed Tandoor stack does not require a privileged LXC.": "A pilha Tandoor revisada não require um LXC privilegiado.",
@@ -6667,6 +6723,7 @@
"The selected VM": "A VM selecionada", "The selected VM": "A VM selecionada",
"The selected VM is running.": "A VM selecionada está em execução.", "The selected VM is running.": "A VM selecionada está em execução.",
"The selected base folder does not exist and could not be created:": "A pasta base selecionada não existe e não pôde ser criada:", "The selected base folder does not exist and could not be created:": "A pasta base selecionada não existe e não pôde ser criada:",
"The selected bridge does not exist": "A bridge selecionada não existe",
"The selected bridge has no IPv4 subnet for the host-monitor firewall": "A ponte selecionada não possui sub-rede IPv4 para o firewall do monitor host", "The selected bridge has no IPv4 subnet for the host-monitor firewall": "A ponte selecionada não possui sub-rede IPv4 para o firewall do monitor host",
"The selected configuration needs to start the LXC during the installation": "A configuração selecionada precisa iniciar o LXC durante a instalação", "The selected configuration needs to start the LXC during the installation": "A configuração selecionada precisa iniciar o LXC durante a instalação",
"The selected container is unprivileged. A privileged container is required for direct device passthrough.": "O contêiner selecionado não tem privilégios. Um contêiner privilegiado é necessário para passagem direta do dispositivo.", "The selected container is unprivileged. A privileged container is required for direct device passthrough.": "O contêiner selecionado não tem privilégios. Um contêiner privilegiado é necessário para passagem direta do dispositivo.",
@@ -6689,6 +6746,7 @@
"The service builds its own image; only a published image can be installed": "O serviço constrói sua própria imagem; somente uma imagem publicada pode ser instalada", "The service builds its own image; only a published image can be installed": "O serviço constrói sua própria imagem; somente uma imagem publicada pode ser instalada",
"The service declares no image:": "O serviço não declara imagem:", "The service declares no image:": "O serviço não declara imagem:",
"The setting has no final value:": "A configuração não tem valor final:", "The setting has no final value:": "A configuração não tem valor final:",
"The settings could not be fully rolled back. Review the container and its saved modification before continuing.": "Não foi possível repor todas as definições. Reveja o contentor e a alteração guardada antes de continuar.",
"The share already exists in smb.conf:": "O compartilhamento já existe em smb.conf:", "The share already exists in smb.conf:": "O compartilhamento já existe em smb.conf:",
"The shared destination is not a directory": "O destino compartilhado não é um diretório", "The shared destination is not a directory": "O destino compartilhado não é um diretório",
"The shared directory points to a protected host path": "A pasta partilhada aponta para uma localização protegida da máquina", "The shared directory points to a protected host path": "A pasta partilhada aponta para uma localização protegida da máquina",
@@ -6756,19 +6814,24 @@
"These Proxmox resources were added outside ProxMenux:": "Estes recursos Proxmox foram adicionados fora do ProxMenux:", "These Proxmox resources were added outside ProxMenux:": "Estes recursos Proxmox foram adicionados fora do ProxMenux:",
"These VMIDs are not free:": "Estes VMIDs não são gratuitos:", "These VMIDs are not free:": "Estes VMIDs não são gratuitos:",
"These are the changes that will be made": "Estas são as mudanças que serão feitas", "These are the changes that will be made": "Estas são as mudanças que serão feitas",
"These changes were made in Proxmox outside ProxMenux:": "Essas alterações foram feitas no Proxmox fora do ProxMenux:",
"These container mount paths have an extension-like suffix:": "Estes caminhos de montagem do contêiner têm um sufixo parecido com uma extensão:", "These container mount paths have an extension-like suffix:": "Estes caminhos de montagem do contêiner têm um sufixo parecido com uma extensão:",
"These containers were restored from a backup and this host has no record of their OCI application:": "Esses contêineres foram restaurados a partir de um backup e este host não possui registro de seu aplicativo OCI:", "These containers were restored from a backup and this host has no record of their OCI application:": "Esses contêineres foram restaurados a partir de um backup e este host não possui registro de seu aplicativo OCI:",
"These devices share the IOMMU group of the selected GPU and will be assigned to the VM with it:": "Estes dispositivos partilham o grupo IOMMU da GPU selecionada e serão atribuídos à VM juntamente com ela:",
"These host backups run with this job and expect its storage:": "Esses backups de host são executados com este trabalho e esperam seu armazenamento:", "These host backups run with this job and expect its storage:": "Esses backups de host são executados com este trabalho e esperam seu armazenamento:",
"These host backups run with this job and will stop running:": "Esses backups de host são executados com este trabalho e deixarão de ser executados:", "These host backups run with this job and will stop running:": "Esses backups de host são executados com este trabalho e deixarão de ser executados:",
"These interface configurations will be removed": "Essas configurações de interface serão removidas", "These interface configurations will be removed": "Essas configurações de interface serão removidas",
"These manual changes cannot be adopted safely:": "Estas alterações manuais não podem ser adotadas com segurança:", "These manual changes cannot be adopted safely:": "Estas alterações manuais não podem ser adotadas com segurança:",
"These paths will not be restored live and will be extracted for manual recovery.": "Esses caminhos não serão restaurados ao vivo e serão extraídos para recuperação manual.", "These paths will not be restored live and will be extracted for manual recovery.": "Esses caminhos não serão restaurados ao vivo e serão extraídos para recuperação manual.",
"These settings require an application-specific adaptation. The image was not downloaded and the container was not recreated.": "Estas definições requerem uma adaptação específica da aplicação. A imagem não foi transferida e o contentor não foi recriado.",
"These values are asked during the installation:": "Estes valores são questionados durante a instalação:", "These values are asked during the installation:": "Estes valores são questionados durante a instalação:",
"They are described in the image documentation:": "Estão descritos na documentação da imagem:", "They are described in the image documentation:": "Estão descritos na documentação da imagem:",
"They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Eles serão adicionados ao oci-compose antes de continuar. Recursos não suportados ou alterados não são importados.", "They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Eles serão adicionados ao oci-compose antes de continuar. Recursos não suportados ou alterados não são importados.",
"They will be saved in oci-compose before continuing.": "Elas serão salvas no oci-compose antes de continuar.",
"This CIFS share is mounted with restrictive permissions.": "Este compartilhamento CIFS é montado com permissões restritivas.", "This CIFS share is mounted with restrictive permissions.": "Este compartilhamento CIFS é montado com permissões restritivas.",
"This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Esta GPU é considerada incompatível com a passagem de GPU para uma VM no ProxMenux.", "This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Esta GPU é considerada incompatível com a passagem de GPU para uma VM no ProxMenux.",
"This NFS share is fully restricted — even the host root cannot write to it.": "Este compartilhamento NFS é totalmente restrito — até mesmo o host root não pode gravar nele.", "This NFS share is fully restricted — even the host root cannot write to it.": "Este compartilhamento NFS é totalmente restrito — até mesmo o host root não pode gravar nele.",
"This Proxmox Backup Server has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PBS source will be disabled.": "Este Proxmox Backup Server não tem assinatura ativa. Mudar para o repositório sem assinatura? A fonte Enterprise do PBS, inacessível, será desativada.",
"This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Esta VM requer etapas extras de instalação. Consulte o guia de instalação em:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144", "This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Esta VM requer etapas extras de instalação. Consulte o guia de instalação em:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144",
"This account is for {registry}. Enter an image from that registry.": "Esta conta é para {registry}. Insira uma imagem desse registro.", "This account is for {registry}. Enter an image from that registry.": "Esta conta é para {registry}. Insira uma imagem desse registro.",
"This action will:": "Esta ação irá:", "This action will:": "Esta ação irá:",
@@ -6830,10 +6893,13 @@
"This may take several minutes...": "Isso pode levar vários minutos...", "This may take several minutes...": "Isso pode levar vários minutos...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Isso significa que o Proxmox lida com o ciclo de vida da montagem nativamente (não é necessário /etc/fstab manual para armazenamentos de host NFS/CIFS).", "This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Isso significa que o Proxmox lida com o ciclo de vida da montagem nativamente (não é necessário /etc/fstab manual para armazenamentos de host NFS/CIFS).",
"This means the credentials are incorrect.": "Isso significa que as credenciais estão incorretas.", "This means the credentials are incorrect.": "Isso significa que as credenciais estão incorretas.",
"This menu requires Proxmox Backup Server.": "Este menu requer Proxmox Backup Server.",
"This might indicate network connectivity issues.": "Isso pode indicar problemas de conectividade de rede.", "This might indicate network connectivity issues.": "Isso pode indicar problemas de conectividade de rede.",
"This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Este monitor usa um LXC privilegiado, compartilha processos e rede com Proxmox e desativa AppArmor no CT. Ele usa o endereço IP e firewall do host. Uma imagem comprometida poderia afetar o host; não expor sua interface web para a Internet.", "This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Este monitor usa um LXC privilegiado, compartilha processos e rede com Proxmox e desativa AppArmor no CT. Ele usa o endereço IP e firewall do host. Uma imagem comprometida poderia afetar o host; não expor sua interface web para a Internet.",
"This only removes the schedule. Existing backup archives are NOT deleted.": "Isso apenas remove a programação. Os arquivos de backup existentes NÃO são excluídos.", "This only removes the schedule. Existing backup archives are NOT deleted.": "Isso apenas remove a programação. Os arquivos de backup existentes NÃO são excluídos.",
"This operation may take several minutes and requires internet connectivity.": "Esta operação pode demorar vários minutos e requer conectividade com a Internet.", "This operation may take several minutes and requires internet connectivity.": "Esta operação pode demorar vários minutos e requer conectividade com a Internet.",
"This operation requires a Proxmox VE host. Use the PBS menu on Proxmox Backup Server.": "Esta operação requer um anfitrião Proxmox VE. Utilize o menu PBS no Proxmox Backup Server.",
"This operation supports PBS 3 on Debian 12 and PBS 4 on Debian 13.": "Esta operação suporta PBS 3 em Debian 12 e PBS 4 em Debian 13.",
"This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Este pacote foi instalado por versões mais antigas do instalador ProxMenux Coral que colocava o driver do kernel M.2 em todos os sistemas, incluindo configurações somente USB.Não é necessário para dispositivos Coral USB, que usam apenas libedgetpu1-std / libedgetpu1-max.", "This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Este pacote foi instalado por versões mais antigas do instalador ProxMenux Coral que colocava o driver do kernel M.2 em todos os sistemas, incluindo configurações somente USB.Não é necessário para dispositivos Coral USB, que usam apenas libedgetpu1-std / libedgetpu1-max.",
"This passphrase is the ONLY way to access encrypted Borg backups.": "Esta senha é a ÚNICA maneira de acessar backups criptografados do Borg.", "This passphrase is the ONLY way to access encrypted Borg backups.": "Esta senha é a ÚNICA maneira de acessar backups criptografados do Borg.",
"This path is already used as a mount point in this container.": "Este caminho já é usado como ponto de montagem neste contêiner.", "This path is already used as a mount point in this container.": "Este caminho já é usado como ponto de montagem neste contêiner.",
@@ -6849,6 +6915,7 @@
"This script cannot be executed from the Proxmox web terminal.": "Este script não pode ser executado no terminal web Proxmox.", "This script cannot be executed from the Proxmox web terminal.": "Este script não pode ser executado no terminal web Proxmox.",
"This script must be run as root.": "Este script deve ser executado como root.", "This script must be run as root.": "Este script deve ser executado como root.",
"This script must be run on a Proxmox host.": "Este script deve ser executado em um host Proxmox.", "This script must be run on a Proxmox host.": "Este script deve ser executado em um host Proxmox.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox Backup Server": "Este script aplicará as seguintes otimizações e ajustes avançados ao seu Proxmox Backup Server",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Este script aplicará as seguintes otimizações e ajustes avançados ao seu servidor Proxmox VE", "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Este script aplicará as seguintes otimizações e ajustes avançados ao seu servidor Proxmox VE",
"This script will update your Proxmox VE system with advanced options:": "Este script atualizará seu sistema Proxmox VE com opções avançadas:", "This script will update your Proxmox VE system with advanced options:": "Este script atualizará seu sistema Proxmox VE com opções avançadas:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Esta sessão está sendo executada no terminal Monitor. Executá-lo a partir daqui cortaria a conexão no meio da instalação e deixaria o switch quebrado.", "This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Esta sessão está sendo executada no terminal Monitor. Executá-lo a partir daqui cortaria a conexão no meio da instalação e deixaria o switch quebrado.",
@@ -6935,7 +7002,7 @@
"Transaction log:": "Registo de transacções:", "Transaction log:": "Registo de transacções:",
"Translation files:": "Arquivos de tradução:", "Translation files:": "Arquivos de tradução:",
"Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission é projetado para uso fácil e poderoso. Transmission tem os recursos que você deseja de um cliente BitTorrent: criptografia, uma interface web, troca de pares, links magnéticos, DHT, μTP, encaminhamento de portas UPnP e NAT-PMP, suporte a webseed, diretórios de relógio, edição de rastreadores, limites de velocidade globais e per-torrent, e muito mais.", "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission é projetado para uso fácil e poderoso. Transmission tem os recursos que você deseja de um cliente BitTorrent: criptografia, uma interface web, troca de pares, links magnéticos, DHT, μTP, encaminhamento de portas UPnP e NAT-PMP, suporte a webseed, diretórios de relógio, edição de rastreadores, limites de velocidade globais e per-torrent, e muito mais.",
"Tried pvesm path and manual detection methods": "Tentei caminho pvesm e métodos de detecção manual", "Tried pvesm path and manual detection methods": "Foram tentados pvesm path e os métodos de deteção manual",
"Trust this certificate and save it for scheduled backups?": "Confiar neste certificado e salvá-lo para backups agendados?", "Trust this certificate and save it for scheduled backups?": "Confiar neste certificado e salvá-lo para backups agendados?",
"Try Again": "Tente novamente", "Try Again": "Tente novamente",
"Try a different search term.": "Experimente um termo de pesquisa diferente.", "Try a different search term.": "Experimente um termo de pesquisa diferente.",
@@ -6980,6 +7047,7 @@
"Unable to identify the root filesystem. Aborting.": "Não foi possível identificar o sistema de arquivos raiz. Abortando.", "Unable to identify the root filesystem. Aborting.": "Não foi possível identificar o sistema de arquivos raiz. Abortando.",
"Unable to resolve system ZFS pool disks. Aborting.": "Não foi possível resolver os discos do pool ZFS do sistema. Abortando.", "Unable to resolve system ZFS pool disks. Aborting.": "Não foi possível resolver os discos do pool ZFS do sistema. Abortando.",
"Unable to resolve system disk topology. Aborting.": "Não é possível resolver a topologia do disco do sistema. Abortando.", "Unable to resolve system disk topology. Aborting.": "Não é possível resolver a topologia do disco do sistema. Abortando.",
"Unapplied changes": "Alterações por aplicar",
"Understand the security implications of privileged containers": "Entenda as implicações de segurança de contêineres privilegiados", "Understand the security implications of privileged containers": "Entenda as implicações de segurança de contêineres privilegiados",
"Unexpected Proxmox inventory; recovery blocked": "Inesperado inventário Proxmox; recuperação bloqueada", "Unexpected Proxmox inventory; recovery blocked": "Inesperado inventário Proxmox; recuperação bloqueada",
"Unexpected formatting directory in the new Valkey volume": "Directório de formatação inesperado no novo volume Valkey", "Unexpected formatting directory in the new Valkey volume": "Directório de formatação inesperado no novo volume Valkey",
@@ -7079,6 +7147,7 @@
"Unsupported OCI-LXC AppArmor profile:": "Perfil do AppArmor OCI-LXC não suportado:", "Unsupported OCI-LXC AppArmor profile:": "Perfil do AppArmor OCI-LXC não suportado:",
"Unsupported OCI-LXC seccomp profile:": "Perfil OCI-LXC seccomp não suportado:", "Unsupported OCI-LXC seccomp profile:": "Perfil OCI-LXC seccomp não suportado:",
"Unsupported Terminal": "Terminal não suportado", "Unsupported Terminal": "Terminal não suportado",
"Unsupported architecture for Figurine:": "Arquitetura não suportada pelo Figurine:",
"Unsupported architecture:": "Arquitetura não suportada:", "Unsupported architecture:": "Arquitetura não suportada:",
"Unsupported backup compression": "Compressão de backup não suportada", "Unsupported backup compression": "Compressão de backup não suportada",
"Unsupported credential pattern:": "Padrão credential não suportado:", "Unsupported credential pattern:": "Padrão credential não suportado:",
@@ -7094,6 +7163,7 @@
"Unsupported mount type": "Tipo de montagem não suportado", "Unsupported mount type": "Tipo de montagem não suportado",
"Unsupported mount type:": "Tipo de montagem não suportado:", "Unsupported mount type:": "Tipo de montagem não suportado:",
"Unsupported native device type:": "Tipo de dispositivo nativo não suportado:", "Unsupported native device type:": "Tipo de dispositivo nativo não suportado:",
"Unsupported network settings": "Definições de rede não suportadas",
"Unsupported operation": "operation não suportado", "Unsupported operation": "operation não suportado",
"Unsupported or unknown Proxmox version; no repository changed.": "Versão Proxmox não suportada ou desconhecida; nenhum repositório foi alterado.", "Unsupported or unknown Proxmox version; no repository changed.": "Versão Proxmox não suportada ou desconhecida; nenhum repositório foi alterado.",
"Unsupported output format:": "Formato de saída não suportado:", "Unsupported output format:": "Formato de saída não suportado:",
@@ -7121,6 +7191,7 @@
"Update NVIDIA in LXC Containers": "Atualizar NVIDIA em contêineres LXC", "Update NVIDIA in LXC Containers": "Atualizar NVIDIA em contêineres LXC",
"Update OCI": "Actualizar OCI", "Update OCI": "Actualizar OCI",
"Update OCI stack": "Actualizar a pilha OCI", "Update OCI stack": "Actualizar a pilha OCI",
"Update PBS using configured repositories": "Atualizar PBS com os repositórios configurados",
"Update PVE enterprise repository (Only if using enterprise):": "Atualize o repositório corporativo PVE (somente se estiver usando corporativo):", "Update PVE enterprise repository (Only if using enterprise):": "Atualize o repositório corporativo PVE (somente se estiver usando corporativo):",
"Update Proxmox VE Appliance Manager": "Atualizar Proxmox VE Appliance Manager", "Update Proxmox VE Appliance Manager": "Atualizar Proxmox VE Appliance Manager",
"Update Proxmox package lists": "Atualizar listas de pacotes Proxmox", "Update Proxmox package lists": "Atualizar listas de pacotes Proxmox",
@@ -7151,7 +7222,7 @@
"Updates file is empty or unreadable.": "O arquivo de atualizações está vazio ou ilegível.", "Updates file is empty or unreadable.": "O arquivo de atualizações está vazio ou ilegível.",
"Updating": "Actualização", "Updating": "Actualização",
"Updating APT package lists...": "Atualizando listas de pacotes APT...", "Updating APT package lists...": "Atualizando listas de pacotes APT...",
"Updating Debian Bookworm → Trixie in sources.list...": "Atualizando Debian Bookworm → Trixie em fontes.list...", "Updating Debian Bookworm → Trixie in sources.list...": "A atualizar Debian Bookworm → Trixie em sources.list...",
"Updating Figurine binary...": "Atualizando o binário da estatueta...", "Updating Figurine binary...": "Atualizando o binário da estatueta...",
"Updating Lynis to the latest version...": "Atualizando Lynis para a versão mais recente...", "Updating Lynis to the latest version...": "Atualizando Lynis para a versão mais recente...",
"Updating PVE application manager...": "Atualizando o gerenciador de aplicativos PVE...", "Updating PVE application manager...": "Atualizando o gerenciador de aplicativos PVE...",
@@ -7173,6 +7244,7 @@
"Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Assistente de atualização: Proxmox VE 8 → 9 (Trixie)", "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Assistente de atualização: Proxmox VE 8 → 9 (Trixie)",
"Upgrade canceled by user": "Atualização cancelada pelo usuário", "Upgrade canceled by user": "Atualização cancelada pelo usuário",
"Upgrade one node at a time": "Atualize um nó por vez", "Upgrade one node at a time": "Atualize um nó por vez",
"Upgrade the system, offering the": "Atualize o sistema, oferecendo o",
"Upgrade to Proxmox VE 9": "Atualize para Proxmox VE 9", "Upgrade to Proxmox VE 9": "Atualize para Proxmox VE 9",
"Upgrading NVIDIA utils (Arch)...": "Atualizando utilitários NVIDIA (Arch)...", "Upgrading NVIDIA utils (Arch)...": "Atualizando utilitários NVIDIA (Arch)...",
"Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Carregar uma cópia criptografada da chave para o PBS para que você possa recuperá-la em um host reinstalado com apenas uma senha?", "Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Carregar uma cópia criptografada da chave para o PBS para que você possa recuperá-la em um host reinstalado com apenas uma senha?",
@@ -7610,6 +7682,7 @@
"active VF(s)": "FV ativa(s)", "active VF(s)": "FV ativa(s)",
"active VFs": "FVs ativas", "active VFs": "FVs ativas",
"active Virtual Functions. Changing its driver binding would destroy every VF.": "Funções virtuais ativas. Alterar a ligação do driver destruiria todos os VF.", "active Virtual Functions. Changing its driver binding would destroy every VF.": "Funções virtuais ativas. Alterar a ligação do driver destruiria todos os VF.",
"added in Proxmox": "adicionada no Proxmox",
"after copy; source tree is incomplete.": "após cópia; a árvore de origem está incompleta.", "after copy; source tree is incomplete.": "após cópia; a árvore de origem está incompleta.",
"after update.": "após a atualização.", "after update.": "após a atualização.",
"all guests": "todos os convidados", "all guests": "todos os convidados",
@@ -7954,8 +8027,8 @@
"pvesm command not found. This should not happen on Proxmox.": "Comando pvesm não encontrado. Isso não deveria acontecer no Proxmox.", "pvesm command not found. This should not happen on Proxmox.": "Comando pvesm não encontrado. Isso não deveria acontecer no Proxmox.",
"pvesm not available.": "pvesm não disponível.", "pvesm not available.": "pvesm não disponível.",
"pvesm not found.": "pvesm não encontrado.", "pvesm not found.": "pvesm não encontrado.",
"pvesm path failed, trying manual detection...": "caminho pvesm falhou, tentando detecção manual...", "pvesm path failed, trying manual detection...": "O pvesm path falhou; a tentar a deteção manual...",
"pvesm status failed": "status pvesm falhou", "pvesm status failed": "O pvesm status falhou",
"pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad é um gerenciador de download livre e Open Source escrito em Python e projetado para ser extremamente leve, facilmente extensível e totalmente gerenciável via web.", "pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad é um gerenciador de download livre e Open Source escrito em Python e projetado para ser extremamente leve, facilmente extensível e totalmente gerenciável via web.",
"pyLoad web interface": "interface web pyLoad", "pyLoad web interface": "interface web pyLoad",
"qBittorrent WebUI password (user: admin)": "Senha do WebUI do qBittorrent (utilizador: administrador)", "qBittorrent WebUI password (user: admin)": "Senha do WebUI do qBittorrent (utilizador: administrador)",
@@ -7977,6 +8050,7 @@
"remapped users": "usuários remapeados", "remapped users": "usuários remapeados",
"remove the (now-empty) directory if possible": "remova o diretório (agora vazio), se possível", "remove the (now-empty) directory if possible": "remova o diretório (agora vazio), se possível",
"removed from Proxmox": "removido do Proxmox", "removed from Proxmox": "removido do Proxmox",
"removed in Proxmox": "removida no Proxmox",
"removed successfully from Proxmox.": "removido com sucesso do Proxmox.", "removed successfully from Proxmox.": "removido com sucesso do Proxmox.",
"requires running the official installer, which restarts the Monitor service.": "requer a execução do instalador oficial, que reinicia o serviço Monitor.", "requires running the official installer, which restarts the Monitor service.": "requer a execução do instalador oficial, que reinicia o serviço Monitor.",
"requires the package": "requer o pacote", "requires the package": "requer o pacote",
@@ -8014,7 +8088,7 @@
"smbclient command is not working properly.": "O comando smbclient não está funcionando corretamente.", "smbclient command is not working properly.": "O comando smbclient não está funcionando corretamente.",
"smbclient command not found after installation.": "Comando smbclient não encontrado após a instalação.", "smbclient command not found after installation.": "Comando smbclient não encontrado após a instalação.",
"sources.list update skipped (no change)": "Atualização de sources.list ignorada (sem alteração)", "sources.list update skipped (no change)": "Atualização de sources.list ignorada (sem alteração)",
"sources.list updated to Trixie": "fontes.list atualizado para Trixie", "sources.list updated to Trixie": "sources.list atualizado para Trixie",
"ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen falhou. Não é possível criar uma nova chave SSH.", "ssh-keygen failed. Cannot create a new SSH key.": "ssh-keygen falhou. Não é possível criar uma nova chave SSH.",
"stale entry/entries for interfaces no longer present": "entradas/entradas obsoletas para interfaces não estão mais presentes", "stale entry/entries for interfaces no longer present": "entradas/entradas obsoletas para interfaces não estão mais presentes",
"standard performance": "desempenho padrão", "standard performance": "desempenho padrão",
@@ -8033,7 +8107,7 @@
"switch_gpu_mode.sh was not found.": "switch_gpu_mode.sh não foi encontrado.", "switch_gpu_mode.sh was not found.": "switch_gpu_mode.sh não foi encontrado.",
"sysfs ROM dump failed — trying ACPI VFCT table...": "Falha no despejo de ROM do sysfs - tentando tabela ACPI VFCT...", "sysfs ROM dump failed — trying ACPI VFCT table...": "Falha no despejo de ROM do sysfs - tentando tabela ACPI VFCT...",
"syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "o syslog-ng permite coletar, analisar, classificar, reescrever e correlacionar logs de toda sua infraestrutura e armazená-los ou roteá-los para ferramentas de análise de log.", "syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "o syslog-ng permite coletar, analisar, classificar, reescrever e correlacionar logs de toda sua infraestrutura e armazená-los ou roteá-los para ferramentas de análise de log.",
"systemctl restart networking failed:": "falha na reinicialização da rede do systemctl:", "systemctl restart networking failed:": "O systemctl restart networking falhou:",
"systemd OnCalendar expression": "Expressão Systemd OnCalendar", "systemd OnCalendar expression": "Expressão Systemd OnCalendar",
"the API key was not generated on the first start": "a tecla API não foi gerada no primeiro início", "the API key was not generated on the first start": "a tecla API não foi gerada no primeiro início",
"the NVIDIA hook of the container is not available:": "o hook NVIDIA do contêiner não está disponível:", "the NVIDIA hook of the container is not available:": "o hook NVIDIA do contêiner não está disponível:",
@@ -8080,18 +8154,20 @@
"using": "usando", "using": "usando",
"using direct path (not guaranteed to survive reboots).": "usando caminho direto (não é garantido que sobreviva às reinicializações).", "using direct path (not guaranteed to survive reboots).": "usando caminho direto (não é garantido que sobreviva às reinicializações).",
"vCPUs:": "vCPUs:", "vCPUs:": "vCPUs:",
"value changed in Proxmox": "valor alterado no Proxmox",
"vfio-pci IDs configured": "IDs vfio-pci configurados", "vfio-pci IDs configured": "IDs vfio-pci configurados",
"vfio-pci IDs in /etc/modprobe.d/vfio.conf": "IDs vfio-pci em /etc/modprobe.d/vfio.conf", "vfio-pci IDs in /etc/modprobe.d/vfio.conf": "IDs vfio-pci em /etc/modprobe.d/vfio.conf",
"video": "vídeo", "video": "vídeo",
"video + recognition": "vídeo + reconhecimento", "video + recognition": "vídeo + reconhecimento",
"video only": "apenas vídeo", "video only": "apenas vídeo",
"vzdump backup speed optimization completed": "otimização da velocidade de backup do vzdump concluída", "vzdump backup speed optimization completed": "otimização da velocidade de backup do vzdump concluída",
"wallabag builds its links from the address given during the installation. If it does not match the address of the container, edit lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME in /etc/pve/lxc/<CTID>.conf with the container stopped, and start it again.": "wallabag constrói seus links a partir do endereço fornecido durante a instalação. Se não corresponder ao endereço do recipiente, edite lxc.environment. tempo de execução: SYMFONY ENV DOMAIN NAME em /etc/pve/lxc/<CTID>.conf com o recipiente parado, e inicie-o novamente.", "wallabag builds its links from the address given during the installation. If it does not match the address of the container, edit lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME in /etc/pve/lxc/<CTID>.conf with the container stopped, and start it again.": "O wallabag gera as ligações com o endereço indicado na instalação. Se não corresponder ao do contentor, pare-o, edite lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME em /etc/pve/lxc/<CTID>.conf e volte a iniciá-lo.",
"wallabag listens on port 80 of the container and stores its data in SQLite.": "wallabag escuta na porta 80 do recipiente e armazena seus dados em SQLite.", "wallabag listens on port 80 of the container and stores its data in SQLite.": "wallabag escuta na porta 80 do recipiente e armazena seus dados em SQLite.",
"wallabag web interface": "Interface Web wallabag", "wallabag web interface": "Interface Web wallabag",
"was": "era", "was": "era",
"was installed, but the kernel reports:": "foi instalado, mas o kernel informa:", "was installed, but the kernel reports:": "foi instalado, mas o kernel informa:",
"when finished": "quando terminado", "when finished": "quando terminado",
"when the server has no subscription (you will be asked)": "quando o servidor não tem assinatura (será solicitado)",
"will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "irá religar a GPU ao vfio-pci na próxima reinicialização, quebrando o driver que está prestes a ser instalado.", "will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "irá religar a GPU ao vfio-pci na próxima reinicialização, quebrando o driver que está prestes a ser instalado.",
"wipefs failed on": "Wipefs falhou em", "wipefs failed on": "Wipefs falhou em",
"with": "com", "with": "com",
+83 -7
View File
@@ -37,6 +37,7 @@
"/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (predvolená cesta Proxmox vzdump)", "/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (predvolená cesta Proxmox vzdump)",
"/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (predvolené v Proxmoxe)", "/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (predvolené v Proxmoxe)",
"1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx pre všetkých. Zdieľaná skupina nie je potrebná.", "1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx pre všetkých. Zdieľaná skupina nie je potrebná.",
"====== PBS UPDATE COMPLETED ======": "====== AKTUALIZÁCIA PBS DOKONČENÁ ======",
"====== PVE UPDATE COMPLETED ======": "====== AKTUALIZÁCIA PVE DOKONČENÁ ======", "====== PVE UPDATE COMPLETED ======": "====== AKTUALIZÁCIA PVE DOKONČENÁ ======",
">> Create a new backup job <<": ">> Vytvoriť novú úlohu zálohovania <<", ">> Create a new backup job <<": ">> Vytvoriť novú úlohu zálohovania <<",
">> Save and return <<": ">> Uložiť a vrátiť <<", ">> Save and return <<": ">> Uložiť a vrátiť <<",
@@ -87,6 +88,7 @@
"A multi-container application is not recreated: its containers are updated together.": "Aplikácia s viacerými kontajnermi sa nevytvorí znova: jej kontajnery sa aktualizujú spoločne.", "A multi-container application is not recreated: its containers are updated together.": "Aplikácia s viacerými kontajnermi sa nevytvorí znova: jej kontajnery sa aktualizujú spoločne.",
"A new ProxMenux version is available:": "Je dostupná nová verzia ProxMenux:", "A new ProxMenux version is available:": "Je dostupná nová verzia ProxMenux:",
"A new kernel is staged for the next boot:": "Na najbližší štart je pripravený nový kernel:", "A new kernel is staged for the next boot:": "Na najbližší štart je pripravený nový kernel:",
"A newer kernel is installed. Reboot before the next backup window to use it.": "Je nainštalované novšie jadro. Aby ste ho mohli použiť, reštartujte ho pred ďalším oknom zálohy.",
"A newer version is available:": "Je dostupná novšia verzia:", "A newer version is available:": "Je dostupná novšia verzia:",
"A pending operation exists for": "Čaká na operation existuje", "A pending operation exists for": "Čaká na operation existuje",
"A pending stack assembly already exists; it is not overwritten": "Nevyriešená zostava stohov už existuje; nie je prepísaná", "A pending stack assembly already exists; it is not overwritten": "Nevyriešená zostava stohov už existuje; nie je prepísaná",
@@ -136,7 +138,7 @@
"ALL DATA ON": "VŠETKY DÁTA NA", "ALL DATA ON": "VŠETKY DÁTA NA",
"ALL DATA ON THIS DISK WILL BE PERMANENTLY LOST!": "VŠETKY DÁTA NA TOMTO DISKU BUDÚ NATRVALO STRATENÉ!", "ALL DATA ON THIS DISK WILL BE PERMANENTLY LOST!": "VŠETKY DÁTA NA TOMTO DISKU BUDÚ NATRVALO STRATENÉ!",
"ALL Utilities": "VŠETKY nástroje", "ALL Utilities": "VŠETKY nástroje",
"ALLOWED_HOSTS cannot contain line breaks": "UDELENÉ HOSTS nemôžu obsahovať prestávky na čiare", "ALLOWED_HOSTS cannot contain line breaks": "ALLOWED_HOSTS nesmie obsahovať zalomenia riadkov",
"AList initial login": "AList začiatočné prihlásenie", "AList initial login": "AList začiatočné prihlásenie",
"AMD (ROCm)": "AMD (ROCm)", "AMD (ROCm)": "AMD (ROCm)",
"AMD (VA-API and ROCm detection)": "AMD (detekcia VA-API a ROCm)", "AMD (VA-API and ROCm detection)": "AMD (detekcia VA-API a ROCm)",
@@ -169,6 +171,8 @@
"APT language configuration updated": "Nastavenie jazykov pre APT bolo aktualizované", "APT language configuration updated": "Nastavenie jazykov pre APT bolo aktualizované",
"APT language downloads restored": "Sťahovanie jazykov cez APT bolo obnovené", "APT language downloads restored": "Sťahovanie jazykov cez APT bolo obnovené",
"APT package lists updated": "Zoznamy APT balíkov aktualizované", "APT package lists updated": "Zoznamy APT balíkov aktualizované",
"APT refresh failed. Check the PBS subscription and repositories in Administration > Repositories. Log:": "Obnovenie APT zlyhalo. Skontrolujte predplatné a repozitáre PBS v časti Správa > Repozitáre. Denník:",
"APT refresh failed. Check the PBS subscription and repositories in the native PBS interface; no source was changed.": "Obnovenie indexov APT zlyhalo. Skontrolujte predplatné a repozitáre PBS v pôvodnom rozhraní PBS; žiadne zdroje sa nezmenili.",
"APT update failed. Check repository access; NVIDIA installation stopped.": "Aktualizácia APT zlyhala. Skontrolujte prístup k úložisku; inštalácia NVIDIA sa zastavila.", "APT update failed. Check repository access; NVIDIA installation stopped.": "Aktualizácia APT zlyhala. Skontrolujte prístup k úložisku; inštalácia NVIDIA sa zastavila.",
"Aborted": "Zrušené", "Aborted": "Zrušené",
"About to restore": "Chystá sa obnova", "About to restore": "Chystá sa obnova",
@@ -318,7 +322,7 @@
"After confirming, you will be asked to choose the NVIDIA driver version to install.": "Po potvrdení si vyberiete verziu ovládača NVIDIA, ktorú chcete nainštalovať.", "After confirming, you will be asked to choose the NVIDIA driver version to install.": "Po potvrdení si vyberiete verziu ovládača NVIDIA, ktorú chcete nainštalovať.",
"After detailed analysis, no changes are needed.": "Po podrobnej analýze nie sú potrebné žiadne zmeny.", "After detailed analysis, no changes are needed.": "Po podrobnej analýze nie sú potrebné žiadne zmeny.",
"After detailed analysis, no cleanup is needed.": "Po podrobnej analýze nie je potrebné žiadne čistenie.", "After detailed analysis, no cleanup is needed.": "Po podrobnej analýze nie je potrebné žiadne čistenie.",
"After logging in, run: ip a to obtain the IP address.\nThen, enter that IP address in your web browser like this:\n http://IP_ADDRESS\n\nThis will open the Umbral OS dashboard.": "Po prihlásení spustite príkaz: ip a, aby ste zistili IP adresu.\nPotom túto IP adresu zadajte do prehliadača takto:\n http://IP_ADRESA\n\nOtvorí sa tým webové rozhranie Umbral OS.", "After logging in, run: ip a to obtain the IP address.\nThen, enter that IP address in your web browser like this:\n http://IP_ADDRESS\n\nThis will open the Umbral OS dashboard.": "Po prihlásení zistite IP adresu príkazom ip a.\nPotom ju zadajte do prehliadača v tomto tvare:\n http://IP_ADDRESS\n\nOtvorí sa panel Umbral OS.",
"After pasting on the server, ensure the file is chmod 600 and owned by": "Po vložení na serveri overte, že súbor má chmod 600 a vlastní ho", "After pasting on the server, ensure the file is chmod 600 and owned by": "Po vložení na serveri overte, že súbor má chmod 600 a vlastní ho",
"After reboot the system will be fully accessible (SSH, web UI, login), but the following components will be reinstalled in BACKGROUND — until they finish, commands like nvidia-smi may not yet be available:": "Po reštarte bude systém plne dostupný (SSH, webové rozhranie, prihlásenie), ale tieto súčasti sa budú doinštalovávať NA POZADÍ - kým neskončia, príkazy ako nvidia-smi ešte nemusia byť dostupné:", "After reboot the system will be fully accessible (SSH, web UI, login), but the following components will be reinstalled in BACKGROUND — until they finish, commands like nvidia-smi may not yet be available:": "Po reštarte bude systém plne dostupný (SSH, webové rozhranie, prihlásenie), ale tieto súčasti sa budú doinštalovávať NA POZADÍ - kým neskončia, príkazy ako nvidia-smi ešte nemusia byť dostupné:",
"After reboot, these components will reinstall in background:": "Po reštarte sa tieto súčasti doinštalujú na pozadí:", "After reboot, these components will reinstall in background:": "Po reštarte sa tieto súčasti doinštalujú na pozadí:",
@@ -425,6 +429,7 @@
"Apply": "Použiť", "Apply": "Použiť",
"Apply AMD CPU fixes": "Použiť opravy pre AMD procesory", "Apply AMD CPU fixes": "Použiť opravy pre AMD procesory",
"Apply Available Updates": "Nainštalovať dostupné aktualizácie", "Apply Available Updates": "Nainštalovať dostupné aktualizácie",
"Apply PBS package upgrade": "Použiť aktualizáciu balíkov PBS",
"Apply and restart services:": "Použiť zmeny a reštartovať služby:", "Apply and restart services:": "Použiť zmeny a reštartovať služby:",
"Apply available updates": "Nainštalovať dostupné aktualizácie", "Apply available updates": "Nainštalovať dostupné aktualizácie",
"Apply boot/initramfs changes": "Použiť zmeny boot/initramfs", "Apply boot/initramfs changes": "Použiť zmeny boot/initramfs",
@@ -438,12 +443,15 @@
"Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Použiť možnosti z aktuálnej šablóny katalógu? Môžu sa vyžadovať nové požadované cesty a nastavenia. Pred opätovným vytvorením CT skontrolujte výslednú konfiguráciu.", "Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Použiť možnosti z aktuálnej šablóny katalógu? Môžu sa vyžadovať nové požadované cesty a nastavenia. Pred opätovným vytvorením CT skontrolujte výslednú konfiguráciu.",
"Apply read+write access for 'others' on the host directory?": "Nastaviť pre 'others' čítanie aj zápis na priečinku hosta?", "Apply read+write access for 'others' on the host directory?": "Nastaviť pre 'others' čítanie aj zápis na priečinku hosta?",
"Apply the options from the current catalog template? Your data and configuration are kept.": "Aplikovať možnosti z aktuálnej katalógovej šablóny? Vaše dáta a konfigurácia sú uchovávané.", "Apply the options from the current catalog template? Your data and configuration are kept.": "Aplikovať možnosti z aktuálnej katalógovej šablóny? Vaše dáta a konfigurácia sú uchovávané.",
"Apply the reviewed package upgrade now? Services may restart and packages listed for removal will be removed.": "Použiť teraz skontrolovanú aktualizáciu balíkov? Služby sa môžu reštartovať a balíky označené na odstránenie budú odstránené.",
"Apply these changes?": "Použiť tieto zmeny?", "Apply these changes?": "Použiť tieto zmeny?",
"Applying AMD-specific fixes...": "Používam opravy špecifické pre AMD...", "Applying AMD-specific fixes...": "Používam opravy špecifické pre AMD...",
"Applying CPU and RAM changes without rebuilding the container...": "Používajú sa zmeny CPU a RAM bez opätovného vytvorenia kontajnera...",
"Applying Changes": "Používam zmeny", "Applying Changes": "Používam zmeny",
"Applying Controller/NVMe passthrough to VM": "Používam priame priradenie Controller/NVMe do VM", "Applying Controller/NVMe passthrough to VM": "Používam priame priradenie Controller/NVMe do VM",
"Applying balanced memory optimization settings...": "Používam vyvážené nastavenia optimalizácie pamäte...", "Applying balanced memory optimization settings...": "Používam vyvážené nastavenia optimalizácie pamäte...",
"Applying changes safely...": "Bezpečne používam zmeny...", "Applying changes safely...": "Bezpečne používam zmeny...",
"Applying container settings without rebuilding the container...": "Aplikujú sa nastavenia bez opätovného vytvorenia kontajnera...",
"Applying custom restore": "Používam vlastnú obnovu", "Applying custom restore": "Používam vlastnú obnovu",
"Applying default VM configuration": "Používam predvolené nastavenie VM", "Applying default VM configuration": "Používam predvolené nastavenie VM",
"Applying host permissions for unprivileged LXC bind-mounts...": "Nastavujem oprávnenia hosta pre LXC bind mounty v neprivilegovaných kontajneroch...", "Applying host permissions for unprivileged LXC bind-mounts...": "Nastavujem oprávnenia hosta pre LXC bind mounty v neprivilegovaných kontajneroch...",
@@ -535,6 +543,7 @@
"Available Borg targets:": "Dostupné Borg ciele:", "Available Borg targets:": "Dostupné Borg ciele:",
"Available Disks on Host": "Dostupné disky na hostovi", "Available Disks on Host": "Dostupné disky na hostovi",
"Available ISO Images": "Dostupné ISO obrazy", "Available ISO Images": "Dostupné ISO obrazy",
"Available PBS Version": "Dostupná verzia PBS",
"Available PBS repositories:": "Dostupné PBS repozitáre:", "Available PBS repositories:": "Dostupné PBS repozitáre:",
"Available PVE Version": "Dostupná verzia PVE", "Available PVE Version": "Dostupná verzia PVE",
"Available Shares:": "Dostupné zdieľania:", "Available Shares:": "Dostupné zdieľania:",
@@ -746,6 +755,7 @@
"Cannot verify the reused disk:": "Nie je možné overiť znovu použitý disk:", "Cannot verify the reused disk:": "Nie je možné overiť znovu použitý disk:",
"Cannot write the image archive to {storage}. Check storage access and mount status.": "Nie je možné zapísať archív obrázkov do {storage}. Skontrolujte prístup k úložisku a stav pripojenia.", "Cannot write the image archive to {storage}. Check storage access and mount status.": "Nie je možné zapísať archív obrázkov do {storage}. Skontrolujte prístup k úložisku a stav pripojenia.",
"Capabilities cannot be kept and all dropped at the same time": "Nie je možné udržať si schopnosti a zároveň ich znížiť.", "Capabilities cannot be kept and all dropped at the same time": "Nie je možné udržať si schopnosti a zároveň ich znížiť.",
"Catalog options": "Možnosti katalógu",
"Category": "Kategória", "Category": "Kategória",
"Caution: Maximum mode generates more heat.": "Pozor: maximálny režim vytvára viac tepla.", "Caution: Maximum mode generates more heat.": "Pozor: maximálny režim vytvára viac tepla.",
"Ceph check skipped by user flag (--ignore-ceph-check)": "Kontrola Ceph preskočená používateľskou voľbou (--ignore-ceph-check)", "Ceph check skipped by user flag (--ignore-ceph-check)": "Kontrola Ceph preskočená používateľskou voľbou (--ignore-ceph-check)",
@@ -880,6 +890,7 @@
"Choose a schedule (systemd calendar-event syntax):": "Vyberte plán (syntax systémového kalendára a udalosti):", "Choose a schedule (systemd calendar-event syntax):": "Vyberte plán (syntax systémového kalendára a udalosti):",
"Choose a script to execute:": "Vyberte skript, ktorý sa má spustiť:", "Choose a script to execute:": "Vyberte skript, ktorý sa má spustiť:",
"Choose a server:": "Vyberte server:", "Choose a server:": "Vyberte server:",
"Choose a setting. Changes are applied only after the final review.": "Vyberte nastavenie. Zmeny sa použijú až po záverečnej kontrole.",
"Choose a share to mount:": "Vyberte zdieľanie na pripojenie:", "Choose a share to mount:": "Vyberte zdieľanie na pripojenie:",
"Choose a specific supported GPU or USB node": "Vyberte konkrétny podporovaný uzol GPU alebo USB", "Choose a specific supported GPU or USB node": "Vyberte konkrétny podporovaný uzol GPU alebo USB",
"Choose action for this controller/NVMe:": "Vyberte akciu pre tento Controller/NVMe:", "Choose action for this controller/NVMe:": "Vyberte akciu pre tento Controller/NVMe:",
@@ -960,6 +971,7 @@
"Code-server is VS Code running on a remote server, accessible through the browser.": "Kód server je VS kód beží na vzdialenom serveri, prístupný cez prehliadač.", "Code-server is VS Code running on a remote server, accessible through the browser.": "Kód server je VS kód beží na vzdialenom serveri, prístupný cez prehliadač.",
"CodeProject.AI Server": "CodeProject. AI Server", "CodeProject.AI Server": "CodeProject. AI Server",
"Command": "Príkaz", "Command": "Príkaz",
"Command failed": "Príkaz zlyhal",
"Command override for an unknown service:": "Prepísať príkaz pre neznámu službu:", "Command override for an unknown service:": "Prepísať príkaz pre neznámu službu:",
"Commenting any residual Bookworm lines in *.list...": "Komentujem zostávajúce riadky Bookworm v *.list...", "Commenting any residual Bookworm lines in *.list...": "Komentujem zostávajúce riadky Bookworm v *.list...",
"Commenting legacy PVE 8 repository .list files (if any)...": "Komentujem staré .list súbory repozitárov PVE 8 (ak existujú)...", "Commenting legacy PVE 8 repository .list files (if any)...": "Komentujem staré .list súbory repozitárov PVE 8 (ak existujú)...",
@@ -1516,6 +1528,7 @@
"Current NVIDIA inventory resolved: a refresh is required": "Aktuálne NVIDIA inventár vyriešený: obnovovanie je required", "Current NVIDIA inventory resolved: a refresh is required": "Aktuálne NVIDIA inventár vyriešený: obnovovanie je required",
"Current NVIDIA inventory resolved: no refresh is required": "Aktuálne NVIDIA inventár vyriešený: žiadne obnovenie je required", "Current NVIDIA inventory resolved: no refresh is required": "Aktuálne NVIDIA inventár vyriešený: žiadne obnovenie je required",
"Current Network Configuration": "Aktuálne nastavenie siete", "Current Network Configuration": "Aktuálne nastavenie siete",
"Current PBS Version": "Aktuálna verzia PBS",
"Current PVE Version": "Aktuálna verzia PVE", "Current PVE Version": "Aktuálna verzia PVE",
"Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Aktuálne host skripty ProxMenux registrujú vzdialené zdieľania ako úložiská Proxmoxu pomocou pvesm.", "Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Aktuálne host skripty ProxMenux registrujú vzdialené zdieľania ako úložiská Proxmoxu pomocou pvesm.",
"Current ProxMenux version:": "Aktuálna verzia ProxMenux:", "Current ProxMenux version:": "Aktuálna verzia ProxMenux:",
@@ -1681,6 +1694,7 @@
"Detected Coral hardware:": "Nájdený Coral hardvér:", "Detected Coral hardware:": "Nájdený Coral hardvér:",
"Detected GPU(s):": "Nájdené GPU:", "Detected GPU(s):": "Nájdené GPU:",
"Detected Intel CPU": "Zistený Intel procesor", "Detected Intel CPU": "Zistený Intel procesor",
"Detected Proxmox Backup Server": "Zistený Proxmox Backup Server",
"Detected Proxmox VE": "Zistený Proxmox VE", "Detected Proxmox VE": "Zistený Proxmox VE",
"Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Zistený Proxmox VE 8.x - inštalujem Ceph Squid pre Debian", "Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Zistený Proxmox VE 8.x - inštalujem Ceph Squid pre Debian",
"Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Zistený Proxmox VE 9.x - inštalujem Ceph Squid pre Debian Trixie", "Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Zistený Proxmox VE 9.x - inštalujem Ceph Squid pre Debian Trixie",
@@ -1762,6 +1776,9 @@
"Disabling and stopping rpcbind service and socket...": "Zakázanie a zastavenie služby rpcbind a soketu...", "Disabling and stopping rpcbind service and socket...": "Zakázanie a zastavenie služby rpcbind a soketu...",
"Disabling non-free firmware warnings...": "Vypínam upozornenia na non-free firmware...", "Disabling non-free firmware warnings...": "Vypínam upozornenia na non-free firmware...",
"Disabling portmapper/rpcbind for security...": "Z bezpečnostných dôvodov vypínam portmapper/rpcbind...", "Disabling portmapper/rpcbind for security...": "Z bezpečnostných dôvodov vypínam portmapper/rpcbind...",
"Discard changes": "Zahodiť zmeny",
"Discard unapplied changes?": "Zahodiť nepoužité zmeny?",
"Discover NFS exports on a server": "Zistiť NFS exporty servera",
"Discover targets on portal:": "Vyhľadať targety na portáli:", "Discover targets on portal:": "Vyhľadať targety na portáli:",
"Discovering iSCSI targets...": "Vyhľadávam iSCSI targety...", "Discovering iSCSI targets...": "Vyhľadávam iSCSI targety...",
"Discovery successful": "Vyhľadanie bolo úspešné", "Discovery successful": "Vyhľadanie bolo úspešné",
@@ -1841,6 +1858,7 @@
"Do you want to proceed with the system update?": "Chcete pokračovať v aktualizácii systému?", "Do you want to proceed with the system update?": "Chcete pokračovať v aktualizácii systému?",
"Do you want to proceed?": "Chcete pokračovať?", "Do you want to proceed?": "Chcete pokračovať?",
"Do you want to reboot now?": "Chcete reštartovať teraz?", "Do you want to reboot now?": "Chcete reštartovať teraz?",
"Do you want to remove the Proxmox subscription banner from the Proxmox Backup Server web interface?": "Chcete odstrániť banner predplatného Proxmox z webového rozhrania Proxmox Backup Server?",
"Do you want to remove the stale entry from vfio.conf and continue?": "Chcete odstrániť zastaraný záznam z vfio.conf a pokračovať?", "Do you want to remove the stale entry from vfio.conf and continue?": "Chcete odstrániť zastaraný záznam z vfio.conf a pokračovať?",
"Do you want to remove the stale entry from vfio.conf?": "Chcete odstrániť zastaraný záznam z vfio.conf?", "Do you want to remove the stale entry from vfio.conf?": "Chcete odstrániť zastaraný záznam z vfio.conf?",
"Do you want to restart the network service now to apply changes?": "Chcete teraz reštartovať sieťovú službu, aby sa zmeny prejavili?", "Do you want to restart the network service now to apply changes?": "Chcete teraz reštartovať sieťovú službu, aby sa zmeny prejavili?",
@@ -2266,6 +2284,7 @@
"Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Repozitár log2ram sa nepodarilo stiahnuť. Skontrolujte /tmp/log2ram_install.log", "Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Repozitár log2ram sa nepodarilo stiahnuť. Skontrolujte /tmp/log2ram_install.log",
"Failed to configure EFI disk": "EFI disk sa nepodarilo nastaviť", "Failed to configure EFI disk": "EFI disk sa nepodarilo nastaviť",
"Failed to configure IOMMU automatically.": "IOMMU sa nepodarilo nastaviť automaticky.", "Failed to configure IOMMU automatically.": "IOMMU sa nepodarilo nastaviť automaticky.",
"Failed to configure Proxmox Backup Server repositories": "Repozitáre Proxmox Backup Server sa nepodarilo nastaviť",
"Failed to configure Proxmox repositories": "Repozitáre Proxmoxu sa nepodarilo nastaviť", "Failed to configure Proxmox repositories": "Repozitáre Proxmoxu sa nepodarilo nastaviť",
"Failed to configure TPM device in VM": "TPM zariadenie sa vo VM nepodarilo nastaviť", "Failed to configure TPM device in VM": "TPM zariadenie sa vo VM nepodarilo nastaviť",
"Failed to configure repositories.": "Repozitáre sa nepodarilo nastaviť.", "Failed to configure repositories.": "Repozitáre sa nepodarilo nastaviť.",
@@ -2668,7 +2687,7 @@
"HAOS One is a community image that runs Docker inside the container. The LXC stays unprivileged, but the inner AppArmor profiles may not be available. The first start downloads Home Assistant Core and its add-ons. If the check fails, the CT and /mnt/data are kept for diagnosis.": "HAOS One je obraz spoločenstva, ktorý prevádzkuje Docker vnútri kontajnera. LXC zostáva neprivilegovaný, ale vnútorné profily AppArmor nemusí byť k dispozícii. Prvý štart stiahne Home Assistant jadro a jeho doplnky. Ak kontrola zlyhá, CT a / mnt/ dáta sa uchovávajú na určenie diagnózy.", "HAOS One is a community image that runs Docker inside the container. The LXC stays unprivileged, but the inner AppArmor profiles may not be available. The first start downloads Home Assistant Core and its add-ons. If the check fails, the CT and /mnt/data are kept for diagnosis.": "HAOS One je obraz spoločenstva, ktorý prevádzkuje Docker vnútri kontajnera. LXC zostáva neprivilegovaný, ale vnútorné profily AppArmor nemusí byť k dispozícii. Prvý štart stiahne Home Assistant jadro a jeho doplnky. Ak kontrola zlyhá, CT a / mnt/ dáta sa uchovávajú na určenie diagnózy.",
"HAOS One profile declined": "Profil HAOS One klesol", "HAOS One profile declined": "Profil HAOS One klesol",
"HAOS One requires an unprivileged unmanaged LXC with nesting and keyctl, 2 cores, 2048 MB RAM, rootfs of at least 12 GB and /mnt/data of at least 16 GB on a container volume included in backups": "HAOS One requires an unprivileged unmanaged LXC with nestering and keyctl, 2 cores, 2048 MB RAM, rootfs najmenej 12 GB a /mnt/data najmenej 16 GB on a container volume included in backions", "HAOS One requires an unprivileged unmanaged LXC with nesting and keyctl, 2 cores, 2048 MB RAM, rootfs of at least 12 GB and /mnt/data of at least 16 GB on a container volume included in backups": "HAOS One requires an unprivileged unmanaged LXC with nestering and keyctl, 2 cores, 2048 MB RAM, rootfs najmenej 12 GB a /mnt/data najmenej 16 GB on a container volume included in backions",
"HTML5 Network Speed Test Server.": "HTML5 Network Speed ​​Test Server.", "HTML5 Network Speed Test Server.": "HTML5 Network Speed Test Server.",
"HTTP service check without a saved URL": "Kontrola služby HTTP bez uloženého URL", "HTTP service check without a saved URL": "Kontrola služby HTTP bez uloženého URL",
"Habridge emulates Philips Hue API to other home automation gateways such as an Amazon Echo/Dot Gen 1 (gen 2 has issues discovering ha-bridge) or other systems that support Philips Hue. The Bridge handles basic commands such as On, Off and brightness commands of the hue protocol. This bridge can control most devices that have a distinct API.": "Habridge napodobňuje Philipsa Spoločnosť Amazon tvrdí, že spoločnosť LuxOpCo by mala mať prístup k webovým stránkam spoločnosti LuxOpCo. Most riadi základné príkazy, ako je On, Off a jas príkazy protokolu odtieňov. Tento most môže ovládať väčšinu zariadení, ktoré majú odlišné API.", "Habridge emulates Philips Hue API to other home automation gateways such as an Amazon Echo/Dot Gen 1 (gen 2 has issues discovering ha-bridge) or other systems that support Philips Hue. The Bridge handles basic commands such as On, Off and brightness commands of the hue protocol. This bridge can control most devices that have a distinct API.": "Habridge napodobňuje Philipsa Spoločnosť Amazon tvrdí, že spoločnosť LuxOpCo by mala mať prístup k webovým stránkam spoločnosti LuxOpCo. Most riadi základné príkazy, ako je On, Off a jas príkazy protokolu odtieňov. Tento most môže ovládať väčšinu zariadení, ktoré majú odlišné API.",
"HandBrake is an open-source tool, built by volunteers, for converting video from nearly any format to a selection of modern, widely supported codecs.": "HandBrake je open-source nástroj, postavený dobrovoľníkmi, pre konverziu video z takmer akéhokoľvek formátu na výber moderných, široko podporovaných kodekov.", "HandBrake is an open-source tool, built by volunteers, for converting video from nearly any format to a selection of modern, widely supported codecs.": "HandBrake je open-source nástroj, postavený dobrovoľníkmi, pre konverziu video z takmer akéhokoľvek formátu na výber moderných, široko podporovaných kodekov.",
@@ -2973,6 +2992,7 @@
"Inaccessible device during VM startup": "nedostupné zariadenie počas štartu VM", "Inaccessible device during VM startup": "nedostupné zariadenie počas štartu VM",
"Inactive": "Neaktívne", "Inactive": "Neaktívne",
"Inactive (entry in fstab, not currently mounted)": "Neaktívne (záznam je vo fstab, ale teraz nie je pripojený)", "Inactive (entry in fstab, not currently mounted)": "Neaktívne (záznam je vo fstab, ale teraz nie je pripojený)",
"Include these changes in oci-compose?": "Zahrnúť tieto zmeny do oci-compose?",
"Include these resources in oci-compose?": "Zahrnúť tieto zdroje do oci-compose?", "Include these resources in oci-compose?": "Zahrnúť tieto zdroje do oci-compose?",
"Includes /etc/network (may drop SSH immediately)": "Obsahuje /etc/network (môže okamžite prerušiť SSH)", "Includes /etc/network (may drop SSH immediately)": "Obsahuje /etc/network (môže okamžite prerušiť SSH)",
"Includes /etc/zfs": "Obsahuje /etc/zfs", "Includes /etc/zfs": "Obsahuje /etc/zfs",
@@ -3029,7 +3049,7 @@
"Initiator IQN is authorised on the target": "IQN iniciátora je na targete povolené", "Initiator IQN is authorised on the target": "IQN iniciátora je na targete povolené",
"Initiator IQN:": "IQN iniciátora:", "Initiator IQN:": "IQN iniciátora:",
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape je profesionálne kvalitný vektorový grafický softvér, ktorý beží na počítačoch Linux, Mac OS X a Windows.", "Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape je profesionálne kvalitný vektorový grafický softvér, ktorý beží na počítačoch Linux, Mac OS X a Windows.",
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Vnútri LXC, vytvoriť administrátor: konzola kimai:používateľ:vytvorte VAŠE VYUŽITE VAŠE EMAIL ROLE_SUPER_ADMIN", "Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "V LXC vytvorte správcu príkazom: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
"Inspect disks before any action": "Skontrolovať disky pred akoukoľvek akciou", "Inspect disks before any action": "Skontrolovať disky pred akoukoľvek akciou",
"Inspect host device nodes": "Skontrolovať device nodes na hostovi", "Inspect host device nodes": "Skontrolovať device nodes na hostovi",
"Inspect passthrough/kernel events": "Skontrolovať passthrough/kernel udalosti", "Inspect passthrough/kernel events": "Skontrolovať passthrough/kernel udalosti",
@@ -3206,9 +3226,11 @@
"Interrupted operation:": "Prerušená operation:", "Interrupted operation:": "Prerušená operation:",
"Interrupted stack operation found": "Prerušený zásobník operation nájdený", "Interrupted stack operation found": "Prerušený zásobník operation nájdený",
"Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.": "Neplatný kandidát 'proxmox-ve' (nie je 9.x alebo chýba). Skontrolujte nastavenie repozitárov a sieť, potom skúste znova.", "Invalid 'proxmox-ve' candidate (not 9.x or none). Please verify your repository configuration and network, then retry.": "Neplatný kandidát 'proxmox-ve' (nie je 9.x alebo chýba). Skontrolujte nastavenie repozitárov a sieť, potom skúste znova.",
"Invalid ALLOWED_HOSTS value": "Neplatná hodnota UDELENÁ HOSTS", "Invalid ALLOWED_HOSTS value": "Neplatná hodnota ALLOWED_HOSTS",
"Invalid Home Assistant OS check timeout": "Neplatný čas odhlásenia Home Assistant OS", "Invalid Home Assistant OS check timeout": "Neplatný čas odhlásenia Home Assistant OS",
"Invalid ID": "Neplatné ID", "Invalid ID": "Neplatné ID",
"Invalid IPv4 address": "Neplatná adresa IPv4",
"Invalid IPv4 gateway": "Neplatná brána IPv4",
"Invalid Intel render path": "Neplatná cesta k zobrazeniu informácií", "Invalid Intel render path": "Neplatná cesta k zobrazeniu informácií",
"Invalid Jellyfin path:": "Neplatná cesta Jellyfin:", "Invalid Jellyfin path:": "Neplatná cesta Jellyfin:",
"Invalid MAC address:": "Neplatná adresa MAC:", "Invalid MAC address:": "Neplatná adresa MAC:",
@@ -3241,6 +3263,7 @@
"Invalid administrator email": "Neplatný administrátorský mail", "Invalid administrator email": "Neplatný administrátorský mail",
"Invalid administrator user name": "Neplatné meno správcu", "Invalid administrator user name": "Neplatné meno správcu",
"Invalid base VMID": "Neplatná základňa VMID", "Invalid base VMID": "Neplatná základňa VMID",
"Invalid bridge": "Neplatný sieťový most",
"Invalid check package:": "Neplatný kontrolný balík:", "Invalid check package:": "Neplatný kontrolný balík:",
"Invalid configuration path:": "Neplatná konfiguračná dráha:", "Invalid configuration path:": "Neplatná konfiguračná dráha:",
"Invalid consume/export volumes": "Neplatné objemy spotreby/vývozu", "Invalid consume/export volumes": "Neplatné objemy spotreby/vývozu",
@@ -3304,6 +3327,7 @@
"Invalid remote name": "Neplatné vzdialené meno", "Invalid remote name": "Neplatné vzdialené meno",
"Invalid remote path": "Neplatná vzdialená cesta", "Invalid remote path": "Neplatná vzdialená cesta",
"Invalid repair version:": "Neplatná verzia opravy:", "Invalid repair version:": "Neplatná verzia opravy:",
"Invalid resource value": "Neplatná hodnota prostriedkov",
"Invalid resources": "Neplatné zdroje", "Invalid resources": "Neplatné zdroje",
"Invalid restored volume path": "Neplatná cesta obnoveného objemu", "Invalid restored volume path": "Neplatná cesta obnoveného objemu",
"Invalid running state": "Neplatný jazdný stav", "Invalid running state": "Neplatný jazdný stav",
@@ -3322,6 +3346,7 @@
"Invalid stack members": "Neplatní členovia stohu", "Invalid stack members": "Neplatní členovia stohu",
"Invalid stack name": "Neplatný názov stohu", "Invalid stack name": "Neplatný názov stohu",
"Invalid stack operation": "Neplatný zásobník operation", "Invalid stack operation": "Neplatný zásobník operation",
"Invalid storage": "Neplatné úložisko",
"Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "Neplatné ID úložiska. Použite iba písmená, čísla, pomlčky a podčiarkovníky.", "Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "Neplatné ID úložiska. Použite iba písmená, čísla, pomlčky a podčiarkovníky.",
"Invalid storage name": "Neplatný názov úložiska", "Invalid storage name": "Neplatný názov úložiska",
"Invalid suite application": "Neplatná aplikácia", "Invalid suite application": "Neplatná aplikácia",
@@ -3436,7 +3461,7 @@
"Keyfile removed.": "Keyfile odstránený.", "Keyfile removed.": "Keyfile odstránený.",
"Keyrings method failed; trying apt-key fallback": "Metóda keyrings zlyhala; skúšam náhradný apt-key postup", "Keyrings method failed; trying apt-key fallback": "Metóda keyrings zlyhala; skúšam náhradný apt-key postup",
"KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - Cross Platform a Open Source Electronics Design Automation Suite.", "KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - Cross Platform a Open Source Electronics Design Automation Suite.",
"Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai nemá predvolený účet. Zadajte kontajner s: pct zadajte {main vmid}", "Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai nemá predvolený účet. Do kontajnera vstúpite príkazom: pct enter {main_vmid}",
"Kimai is a professional grade time-tracking application, free and open-source.": "Kimai je profesionálna časová aplikácia, zadarmo a open-source.", "Kimai is a professional grade time-tracking application, free and open-source.": "Kimai je profesionálna časová aplikácia, zadarmo a open-source.",
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa je výkonný nástroj navrhnutý tak, aby vám plnú kontrolu nad vaše mediálne knižnice. S Kometa, môžete vziať svoje prispôsobenie na ďalšiu úroveň, s granulovanou kontrolou nad metaúdaje, zbierky, prekrývanie, a oveľa viac.", "Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa je výkonný nástroj navrhnutý tak, aby vám plnú kontrolu nad vaše mediálne knižnice. S Kometa, môžete vziať svoje prispôsobenie na ďalšiu úroveň, s granulovanou kontrolou nad metaúdaje, zbierky, prekrývanie, a oveľa viac.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa číta svoju konfiguráciu z /config/config.yml a kontajner iba lode /config/config.yml.templát. Skopírujte šablónu pre config.yml, vyplňte required Plex a TMDb pripojenia, potom reštartujte kontajner.", "Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa číta svoju konfiguráciu z /config/config.yml a kontajner iba lode /config/config.yml.templát. Skopírujte šablónu pre config.yml, vyplňte required Plex a TMDb pripojenia, potom reštartujte kontajner.",
@@ -4153,6 +4178,7 @@
"No active Proxmox storage accepts OCI template archives.": "Žiadne aktívne úložisko Proxmox neakceptuje archívy šablón OCI.", "No active Proxmox storage accepts OCI template archives.": "Žiadne aktívne úložisko Proxmox neakceptuje archívy šablón OCI.",
"No active exports or showmount not available": "Nie sú aktívne exporty alebo príkaz showmount nie je dostupný", "No active exports or showmount not available": "Nie sú aktívne exporty alebo príkaz showmount nie je dostupný",
"No active session": "Žiadna aktívna relácia", "No active session": "Žiadna aktívna relácia",
"No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.": "Žiadne aktívne predplatné a žiadne použiteľné úložisko PBS.Neinteraktívny režim nemôže zmeniť zdroje APT;nakonfigurujte ich v Administrácia > Repozitáre.",
"No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Žiadne aktívne predplatné a žiadne použiteľné úložisko PVE. Neinteraktívny režim nemôže zmeniť zdroje APT; nakonfigurujte ich v časti Uzol > Aktualizácie > Úložiská.", "No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Žiadne aktívne predplatné a žiadne použiteľné úložisko PVE. Neinteraktívny režim nemôže zmeniť zdroje APT; nakonfigurujte ich v časti Uzol > Aktualizácie > Úložiská.",
"No additional GPU can be added.": "Nie je možné pridať ďalšiu GPU.", "No additional GPU can be added.": "Nie je možné pridať ďalšiu GPU.",
"No additional device needs to be added.": "Netreba pridávať žiadne ďalšie zariadenie.", "No additional device needs to be added.": "Netreba pridávať žiadne ďalšie zariadenie.",
@@ -4257,6 +4283,7 @@
"No password provided.": "Nebolo zadané heslo.", "No password provided.": "Nebolo zadané heslo.",
"No paths available": "Nie sú dostupné žiadne cesty", "No paths available": "Nie sú dostupné žiadne cesty",
"No paths selected. Select at least one path.": "Nie sú vybrané žiadne cesty. Vyberte aspoň jednu cestu.", "No paths selected. Select at least one path.": "Nie sú vybrané žiadne cesty. Vyberte aspoň jednu cestu.",
"No pending changes": "Žiadne čakajúce zmeny",
"No pending paths to schedule for reboot.": "Nie sú žiadne čakajúce cesty, ktoré by sa mali naplánovať na reštart.", "No pending paths to schedule for reboot.": "Nie sú žiadne čakajúce cesty, ktoré by sa mali naplánovať na reštart.",
"No permanent NFS mounts configured.": "Nie sú nastavené žiadne trvalé NFS pripojenia.", "No permanent NFS mounts configured.": "Nie sú nastavené žiadne trvalé NFS pripojenia.",
"No permanent Samba mounts configured.": "Nie sú nastavené žiadne trvalé Samba pripojenia.", "No permanent Samba mounts configured.": "Nie sú nastavené žiadne trvalé Samba pripojenia.",
@@ -4400,6 +4427,7 @@
"OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM bol odstránený (Puppet súbory môže byť potrebné vyčistiť ručne)", "OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM bol odstránený (Puppet súbory môže byť potrebné vyčistiť ručne)",
"OVH server detected": "Zistený OVH server", "OVH server detected": "Zistený OVH server",
"OVH server detection and RTM installation process completed": "Kontrola OVH servera a inštalácia RTM sú dokončené", "OVH server detection and RTM installation process completed": "Kontrola OVH servera a inštalácia RTM sú dokončené",
"Object detector": "Detektor objektov",
"Object detector for Frigate": "Detektor objektov pre Frigate", "Object detector for Frigate": "Detektor objektov pre Frigate",
"Observer is not responding on port 4357": "Pozorovateľ nereaguje na prístav 4357", "Observer is not responding on port 4357": "Pozorovateľ nereaguje na prístav 4357",
"Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian je note-prijímajúce aplikácie, ktoré vám umožní vytvoriť, odkaz, a usporiadať poznámky na vašom zariadení, so stovkami pluginov a tém prispôsobiť svoje pracovné postupy. Môžete tiež zverejniť svoje poznámky on-line, prístup je offline, a synchronizovať ich bezpečne s end-to-end šifrovanie.", "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian je note-prijímajúce aplikácie, ktoré vám umožní vytvoriť, odkaz, a usporiadať poznámky na vašom zariadení, so stovkami pluginov a tém prispôsobiť svoje pracovné postupy. Môžete tiež zverejniť svoje poznámky on-line, prístup je offline, a synchronizovať ich bezpečne s end-to-end šifrovanie.",
@@ -4525,11 +4553,17 @@
"PBS backup error log": "Záznam chyby zálohy do PBS", "PBS backup error log": "Záznam chyby zálohy do PBS",
"PBS backup failed.": "Záloha do PBS zlyhala.", "PBS backup failed.": "Záloha do PBS zlyhala.",
"PBS certificate": "PBS certifikát", "PBS certificate": "PBS certifikát",
"PBS datastores": "Dátové úložiská PBS",
"PBS encryption keyfile (show / replace / remove)": "Šifrovací keyfile pre PBS (zobraziť / nahradiť / odstrániť)", "PBS encryption keyfile (show / replace / remove)": "Šifrovací keyfile pre PBS (zobraziť / nahradiť / odstrániť)",
"PBS encryption keyfile — detailed info": "Šifrovací keyfile pre PBS - podrobnosti", "PBS encryption keyfile — detailed info": "Šifrovací keyfile pre PBS - podrobnosti",
"PBS extraction failed": "Rozbalenie z PBS zlyhalo", "PBS extraction failed": "Rozbalenie z PBS zlyhalo",
"PBS host or IP address:": "PBS host alebo IP adresa:", "PBS host or IP address:": "PBS host alebo IP adresa:",
"PBS package update": "Aktualizácia balíkov PBS",
"PBS package upgrade completed. Check whether a reboot is required before your next backup window.": "Aktualizácia balíkov PBS bola dokončená. Pred ďalším zálohovacím oknom skontrolujte, či je potrebný reštart.",
"PBS package upgrade failed. Review the APT output before retrying.": "Aktualizácia balíkov PBS zlyhala. Pred ďalším pokusom skontrolujte výstup APT.",
"PBS upload": "Nahrávanie do PBS", "PBS upload": "Nahrávanie do PBS",
"PBS version and packages": "Verzia PBS a balíky",
"PBS: using the configured APT repositories without changing sources.": "PBS: používajú sa nakonfigurované repozitáre APT bez zmeny zdrojov.",
"PCI Address": "PCI adresa", "PCI Address": "PCI adresa",
"PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "PCI passthrough, stav TPM, cloud-init nastavenie, Proxmox hooky", "PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "PCI passthrough, stav TPM, cloud-init nastavenie, Proxmox hooky",
"PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "PCI passthrough, stav TPM, cloud-init, snapshoty, Proxmox špecifické hooky", "PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "PCI passthrough, stav TPM, cloud-init, snapshoty, Proxmox špecifické hooky",
@@ -4866,12 +4900,17 @@
"Proxmology logo applied": "Logo Proxmology bolo použité", "Proxmology logo applied": "Logo Proxmology bolo použité",
"Proxmox 9 system update allready": "Systém Proxmox 9 je už aktualizovaný", "Proxmox 9 system update allready": "Systém Proxmox 9 je už aktualizovaný",
"Proxmox APT repositories configured": "APT repozitáre Proxmoxu sú nastavené", "Proxmox APT repositories configured": "APT repozitáre Proxmoxu sú nastavené",
"Proxmox Backup Server": "Proxmox Backup Server",
"Proxmox Backup Server (PBS) destinations": "Ciele Proxmox Backup Servera (PBS)", "Proxmox Backup Server (PBS) destinations": "Ciele Proxmox Backup Servera (PBS)",
"Proxmox Backup Server APT repositories configured": "APT repozitáre Proxmox Backup Server sú nastavené",
"Proxmox Backup Server Update": "Aktualizácia Proxmox Backup Server",
"Proxmox Backup Server update completed.": "Aktualizácia Proxmox Backup Server bola dokončená.",
"Proxmox CIFS Storage Status:": "Stav CIFS úložiska v Proxmoxe:", "Proxmox CIFS Storage Status:": "Stav CIFS úložiska v Proxmoxe:",
"Proxmox NFS Storage Status:": "Stav NFS úložiska v Proxmoxe:", "Proxmox NFS Storage Status:": "Stav NFS úložiska v Proxmoxe:",
"Proxmox System Update": "Aktualizácia systému Proxmox", "Proxmox System Update": "Aktualizácia systému Proxmox",
"Proxmox Update": "Aktualizácia Proxmoxu", "Proxmox Update": "Aktualizácia Proxmoxu",
"Proxmox VE": "Proxmox VE", "Proxmox VE": "Proxmox VE",
"Proxmox VE 8 package upgrade failed. Review the APT output before retrying.": "Aktualizácia balíkov Proxmox VE 8 zlyhala. Pred ďalším pokusom skontrolujte výstup APT.",
"Proxmox VE 8 system update completed successfully": "Aktualizácia systému Proxmox VE 8 bola úspešne dokončená", "Proxmox VE 8 system update completed successfully": "Aktualizácia systému Proxmox VE 8 bola úspešne dokončená",
"Proxmox VE 8 to 9 Manual Upgrade Guide": "Ručný sprievodca aktualizáciou Proxmox VE 8 na 9", "Proxmox VE 8 to 9 Manual Upgrade Guide": "Ručný sprievodca aktualizáciou Proxmox VE 8 na 9",
"Proxmox VE 9.x no-subscription repository created": "Proxmox VE 9.x no-subscription repozitár vytvorený", "Proxmox VE 9.x no-subscription repository created": "Proxmox VE 9.x no-subscription repozitár vytvorený",
@@ -4879,6 +4918,7 @@
"Proxmox VE configuration completed.": "Nastavenie Proxmox VE je dokončené.", "Proxmox VE configuration completed.": "Nastavenie Proxmox VE je dokončené.",
"Proxmox VE safe update completed": "Bezpečná aktualizácia Proxmox VE je dokončená", "Proxmox VE safe update completed": "Bezpečná aktualizácia Proxmox VE je dokončená",
"Proxmox auth logger service created and started": "Služba na logovanie Proxmox prihlásení bola vytvorená a spustená", "Proxmox auth logger service created and started": "Služba na logovanie Proxmox prihlásení bola vytvorená a spustená",
"Proxmox could not apply the container settings": "Proxmox nedokázal použiť nastavenia kontajnera",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Proxmox enterprise repozitár môže chýbať alebo byť nedostupný. Skúšam prepnúť na no-subscription...", "Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Proxmox enterprise repozitár môže chýbať alebo byť nedostupný. Skúšam prepnúť na no-subscription...",
"Proxmox filter configured": "Proxmox filter nastavený", "Proxmox filter configured": "Proxmox filter nastavený",
"Proxmox iSCSI Storage Status:": "Stav iSCSI úložiska v Proxmoxe:", "Proxmox iSCSI Storage Status:": "Stav iSCSI úložiska v Proxmoxe:",
@@ -4995,6 +5035,7 @@
"Rebuilding initramfs to apply nouveau blacklist before installation...": "Znovu vytváram initramfs, aby sa blacklist nouveau použil ešte pred inštaláciou...", "Rebuilding initramfs to apply nouveau blacklist before installation...": "Znovu vytváram initramfs, aby sa blacklist nouveau použil ešte pred inštaláciou...",
"Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "Prebudovanie kontajnera a jeho obnovenie zo zálohy sa testuje na rovnakom obrázku; prvá aktualizácia na novší obrázok je ešte potrebné pozorovať.", "Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "Prebudovanie kontajnera a jeho obnovenie zo zálohy sa testuje na rovnakom obrázku; prvá aktualizácia na novší obrázok je ešte potrebné pozorovať.",
"Recent": "Nedávne", "Recent": "Nedávne",
"Recent PBS tasks": "Nedávne úlohy PBS",
"Recent Samba Servers": "Posledné Samba servery", "Recent Samba Servers": "Posledné Samba servery",
"Recent Samba server": "Posledný Samba server", "Recent Samba server": "Posledný Samba server",
"Recent logs:": "Posledné záznamy:", "Recent logs:": "Posledné záznamy:",
@@ -5054,6 +5095,7 @@
"Recreation completed. Data kept.": "Rekreácia dokončená. Údaje uchovávané.", "Recreation completed. Data kept.": "Rekreácia dokončená. Údaje uchovávané.",
"Recreation prepared": "Pripravené rekreácie", "Recreation prepared": "Pripravené rekreácie",
"Refresh APT index and verify repositories:": "Obnovte APT index a overte repozitáre:", "Refresh APT index and verify repositories:": "Obnovte APT index a overte repozitáre:",
"Refresh the configured APT repositories and review the PBS package upgrade? Repository files will be preserved.": "Obnoviť indexy nakonfigurovaných repozitárov APT a skontrolovať aktualizáciu balíkov PBS? Súbory repozitárov sa zachovajú.",
"Refresh token of the connector, from the Twingate admin console": "Obnovovací token konektora z administrátorskej konzoly Twingate", "Refresh token of the connector, from the Twingate admin console": "Obnovovací token konektora z administrátorskej konzoly Twingate",
"Refresh your browser (Ctrl+Shift+R) to see changes": "Obnovte prehliadač (Ctrl+Shift+R), aby ste videli zmeny", "Refresh your browser (Ctrl+Shift+R) to see changes": "Obnovte prehliadač (Ctrl+Shift+R), aby ste videli zmeny",
"Refresh your browser to see changes (server restart may be required)": "Obnovte stránku v prehliadači, aby ste videli zmeny (môže byť potrebný reštart servera)", "Refresh your browser to see changes (server restart may be required)": "Obnovte stránku v prehliadači, aby ste videli zmeny (môže byť potrebný reštart servera)",
@@ -5143,6 +5185,8 @@
"Remove saved credentials": "Odstráňte uložené poverenia", "Remove saved credentials": "Odstráňte uložené poverenia",
"Remove subscription banner": "Odstrániť hlásenie o predplatnom", "Remove subscription banner": "Odstrániť hlásenie o predplatnom",
"Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Odstrániť aplikáciu? Jeho kontajnerové disky sa vymažú a iba záloha ich môže vrátiť späť.", "Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Odstrániť aplikáciu? Jeho kontajnerové disky sa vymažú a iba záloha ich môže vrátiť späť.",
"Remove the lock left on a VM by an interrupted task": "Odstrániť zámok, ktorý na VM zanechala prerušená úloha",
"Remove the lock left on a container by an interrupted task": "Odstrániť zámok, ktorý na kontajneri zanechala prerušená úloha",
"Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Odstrániť uložené poverenia pre {registry}?\nSpustené aplikácie budú naďalej fungovať.", "Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Odstrániť uložené poverenia pre {registry}?\nSpustené aplikácie budú naďalej fungovať.",
"Remove the unprivileged flag from configuration:": "Odstrániť príznak neprivilegovaného kontajnera z nastavenia:", "Remove the unprivileged flag from configuration:": "Odstrániť príznak neprivilegovaného kontajnera z nastavenia:",
"Remove unused packages and their config": "Odstrániť nepoužívané balíky aj ich nastavenia", "Remove unused packages and their config": "Odstrániť nepoužívané balíky aj ich nastavenia",
@@ -5224,6 +5268,7 @@
"Repository helper unavailable. NVIDIA installation stopped.": "Pomocník úložiska nie je k dispozícii. Inštalácia NVIDIA sa zastavila.", "Repository helper unavailable. NVIDIA installation stopped.": "Pomocník úložiska nie je k dispozícii. Inštalácia NVIDIA sa zastavila.",
"Repository policy returned an unexpected result.": "Politika úložiska vrátila neočakávaný výsledok.", "Repository policy returned an unexpected result.": "Politika úložiska vrátila neočakávaný výsledok.",
"Repository ready.": "Repozitár je pripravený.", "Repository ready.": "Repozitár je pripravený.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.": "Zdroje úložiska sa zmenili, ale obnovenie zoznamu balíkov APT zlyhalo. Prevádzka zastavená;zdroje neboli vrátené späť. Pred pokračovaním skontrolujte Administrácia > Repozitáre a zopakujte apt-get update.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Zdroje úložiska sa zmenili, ale obnovenie zoznamu balíkov APT zlyhalo. Prevádzka zastavená; zdroje neboli vrátené späť. Pred pokračovaním skontrolujte Node > Updates > Repository a zopakujte apt-get update.", "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Zdroje úložiska sa zmenili, ale obnovenie zoznamu balíkov APT zlyhalo. Prevádzka zastavená; zdroje neboli vrátené späť. Pred pokračovaním skontrolujte Node > Updates > Repository a zopakujte apt-get update.",
"Repository switch declined; no APT source changed.": "Prepnutie úložiska bolo odmietnuté; žiadny zdroj APT sa nezmenil.", "Repository switch declined; no APT source changed.": "Prepnutie úložiska bolo odmietnuté; žiadny zdroj APT sa nezmenil.",
"Repository:": "Repozitár:", "Repository:": "Repozitár:",
@@ -5318,7 +5363,9 @@
"Reverting ZFS ARC tuning...": "Vraciam späť ladenie ZFS ARC...", "Reverting ZFS ARC tuning...": "Vraciam späť ladenie ZFS ARC...",
"Reverting pigz wrapper...": "Vraciam späť wrapper pigz...", "Reverting pigz wrapper...": "Vraciam späť wrapper pigz...",
"Reverting vzdump speed tuning...": "Vraciam späť ladenie rýchlosti vzdump...", "Reverting vzdump speed tuning...": "Vraciam späť ladenie rýchlosti vzdump...",
"Review and apply": "Skontrolovať a použiť",
"Review external OCI changes": "Skontrolujte externé zmeny OCI", "Review external OCI changes": "Skontrolujte externé zmeny OCI",
"Review package upgrade": "Skontrolovať aktualizáciu balíkov",
"Review passthrough config files": "Skontrolovať konfiguračné súbory passthrough", "Review passthrough config files": "Skontrolovať konfiguračné súbory passthrough",
"Review what will be removed": "Skontrolujte, čo sa odstráni", "Review what will be removed": "Skontrolujte, čo sa odstráni",
"Rip DVD and Blu-ray media from a browser": "Rip DVD a Blu-ray médiá z prehliadača", "Rip DVD and Blu-ray media from a browser": "Rip DVD a Blu-ray médiá z prehliadača",
@@ -5361,6 +5408,7 @@
"Run the following inside the VM:": "Vo vnútri VM spustite:", "Run the following inside the VM:": "Vo vnútri VM spustite:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "Prepnutie vetvy vydania spustite cez SSH alebo priamo z konzoly Proxmox servera pomocou:", "Run the release-channel switch from an SSH session or the Proxmox host console with:": "Prepnutie vetvy vydania spustite cez SSH alebo priamo z konzoly Proxmox servera pomocou:",
"Run the update from an SSH session or the Proxmox host console with:": "Spustite aktualizáciu zo SSH relácie alebo z konzoly Proxmox hosta pomocou:", "Run the update from an SSH session or the Proxmox host console with:": "Spustite aktualizáciu zo SSH relácie alebo z konzoly Proxmox hosta pomocou:",
"Run this operation as root.": "Túto operáciu spustite ako root.",
"Run upgrade checklist script:": "Spustite kontrolný skript aktualizácie:", "Run upgrade checklist script:": "Spustite kontrolný skript aktualizácie:",
"Running": "Beží", "Running": "Beží",
"Running Lynis security audit...": "Spúšťam bezpečnostný audit Lynis...", "Running Lynis security audit...": "Spúšťam bezpečnostný audit Lynis...",
@@ -5775,6 +5823,7 @@
"ShareCoveX panel": "Panel ShareCoveX", "ShareCoveX panel": "Panel ShareCoveX",
"Shared Directory Ready:": "Zdieľaný priečinok je pripravený:", "Shared Directory Ready:": "Zdieľaný priečinok je pripravený:",
"Shared Group": "Zdieľaná skupina", "Shared Group": "Zdieľaná skupina",
"Shared directories": "Zdieľané priečinky",
"Shared directory created:": "Vytvorený zdieľaný adresár:", "Shared directory created:": "Vytvorený zdieľaný adresár:",
"Shared directory for consume and export": "Zdieľaný adresár pre spotrebu a export", "Shared directory for consume and export": "Zdieľaný adresár pre spotrebu a export",
"Shared directory for copy/sync operations": "Zdieľaný adresár pre kopírovanie/sync operations", "Shared directory for copy/sync operations": "Zdieľaný adresár pre kopírovanie/sync operations",
@@ -5804,6 +5853,7 @@
"Should show pve-manager/9.x.x": "Malo by sa zobraziť pve-manager/9.x.x", "Should show pve-manager/9.x.x": "Malo by sa zobraziť pve-manager/9.x.x",
"Shoutrrr addresses for the notifications (optional)": "Adresy Shoutrrr pre upozornenia (voliteľné)", "Shoutrrr addresses for the notifications (optional)": "Adresy Shoutrrr pre upozornenia (voliteľné)",
"Show All Scripts": "Zobraziť všetky skripty", "Show All Scripts": "Zobraziť všetky skripty",
"Show CT users and their UID/GID on the host": "Používatelia CT a ich UID/GID na hostiteľovi",
"Show CT users in table format": "Zobraziť používateľov CT v tabuľke", "Show CT users in table format": "Zobraziť používateľov CT v tabuľke",
"Show Container Privilege Status": "Zobraziť stav oprávnení kontajnerov", "Show Container Privilege Status": "Zobraziť stav oprávnení kontajnerov",
"Show DNS resolution status": "Zobraziť stav DNS prekladu", "Show DNS resolution status": "Zobraziť stav DNS prekladu",
@@ -5965,6 +6015,7 @@
"Start the VM to begin Windows installation from the mounted ISO.": "Spustite VM a začnite inštaláciu Windowsu z pripojeného ISO.", "Start the VM to begin Windows installation from the mounted ISO.": "Spustite VM a začnite inštaláciu Windowsu z pripojeného ISO.",
"Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Spustiť aplikácie po ich zaregistrovaní? Odpoveď Nie, ak pôvodné kontajnery stále bežia na inom hostiteľovi: oba by používali rovnaké adresy.", "Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Spustiť aplikácie po ich zaregistrovaní? Odpoveď Nie, ak pôvodné kontajnery stále bežia na inom hostiteľovi: oba by používali rovnaké adresy.",
"Start the converted container:": "Spustiť prevedený kontajner:", "Start the converted container:": "Spustiť prevedený kontajner:",
"Start the interactive Proxmox package upgrade": "Spustiť interaktívnu aktualizáciu balíkov Proxmoxu",
"Start the main system upgrade:": "Spustite hlavnú aktualizáciu systému:", "Start the main system upgrade:": "Spustite hlavnú aktualizáciu systému:",
"Start the stack with Proxmox": "Spustiť zásobník s Proxmox", "Start the stack with Proxmox": "Spustiť zásobník s Proxmox",
"Start uploading to PBS — sets a recovery passphrase": "Spustiť nahrávanie do PBS - nastaví obnovovaciu frázu", "Start uploading to PBS — sets a recovery passphrase": "Spustiť nahrávanie do PBS - nastaví obnovovaciu frázu",
@@ -6243,6 +6294,7 @@
"The Paperless persistent volumes need at least 8 GB": "Trvalé objemy bez dokumentov potrebujú najmenej 8 GB", "The Paperless persistent volumes need at least 8 GB": "Trvalé objemy bez dokumentov potrebujú najmenej 8 GB",
"The PostgreSQL volume needs at least 4 GB": "Objem PostgreSQL potrebuje najmenej 4 GB", "The PostgreSQL volume needs at least 4 GB": "Objem PostgreSQL potrebuje najmenej 4 GB",
"The PostgreSQL volume needs at least 8 GB": "Objem PostgreSQL potrebuje najmenej 8 GB", "The PostgreSQL volume needs at least 8 GB": "Objem PostgreSQL potrebuje najmenej 8 GB",
"The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "Aktualizácia Proxmox VE 8 sa nedokončila. Prechod na Proxmox VE 9 sa nespustil.",
"The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Chýba zväzok kľúčov archívu Proxmox;Inštalácia Ceph nemôže bezpečne pokračovať", "The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Chýba zväzok kľúčov archívu Proxmox;Inštalácia Ceph nemôže bezpečne pokračovať",
"The Proxmox inventory and the local configurations differ": "Súpis Proxmox a miestne konfigurácie sa líšia", "The Proxmox inventory and the local configurations differ": "Súpis Proxmox a miestne konfigurácie sa líšia",
"The ROCm image has no support for the AMD GPU of this host:": "Obraz ROCm nemá podporu pre AMD GPU tohto hostiteľa:", "The ROCm image has no support for the AMD GPU of this host:": "Obraz ROCm nemá podporu pre AMD GPU tohto hostiteľa:",
@@ -6313,6 +6365,7 @@
"The build could not be checked beforehand; continuing without that check.": "Zostavenie nebolo možné vopred skontrolovať;pokračovanie bez tejto kontroly.", "The build could not be checked beforehand; continuing without that check.": "Zostavenie nebolo možné vopred skontrolovať;pokračovanie bez tejto kontroly.",
"The cached image does not match the current digest": "Comment", "The cached image does not match the current digest": "Comment",
"The cached image is damaged; it will be downloaded again.": "Comment", "The cached image is damaged; it will be downloaded again.": "Comment",
"The changes need the application installer": "Zmeny vyžadujú inštalačný program aplikácie",
"The character device does not exist:": "Znakové zariadenie neexistuje:", "The character device does not exist:": "Znakové zariadenie neexistuje:",
"The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Príkaz vyžaduje heslo, ktoré nie je ozvene. Po vytvorení užívateľov, webové rozhranie žiada o užívateľské meno a heslo.", "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Príkaz vyžaduje heslo, ktoré nie je ozvene. Po vytvorení užívateľov, webové rozhranie žiada o užívateľské meno a heslo.",
"The command does not name an image": "Príkaz nepomenuje obrázok", "The command does not name an image": "Príkaz nepomenuje obrázok",
@@ -6432,8 +6485,10 @@
"The host bind source does not exist:": "Zdroj hostiteľskej väzby neexistuje:", "The host bind source does not exist:": "Zdroj hostiteľskej väzby neexistuje:",
"The host bind source is not a regular file or directory:": "Zdroj odkazu hostiteľa nie je bežný súbor alebo adresár:", "The host bind source is not a regular file or directory:": "Zdroj odkazu hostiteľa nie je bežný súbor alebo adresár:",
"The host directory changed before it was mounted": "Adresár hostiteľa sa zmenil predtým, ako bol pripojený", "The host directory changed before it was mounted": "Adresár hostiteľa sa zmenil predtým, ako bol pripojený",
"The host directory does not exist": "Adresár na hostiteľovi neexistuje",
"The host directory may not be accessible from an unprivileged container.": "Priečinok na hostovi nemusí byť dostupný z neprivilegovaného kontajnera.", "The host directory may not be accessible from an unprivileged container.": "Priečinok na hostovi nemusí byť dostupný z neprivilegovaného kontajnera.",
"The host has no IPv4 address on the selected bridge": "Hostiteľ nemá IPv4 adresu na vybranom moste", "The host has no IPv4 address on the selected bridge": "Hostiteľ nemá IPv4 adresu na vybranom moste",
"The host loses access to them. If the group includes a network or storage controller the host needs, cancel at the summary.": "Hostiteľ k nim stratí prístup. Ak skupina obsahuje sieťový alebo úložný radič, ktorý hostiteľ potrebuje, zrušte to v súhrne.",
"The host monitor does not see the real host memory": "Monitor hostiteľa nevidí skutočnú pamäť hostiteľa", "The host monitor does not see the real host memory": "Monitor hostiteľa nevidí skutočnú pamäť hostiteľa",
"The host monitor does not share this host namespace:": "Monitor hostiteľa nezdieľa tento menový priestor hostiteľa:", "The host monitor does not share this host namespace:": "Monitor hostiteľa nezdieľa tento menový priestor hostiteľa:",
"The host monitor include is unknown or differs from the saved record": "Include monitora hostiteľa je neznámy alebo sa líši od uloženého záznamu", "The host monitor include is unknown or differs from the saved record": "Include monitora hostiteľa je neznámy alebo sa líši od uloženého záznamu",
@@ -6623,6 +6678,7 @@
"The repair must preserve the image dependencies:": "Oprava musí zachovať závislosť obrazu:", "The repair must preserve the image dependencies:": "Oprava musí zachovať závislosť obrazu:",
"The requested VMID block is already in use": "Požadovaný blok VMID sa už používa", "The requested VMID block is already in use": "Požadovaný blok VMID sa už používa",
"The requested machine learning GPU profile is not working; it is not replaced by CPU": "Požadovaný profil strojového učenia sa GPU nefunguje; nie je nahradený CPU", "The requested machine learning GPU profile is not working; it is not replaced by CPU": "Požadovaný profil strojového učenia sa GPU nefunguje; nie je nahradený CPU",
"The resource edit could not be fully rolled back. Open Manage again to review and import the current Proxmox settings.": "Zmenu prostriedkov sa nepodarilo úplne vrátiť späť. Znova otvorte správu aplikácií, skontrolujte aktuálne nastavenia systému Proxmox a prevezmite ich.",
"The restored service did not pass its health check": "Obnovená služba neprešla kontrolou stavu", "The restored service did not pass its health check": "Obnovená služba neprešla kontrolou stavu",
"The restored service stopped; the recovery is not confirmed": "Obnovená služba sa zastavila; obnova sa nepotvrdila", "The restored service stopped; the recovery is not confirmed": "Obnovená služba sa zastavila; obnova sa nepotvrdila",
"The reviewed Tandoor stack does not require a privileged LXC.": "Preskúmaný Tandoor stack require privilegovaný LXC.", "The reviewed Tandoor stack does not require a privileged LXC.": "Preskúmaný Tandoor stack require privilegovaný LXC.",
@@ -6668,6 +6724,7 @@
"The selected VM": "Vybraná VM", "The selected VM": "Vybraná VM",
"The selected VM is running.": "Vybraná VM beží.", "The selected VM is running.": "Vybraná VM beží.",
"The selected base folder does not exist and could not be created:": "Vybraný základný priečinok neexistuje a nepodarilo sa ho vytvoriť:", "The selected base folder does not exist and could not be created:": "Vybraný základný priečinok neexistuje a nepodarilo sa ho vytvoriť:",
"The selected bridge does not exist": "Vybraný sieťový most neexistuje",
"The selected bridge has no IPv4 subnet for the host-monitor firewall": "Vybraný bridge nemá IPv4 podsieť pre firewall monitora hostiteľa", "The selected bridge has no IPv4 subnet for the host-monitor firewall": "Vybraný bridge nemá IPv4 podsieť pre firewall monitora hostiteľa",
"The selected configuration needs to start the LXC during the installation": "Vybraná konfigurácia musí spustiť LXC počas inštalácie", "The selected configuration needs to start the LXC during the installation": "Vybraná konfigurácia musí spustiť LXC počas inštalácie",
"The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Vybraný kontajner je neprivilegovaný. Na priame priradenie zariadenia je potrebný privilegovaný kontajner.", "The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Vybraný kontajner je neprivilegovaný. Na priame priradenie zariadenia je potrebný privilegovaný kontajner.",
@@ -6690,6 +6747,7 @@
"The service builds its own image; only a published image can be installed": "Služba buduje svoj vlastný obraz; môže byť nainštalovaný iba zverejnený obrázok", "The service builds its own image; only a published image can be installed": "Služba buduje svoj vlastný obraz; môže byť nainštalovaný iba zverejnený obrázok",
"The service declares no image:": "Služba neprehlasuje žiadny obrázok:", "The service declares no image:": "Služba neprehlasuje žiadny obrázok:",
"The setting has no final value:": "Nastavenie nemá žiadnu konečnú hodnotu:", "The setting has no final value:": "Nastavenie nemá žiadnu konečnú hodnotu:",
"The settings could not be fully rolled back. Review the container and its saved modification before continuing.": "Nepodarilo sa vrátiť všetky nastavenia. Pred pokračovaním skontrolujte kontajner a uloženú zmenu.",
"The share already exists in smb.conf:": "Zdieľanie už existuje v smb.conf:", "The share already exists in smb.conf:": "Zdieľanie už existuje v smb.conf:",
"The shared destination is not a directory": "Zdieľaná destinácia nie je adresár", "The shared destination is not a directory": "Zdieľaná destinácia nie je adresár",
"The shared directory points to a protected host path": "Zdieľaný adresár ukazuje na chránenú cestu hostiteľa", "The shared directory points to a protected host path": "Zdieľaný adresár ukazuje na chránenú cestu hostiteľa",
@@ -6757,19 +6815,24 @@
"These Proxmox resources were added outside ProxMenux:": "Tieto zdroje Proxmox boli pridané mimo ProxMenux:", "These Proxmox resources were added outside ProxMenux:": "Tieto zdroje Proxmox boli pridané mimo ProxMenux:",
"These VMIDs are not free:": "Tieto VMID nie sú zadarmo:", "These VMIDs are not free:": "Tieto VMID nie sú zadarmo:",
"These are the changes that will be made": "Tieto zmeny sa vykonajú", "These are the changes that will be made": "Tieto zmeny sa vykonajú",
"These changes were made in Proxmox outside ProxMenux:": "Tieto zmeny boli vykonané v Proxmox mimo ProxMenux:",
"These container mount paths have an extension-like suffix:": "Tieto cesty pripojenia v kontajneri majú príponu podobnú koncovke súboru:", "These container mount paths have an extension-like suffix:": "Tieto cesty pripojenia v kontajneri majú príponu podobnú koncovke súboru:",
"These containers were restored from a backup and this host has no record of their OCI application:": "Tieto kontajnery boli obnovené zo zálohy a tento hostiteľ nemá žiadny záznam o ich aplikácii OCI:", "These containers were restored from a backup and this host has no record of their OCI application:": "Tieto kontajnery boli obnovené zo zálohy a tento hostiteľ nemá žiadny záznam o ich aplikácii OCI:",
"These devices share the IOMMU group of the selected GPU and will be assigned to the VM with it:": "Tieto zariadenia zdieľajú skupinu IOMMU vybranej GPU a priradia sa k VM spolu s ňou:",
"These host backups run with this job and expect its storage:": "Tieto zálohy hostiteľa sa spúšťajú s touto úlohou a očakávajú jej uloženie:", "These host backups run with this job and expect its storage:": "Tieto zálohy hostiteľa sa spúšťajú s touto úlohou a očakávajú jej uloženie:",
"These host backups run with this job and will stop running:": "Tieto zálohy hostiteľa sa spúšťajú s touto úlohou a prestanú sa spúšťať:", "These host backups run with this job and will stop running:": "Tieto zálohy hostiteľa sa spúšťajú s touto úlohou a prestanú sa spúšťať:",
"These interface configurations will be removed": "Tieto nastavenia rozhraní sa odstránia", "These interface configurations will be removed": "Tieto nastavenia rozhraní sa odstránia",
"These manual changes cannot be adopted safely:": "Tieto manuálne zmeny nie je možné bezpečne prijať:", "These manual changes cannot be adopted safely:": "Tieto manuálne zmeny nie je možné bezpečne prijať:",
"These paths will not be restored live and will be extracted for manual recovery.": "Tieto cesty sa neobnovia za behu a rozbalia sa na ručnú obnovu.", "These paths will not be restored live and will be extracted for manual recovery.": "Tieto cesty sa neobnovia za behu a rozbalia sa na ručnú obnovu.",
"These settings require an application-specific adaptation. The image was not downloaded and the container was not recreated.": "Tieto nastavenia vyžadujú úpravu špecifickú pre aplikáciu. Obraz sa nestiahol a kontajner sa nevytvoril znova.",
"These values are asked during the installation:": "Tieto hodnoty sa požadujú počas inštalácie:", "These values are asked during the installation:": "Tieto hodnoty sa požadujú počas inštalácie:",
"They are described in the image documentation:": "Ich popis nájdete v dokumentácii image:", "They are described in the image documentation:": "Ich popis nájdete v dokumentácii image:",
"They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Pred pokračovaním budú pridané do oci-compose. Nepodporované alebo zmenené zdroje sa neimportujú.", "They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "Pred pokračovaním budú pridané do oci-compose. Nepodporované alebo zmenené zdroje sa neimportujú.",
"They will be saved in oci-compose before continuing.": "Pred pokračovaním budú uložené v oci-compose.",
"This CIFS share is mounted with restrictive permissions.": "Toto CIFS zdieľanie je pripojené s obmedzujúcimi oprávneniami.", "This CIFS share is mounted with restrictive permissions.": "Toto CIFS zdieľanie je pripojené s obmedzujúcimi oprávneniami.",
"This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Táto GPU sa v ProxMenux považuje za nekompatibilnú s GPU passthrough do VM.", "This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Táto GPU sa v ProxMenux považuje za nekompatibilnú s GPU passthrough do VM.",
"This NFS share is fully restricted — even the host root cannot write to it.": "Toto NFS zdieľanie je úplne obmedzené - ani root na hostovi doň nevie zapisovať.", "This NFS share is fully restricted — even the host root cannot write to it.": "Toto NFS zdieľanie je úplne obmedzené - ani root na hostovi doň nevie zapisovať.",
"This Proxmox Backup Server has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PBS source will be disabled.": "Tento Proxmox Backup Server nemá aktívne predplatné. Prepnúť na repozitár bez predplatného? Nedostupný zdroj Enterprise PBS bude vypnutý.",
"This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Táto VM vyžaduje ďalšie kroky inštalácie. Návod nájdete tu:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144", "This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Táto VM vyžaduje ďalšie kroky inštalácie. Návod nájdete tu:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144",
"This account is for {registry}. Enter an image from that registry.": "Tento účet je pre {registry}. Zadajte obrázok z tohto registra.", "This account is for {registry}. Enter an image from that registry.": "Tento účet je pre {registry}. Zadajte obrázok z tohto registra.",
"This action will:": "Táto akcia vykoná:", "This action will:": "Táto akcia vykoná:",
@@ -6831,10 +6894,13 @@
"This may take several minutes...": "Môže to trvať niekoľko minút...", "This may take several minutes...": "Môže to trvať niekoľko minút...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "To znamená, že Proxmox sa o pripojenie stará priamo; pre NFS/CIFS úložiská na hostovi netreba ručne upravovať /etc/fstab.", "This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "To znamená, že Proxmox sa o pripojenie stará priamo; pre NFS/CIFS úložiská na hostovi netreba ručne upravovať /etc/fstab.",
"This means the credentials are incorrect.": "To znamená, že prihlasovacie údaje nie sú správne.", "This means the credentials are incorrect.": "To znamená, že prihlasovacie údaje nie sú správne.",
"This menu requires Proxmox Backup Server.": "Táto ponuka vyžaduje Proxmox Backup Server.",
"This might indicate network connectivity issues.": "Môže to znamenať problém so sieťovým pripojením.", "This might indicate network connectivity issues.": "Môže to znamenať problém so sieťovým pripojením.",
"This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Tento monitor používa privilegovaný LXC, zdieľa procesy a sieť s Proxmox a vypne AppArmor v CT. Používa IP adresu a firewall hostiteľa. Kompromitovaný obraz by mohol mať vplyv na hostiteľa; nevystavujte svoje webové UI internetu.", "This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Tento monitor používa privilegovaný LXC, zdieľa procesy a sieť s Proxmox a vypne AppArmor v CT. Používa IP adresu a firewall hostiteľa. Kompromitovaný obraz by mohol mať vplyv na hostiteľa; nevystavujte svoje webové UI internetu.",
"This only removes the schedule. Existing backup archives are NOT deleted.": "Týmto sa odstráni iba plán. Existujúce zálohy NEBUDÚ vymazané.", "This only removes the schedule. Existing backup archives are NOT deleted.": "Týmto sa odstráni iba plán. Existujúce zálohy NEBUDÚ vymazané.",
"This operation may take several minutes and requires internet connectivity.": "Táto operácia môže trvať niekoľko minút a vyžaduje pripojenie na internet.", "This operation may take several minutes and requires internet connectivity.": "Táto operácia môže trvať niekoľko minút a vyžaduje pripojenie na internet.",
"This operation requires a Proxmox VE host. Use the PBS menu on Proxmox Backup Server.": "Táto operácia vyžaduje hostiteľa Proxmox VE. Na Proxmox Backup Serveri použite ponuku PBS.",
"This operation supports PBS 3 on Debian 12 and PBS 4 on Debian 13.": "Táto operácia podporuje PBS 3 na Debiane 12 a PBS 4 na Debiane 13.",
"This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Tento balík bol nainštalovaný staršími verziami inštalačného programu ProxMenux Coral, ktorý umiestnil ovládač jadra M.2 na každý systém, vrátane nastavení iba cez USB.Nie je potrebný pre zariadenia Coral USB, ktoré používajú iba libedgetpu1-std / libedgetpu1-max.", "This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Tento balík bol nainštalovaný staršími verziami inštalačného programu ProxMenux Coral, ktorý umiestnil ovládač jadra M.2 na každý systém, vrátane nastavení iba cez USB.Nie je potrebný pre zariadenia Coral USB, ktoré používajú iba libedgetpu1-std / libedgetpu1-max.",
"This passphrase is the ONLY way to access encrypted Borg backups.": "Táto fráza je JEDINÝ spôsob, ako sa dostať k šifrovaným Borg zálohám.", "This passphrase is the ONLY way to access encrypted Borg backups.": "Táto fráza je JEDINÝ spôsob, ako sa dostať k šifrovaným Borg zálohám.",
"This path is already used as a mount point in this container.": "Táto cesta už je v tomto kontajneri použitá ako mount point.", "This path is already used as a mount point in this container.": "Táto cesta už je v tomto kontajneri použitá ako mount point.",
@@ -6850,6 +6916,7 @@
"This script cannot be executed from the Proxmox web terminal.": "Tento skript sa nedá spustiť z webového terminálu Proxmoxu.", "This script cannot be executed from the Proxmox web terminal.": "Tento skript sa nedá spustiť z webového terminálu Proxmoxu.",
"This script must be run as root.": "Tento skript musí byť spustený ako root.", "This script must be run as root.": "Tento skript musí byť spustený ako root.",
"This script must be run on a Proxmox host.": "Tento skript musí byť spustený na Proxmox serveri.", "This script must be run on a Proxmox host.": "Tento skript musí byť spustený na Proxmox serveri.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox Backup Server": "Tento skript použije nasledujúce optimalizácie a pokročilé úpravy na váš Proxmox Backup Server",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Tento skript použije tieto optimalizácie a pokročilé úpravy na vašom serveri Proxmox VE", "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Tento skript použije tieto optimalizácie a pokročilé úpravy na vašom serveri Proxmox VE",
"This script will update your Proxmox VE system with advanced options:": "Tento skript aktualizuje váš Proxmox VE systém s pokročilými možnosťami:", "This script will update your Proxmox VE system with advanced options:": "Tento skript aktualizuje váš Proxmox VE systém s pokročilými možnosťami:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Táto relácia beží v termináli Monitora. Ak by sa to spustilo odtiaľto, pripojenie sa počas inštalácie preruší a prepnutie môže zostať v zlom stave.", "This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Táto relácia beží v termináli Monitora. Ak by sa to spustilo odtiaľto, pripojenie sa počas inštalácie preruší a prepnutie môže zostať v zlom stave.",
@@ -6981,6 +7048,7 @@
"Unable to identify the root filesystem. Aborting.": "Nepodarilo sa identifikovať koreňový súborový systém. Prerušuje sa.", "Unable to identify the root filesystem. Aborting.": "Nepodarilo sa identifikovať koreňový súborový systém. Prerušuje sa.",
"Unable to resolve system ZFS pool disks. Aborting.": "Nepodarilo sa vyriešiť systémové disky ZFS fondu. Prerušuje sa.", "Unable to resolve system ZFS pool disks. Aborting.": "Nepodarilo sa vyriešiť systémové disky ZFS fondu. Prerušuje sa.",
"Unable to resolve system disk topology. Aborting.": "Nepodarilo sa vyriešiť topológiu systémového disku. Prerušuje sa.", "Unable to resolve system disk topology. Aborting.": "Nepodarilo sa vyriešiť topológiu systémového disku. Prerušuje sa.",
"Unapplied changes": "Nepoužité zmeny",
"Understand the security implications of privileged containers": "Uistite sa, že rozumiete bezpečnostným dôsledkom privilegovaných kontajnerov", "Understand the security implications of privileged containers": "Uistite sa, že rozumiete bezpečnostným dôsledkom privilegovaných kontajnerov",
"Unexpected Proxmox inventory; recovery blocked": "Neočakávaný inventár Proxmox; zablokovaná obnova", "Unexpected Proxmox inventory; recovery blocked": "Neočakávaný inventár Proxmox; zablokovaná obnova",
"Unexpected formatting directory in the new Valkey volume": "Neočakávaný formátovanie adresára v novom hlasitosti Valkey", "Unexpected formatting directory in the new Valkey volume": "Neočakávaný formátovanie adresára v novom hlasitosti Valkey",
@@ -7080,6 +7148,7 @@
"Unsupported OCI-LXC AppArmor profile:": "Nepodporovaný profil OCI-LXC AppArmor:", "Unsupported OCI-LXC AppArmor profile:": "Nepodporovaný profil OCI-LXC AppArmor:",
"Unsupported OCI-LXC seccomp profile:": "Nepodporovaný profil OCI-LXC seccomp:", "Unsupported OCI-LXC seccomp profile:": "Nepodporovaný profil OCI-LXC seccomp:",
"Unsupported Terminal": "Nepodporovaný terminál", "Unsupported Terminal": "Nepodporovaný terminál",
"Unsupported architecture for Figurine:": "Nepodporovaná architektúra pre Figurine:",
"Unsupported architecture:": "Nepodporovaná architektúra:", "Unsupported architecture:": "Nepodporovaná architektúra:",
"Unsupported backup compression": "Nepodporovaná kompresia zálohy", "Unsupported backup compression": "Nepodporovaná kompresia zálohy",
"Unsupported credential pattern:": "Nepodporovaný credential vzor:", "Unsupported credential pattern:": "Nepodporovaný credential vzor:",
@@ -7095,6 +7164,7 @@
"Unsupported mount type": "Nepodporovaný typ pripojenia", "Unsupported mount type": "Nepodporovaný typ pripojenia",
"Unsupported mount type:": "Nepodporovaný typ pripojenia:", "Unsupported mount type:": "Nepodporovaný typ pripojenia:",
"Unsupported native device type:": "Nepodporovaný natívne typ zariadenia:", "Unsupported native device type:": "Nepodporovaný natívne typ zariadenia:",
"Unsupported network settings": "Nepodporované sieťové nastavenia",
"Unsupported operation": "Nepodporovaná operation", "Unsupported operation": "Nepodporovaná operation",
"Unsupported or unknown Proxmox version; no repository changed.": "Nepodporovaná alebo neznáma verzia Proxmox; žiadne úložisko sa nezmenilo.", "Unsupported or unknown Proxmox version; no repository changed.": "Nepodporovaná alebo neznáma verzia Proxmox; žiadne úložisko sa nezmenilo.",
"Unsupported output format:": "Nepodporovaný výstupný formát:", "Unsupported output format:": "Nepodporovaný výstupný formát:",
@@ -7122,6 +7192,7 @@
"Update NVIDIA in LXC Containers": "Aktualizovať NVIDIA v LXC kontajneroch", "Update NVIDIA in LXC Containers": "Aktualizovať NVIDIA v LXC kontajneroch",
"Update OCI": "Aktualizovať OCI", "Update OCI": "Aktualizovať OCI",
"Update OCI stack": "Aktualizovať OCI stack", "Update OCI stack": "Aktualizovať OCI stack",
"Update PBS using configured repositories": "Aktualizovať PBS pomocou nakonfigurovaných repozitárov",
"Update PVE enterprise repository (Only if using enterprise):": "Aktualizujte PVE enterprise repozitár (iba ak používate enterprise):", "Update PVE enterprise repository (Only if using enterprise):": "Aktualizujte PVE enterprise repozitár (iba ak používate enterprise):",
"Update Proxmox VE Appliance Manager": "Aktualizovať správcu aplikácií Proxmox VE", "Update Proxmox VE Appliance Manager": "Aktualizovať správcu aplikácií Proxmox VE",
"Update Proxmox package lists": "Aktualizovať zoznamy Proxmox balíkov", "Update Proxmox package lists": "Aktualizovať zoznamy Proxmox balíkov",
@@ -7174,6 +7245,7 @@
"Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Asistent aktualizácie: Proxmox VE 8 → 9 (Trixie)", "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Asistent aktualizácie: Proxmox VE 8 → 9 (Trixie)",
"Upgrade canceled by user": "Aktualizácia zrušená používateľom", "Upgrade canceled by user": "Aktualizácia zrušená používateľom",
"Upgrade one node at a time": "Aktualizujte vždy iba jeden uzol naraz", "Upgrade one node at a time": "Aktualizujte vždy iba jeden uzol naraz",
"Upgrade the system, offering the": "Aktualizujte systém a ponúknite",
"Upgrade to Proxmox VE 9": "Aktualizovať na Proxmox VE 9", "Upgrade to Proxmox VE 9": "Aktualizovať na Proxmox VE 9",
"Upgrading NVIDIA utils (Arch)...": "Aktualizujem NVIDIA nástroje (Arch)...", "Upgrading NVIDIA utils (Arch)...": "Aktualizujem NVIDIA nástroje (Arch)...",
"Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Nahrať šifrovanú kópiu kľúča do PBS, aby ste ju vedeli obnoviť na preinštalovanom hostovi iba pomocou frázy?", "Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Nahrať šifrovanú kópiu kľúča do PBS, aby ste ju vedeli obnoviť na preinštalovanom hostovi iba pomocou frázy?",
@@ -7611,6 +7683,7 @@
"active VF(s)": "aktívne VF", "active VF(s)": "aktívne VF",
"active VFs": "aktívnymi VF", "active VFs": "aktívnymi VF",
"active Virtual Functions. Changing its driver binding would destroy every VF.": "aktívnymi Virtual Functions. Zmena naviazania ovládača by zrušila každú VF.", "active Virtual Functions. Changing its driver binding would destroy every VF.": "aktívnymi Virtual Functions. Zmena naviazania ovládača by zrušila každú VF.",
"added in Proxmox": "pridané v Proxmox",
"after copy; source tree is incomplete.": "po kopírovaní; zdrojový strom je neúplný.", "after copy; source tree is incomplete.": "po kopírovaní; zdrojový strom je neúplný.",
"after update.": "po aktualizácii.", "after update.": "po aktualizácii.",
"all guests": "všetci hostia", "all guests": "všetci hostia",
@@ -7978,6 +8051,7 @@
"remapped users": "remapovanými používateľmi", "remapped users": "remapovanými používateľmi",
"remove the (now-empty) directory if possible": "odstrániť teraz prázdny priečinok, ak to bude možné", "remove the (now-empty) directory if possible": "odstrániť teraz prázdny priečinok, ak to bude možné",
"removed from Proxmox": "odstránené z Proxmoxu", "removed from Proxmox": "odstránené z Proxmoxu",
"removed in Proxmox": "odstránené v Proxmox",
"removed successfully from Proxmox.": "úspešne odstránené z Proxmoxu.", "removed successfully from Proxmox.": "úspešne odstránené z Proxmoxu.",
"requires running the official installer, which restarts the Monitor service.": "vyžaduje spustenie oficiálneho inštalátora, ktorý reštartuje službu Monitor.", "requires running the official installer, which restarts the Monitor service.": "vyžaduje spustenie oficiálneho inštalátora, ktorý reštartuje službu Monitor.",
"requires the package": "vyžaduje balík", "requires the package": "vyžaduje balík",
@@ -8081,18 +8155,20 @@
"using": "cez", "using": "cez",
"using direct path (not guaranteed to survive reboots).": "používa sa priama cesta (nie je zaručené, že prežije reštarty).", "using direct path (not guaranteed to survive reboots).": "používa sa priama cesta (nie je zaručené, že prežije reštarty).",
"vCPUs:": "vCPU:", "vCPUs:": "vCPU:",
"value changed in Proxmox": "hodnota zmenená v Proxmox",
"vfio-pci IDs configured": "vfio-pci ID nastavené", "vfio-pci IDs configured": "vfio-pci ID nastavené",
"vfio-pci IDs in /etc/modprobe.d/vfio.conf": "vfio-pci ID v /etc/modprobe.d/vfio.conf", "vfio-pci IDs in /etc/modprobe.d/vfio.conf": "vfio-pci ID v /etc/modprobe.d/vfio.conf",
"video": "video", "video": "video",
"video + recognition": "video + rozpoznávanie", "video + recognition": "video + rozpoznávanie",
"video only": "iba video", "video only": "iba video",
"vzdump backup speed optimization completed": "Optimalizácia rýchlosti záloh vzdump je dokončená", "vzdump backup speed optimization completed": "Optimalizácia rýchlosti záloh vzdump je dokončená",
"wallabag builds its links from the address given during the installation. If it does not match the address of the container, edit lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME in /etc/pve/lxc/<CTID>.conf with the container stopped, and start it again.": "wallabag buduje svoje odkazy z adresy uvedenej počas inštalácie. Ak nezodpovedá adrese kontajnera, upravte prostredie lxc. čas: SYMFONY ENV DOMAIN NAME in /etc/pve/lxc/ <CTID>.conf s kontajnerom zastavil, a začať znova.", "wallabag builds its links from the address given during the installation. If it does not match the address of the container, edit lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME in /etc/pve/lxc/<CTID>.conf with the container stopped, and start it again.": "wallabag vytvára odkazy z adresy zadanej pri inštalácii. Ak sa nezhoduje s adresou kontajnera, zastavte ho, upravte lxc.environment.runtime: SYMFONY__ENV__DOMAIN_NAME v /etc/pve/lxc/<CTID>.conf a znova ho spustite.",
"wallabag listens on port 80 of the container and stores its data in SQLite.": "wallabag počúva port 80 kontajnera a ukladá svoje údaje v SQLite.", "wallabag listens on port 80 of the container and stores its data in SQLite.": "wallabag počúva port 80 kontajnera a ukladá svoje údaje v SQLite.",
"wallabag web interface": "Webové rozhranie wallabag", "wallabag web interface": "Webové rozhranie wallabag",
"was": "bolo", "was": "bolo",
"was installed, but the kernel reports:": "bol nainštalovaný, ale jadro hlási:", "was installed, but the kernel reports:": "bol nainštalovaný, ale jadro hlási:",
"when finished": "po dokončení", "when finished": "po dokončení",
"when the server has no subscription (you will be asked)": "keď server nemá predplatné (budete požiadaný)",
"will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "pri ďalšom reštarte znova naviaže GPU na vfio-pci, čím sa preruší ovládač, ktorý sa má nainštalovať.", "will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "pri ďalšom reštarte znova naviaže GPU na vfio-pci, čím sa preruší ovládač, ktorý sa má nainštalovať.",
"wipefs failed on": "wipefs zlyhalo na", "wipefs failed on": "wipefs zlyhalo na",
"with": "za", "with": "za",
+97 -21
View File
@@ -37,6 +37,7 @@
"/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (Proxmox standard vzdump sökväg)", "/var/lib/vz/dump (Proxmox default vzdump path)": "/var/lib/vz/dump (Proxmox standard vzdump sökväg)",
"/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (Proxmox standard)", "/var/lib/vz/dump (Proxmox default)": "/var/lib/vz/dump (Proxmox standard)",
"1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx för alla. Ingen delad grupp behövs.", "1777 = sticky bit + rwx for all. No shared group needed.": "1777 = sticky bit + rwx för alla. Ingen delad grupp behövs.",
"====== PBS UPDATE COMPLETED ======": "====== PBS UPPDATERING AVSLUTAD ======",
"====== PVE UPDATE COMPLETED ======": "====== PVE UPPDATERING AVSLUTAD ======", "====== PVE UPDATE COMPLETED ======": "====== PVE UPPDATERING AVSLUTAD ======",
">> Create a new backup job <<": ">> Skapa ett nytt säkerhetskopieringsjobb <<", ">> Create a new backup job <<": ">> Skapa ett nytt säkerhetskopieringsjobb <<",
">> Save and return <<": ">> Spara och returnera <<", ">> Save and return <<": ">> Spara och returnera <<",
@@ -87,6 +88,7 @@
"A multi-container application is not recreated: its containers are updated together.": "Ett program med flera behållare återskapas inte: dess behållare uppdateras tillsammans.", "A multi-container application is not recreated: its containers are updated together.": "Ett program med flera behållare återskapas inte: dess behållare uppdateras tillsammans.",
"A new ProxMenux version is available:": "En ny ProxMenux-version är tillgänglig:", "A new ProxMenux version is available:": "En ny ProxMenux-version är tillgänglig:",
"A new kernel is staged for the next boot:": "En ny kärna är iscensatt för nästa uppstart:", "A new kernel is staged for the next boot:": "En ny kärna är iscensatt för nästa uppstart:",
"A newer kernel is installed. Reboot before the next backup window to use it.": "En nyare kärna är installerad. Starta om innan nästa säkerhetskopieringsfönster för att använda det.",
"A newer version is available:": "En nyare version finns tillgänglig:", "A newer version is available:": "En nyare version finns tillgänglig:",
"A pending operation exists for": "En väntande operation finns för", "A pending operation exists for": "En väntande operation finns för",
"A pending stack assembly already exists; it is not overwritten": "En väntande stack församling existerar redan; den är inte överskriven", "A pending stack assembly already exists; it is not overwritten": "En väntande stack församling existerar redan; den är inte överskriven",
@@ -169,6 +171,8 @@
"APT language configuration updated": "APT-språkkonfigurationen uppdaterad", "APT language configuration updated": "APT-språkkonfigurationen uppdaterad",
"APT language downloads restored": "APT-språknedladdningar återställda", "APT language downloads restored": "APT-språknedladdningar återställda",
"APT package lists updated": "APT-paketlistor uppdaterade", "APT package lists updated": "APT-paketlistor uppdaterade",
"APT refresh failed. Check the PBS subscription and repositories in Administration > Repositories. Log:": "APT-uppdatering misslyckades. Kontrollera PBS-prenumerationen och arkiven i Administration > Repositories. Logga:",
"APT refresh failed. Check the PBS subscription and repositories in the native PBS interface; no source was changed.": "APT-uppdateringen misslyckades. Kontrollera PBS-prenumerationen och paketförråden i PBS eget gränssnitt; inga paketkällor ändrades.",
"APT update failed. Check repository access; NVIDIA installation stopped.": "APT-uppdateringen misslyckades. Kontrollera åtkomsten till arkiven; NVIDIA-installationen stoppades.", "APT update failed. Check repository access; NVIDIA installation stopped.": "APT-uppdateringen misslyckades. Kontrollera åtkomsten till arkiven; NVIDIA-installationen stoppades.",
"Aborted": "Avbruten", "Aborted": "Avbruten",
"About to restore": "På väg att återställa", "About to restore": "På väg att återställa",
@@ -318,7 +322,7 @@
"After confirming, you will be asked to choose the NVIDIA driver version to install.": "Efter bekräftelse kommer du att bli ombedd att välja NVIDIA-drivrutinsversionen att installera.", "After confirming, you will be asked to choose the NVIDIA driver version to install.": "Efter bekräftelse kommer du att bli ombedd att välja NVIDIA-drivrutinsversionen att installera.",
"After detailed analysis, no changes are needed.": "Efter detaljerad analys behövs inga ändringar.", "After detailed analysis, no changes are needed.": "Efter detaljerad analys behövs inga ändringar.",
"After detailed analysis, no cleanup is needed.": "Efter detaljerad analys behövs ingen rengöring.", "After detailed analysis, no cleanup is needed.": "Efter detaljerad analys behövs ingen rengöring.",
"After logging in, run: ip a to obtain the IP address.\nThen, enter that IP address in your web browser like this:\n http://IP_ADDRESS\n\nThis will open the Umbral OS dashboard.": "När du har loggat in, kör: ip a för att få IP-adressen.\nAnge sedan den IP-adressen i din webbläsare så här:\n http://IP_ADRESS\n\nDetta öppnar Umbral OS-instrumentpanelen.", "After logging in, run: ip a to obtain the IP address.\nThen, enter that IP address in your web browser like this:\n http://IP_ADDRESS\n\nThis will open the Umbral OS dashboard.": "Kör ip a efter inloggningen för att ta reda på IP-adressen.\nAnge sedan adressen i webbläsaren så här:\n http://IP_ADDRESS\n\nDå öppnas instrumentpanelen för Umbral OS.",
"After pasting on the server, ensure the file is chmod 600 and owned by": "Efter att ha klistrat in på servern, se till att filen är chmod 600 och ägs av", "After pasting on the server, ensure the file is chmod 600 and owned by": "Efter att ha klistrat in på servern, se till att filen är chmod 600 och ägs av",
"After reboot the system will be fully accessible (SSH, web UI, login), but the following components will be reinstalled in BACKGROUND — until they finish, commands like nvidia-smi may not yet be available:": "Efter omstart kommer systemet att vara fullt tillgängligt (SSH, webbgränssnitt, inloggning), men följande komponenter kommer att installeras om i BAKGRUND - tills de är klara kanske kommandon som nvidia-smi inte är tillgängliga ännu:", "After reboot the system will be fully accessible (SSH, web UI, login), but the following components will be reinstalled in BACKGROUND — until they finish, commands like nvidia-smi may not yet be available:": "Efter omstart kommer systemet att vara fullt tillgängligt (SSH, webbgränssnitt, inloggning), men följande komponenter kommer att installeras om i BAKGRUND - tills de är klara kanske kommandon som nvidia-smi inte är tillgängliga ännu:",
"After reboot, these components will reinstall in background:": "Efter omstart kommer dessa komponenter att installeras om i bakgrunden:", "After reboot, these components will reinstall in background:": "Efter omstart kommer dessa komponenter att installeras om i bakgrunden:",
@@ -330,7 +334,7 @@
"After this LXC → VM switch, reboot the host so the new binding state is applied cleanly.": "Efter denna LXC → VM-switch, starta om värden så att det nya bindningsläget tillämpas korrekt.", "After this LXC → VM switch, reboot the host so the new binding state is applied cleanly.": "Efter denna LXC → VM-switch, starta om värden så att det nya bindningsläget tillämpas korrekt.",
"Airsonic Advanced web interface": "Airsonic Advanced webbgränssnitt", "Airsonic Advanced web interface": "Airsonic Advanced webbgränssnitt",
"Airsonic-advanced is a free, web-based media streamer, providing ubiquitious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.": "Airsonic-advanced är en gratis, webbaserad media streamer, som ger ubiquitious tillgång till din musik. Använd den för att dela din musik med vänner, eller för att lyssna på din egen musik på jobbet. Du kan strömma till flera spelare samtidigt, till exempel till en spelare i ditt kök och en annan i ditt vardagsrum.", "Airsonic-advanced is a free, web-based media streamer, providing ubiquitious access to your music. Use it to share your music with friends, or to listen to your own music while at work. You can stream to multiple players simultaneously, for instance to one player in your kitchen and another in your living room.": "Airsonic-advanced är en gratis, webbaserad media streamer, som ger ubiquitious tillgång till din musik. Använd den för att dela din musik med vänner, eller för att lyssna på din egen musik på jobbet. Du kan strömma till flera spelare samtidigt, till exempel till en spelare i ditt kök och en annan i ditt vardagsrum.",
"Aliases added to .bashrc": "Alias ​​har lagts till i .bashrc", "Aliases added to .bashrc": "Alias har lagts till i .bashrc",
"Alist Sync web interface": "Alist Sync webbgränssnitt", "Alist Sync web interface": "Alist Sync webbgränssnitt",
"All": "Alla", "All": "Alla",
"All Available Scripts": "Alla tillgängliga skript", "All Available Scripts": "Alla tillgängliga skript",
@@ -425,6 +429,7 @@
"Apply": "Tillämpas", "Apply": "Tillämpas",
"Apply AMD CPU fixes": "Tillämpa AMD CPU-fixar", "Apply AMD CPU fixes": "Tillämpa AMD CPU-fixar",
"Apply Available Updates": "Tillämpa tillgängliga uppdateringar", "Apply Available Updates": "Tillämpa tillgängliga uppdateringar",
"Apply PBS package upgrade": "Tillämpa PBS-paketuppgraderingen",
"Apply and restart services:": "Tillämpa och starta om tjänster:", "Apply and restart services:": "Tillämpa och starta om tjänster:",
"Apply available updates": "Tillämpa tillgängliga uppdateringar", "Apply available updates": "Tillämpa tillgängliga uppdateringar",
"Apply boot/initramfs changes": "Tillämpa boot/initramfs ändringar", "Apply boot/initramfs changes": "Tillämpa boot/initramfs ändringar",
@@ -438,12 +443,15 @@
"Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Vill du använda alternativ från den aktuella katalogmallen? Nya nödvändiga sökvägar och inställningar kan begäras. Granska den resulterande konfigurationen innan du återskapar CT.", "Apply options from the current catalog template? New required paths and settings may be requested. Review the resulting configuration before recreating the CT.": "Vill du använda alternativ från den aktuella katalogmallen? Nya nödvändiga sökvägar och inställningar kan begäras. Granska den resulterande konfigurationen innan du återskapar CT.",
"Apply read+write access for 'others' on the host directory?": "Tillämpa läs+skrivbehörighet för \"andra\" på värdkatalogen?", "Apply read+write access for 'others' on the host directory?": "Tillämpa läs+skrivbehörighet för \"andra\" på värdkatalogen?",
"Apply the options from the current catalog template? Your data and configuration are kept.": "Applicera alternativen från den aktuella katalogmallen? Din data och konfiguration hålls.", "Apply the options from the current catalog template? Your data and configuration are kept.": "Applicera alternativen från den aktuella katalogmallen? Din data och konfiguration hålls.",
"Apply the reviewed package upgrade now? Services may restart and packages listed for removal will be removed.": "Tillämpa den granskade paketuppgraderingen nu? Tjänster kan startas om och paket som anges för borttagning kommer att tas bort.",
"Apply these changes?": "Tillämpa dessa ändringar?", "Apply these changes?": "Tillämpa dessa ändringar?",
"Applying AMD-specific fixes...": "Tillämpar AMD-specifika korrigeringar...", "Applying AMD-specific fixes...": "Tillämpar AMD-specifika korrigeringar...",
"Applying CPU and RAM changes without rebuilding the container...": "Tillämpar ändringarna av CPU och RAM utan att återskapa containern...",
"Applying Changes": "Tillämpa ändringar", "Applying Changes": "Tillämpa ändringar",
"Applying Controller/NVMe passthrough to VM": "Tillämpar styrenhets-/NVMe-passthrough på VM", "Applying Controller/NVMe passthrough to VM": "Tillämpar styrenhets-/NVMe-passthrough på VM",
"Applying balanced memory optimization settings...": "Tillämpar inställningar för balanserad minnesoptimering...", "Applying balanced memory optimization settings...": "Tillämpar inställningar för balanserad minnesoptimering...",
"Applying changes safely...": "Tillämpar ändringar på ett säkert sätt...", "Applying changes safely...": "Tillämpar ändringar på ett säkert sätt...",
"Applying container settings without rebuilding the container...": "Tillämpar inställningarna utan att återskapa containern...",
"Applying custom restore": "Använder anpassad återställning", "Applying custom restore": "Använder anpassad återställning",
"Applying default VM configuration": "Tillämpar standard VM-konfiguration", "Applying default VM configuration": "Tillämpar standard VM-konfiguration",
"Applying host permissions for unprivileged LXC bind-mounts...": "Tillämpar värdbehörigheter för oprivilegierade LXC bind-mounts...", "Applying host permissions for unprivileged LXC bind-mounts...": "Tillämpar värdbehörigheter för oprivilegierade LXC bind-mounts...",
@@ -535,6 +543,7 @@
"Available Borg targets:": "Tillgängliga Borg-mål:", "Available Borg targets:": "Tillgängliga Borg-mål:",
"Available Disks on Host": "Tillgängliga diskar på värd", "Available Disks on Host": "Tillgängliga diskar på värd",
"Available ISO Images": "Tillgängliga ISO-bilder", "Available ISO Images": "Tillgängliga ISO-bilder",
"Available PBS Version": "Tillgänglig PBS-version",
"Available PBS repositories:": "Tillgängliga PBS-lager:", "Available PBS repositories:": "Tillgängliga PBS-lager:",
"Available PVE Version": "Tillgänglig PVE-version", "Available PVE Version": "Tillgänglig PVE-version",
"Available Shares:": "Tillgängliga andelar:", "Available Shares:": "Tillgängliga andelar:",
@@ -746,6 +755,7 @@
"Cannot verify the reused disk:": "Kan inte verifiera den återanvända disken:", "Cannot verify the reused disk:": "Kan inte verifiera den återanvända disken:",
"Cannot write the image archive to {storage}. Check storage access and mount status.": "Kan inte skriva bildarkivet till {storage}. Kontrollera lagringsåtkomst och monteringsstatus.", "Cannot write the image archive to {storage}. Check storage access and mount status.": "Kan inte skriva bildarkivet till {storage}. Kontrollera lagringsåtkomst och monteringsstatus.",
"Capabilities cannot be kept and all dropped at the same time": "Förmågor kan inte hållas och alla tappas samtidigt", "Capabilities cannot be kept and all dropped at the same time": "Förmågor kan inte hållas och alla tappas samtidigt",
"Catalog options": "Katalogalternativ",
"Category": "Kategori", "Category": "Kategori",
"Caution: Maximum mode generates more heat.": "Varning: Maximalt läge genererar mer värme.", "Caution: Maximum mode generates more heat.": "Varning: Maximalt läge genererar mer värme.",
"Ceph check skipped by user flag (--ignore-ceph-check)": "Ceph-kontroll hoppas över av användarflaggan (--ignore-ceph-check)", "Ceph check skipped by user flag (--ignore-ceph-check)": "Ceph-kontroll hoppas över av användarflaggan (--ignore-ceph-check)",
@@ -880,6 +890,7 @@
"Choose a schedule (systemd calendar-event syntax):": "Välj ett schema (syntax för systemd kalenderhändelser):", "Choose a schedule (systemd calendar-event syntax):": "Välj ett schema (syntax för systemd kalenderhändelser):",
"Choose a script to execute:": "Välj ett skript att köra:", "Choose a script to execute:": "Välj ett skript att köra:",
"Choose a server:": "Välj en server:", "Choose a server:": "Välj en server:",
"Choose a setting. Changes are applied only after the final review.": "Välj en inställning. Ändringarna tillämpas först efter den slutliga granskningen.",
"Choose a share to mount:": "Välj en resurs att montera:", "Choose a share to mount:": "Välj en resurs att montera:",
"Choose a specific supported GPU or USB node": "Välj en specifik GPU- eller USB-nod som stöds", "Choose a specific supported GPU or USB node": "Välj en specifik GPU- eller USB-nod som stöds",
"Choose action for this controller/NVMe:": "Välj åtgärd för denna kontroller/NVMe:", "Choose action for this controller/NVMe:": "Välj åtgärd för denna kontroller/NVMe:",
@@ -960,6 +971,7 @@
"Code-server is VS Code running on a remote server, accessible through the browser.": "Kod-server är VS-kod som körs på en fjärrserver, tillgänglig via webbläsaren.", "Code-server is VS Code running on a remote server, accessible through the browser.": "Kod-server är VS-kod som körs på en fjärrserver, tillgänglig via webbläsaren.",
"CodeProject.AI Server": "CodeProject. AI Server", "CodeProject.AI Server": "CodeProject. AI Server",
"Command": "Kommando", "Command": "Kommando",
"Command failed": "Kommandot misslyckades",
"Command override for an unknown service:": "Kommando åsidosätter en okänd tjänst:", "Command override for an unknown service:": "Kommando åsidosätter en okänd tjänst:",
"Commenting any residual Bookworm lines in *.list...": "Kommenterar eventuella återstående Bookworm-rader i *.list...", "Commenting any residual Bookworm lines in *.list...": "Kommenterar eventuella återstående Bookworm-rader i *.list...",
"Commenting legacy PVE 8 repository .list files (if any)...": "Kommenterar äldre PVE 8-arkiv .list-filer (om några)...", "Commenting legacy PVE 8 repository .list files (if any)...": "Kommenterar äldre PVE 8-arkiv .list-filer (om några)...",
@@ -1516,6 +1528,7 @@
"Current NVIDIA inventory resolved: a refresh is required": "Nuvarande NVIDIA inventering löst: en uppfriskning är required", "Current NVIDIA inventory resolved: a refresh is required": "Nuvarande NVIDIA inventering löst: en uppfriskning är required",
"Current NVIDIA inventory resolved: no refresh is required": "Nuvarande NVIDIA inventering löst: ingen uppfriskning är required", "Current NVIDIA inventory resolved: no refresh is required": "Nuvarande NVIDIA inventering löst: ingen uppfriskning är required",
"Current Network Configuration": "Aktuell nätverkskonfiguration", "Current Network Configuration": "Aktuell nätverkskonfiguration",
"Current PBS Version": "Aktuell PBS-version",
"Current PVE Version": "Aktuell PVE-version", "Current PVE Version": "Aktuell PVE-version",
"Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Aktuella ProxMenux-värdskript registrerar fjärrresurser som Proxmox-lagringar med hjälp av pvesm.", "Current ProxMenux host scripts register remote shares as Proxmox storages using pvesm.": "Aktuella ProxMenux-värdskript registrerar fjärrresurser som Proxmox-lagringar med hjälp av pvesm.",
"Current ProxMenux version:": "Aktuell ProxMenux-version:", "Current ProxMenux version:": "Aktuell ProxMenux-version:",
@@ -1630,8 +1643,8 @@
"Default Proxmox logo applied": "Standard Proxmox-logotyp tillämpas", "Default Proxmox logo applied": "Standard Proxmox-logotyp tillämpas",
"Default is none (video-only passthrough). Use SPACE to toggle selections.": "Standard är ingen (endast videogenomströmning). Använd MELLANSLAG för att växla mellan val.", "Default is none (video-only passthrough). Use SPACE to toggle selections.": "Standard är ingen (endast videogenomströmning). Använd MELLANSLAG för att växla mellan val.",
"Default journald configuration restored": "Standard journalkonfiguration återställd", "Default journald configuration restored": "Standard journalkonfiguration återställd",
"Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "Standardplatsen är /mnt/<namn>. Delingen kommer att monteras här på värden med öppna behörigheter så att en oprivilegierad LXC kan bind-mounta och skriva till den. För LXC-åtkomst, bind-montera den här sökvägen med LXC Mount Manager.", "Default location is /mnt/<name>. The share will be mounted here on the host with open permissions so an unprivileged LXC can bind-mount and write to it. For LXC access, bind-mount this path with the LXC Mount Manager.": "Standardplatsen är /mnt/<name>. Den delade resursen monteras här på värden med öppna behörigheter, så att en oprivilegierad LXC-container kan bind-montera den och skriva till den. Använd LXC Mount Manager för att skapa monteringen i LXC-containern.",
"Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "Standardplatsen är /mnt/<namn>. Delen kommer att monteras här på värden. Använd denna sökväg i /etc/fstab. För LXC-åtkomst, bind-montera den här sökvägen med LXC Mount Manager.", "Default location is /mnt/<name>. The share will be mounted here on the host. Use this path in /etc/fstab. For LXC access, bind-mount this path with the LXC Mount Manager.": "Standardplatsen är /mnt/<name>. Den delade resursen monteras här på värden. Använd sökvägen i /etc/fstab. För åtkomst från en LXC-container, skapa en bind-montering av sökvägen med LXC Mount Manager.",
"Default login": "Standard inloggning", "Default login": "Standard inloggning",
"Default options": "Standardalternativ", "Default options": "Standardalternativ",
"Default options read/write": "Standardalternativ läs/skriv", "Default options read/write": "Standardalternativ läs/skriv",
@@ -1681,6 +1694,7 @@
"Detected Coral hardware:": "Upptäckt Coral-hårdvara:", "Detected Coral hardware:": "Upptäckt Coral-hårdvara:",
"Detected GPU(s):": "Upptäckta GPU:er:", "Detected GPU(s):": "Upptäckta GPU:er:",
"Detected Intel CPU": "Upptäckte Intel CPU", "Detected Intel CPU": "Upptäckte Intel CPU",
"Detected Proxmox Backup Server": "Upptäckte Proxmox Backup Server",
"Detected Proxmox VE": "Detekterade Proxmox VE", "Detected Proxmox VE": "Detekterade Proxmox VE",
"Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Upptäckte Proxmox VE 8.x - Installerar Ceph Squid för Debian", "Detected Proxmox VE 8.x - Installing Ceph Squid for Debian": "Upptäckte Proxmox VE 8.x - Installerar Ceph Squid för Debian",
"Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Upptäckte Proxmox VE 9.x - Installerar Ceph Squid för Debian Trixie", "Detected Proxmox VE 9.x - Installing Ceph Squid for Debian Trixie": "Upptäckte Proxmox VE 9.x - Installerar Ceph Squid för Debian Trixie",
@@ -1762,6 +1776,9 @@
"Disabling and stopping rpcbind service and socket...": "Inaktivera och stoppa rpcbind-tjänsten och -socket...", "Disabling and stopping rpcbind service and socket...": "Inaktivera och stoppa rpcbind-tjänsten och -socket...",
"Disabling non-free firmware warnings...": "Inaktiverar icke-fri firmware-varningar...", "Disabling non-free firmware warnings...": "Inaktiverar icke-fri firmware-varningar...",
"Disabling portmapper/rpcbind for security...": "Inaktiverar portmapper/rpcbind för säkerhet...", "Disabling portmapper/rpcbind for security...": "Inaktiverar portmapper/rpcbind för säkerhet...",
"Discard changes": "Kasta ändringar",
"Discard unapplied changes?": "Kasta ändringar som inte har tillämpats?",
"Discover NFS exports on a server": "Hitta NFS-exporter på en server",
"Discover targets on portal:": "Upptäck mål på portalen:", "Discover targets on portal:": "Upptäck mål på portalen:",
"Discovering iSCSI targets...": "Upptäcker iSCSI-mål...", "Discovering iSCSI targets...": "Upptäcker iSCSI-mål...",
"Discovery successful": "Upptäckten lyckades", "Discovery successful": "Upptäckten lyckades",
@@ -1841,6 +1858,7 @@
"Do you want to proceed with the system update?": "Vill du fortsätta med systemuppdateringen?", "Do you want to proceed with the system update?": "Vill du fortsätta med systemuppdateringen?",
"Do you want to proceed?": "Vill du fortsätta?", "Do you want to proceed?": "Vill du fortsätta?",
"Do you want to reboot now?": "Vill du starta om nu?", "Do you want to reboot now?": "Vill du starta om nu?",
"Do you want to remove the Proxmox subscription banner from the Proxmox Backup Server web interface?": "Vill du ta bort Proxmox-prenumerationsbannern från Proxmox Backup Server-webbgränssnittet?",
"Do you want to remove the stale entry from vfio.conf and continue?": "Vill du ta bort den inaktuella posten från vfio.conf och fortsätta?", "Do you want to remove the stale entry from vfio.conf and continue?": "Vill du ta bort den inaktuella posten från vfio.conf och fortsätta?",
"Do you want to remove the stale entry from vfio.conf?": "Vill du ta bort den inaktuella posten från vfio.conf?", "Do you want to remove the stale entry from vfio.conf?": "Vill du ta bort den inaktuella posten från vfio.conf?",
"Do you want to restart the network service now to apply changes?": "Vill du starta om nätverkstjänsten nu för att tillämpa ändringar?", "Do you want to restart the network service now to apply changes?": "Vill du starta om nätverkstjänsten nu för att tillämpa ändringar?",
@@ -2018,7 +2036,7 @@
"Enter VM ID:": "Ange VM ID:", "Enter VM ID:": "Ange VM ID:",
"Enter VM/CT ID:": "Ange VM/CT ID:", "Enter VM/CT ID:": "Ange VM/CT ID:",
"Enter ZFS pool name for the selected disk:": "Ange ZFS-poolnamn för den valda disken:", "Enter ZFS pool name for the selected disk:": "Ange ZFS-poolnamn för den valda disken:",
"Enter a name for the mount point (used as /mnt/<name>):": "Ange ett namn för monteringspunkten (används som /mnt/<namn>):", "Enter a name for the mount point (used as /mnt/<name>):": "Ange ett namn för monteringspunkten (används som /mnt/<name>):",
"Enter a name for the new virtual machine:": "Ange ett namn för den nya virtuella maskinen:", "Enter a name for the new virtual machine:": "Ange ett namn för den nya virtuella maskinen:",
"Enter a non-empty password or access token on a single line.": "Ange ett icke-tomt lösenord eller åtkomsttoken på en enda rad.", "Enter a non-empty password or access token on a single line.": "Ange ett icke-tomt lösenord eller åtkomsttoken på en enda rad.",
"Enter a number, or write or paste a command:": "Ange ett nummer, eller skriv eller klistra in ett kommando:", "Enter a number, or write or paste a command:": "Ange ett nummer, eller skriv eller klistra in ett kommando:",
@@ -2143,7 +2161,7 @@
"Etherpad admin page": "Etherpad admin sida", "Etherpad admin page": "Etherpad admin sida",
"Every 12 hours": "Var 12:e timme", "Every 12 hours": "Var 12:e timme",
"Every 3 hours": "Var 3:e timme", "Every 3 hours": "Var 3:e timme",
"Every 6 hours": "Var 6:e ​​timme", "Every 6 hours": "Var 6:e timme",
"Every fail2ban jail ships disabled. Enable the ones you need in /config/fail2ban/jail.local, taking the ready-made jails in /config/fail2ban/jail.d/ as reference, then restart the container.": "Varje fail2ban fängslade fartyg. Möjliggöra de du behöver i /config / fail2ban /jail.local, ta de färdiga fängelserna i /config / fail2ban /jail.d / som referens, sedan starta om behållaren.", "Every fail2ban jail ships disabled. Enable the ones you need in /config/fail2ban/jail.local, taking the ready-made jails in /config/fail2ban/jail.d/ as reference, then restart the container.": "Varje fail2ban fängslade fartyg. Möjliggöra de du behöver i /config / fail2ban /jail.local, ta de färdiga fängelserna i /config / fail2ban /jail.d / som referens, sedan starta om behållaren.",
"Every hour": "Varje timme", "Every hour": "Varje timme",
"Every member of the stack is back to its previous installation.": "Varje medlem av stacken är tillbaka till sin tidigare installation.", "Every member of the stack is back to its previous installation.": "Varje medlem av stacken är tillbaka till sin tidigare installation.",
@@ -2166,7 +2184,7 @@
"Example: pct stop 110": "Exempel: pct stop 110", "Example: pct stop 110": "Exempel: pct stop 110",
"Example: pct stop 114": "Exempel: pct stop 114", "Example: pct stop 114": "Exempel: pct stop 114",
"Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Exempel: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,säkerhetskopia", "Example: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,backup": "Exempel: pvesm add nfs nfs-nas --server 192.168.1.50 --export /volume1/proxmox --content import,säkerhetskopia",
"Example: pvesm path local-lvm:vm-114-disk-0": "Exempel: pvesm sökväg local-lvm:vm-114-disk-0", "Example: pvesm path local-lvm:vm-114-disk-0": "Exempel: pvesm path local-lvm:vm-114-disk-0",
"Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Exempel: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf", "Example: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf": "Exempel: sed -i '/^unprivileged: 1/d' /etc/pve/lxc/110.conf",
"Examples:": "Exempel:", "Examples:": "Exempel:",
"Execute destructive rollback?": "Utföra destruktiv återställning?", "Execute destructive rollback?": "Utföra destruktiv återställning?",
@@ -2266,6 +2284,7 @@
"Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Det gick inte att klona log2ram-förrådet. Kontrollera /tmp/log2ram_install.log", "Failed to clone log2ram repository. Check /tmp/log2ram_install.log": "Det gick inte att klona log2ram-förrådet. Kontrollera /tmp/log2ram_install.log",
"Failed to configure EFI disk": "Det gick inte att konfigurera EFI-disken", "Failed to configure EFI disk": "Det gick inte att konfigurera EFI-disken",
"Failed to configure IOMMU automatically.": "Det gick inte att konfigurera IOMMU automatiskt.", "Failed to configure IOMMU automatically.": "Det gick inte att konfigurera IOMMU automatiskt.",
"Failed to configure Proxmox Backup Server repositories": "Det gick inte att konfigurera Proxmox Backup Server-förråd",
"Failed to configure Proxmox repositories": "Det gick inte att konfigurera Proxmox-förråd", "Failed to configure Proxmox repositories": "Det gick inte att konfigurera Proxmox-förråd",
"Failed to configure TPM device in VM": "Det gick inte att konfigurera TPM-enhet i virtuell dator", "Failed to configure TPM device in VM": "Det gick inte att konfigurera TPM-enhet i virtuell dator",
"Failed to configure repositories.": "Det gick inte att konfigurera förråd.", "Failed to configure repositories.": "Det gick inte att konfigurera förråd.",
@@ -2477,7 +2496,7 @@
"For a consistent export, the VM should be stopped.": "För en konsekvent export bör den virtuella datorn stoppas.", "For a consistent export, the VM should be stopped.": "För en konsekvent export bör den virtuella datorn stoppas.",
"For any question, issue or bug report, please use the official repository": "För alla frågor, problem eller felrapporter, använd det officiella arkivet", "For any question, issue or bug report, please use the official repository": "För alla frågor, problem eller felrapporter, använd det officiella arkivet",
"For dedicated GPUs, FLR is required by this policy to reduce VM": "För dedikerade GPU:er krävs FLR enligt denna policy för att minska virtuella datorer", "For dedicated GPUs, FLR is required by this policy to reduce VM": "För dedikerade GPU:er krävs FLR enligt denna policy för att minska virtuella datorer",
"For example: pct enter 101": "Till exempel: pct ange 101", "For example: pct enter 101": "Till exempel: pct enter 101",
"For guest shares add: --options guest": "För gästandelar lägg till: --options gäst", "For guest shares add: --options guest": "För gästandelar lägg till: --options gäst",
"For security reasons, the system cannot format it.": "Av säkerhetsskäl kan systemet inte formatera det.", "For security reasons, the system cannot format it.": "Av säkerhetsskäl kan systemet inte formatera det.",
"For unprivileged containers, use instead:": "För oprivilegierade behållare, använd istället:", "For unprivileged containers, use instead:": "För oprivilegierade behållare, använd istället:",
@@ -2668,7 +2687,7 @@
"HAOS One is a community image that runs Docker inside the container. The LXC stays unprivileged, but the inner AppArmor profiles may not be available. The first start downloads Home Assistant Core and its add-ons. If the check fails, the CT and /mnt/data are kept for diagnosis.": "HAOS One är en gemenskapsbild som kör Docker inuti behållaren. LXC är oprivilegierad, men de inre AppArmor-profilerna kanske inte är tillgängliga. Den första starten hämtar Home Assistant Core och dess tillägg. Om kontrollen misslyckas, CT och /mnt / data hålls för diagnos.", "HAOS One is a community image that runs Docker inside the container. The LXC stays unprivileged, but the inner AppArmor profiles may not be available. The first start downloads Home Assistant Core and its add-ons. If the check fails, the CT and /mnt/data are kept for diagnosis.": "HAOS One är en gemenskapsbild som kör Docker inuti behållaren. LXC är oprivilegierad, men de inre AppArmor-profilerna kanske inte är tillgängliga. Den första starten hämtar Home Assistant Core och dess tillägg. Om kontrollen misslyckas, CT och /mnt / data hålls för diagnos.",
"HAOS One profile declined": "HAOS One-profilen minskade", "HAOS One profile declined": "HAOS One-profilen minskade",
"HAOS One requires an unprivileged unmanaged LXC with nesting and keyctl, 2 cores, 2048 MB RAM, rootfs of at least 12 GB and /mnt/data of at least 16 GB on a container volume included in backups": "HAOS One requires en oprivilegierad ohanterad LXC med häckning och keyctl, 2 kärnor, 2048 MB RAM, rötter på minst 12 GB och /mnt / data på minst 16 GB på en behållarvolym som ingår i backups", "HAOS One requires an unprivileged unmanaged LXC with nesting and keyctl, 2 cores, 2048 MB RAM, rootfs of at least 12 GB and /mnt/data of at least 16 GB on a container volume included in backups": "HAOS One requires en oprivilegierad ohanterad LXC med häckning och keyctl, 2 kärnor, 2048 MB RAM, rötter på minst 12 GB och /mnt / data på minst 16 GB på en behållarvolym som ingår i backups",
"HTML5 Network Speed Test Server.": "HTML5 Network Speed ​​Test Server.", "HTML5 Network Speed Test Server.": "HTML5 Network Speed Test Server.",
"HTTP service check without a saved URL": "HTTP service check utan sparad URL", "HTTP service check without a saved URL": "HTTP service check utan sparad URL",
"Habridge emulates Philips Hue API to other home automation gateways such as an Amazon Echo/Dot Gen 1 (gen 2 has issues discovering ha-bridge) or other systems that support Philips Hue. The Bridge handles basic commands such as On, Off and brightness commands of the hue protocol. This bridge can control most devices that have a distinct API.": "Habridge emulerar Philips Hue API till andra hemautomatiseringsgateways som Amazon Echo / Dot Gen 1 (gen 2 har problem med att upptäcka ha-bro) eller andra system som stöder Philips Hue. Bron hanterar grundläggande kommandon som On, Off och ljusstyrka kommandon av nyansprotokollet. Denna bro kan styra de flesta enheter som har ett tydligt API.", "Habridge emulates Philips Hue API to other home automation gateways such as an Amazon Echo/Dot Gen 1 (gen 2 has issues discovering ha-bridge) or other systems that support Philips Hue. The Bridge handles basic commands such as On, Off and brightness commands of the hue protocol. This bridge can control most devices that have a distinct API.": "Habridge emulerar Philips Hue API till andra hemautomatiseringsgateways som Amazon Echo / Dot Gen 1 (gen 2 har problem med att upptäcka ha-bro) eller andra system som stöder Philips Hue. Bron hanterar grundläggande kommandon som On, Off och ljusstyrka kommandon av nyansprotokollet. Denna bro kan styra de flesta enheter som har ett tydligt API.",
"HandBrake is an open-source tool, built by volunteers, for converting video from nearly any format to a selection of modern, widely supported codecs.": "HandBrake är ett open-source verktyg, byggt av volontärer, för att konvertera video från nästan alla format till ett urval av moderna, allmänt stödda codecs.", "HandBrake is an open-source tool, built by volunteers, for converting video from nearly any format to a selection of modern, widely supported codecs.": "HandBrake är ett open-source verktyg, byggt av volontärer, för att konvertera video från nästan alla format till ett urval av moderna, allmänt stödda codecs.",
@@ -2755,7 +2774,7 @@
"Host backup attached to PVE job": "Värdsäkerhetskopia kopplad till PVE-jobb", "Host backup attached to PVE job": "Värdsäkerhetskopia kopplad till PVE-jobb",
"Host data is not restored by the backup; confirm it with --acknowledge-external-data": "Värddata återställs inte av säkerhetskopieringen; bekräfta den med --acknowledge-external-data", "Host data is not restored by the backup; confirm it with --acknowledge-external-data": "Värddata återställs inte av säkerhetskopieringen; bekräfta den med --acknowledge-external-data",
"Host device for /dev/kvm": "Värd enhet för /dev/kvm", "Host device for /dev/kvm": "Värd enhet för /dev/kvm",
"Host device for /dev/net/tun": "Värd enhet för /dev/nett/tun", "Host device for /dev/net/tun": "Enhet på värden för /dev/net/tun",
"Host device for /dev/ttyUSB0": "Värd enhet för /dev/ttyUSB0", "Host device for /dev/ttyUSB0": "Värd enhet för /dev/ttyUSB0",
"Host device for /dev/video10": "Värd enhet för /dev/video10", "Host device for /dev/video10": "Värd enhet för /dev/video10",
"Host device for /dev/video11": "Värd enhet för /dev/video11", "Host device for /dev/video11": "Värd enhet för /dev/video11",
@@ -2889,8 +2908,8 @@
"If notifications are enabled (Telegram/Discord/ntfy/...), you will receive a \"Host restore finished\" message when all background tasks complete.": "Om aviseringar är aktiverade (Telegram/Discord/ntfy/...), kommer du att få meddelandet \"Värdåterställning avslutad\" när alla bakgrundsuppgifter är klara.", "If notifications are enabled (Telegram/Discord/ntfy/...), you will receive a \"Host restore finished\" message when all background tasks complete.": "Om aviseringar är aktiverade (Telegram/Discord/ntfy/...), kommer du att få meddelandet \"Värdåterställning avslutad\" när alla bakgrundsuppgifter är klara.",
"If passthrough fails on Windows: install RadeonResetBugFix.": "Om passthrough misslyckas på Windows: installera RadeonResetBugFix.", "If passthrough fails on Windows: install RadeonResetBugFix.": "Om passthrough misslyckas på Windows: installera RadeonResetBugFix.",
"If path not accessible (ZFS/BTRFS):": "Om sökväg inte är tillgänglig (ZFS/BTRFS):", "If path not accessible (ZFS/BTRFS):": "Om sökväg inte är tillgänglig (ZFS/BTRFS):",
"If pvesm path returned a DEVICE (LVM):": "Om pvesm-sökvägen returnerade en DEVICE (LVM):", "If pvesm path returned a DEVICE (LVM):": "Om pvesm path returnerade en ENHET (LVM):",
"If pvesm path returned a DIRECTORY (ZFS/BTRFS):": "Om pvesm-sökvägen returnerade en DIRECTORY (ZFS/BTRFS):", "If pvesm path returned a DIRECTORY (ZFS/BTRFS):": "Om pvesm path returnerade en KATALOG (ZFS/BTRFS):",
"If repositories error:": "Om förrådsfel:", "If repositories error:": "Om förrådsfel:",
"If the container had already been changed, its previous native backup is restored. Shared host directories are not reverted. The disks of the failed attempt are removed when the cleanup succeeds.": "Om behållaren redan hade ändrats, återställs dess tidigare inbyggda säkerhetskopia. Delade värdkataloger återställs inte. Diskarna för det misslyckade försöket tas bort när rensningen lyckas.", "If the container had already been changed, its previous native backup is restored. Shared host directories are not reverted. The disks of the failed attempt are removed when the cleanup succeeds.": "Om behållaren redan hade ändrats, återställs dess tidigare inbyggda säkerhetskopia. Delade värdkataloger återställs inte. Diskarna för det misslyckade försöket tas bort när rensningen lyckas.",
"If the host freezes, remove hostpci entries from": "Om värden fryser, ta bort hostpci-poster från", "If the host freezes, remove hostpci entries from": "Om värden fryser, ta bort hostpci-poster från",
@@ -2973,6 +2992,7 @@
"Inaccessible device during VM startup": "Otillgänglig enhet under VM-start", "Inaccessible device during VM startup": "Otillgänglig enhet under VM-start",
"Inactive": "Inaktiv", "Inactive": "Inaktiv",
"Inactive (entry in fstab, not currently mounted)": "Inaktiv (post i fstab, inte för närvarande monterad)", "Inactive (entry in fstab, not currently mounted)": "Inaktiv (post i fstab, inte för närvarande monterad)",
"Include these changes in oci-compose?": "Inkludera dessa ändringar i oci-compose?",
"Include these resources in oci-compose?": "Inkludera dessa resurser i oci-compose?", "Include these resources in oci-compose?": "Inkludera dessa resurser i oci-compose?",
"Includes /etc/network (may drop SSH immediately)": "Inkluderar /etc/network (kan släppa SSH omedelbart)", "Includes /etc/network (may drop SSH immediately)": "Inkluderar /etc/network (kan släppa SSH omedelbart)",
"Includes /etc/zfs": "Inkluderar /etc/zfs", "Includes /etc/zfs": "Inkluderar /etc/zfs",
@@ -3029,7 +3049,7 @@
"Initiator IQN is authorised on the target": "Initiator IQN är auktoriserad på målet", "Initiator IQN is authorised on the target": "Initiator IQN är auktoriserad på målet",
"Initiator IQN:": "Initiativtagare IQN:", "Initiator IQN:": "Initiativtagare IQN:",
"Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape är professionell kvalitet vektor grafik programvara som körs på Linux, Mac OS X och Windows stationära datorer.", "Inkscape is professional quality vector graphics software which runs on Linux, Mac OS X and Windows desktop computers.": "Inkscape är professionell kvalitet vektor grafik programvara som körs på Linux, Mac OS X och Windows stationära datorer.",
"Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Inuti LXC, skapa administratören: konsol kimai:user: skapa ditt USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN", "Inside the LXC, create the administrator: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN": "Skapa administratören inne i LXC-containern: console kimai:user:create YOUR_USERNAME YOUR_EMAIL ROLE_SUPER_ADMIN",
"Inspect disks before any action": "Inspektera diskar innan någon åtgärd", "Inspect disks before any action": "Inspektera diskar innan någon åtgärd",
"Inspect host device nodes": "Inspektera värdenhetsnoder", "Inspect host device nodes": "Inspektera värdenhetsnoder",
"Inspect passthrough/kernel events": "Inspektera passthrough/kärnhändelser", "Inspect passthrough/kernel events": "Inspektera passthrough/kärnhändelser",
@@ -3209,6 +3229,8 @@
"Invalid ALLOWED_HOSTS value": "Invalid ALLOWED_HOSTS värde", "Invalid ALLOWED_HOSTS value": "Invalid ALLOWED_HOSTS värde",
"Invalid Home Assistant OS check timeout": "Ogiltig Home Assistant OS check timeout", "Invalid Home Assistant OS check timeout": "Ogiltig Home Assistant OS check timeout",
"Invalid ID": "Ogiltigt ID", "Invalid ID": "Ogiltigt ID",
"Invalid IPv4 address": "Ogiltig IPv4-adress",
"Invalid IPv4 gateway": "Ogiltig IPv4-gateway",
"Invalid Intel render path": "Invalid Intel render bana", "Invalid Intel render path": "Invalid Intel render bana",
"Invalid Jellyfin path:": "Invalid Jellyfin väg:", "Invalid Jellyfin path:": "Invalid Jellyfin väg:",
"Invalid MAC address:": "Ogiltig MAC-adress:", "Invalid MAC address:": "Ogiltig MAC-adress:",
@@ -3241,6 +3263,7 @@
"Invalid administrator email": "Invalid administratör e-post", "Invalid administrator email": "Invalid administratör e-post",
"Invalid administrator user name": "Invalid administratör användarnamn", "Invalid administrator user name": "Invalid administratör användarnamn",
"Invalid base VMID": "Ogiltig bas VMID", "Invalid base VMID": "Ogiltig bas VMID",
"Invalid bridge": "Ogiltig brygga",
"Invalid check package:": "Ogiltigt checkpaket:", "Invalid check package:": "Ogiltigt checkpaket:",
"Invalid configuration path:": "Ogiltig konfigurationsväg:", "Invalid configuration path:": "Ogiltig konfigurationsväg:",
"Invalid consume/export volumes": "Ogiltiga konsum/exportvolymer", "Invalid consume/export volumes": "Ogiltiga konsum/exportvolymer",
@@ -3304,6 +3327,7 @@
"Invalid remote name": "Ogiltigt fjärrnamn", "Invalid remote name": "Ogiltigt fjärrnamn",
"Invalid remote path": "Invalid fjärrväg", "Invalid remote path": "Invalid fjärrväg",
"Invalid repair version:": "Ogiltig reparationsversion:", "Invalid repair version:": "Ogiltig reparationsversion:",
"Invalid resource value": "Ogiltigt resursvärde",
"Invalid resources": "Ogiltiga resurser", "Invalid resources": "Ogiltiga resurser",
"Invalid restored volume path": "Invalid återställd volymväg", "Invalid restored volume path": "Invalid återställd volymväg",
"Invalid running state": "Ogiltig körstatus", "Invalid running state": "Ogiltig körstatus",
@@ -3322,6 +3346,7 @@
"Invalid stack members": "Ogiltiga stackmedlemmar", "Invalid stack members": "Ogiltiga stackmedlemmar",
"Invalid stack name": "Invalid stack namn", "Invalid stack name": "Invalid stack namn",
"Invalid stack operation": "Ogiltig stack operation", "Invalid stack operation": "Ogiltig stack operation",
"Invalid storage": "Ogiltig lagring",
"Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "Ogiltigt lagrings-ID. Använd endast bokstäver, siffror, bindestreck och understreck.", "Invalid storage ID. Use only letters, numbers, hyphens and underscores.": "Ogiltigt lagrings-ID. Använd endast bokstäver, siffror, bindestreck och understreck.",
"Invalid storage name": "Ogiltigt lagringsnamn", "Invalid storage name": "Ogiltigt lagringsnamn",
"Invalid suite application": "Invalid suite applikation", "Invalid suite application": "Invalid suite applikation",
@@ -3436,7 +3461,7 @@
"Keyfile removed.": "Nyckelfilen har tagits bort.", "Keyfile removed.": "Nyckelfilen har tagits bort.",
"Keyrings method failed; trying apt-key fallback": "Nyckelringsmetoden misslyckades; försöker apt-key fallback", "Keyrings method failed; trying apt-key fallback": "Nyckelringsmetoden misslyckades; försöker apt-key fallback",
"KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - En Cross Platform och Open Source Electronics Design Automation Suite.", "KiCad - A Cross Platform and Open Source Electronics Design Automation Suite.": "KiCad - En Cross Platform och Open Source Electronics Design Automation Suite.",
"Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai har inget standardkonto. Ange behållaren med: pct enter {main vmid}", "Kimai has no default account. Enter the container with: pct enter {main_vmid}": "Kimai har inget standardkonto. Öppna ett skal i containern med: pct enter {main_vmid}",
"Kimai is a professional grade time-tracking application, free and open-source.": "Kimai är en professionell tidsspårningsapplikation, fri och öppen källkod.", "Kimai is a professional grade time-tracking application, free and open-source.": "Kimai är en professionell tidsspårningsapplikation, fri och öppen källkod.",
"Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa är ett kraftfullt verktyg för att ge dig fullständig kontroll över dina mediebibliotek. Med Kometa kan du ta din anpassning till nästa nivå, med granulär kontroll över metadata, samlingar, överlagringar och mycket mer.", "Kometa is a powerful tool designed to give you complete control over your media libraries. With Kometa, you can take your customization to the next level, with granular control over metadata, collections, overlays, and much more.": "Kometa är ett kraftfullt verktyg för att ge dig fullständig kontroll över dina mediebibliotek. Med Kometa kan du ta din anpassning till nästa nivå, med granulär kontroll över metadata, samlingar, överlagringar och mycket mer.",
"Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa läser sin konfiguration från /config/config.yml och behållaren endast fartyg /config/config.yml.template. Kopiera mallen till config.yml, fyll i required Plex och TMDb-anslutningar och starta sedan om behållaren.", "Kometa reads its configuration from /config/config.yml and the container only ships /config/config.yml.template. Copy the template to config.yml, fill in the required Plex and TMDb connections, then restart the container.": "Kometa läser sin konfiguration från /config/config.yml och behållaren endast fartyg /config/config.yml.template. Kopiera mallen till config.yml, fyll i required Plex och TMDb-anslutningar och starta sedan om behållaren.",
@@ -3507,7 +3532,7 @@
"Limiting size and optimizing journald": "Begränsning av storlek och optimering av journal", "Limiting size and optimizing journald": "Begränsning av storlek och optimering av journal",
"Limiting size and optimizing journald...": "Begränsar storlek och optimerar journal...", "Limiting size and optimizing journald...": "Begränsar storlek och optimerar journal...",
"Limnoria A robust, full-featured, and user/programmer-friendly Python IRC bot, with many existing plugins. Successor of the well-known Supybot.": "Limnoria En robust, fullfjädrad och användarvänlig Python IRC bot, med många befintliga plugins. Framgång för den välkända Supybot.", "Limnoria A robust, full-featured, and user/programmer-friendly Python IRC bot, with many existing plugins. Successor of the well-known Supybot.": "Limnoria En robust, fullfjädrad och användarvänlig Python IRC bot, med många befintliga plugins. Framgång för den välkända Supybot.",
"Limnoria joins no IRC network until its configuration file exists. Create it with the setup wizard from the Proxmox host: pct exec <CTID> -- bash -c 'cd /config && limnoria-wizard'": "Limnoria går med i inget IRC-nätverk tills dess konfigurationsfil finns. Skapa den med installationsguiden från Proxmox-värden: pct exec <CTID> - bash -c \"cd /config & & & & limnoria-wizard\"", "Limnoria joins no IRC network until its configuration file exists. Create it with the setup wizard from the Proxmox host: pct exec <CTID> -- bash -c 'cd /config && limnoria-wizard'": "Limnoria ansluter inte till något IRC-nätverk förrän konfigurationsfilen finns. Skapa den med installationsguiden från Proxmox-värden: pct exec <CTID> -- bash -c 'cd /config && limnoria-wizard'",
"Line to paste (single line, including \"command=...\" prefix):": "Rad att klistra in (en rad, inklusive \"command=...\" prefix):", "Line to paste (single line, including \"command=...\" prefix):": "Rad att klistra in (en rad, inklusive \"command=...\" prefix):",
"Linux Installation Options": "Linux installationsalternativ", "Linux Installation Options": "Linux installationsalternativ",
"Linux/Mac path:": "Linux/Mac sökväg:", "Linux/Mac path:": "Linux/Mac sökväg:",
@@ -4153,6 +4178,7 @@
"No active Proxmox storage accepts OCI template archives.": "Ingen aktiv Proxmox-lagring accepterar OCI-mallarkiv.", "No active Proxmox storage accepts OCI template archives.": "Ingen aktiv Proxmox-lagring accepterar OCI-mallarkiv.",
"No active exports or showmount not available": "Inga aktiva exporter eller showmount är inte tillgängligt", "No active exports or showmount not available": "Inga aktiva exporter eller showmount är inte tillgängligt",
"No active session": "Ingen aktiv session", "No active session": "Ingen aktiv session",
"No active subscription and no usable PBS repository. Noninteractive mode cannot change APT sources; configure them in Administration > Repositories.": "Inget aktivt abonnemang och inget användbart PBS-förråd. Icke-interaktivt läge kan inte ändra APT-källor;konfigurera dem i Administration > Repositories.",
"No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Ingen aktiv prenumeration och inget användbart PVE-arkiv. Icke-interaktivt läge kan inte ändra APT-källor; konfigurera dem i Node > Updates > Repositories.", "No active subscription and no usable PVE repository. Noninteractive mode cannot change APT sources; configure them in Node > Updates > Repositories.": "Ingen aktiv prenumeration och inget användbart PVE-arkiv. Icke-interaktivt läge kan inte ändra APT-källor; konfigurera dem i Node > Updates > Repositories.",
"No additional GPU can be added.": "Ingen ytterligare GPU kan läggas till.", "No additional GPU can be added.": "Ingen ytterligare GPU kan läggas till.",
"No additional device needs to be added.": "Ingen ytterligare enhet behöver läggas till.", "No additional device needs to be added.": "Ingen ytterligare enhet behöver läggas till.",
@@ -4257,6 +4283,7 @@
"No password provided.": "Inget lösenord angett.", "No password provided.": "Inget lösenord angett.",
"No paths available": "Inga vägar tillgängliga", "No paths available": "Inga vägar tillgängliga",
"No paths selected. Select at least one path.": "Inga sökvägar har valts. Välj minst en väg.", "No paths selected. Select at least one path.": "Inga sökvägar har valts. Välj minst en väg.",
"No pending changes": "Inga väntande ändringar",
"No pending paths to schedule for reboot.": "Inga väntande sökvägar att schemalägga för omstart.", "No pending paths to schedule for reboot.": "Inga väntande sökvägar att schemalägga för omstart.",
"No permanent NFS mounts configured.": "Inga permanenta NFS-monteringar har konfigurerats.", "No permanent NFS mounts configured.": "Inga permanenta NFS-monteringar har konfigurerats.",
"No permanent Samba mounts configured.": "Inga permanenta Samba-fästen har konfigurerats.", "No permanent Samba mounts configured.": "Inga permanenta Samba-fästen har konfigurerats.",
@@ -4398,6 +4425,7 @@
"OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM har tagits bort (Puppet artefakter kan behöva manuell rengöring)", "OVH RTM removed (Puppet artefacts may need manual cleanup)": "OVH RTM har tagits bort (Puppet artefakter kan behöva manuell rengöring)",
"OVH server detected": "OVH-server upptäckt", "OVH server detected": "OVH-server upptäckt",
"OVH server detection and RTM installation process completed": "OVH-serverdetektering och RTM-installation har slutförts", "OVH server detection and RTM installation process completed": "OVH-serverdetektering och RTM-installation har slutförts",
"Object detector": "Objektdetektor",
"Object detector for Frigate": "Objektdetektor för Frigate", "Object detector for Frigate": "Objektdetektor för Frigate",
"Observer is not responding on port 4357": "Observer svarar inte på port 4357", "Observer is not responding on port 4357": "Observer svarar inte på port 4357",
"Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian är en noterande app som låter dig skapa, länka och organisera dina anteckningar på din enhet, med hundratals plugins och teman för att anpassa ditt arbetsflöde. Du kan också publicera dina anteckningar online, komma åt dem offline och synkronisera dem säkert med end-to-end-kryptering.", "Obsidian is a note-taking app that lets you create, link, and organize your notes on your device, with hundreds of plugins and themes to customize your workflow. You can also publish your notes online, access them offline, and sync them securely with end-to-end encryption.": "Obsidian är en noterande app som låter dig skapa, länka och organisera dina anteckningar på din enhet, med hundratals plugins och teman för att anpassa ditt arbetsflöde. Du kan också publicera dina anteckningar online, komma åt dem offline och synkronisera dem säkert med end-to-end-kryptering.",
@@ -4523,11 +4551,17 @@
"PBS backup error log": "PBS säkerhetskopia fellogg", "PBS backup error log": "PBS säkerhetskopia fellogg",
"PBS backup failed.": "PBS-säkerhetskopiering misslyckades.", "PBS backup failed.": "PBS-säkerhetskopiering misslyckades.",
"PBS certificate": "PBS-certifikat", "PBS certificate": "PBS-certifikat",
"PBS datastores": "PBS-datalager",
"PBS encryption keyfile (show / replace / remove)": "PBS-krypteringsnyckelfil (visa / ersätt / ta bort)", "PBS encryption keyfile (show / replace / remove)": "PBS-krypteringsnyckelfil (visa / ersätt / ta bort)",
"PBS encryption keyfile — detailed info": "PBS-krypteringsnyckelfil — detaljerad information", "PBS encryption keyfile — detailed info": "PBS-krypteringsnyckelfil — detaljerad information",
"PBS extraction failed": "PBS-extraktion misslyckades", "PBS extraction failed": "PBS-extraktion misslyckades",
"PBS host or IP address:": "PBS-värd eller IP-adress:", "PBS host or IP address:": "PBS-värd eller IP-adress:",
"PBS package update": "PBS-paketuppdatering",
"PBS package upgrade completed. Check whether a reboot is required before your next backup window.": "PBS-paketuppgraderingen är klar. Kontrollera om en omstart behövs före nästa säkerhetskopieringsfönster.",
"PBS package upgrade failed. Review the APT output before retrying.": "PBS-paketuppgraderingen misslyckades. Granska APT-utdata innan du försöker igen.",
"PBS upload": "PBS uppladdning", "PBS upload": "PBS uppladdning",
"PBS version and packages": "PBS-version och paket",
"PBS: using the configured APT repositories without changing sources.": "PBS: använder de konfigurerade APT-paketförråden utan att ändra källor.",
"PCI Address": "PCI-adress", "PCI Address": "PCI-adress",
"PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "PCI-genomföring, TPM-tillstånd, cloud-init-konfiguration, Proxmox-krokar", "PCI passthrough, TPM state, cloud-init configuration, Proxmox hooks": "PCI-genomföring, TPM-tillstånd, cloud-init-konfiguration, Proxmox-krokar",
"PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "PCI-genomföring, TPM-tillstånd, moln-init, ögonblicksbilder, Proxmox-specifika krokar", "PCI passthrough, TPM state, cloud-init, snapshots, Proxmox-specific hooks": "PCI-genomföring, TPM-tillstånd, moln-init, ögonblicksbilder, Proxmox-specifika krokar",
@@ -4567,7 +4601,7 @@
"Partition created:": "Partition skapad:", "Partition created:": "Partition skapad:",
"Partition table wiped": "Skiljebord torkat", "Partition table wiped": "Skiljebord torkat",
"Pass /dev/kvm to the LXC": "Pass /dev/kvm till LXC", "Pass /dev/kvm to the LXC": "Pass /dev/kvm till LXC",
"Pass /dev/net/tun to the LXC": "Pass /dev/netto/tun till LXC", "Pass /dev/net/tun to the LXC": "Ge LXC-containern åtkomst till /dev/net/tun",
"Pass /dev/ttyUSB0 to the LXC": "Pass /dev/ttyUSB0 till LXC", "Pass /dev/ttyUSB0 to the LXC": "Pass /dev/ttyUSB0 till LXC",
"Pass /dev/video10 to the LXC": "Pass /dev/video10 till LXC", "Pass /dev/video10 to the LXC": "Pass /dev/video10 till LXC",
"Pass /dev/video11 to the LXC": "Pass /dev/video11 till LXC", "Pass /dev/video11 to the LXC": "Pass /dev/video11 till LXC",
@@ -4864,12 +4898,17 @@
"Proxmology logo applied": "Proxmology-logotyp applicerad", "Proxmology logo applied": "Proxmology-logotyp applicerad",
"Proxmox 9 system update allready": "Proxmox 9 systemuppdatering redan", "Proxmox 9 system update allready": "Proxmox 9 systemuppdatering redan",
"Proxmox APT repositories configured": "Proxmox APT-förråd konfigurerade", "Proxmox APT repositories configured": "Proxmox APT-förråd konfigurerade",
"Proxmox Backup Server": "Proxmox Backup Server",
"Proxmox Backup Server (PBS) destinations": "Destinationer för Proxmox Backup Server (PBS)", "Proxmox Backup Server (PBS) destinations": "Destinationer för Proxmox Backup Server (PBS)",
"Proxmox Backup Server APT repositories configured": "Proxmox Backup Server APT-förråd konfigurerade",
"Proxmox Backup Server Update": "Proxmox Backup Server Uppdatering",
"Proxmox Backup Server update completed.": "Proxmox Backup Server-uppdatering slutförd.",
"Proxmox CIFS Storage Status:": "Proxmox CIFS-lagringsstatus:", "Proxmox CIFS Storage Status:": "Proxmox CIFS-lagringsstatus:",
"Proxmox NFS Storage Status:": "Proxmox NFS-lagringsstatus:", "Proxmox NFS Storage Status:": "Proxmox NFS-lagringsstatus:",
"Proxmox System Update": "Proxmox systemuppdatering", "Proxmox System Update": "Proxmox systemuppdatering",
"Proxmox Update": "Proxmox-uppdatering", "Proxmox Update": "Proxmox-uppdatering",
"Proxmox VE": "Proxmox VE", "Proxmox VE": "Proxmox VE",
"Proxmox VE 8 package upgrade failed. Review the APT output before retrying.": "Paketuppgraderingen av Proxmox VE 8 misslyckades. Granska APT-utdata innan du försöker igen.",
"Proxmox VE 8 system update completed successfully": "Systemuppdateringen av Proxmox VE 8 slutfördes framgångsrikt", "Proxmox VE 8 system update completed successfully": "Systemuppdateringen av Proxmox VE 8 slutfördes framgångsrikt",
"Proxmox VE 8 to 9 Manual Upgrade Guide": "Proxmox VE 8 till 9 Manuell uppgraderingsguide", "Proxmox VE 8 to 9 Manual Upgrade Guide": "Proxmox VE 8 till 9 Manuell uppgraderingsguide",
"Proxmox VE 9.x no-subscription repository created": "Proxmox VE 9.x arkiv utan prenumeration skapat", "Proxmox VE 9.x no-subscription repository created": "Proxmox VE 9.x arkiv utan prenumeration skapat",
@@ -4877,6 +4916,7 @@
"Proxmox VE configuration completed.": "Proxmox VE-konfigurationen slutförd.", "Proxmox VE configuration completed.": "Proxmox VE-konfigurationen slutförd.",
"Proxmox VE safe update completed": "Proxmox VE säker uppdatering slutförd", "Proxmox VE safe update completed": "Proxmox VE säker uppdatering slutförd",
"Proxmox auth logger service created and started": "Proxmox authlogger-tjänst skapad och startade", "Proxmox auth logger service created and started": "Proxmox authlogger-tjänst skapad och startade",
"Proxmox could not apply the container settings": "Proxmox kunde inte tillämpa containerns inställningar",
"Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Proxmox företagsrepo kan saknas eller är otillgänglig. Försöker byta till ingen prenumeration...", "Proxmox enterprise repo might be missing or inaccessible. Trying to switch to no-subscription...": "Proxmox företagsrepo kan saknas eller är otillgänglig. Försöker byta till ingen prenumeration...",
"Proxmox filter configured": "Proxmox-filter konfigurerat", "Proxmox filter configured": "Proxmox-filter konfigurerat",
"Proxmox iSCSI Storage Status:": "Proxmox iSCSI-lagringsstatus:", "Proxmox iSCSI Storage Status:": "Proxmox iSCSI-lagringsstatus:",
@@ -4993,6 +5033,7 @@
"Rebuilding initramfs to apply nouveau blacklist before installation...": "Bygger om initramfs för att tillämpa nouveau blacklist före installation...", "Rebuilding initramfs to apply nouveau blacklist before installation...": "Bygger om initramfs för att tillämpa nouveau blacklist före installation...",
"Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "Att bygga om behållaren och återställa den från dess säkerhetskopia testas på samma bild; den första uppdateringen av en nyare bild är fortfarande att observera.", "Rebuilding the container and recovering it from its backup are tested on the same image; the first update to a newer image is still to be observed.": "Att bygga om behållaren och återställa den från dess säkerhetskopia testas på samma bild; den första uppdateringen av en nyare bild är fortfarande att observera.",
"Recent": "Ny", "Recent": "Ny",
"Recent PBS tasks": "Senaste PBS-uppgifter",
"Recent Samba Servers": "Senaste Samba-servrar", "Recent Samba Servers": "Senaste Samba-servrar",
"Recent Samba server": "Senaste Samba-servern", "Recent Samba server": "Senaste Samba-servern",
"Recent logs:": "Senaste loggar:", "Recent logs:": "Senaste loggar:",
@@ -5052,6 +5093,7 @@
"Recreation completed. Data kept.": "Recreation slutförd. Data hålls.", "Recreation completed. Data kept.": "Recreation slutförd. Data hålls.",
"Recreation prepared": "Rekreation förberedd", "Recreation prepared": "Rekreation förberedd",
"Refresh APT index and verify repositories:": "Uppdatera APT-index och verifiera förråd:", "Refresh APT index and verify repositories:": "Uppdatera APT-index och verifiera förråd:",
"Refresh the configured APT repositories and review the PBS package upgrade? Repository files will be preserved.": "Uppdatera de konfigurerade APT-paketförråden och granska PBS-paketuppgraderingen? Paketförrådens filer bevaras.",
"Refresh token of the connector, from the Twingate admin console": "Uppdateringstoken för kontakten, från Twingate-administratörskonsolen", "Refresh token of the connector, from the Twingate admin console": "Uppdateringstoken för kontakten, från Twingate-administratörskonsolen",
"Refresh your browser (Ctrl+Shift+R) to see changes": "Uppdatera din webbläsare (Ctrl+Skift+R) för att se ändringar", "Refresh your browser (Ctrl+Shift+R) to see changes": "Uppdatera din webbläsare (Ctrl+Skift+R) för att se ändringar",
"Refresh your browser to see changes (server restart may be required)": "Uppdatera din webbläsare för att se ändringar (serverns omstart kan krävas)", "Refresh your browser to see changes (server restart may be required)": "Uppdatera din webbläsare för att se ändringar (serverns omstart kan krävas)",
@@ -5141,6 +5183,8 @@
"Remove saved credentials": "Ta bort sparade autentiseringsuppgifter", "Remove saved credentials": "Ta bort sparade autentiseringsuppgifter",
"Remove subscription banner": "Ta bort prenumerationsbanner", "Remove subscription banner": "Ta bort prenumerationsbanner",
"Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Vill du ta bort programmet? Dess containerdiskar raderas och endast en säkerhetskopia kan ta tillbaka dem.", "Remove the application? Its container disks are deleted, and only a backup can bring them back.": "Vill du ta bort programmet? Dess containerdiskar raderas och endast en säkerhetskopia kan ta tillbaka dem.",
"Remove the lock left on a VM by an interrupted task": "Ta bort låset som en avbruten uppgift lämnat på en VM",
"Remove the lock left on a container by an interrupted task": "Ta bort låset som en avbruten uppgift lämnat på en container",
"Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Ta bort de sparade referenserna för {registry}?\nAppar som körs fortsätter att fungera.", "Remove the saved credentials for {registry}?\nRunning apps will keep working.": "Ta bort de sparade referenserna för {registry}?\nAppar som körs fortsätter att fungera.",
"Remove the unprivileged flag from configuration:": "Ta bort den oprivilegierade flaggan från konfigurationen:", "Remove the unprivileged flag from configuration:": "Ta bort den oprivilegierade flaggan från konfigurationen:",
"Remove unused packages and their config": "Ta bort oanvända paket och deras konfiguration", "Remove unused packages and their config": "Ta bort oanvända paket och deras konfiguration",
@@ -5210,7 +5254,7 @@
"Replace": "Ersätta", "Replace": "Ersätta",
"Replace <container-id> with the actual ID.": "Ersätt <container-id> med det faktiska ID:t.", "Replace <container-id> with the actual ID.": "Ersätt <container-id> med det faktiska ID:t.",
"Replace <container-id> with your actual container ID": "Ersätt <container-id> med ditt faktiska container-ID", "Replace <container-id> with your actual container ID": "Ersätt <container-id> med ditt faktiska container-ID",
"Replace <storage-identifier> with the value from step 4": "Ersätt <lagringsidentifierare> med värdet från steg 4", "Replace <storage-identifier> with the value from step 4": "Ersätt <storage-identifier> med värdet från steg 4",
"Replace invalid port(s)": "Ersätt ogiltiga port(ar)", "Replace invalid port(s)": "Ersätt ogiltiga port(ar)",
"Replace it?": "Byt ut den?", "Replace it?": "Byt ut den?",
"Replace paths with your actual values from step 5": "Ersätt sökvägar med dina faktiska värden från steg 5", "Replace paths with your actual values from step 5": "Ersätt sökvägar med dina faktiska värden från steg 5",
@@ -5222,6 +5266,7 @@
"Repository helper unavailable. NVIDIA installation stopped.": "Arkivhjälparen är inte tillgänglig. NVIDIA-installationen stoppades.", "Repository helper unavailable. NVIDIA installation stopped.": "Arkivhjälparen är inte tillgänglig. NVIDIA-installationen stoppades.",
"Repository policy returned an unexpected result.": "Arkivpolicyn gav ett oväntat resultat.", "Repository policy returned an unexpected result.": "Arkivpolicyn gav ett oväntat resultat.",
"Repository ready.": "Förvaret klart.", "Repository ready.": "Förvaret klart.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Administration > Repositories and retry apt-get update before continuing.": "Lagringskällorna har ändrats, men uppdateringen av APT-paketlistan misslyckades. Drift stoppad;källor rullades inte tillbaka. Inspektera Administration > Repositories och försök apt-get update igen innan du fortsätter.",
"Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Arkivkällorna har ändrats, men uppdateringen av APT-paketlistan misslyckades. Åtgärden stoppades; källorna återställdes inte. Kontrollera Node > Updates > Repositories och försök apt-get update igen innan du fortsätter.", "Repository sources changed, but APT package-list refresh failed. Operation stopped; sources were not rolled back. Inspect Node > Updates > Repositories and retry apt-get update before continuing.": "Arkivkällorna har ändrats, men uppdateringen av APT-paketlistan misslyckades. Åtgärden stoppades; källorna återställdes inte. Kontrollera Node > Updates > Repositories och försök apt-get update igen innan du fortsätter.",
"Repository switch declined; no APT source changed.": "Arkivbytet avvisades; ingen APT-källa har ändrats.", "Repository switch declined; no APT source changed.": "Arkivbytet avvisades; ingen APT-källa har ändrats.",
"Repository:": "Förvar:", "Repository:": "Förvar:",
@@ -5316,7 +5361,9 @@
"Reverting ZFS ARC tuning...": "Återställer ZFS ARC-inställning...", "Reverting ZFS ARC tuning...": "Återställer ZFS ARC-inställning...",
"Reverting pigz wrapper...": "Återställer pigz-omslag...", "Reverting pigz wrapper...": "Återställer pigz-omslag...",
"Reverting vzdump speed tuning...": "Återställer vzdump-hastighetsinställning...", "Reverting vzdump speed tuning...": "Återställer vzdump-hastighetsinställning...",
"Review and apply": "Granska och tillämpa",
"Review external OCI changes": "Granska externa OCI ändringar", "Review external OCI changes": "Granska externa OCI ändringar",
"Review package upgrade": "Granska paketuppgraderingen",
"Review passthrough config files": "Granska passthrough-konfigurationsfiler", "Review passthrough config files": "Granska passthrough-konfigurationsfiler",
"Review what will be removed": "Granska vad som kommer att tas bort", "Review what will be removed": "Granska vad som kommer att tas bort",
"Rip DVD and Blu-ray media from a browser": "Rip DVD och Blu-ray media från en webbläsare", "Rip DVD and Blu-ray media from a browser": "Rip DVD och Blu-ray media från en webbläsare",
@@ -5359,6 +5406,7 @@
"Run the following inside the VM:": "Kör följande inuti den virtuella datorn:", "Run the following inside the VM:": "Kör följande inuti den virtuella datorn:",
"Run the release-channel switch from an SSH session or the Proxmox host console with:": "Kör release-kanalväxeln från en SSH-session eller Proxmox-värdkonsolen med:", "Run the release-channel switch from an SSH session or the Proxmox host console with:": "Kör release-kanalväxeln från en SSH-session eller Proxmox-värdkonsolen med:",
"Run the update from an SSH session or the Proxmox host console with:": "Kör uppdateringen från en SSH-session eller Proxmox-värdkonsolen med:", "Run the update from an SSH session or the Proxmox host console with:": "Kör uppdateringen från en SSH-session eller Proxmox-värdkonsolen med:",
"Run this operation as root.": "Kör denna åtgärd som root.",
"Run upgrade checklist script:": "Kör skript för uppgraderingschecklista:", "Run upgrade checklist script:": "Kör skript för uppgraderingschecklista:",
"Running": "Spring", "Running": "Spring",
"Running Lynis security audit...": "Kör Lynis säkerhetsgranskning...", "Running Lynis security audit...": "Kör Lynis säkerhetsgranskning...",
@@ -5773,6 +5821,7 @@
"ShareCoveX panel": "ShareCoveX panel", "ShareCoveX panel": "ShareCoveX panel",
"Shared Directory Ready:": "Redo för delad katalog:", "Shared Directory Ready:": "Redo för delad katalog:",
"Shared Group": "Delad grupp", "Shared Group": "Delad grupp",
"Shared directories": "Delade kataloger",
"Shared directory created:": "Delad katalog skapad:", "Shared directory created:": "Delad katalog skapad:",
"Shared directory for consume and export": "Delad katalog för konsumering och export", "Shared directory for consume and export": "Delad katalog för konsumering och export",
"Shared directory for copy/sync operations": "Delad katalog för kopia/synkronisera operations", "Shared directory for copy/sync operations": "Delad katalog för kopia/synkronisera operations",
@@ -5802,6 +5851,7 @@
"Should show pve-manager/9.x.x": "Bör visa pve-manager/9.x.x", "Should show pve-manager/9.x.x": "Bör visa pve-manager/9.x.x",
"Shoutrrr addresses for the notifications (optional)": "Shoutrrr-adresser för aviseringarna (valfritt)", "Shoutrrr addresses for the notifications (optional)": "Shoutrrr-adresser för aviseringarna (valfritt)",
"Show All Scripts": "Visa alla skript", "Show All Scripts": "Visa alla skript",
"Show CT users and their UID/GID on the host": "CT-användare och deras UID/GID på värden",
"Show CT users in table format": "Visa CT-användare i tabellformat", "Show CT users in table format": "Visa CT-användare i tabellformat",
"Show Container Privilege Status": "Visa status för containerprivilegier", "Show Container Privilege Status": "Visa status för containerprivilegier",
"Show DNS resolution status": "Visa DNS-upplösningsstatus", "Show DNS resolution status": "Visa DNS-upplösningsstatus",
@@ -5963,6 +6013,7 @@
"Start the VM to begin Windows installation from the mounted ISO.": "Starta den virtuella datorn för att påbörja Windows-installationen från den monterade ISO.", "Start the VM to begin Windows installation from the mounted ISO.": "Starta den virtuella datorn för att påbörja Windows-installationen från den monterade ISO.",
"Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Starta applikationerna när de är registrerade? Svara Nej om de ursprungliga behållarna fortfarande körs på en annan värd: båda skulle använda samma adresser.", "Start the applications once they are registered? Answer No if the original containers are still running on another host: both would use the same addresses.": "Starta applikationerna när de är registrerade? Svara Nej om de ursprungliga behållarna fortfarande körs på en annan värd: båda skulle använda samma adresser.",
"Start the converted container:": "Starta den konverterade behållaren:", "Start the converted container:": "Starta den konverterade behållaren:",
"Start the interactive Proxmox package upgrade": "Starta den interaktiva paketuppgraderingen av Proxmox",
"Start the main system upgrade:": "Starta huvudsystemuppgraderingen:", "Start the main system upgrade:": "Starta huvudsystemuppgraderingen:",
"Start the stack with Proxmox": "Börja stapeln med Proxmox", "Start the stack with Proxmox": "Börja stapeln med Proxmox",
"Start uploading to PBS — sets a recovery passphrase": "Börja ladda upp till PBS — ställer in en återställningslösenfras", "Start uploading to PBS — sets a recovery passphrase": "Börja ladda upp till PBS — ställer in en återställningslösenfras",
@@ -6242,6 +6293,7 @@
"The Paperless persistent volumes need at least 8 GB": "De papperslösa ihållande volymerna behöver minst 8 GB", "The Paperless persistent volumes need at least 8 GB": "De papperslösa ihållande volymerna behöver minst 8 GB",
"The PostgreSQL volume needs at least 4 GB": "PostgreSQL-volymen behöver minst 4 GB", "The PostgreSQL volume needs at least 4 GB": "PostgreSQL-volymen behöver minst 4 GB",
"The PostgreSQL volume needs at least 8 GB": "PostgreSQL-volymen behöver minst 8 GB", "The PostgreSQL volume needs at least 8 GB": "PostgreSQL-volymen behöver minst 8 GB",
"The Proxmox VE 8 update did not finish. The upgrade to Proxmox VE 9 was not started.": "Uppdateringen av Proxmox VE 8 slutfördes inte. Uppgraderingen till Proxmox VE 9 startades inte.",
"The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Proxmox arkivnyckelringen saknas;Ceph-installationen kan inte fortsätta på ett säkert sätt", "The Proxmox archive keyring is missing; Ceph installation cannot continue safely": "Proxmox arkivnyckelringen saknas;Ceph-installationen kan inte fortsätta på ett säkert sätt",
"The Proxmox inventory and the local configurations differ": "Proxmox-inventeringen och de lokala konfigurationerna skiljer sig åt", "The Proxmox inventory and the local configurations differ": "Proxmox-inventeringen och de lokala konfigurationerna skiljer sig åt",
"The ROCm image has no support for the AMD GPU of this host:": "ROCm-avbilden har inget stöd för AMD GPU på denna värd:", "The ROCm image has no support for the AMD GPU of this host:": "ROCm-avbilden har inget stöd för AMD GPU på denna värd:",
@@ -6312,6 +6364,7 @@
"The build could not be checked beforehand; continuing without that check.": "Bygget kunde inte kontrolleras i förväg;fortsätter utan den kontrollen.", "The build could not be checked beforehand; continuing without that check.": "Bygget kunde inte kontrolleras i förväg;fortsätter utan den kontrollen.",
"The cached image does not match the current digest": "Den cachade bilden matchar inte den nuvarande smältningen", "The cached image does not match the current digest": "Den cachade bilden matchar inte den nuvarande smältningen",
"The cached image is damaged; it will be downloaded again.": "Den cachade bilden är skadad; den kommer att laddas ner igen.", "The cached image is damaged; it will be downloaded again.": "Den cachade bilden är skadad; den kommer att laddas ner igen.",
"The changes need the application installer": "Ändringarna kräver programmets installationsverktyg",
"The character device does not exist:": "Karaktärenheten existerar inte:", "The character device does not exist:": "Karaktärenheten existerar inte:",
"The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Kommandot begär ett lösenord som inte echoed. Efter att ha skapat användarna ber webbgränssnittet om ett användarnamn och ett lösenord.", "The command asks for a password that is not echoed. After creating the users, the web interface asks for a user name and a password.": "Kommandot begär ett lösenord som inte echoed. Efter att ha skapat användarna ber webbgränssnittet om ett användarnamn och ett lösenord.",
"The command does not name an image": "Kommandot heter inte en bild", "The command does not name an image": "Kommandot heter inte en bild",
@@ -6431,8 +6484,10 @@
"The host bind source does not exist:": "Värdbindningskällan finns inte:", "The host bind source does not exist:": "Värdbindningskällan finns inte:",
"The host bind source is not a regular file or directory:": "Värdbindningskällan är inte en vanlig fil eller katalog:", "The host bind source is not a regular file or directory:": "Värdbindningskällan är inte en vanlig fil eller katalog:",
"The host directory changed before it was mounted": "Värdkatalogen ändrades innan den monterades", "The host directory changed before it was mounted": "Värdkatalogen ändrades innan den monterades",
"The host directory does not exist": "Katalogen på värden finns inte",
"The host directory may not be accessible from an unprivileged container.": "Värdkatalogen kanske inte är tillgänglig från en oprivilegierad behållare.", "The host directory may not be accessible from an unprivileged container.": "Värdkatalogen kanske inte är tillgänglig från en oprivilegierad behållare.",
"The host has no IPv4 address on the selected bridge": "Värden har ingen IPv4-adress på den valda bron", "The host has no IPv4 address on the selected bridge": "Värden har ingen IPv4-adress på den valda bron",
"The host loses access to them. If the group includes a network or storage controller the host needs, cancel at the summary.": "Värden förlorar åtkomsten till dem. Om gruppen innehåller en nätverks- eller lagringskontroller som värden behöver, avbryt i sammanfattningen.",
"The host monitor does not see the real host memory": "Värdskärmen ser inte det verkliga värdminnet", "The host monitor does not see the real host memory": "Värdskärmen ser inte det verkliga värdminnet",
"The host monitor does not share this host namespace:": "Värdskärmen delar inte detta värdnamnsutrymme:", "The host monitor does not share this host namespace:": "Värdskärmen delar inte detta värdnamnsutrymme:",
"The host monitor include is unknown or differs from the saved record": "Värdövervakarens include är okänd eller skiljer sig från den sparade posten", "The host monitor include is unknown or differs from the saved record": "Värdövervakarens include är okänd eller skiljer sig från den sparade posten",
@@ -6622,6 +6677,7 @@
"The repair must preserve the image dependencies:": "Reparationen måste bevara bildberoenden:", "The repair must preserve the image dependencies:": "Reparationen måste bevara bildberoenden:",
"The requested VMID block is already in use": "Det begärda VMID-blocket används redan", "The requested VMID block is already in use": "Det begärda VMID-blocket används redan",
"The requested machine learning GPU profile is not working; it is not replaced by CPU": "Den begärda maskininlärningen GPU-profilen fungerar inte; den ersätts inte av CPU.", "The requested machine learning GPU profile is not working; it is not replaced by CPU": "Den begärda maskininlärningen GPU-profilen fungerar inte; den ersätts inte av CPU.",
"The resource edit could not be fully rolled back. Open Manage again to review and import the current Proxmox settings.": "Det gick inte att helt återställa resursändringen. Öppna hanteringsmenyn igen för att granska och importera de aktuella Proxmox-inställningarna.",
"The restored service did not pass its health check": "Den återställda tjänsten passerade inte hälsokontrollen", "The restored service did not pass its health check": "Den återställda tjänsten passerade inte hälsokontrollen",
"The restored service stopped; the recovery is not confirmed": "Den återställda tjänsten stannade; återhämtningen bekräftas inte", "The restored service stopped; the recovery is not confirmed": "Den återställda tjänsten stannade; återhämtningen bekräftas inte",
"The reviewed Tandoor stack does not require a privileged LXC.": "Den granskade Tandoor stack inte require en privilegierad LXC.", "The reviewed Tandoor stack does not require a privileged LXC.": "Den granskade Tandoor stack inte require en privilegierad LXC.",
@@ -6667,6 +6723,7 @@
"The selected VM": "Den valda virtuella datorn", "The selected VM": "Den valda virtuella datorn",
"The selected VM is running.": "Den valda virtuella datorn körs.", "The selected VM is running.": "Den valda virtuella datorn körs.",
"The selected base folder does not exist and could not be created:": "Den valda basmappen finns inte och kunde inte skapas:", "The selected base folder does not exist and could not be created:": "Den valda basmappen finns inte och kunde inte skapas:",
"The selected bridge does not exist": "Den valda bryggan finns inte",
"The selected bridge has no IPv4 subnet for the host-monitor firewall": "Den valda bryggan har inget IPv4-undernät för värd-monitorns brandvägg", "The selected bridge has no IPv4 subnet for the host-monitor firewall": "Den valda bryggan har inget IPv4-undernät för värd-monitorns brandvägg",
"The selected configuration needs to start the LXC during the installation": "Den valda konfigurationen måste starta LXC under installationen", "The selected configuration needs to start the LXC during the installation": "Den valda konfigurationen måste starta LXC under installationen",
"The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Den valda behållaren är oprivilegierad. En privilegierad behållare krävs för direkt enhetsgenomföring.", "The selected container is unprivileged. A privileged container is required for direct device passthrough.": "Den valda behållaren är oprivilegierad. En privilegierad behållare krävs för direkt enhetsgenomföring.",
@@ -6689,6 +6746,7 @@
"The service builds its own image; only a published image can be installed": "Tjänsten bygger sin egen bild; endast en publicerad bild kan installeras.", "The service builds its own image; only a published image can be installed": "Tjänsten bygger sin egen bild; endast en publicerad bild kan installeras.",
"The service declares no image:": "Tjänsten förklarar ingen bild:", "The service declares no image:": "Tjänsten förklarar ingen bild:",
"The setting has no final value:": "Inställningen har inget slutvärde:", "The setting has no final value:": "Inställningen har inget slutvärde:",
"The settings could not be fully rolled back. Review the container and its saved modification before continuing.": "Det gick inte att återställa alla inställningar. Kontrollera containern och den sparade ändringen innan du fortsätter.",
"The share already exists in smb.conf:": "Delningen finns redan i smb.conf:", "The share already exists in smb.conf:": "Delningen finns redan i smb.conf:",
"The shared destination is not a directory": "Den delade destinationen är inte en katalog", "The shared destination is not a directory": "Den delade destinationen är inte en katalog",
"The shared directory points to a protected host path": "Den delade katalogen pekar på en skyddad värdväg", "The shared directory points to a protected host path": "Den delade katalogen pekar på en skyddad värdväg",
@@ -6756,19 +6814,24 @@
"These Proxmox resources were added outside ProxMenux:": "Dessa Proxmox-resurser lades till utanför ProxMenux:", "These Proxmox resources were added outside ProxMenux:": "Dessa Proxmox-resurser lades till utanför ProxMenux:",
"These VMIDs are not free:": "Dessa VMID är inte gratis:", "These VMIDs are not free:": "Dessa VMID är inte gratis:",
"These are the changes that will be made": "Det är dessa ändringar som kommer att göras", "These are the changes that will be made": "Det är dessa ändringar som kommer att göras",
"These changes were made in Proxmox outside ProxMenux:": "Dessa ändringar gjordes i Proxmox utanför ProxMenux:",
"These container mount paths have an extension-like suffix:": "De här monteringssökvägarna i containern har ett suffix som liknar ett filtillägg:", "These container mount paths have an extension-like suffix:": "De här monteringssökvägarna i containern har ett suffix som liknar ett filtillägg:",
"These containers were restored from a backup and this host has no record of their OCI application:": "Dessa behållare återställdes från en säkerhetskopia och denna värd har ingen post för deras OCI-applikation:", "These containers were restored from a backup and this host has no record of their OCI application:": "Dessa behållare återställdes från en säkerhetskopia och denna värd har ingen post för deras OCI-applikation:",
"These devices share the IOMMU group of the selected GPU and will be assigned to the VM with it:": "Dessa enheter delar IOMMU-gruppen med den valda GPU:n och tilldelas till VM:en tillsammans med den:",
"These host backups run with this job and expect its storage:": "Dessa värdsäkerhetskopior körs med det här jobbet och förväntar sig dess lagring:", "These host backups run with this job and expect its storage:": "Dessa värdsäkerhetskopior körs med det här jobbet och förväntar sig dess lagring:",
"These host backups run with this job and will stop running:": "Dessa värdsäkerhetskopior körs med detta jobb och kommer att sluta köras:", "These host backups run with this job and will stop running:": "Dessa värdsäkerhetskopior körs med detta jobb och kommer att sluta köras:",
"These interface configurations will be removed": "Dessa gränssnittskonfigurationer kommer att tas bort", "These interface configurations will be removed": "Dessa gränssnittskonfigurationer kommer att tas bort",
"These manual changes cannot be adopted safely:": "Dessa manuella ändringar kan inte användas på ett säkert sätt:", "These manual changes cannot be adopted safely:": "Dessa manuella ändringar kan inte användas på ett säkert sätt:",
"These paths will not be restored live and will be extracted for manual recovery.": "Dessa sökvägar kommer inte att återställas live och kommer att extraheras för manuell återställning.", "These paths will not be restored live and will be extracted for manual recovery.": "Dessa sökvägar kommer inte att återställas live och kommer att extraheras för manuell återställning.",
"These settings require an application-specific adaptation. The image was not downloaded and the container was not recreated.": "Dessa inställningar kräver en anpassning för just det här programmet. Avbildningen hämtades inte och containern återskapades inte.",
"These values are asked during the installation:": "Dessa värden tillfrågas under installationen:", "These values are asked during the installation:": "Dessa värden tillfrågas under installationen:",
"They are described in the image documentation:": "De beskrivs i bilddokumentationen:", "They are described in the image documentation:": "De beskrivs i bilddokumentationen:",
"They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "De kommer att läggas till oci-compose innan du fortsätter. Resurser som inte stöds eller ändras importeras inte.", "They will be added to oci-compose before continuing. Unsupported or changed resources are not imported.": "De kommer att läggas till oci-compose innan du fortsätter. Resurser som inte stöds eller ändras importeras inte.",
"They will be saved in oci-compose before continuing.": "De kommer att sparas i oci-compose innan du fortsätter.",
"This CIFS share is mounted with restrictive permissions.": "Denna CIFS-andel är monterad med restriktiva behörigheter.", "This CIFS share is mounted with restrictive permissions.": "Denna CIFS-andel är monterad med restriktiva behörigheter.",
"This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Denna GPU anses vara inkompatibel med GPU-genomföring till en virtuell dator i ProxMenux.", "This GPU is considered incompatible with GPU passthrough to a VM in ProxMenux.": "Denna GPU anses vara inkompatibel med GPU-genomföring till en virtuell dator i ProxMenux.",
"This NFS share is fully restricted — even the host root cannot write to it.": "Denna NFS-resurs är helt begränsad — även värdroten kan inte skriva till den.", "This NFS share is fully restricted — even the host root cannot write to it.": "Denna NFS-resurs är helt begränsad — även värdroten kan inte skriva till den.",
"This Proxmox Backup Server has no active subscription, switch to the no-subscription repository? The inaccessible Enterprise PBS source will be disabled.": "Denna Proxmox Backup Server har ingen aktiv prenumeration, byta till arkivet utan prenumeration? Den otillgängliga Enterprise PBS-källan kommer att inaktiveras.",
"This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Denna virtuella dator kräver extra installationssteg, se installationsguiden på:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144", "This VM requires extra installation steps, see install guide at:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144": "Denna virtuella dator kräver extra installationssteg, se installationsguiden på:\nhttps://github.com/community-scripts/ProxmoxVE/discussions/144",
"This account is for {registry}. Enter an image from that registry.": "Detta konto är för {registry}. Ange en bild från det registret.", "This account is for {registry}. Enter an image from that registry.": "Detta konto är för {registry}. Ange en bild från det registret.",
"This action will:": "Denna åtgärd kommer att:", "This action will:": "Denna åtgärd kommer att:",
@@ -6830,10 +6893,13 @@
"This may take several minutes...": "Detta kan ta flera minuter...", "This may take several minutes...": "Detta kan ta flera minuter...",
"This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Detta innebär att Proxmox hanterar monteringslivscykeln naturligt (ingen manuell /etc/fstab behövs för NFS/CIFS-värdlagringar).", "This means Proxmox handles mount lifecycle natively (no manual /etc/fstab needed for NFS/CIFS host storages).": "Detta innebär att Proxmox hanterar monteringslivscykeln naturligt (ingen manuell /etc/fstab behövs för NFS/CIFS-värdlagringar).",
"This means the credentials are incorrect.": "Det betyder att inloggningsuppgifterna är felaktiga.", "This means the credentials are incorrect.": "Det betyder att inloggningsuppgifterna är felaktiga.",
"This menu requires Proxmox Backup Server.": "Denna meny kräver Proxmox Backup Server.",
"This might indicate network connectivity issues.": "Detta kan tyda på problem med nätverksanslutning.", "This might indicate network connectivity issues.": "Detta kan tyda på problem med nätverksanslutning.",
"This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Denna bildskärm använder en privilegierad LXC, delar processer och nätverk med Proxmox och inaktiverar AppArmor i CT. Den använder IP-adressen och brandväggen i värden. En kompromissad bild kan påverka värden; exponera inte sitt webb-UI på Internet.", "This monitor uses a privileged LXC, shares processes and network with Proxmox and disables AppArmor in the CT. It uses the IP address and firewall of the host. A compromised image could affect the host; do not expose its web UI to the Internet.": "Denna bildskärm använder en privilegierad LXC, delar processer och nätverk med Proxmox och inaktiverar AppArmor i CT. Den använder IP-adressen och brandväggen i värden. En kompromissad bild kan påverka värden; exponera inte sitt webb-UI på Internet.",
"This only removes the schedule. Existing backup archives are NOT deleted.": "Detta tar bara bort schemat. Befintliga säkerhetskopieringsarkiv raderas INTE.", "This only removes the schedule. Existing backup archives are NOT deleted.": "Detta tar bara bort schemat. Befintliga säkerhetskopieringsarkiv raderas INTE.",
"This operation may take several minutes and requires internet connectivity.": "Denna operation kan ta flera minuter och kräver internetanslutning.", "This operation may take several minutes and requires internet connectivity.": "Denna operation kan ta flera minuter och kräver internetanslutning.",
"This operation requires a Proxmox VE host. Use the PBS menu on Proxmox Backup Server.": "Denna åtgärd kräver en Proxmox VE-värd. Använd PBS-menyn på Proxmox Backup Server.",
"This operation supports PBS 3 on Debian 12 and PBS 4 on Debian 13.": "Denna åtgärd stöder PBS 3 på Debian 12 och PBS 4 på Debian 13.",
"This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Det här paketet installerades av äldre versioner av ProxMenux Coral-installationsprogrammet som placerade M.2-kärndrivrutinen på alla system, inklusive inställningar för endast USB.Det behövs inte för Coral USB-enheter, som endast använder libedgetpu1-std / libedgetpu1-max.", "This package was installed by older versions of the ProxMenux Coral installer that placed the M.2 kernel driver on every system, including USB-only setups. It is not needed for Coral USB devices, which use libedgetpu1-std / libedgetpu1-max only.": "Det här paketet installerades av äldre versioner av ProxMenux Coral-installationsprogrammet som placerade M.2-kärndrivrutinen på alla system, inklusive inställningar för endast USB.Det behövs inte för Coral USB-enheter, som endast använder libedgetpu1-std / libedgetpu1-max.",
"This passphrase is the ONLY way to access encrypted Borg backups.": "Denna lösenfras är det ENDA sättet att komma åt krypterade Borg-säkerhetskopior.", "This passphrase is the ONLY way to access encrypted Borg backups.": "Denna lösenfras är det ENDA sättet att komma åt krypterade Borg-säkerhetskopior.",
"This path is already used as a mount point in this container.": "Den här sökvägen används redan som monteringspunkt i den här behållaren.", "This path is already used as a mount point in this container.": "Den här sökvägen används redan som monteringspunkt i den här behållaren.",
@@ -6849,6 +6915,7 @@
"This script cannot be executed from the Proxmox web terminal.": "Detta skript kan inte köras från Proxmox webbterminal.", "This script cannot be executed from the Proxmox web terminal.": "Detta skript kan inte köras från Proxmox webbterminal.",
"This script must be run as root.": "Detta skript måste köras som root.", "This script must be run as root.": "Detta skript måste köras som root.",
"This script must be run on a Proxmox host.": "Detta skript måste köras på en Proxmox-värd.", "This script must be run on a Proxmox host.": "Detta skript måste köras på en Proxmox-värd.",
"This script will apply the following optimizations and advanced adjustments to your Proxmox Backup Server": "Detta skript kommer att tillämpa följande optimeringar och avancerade justeringar på din Proxmox Backup Server",
"This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Detta skript kommer att tillämpa följande optimeringar och avancerade justeringar på din Proxmox VE-server", "This script will apply the following optimizations and advanced adjustments to your Proxmox VE server": "Detta skript kommer att tillämpa följande optimeringar och avancerade justeringar på din Proxmox VE-server",
"This script will update your Proxmox VE system with advanced options:": "Detta skript kommer att uppdatera ditt Proxmox VE-system med avancerade alternativ:", "This script will update your Proxmox VE system with advanced options:": "Detta skript kommer att uppdatera ditt Proxmox VE-system med avancerade alternativ:",
"This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Den här sessionen körs i Monitor-terminalen. Om du kör den härifrån bryts anslutningen mitt under installationen och läget förblir felaktigt.", "This session is running in the Monitor terminal. Running it from here would cut the connection mid-install and leave the switch in a broken state.": "Den här sessionen körs i Monitor-terminalen. Om du kör den härifrån bryts anslutningen mitt under installationen och läget förblir felaktigt.",
@@ -6935,7 +7002,7 @@
"Transaction log:": "Transaktionslogg:", "Transaction log:": "Transaktionslogg:",
"Translation files:": "Översättningsfiler:", "Translation files:": "Översättningsfiler:",
"Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission är utformad för enkel, kraftfull användning. Transmission har de funktioner du vill ha från en BitTorrent-klient: kryptering, ett webbgränssnitt, peer-utbyte, magnetlänkar, DHT, μTP, UPnP och NAT-PMP-port vidarebefordran, webseed support, watch kataloger, tracker redigering, globala och per-torrent hastighet gränser, och mer.", "Transmission is designed for easy, powerful use. Transmission has the features you want from a BitTorrent client: encryption, a web interface, peer exchange, magnet links, DHT, µTP, UPnP and NAT-PMP port forwarding, webseed support, watch directories, tracker editing, global and per-torrent speed limits, and more.": "Transmission är utformad för enkel, kraftfull användning. Transmission har de funktioner du vill ha från en BitTorrent-klient: kryptering, ett webbgränssnitt, peer-utbyte, magnetlänkar, DHT, μTP, UPnP och NAT-PMP-port vidarebefordran, webseed support, watch kataloger, tracker redigering, globala och per-torrent hastighet gränser, och mer.",
"Tried pvesm path and manual detection methods": "Testade pvesm-väg och manuella detekteringsmetoder", "Tried pvesm path and manual detection methods": "Har provat pvesm path och metoder för manuell identifiering",
"Trust this certificate and save it for scheduled backups?": "Lita på det här certifikatet och spara det för schemalagda säkerhetskopieringar?", "Trust this certificate and save it for scheduled backups?": "Lita på det här certifikatet och spara det för schemalagda säkerhetskopieringar?",
"Try Again": "Försök igen", "Try Again": "Försök igen",
"Try a different search term.": "Prova en annan sökterm.", "Try a different search term.": "Prova en annan sökterm.",
@@ -6980,6 +7047,7 @@
"Unable to identify the root filesystem. Aborting.": "Det går inte att identifiera rotfilsystemet. Avbryter.", "Unable to identify the root filesystem. Aborting.": "Det går inte att identifiera rotfilsystemet. Avbryter.",
"Unable to resolve system ZFS pool disks. Aborting.": "Det går inte att lösa system ZFS pooldiskar. Avbryter.", "Unable to resolve system ZFS pool disks. Aborting.": "Det går inte att lösa system ZFS pooldiskar. Avbryter.",
"Unable to resolve system disk topology. Aborting.": "Det gick inte att lösa systemdisktopologin. Avbryter.", "Unable to resolve system disk topology. Aborting.": "Det gick inte att lösa systemdisktopologin. Avbryter.",
"Unapplied changes": "Ändringar som inte har tillämpats",
"Understand the security implications of privileged containers": "Förstå säkerhetskonsekvenserna av privilegierade containrar", "Understand the security implications of privileged containers": "Förstå säkerhetskonsekvenserna av privilegierade containrar",
"Unexpected Proxmox inventory; recovery blocked": "Oväntad Proxmox inventering; återhämtning blockerad", "Unexpected Proxmox inventory; recovery blocked": "Oväntad Proxmox inventering; återhämtning blockerad",
"Unexpected formatting directory in the new Valkey volume": "Oväntad formateringskatalog i den nya Valkey-volymen", "Unexpected formatting directory in the new Valkey volume": "Oväntad formateringskatalog i den nya Valkey-volymen",
@@ -7079,6 +7147,7 @@
"Unsupported OCI-LXC AppArmor profile:": "Ostödd OCI-LXC AppArmor-profil:", "Unsupported OCI-LXC AppArmor profile:": "Ostödd OCI-LXC AppArmor-profil:",
"Unsupported OCI-LXC seccomp profile:": "Unsupported OCI-LXC seccomp profil:", "Unsupported OCI-LXC seccomp profile:": "Unsupported OCI-LXC seccomp profil:",
"Unsupported Terminal": "Terminal som inte stöds", "Unsupported Terminal": "Terminal som inte stöds",
"Unsupported architecture for Figurine:": "Arkitekturen stöds inte av Figurine:",
"Unsupported architecture:": "Ostödd arkitektur:", "Unsupported architecture:": "Ostödd arkitektur:",
"Unsupported backup compression": "Unsupported backup komprimering", "Unsupported backup compression": "Unsupported backup komprimering",
"Unsupported credential pattern:": "Unsupported credential mönster:", "Unsupported credential pattern:": "Unsupported credential mönster:",
@@ -7094,6 +7163,7 @@
"Unsupported mount type": "Unsupported Mount Typ", "Unsupported mount type": "Unsupported Mount Typ",
"Unsupported mount type:": "Unsupported Mount Typ:", "Unsupported mount type:": "Unsupported Mount Typ:",
"Unsupported native device type:": "Inbyggd enhetstyp som inte stöds:", "Unsupported native device type:": "Inbyggd enhetstyp som inte stöds:",
"Unsupported network settings": "Nätverksinställningarna stöds inte",
"Unsupported operation": "Unsupported operation", "Unsupported operation": "Unsupported operation",
"Unsupported or unknown Proxmox version; no repository changed.": "Ostödd eller okänd version av Proxmox; inget arkiv har ändrats.", "Unsupported or unknown Proxmox version; no repository changed.": "Ostödd eller okänd version av Proxmox; inget arkiv har ändrats.",
"Unsupported output format:": "Utdataformat som inte stöds:", "Unsupported output format:": "Utdataformat som inte stöds:",
@@ -7121,6 +7191,7 @@
"Update NVIDIA in LXC Containers": "Uppdatera NVIDIA i LXC-behållare", "Update NVIDIA in LXC Containers": "Uppdatera NVIDIA i LXC-behållare",
"Update OCI": "Uppdatering OCI", "Update OCI": "Uppdatering OCI",
"Update OCI stack": "Uppdatera OCI stack", "Update OCI stack": "Uppdatera OCI stack",
"Update PBS using configured repositories": "Uppdatera PBS med de konfigurerade paketförråden",
"Update PVE enterprise repository (Only if using enterprise):": "Uppdatera Proxmox VE:s företagsarkiv (endast om du använder företagsabonnemang):", "Update PVE enterprise repository (Only if using enterprise):": "Uppdatera Proxmox VE:s företagsarkiv (endast om du använder företagsabonnemang):",
"Update Proxmox VE Appliance Manager": "Uppdatera Proxmox VE Appliance Manager", "Update Proxmox VE Appliance Manager": "Uppdatera Proxmox VE Appliance Manager",
"Update Proxmox package lists": "Uppdatera Proxmox-paketlistor", "Update Proxmox package lists": "Uppdatera Proxmox-paketlistor",
@@ -7173,6 +7244,7 @@
"Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Uppgraderingsassistent: Proxmox VE 8 → 9 (Ttrixie)", "Upgrade assistant: Proxmox VE 8 → 9 (Trixie)": "Uppgraderingsassistent: Proxmox VE 8 → 9 (Ttrixie)",
"Upgrade canceled by user": "Uppgraderingen avbröts av användaren", "Upgrade canceled by user": "Uppgraderingen avbröts av användaren",
"Upgrade one node at a time": "Uppgradera en nod i taget", "Upgrade one node at a time": "Uppgradera en nod i taget",
"Upgrade the system, offering the": "Uppgradera systemet och erbjuder",
"Upgrade to Proxmox VE 9": "Uppgradera till Proxmox VE 9", "Upgrade to Proxmox VE 9": "Uppgradera till Proxmox VE 9",
"Upgrading NVIDIA utils (Arch)...": "Uppgraderar NVIDIA utils (Arch)...", "Upgrading NVIDIA utils (Arch)...": "Uppgraderar NVIDIA utils (Arch)...",
"Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Ladda upp en krypterad kopia av nyckeln till PBS så att du kan återställa den på en ominstallerad värd med bara en lösenfras?", "Upload an encrypted copy of the key to PBS so you can recover it on a reinstalled host with just a passphrase?": "Ladda upp en krypterad kopia av nyckeln till PBS så att du kan återställa den på en ominstallerad värd med bara en lösenfras?",
@@ -7610,6 +7682,7 @@
"active VF(s)": "aktiva VF(s)", "active VF(s)": "aktiva VF(s)",
"active VFs": "aktiva VF:er", "active VFs": "aktiva VF:er",
"active Virtual Functions. Changing its driver binding would destroy every VF.": "aktiva virtuella funktioner. Att ändra dess drivrutinsbindning skulle förstöra varje VF.", "active Virtual Functions. Changing its driver binding would destroy every VF.": "aktiva virtuella funktioner. Att ändra dess drivrutinsbindning skulle förstöra varje VF.",
"added in Proxmox": "lagt till i Proxmox",
"after copy; source tree is incomplete.": "efter kopia; källträdet är ofullständigt.", "after copy; source tree is incomplete.": "efter kopia; källträdet är ofullständigt.",
"after update.": "efter uppdatering.", "after update.": "efter uppdatering.",
"all guests": "alla gäster", "all guests": "alla gäster",
@@ -7954,8 +8027,8 @@
"pvesm command not found. This should not happen on Proxmox.": "pvesm-kommandot hittades inte. Detta bör inte hända på Proxmox.", "pvesm command not found. This should not happen on Proxmox.": "pvesm-kommandot hittades inte. Detta bör inte hända på Proxmox.",
"pvesm not available.": "pvesm inte tillgänglig.", "pvesm not available.": "pvesm inte tillgänglig.",
"pvesm not found.": "pvesm hittades inte.", "pvesm not found.": "pvesm hittades inte.",
"pvesm path failed, trying manual detection...": "pvesm sökväg misslyckades, försöker manuell upptäckt...", "pvesm path failed, trying manual detection...": "pvesm path misslyckades, försöker identifiera manuellt...",
"pvesm status failed": "pvesm-status misslyckades", "pvesm status failed": "pvesm status misslyckades",
"pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad är en gratis och öppen källkod nedladdningshanterare skriven i Python och utformad för att vara extremt lätt, lättföränderlig och fullt hanterbar via webben.", "pyLoad is a Free and Open Source download manager written in Python and designed to be extremely lightweight, easily extensible and fully manageable via web.": "pyLoad är en gratis och öppen källkod nedladdningshanterare skriven i Python och utformad för att vara extremt lätt, lättföränderlig och fullt hanterbar via webben.",
"pyLoad web interface": "PyLoad webbgränssnitt", "pyLoad web interface": "PyLoad webbgränssnitt",
"qBittorrent WebUI password (user: admin)": "qBittorrent WebUI lösenord (användare: admin)", "qBittorrent WebUI password (user: admin)": "qBittorrent WebUI lösenord (användare: admin)",
@@ -7977,6 +8050,7 @@
"remapped users": "ommappade användare", "remapped users": "ommappade användare",
"remove the (now-empty) directory if possible": "ta bort den (nu tomma) katalogen om möjligt", "remove the (now-empty) directory if possible": "ta bort den (nu tomma) katalogen om möjligt",
"removed from Proxmox": "borttagen från Proxmox", "removed from Proxmox": "borttagen från Proxmox",
"removed in Proxmox": "borttagen i Proxmox",
"removed successfully from Proxmox.": "har tagits bort från Proxmox.", "removed successfully from Proxmox.": "har tagits bort från Proxmox.",
"requires running the official installer, which restarts the Monitor service.": "kräver att det officiella installationsprogrammet körs, vilket startar om Monitor-tjänsten.", "requires running the official installer, which restarts the Monitor service.": "kräver att det officiella installationsprogrammet körs, vilket startar om Monitor-tjänsten.",
"requires the package": "kräver paketet", "requires the package": "kräver paketet",
@@ -8033,7 +8107,7 @@
"switch_gpu_mode.sh was not found.": "switch_gpu_mode.sh hittades inte.", "switch_gpu_mode.sh was not found.": "switch_gpu_mode.sh hittades inte.",
"sysfs ROM dump failed — trying ACPI VFCT table...": "sysfs ROM-dump misslyckades — försöker ACPI VFCT-tabell...", "sysfs ROM dump failed — trying ACPI VFCT table...": "sysfs ROM-dump misslyckades — försöker ACPI VFCT-tabell...",
"syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "syslog-ng låter dig flexibelt samla in, analysera, klassificera, skriva och korrelera loggar från hela din infrastruktur och lagra eller dirigera dem för att logga analysverktyg.", "syslog-ng allows you to flexibly collect, parse, classify, rewrite and correlate logs from across your infrastructure and store or route them to log analysis tools.": "syslog-ng låter dig flexibelt samla in, analysera, klassificera, skriva och korrelera loggar från hela din infrastruktur och lagra eller dirigera dem för att logga analysverktyg.",
"systemctl restart networking failed:": "systemctl omstart av nätverk misslyckades:", "systemctl restart networking failed:": "systemctl restart networking misslyckades:",
"systemd OnCalendar expression": "systemd OnCalendar-uttryck", "systemd OnCalendar expression": "systemd OnCalendar-uttryck",
"the API key was not generated on the first start": "API-nyckeln genererades inte i första början", "the API key was not generated on the first start": "API-nyckeln genererades inte i första början",
"the NVIDIA hook of the container is not available:": "NVIDIA-hooken för behållaren är inte tillgänglig:", "the NVIDIA hook of the container is not available:": "NVIDIA-hooken för behållaren är inte tillgänglig:",
@@ -8080,6 +8154,7 @@
"using": "använder", "using": "använder",
"using direct path (not guaranteed to survive reboots).": "använder direkt sökväg (inte garanterat att överleva omstarter).", "using direct path (not guaranteed to survive reboots).": "använder direkt sökväg (inte garanterat att överleva omstarter).",
"vCPUs:": "vCPU:er:", "vCPUs:": "vCPU:er:",
"value changed in Proxmox": "värde ändrat i Proxmox",
"vfio-pci IDs configured": "vfio-pci ID:n konfigurerade", "vfio-pci IDs configured": "vfio-pci ID:n konfigurerade",
"vfio-pci IDs in /etc/modprobe.d/vfio.conf": "vfio-pci ID i /etc/modprobe.d/vfio.conf", "vfio-pci IDs in /etc/modprobe.d/vfio.conf": "vfio-pci ID i /etc/modprobe.d/vfio.conf",
"video": "video", "video": "video",
@@ -8092,6 +8167,7 @@
"was": "var", "was": "var",
"was installed, but the kernel reports:": "installerades, men kärnan rapporterar:", "was installed, but the kernel reports:": "installerades, men kärnan rapporterar:",
"when finished": "När den är färdig", "when finished": "När den är färdig",
"when the server has no subscription (you will be asked)": "när servern inte har något abonnemang (du kommer att bli tillfrågad)",
"will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "kommer att binda om GPU:n till vfio-pci vid nästa omstart, vilket bryter drivrutinen som är på väg att installeras.", "will rebind the GPU to vfio-pci on the next reboot, breaking the driver that is about to be installed.": "kommer att binda om GPU:n till vfio-pci vid nästa omstart, vilket bryter drivrutinen som är på väg att installeras.",
"wipefs failed on": "wipefs misslyckades", "wipefs failed on": "wipefs misslyckades",
"with": "med", "with": "med",
+5 -1
View File
@@ -142,7 +142,7 @@ An application made of several images is one entry of the catalog and one instal
| Operation | What changes | What is kept | | Operation | What changes | What is kept |
|---|---|---| |---|---|---|
| **Update** | The rootfs is replaced by the image the saved tag publishes today | Record, container disks, host directories, network and devices | | **Update** | The rootfs is replaced by the image the saved tag publishes today | Record, container disks, host directories, network and devices |
| **Modify** | An editor opens with the current resources, network, paths and GPU; the container is rebuilt with the changes | The data of container disks and host directories | | **Modify** | Edit and confirm the configuration; supported native settings apply directly, while other changes use the existing recreation flow | Native edits keep the installed image and rootfs; both paths preserve persistent data |
| **Recreate** (multi-container) | Every container is rebuilt from the image it was installed with | Data, network and devices | | **Recreate** (multi-container) | Every container is rebuilt from the image it was installed with | Data, network and devices |
| **Watchdog** | Whether the application is started again when it stops on its own | Everything; only the choice is saved | | **Watchdog** | Whether the application is started again when it stops on its own | Everything; only the choice is saved |
| **Remove** | The LXC, or every container of the application, its disks and its record | Host directories, with their content | | **Remove** | The LXC, or every container of the application, its disks and its record | Host directories, with their content |
@@ -159,6 +159,10 @@ Settings changed in Proxmox VE after the installation, such as memory, cores or
A rolling tag is not an unattended update: the digest changes only through an explicit update. A rolling tag is not an unattended update: the digest changes only through an explicit update.
For standalone applications, CPU and RAM changes apply live. Supported network settings, environment overrides, directory mounts, additional managed volumes and native devices use a controlled stop/start when needed. Modify does not query the registry or download an image for these native edits, even if the cached archive has already been cleaned. Removing a managed mount detaches its disk without deleting its data; subsequent image updates park that unused disk and return it to the container. An optional retained backup does not force recreation.
The native path checks the saved configuration and installation identity, keeps a private before/after journal, and restores the original settings if applying them or checking the restarted service fails. It refuses to overwrite concurrent edits. Changes that require installer adaptations, such as NVIDIA runtime changes, GPU permission/environment setup, file mounts, completion notes or a refreshed catalog template, use the existing validated recreation flow. The route is chosen before applying any settings, so one proposal is not partially applied by both paths. The recreation flow reuses a verified cached archive when it matches the resolved image; it downloads an image when a suitable archive is unavailable. Image changes and coordinated stack operations retain their separate update flow.
## From a Compose file or a docker run command to an oci-compose ## From a Compose file or a docker run command to an oci-compose
The converter reads a Compose file with one service. A `docker run` command and an image reference alone are first written as a Compose file and follow the same path, so the three produce the same kind of oci-compose. The converter reads a Compose file with one service. A `docker run` command and an image reference alone are first written as a Compose file and follow the same path, so the three produce the same kind of oci-compose.
+29 -9
View File
@@ -359,13 +359,18 @@ cleanup_canonical_inputs() {
# A derived check accepts any HTTP answer below 500: the application is up, # A derived check accepts any HTTP answer below 500: the application is up,
# even when its first page is a redirect or asks for a login. # even when its first page is a redirect or asks for a login.
healthcheck_probe() { healthcheck_probe() {
local code status=0
if [[ ${HC_ANY_STATUS:-false} == true ]]; then if [[ ${HC_ANY_STATUS:-false} == true ]]; then
local code code=$(curl "${CURL_ARGS[@]}" "$HC_URL" 2>/dev/null) || status=$?
code=$(curl "${CURL_ARGS[@]}" "$HC_URL" 2>/dev/null) || return 1 (( status == 0 )) && [[ $code =~ ^[1-4][0-9][0-9]$ ]] && return 0
[[ $code =~ ^[1-4][0-9][0-9]$ ]]
else else
curl "${CURL_ARGS[@]}" "$HC_URL" 2>/dev/null curl "${CURL_ARGS[@]}" "$HC_URL" >/dev/null 2>&1 || status=$?
(( status == 0 )) && return 0
fi fi
# A port taken from the compose file may serve a database or SSH: an answer
# in another protocol (HTTP/0.9, weird or empty reply, reset) means it is up.
[[ ${HC_ACCEPT_NON_HTTP:-false} == true ]] \
&& (( status == 1 || status == 8 || status == 52 || status == 56 ))
} }
cleanup_failed_install() { cleanup_failed_install() {
@@ -1025,15 +1030,18 @@ apply_extra_hosts() {
} }
# Settings an update gives back to the rebuilt container before it starts: # Settings an update gives back to the rebuilt container before it starts:
# the LAN leg of a suite or stack member and its start order. # the LAN leg of a suite or stack member, its start order and current tags.
apply_kept_settings() { apply_kept_settings() {
local key value local key value
while IFS=$'\t' read -r key value; do while IFS=$'\t' read -r key value; do
[[ -n $key ]] || continue [[ -n $key ]] || continue
[[ $key == net1 || $key == startup ]] \ [[ $key == net1 || $key == startup || $key == tags ]] \
|| die "$(translate "Unsupported kept setting:") $key" || die "$(translate "Unsupported kept setting:") $key"
oci_quiet pct set "$VMID" "--$key" "$value" \ if [[ $key == tags && -z $value ]]; then
|| die "$(translate "Could not restore the container setting:") $key" oci_quiet pct set "$VMID" --delete tags
else
oci_quiet pct set "$VMID" "--$key" "$value"
fi || die "$(translate "Could not restore the container setting:") $key"
done < <(jq -r '.kept_proxmox_settings // {} | to_entries[] | [.key, .value] | @tsv' "$DEPLOYMENT_FILE") done < <(jq -r '.kept_proxmox_settings // {} | to_entries[] | [.key, .value] | @tsv' "$DEPLOYMENT_FILE")
} }
@@ -1091,7 +1099,7 @@ ensure_runtime_home() {
passwd_file="${rootfs}/etc/passwd" passwd_file="${rootfs}/etc/passwd"
mount_ct_rootfs mount_ct_rootfs
if ! home=$(python3 "$OCI_RUNTIME_RESOLVER" --home "$passwd_file" "$uid" 2>>"${OCI_LOG:-/dev/stderr}"); then if ! home=$(python3 "$OCI_RUNTIME_RESOLVER" --home "$passwd_file" "$uid" 2>>"${OCI_LOG:-/dev/stderr}"); then
oci_quiet pct unmount "$VMID" || true oci_quiet pct unmount "$VMID" || die "$(translate "Could not unmount the container filesystem")"
return 0 return 0
fi fi
oci_quiet pct unmount "$VMID" || die "$(translate "Could not unmount the container filesystem")" oci_quiet pct unmount "$VMID" || die "$(translate "Could not unmount the container filesystem")"
@@ -1230,6 +1238,17 @@ apply_installer_profile() {
fi fi
[[ -d ${rootfs}${target} ]] \ [[ -d ${rootfs}${target} ]] \
|| { oci_log "The volume to prepare does not exist: $target"; failed=1; break; } || { oci_log "The volume to prepare does not exist: $target"; failed=1; break; }
# A reused volume may intentionally be mounted read-only (for example,
# /run/secrets). Do not remove files or change ownership/mode on it.
# Use the most specific containing mount: an RW child can override RO.
if [[ $(jq -r --arg target "$target" '
[.mounts[]? | select(.container_path as $path |
$target == $path or ($target | startswith($path + "/")))]
| sort_by(.container_path | length) | last | .read_only // false' \
"$DEPLOYMENT_FILE") == true ]]; then
oci_log "Skipping preparation of read-only volume: $target"
continue
fi
if [[ $remove_lost_found == true ]]; then if [[ $remove_lost_found == true ]]; then
rm -rf "${rootfs}${target}/lost+found" || { failed=1; break; } rm -rf "${rootfs}${target}/lost+found" || { failed=1; break; }
fi fi
@@ -2253,6 +2272,7 @@ if [[ $START_AFTER == 1 && $HAS_STARTUP_HEALTHCHECK == 1 ]]; then
[[ -n $IP ]] || die "$(translate "The healthcheck cannot run without an IP address")" [[ -n $IP ]] || die "$(translate "The healthcheck cannot run without an IP address")"
HC_URL="${HC_SCHEME}://${IP}:${HC_PORT}${HC_PATH}" HC_URL="${HC_SCHEME}://${IP}:${HC_PORT}${HC_PATH}"
HC_ANY_STATUS=$(jq -r '.accept_any_status // false' <<<"$STARTUP_HEALTHCHECK") HC_ANY_STATUS=$(jq -r '.accept_any_status // false' <<<"$STARTUP_HEALTHCHECK")
HC_ACCEPT_NON_HTTP=$(jq -r '.accept_non_http // false' <<<"$STARTUP_HEALTHCHECK")
if [[ $HC_ANY_STATUS == true ]]; then if [[ $HC_ANY_STATUS == true ]]; then
CURL_ARGS=(-sS --noproxy '*' -o /dev/null -w '%{http_code}' --max-time "$HC_REQUEST_TIMEOUT") CURL_ARGS=(-sS --noproxy '*' -o /dev/null -w '%{http_code}' --max-time "$HC_REQUEST_TIMEOUT")
else else
+47 -4
View File
@@ -38,6 +38,8 @@ DIRECTORY = '.proxmenux'
NAME = 'oci-record.json' NAME = 'oci-record.json'
KIND = 'proxmenux.oci-carried-record' KIND = 'proxmenux.oci-carried-record'
STAMP = 'carried.sha256' STAMP = 'carried.sha256'
CONFIG_CACHE = 'carried-config.json'
LOCAL_CONFIGS = Path('/etc/pve/lxc')
LIMIT = 16 * 1024 * 1024 LIMIT = 16 * 1024 * 1024
STACK_CONTRACT = '/etc/pve/priv/proxmenux-stack-{}.json' STACK_CONTRACT = '/etc/pve/priv/proxmenux-stack-{}.json'
NVIDIA_HOOK = re.compile(r'/usr/local/lib/proxmenux/oci/nvidia-mount-([a-f0-9]{64})\.sh') NVIDIA_HOOK = re.compile(r'/usr/local/lib/proxmenux/oci/nvidia-mount-([a-f0-9]{64})\.sh')
@@ -279,6 +281,47 @@ def read_stamp(path):
return value if isinstance(value, dict) else {} return value if isinstance(value, dict) else {}
def configuration(root, vmid, verify=False):
"""Reuse pct's formatted output only while its local source is unchanged.
Opening Manage checks every carried copy. Starting a fresh pct process
for each unchanged guest dominates that check. The private host cache is
keyed by the complete native configuration, including notes, pending
changes and snapshot sections. Lifecycle verification always calls pct.
An unavailable source, invalid cache or concurrent edit falls back to pct.
"""
source = LOCAL_CONFIGS / f'{int(vmid)}.conf'
cache = instances.location(root, vmid).parent / CONFIG_CACHE
def fingerprint():
try:
return sha(source.read_bytes())
except OSError:
return None
before = fingerprint()
if before is not None and not verify:
try:
if not cache.is_symlink() and cache.stat().st_size <= LIMIT:
value = json.loads(cache.read_text())
if (isinstance(value, dict) and value.get('schema_version') == 1
and value.get('source_sha256') == before
and isinstance(value.get('config'), str) and value['config']
and fingerprint() == before):
return value['config']
except (OSError, ValueError):
pass
result = _run('pct', 'config', str(vmid))
if result.returncode != 0:
return None
# Do not associate output from one revision with another revision's hash.
if before is not None and fingerprint() == before:
with contextlib.suppress(OSError, ValueError):
instances.write(cache, {'schema_version': 1, 'source_sha256': before,
'config': result.stdout})
return result.stdout
def superseded(record, existing, stamp): def superseded(record, existing, stamp):
"""Whether the container carries a copy this host did not write, with a """Whether the container carries a copy this host did not write, with a
record that differs from the one of this host. That is a container that record that differs from the one of this host. That is a container that
@@ -308,10 +351,10 @@ def carry(root, vmid, mount_stopped=True, verify=False, adopted=False):
if record.get('status') != 'installed' or record.get('pending_transaction') \ if record.get('status') != 'installed' or record.get('pending_transaction') \
or record.get('pending_stack_transaction'): or record.get('pending_stack_transaction'):
return 'skipped' return 'skipped'
result = _run('pct', 'config', str(vmid)) config = configuration(root, vmid, verify)
if result.returncode != 0 or instances.identity(result.stdout.encode()) != record['installation_id']: if config is None or instances.identity(config.encode()) != record['installation_id']:
return 'skipped' return 'skipped'
value = bundle(record, result.stdout) value = bundle(record, config)
expected = digest(value) expected = digest(value)
record_path = instances.location(root, vmid) record_path = instances.location(root, vmid)
path = record_path.parent / STAMP path = record_path.parent / STAMP
@@ -340,7 +383,7 @@ def carry(root, vmid, mount_stopped=True, verify=False, adopted=False):
else: else:
value['saved_at'] = datetime.datetime.now(datetime.timezone.utc).isoformat() value['saved_at'] = datetime.datetime.now(datetime.timezone.utc).isoformat()
value['generation'] = uuid.uuid4().hex value['generation'] = uuid.uuid4().hex
write_copy(rootfs, value, mapped_root(result.stdout)) write_copy(rootfs, value, mapped_root(config))
stamp = {'generation': value['generation']} stamp = {'generation': value['generation']}
outcome = 'carried' outcome = 'carried'
if outcome == 'stale': if outcome == 'stale':
+50 -3
View File
@@ -9,6 +9,7 @@ import oci_instances as instances
import oci_gpu_devices as gpu_devices import oci_gpu_devices as gpu_devices
import oci_host_mounts as host_mounts import oci_host_mounts as host_mounts
import oci_instance_transaction as transaction import oci_instance_transaction as transaction
import oci_native_modify as native_modify
from oci_installation_state import parse_config, sha from oci_installation_state import parse_config, sha
from oci_ui import translate from oci_ui import translate
@@ -88,6 +89,43 @@ def _device(key, value):
return device return device
ENVIRONMENT_KEYS = ('env', 'lxc.environment.runtime')
def _environment(record, config):
"""Variables changed in Proxmox since the last operation. Values are
applied but never listed: they may be passwords."""
before = native_modify.environment(record['observed']['config'].encode())
now = native_modify.environment(config)
defaults = {item.split('=', 1)[0] for item in record['observed']['image'].get('defaults', {}).get('Env', [])
if '=' in item}
removed = sorted(before.keys() - now.keys())
# An image default comes back with a new container; its absence cannot be kept.
if any(name in defaults for name in removed):
raise ValueError(f"{translate('These manual changes cannot be adopted safely:')} env")
changed = sorted(name for name in now if before.get(name) != now[name])
return {name: now[name] for name in changed}, removed
def _adopt_environment(deployment, values, removed):
items = deployment.setdefault('environment', [])
details = []
for name, value in values.items():
matches = [item for item in items if item['name'] == name]
if len(matches) > 1:
raise ValueError(translate('Duplicate variable in the contract; review it before editing'))
if matches:
matches[0]['value'] = value
details.append(f"env {name}: {translate('value changed in Proxmox')}")
else:
items.append({'name': name, 'value': value, 'sensitive': True})
details.append(f"env {name}: {translate('added in Proxmox')}")
for name in removed:
items[:] = [item for item in items if item['name'] != name]
details.append(f"env {name}: {translate('removed in Proxmox')}")
return details
def propose(record, config): def propose(record, config):
if (record.get('status') != 'installed' or record.get('stack') or record.get('stack_member') if (record.get('status') != 'installed' or record.get('stack') or record.get('stack_member')
or record.get('native_stack_intent') or record.get('deployment', {}).get('stack_managed')): or record.get('native_stack_intent') or record.get('deployment', {}).get('stack_managed')):
@@ -101,16 +139,20 @@ def propose(record, config):
# was checked above, so a presentation-only change needs no adoption. # was checked above, so a presentation-only change needs no adoption.
if 'description' in changed: if 'description' in changed:
changed.remove('description') changed.remove('description')
environment = None
if any(key in ENVIRONMENT_KEYS for key in changed):
changed = [key for key in changed if key not in ENVIRONMENT_KEYS]
environment = _environment(record, config)
new_keys = [key for key in changed if key not in before and re.fullmatch(r'(mp|dev)[0-9]+', key)] new_keys = [key for key in changed if key not in before and re.fullmatch(r'(mp|dev)[0-9]+', key)]
unsupported = [key for key in changed if key not in new_keys and key not in transaction.ADOPTABLE unsupported = [key for key in changed if key not in new_keys and key not in transaction.ADOPTABLE
and key not in transaction.KEPT_AS_IS] and key not in transaction.KEPT_AS_IS]
if unsupported: if unsupported:
raise ValueError(f"{translate('These manual changes cannot be adopted safely:')} {', '.join(unsupported)}") raise ValueError(f"{translate('These manual changes cannot be adopted safely:')} {', '.join(unsupported)}")
if not new_keys: if not new_keys and environment is None:
return None return None
candidate = copy.deepcopy(record) candidate = copy.deepcopy(record)
deployment = candidate['deployment'] deployment = candidate['deployment']
details = [] details = _adopt_environment(deployment, *environment) if environment else []
for key in new_keys: for key in new_keys:
if key.startswith('mp'): if key.startswith('mp'):
mount = _mount(key, current[key], record['vmid']) mount = _mount(key, current[key], record['vmid'])
@@ -122,8 +164,13 @@ def propose(record, config):
deployment.setdefault('devices', []).append(device) deployment.setdefault('devices', []).append(device)
kind = 'GPU' if gpu_devices.gpu_path(device['host_path']) else 'USB/Coral/NPU' kind = 'GPU' if gpu_devices.gpu_path(device['host_path']) else 'USB/Coral/NPU'
details.append(f"{key}: {device['host_path']} ({kind})") details.append(f"{key}: {device['host_path']} ({kind})")
skipped = [*new_keys, *(ENVIRONMENT_KEYS if environment else ())]
filtered = b'\n'.join(line for line in config.splitlines() filtered = b'\n'.join(line for line in config.splitlines()
if not any(line.startswith(key.encode() + b': ') for key in new_keys)) + b'\n' if not any(line.startswith(key.encode() + b': ') for key in skipped))
if environment:
filtered += b''.join(b'\n' + line for line in record['observed']['config'].encode().splitlines()
if any(line.startswith(key.encode() + b': ') for key in ENVIRONMENT_KEYS))
filtered += b'\n'
for key, value in transaction.external_changes(record, filtered).items(): for key, value in transaction.external_changes(record, filtered).items():
section, name = transaction.ADOPTABLE[key] section, name = transaction.ADOPTABLE[key]
target = deployment.setdefault(section, {}) if section else deployment target = deployment.setdefault(section, {}) if section else deployment
+102 -24
View File
@@ -320,6 +320,36 @@ def mounts(config):
return result return result
def unused_volumes(config):
return {key: value for key, value in parse_config(config).items()
if re.fullmatch(r'unused[0-9]+', key)}
def move_retained_unused(state, to_stage):
"""Unused disks are not in vzdump. Park and return them separately, using
PVE's volume reassignment API, never --delete (which frees an unused disk).
The journal's keys and both ownership markers also make retries safe."""
keys = set(state.get('retained_unused', {}))
if not keys:
return
vmid, stage = state['vmid'], state['stage']
guest_marker = state['record']['installation_id']
holder_marker = 'proxmenux-transaction=' + state['id']
source, target = (vmid, stage) if to_stage else (stage, vmid)
source_marker, target_marker = ((guest_marker, holder_marker) if to_stage
else (holder_marker, guest_marker))
for key in sorted(keys):
present = unused_volumes(owned(source, source_marker))
destination = unused_volumes(owned(target, target_marker))
if key not in present:
if key in destination:
continue # Already reassigned before an interruption.
raise ValueError(translate('There are extra disks or bind mounts outside the journal; the rootfs is not replaced'))
if key in destination or (to_stage and present.keys() - keys):
raise ValueError(translate('There are extra disks or bind mounts outside the journal; the rootfs is not replaced'))
run('pct', 'move-volume', str(source), key, '--target-vmid', str(target), '--target-volume', key)
def is_file_bind(mount, files=()): def is_file_bind(mount, files=()):
return mount['type'] == 'host-bind' and (mount['source'] in files or Path(mount['source']).is_file()) return mount['type'] == 'host-bind' and (mount['source'] in files or Path(mount['source']).is_file())
@@ -352,6 +382,22 @@ def profile_keys(deployment):
return keys return keys
GUESSED_ENDPOINT = 'compose-first-tcp-port-fallback'
# curl exit codes for a port that accepted the connection and answered in a
# protocol other than HTTP: HTTP/0.9 refused, weird reply, empty reply, reset.
NON_HTTP_ANSWER = (1, 8, 52, 56)
def probe_http(args):
"""Run one curl probe and return its output and exit code."""
log('$ ' + shlex.join(str(arg) for arg in args))
try:
result = subprocess.run(args, capture_output=True, timeout=60, close_fds=True)
except subprocess.TimeoutExpired:
return b'', -1
return result.stdout, result.returncode
def effective_healthcheck(template): def effective_healthcheck(template):
"""The template's own health check or, when it has none, one derived from """The template's own health check or, when it has none, one derived from
its web address: any HTTP answer below 500 means the application is up.""" its web address: any HTTP answer below 500 means the application is up."""
@@ -367,9 +413,14 @@ def effective_healthcheck(template):
path = str(endpoint.get('path') or '/') path = str(endpoint.get('path') or '/')
if (endpoint.get('scheme') in ('http', 'https') and str(endpoint.get('port', '')).isdigit() if (endpoint.get('scheme') in ('http', 'https') and str(endpoint.get('port', '')).isdigit()
and path.startswith('/') and not any(c.isspace() for c in path)): and path.startswith('/') and not any(c.isspace() for c in path)):
return {'scheme': endpoint['scheme'], 'port': int(endpoint['port']), 'path': path, check = {'scheme': endpoint['scheme'], 'port': int(endpoint['port']), 'path': path,
'timeout_seconds': 300, 'request_timeout_seconds': 10, 'stability_seconds': 4, 'timeout_seconds': 300, 'request_timeout_seconds': 10, 'stability_seconds': 4,
'verify_tls': False, 'accept_any_status': True} 'verify_tls': False, 'accept_any_status': True}
# A port taken from the compose file because the recipe names no
# web address may serve a database or SSH: any answer counts.
if endpoint.get('source') == GUESSED_ENDPOINT:
check['accept_non_http'] = True
return check
# Without a web address, the new image must at least keep the container running. # Without a web address, the new image must at least keep the container running.
return {'type': 'running', 'timeout_seconds': 90, 'stability_seconds': 20} return {'type': 'running', 'timeout_seconds': 90, 'stability_seconds': 20}
@@ -415,10 +466,9 @@ def candidate_contract(record, operation, proposal=None):
return with_default_healthcheck(result) return with_default_healthcheck(result)
# Resource settings edited in Proxmox that the new container keeps.
# Proxmox settings the rebuilt container gets back exactly as they are: the # Proxmox settings the rebuilt container gets back exactly as they are: the
# LAN leg ProxMenux adds to a suite or stack member, and the start order. # LAN leg ProxMenux adds to a suite or stack member, the start order and tags.
KEPT_AS_IS = ('net1', 'startup') KEPT_AS_IS = ('net1', 'startup', 'tags')
ADOPTABLE = {'memory': ('resources', 'memory_mb'), 'swap': ('resources', 'swap_mb'), ADOPTABLE = {'memory': ('resources', 'memory_mb'), 'swap': ('resources', 'swap_mb'),
'cores': ('resources', 'cores'), 'cpulimit': ('resources', 'cores'), 'cores': ('resources', 'cores'), 'cpulimit': ('resources', 'cores'),
'cpuunits': ('resources', 'cpu_units'), 'onboot': (None, 'onboot')} 'cpuunits': ('resources', 'cpu_units'), 'onboot': (None, 'onboot')}
@@ -453,7 +503,7 @@ def external_changes(record, config, adopt=True):
return values return values
def preflight(record, candidate, config, coordinated=None): def preflight(record, candidate, config, coordinated=None, in_place=False, baseline=True):
deployment = record['deployment'] deployment = record['deployment']
desired = candidate['deployment'] desired = candidate['deployment']
if coordinated and (record.get('native_stack_intent') or deployment.get('rootfs_adaptation_replay_required')): if coordinated and (record.get('native_stack_intent') or deployment.get('rootfs_adaptation_replay_required')):
@@ -462,7 +512,7 @@ def preflight(record, candidate, config, coordinated=None):
raise ValueError(translate('Stack members are updated together with their stack')) raise ValueError(translate('Stack members are updated together with their stack'))
if instances.identity(config) != record['installation_id']: if instances.identity(config) != record['installation_id']:
raise ValueError(translate('The container identity changed; the container is not replaced')) raise ValueError(translate('The container identity changed; the container is not replaced'))
for key, value in external_changes(record, config, adopt=not coordinated).items(): for key, value in (external_changes(record, config, adopt=not coordinated) if baseline else {}).items():
section, name = ADOPTABLE[key] section, name = ADOPTABLE[key]
recorded = deployment.get(section, {}) if section else deployment recorded = deployment.get(section, {}) if section else deployment
target = desired.setdefault(section, {}) if section else desired target = desired.setdefault(section, {}) if section else desired
@@ -491,7 +541,17 @@ def preflight(record, candidate, config, coordinated=None):
runtime_keys.add('lxc.mount.entry') runtime_keys.add('lxc.mount.entry')
runtime_settings.check(config, deployment, record['vmid']) runtime_settings.check(config, deployment, record['vmid'])
coordinated_keys = {'hookscript', *KEPT_AS_IS} if coordinated else set(KEPT_AS_IS) coordinated_keys = {'hookscript', *KEPT_AS_IS} if coordinated else set(KEPT_AS_IS)
if '[' in config.decode() or keys - BASIC - runtime_keys - coordinated_keys - {k for k in keys if re.fullmatch(r'(mp|dev)[0-9]+', k)}: preserved_keys = {'nameserver', 'searchdomain', 'timezone', 'protection'} if in_place else set()
detached = unused_volumes(config)
if baseline and detached != unused_volumes(record['observed'].get('config', '').encode()):
raise ValueError(translate('The container disks do not match the saved record'))
used_volumes = {value.split(',', 1)[0] for key, value in cfg.items()
if key == 'rootfs' or re.fullmatch(r'mp[0-9]+', key)}
if (len(set(detached.values())) != len(detached)
or any(not re.fullmatch(r'[A-Za-z0-9_.-]+:[^\s,]+', value) or value in used_volumes
for value in detached.values())):
raise ValueError(translate('The container disks do not match the saved record'))
if '[' in config.decode() or keys - BASIC - runtime_keys - coordinated_keys - preserved_keys - {k for k in keys if re.fullmatch(r'(mp|dev|unused)[0-9]+', k)}:
raise ValueError(translate('The container has advanced Proxmox settings outside the supported profile')) raise ValueError(translate('The container has advanced Proxmox settings outside the supported profile'))
unprivileged = deployment.get('security', {}).get('unprivileged', True) is not False unprivileged = deployment.get('security', {}).get('unprivileged', True) is not False
if (desired.get('security', {}).get('unprivileged', True) is not False) != unprivileged \ if (desired.get('security', {}).get('unprivileged', True) is not False) != unprivileged \
@@ -511,9 +571,10 @@ def preflight(record, candidate, config, coordinated=None):
target = host_mounts.valid_path(mount['container_path']) target = host_mounts.valid_path(mount['container_path'])
if mount['type'] == 'host-bind' and mount['source'] in declared and mount.get('read_only'): if mount['type'] == 'host-bind' and mount['source'] in declared and mount.get('read_only'):
continue continue
if (any(target == p or target.startswith(p.rstrip('/') + '/') other = next((p for p in paths if target == p or target.startswith(p.rstrip('/') + '/')
or p.startswith(target.rstrip('/') + '/') for p in paths)): or p.startswith(target.rstrip('/') + '/')), None)
raise ValueError(translate('Mount paths must not overlap')) if other:
raise ValueError(f"{translate('Mount paths must not overlap')}: {target} - {other}")
if mount['type'] == 'managed-volume': if mount['type'] == 'managed-volume':
if mount.get('backup') is not True or not isinstance(mount.get('size_gb'), int) or mount['size_gb'] < 1: if mount.get('backup') is not True or not isinstance(mount.get('size_gb'), int) or mount['size_gb'] < 1:
raise ValueError(translate('Managed disks must have backup enabled and a valid size')) raise ValueError(translate('Managed disks must have backup enabled and a valid size'))
@@ -532,7 +593,8 @@ def preflight(record, candidate, config, coordinated=None):
raise ValueError(translate('Changing the rootfs or its storage requires a separate migration')) raise ValueError(translate('Changing the rootfs or its storage requires a separate migration'))
actual = mounts(config) actual = mounts(config)
gpu_devices.check(config, deployment) gpu_devices.check(config, deployment)
gpu_devices.verify_baseline(record['observed'].get('gpu_devices', {}), deployment) if baseline:
gpu_devices.verify_baseline(record['observed'].get('gpu_devices', {}), deployment)
recorded_files = {m['source']: bool(m.get('read_only')) for m in deployment.get('mounts', []) if is_file_bind(m, files)} recorded_files = {m['source']: bool(m.get('read_only')) for m in deployment.get('mounts', []) if is_file_bind(m, files)}
if files != recorded_files: if files != recorded_files:
raise ValueError(translate('A container mount has a source, backup or permission different from the saved record')) raise ValueError(translate('A container mount has a source, backup or permission different from the saved record'))
@@ -547,7 +609,9 @@ def preflight(record, candidate, config, coordinated=None):
or mount.get('ro', '0') != ('1' if old[target].get('read_only') else '0')): or mount.get('ro', '0') != ('1' if old[target].get('read_only') else '0')):
raise ValueError(translate('A container mount has a source, backup or permission different from the saved record')) raise ValueError(translate('A container mount has a source, backup or permission different from the saved record'))
for target in old.keys() & new.keys(): for target in old.keys() & new.keys():
if any(old[target].get(k) != new[target].get(k) for k in ('type', 'source', 'size_gb')): directory_bind = old[target]['type'] == new[target]['type'] == 'host-bind'
fixed = ('type', 'size_gb') if directory_bind else ('type', 'source', 'size_gb')
if any(old[target].get(k) != new[target].get(k) for k in fixed):
raise ValueError(translate('Changing the storage or size of a disk requires a migration; empty disks are not created')) raise ValueError(translate('Changing the storage or size of a disk requires a migration; empty disks are not created'))
protected = ('/bin', '/sbin', '/etc', '/usr', '/lib', '/lib64', '/proc', '/sys', '/dev', '/run') protected = ('/bin', '/sbin', '/etc', '/usr', '/lib', '/lib64', '/proc', '/sys', '/dev', '/run')
for target in new.keys() - old.keys(): for target in new.keys() - old.keys():
@@ -734,11 +798,10 @@ def healthcheck(vmid, template):
args += ['-w', '%{http_code}'] args += ['-w', '%{http_code}']
if not check.get('verify_tls', False): if not check.get('verify_tls', False):
args.append('-k') args.append('-k')
try: answer, status = probe_http([*args, f'{scheme}://{ip}:{port}{path}'])
answer = run(*args, f'{scheme}://{ip}:{port}{path}') ok = status == 0 and (not any_status or re.fullmatch(rb'[1-4][0-9][0-9]', answer.strip()) is not None)
ok = not any_status or re.fullmatch(rb'[1-4][0-9][0-9]', answer.strip()) is not None if not ok and check.get('accept_non_http') and status in NON_HTTP_ANSWER:
except RuntimeError: ok = True
pass
if ok: if ok:
stable_since = stable_since or time.monotonic() stable_since = stable_since or time.monotonic()
if time.monotonic() - stable_since >= stability: if time.monotonic() - stable_since >= stability:
@@ -1041,6 +1104,7 @@ def apply(root, vmid, archive, operation, proposal=None, registry_digest=None, i
for p, m in actual.items() if p in required and not m['volume'].startswith('/')]) for p, m in actual.items() if p in required and not m['volume'].startswith('/')])
state = {'schema_version': 1, 'id': directory.name, 'vmid': vmid, 'operation': operation, state = {'schema_version': 1, 'id': directory.name, 'vmid': vmid, 'operation': operation,
'record': record, 'candidate_contract': candidate, 'before_config': before.decode(), 'record': record, 'candidate_contract': candidate, 'before_config': before.decode(),
'retained_unused': unused_volumes(before),
'original_description': description, 'original_description': description,
'archive': str(archive), 'archive_sha256': filehash(archive), 'archive': str(archive), 'archive_sha256': filehash(archive),
'registry_digest': registry_digest or image['manifest_digest'], 'registry_digest': registry_digest or image['manifest_digest'],
@@ -1051,6 +1115,9 @@ def apply(root, vmid, archive, operation, proposal=None, registry_digest=None, i
'original_gpu_devices': original_gpu, 'desired_gpu_devices': desired_gpu, 'original_gpu_devices': original_gpu, 'desired_gpu_devices': desired_gpu,
'was_running': run('pct', 'status', str(vmid)).strip() == b'status: running'} 'was_running': run('pct', 'status', str(vmid)).strip() == b'status: running'}
state['kept_config'] = {key: cfg[key] for key in KEPT_AS_IS if key in cfg} state['kept_config'] = {key: cfg[key] for key in KEPT_AS_IS if key in cfg}
# The installer adds default tags. Preserve an explicit absence too, so
# deleting all tags in Proxmox does not bring them back on the next update.
state['kept_config']['tags'] = cfg.get('tags', '')
if not coordinated and state['kept_config']: if not coordinated and state['kept_config']:
# Applied by the installer before the new container starts, so the # Applied by the installer before the new container starts, so the
# application has its LAN leg from the first second. # application has its LAN leg from the first second.
@@ -1058,9 +1125,10 @@ def apply(root, vmid, archive, operation, proposal=None, registry_digest=None, i
if coordinated: if coordinated:
state['coordinated'] = coordinated state['coordinated'] = coordinated
state['preserved_stack_config'] = {key: cfg[key] for key in ('net1', 'startup', 'hookscript') if key in cfg} state['preserved_stack_config'] = {key: cfg[key] for key in ('net1', 'startup', 'hookscript') if key in cfg}
state['preserved_stack_config']['tags'] = cfg.get('tags', '')
if operation == 'update': if operation == 'update':
state['preserved_stack_config'].update({key: cfg[key] for key in state['preserved_stack_config'].update({key: cfg[key] for key in
('net0', 'features', 'cmode', 'console', 'tty', 'cpuunits', 'tags') if key in cfg}) ('net0', 'features', 'cmode', 'console', 'tty', 'cpuunits') if key in cfg})
checkpoint(journal, state, 'prepared') checkpoint(journal, state, 'prepared')
pending = copy.deepcopy(record) pending = copy.deepcopy(record)
pending.update(status='updating', pending_transaction=str(journal), transaction_id=state['id']) pending.update(status='updating', pending_transaction=str(journal), transaction_id=state['id'])
@@ -1114,6 +1182,7 @@ def apply(root, vmid, archive, operation, proposal=None, registry_digest=None, i
else: else:
owned(stage, 'proxmenux-transaction=' + state['id']) owned(stage, 'proxmenux-transaction=' + state['id'])
run('pct', 'move-volume', str(vmid), mount['key'], '--target-vmid', str(stage), '--target-volume', mount['key']) run('pct', 'move-volume', str(vmid), mount['key'], '--target-vmid', str(stage), '--target-volume', mount['key'])
move_retained_unused(state, to_stage=True)
checkpoint(journal, state, 'data-parked') checkpoint(journal, state, 'data-parked')
if show: if show:
msg_ok(translate('Data volumes protected')) msg_ok(translate('Data volumes protected'))
@@ -1123,8 +1192,8 @@ def apply(root, vmid, archive, operation, proposal=None, registry_digest=None, i
msg_info(translate('Installing the new image...') if update else translate('Recreating the container...')) msg_info(translate('Installing the new image...') if update else translate('Recreating the container...'))
current = owned(vmid, record['installation_id']) current = owned(vmid, record['installation_id'])
expected = b''.join(line for line in before.splitlines(keepends=True) expected = b''.join(line for line in before.splitlines(keepends=True)
if not re.match(rb'mp[0-9]+: ', line)) if not re.match(rb'(mp|unused)[0-9]+: ', line))
if mounts(current) or current != expected: if mounts(current) or unused_volumes(current) or current != expected:
raise ValueError(translate('A concurrent change was detected; the container is not removed')) raise ValueError(translate('A concurrent change was detected; the container is not removed'))
host_mounts.verify_sources(original_sources) host_mounts.verify_sources(original_sources)
gpu_devices.verify(original_gpu) gpu_devices.verify(original_gpu)
@@ -1139,9 +1208,13 @@ def apply(root, vmid, archive, operation, proposal=None, registry_digest=None, i
install_candidate(root, journal, state, progress) install_candidate(root, journal, state, progress)
restore_description(vmid, state) restore_description(vmid, state)
restore_firewall(vmid, state) restore_firewall(vmid, state)
move_retained_unused(state, to_stage=False)
if coordinated: if coordinated:
for key, value in state['preserved_stack_config'].items(): for key, value in state['preserved_stack_config'].items():
run('pct', 'set', str(vmid), '--' + key, value) if key == 'tags' and not value:
run('pct', 'set', str(vmid), '--delete', 'tags')
else:
run('pct', 'set', str(vmid), '--' + key, value)
check_runtime_mounts(run('pct', 'config', str(vmid)), candidate['deployment']) check_runtime_mounts(run('pct', 'config', str(vmid)), candidate['deployment'])
gpu_devices.check(run('pct', 'config', str(vmid)), candidate['deployment']) gpu_devices.check(run('pct', 'config', str(vmid)), candidate['deployment'])
state['staged_config_sha256'] = sha(owned(vmid, record['installation_id'])) state['staged_config_sha256'] = sha(owned(vmid, record['installation_id']))
@@ -1273,6 +1346,9 @@ def complete_recovery(root, journal, state):
def recover(root, journal): def recover(root, journal):
state = json.loads(journal.read_text()) state = json.loads(journal.read_text())
if state.get('kind') == 'native-modify':
import oci_native_modify
return oci_native_modify.recover(root, journal)
vmid = state['vmid'] vmid = state['vmid']
show = not state.get('coordinated') show = not state.get('coordinated')
if show: if show:
@@ -1335,8 +1411,9 @@ def recover(root, journal):
stop(vmid) stop(vmid)
current = owned(vmid, record['installation_id']) current = owned(vmid, record['installation_id'])
gpu_devices.check_recovery_entries(current, state) gpu_devices.check_recovery_entries(current, state)
if any(re.fullmatch(r'unused[0-9]+', k) for k in parse_config(current)): if unused_volumes(current).keys() - state.get('retained_unused', {}).keys():
raise ValueError(translate('There are extra disks or bind mounts outside the journal; the rootfs is not replaced')) raise ValueError(translate('There are extra disks or bind mounts outside the journal; the rootfs is not replaced'))
move_retained_unused(state, to_stage=True)
known_binds = {(m['source'], m['container_path']) known_binds = {(m['source'], m['container_path'])
for plan in (state['record']['deployment'], state['candidate_contract']['deployment']) for plan in (state['record']['deployment'], state['candidate_contract']['deployment'])
for m in plan.get('mounts', []) if m['type'] == 'host-bind'} for m in plan.get('mounts', []) if m['type'] == 'host-bind'}
@@ -1364,6 +1441,7 @@ def recover(root, journal):
host_mounts.verify_sources(state.get('original_host_sources', {})) host_mounts.verify_sources(state.get('original_host_sources', {}))
run('pct', 'restore', str(vmid), backup, '--force', '1', '--storage', run('pct', 'restore', str(vmid), backup, '--force', '1', '--storage',
state['record']['deployment']['rootfs']['storage']) state['record']['deployment']['rootfs']['storage'])
move_retained_unused(state, to_stage=False)
state['restore_config_sha256'] = sha(owned(vmid, state['record']['installation_id'])) state['restore_config_sha256'] = sha(owned(vmid, state['record']['installation_id']))
checkpoint(journal, state, 'checking-recovery') checkpoint(journal, state, 'checking-recovery')
if show: if show:
+5 -3
View File
@@ -230,8 +230,9 @@ def identity(config):
return match.group(1) if match else None return match.group(1) if match else None
def same_config_except_notes(record, config): def same_config_except_notes(record, config, ignore_tags=False):
"""Accept a presentation-only note edit, never a changed OCI identity or LXC setting.""" """Accept a presentation-only note edit, never a changed OCI identity or LXC setting.
With `ignore_tags`, Proxmox tags may differ too: the operation keeps them as they are."""
previous = record['observed']['config'].encode() previous = record['observed']['config'].encode()
expected = record['installation_id'] expected = record['installation_id']
if identity(previous) != expected or identity(config) != expected: if identity(previous) != expected or identity(config) != expected:
@@ -242,7 +243,8 @@ def same_config_except_notes(record, config):
notes = [line for line in lines if line.startswith(b'description: ')] notes = [line for line in lines if line.startswith(b'description: ')]
if len(notes) != 1: if len(notes) != 1:
return None return None
return b''.join(line for line in lines if not line.startswith(b'description: ')) dropped = (b'description: ', b'tags: ') if ignore_tags else (b'description: ',)
return b''.join(line for line in lines if not line.startswith(dropped))
original = without_notes(previous) original = without_notes(previous)
return original is not None and original == without_notes(config) return original is not None and original == without_notes(config)

Some files were not shown because too many files have changed in this diff Show More