backup/app.go

796 lines
21 KiB
Go
Raw Normal View History

2021-10-16 15:39:54 +02:00
package main
import (
"fmt"
"log"
"regexp"
"time"
)
type AppConfig struct {
Name string `json:"name"`
Schedule []string `json:"schedule"`
Sources []Location `json:"src"`
Destinations []Location `json:"dest"`
Before map[string]Location `json:"before"`
After map[string]Location `json:"after"`
}
2021-11-14 10:20:44 +01:00
func (a AppConfig) getSchedule() (schedule string, err error) {
2021-10-16 15:39:54 +02:00
if *debugFlag {
log.Printf("AppConfig.getSchedule : %s : Start", a.Name)
}
refreshSnapshot := make(map[string]bool)
for _, v := range a.Sources {
if !cfg.Box[v.Box()].online {
return "", nil
}
2021-10-16 15:39:54 +02:00
refreshSnapshot[v.Box()] = true
}
for k, _ := range refreshSnapshot {
2021-11-14 03:53:13 +01:00
err := cfg.Box[k].ZFSUpdateSnapshotList()
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
2021-11-14 03:53:13 +01:00
log.Printf("AppConfig.getSchedule : %s : ZFSUpdateSnapshotList(%s) : %s", a.Name, k, err)
2021-10-16 15:39:54 +02:00
}
return "", err
}
}
2021-11-14 10:20:44 +01:00
var ok bool
2021-10-16 15:39:54 +02:00
if *schedFlag != "" {
schedule = *schedFlag
2021-11-14 10:20:44 +01:00
} else if ok, err = a.needYearlySnapshot(); ok && err == nil {
2021-10-16 15:39:54 +02:00
schedule = "yearly"
2021-11-14 10:20:44 +01:00
} else if ok, err = a.needMonthlySnapshot(); ok && err == nil {
2021-10-16 15:39:54 +02:00
schedule = "monthly"
2021-11-14 10:20:44 +01:00
} else if ok, err = a.needWeeklySnapshot(); ok && err == nil {
2021-10-16 15:39:54 +02:00
schedule = "weekly"
2021-11-14 10:20:44 +01:00
} else if ok, err = a.needDailySnapshot(); ok && err == nil {
2021-10-16 15:39:54 +02:00
schedule = "daily"
2021-11-14 10:20:44 +01:00
} else if ok, err = a.needHourlySnapshot(); ok && err == nil {
2021-10-16 15:39:54 +02:00
schedule = "hourly"
} else {
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
if ret, ok := cfg.Zfsnap[schedule]; !ok {
2021-11-14 10:20:44 +01:00
schedule = ""
err = fmt.Errorf("no retention for %s", schedule)
2021-10-16 15:39:54 +02:00
} else {
re := regexp.MustCompile(`^([0-9]+[ymwdhMs]{1}|forever)$`)
if !re.MatchString(ret) {
2021-11-14 10:20:44 +01:00
schedule = ""
err = fmt.Errorf("wrong retention format for %s", schedule)
2021-10-16 15:39:54 +02:00
}
}
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
2021-11-14 10:20:44 +01:00
func (a AppConfig) needYearlySnapshot() (ret bool, err error) {
2021-10-16 15:39:54 +02:00
if *debugFlag {
log.Printf("AppConfig.needYearlySnapshot : %s : Start", a.Name)
}
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
// schedule enabled for app ?
for _, v := range a.Schedule {
if v == "yearly" {
ret = true
}
}
if !ret {
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
// finding out the timestamps existing
timeSource := make(map[string]map[time.Time]struct{})
timeTotal := make(map[time.Time]struct{})
re := regexp.MustCompile(`^yearly-(?P<Date>[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}.[0-9]{2}.[0-9]{2})--([0-9]+[ymwdhMs]{1}|forever)$`)
for _, src := range a.Sources {
timeSource[string(src)] = make(map[time.Time]struct{})
2021-11-14 10:20:44 +01:00
var snapList []Snapshot
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
if err != nil {
return
}
for _, snap := range snapList {
2021-10-16 15:39:54 +02:00
if src.Path() == snap.Path() {
if re.MatchString(snap.Name()) {
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
2021-11-14 10:20:44 +01:00
var dateTime time.Time
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
2021-11-14 10:20:44 +01:00
log.Printf("AppConfig.needYearlySnapshot : %s : time.ParseInLocation(%s) : %s", a.Name, dateString, err)
2021-10-16 15:39:54 +02:00
}
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
} else {
timeSource[string(src)][dateTime] = struct{}{}
timeTotal[dateTime] = struct{}{}
}
}
}
}
}
// cleaning up the available timestamps for common timestamps
for t, _ := range timeTotal {
for _, v := range timeSource {
if _, ok := v[t]; !ok {
delete(timeTotal, t)
}
}
}
// finding an eligible timestamp
for t, _ := range timeTotal {
2021-11-14 05:21:22 +01:00
if t.Year() == cfg.Now.Year() {
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
}
}
// no timestamp => need the snapshot !
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
2021-11-14 10:20:44 +01:00
func (a AppConfig) needMonthlySnapshot() (ret bool, err error) {
2021-10-16 15:39:54 +02:00
if *debugFlag {
log.Printf("AppConfig.needMonthlySnapshot : %s : Start", a.Name)
}
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
// schedule enabled for app ?
for _, v := range a.Schedule {
if v == "monthly" {
ret = true
}
}
if !ret {
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
// finding out the timestamps existing
timeSource := make(map[string]map[time.Time]struct{})
timeTotal := make(map[time.Time]struct{})
re := regexp.MustCompile(`^(yearly|monthly)-(?P<Date>[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}.[0-9]{2}.[0-9]{2})--([0-9]+[ymwdhMs]{1}|forever)$`)
for _, src := range a.Sources {
timeSource[string(src)] = make(map[time.Time]struct{})
2021-11-14 10:20:44 +01:00
var snapList []Snapshot
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
if err != nil {
return
}
for _, snap := range snapList {
2021-10-16 15:39:54 +02:00
if src.Path() == snap.Path() {
if re.MatchString(snap.Name()) {
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
2021-11-14 10:20:44 +01:00
var dateTime time.Time
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
}
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
} else {
timeSource[string(src)][dateTime] = struct{}{}
timeTotal[dateTime] = struct{}{}
}
}
}
}
}
// cleaning up the available timestamps for common timestamps
for t, _ := range timeTotal {
for _, v := range timeSource {
if _, ok := v[t]; !ok {
delete(timeTotal, t)
}
}
}
// finding an eligible timestamp
for t, _ := range timeTotal {
2021-11-14 05:21:22 +01:00
if t.Year() == cfg.Now.Year() && t.Month() == cfg.Now.Month() {
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
}
}
// no timestamp => need the snapshot !
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
2021-11-14 10:20:44 +01:00
func (a AppConfig) needWeeklySnapshot() (ret bool, err error) {
2021-10-16 15:39:54 +02:00
if *debugFlag {
log.Printf("AppConfig.needWeeklySnapshot : %s : Start", a.Name)
}
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
// schedule enabled for app ?
for _, v := range a.Schedule {
if v == "weekly" {
ret = true
}
}
if !ret {
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
// finding out the timestamps existing
timeSource := make(map[string]map[time.Time]struct{})
timeTotal := make(map[time.Time]struct{})
re := regexp.MustCompile(`^(yearly|monthly|weekly)-(?P<Date>[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}.[0-9]{2}.[0-9]{2})--([0-9]+[ymwdhMs]{1}|forever)$`)
for _, src := range a.Sources {
timeSource[string(src)] = make(map[time.Time]struct{})
2021-11-14 10:20:44 +01:00
var snapList []Snapshot
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
if err != nil {
return
}
for _, snap := range snapList {
2021-10-16 15:39:54 +02:00
if src.Path() == snap.Path() {
if re.MatchString(snap.Name()) {
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
2021-11-14 10:20:44 +01:00
var dateTime time.Time
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
}
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
} else {
timeSource[string(src)][dateTime] = struct{}{}
timeTotal[dateTime] = struct{}{}
}
}
}
}
}
// cleaning up the available timestamps for common timestamps
for t, _ := range timeTotal {
for _, v := range timeSource {
if _, ok := v[t]; !ok {
delete(timeTotal, t)
}
}
}
// finding an eligible timestamp
2021-11-14 05:21:22 +01:00
nowYear, nowWeek := cfg.Now.ISOWeek()
2021-10-16 15:39:54 +02:00
for t, _ := range timeTotal {
snapYear, snapWeek := t.ISOWeek()
if nowYear == snapYear && nowWeek == snapWeek {
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
}
}
// no timestamp => need the snapshot !
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
2021-11-14 10:20:44 +01:00
func (a AppConfig) needDailySnapshot() (ret bool, err error) {
2021-10-16 15:39:54 +02:00
if *debugFlag {
log.Printf("AppConfig.needDailySnapshot : %s : Start", a.Name)
}
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
// schedule enabled for app ?
for _, v := range a.Schedule {
if v == "daily" {
ret = true
}
}
if !ret {
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
// finding out the timestamps existing
timeSource := make(map[string]map[time.Time]struct{})
timeTotal := make(map[time.Time]struct{})
re := regexp.MustCompile(`^(yearly|monthly|weekly|daily)-(?P<Date>[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}.[0-9]{2}.[0-9]{2})--([0-9]+[ymwdhMs]{1}|forever)$`)
for _, src := range a.Sources {
timeSource[string(src)] = make(map[time.Time]struct{})
2021-11-14 10:20:44 +01:00
var snapList []Snapshot
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
if err != nil {
return
}
for _, snap := range snapList {
2021-10-16 15:39:54 +02:00
if src.Path() == snap.Path() {
if re.MatchString(snap.Name()) {
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
2021-11-14 10:20:44 +01:00
var dateTime time.Time
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
}
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
} else {
timeSource[string(src)][dateTime] = struct{}{}
timeTotal[dateTime] = struct{}{}
}
}
}
}
}
// cleaning up the available timestamps for common timestamps
for t, _ := range timeTotal {
for _, v := range timeSource {
if _, ok := v[t]; !ok {
delete(timeTotal, t)
}
}
}
// finding an eligible timestamp
for t, _ := range timeTotal {
2021-11-14 05:21:22 +01:00
if t.Year() == cfg.Now.Year() && t.Month() == cfg.Now.Month() && t.Day() == cfg.Now.Day() {
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
}
}
// no timestamp => need the snapshot !
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
2021-11-14 10:20:44 +01:00
func (a AppConfig) needHourlySnapshot() (ret bool, err error) {
2021-10-16 15:39:54 +02:00
if *debugFlag {
log.Printf("AppConfig.needHourlySnapshot : %s : Start", a.Name)
}
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
// schedule enabled for app ?
for _, v := range a.Schedule {
if v == "hourly" {
ret = true
}
}
if !ret {
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
// finding out the timestamps existing
timeSource := make(map[string]map[time.Time]struct{})
timeTotal := make(map[time.Time]struct{})
re := regexp.MustCompile(`^(yearly|monthly|weekly|daily|hourly)-(?P<Date>[0-9]{4}-[0-9]{2}-[0-9]{2}_[0-9]{2}.[0-9]{2}.[0-9]{2})--([0-9]+[ymwdhMs]{1}|forever)$`)
for _, src := range a.Sources {
timeSource[string(src)] = make(map[time.Time]struct{})
2021-11-14 10:20:44 +01:00
var snapList []Snapshot
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
if err != nil {
return
}
for _, snap := range snapList {
2021-10-16 15:39:54 +02:00
if src.Path() == snap.Path() {
if re.MatchString(snap.Name()) {
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
2021-11-14 10:20:44 +01:00
var dateTime time.Time
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
}
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
} else {
timeSource[string(src)][dateTime] = struct{}{}
timeTotal[dateTime] = struct{}{}
}
}
}
}
}
// cleaning up the available timestamps for common timestamps
for t, _ := range timeTotal {
for _, v := range timeSource {
if _, ok := v[t]; !ok {
delete(timeTotal, t)
}
}
}
// finding an eligible timestamp
for t, _ := range timeTotal {
2021-11-14 05:21:22 +01:00
if t.Year() == cfg.Now.Year() && t.Month() == cfg.Now.Month() && t.Day() == cfg.Now.Day() && t.Hour() == cfg.Now.Hour() {
2021-11-14 10:20:44 +01:00
ret = false
2021-10-16 15:39:54 +02:00
}
}
// no timestamp => need the snapshot !
2021-11-14 10:20:44 +01:00
return
2021-10-16 15:39:54 +02:00
}
2021-10-18 16:13:04 +02:00
func (a AppConfig) CheckZFS() error {
2021-10-16 15:39:54 +02:00
if *debugFlag {
2021-10-18 16:13:04 +02:00
log.Printf("AppConfig.CheckZFS : %s : Start", a.Name)
2021-10-16 15:39:54 +02:00
}
for _, src := range a.Sources {
2021-11-14 03:53:13 +01:00
if !cfg.Box[src.Box()].ZFSIsZFS(src.Path()) {
2021-10-16 15:39:54 +02:00
return fmt.Errorf("No path %s on source", string(src))
}
for _, dest := range a.Destinations {
2022-04-16 15:30:47 +02:00
if cfg.Box[dest.Box()].online {
if !cfg.Box[dest.Box()].ZFSIsZFS(dest.Path() + "/" + src.Box() + "/" + src.Path()) {
err := cfg.Box[dest.Box()].ZFSCreateZFS(dest.Path() + "/" + src.Box() + "/" + src.Path())
if err != nil {
if *debugFlag {
log.Printf("AppConfig.CheckZFS : %s : Error creating %s on %s", a.Name, dest.Path()+"/"+src.Box()+"/"+src.Path(), dest.Box())
}
return err
2021-10-16 15:39:54 +02:00
}
}
}
}
}
2021-10-18 16:13:04 +02:00
return nil
}
func (a AppConfig) ExecBefore(schedule string) error {
if *debugFlag {
log.Printf("AppConfig.ExecBefore : %s : Start %s", a.Name, schedule)
}
2021-10-16 15:39:54 +02:00
for k, v := range a.Before {
re := regexp.MustCompile(k)
if re.MatchString(schedule) {
2021-11-14 08:58:47 +01:00
_, err := cfg.Box[v.Box()].SSHExec(v.Path())
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
2021-10-18 16:13:04 +02:00
log.Printf("AppConfig.ExecBefore : %s : Error executing %s", a.Name, string(v))
2021-10-16 15:39:54 +02:00
}
return err
}
}
}
2021-10-18 16:13:04 +02:00
return nil
}
func (a AppConfig) ExecAfter(schedule string) error {
if *debugFlag {
log.Printf("AppConfig.ExecAfter : %s : Start %s", a.Name, schedule)
}
for k, v := range a.After {
re := regexp.MustCompile(k)
if re.MatchString(schedule) {
2021-11-14 08:58:47 +01:00
_, err := cfg.Box[v.Box()].SSHExec(v.Path())
2021-10-18 16:13:04 +02:00
if err != nil {
if *debugFlag {
log.Printf("AppConfig.ExecAfter : %s : Error executing %s on %s", a.Name, v.Path(), v.Box())
}
return err
}
}
}
return nil
}
func (a AppConfig) TakeSnapshot(schedule string) error {
if *debugFlag {
log.Printf("AppConfig.TakeSnapshot : %s : Start %s", a.Name, schedule)
}
2021-10-16 15:39:54 +02:00
for _, v := range a.Sources {
2021-11-14 05:21:22 +01:00
err := cfg.Box[v.Box()].ZFSTakeSnapshot(schedule, v.Path())
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
2021-11-14 05:21:22 +01:00
log.Printf("AppConfig.TakeSnapshot : %s : ZFSTakeSnapshot", a.Name)
2021-10-16 15:39:54 +02:00
}
return err
}
}
2021-10-18 16:13:04 +02:00
return nil
}
func (a AppConfig) RefreshSnapshot() error {
if *debugFlag {
log.Printf("AppConfig.RefreshSnapshot : %s : Start", a.Name)
}
refreshSnapshot := make(map[string]struct{})
for _, v := range a.Sources {
2022-06-17 16:07:37 +02:00
if cfg.Box[v.Box()].online {
refreshSnapshot[v.Box()] = struct{}{}
}
2021-10-18 16:13:04 +02:00
}
2021-10-16 15:39:54 +02:00
for _, v := range a.Destinations {
2022-04-16 15:11:05 +02:00
if cfg.Box[v.Box()].online {
refreshSnapshot[v.Box()] = struct{}{}
}
2021-10-16 15:39:54 +02:00
}
for k, _ := range refreshSnapshot {
if *debugFlag {
2021-10-18 16:13:04 +02:00
log.Printf("AppConfig.RefreshSnapshot : %s : refreshing snapshots for source %s", a.Name, k)
2021-10-16 15:39:54 +02:00
}
2021-11-14 03:53:13 +01:00
err := cfg.Box[k].ZFSUpdateSnapshotList()
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
2021-10-18 16:13:04 +02:00
log.Printf("AppConfig.RefreshSnapshot : %s : Error getting snapshots on %s", a.Name, k)
2021-10-16 15:39:54 +02:00
}
return err
}
}
2021-10-18 16:13:04 +02:00
return nil
}
func (a AppConfig) CreatePath() (err error) {
if *debugFlag {
log.Printf("AppConfig.CreatePath : %s : Start", a.Name)
}
for _, src := range a.Sources {
for _, dest := range a.Destinations {
if cfg.Box[dest.Box()].online {
if *debugFlag {
log.Printf("AppConfig.CreatePath : %s : Checking path on %s", a.Name, string(dest))
}
if !cfg.Box[dest.Box()].ZFSIsZFS(dest.Path() + "/" + src.Box() + "/" + src.Path()) {
if *debugFlag {
log.Printf("AppConfig.CreatePath : %s : Creating on %s path %s", a.Name, dest.Box(), dest.Path()+"/"+src.Box()+"/"+src.Path())
}
if err = cfg.Box[dest.Box()].ZFSCreateZFS(dest.Path() + "/" + src.Box() + "/" + src.Path()); err != nil {
if *debugFlag {
log.Printf("AppConfig.CreatePath : %s : Creating on %s path %s failed (%s)", a.Name, dest.Box(), dest.Path()+"/"+src.Box()+"/"+src.Path(), err)
}
return
}
}
}
}
}
err = nil
return
}
2021-11-14 10:20:44 +01:00
func (a AppConfig) SendSnapshots() (err error) {
2021-10-18 16:13:04 +02:00
if *debugFlag {
log.Printf("AppConfig.SendSnapshots : %s : Start", a.Name)
}
2021-10-16 15:39:54 +02:00
for _, src := range a.Sources {
2022-06-17 16:11:20 +02:00
if cfg.Box[src.Box()].online {
for _, dest := range a.Destinations {
if cfg.Box[dest.Box()].online {
2021-10-16 15:39:54 +02:00
if *debugFlag {
2022-06-17 16:11:20 +02:00
log.Printf("AppConfig.SendSnapshots : %s : Sending snapshots from %s to %s", a.Name, string(src), string(dest))
2021-10-16 15:39:54 +02:00
}
2021-11-14 10:20:44 +01:00
2022-06-17 16:11:20 +02:00
var dLastSnapshot Snapshot
dLastSnapshot, err = cfg.Box[dest.Box()].ZFSGetLastSnapshot(dest.Path() + "/" + src.Box() + "/" + src.Path())
if err != nil && err.Error() == "no snapshot" {
2022-04-16 09:16:23 +02:00
if *debugFlag {
2022-06-17 16:11:20 +02:00
log.Printf("AppConfig.SendSnapshots : %s : No snapshot for %s on %s", a.Name, string(src), dest.Box())
2022-04-16 09:16:23 +02:00
}
2022-06-17 16:11:20 +02:00
var sFirstSnapshot Snapshot
sFirstSnapshot, err = cfg.Box[src.Box()].ZFSGetFirstSnapshot(src.Path())
if err != nil {
if *debugFlag {
log.Printf("AppConfig.SendSnapshots : %s : No snapshot for %s", a.Name, string(src))
}
return
2021-10-16 15:39:54 +02:00
}
2022-04-16 14:57:45 +02:00
if *debugFlag {
2022-06-17 16:11:20 +02:00
log.Printf("AppConfig.SendSnapshots : %s : Initializing snapshot on %s from %s", a.Name, dest.Box(), string(sFirstSnapshot))
2022-04-16 14:57:45 +02:00
}
2022-09-07 16:21:03 +02:00
_, err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + cfg.Box[src.Box()].Host() + " zfs send " + string(sFirstSnapshot) + " | zfs recv -F " + dest.Path() + "/" + src.Box() + "/" + src.Path())
2022-04-16 14:57:45 +02:00
if err != nil {
if *debugFlag {
2022-06-17 16:11:20 +02:00
log.Printf("AppConfig.SendSnapshots : %s : Initializing snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sFirstSnapshot), err)
2022-04-16 14:57:45 +02:00
}
return
}
2022-06-17 16:11:20 +02:00
var (
sCurrSnapshot, sNextSnapshot Snapshot
isLastSnapshot bool
)
sNextSnapshot = sFirstSnapshot
isLastSnapshot, _ = cfg.Box[src.Box()].ZFSIsLastSnapshot(sNextSnapshot)
if !isLastSnapshot {
sCurrSnapshot = sNextSnapshot
sNextSnapshot, err = cfg.Box[src.Box()].ZFSGetLastSnapshot(sNextSnapshot.Path())
if *debugFlag {
log.Printf("AppConfig.SendSnapshots : %s : Sending incrementally %s to %s", a.Name, string(sNextSnapshot), dest.Box())
}
if err != nil && err.Error() != "no snapshot" {
return
}
2022-09-07 16:21:03 +02:00
_, err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + cfg.Box[src.Box()].Host() + " zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | zfs recv -F " + dest.Path() + "/" + src.Box() + "/" + src.Path())
2022-06-17 16:11:20 +02:00
if err != nil {
if *debugFlag {
log.Printf("AppConfig.SendSnapshots : %s : Sending snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sNextSnapshot), err)
}
return
}
}
2021-10-16 15:39:54 +02:00
if *debugFlag {
2022-06-17 16:11:20 +02:00
log.Printf("AppConfig.SendSnapshots : %s : All snapshots sent for %s", a.Name, string(src))
2021-10-16 15:39:54 +02:00
}
2022-06-17 16:11:20 +02:00
} else {
if *debugFlag {
log.Printf("AppConfig.SendSnapshots : %s : Last snapshot on %s is %s", a.Name, dest.Box(), string(dLastSnapshot))
2022-04-16 14:57:45 +02:00
}
2022-06-17 16:11:20 +02:00
var (
sCurrSnapshot, sNextSnapshot Snapshot
isLastSnapshot bool
)
sNextSnapshot = Snapshot(string(dLastSnapshot)[len(dest.Path())+len(src.Box())+2:])
isLastSnapshot, _ = cfg.Box[src.Box()].ZFSIsLastSnapshot(sNextSnapshot)
if !isLastSnapshot {
sCurrSnapshot = sNextSnapshot
sNextSnapshot, err = cfg.Box[src.Box()].ZFSGetLastSnapshot(sNextSnapshot.Path())
2022-04-16 14:57:45 +02:00
if *debugFlag {
2022-06-17 16:11:20 +02:00
log.Printf("AppConfig.SendSnapshots : %s : Sending incrementally %s to %s", a.Name, string(sNextSnapshot), dest.Box())
}
if err != nil && err.Error() != "no snapshot" {
return
}
2022-09-07 16:21:03 +02:00
_, err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + cfg.Box[src.Box()].Host() + " zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | zfs recv -F " + dest.Path() + "/" + src.Box() + "/" + src.Path())
2022-06-17 16:11:20 +02:00
if err != nil {
if *debugFlag {
log.Printf("AppConfig.SendSnapshots : %s : Sending snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sNextSnapshot), err)
}
return
2022-04-16 14:57:45 +02:00
}
}
2021-10-16 15:39:54 +02:00
}
}
}
}
}
2021-11-14 10:20:44 +01:00
err = nil
return
2021-10-18 16:13:04 +02:00
}
func (a AppConfig) CleanupSnapshot() error {
if *debugFlag {
2021-10-18 16:20:44 +02:00
log.Printf("AppConfig.CleanupSnapshot : %s : Start", a.Name)
2021-10-18 16:13:04 +02:00
}
cleanupSnapshot := make(map[string]string)
for _, dest := range a.Destinations {
2022-04-16 15:30:47 +02:00
if cfg.Box[dest.Box()].online {
for _, src := range a.Sources {
2022-06-17 16:03:12 +02:00
if cfg.Box[src.Box()].online {
cleanupSnapshot[src.Box()] = cleanupSnapshot[src.Box()] + " " + src.Path()
cleanupSnapshot[dest.Box()] = cleanupSnapshot[dest.Box()] + " " + dest.Path() + "/" + src.Box() + "/" + src.Path()
}
2022-04-16 15:30:47 +02:00
}
2021-10-18 16:13:04 +02:00
}
}
for k, v := range cleanupSnapshot {
2021-10-16 15:39:54 +02:00
if *debugFlag {
2022-06-17 14:54:14 +02:00
log.Printf("AppConfig.CleanupSnapshot : %s : cleaning snapshots on %s for %s", a.Name, k, v)
2021-10-16 15:39:54 +02:00
}
2021-11-14 08:58:47 +01:00
_, err := cfg.Box[k].SSHExec("zfsnap destroy -p hourly- -p daily- -p weekly- -p monthly- -p yearly-" + v)
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
2021-10-18 16:13:04 +02:00
log.Printf("AppConfig.CleanupSnapshot : %s : Error executing zfsnap on %s", a.Name, k)
2021-10-16 15:39:54 +02:00
}
return err
}
}
2021-10-18 16:13:04 +02:00
return nil
}
func (a AppConfig) RunAppBackup() error {
if *debugFlag {
log.Printf("AppConfig.RunAppBackup : %s : Start", a.Name)
}
schedule, err := a.getSchedule()
if err != nil {
2021-10-16 15:39:54 +02:00
if *debugFlag {
2021-10-18 16:13:04 +02:00
log.Printf("AppConfig.RunAppBackup : %s : Error getting schedule : %s", a.Name, err)
2021-10-16 15:39:54 +02:00
}
2021-10-18 16:13:04 +02:00
return err
}
2021-11-04 14:33:44 +01:00
if schedule != "" || *slowFlag {
2021-10-18 16:13:04 +02:00
err = a.CheckZFS()
2021-10-16 15:39:54 +02:00
if err != nil {
if *debugFlag {
2021-10-18 16:13:04 +02:00
log.Printf("AppConfig.RunAppBackup : %s : CheckZFS : %s", a.Name, err)
}
return err
}
2021-11-04 14:33:44 +01:00
}
2021-10-18 16:13:04 +02:00
2022-06-17 14:54:14 +02:00
err = a.CleanupSnapshot()
if err != nil {
if *debugFlag {
log.Printf("AppConfig.RunAppBackup : %s : CleanupSnapshot : %s", a.Name, err)
}
return err
}
2021-11-04 14:33:44 +01:00
if schedule != "" {
2021-10-18 16:13:04 +02:00
err = a.ExecBefore(schedule)
if err != nil {
if *debugFlag {
log.Printf("AppConfig.RunAppBackup : %s : ExecBefore : %s", a.Name, err)
2021-10-16 15:39:54 +02:00
}
return err
}
2021-10-19 02:45:51 +02:00
err = a.TakeSnapshot(schedule)
if err != nil {
if *debugFlag {
log.Printf("AppConfig.RunAppBackup : %s : TakeSnapshot : %s", a.Name, err)
}
return err
}
2021-10-16 15:39:54 +02:00
}
2021-10-18 16:13:04 +02:00
err = a.RefreshSnapshot()
if err != nil {
if *debugFlag {
log.Printf("AppConfig.RunAppBackup : %s : RefreshSnapshot : %s", a.Name, err)
}
return err
}
err = a.CreatePath()
if err != nil {
if *debugFlag {
log.Printf("AppConfig.RunAppBackup : %s : CreatePath : %s", a.Name, err)
}
return err
}
2021-10-18 16:13:04 +02:00
err = a.SendSnapshots()
if err != nil {
if *debugFlag {
log.Printf("AppConfig.RunAppBackup : %s : SendSnapshots : %s", a.Name, err)
}
return err
}
if schedule != "" {
err = a.ExecAfter(schedule)
if err != nil {
if *debugFlag {
log.Printf("AppConfig.RunAppBackup : %s : ExecAfter : %s", a.Name, err)
2021-10-16 15:39:54 +02:00
}
2021-10-18 16:13:04 +02:00
return err
2021-10-16 15:39:54 +02:00
}
}
2021-10-18 16:13:04 +02:00
2021-10-16 15:39:54 +02:00
return nil
}