from PyQt6.QtCore import QProcess, Qt from PyQt6.QtWidgets import QHBoxLayout, QInputDialog, QLineEdit, QMessageBox, QVBoxLayout, QWidget from raveos_center import muveletek, snapshotok from raveos_center.elemek import Kartya, cimke, gomb from raveos_center.folyamat import FolyamatAblak from raveos_center.hatter import ervenytelenit, gyorsitott, tarolt AZON = "snapshotok" CIM = "Snapshotok" class SnapshotSor(QWidget): def __init__(self, elem, utolso, visszaallit, torol): super().__init__() self.setObjectName("appsor") self.setProperty("utolso", utolso) self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True) sor = QHBoxLayout(self) sor.setContentsMargins(16, 12, 16, 12) sor.setSpacing(16) datum = cimke(elem["datum"], "mono") datum.setFixedWidth(150) sor.addWidget(datum) szovegek = QVBoxLayout() szovegek.setSpacing(2) szovegek.addWidget(cimke(elem["leiras"] or "-", None, True)) szovegek.addWidget(cimke(f"#{elem['szam']} {elem['tipus_nev']}", "monohalvany")) sor.addLayout(szovegek, 1) helper = muveletek.helper_elerheto() vissza = gomb("Visszaállítás", kicsi=True) vissza.setEnabled(helper) vissza.clicked.connect(lambda: visszaallit(elem)) sor.addWidget(vissza, 0, Qt.AlignmentFlag.AlignVCenter) torles = gomb("Törlés", kicsi=True) torles.setEnabled(helper) torles.clicked.connect(lambda: torol(elem)) sor.addWidget(torles, 0, Qt.AlignmentFlag.AlignVCenter) class Snapshotok(QWidget): def __init__(self, kozpont): super().__init__() self.kozpont = kozpont self.adat = None doboz = QVBoxLayout(self) doboz.setContentsMargins(0, 20, 0, 0) doboz.setSpacing(16) fej = QHBoxLayout() fej.setSpacing(16) self.leiras = cimke( "A btrfs snapshot a rendszer egy pillanatnyi állapota. Ha egy frissítés után gond van, egy korábbi " "állapotra vissza tudsz állni. A saját fájljaidat (/home) a visszaállítás nem érinti.", "masodlagos", True, ) fej.addWidget(self.leiras, 1) self.uj_gomb = gomb("Új snapshot", fo=True) self.uj_gomb.clicked.connect(self.uj) fej.addWidget(self.uj_gomb, 0, Qt.AlignmentFlag.AlignTop) doboz.addLayout(fej) self.info = cimke("", "monozold", True) self.info.hide() doboz.addWidget(self.info) self.figyelem = cimke("", "figyelem", True) self.figyelem.hide() doboz.addWidget(self.figyelem) self.beallitas_gomb = gomb("Snapper beállítása", fo=True) self.beallitas_gomb.clicked.connect(self._beallitas) self.beallitas_gomb.hide() gs = QHBoxLayout() gs.addWidget(self.beallitas_gomb) gs.addStretch(1) doboz.addLayout(gs) self.kartya = Kartya(terkoz=0, margo=0) doboz.addWidget(self.kartya) doboz.addStretch(1) def megjelenik(self, *args): if tarolt("snapshotok") is None: self.uj_gomb.setEnabled(False) self._info("Betöltés...") gyorsitott("snapshotok", snapshotok.allapot, kesz=self._kesz, hiba=self._hiba, kor=30) if args and args[0] == "uj": self.uj() def frissit(self): ervenytelenit("snapshotok", "snapshot") gyorsitott("snapshotok", snapshotok.allapot, kesz=self._kesz, hiba=self._hiba) def _hiba(self, uzenet): self._figyelmeztet(f"A snapshotok nem olvashatók: {uzenet}") def _info(self, szoveg): self.info.setText(szoveg) self.info.setVisible(bool(szoveg)) def _figyelmeztet(self, szoveg): self.figyelem.setText(szoveg) self.figyelem.setVisible(bool(szoveg)) def _kesz(self, adat): self.adat = adat helper = muveletek.helper_elerheto() kesz = adat["fajlrendszer"] == "btrfs" and adat["snapper"] self.uj_gomb.setVisible(kesz) self.uj_gomb.setEnabled(kesz and helper) self.beallitas_gomb.setVisible(snapshotok.snapper_telepitheto(adat)) self.beallitas_gomb.setEnabled(helper) self.kartya.setVisible(kesz) if adat["fajlrendszer"] != "btrfs": self._info("") self._figyelmeztet( f"Snapshothoz btrfs fájlrendszer kell. Ez a gép {adat['fajlrendszer'] or 'ismeretlen'} fájlrendszert " "használ, ezért itt nem érhető el. A RaveOS telepítésekor a lemez partícionálásánál ext4 és btrfs " "közül lehet választani: ha snapshotot szeretnél, a btrfs-t válaszd." ) return if not adat["snapper"]: self._info("") if snapshotok.snapper_telepitheto(adat): self._figyelmeztet( "A gép btrfs-t használ, de a snapper még nincs beállítva. Egy kattintással beállítható: " "utána minden csomagművelet előtt automatikusan snapshot készül." ) else: self._figyelmeztet( "A gép btrfs-t használ, de az alvolumen-elrendezése eltér a RaveOS-étól (@ és @snapshots), " "ezért a snapper itt nem állítható be automatikusan." ) return if adat["alvolumen"] != "/@": self._figyelmeztet("A rendszer most nem a fő alvolumenről (@) fut, ezért visszaállítás nem indítható.") elif adat["hiba"]: self._figyelmeztet(adat["hiba"]) elif not helper: self._figyelmeztet("A RaveOS Center rendszerhelpere nincs telepítve, ezért snapshot nem kezelhető.") else: self._figyelmeztet("") self._info( ("Automatikus snapshot minden csomagművelet előtt és után (snap-pac). " if adat["snap_pac"] else "") + f"{len(adat['lista'])} snapshot, a régiek automatikusan törlődnek." ) self._lista(adat) def _lista(self, adat): elrendezes = self.kartya.elrendezes while elrendezes.count(): w = elrendezes.takeAt(0).widget() if w: w.deleteLater() if not adat["lista"]: ures = cimke("Még nincs snapshot.", "halvany") ures.setContentsMargins(16, 16, 16, 16) elrendezes.addWidget(ures) return for i, elem in enumerate(adat["lista"]): elrendezes.addWidget(SnapshotSor(elem, i == len(adat["lista"]) - 1, self.visszaallit, self.torol)) def _futtat(self, cim, lepes, siker): ablak = FolyamatAblak(self, cim, [lepes], siker) ablak.exec() self.frissit() return ablak.rendben def uj(self): if not self.adat or not self.uj_gomb.isEnabled(): return leiras, ok = QInputDialog.getText( self, "Új snapshot", "Leírás (például: Driver csere előtt):", QLineEdit.EchoMode.Normal, "Kézi snapshot" ) leiras = leiras.strip() if not ok: return if not snapshotok.ervenyes_leiras(leiras): QMessageBox.warning(self, "Új snapshot", "A leírás 1-80 karakter lehet: betűk, számok, szóköz és . _ : , ( ) -") return self._futtat("Új snapshot", muveletek.Lepes("Snapshot készítése", muveletek.root("snapshot", leiras)), "A snapshot elkészült.") def torol(self, elem): if QMessageBox.question( self, "Snapshot törlése", f"Törlöd a(z) #{elem['szam']} snapshotot?\n{elem['datum']} {elem['leiras']}" ) != QMessageBox.StandardButton.Yes: return self._futtat( "Snapshot törlése", muveletek.Lepes("Snapshot törlése", muveletek.root("snapshot-torles", str(elem["szam"]))), "A snapshot törölve.", ) def visszaallit(self, elem): kernelek = snapshotok.kernel_valtozas(elem["szam"]) ellenorizheto = kernelek is not None kernelek = kernelek or [] hianyzo = [k for k in kernelek if not k["cache"]] if hianyzo: QMessageBox.warning( self, "Visszaállítás", "Ennek a snapshotnak a kernelcsomagja már nincs meg a csomag-cache-ben " f"({', '.join(k['nev'] + ' ' + k['regi'] for k in hianyzo)}), ezért a visszaállított rendszer nem " "indulna el. Válassz újabb snapshotot.", ) return kernel_szoveg = "".join(f"\n- {k['nev']}: {k['most'] or 'nincs'} -> {k['regi']}" for k in kernelek) szoveg = ( f"A rendszer visszaáll erre az állapotra:\n#{elem['szam']} {elem['datum']} {elem['leiras']}\n\n" "A saját fájljaid (/home) nem változnak. A mostani rendszer a következő visszaállításig megmarad " "tartalékként. A változás újraindítás után lép életbe." + (f"\n\nA kernel is visszaáll:{kernel_szoveg}" if kernelek else "") + ("" if ellenorizheto else "\n\nA snapshot kernelverziója innen nem olvasható, a visszaállítás előtt a rendszer " "ellenőrzi, és ha a kernelcsomag hiányzik, nem végzi el.") + "\n\nFolytatod?" ) if QMessageBox.question(self, "Visszaállítás", szoveg) != QMessageBox.StandardButton.Yes: return rendben = self._futtat( "Visszaállítás", muveletek.Lepes("Snapshot visszaállítása", muveletek.root("snapshot-visszaallit", str(elem["szam"]))), "A visszaállítás elkészült. Indítsd újra a gépet.", ) if not rendben: return doboz = QMessageBox(self) doboz.setWindowTitle("Visszaállítás") doboz.setIcon(QMessageBox.Icon.Warning) doboz.setText( "A visszaállítás újraindítás után lép életbe. Amit addig a gépen változtatsz (telepítés, beállítás, " "rendszerfájlok), az a tartalék rendszerbe kerül, és elvész. A /home-ban lévő fájljaid megmaradnak.\n\n" "Javasolt most újraindítani." ) most = doboz.addButton("Újraindítás most", QMessageBox.ButtonRole.AcceptRole) doboz.addButton("Később", QMessageBox.ButtonRole.RejectRole) doboz.setDefaultButton(most) doboz.exec() if doboz.clickedButton() is most: QProcess.startDetached("systemctl", ["reboot"]) def _beallitas(self): self._futtat( "Snapper beállítása", muveletek.Lepes("Snapper beállítása", muveletek.root("snapper-beallitas")), "A snapper beállítva, az első snapshot elkészült.", ) FUL = Snapshotok