89 lines
3.0 KiB
Python
89 lines
3.0 KiB
Python
"""Namensschema der von pvesnap verwalteten Snapshots.
|
|
|
|
Ein Name sieht so aus: auto-taeglich-20260730-023000
|
|
^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^
|
|
| | Datum Uhrzeit
|
|
| Kurzname der Gruppe
|
|
Praefix aus [global]
|
|
|
|
Nur Snapshots, die exakt auf dieses Muster passen, werden von pvesnap
|
|
angefasst. Von Hand angelegte Snapshots bleiben damit garantiert unberuehrt.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import re
|
|
from datetime import datetime
|
|
|
|
# Proxmox erlaubt fuer Snapshot-Namen nur begrenzte Laenge - wir bleiben
|
|
# bewusst deutlich darunter.
|
|
MAX_NAME_LEN = 40
|
|
_STAMP_LEN = len("-20260730-023000")
|
|
|
|
|
|
def max_slug_len(prefix):
|
|
return max(1, MAX_NAME_LEN - len(prefix) - _STAMP_LEN)
|
|
|
|
|
|
def effective_slug(prefix, slug):
|
|
"""Der Kurzname, wie er tatsaechlich im Snapshot-Namen landet."""
|
|
return slug[: max_slug_len(prefix)]
|
|
|
|
|
|
def build_name(prefix, slug, moment):
|
|
return "%s-%s-%s" % (prefix, effective_slug(prefix, slug),
|
|
moment.strftime("%Y%m%d-%H%M%S"))
|
|
|
|
|
|
def name_pattern(prefix, slug=None):
|
|
part = re.escape(effective_slug(prefix, slug)) if slug else r"[A-Za-z0-9]+"
|
|
return re.compile(r"^%s-(%s)-(\d{8})-(\d{6})$" % (re.escape(prefix), part))
|
|
|
|
|
|
def parse_name(prefix, name):
|
|
"""Zerlegt einen Snapshot-Namen; gibt None zurueck, wenn er nicht zu uns gehoert."""
|
|
match = name_pattern(prefix).match(str(name))
|
|
if not match:
|
|
return None
|
|
slug, date_part, time_part = match.groups()
|
|
try:
|
|
created = datetime.strptime(date_part + time_part, "%Y%m%d%H%M%S")
|
|
except ValueError:
|
|
return None
|
|
return {"slug": slug, "created": created}
|
|
|
|
|
|
def render_description(template, group, guest, moment, prefix):
|
|
"""Setzt die Platzhalter der Beschreibungs-Vorlage ein."""
|
|
from .util import format_duration # lokal, um Zyklen zu vermeiden
|
|
|
|
values = {
|
|
"prefix": prefix,
|
|
"group": group.name,
|
|
"group_slug": group.slug,
|
|
"vmid": guest.vmid,
|
|
"name": guest.name or "",
|
|
"type": "LXC" if guest.type == "lxc" else "VM",
|
|
"node": guest.node or "",
|
|
"pool": guest.pool or "",
|
|
"tags": ",".join(guest.tags),
|
|
"date": moment.strftime("%Y-%m-%d"),
|
|
"time": moment.strftime("%H:%M:%S"),
|
|
"datetime": moment.strftime("%Y-%m-%d %H:%M:%S"),
|
|
"timestamp": int(moment.timestamp()),
|
|
"keep_time": format_duration(group.keep_time),
|
|
"keep_count": group.keep_count if group.keep_count else "unbegrenzt",
|
|
"schedule": _schedule_text(group),
|
|
}
|
|
try:
|
|
text = str(template).format(**values)
|
|
except (KeyError, IndexError, ValueError):
|
|
# Unbekannter Platzhalter: lieber eine brauchbare Beschreibung als ein Abbruch.
|
|
text = "pvesnap | Gruppe: %s | erstellt: %s" % (group.name, values["datetime"])
|
|
return text.strip()[:1024]
|
|
|
|
|
|
def _schedule_text(group):
|
|
from .schedule import describe
|
|
return describe(group)
|