package restore import ( "fmt" "os" "regexp" "strings" "gitea.hsrv.site/mrleo1nid/backup-master/internal/sysx" ) // restoreSystem повторяет системные параметры старого сервера: часовой пояс // (от него зависит время cron-заданий), swap-файл и вручную поставленные пакеты. func (r *restorer) restoreSystem() { step("Системные параметры") r.restoreTimezone() r.restoreSwap() r.restorePackages() } func (r *restorer) restoreTimezone() { tz := r.m.Timezone if tz == "" { return } if cur := sysx.Timezone(r.ctx); cur == tz { info("часовой пояс %s — как на старом сервере", tz) return } if err := sysx.Run(r.ctx, "timedatectl", "set-timezone", tz); err != nil { r.warn("часовой пояс %s не установлен: %v", tz, err) return } info("часовой пояс: %s", tz) } func (r *restorer) restoreSwap() { if len(r.m.Swap) == 0 { return } if len(sysx.SwapFiles()) > 0 { info("swap уже есть — не трогаю") return } if out, _ := sysx.Output(r.ctx, "swapon", "--show", "--noheadings"); out != "" { info("swap уже есть (раздел) — не трогаю") return } for _, sw := range r.m.Swap { if sysx.Exists(sw.Path) { r.warn("swap: %s уже существует, но не подключён — пропускаю", sw.Path) continue } mb := sw.Size / (1 << 20) script := fmt.Sprintf(`set -e fallocate -l %dM %[2]q || dd if=/dev/zero of=%[2]q bs=1M count=%[1]d status=none chmod 600 %[2]q mkswap %[2]q >/dev/null swapon %[2]q grep -qF %[2]q /etc/fstab || echo %[3]q >> /etc/fstab`, mb, sw.Path, sw.Path+" none swap sw 0 0") if err := sysx.Shell(r.ctx, script); err != nil { r.warn("swap %s: %v", sw.Path, err) continue } info("swap %s, %d МБ", sw.Path, mb) } } // Пакеты ядра конкретных версий и метапакеты облачных образов ставить не нужно. var skipPackageRe = regexp.MustCompile(`^linux-(image|headers|modules|tools|cloud-tools|hwe)-\d|^linux-(image|headers|modules)-extra`) func (r *restorer) restorePackages() { if len(r.m.ManualPackages) == 0 { return } installed := map[string]bool{} if out, err := sysx.Output(r.ctx, "dpkg-query", "-W", "-f=${Package} ${db:Status-Status}\n"); err == nil { for _, line := range strings.Split(out, "\n") { if name, st, ok := strings.Cut(line, " "); ok && st == "installed" { installed[name] = true } } } available := map[string]bool{} if out, err := sysx.Output(r.ctx, "apt-cache", "pkgnames"); err == nil { for _, n := range strings.Fields(out) { available[n] = true } } var install, missing []string for _, p := range r.m.ManualPackages { name, _, _ := strings.Cut(p, ":") // pkg:amd64 if installed[name] || skipPackageRe.MatchString(name) { continue } if available[name] { install = append(install, name) } else { missing = append(missing, name) } } if len(install) > 0 { info("доустанавливаю пакеты со старого сервера: %s", strings.Join(install, " ")) _ = os.Setenv("DEBIAN_FRONTEND", "noninteractive") args := append([]string{"install", "-y", "-q", "-o", "Dpkg::Options::=--force-confdef", "-o", "Dpkg::Options::=--force-confold"}, install...) if err := sysx.Run(r.ctx, "apt-get", args...); err != nil { r.warn("часть пакетов не установилась: %v", err) } } if len(missing) > 0 { r.warn("пакетов нет в репозиториях этого сервера (ставились из своих репозиториев или .deb): %s", strings.Join(missing, " ")) } }