update snapshot processing
This commit is contained in:
parent
e192906265
commit
0d3981ef4a
200
app.go
200
app.go
@ -16,8 +16,7 @@ type AppConfig struct {
|
||||
After map[string]Location `json:"after"`
|
||||
}
|
||||
|
||||
func (a AppConfig) getSchedule() (string, error) {
|
||||
var schedule string
|
||||
func (a AppConfig) getSchedule() (schedule string, err error) {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.getSchedule : %s : Start", a.Name)
|
||||
}
|
||||
@ -36,38 +35,42 @@ func (a AppConfig) getSchedule() (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
var ok bool
|
||||
if *schedFlag != "" {
|
||||
schedule = *schedFlag
|
||||
} else if a.needYearlySnapshot() {
|
||||
} else if ok, err = a.needYearlySnapshot(); ok && err == nil {
|
||||
schedule = "yearly"
|
||||
} else if a.needMonthlySnapshot() {
|
||||
} else if ok, err = a.needMonthlySnapshot(); ok && err == nil {
|
||||
schedule = "monthly"
|
||||
} else if a.needWeeklySnapshot() {
|
||||
} else if ok, err = a.needWeeklySnapshot(); ok && err == nil {
|
||||
schedule = "weekly"
|
||||
} else if a.needDailySnapshot() {
|
||||
} else if ok, err = a.needDailySnapshot(); ok && err == nil {
|
||||
schedule = "daily"
|
||||
} else if a.needHourlySnapshot() {
|
||||
} else if ok, err = a.needHourlySnapshot(); ok && err == nil {
|
||||
schedule = "hourly"
|
||||
} else {
|
||||
return schedule, nil
|
||||
return
|
||||
}
|
||||
|
||||
if ret, ok := cfg.Zfsnap[schedule]; !ok {
|
||||
return "", fmt.Errorf("no retention for %s", schedule)
|
||||
schedule = ""
|
||||
err = fmt.Errorf("no retention for %s", schedule)
|
||||
} else {
|
||||
re := regexp.MustCompile(`^([0-9]+[ymwdhMs]{1}|forever)$`)
|
||||
if !re.MatchString(ret) {
|
||||
return "", fmt.Errorf("wrong retention format for %s", schedule)
|
||||
schedule = ""
|
||||
err = fmt.Errorf("wrong retention format for %s", schedule)
|
||||
}
|
||||
}
|
||||
return schedule, nil
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppConfig) needYearlySnapshot() bool {
|
||||
func (a AppConfig) needYearlySnapshot() (ret bool, err error) {
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needYearlySnapshot : %s : Start", a.Name)
|
||||
}
|
||||
ret := false
|
||||
ret = false
|
||||
|
||||
// schedule enabled for app ?
|
||||
for _, v := range a.Schedule {
|
||||
@ -76,7 +79,7 @@ func (a AppConfig) needYearlySnapshot() bool {
|
||||
}
|
||||
}
|
||||
if !ret {
|
||||
return false
|
||||
return
|
||||
}
|
||||
|
||||
// finding out the timestamps existing
|
||||
@ -85,15 +88,25 @@ func (a AppConfig) needYearlySnapshot() bool {
|
||||
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{})
|
||||
for _, snap := range cfg.Box[src.Box()].ZFSGetSnapshotList() {
|
||||
|
||||
var snapList []Snapshot
|
||||
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, snap := range snapList {
|
||||
if src.Path() == snap.Path() {
|
||||
if re.MatchString(snap.Name()) {
|
||||
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
|
||||
dateTime, err := time.Parse("2006-01-02_15.04.05", dateString)
|
||||
|
||||
var dateTime time.Time
|
||||
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
|
||||
log.Printf("AppConfig.needYearlySnapshot : %s : time.ParseInLocation(%s) : %s", a.Name, dateString, err)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
timeSource[string(src)][dateTime] = struct{}{}
|
||||
timeTotal[dateTime] = struct{}{}
|
||||
@ -115,20 +128,20 @@ func (a AppConfig) needYearlySnapshot() bool {
|
||||
// finding an eligible timestamp
|
||||
for t, _ := range timeTotal {
|
||||
if t.Year() == cfg.Now.Year() {
|
||||
return false
|
||||
ret = false
|
||||
}
|
||||
}
|
||||
|
||||
// no timestamp => need the snapshot !
|
||||
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppConfig) needMonthlySnapshot() bool {
|
||||
func (a AppConfig) needMonthlySnapshot() (ret bool, err error) {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needMonthlySnapshot : %s : Start", a.Name)
|
||||
}
|
||||
ret := false
|
||||
ret = false
|
||||
|
||||
// schedule enabled for app ?
|
||||
for _, v := range a.Schedule {
|
||||
@ -137,7 +150,7 @@ func (a AppConfig) needMonthlySnapshot() bool {
|
||||
}
|
||||
}
|
||||
if !ret {
|
||||
return false
|
||||
return
|
||||
}
|
||||
|
||||
// finding out the timestamps existing
|
||||
@ -146,15 +159,25 @@ func (a AppConfig) needMonthlySnapshot() bool {
|
||||
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{})
|
||||
for _, snap := range cfg.Box[src.Box()].ZFSGetSnapshotList() {
|
||||
|
||||
var snapList []Snapshot
|
||||
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, snap := range snapList {
|
||||
if src.Path() == snap.Path() {
|
||||
if re.MatchString(snap.Name()) {
|
||||
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
|
||||
dateTime, err := time.Parse("2006-01-02_15.04.05", dateString)
|
||||
|
||||
var dateTime time.Time
|
||||
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
timeSource[string(src)][dateTime] = struct{}{}
|
||||
timeTotal[dateTime] = struct{}{}
|
||||
@ -176,20 +199,20 @@ func (a AppConfig) needMonthlySnapshot() bool {
|
||||
// finding an eligible timestamp
|
||||
for t, _ := range timeTotal {
|
||||
if t.Year() == cfg.Now.Year() && t.Month() == cfg.Now.Month() {
|
||||
return false
|
||||
ret = false
|
||||
}
|
||||
}
|
||||
|
||||
// no timestamp => need the snapshot !
|
||||
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppConfig) needWeeklySnapshot() bool {
|
||||
func (a AppConfig) needWeeklySnapshot() (ret bool, err error) {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needWeeklySnapshot : %s : Start", a.Name)
|
||||
}
|
||||
ret := false
|
||||
ret = false
|
||||
|
||||
// schedule enabled for app ?
|
||||
for _, v := range a.Schedule {
|
||||
@ -198,7 +221,7 @@ func (a AppConfig) needWeeklySnapshot() bool {
|
||||
}
|
||||
}
|
||||
if !ret {
|
||||
return false
|
||||
return
|
||||
}
|
||||
|
||||
// finding out the timestamps existing
|
||||
@ -207,15 +230,25 @@ func (a AppConfig) needWeeklySnapshot() bool {
|
||||
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{})
|
||||
for _, snap := range cfg.Box[src.Box()].ZFSGetSnapshotList() {
|
||||
|
||||
var snapList []Snapshot
|
||||
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, snap := range snapList {
|
||||
if src.Path() == snap.Path() {
|
||||
if re.MatchString(snap.Name()) {
|
||||
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
|
||||
dateTime, err := time.Parse("2006-01-02_15.04.05", dateString)
|
||||
|
||||
var dateTime time.Time
|
||||
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
timeSource[string(src)][dateTime] = struct{}{}
|
||||
timeTotal[dateTime] = struct{}{}
|
||||
@ -239,20 +272,20 @@ func (a AppConfig) needWeeklySnapshot() bool {
|
||||
for t, _ := range timeTotal {
|
||||
snapYear, snapWeek := t.ISOWeek()
|
||||
if nowYear == snapYear && nowWeek == snapWeek {
|
||||
return false
|
||||
ret = false
|
||||
}
|
||||
}
|
||||
|
||||
// no timestamp => need the snapshot !
|
||||
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppConfig) needDailySnapshot() bool {
|
||||
func (a AppConfig) needDailySnapshot() (ret bool, err error) {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needDailySnapshot : %s : Start", a.Name)
|
||||
}
|
||||
ret := false
|
||||
ret = false
|
||||
|
||||
// schedule enabled for app ?
|
||||
for _, v := range a.Schedule {
|
||||
@ -261,7 +294,7 @@ func (a AppConfig) needDailySnapshot() bool {
|
||||
}
|
||||
}
|
||||
if !ret {
|
||||
return false
|
||||
return
|
||||
}
|
||||
|
||||
// finding out the timestamps existing
|
||||
@ -270,15 +303,25 @@ func (a AppConfig) needDailySnapshot() bool {
|
||||
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{})
|
||||
for _, snap := range cfg.Box[src.Box()].ZFSGetSnapshotList() {
|
||||
|
||||
var snapList []Snapshot
|
||||
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, snap := range snapList {
|
||||
if src.Path() == snap.Path() {
|
||||
if re.MatchString(snap.Name()) {
|
||||
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
|
||||
dateTime, err := time.Parse("2006-01-02_15.04.05", dateString)
|
||||
|
||||
var dateTime time.Time
|
||||
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
timeSource[string(src)][dateTime] = struct{}{}
|
||||
timeTotal[dateTime] = struct{}{}
|
||||
@ -300,20 +343,20 @@ func (a AppConfig) needDailySnapshot() bool {
|
||||
// finding an eligible timestamp
|
||||
for t, _ := range timeTotal {
|
||||
if t.Year() == cfg.Now.Year() && t.Month() == cfg.Now.Month() && t.Day() == cfg.Now.Day() {
|
||||
return false
|
||||
ret = false
|
||||
}
|
||||
}
|
||||
|
||||
// no timestamp => need the snapshot !
|
||||
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppConfig) needHourlySnapshot() bool {
|
||||
func (a AppConfig) needHourlySnapshot() (ret bool, err error) {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needHourlySnapshot : %s : Start", a.Name)
|
||||
}
|
||||
ret := false
|
||||
ret = false
|
||||
|
||||
// schedule enabled for app ?
|
||||
for _, v := range a.Schedule {
|
||||
@ -322,7 +365,7 @@ func (a AppConfig) needHourlySnapshot() bool {
|
||||
}
|
||||
}
|
||||
if !ret {
|
||||
return false
|
||||
return
|
||||
}
|
||||
|
||||
// finding out the timestamps existing
|
||||
@ -331,15 +374,25 @@ func (a AppConfig) needHourlySnapshot() bool {
|
||||
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{})
|
||||
for _, snap := range cfg.Box[src.Box()].ZFSGetSnapshotList() {
|
||||
|
||||
var snapList []Snapshot
|
||||
snapList, err = cfg.Box[src.Box()].ZFSGetSnapshotList()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, snap := range snapList {
|
||||
if src.Path() == snap.Path() {
|
||||
if re.MatchString(snap.Name()) {
|
||||
dateString := re.ReplaceAllString(snap.Name(), "${Date}")
|
||||
dateTime, err := time.Parse("2006-01-02_15.04.05", dateString)
|
||||
|
||||
var dateTime time.Time
|
||||
dateTime, err = time.ParseInLocation("2006-01-02_15.04.05", dateString, time.Now().Location())
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
|
||||
}
|
||||
return
|
||||
} else {
|
||||
timeSource[string(src)][dateTime] = struct{}{}
|
||||
timeTotal[dateTime] = struct{}{}
|
||||
@ -361,13 +414,13 @@ func (a AppConfig) needHourlySnapshot() bool {
|
||||
// finding an eligible timestamp
|
||||
for t, _ := range timeTotal {
|
||||
if t.Year() == cfg.Now.Year() && t.Month() == cfg.Now.Month() && t.Day() == cfg.Now.Day() && t.Hour() == cfg.Now.Hour() {
|
||||
return false
|
||||
ret = false
|
||||
}
|
||||
}
|
||||
|
||||
// no timestamp => need the snapshot !
|
||||
|
||||
return true
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppConfig) CheckZFS() error {
|
||||
@ -480,7 +533,7 @@ func (a AppConfig) RefreshSnapshot() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a AppConfig) SendSnapshots() error {
|
||||
func (a AppConfig) SendSnapshots() (err error) {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.SendSnapshots : %s : Start", a.Name)
|
||||
}
|
||||
@ -490,18 +543,23 @@ func (a AppConfig) SendSnapshots() error {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.SendSnapshots : %s : Sending snapshots from %s to %s", a.Name, string(src), string(dest))
|
||||
}
|
||||
dLastSnapshot, err := cfg.Box[dest.Box()].ZFSGetLastSnapshot(dest.Path() + "/" + src.Box() + "/" + src.Path())
|
||||
if err != nil {
|
||||
|
||||
var dLastSnapshot Snapshot
|
||||
dLastSnapshot, err = cfg.Box[dest.Box()].ZFSGetLastSnapshot(dest.Path() + "/" + src.Box() + "/" + src.Path())
|
||||
if err != nil && err.Error() == "no snapshot" {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.SendSnapshots : %s : No snapshot for %s on %s", a.Name, string(src), dest.Box())
|
||||
}
|
||||
sFirstSnapshot, err := cfg.Box[src.Box()].ZFSGetFirstSnapshot(src.Path())
|
||||
|
||||
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 err
|
||||
return
|
||||
}
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.SendSnapshots : %s : Initializing snapshot on %s from %s", a.Name, dest.Box(), string(sFirstSnapshot))
|
||||
}
|
||||
@ -510,23 +568,32 @@ func (a AppConfig) SendSnapshots() error {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.SendSnapshots : %s : Initializing snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sFirstSnapshot), err)
|
||||
}
|
||||
return err
|
||||
return
|
||||
}
|
||||
var sCurrSnapshot Snapshot
|
||||
sNextSnapshot := sFirstSnapshot
|
||||
for !cfg.Box[src.Box()].ZFSIsLastSnapshot(sNextSnapshot) {
|
||||
|
||||
var (
|
||||
sCurrSnapshot, sNextSnapshot Snapshot
|
||||
isLastSnapshot bool
|
||||
)
|
||||
sNextSnapshot = sFirstSnapshot
|
||||
isLastSnapshot, _ = cfg.Box[src.Box()].ZFSIsLastSnapshot(sNextSnapshot)
|
||||
for !isLastSnapshot {
|
||||
sCurrSnapshot = sNextSnapshot
|
||||
sNextSnapshot, err = cfg.Box[src.Box()].ZFSGetNextSnapshot(sNextSnapshot)
|
||||
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
|
||||
}
|
||||
_, err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + src.Box() + " zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path())
|
||||
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 err
|
||||
return
|
||||
}
|
||||
isLastSnapshot, _ = cfg.Box[src.Box()].ZFSIsLastSnapshot(sNextSnapshot)
|
||||
}
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.SendSnapshots : %s : All snapshots sent for %s", a.Name, string(src))
|
||||
@ -536,26 +603,35 @@ func (a AppConfig) SendSnapshots() error {
|
||||
if *debugFlag {
|
||||
log.Printf("AppConfig.SendSnapshots : %s : Last snapshot on %s is %s", a.Name, dest.Box(), string(dLastSnapshot))
|
||||
}
|
||||
var sCurrSnapshot Snapshot
|
||||
sNextSnapshot := Snapshot(string(dLastSnapshot)[len(dest.Path())+len(src.Box())+2:])
|
||||
for !cfg.Box[src.Box()].ZFSIsLastSnapshot(sNextSnapshot) {
|
||||
var (
|
||||
sCurrSnapshot, sNextSnapshot Snapshot
|
||||
isLastSnapshot bool
|
||||
)
|
||||
sNextSnapshot = Snapshot(string(dLastSnapshot)[len(dest.Path())+len(src.Box())+2:])
|
||||
isLastSnapshot, _ = cfg.Box[src.Box()].ZFSIsLastSnapshot(sNextSnapshot)
|
||||
for !isLastSnapshot {
|
||||
sCurrSnapshot = sNextSnapshot
|
||||
sNextSnapshot, err = cfg.Box[src.Box()].ZFSGetNextSnapshot(sNextSnapshot)
|
||||
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
|
||||
}
|
||||
_, err = cfg.Box[dest.Box()].SSHExec("ssh " + cfg.Box[src.Box()].User + "@" + src.Box() + " zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path())
|
||||
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 err
|
||||
return
|
||||
}
|
||||
isLastSnapshot, _ = cfg.Box[src.Box()].ZFSIsLastSnapshot(sNextSnapshot)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
err = nil
|
||||
return
|
||||
}
|
||||
|
||||
func (a AppConfig) CleanupSnapshot() error {
|
||||
|
157
box.go
157
box.go
@ -17,28 +17,165 @@ type Box struct {
|
||||
zfs *ZFSConfig
|
||||
}
|
||||
|
||||
func (b *Box) ZFSGetLastSnapshot(path string) (s Snapshot, err error) {
|
||||
return b.ssh.getLastSnapshot(path)
|
||||
func (b *Box) ZFSGetLastSnapshot(path string) (last Snapshot, err error) {
|
||||
err = b.SnapshotInitialize()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("Box.ZFSGetLastSnapshot : %s : Start %s (%d snapshots)", b.Name, path, len(b.zfs.SnapshotList))
|
||||
}
|
||||
|
||||
for _, v := range b.zfs.SnapshotList {
|
||||
if v.Path() == path {
|
||||
last = v
|
||||
}
|
||||
}
|
||||
if len(string(last)) == 0 {
|
||||
err = fmt.Errorf("no snapshot")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Box) ZFSIsLastSnapshot(s Snapshot) bool {
|
||||
return b.ssh.isLastSnapshot(s)
|
||||
func (b *Box) ZFSIsLastSnapshot(src Snapshot) (is bool, err error) {
|
||||
err = b.SnapshotInitialize()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.isLastSnapshot : %s : Start %s", b.Name, string(src))
|
||||
}
|
||||
|
||||
_, err = b.ZFSGetNextSnapshot(src)
|
||||
if err != nil {
|
||||
if err.Error() == "no snapshot" {
|
||||
is = true
|
||||
err = nil
|
||||
}
|
||||
} else {
|
||||
is = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Box) ZFSGetFirstSnapshot(path string) (s Snapshot, err error) {
|
||||
return b.ssh.getFirstSnapshot(path)
|
||||
func (b *Box) ZFSGetFirstSnapshot(path string) (first Snapshot, err error) {
|
||||
err = b.SnapshotInitialize()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getFirstSnapshot : Start %s:%s", b.Name, path)
|
||||
}
|
||||
|
||||
for _, v := range b.zfs.SnapshotList {
|
||||
if v.Path() == path {
|
||||
first = v
|
||||
return
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("no snapshot")
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Box) ZFSGetNextSnapshot(src Snapshot) (next Snapshot, err error) {
|
||||
return b.ssh.getNextSnapshot(src)
|
||||
err = b.SnapshotInitialize()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("Box.ZFSGetNextSnapshot : Start %s:%s", b.Name, string(src))
|
||||
}
|
||||
|
||||
for id, v := range b.zfs.SnapshotList {
|
||||
if v == src {
|
||||
if len(b.zfs.SnapshotList) > id+1 {
|
||||
next = b.zfs.SnapshotList[id+1]
|
||||
if next.Path() == src.Path() {
|
||||
return
|
||||
} else {
|
||||
err = fmt.Errorf("no snapshot")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("no snapshot")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
err = fmt.Errorf("no snapshot")
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Box) ZFSUpdateSnapshotList() (err error) {
|
||||
return b.ssh.getSnapshotList()
|
||||
b.zfs.M.Lock()
|
||||
if b.zfs.SnapshotDeleted || b.zfs.SnapshotAdded {
|
||||
b.zfs.SnapshotInitialized = false
|
||||
}
|
||||
b.zfs.M.Unlock()
|
||||
|
||||
err = b.SnapshotInitialize()
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Box) ZFSGetSnapshotList() []Snapshot {
|
||||
return b.ssh.snapshot
|
||||
func (b *Box) ZFSGetSnapshotList() (snaps []Snapshot, err error) {
|
||||
err = b.SnapshotInitialize()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
b.zfs.M.Lock()
|
||||
defer b.zfs.M.Unlock()
|
||||
|
||||
snaps = b.zfs.SnapshotList
|
||||
return
|
||||
}
|
||||
|
||||
func (b *Box) SnapshotInitialize() (err error) {
|
||||
b.zfs.M.Lock()
|
||||
defer b.zfs.M.Unlock()
|
||||
|
||||
if b.zfs.SnapshotInitialized {
|
||||
return nil
|
||||
}
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("Box.SnapshotInitialize : %s : Start", b.Name)
|
||||
}
|
||||
|
||||
b.zfs.SnapshotList = make([]Snapshot, 0)
|
||||
|
||||
var buf *bytes.Buffer
|
||||
buf, err = b.SSHExec("zfs list -H -t snapshot -o name")
|
||||
|
||||
csvReader := csv.NewReader(buf)
|
||||
csvReader.Comma = '\t'
|
||||
csvReader.FieldsPerRecord = 1
|
||||
|
||||
csvData, err := csvReader.ReadAll()
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Box.SnapshotInitialize : %s : csvReader.ReadAll() : %s", b.Name, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rec := range csvData {
|
||||
b.zfs.SnapshotList = append(b.zfs.SnapshotList, Snapshot(rec[0]))
|
||||
}
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("Box.SnapshotInitialize : %s : read %d zfs snapshots", b.Name, len(b.zfs.SnapshotList))
|
||||
}
|
||||
|
||||
b.zfs.SnapshotInitialized = true
|
||||
b.zfs.SnapshotAdded = false
|
||||
b.zfs.SnapshotDeleted = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Box) ZFSUpdateList() (err error) {
|
||||
|
128
ssh.go
128
ssh.go
@ -2,8 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
@ -15,135 +13,9 @@ type SSHConfig struct {
|
||||
client *ssh.Client
|
||||
logged bool
|
||||
name string
|
||||
zfs map[string]string
|
||||
snapshot []Snapshot
|
||||
}
|
||||
|
||||
func (s *SSHConfig) getLastSnapshot(path string) (Snapshot, error) {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getLastSnapshot : Start %s:%s (%d snapshots)", s.name, path, len(s.snapshot))
|
||||
}
|
||||
var last Snapshot
|
||||
for _, v := range s.snapshot {
|
||||
if v.Path() == path {
|
||||
last = v
|
||||
} else {
|
||||
if len(string(last)) > 0 {
|
||||
return last, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(string(last)) > 0 {
|
||||
return last, nil
|
||||
}
|
||||
return last, fmt.Errorf("no snapshot")
|
||||
}
|
||||
|
||||
func (s *SSHConfig) isLastSnapshot(snapshot Snapshot) bool {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.isLastSnapshot : Start %s:%s", s.name, string(snapshot))
|
||||
}
|
||||
_, err := s.getNextSnapshot(snapshot)
|
||||
if err != nil {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (s *SSHConfig) getFirstSnapshot(path string) (Snapshot, error) {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getFirstSnapshot : Start %s:%s", s.name, path)
|
||||
}
|
||||
var first Snapshot
|
||||
for _, v := range s.snapshot {
|
||||
if v.Path() == path {
|
||||
first = v
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getFirstSnapshot : Return %s", string(first))
|
||||
}
|
||||
return first, nil
|
||||
}
|
||||
}
|
||||
return first, fmt.Errorf("no snapshot")
|
||||
}
|
||||
|
||||
func (s *SSHConfig) getNextSnapshot(snapshot Snapshot) (Snapshot, error) {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getNextSnapshot : Start %s:%s", s.name, string(snapshot))
|
||||
}
|
||||
var next Snapshot
|
||||
for id, v := range s.snapshot {
|
||||
if v == snapshot {
|
||||
if len(s.snapshot) > id+1 {
|
||||
next = s.snapshot[id+1]
|
||||
if next.Path() == snapshot.Path() {
|
||||
return next, nil
|
||||
} else {
|
||||
return next, fmt.Errorf("no snapshot")
|
||||
}
|
||||
} else {
|
||||
return next, fmt.Errorf("no snapshot")
|
||||
}
|
||||
}
|
||||
}
|
||||
return next, fmt.Errorf("no snapshot")
|
||||
}
|
||||
|
||||
func (s *SSHConfig) getSnapshotList() error {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getSnapshotList : %s : Start", s.name)
|
||||
}
|
||||
if !s.logged {
|
||||
return fmt.Errorf("Client %s not logged in.", s.name)
|
||||
}
|
||||
|
||||
session, err := s.client.NewSession()
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getSnapshotList : %s : client.NewSession() : %s", s.name, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
session.Stdout = &b
|
||||
|
||||
err = session.Run("TZ=\"" + cfg.Timezone + "\" zfs list -H -t snapshot -o name")
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getSnapshotList : %s : session.Run() : %s", s.name, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
s.snapshot = make([]Snapshot, 0)
|
||||
|
||||
csvReader := csv.NewReader(&b)
|
||||
csvReader.Comma = '\t'
|
||||
csvReader.FieldsPerRecord = 1
|
||||
|
||||
csvData, err := csvReader.ReadAll()
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getSnapshotList : %s : csvReader.ReadAll() : %s", s.name, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
for _, rec := range csvData {
|
||||
s.snapshot = append(s.snapshot, Snapshot(rec[0]))
|
||||
}
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.getSnapshotList : %s : read %d zfs snapshots", s.name, len(s.snapshot))
|
||||
}
|
||||
|
||||
session.Close()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *SSHConfig) exec(cmd string) (b *bytes.Buffer, err error) {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.exec : %s : Start %s", s.name, cmd)
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Code generated by version.sh (@generated) DO NOT EDIT.
|
||||
package main
|
||||
var githash = "2704149"
|
||||
var buildstamp = "2021-11-14_07:57:44"
|
||||
var commits = "20"
|
||||
var version = "2704149-b20 - 2021-11-14_07:57:44"
|
||||
var githash = "e192906"
|
||||
var buildstamp = "2021-11-14_09:20:16"
|
||||
var commits = "21"
|
||||
var version = "e192906-b21 - 2021-11-14_09:20:16"
|
||||
|
Loading…
Reference in New Issue
Block a user