update
This commit is contained in:
		
							parent
							
								
									6c3cd690de
								
							
						
					
					
						commit
						9eb8cee7d8
					
				
							
								
								
									
										576
									
								
								app.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										576
									
								
								app.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,576 @@
 | 
				
			|||||||
 | 
					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"`
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a AppConfig) getTime() time.Time {
 | 
				
			||||||
 | 
						for _, v := range a.Sources {
 | 
				
			||||||
 | 
							return cfg.Box[v.Box()].ssh.now
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, v := range a.Destinations {
 | 
				
			||||||
 | 
							return cfg.Box[v.Box()].ssh.now
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return time.Now()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a AppConfig) getSchedule() (string, error) {
 | 
				
			||||||
 | 
						var schedule string
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("AppConfig.getSchedule : %s : Start", a.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						refreshSnapshot := make(map[string]bool)
 | 
				
			||||||
 | 
						for _, v := range a.Sources {
 | 
				
			||||||
 | 
							refreshSnapshot[v.Box()] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for k, _ := range refreshSnapshot {
 | 
				
			||||||
 | 
							err := cfg.Box[k].ssh.getSnapshotList()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("AppConfig.getSchedule : %s : getSnapshotList(%s) : %s", a.Name, k, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return "", err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if *schedFlag != "" {
 | 
				
			||||||
 | 
							schedule = *schedFlag
 | 
				
			||||||
 | 
						} else if a.needYearlySnapshot() {
 | 
				
			||||||
 | 
							schedule = "yearly"
 | 
				
			||||||
 | 
						} else if a.needMonthlySnapshot() {
 | 
				
			||||||
 | 
							schedule = "monthly"
 | 
				
			||||||
 | 
						} else if a.needWeeklySnapshot() {
 | 
				
			||||||
 | 
							schedule = "weekly"
 | 
				
			||||||
 | 
						} else if a.needDailySnapshot() {
 | 
				
			||||||
 | 
							schedule = "daily"
 | 
				
			||||||
 | 
						} else if a.needHourlySnapshot() {
 | 
				
			||||||
 | 
							schedule = "hourly"
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							return schedule, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ret, ok := cfg.Zfsnap[schedule]; !ok {
 | 
				
			||||||
 | 
							return "", 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)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return schedule, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a AppConfig) needYearlySnapshot() bool {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("AppConfig.needYearlySnapshot : %s : Start", a.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ret := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// schedule enabled for app ?
 | 
				
			||||||
 | 
						for _, v := range a.Schedule {
 | 
				
			||||||
 | 
							if v == "yearly" {
 | 
				
			||||||
 | 
								ret = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !ret {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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{})
 | 
				
			||||||
 | 
							for _, snap := range cfg.Box[src.Box()].ssh.snapshot {
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											if *debugFlag {
 | 
				
			||||||
 | 
												log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} 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
 | 
				
			||||||
 | 
						now := a.getTime()
 | 
				
			||||||
 | 
						for t, _ := range timeTotal {
 | 
				
			||||||
 | 
							if t.Year() == now.Year() {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// no timestamp => need the snapshot !
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a AppConfig) needMonthlySnapshot() bool {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("AppConfig.needMonthlySnapshot : %s : Start", a.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ret := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// schedule enabled for app ?
 | 
				
			||||||
 | 
						for _, v := range a.Schedule {
 | 
				
			||||||
 | 
							if v == "monthly" {
 | 
				
			||||||
 | 
								ret = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !ret {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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{})
 | 
				
			||||||
 | 
							for _, snap := range cfg.Box[src.Box()].ssh.snapshot {
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											if *debugFlag {
 | 
				
			||||||
 | 
												log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} 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
 | 
				
			||||||
 | 
						now := a.getTime()
 | 
				
			||||||
 | 
						for t, _ := range timeTotal {
 | 
				
			||||||
 | 
							if t.Year() == now.Year() && t.Month() == now.Month() {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// no timestamp => need the snapshot !
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a AppConfig) needWeeklySnapshot() bool {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("AppConfig.needWeeklySnapshot : %s : Start", a.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ret := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// schedule enabled for app ?
 | 
				
			||||||
 | 
						for _, v := range a.Schedule {
 | 
				
			||||||
 | 
							if v == "weekly" {
 | 
				
			||||||
 | 
								ret = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !ret {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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{})
 | 
				
			||||||
 | 
							for _, snap := range cfg.Box[src.Box()].ssh.snapshot {
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											if *debugFlag {
 | 
				
			||||||
 | 
												log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} 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
 | 
				
			||||||
 | 
						now := a.getTime()
 | 
				
			||||||
 | 
						nowYear, nowWeek := now.ISOWeek()
 | 
				
			||||||
 | 
						for t, _ := range timeTotal {
 | 
				
			||||||
 | 
							snapYear, snapWeek := t.ISOWeek()
 | 
				
			||||||
 | 
							if nowYear == snapYear && nowWeek == snapWeek {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// no timestamp => need the snapshot !
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a AppConfig) needDailySnapshot() bool {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("AppConfig.needDailySnapshot : %s : Start", a.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ret := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// schedule enabled for app ?
 | 
				
			||||||
 | 
						for _, v := range a.Schedule {
 | 
				
			||||||
 | 
							if v == "daily" {
 | 
				
			||||||
 | 
								ret = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !ret {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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{})
 | 
				
			||||||
 | 
							for _, snap := range cfg.Box[src.Box()].ssh.snapshot {
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											if *debugFlag {
 | 
				
			||||||
 | 
												log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} 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
 | 
				
			||||||
 | 
						now := a.getTime()
 | 
				
			||||||
 | 
						for t, _ := range timeTotal {
 | 
				
			||||||
 | 
							if t.Year() == now.Year() && t.Month() == now.Month() && t.Day() == now.Day() {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// no timestamp => need the snapshot !
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a AppConfig) needHourlySnapshot() bool {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("AppConfig.needHourlySnapshot : %s : Start", a.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ret := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// schedule enabled for app ?
 | 
				
			||||||
 | 
						for _, v := range a.Schedule {
 | 
				
			||||||
 | 
							if v == "hourly" {
 | 
				
			||||||
 | 
								ret = true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if !ret {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 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{})
 | 
				
			||||||
 | 
							for _, snap := range cfg.Box[src.Box()].ssh.snapshot {
 | 
				
			||||||
 | 
								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)
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											if *debugFlag {
 | 
				
			||||||
 | 
												log.Printf("AppConfig.needYearlySnapshot : %s : time.Parse(%s) : %s", a.Name, dateString, err)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} 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
 | 
				
			||||||
 | 
						now := a.getTime()
 | 
				
			||||||
 | 
						for t, _ := range timeTotal {
 | 
				
			||||||
 | 
							if t.Year() == now.Year() && t.Month() == now.Month() && t.Day() == now.Day() && t.Hour() == now.Hour() {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// no timestamp => need the snapshot !
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (a AppConfig) RunAppBackup() error {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("AppConfig.RunAppBackup : %s : Start", a.Name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						schedule, err := a.getSchedule()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("AppConfig.RunAppBackup : %s : Error getting schedule : %s", a.Name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if schedule == "" {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, src := range a.Sources {
 | 
				
			||||||
 | 
							if !cfg.Box[src.Box()].ssh.isZFS(src.Path()) {
 | 
				
			||||||
 | 
								return fmt.Errorf("No path %s on source", string(src))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							for _, dest := range a.Destinations {
 | 
				
			||||||
 | 
								if !cfg.Box[dest.Box()].ssh.isZFS(dest.Path() + "/" + src.Box() + "/" + src.Path()) {
 | 
				
			||||||
 | 
									err := cfg.Box[dest.Box()].ssh.createZFS(dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										if *debugFlag {
 | 
				
			||||||
 | 
											log.Printf("AppConfig.RunAppBackup : %s : Error creating %s on %s", a.Name, dest.Path()+"/"+src.Box()+"/"+src.Path(), dest.Box())
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for k, v := range a.Before {
 | 
				
			||||||
 | 
							re := regexp.MustCompile(k)
 | 
				
			||||||
 | 
							if re.MatchString(schedule) {
 | 
				
			||||||
 | 
								err := cfg.Box[v.Box()].ssh.exec(v.Path())
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if *debugFlag {
 | 
				
			||||||
 | 
										log.Printf("AppConfig.RunAppBackup : %s : Error executing %s", a.Name, string(v))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						refreshSnapshot := make(map[string]bool)
 | 
				
			||||||
 | 
						takeSnapshot := make(map[string]string)
 | 
				
			||||||
 | 
						delSnapshot := make(map[string]string)
 | 
				
			||||||
 | 
						for _, v := range a.Sources {
 | 
				
			||||||
 | 
							takeSnapshot[v.Box()] = takeSnapshot[v.Box()] + " " + v.Path()
 | 
				
			||||||
 | 
							refreshSnapshot[v.Box()] = true
 | 
				
			||||||
 | 
							for _, v2 := range a.Destinations {
 | 
				
			||||||
 | 
								delSnapshot[v2.Box()] = delSnapshot[v2.Box()] + " " + v2.Path() + "/" + v.Box() + "/" + v.Path()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for k, v := range takeSnapshot {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("AppConfig.RunAppBackup : %s : taking snapshot on %s for %s", a.Name, k, v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err := cfg.Box[k].ssh.exec("/usr/sbin/zfsnap snapshot -p " + schedule + "- -a " + cfg.Zfsnap[schedule] + v)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("AppConfig.RunAppBackup : %s : Error executing zfsnap on %s", a.Name, k)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, v := range a.Destinations {
 | 
				
			||||||
 | 
							refreshSnapshot[v.Box()] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for k, _ := range refreshSnapshot {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("AppConfig.RunAppBackup : %s : refreshing snapshots for source %s", a.Name, k)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err := cfg.Box[k].ssh.getSnapshotList()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("AppConfig.RunAppBackup : %s : Error getting snapshots on %s", a.Name, k)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, src := range a.Sources {
 | 
				
			||||||
 | 
							for _, dest := range a.Destinations {
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("AppConfig.RunAppBackup : %s : Sending snapshots from %s to %s", a.Name, string(src), string(dest))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								dLastSnapshot, err := cfg.Box[dest.Box()].ssh.getLastSnapshot(dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if *debugFlag {
 | 
				
			||||||
 | 
										log.Printf("AppConfig.RunAppBackup : %s : No snapshot for %s on %s", a.Name, string(src), dest.Box())
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									sFirstSnapshot, err := cfg.Box[src.Box()].ssh.getFirstSnapshot(src.Path())
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										if *debugFlag {
 | 
				
			||||||
 | 
											log.Printf("AppConfig.RunAppBackup : %s : No snapshot for %s", a.Name, string(src))
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if *debugFlag {
 | 
				
			||||||
 | 
										log.Printf("AppConfig.RunAppBackup : %s : Initializing snapshot on %s from %s", a.Name, dest.Box(), string(sFirstSnapshot))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									err = cfg.Box[dest.Box()].ssh.exec("/usr/bin/ssh root@" + src.Box() + " /sbin/zfs send " + string(sFirstSnapshot) + " | /sbin/zfs recv -F " + dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										if *debugFlag {
 | 
				
			||||||
 | 
											log.Printf("AppConfig.RunAppBackup : %s : Initializing snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sFirstSnapshot), err)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										return err
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									var sCurrSnapshot Snapshot
 | 
				
			||||||
 | 
									sNextSnapshot := sFirstSnapshot
 | 
				
			||||||
 | 
									for !cfg.Box[src.Box()].ssh.isLastSnapshot(sNextSnapshot) {
 | 
				
			||||||
 | 
										sCurrSnapshot = sNextSnapshot
 | 
				
			||||||
 | 
										sNextSnapshot, err = cfg.Box[src.Box()].ssh.getNextSnapshot(sNextSnapshot)
 | 
				
			||||||
 | 
										if *debugFlag {
 | 
				
			||||||
 | 
											log.Printf("AppConfig.RunAppBackup : %s : Sending incrementally %s to %s", a.Name, string(sNextSnapshot), dest.Box())
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										err = cfg.Box[dest.Box()].ssh.exec("/usr/bin/ssh root@" + src.Box() + " /sbin/zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | /sbin/zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											if *debugFlag {
 | 
				
			||||||
 | 
												log.Printf("AppConfig.RunAppBackup : %s : Sending snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sNextSnapshot), err)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if *debugFlag {
 | 
				
			||||||
 | 
										log.Printf("AppConfig.RunAppBackup : %s : All snapshots sent for %s", a.Name, string(src))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									if *debugFlag {
 | 
				
			||||||
 | 
										log.Printf("AppConfig.RunAppBackup : %s : Last snapshot on %s is %s", a.Name, dest.Box(), string(dLastSnapshot))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									var sCurrSnapshot Snapshot
 | 
				
			||||||
 | 
									sNextSnapshot := Snapshot(string(dLastSnapshot)[len(string(dest))+2:])
 | 
				
			||||||
 | 
									for !cfg.Box[src.Box()].ssh.isLastSnapshot(sNextSnapshot) {
 | 
				
			||||||
 | 
										sCurrSnapshot = sNextSnapshot
 | 
				
			||||||
 | 
										sNextSnapshot, err = cfg.Box[src.Box()].ssh.getNextSnapshot(sNextSnapshot)
 | 
				
			||||||
 | 
										if *debugFlag {
 | 
				
			||||||
 | 
											log.Printf("AppConfig.RunAppBackup : %s : Sending incrementally %s to %s", a.Name, string(sNextSnapshot), dest.Box())
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										err = cfg.Box[dest.Box()].ssh.exec("/usr/bin/ssh root@" + src.Box() + " /sbin/zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | /sbin/zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
				
			||||||
 | 
										if err != nil {
 | 
				
			||||||
 | 
											if *debugFlag {
 | 
				
			||||||
 | 
												log.Printf("AppConfig.RunAppBackup : %s : Sending snapshot on %s from %s failed (%s)", a.Name, dest.Box(), string(sNextSnapshot), err)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
											return err
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for k, v := range takeSnapshot {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("AppConfig.RunAppBackup : %s : cleaning snapshot on %s for %s", a.Name, k, v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err := cfg.Box[k].ssh.exec("/usr/sbin/zfsnap destroy -p hourly- -p daily- -p weekly- -p monthly- -p yearly- " + v)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("AppConfig.RunAppBackup : %s : Error executing zfsnap on %s", a.Name, k)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for k, v := range delSnapshot {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("AppConfig.RunAppBackup : %s : cleaning snapshot on %s for %s", a.Name, k, v)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err := cfg.Box[k].ssh.exec("/usr/sbin/zfsnap destroy -p hourly- -p daily- -p weekly- -p monthly- -p yearly- " + v)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("AppConfig.RunAppBackup : %s : Error executing zfsnap on %s", a.Name, k)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for k, v := range a.After {
 | 
				
			||||||
 | 
							re := regexp.MustCompile(k)
 | 
				
			||||||
 | 
							if re.MatchString(schedule) {
 | 
				
			||||||
 | 
								err := cfg.Box[v.Box()].ssh.exec(v.Path())
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if *debugFlag {
 | 
				
			||||||
 | 
										log.Printf("AppConfig.RunAppBackup : %s : Error executing %s on %s", a.Name, v.Path(), v.Box())
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										564
									
								
								backup.go
									
									
									
									
									
								
							
							
						
						
									
										564
									
								
								backup.go
									
									
									
									
									
								
							@ -2,7 +2,6 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"encoding/csv"
 | 
					 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
@ -10,46 +9,22 @@ import (
 | 
				
			|||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/crypto/ssh"
 | 
						"golang.org/x/crypto/ssh"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
	Zfsnap   map[string]string     `json:"zfsnap"`
 | 
						Zfsnap   map[string]string     `json:"zfsnap"`
 | 
				
			||||||
	Box    map[string]BoxConfig `json:"box"`
 | 
						Box      map[string]*BoxConfig `json:"box"`
 | 
				
			||||||
	Apps     []AppConfig           `json:apps`
 | 
						Apps     []AppConfig           `json:apps`
 | 
				
			||||||
	ssh    map[string]*SSHConfig
 | 
						Timezone string                `json:"timezone"`
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Location string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type Snapshot string
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
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"`
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type SSHConfig struct {
 | 
					 | 
				
			||||||
	signer   ssh.Signer
 | 
					 | 
				
			||||||
	config   *ssh.ClientConfig
 | 
					 | 
				
			||||||
	client   *ssh.Client
 | 
					 | 
				
			||||||
	logged   bool
 | 
					 | 
				
			||||||
	name     string
 | 
					 | 
				
			||||||
	zfs      map[string]string
 | 
					 | 
				
			||||||
	snapshot []Snapshot
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type BoxConfig struct {
 | 
					type BoxConfig struct {
 | 
				
			||||||
	Addr string `json:"addr"`
 | 
						Addr string `json:"addr"`
 | 
				
			||||||
	User string `json:"user"`
 | 
						User string `json:"user"`
 | 
				
			||||||
	Key  string `json:"key"`
 | 
						Key  string `json:"key"`
 | 
				
			||||||
 | 
						ssh  *SSHConfig
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
@ -60,38 +35,11 @@ var (
 | 
				
			|||||||
	cfg       Config
 | 
						cfg       Config
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (l Location) Box() string {
 | 
					 | 
				
			||||||
	s := strings.Split(string(l), `:`)
 | 
					 | 
				
			||||||
	return s[0]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l Location) Path() string {
 | 
					 | 
				
			||||||
	s := strings.Split(string(l), `:`)
 | 
					 | 
				
			||||||
	return s[1]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (l Location) Valid() bool {
 | 
					 | 
				
			||||||
	s := strings.Split(string(l), `:`)
 | 
					 | 
				
			||||||
	return len(s) == 2
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s Snapshot) Path() string {
 | 
					 | 
				
			||||||
	s2 := strings.Split(string(s), `@`)
 | 
					 | 
				
			||||||
	return s2[0]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s Snapshot) Name() string {
 | 
					 | 
				
			||||||
	s2 := strings.Split(string(s), `@`)
 | 
					 | 
				
			||||||
	return s2[1]
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s Snapshot) Append(path string) Snapshot {
 | 
					 | 
				
			||||||
	s2 := strings.Split(string(s), `@`)
 | 
					 | 
				
			||||||
	return Snapshot(s2[0] + "/" + path + "@" + s2[1])
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
//Load config from file
 | 
					//Load config from file
 | 
				
			||||||
func (c *Config) Load() error {
 | 
					func (c *Config) Load() error {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("SSHConfig.Load : Start")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	b, err := ioutil.ReadFile(*cfgFile)
 | 
						b, err := ioutil.ReadFile(*cfgFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if *debugFlag {
 | 
							if *debugFlag {
 | 
				
			||||||
@ -108,13 +56,12 @@ func (c *Config) Load() error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cfg.ssh = make(map[string]*SSHConfig)
 | 
					 | 
				
			||||||
	for k, v := range c.Box {
 | 
						for k, v := range c.Box {
 | 
				
			||||||
		s := &SSHConfig{
 | 
							s := &SSHConfig{
 | 
				
			||||||
			logged: false,
 | 
								logged: false,
 | 
				
			||||||
			name:   k,
 | 
								name:   k,
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		cfg.ssh[k] = s
 | 
							v.ssh = s
 | 
				
			||||||
		keyRaw, err := ioutil.ReadFile(v.Key)
 | 
							keyRaw, err := ioutil.ReadFile(v.Key)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if *debugFlag {
 | 
								if *debugFlag {
 | 
				
			||||||
@ -165,7 +112,8 @@ func (c *Config) Load() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		var b bytes.Buffer
 | 
							var b bytes.Buffer
 | 
				
			||||||
		session.Stdout = &b
 | 
							session.Stdout = &b
 | 
				
			||||||
		err = session.Run("/usr/sbin/zfsnap --version")
 | 
					
 | 
				
			||||||
 | 
							err = session.Run("TZ=\"" + cfg.Timezone + "\" /usr/sbin/zfsnap --version")
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if *debugFlag {
 | 
								if *debugFlag {
 | 
				
			||||||
				log.Printf("Config.Load : client.NewSession(%s) : %s", k, err)
 | 
									log.Printf("Config.Load : client.NewSession(%s) : %s", k, err)
 | 
				
			||||||
@ -177,7 +125,16 @@ func (c *Config) Load() error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		session.Close()
 | 
							session.Close()
 | 
				
			||||||
		s.logged = true
 | 
							s.logged = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, box := range c.Box {
 | 
				
			||||||
 | 
							err = box.ssh.getTime()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("Config.Load : ssh.getTime() : %s", err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, app := range c.Apps {
 | 
						for _, app := range c.Apps {
 | 
				
			||||||
@ -185,7 +142,7 @@ func (c *Config) Load() error {
 | 
				
			|||||||
			if !src.Valid() {
 | 
								if !src.Valid() {
 | 
				
			||||||
				return fmt.Errorf("Source not valid : %s", string(src))
 | 
									return fmt.Errorf("Source not valid : %s", string(src))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if _, ok := cfg.ssh[src.Box()]; !ok {
 | 
								if _, ok := cfg.Box[src.Box()]; !ok {
 | 
				
			||||||
				return fmt.Errorf("No box defined for source : %s", string(src))
 | 
									return fmt.Errorf("No box defined for source : %s", string(src))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -193,7 +150,7 @@ func (c *Config) Load() error {
 | 
				
			|||||||
			if !dest.Valid() {
 | 
								if !dest.Valid() {
 | 
				
			||||||
				return fmt.Errorf("Destination not valid : %s", string(dest))
 | 
									return fmt.Errorf("Destination not valid : %s", string(dest))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if _, ok := cfg.ssh[dest.Box()]; !ok {
 | 
								if _, ok := cfg.Box[dest.Box()]; !ok {
 | 
				
			||||||
				return fmt.Errorf("No box defined for destination : %s", string(dest))
 | 
									return fmt.Errorf("No box defined for destination : %s", string(dest))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -208,7 +165,7 @@ func (c *Config) Load() error {
 | 
				
			|||||||
			if !before.Valid() {
 | 
								if !before.Valid() {
 | 
				
			||||||
				return fmt.Errorf("Before not valid : %s", string(before))
 | 
									return fmt.Errorf("Before not valid : %s", string(before))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if _, ok := cfg.ssh[before.Box()]; !ok {
 | 
								if _, ok := cfg.Box[before.Box()]; !ok {
 | 
				
			||||||
				return fmt.Errorf("No box defined for before : %s", string(before))
 | 
									return fmt.Errorf("No box defined for before : %s", string(before))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -223,7 +180,7 @@ func (c *Config) Load() error {
 | 
				
			|||||||
			if !after.Valid() {
 | 
								if !after.Valid() {
 | 
				
			||||||
				return fmt.Errorf("After not valid : %s", string(after))
 | 
									return fmt.Errorf("After not valid : %s", string(after))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if _, ok := cfg.ssh[after.Box()]; !ok {
 | 
								if _, ok := cfg.Box[after.Box()]; !ok {
 | 
				
			||||||
				return fmt.Errorf("No box defined for after : %s", string(after))
 | 
									return fmt.Errorf("No box defined for after : %s", string(after))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -237,449 +194,6 @@ func (c *Config) Close() error {
 | 
				
			|||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
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
 | 
					 | 
				
			||||||
			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("/usr/sbin/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) getZFSList() error {
 | 
					 | 
				
			||||||
	if *debugFlag {
 | 
					 | 
				
			||||||
		log.Printf("SSHConfig.getZFSList : Start %s", 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.getZFSList : client.NewSession(%s) : %s", s.name, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	var b bytes.Buffer
 | 
					 | 
				
			||||||
	session.Stdout = &b
 | 
					 | 
				
			||||||
	err = session.Run("/sbin/zfs list -H -o name,mountpoint")
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if *debugFlag {
 | 
					 | 
				
			||||||
			log.Printf("SSHConfig.getZFSList : session.Run(%s) : %s", s.name, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	s.zfs = make(map[string]string)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	csvReader := csv.NewReader(&b)
 | 
					 | 
				
			||||||
	csvReader.Comma = '\t'
 | 
					 | 
				
			||||||
	csvReader.FieldsPerRecord = 2
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	csvData, err := csvReader.ReadAll()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if *debugFlag {
 | 
					 | 
				
			||||||
			log.Printf("SSHConfig.getZFSList : csvReader.ReadAll(%s) : %s", s.name, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, rec := range csvData {
 | 
					 | 
				
			||||||
		s.zfs[rec[0]] = rec[1]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if *debugFlag {
 | 
					 | 
				
			||||||
		log.Printf("SSHConfig.getZFSList : %s : read %d zfs file systems", s.name, len(s.zfs))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	session.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *SSHConfig) isZFS(path string) bool {
 | 
					 | 
				
			||||||
	if *debugFlag {
 | 
					 | 
				
			||||||
		log.Printf("SSHConfig.isZFS : Start %s:%s", s.name, path)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(s.zfs) == 0 {
 | 
					 | 
				
			||||||
		err := s.getZFSList()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			if *debugFlag {
 | 
					 | 
				
			||||||
				log.Printf("SSHConfig.isZFS : s.getZFSList(%s) : %s", s.name, err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return false
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	_, ok := s.zfs[path]
 | 
					 | 
				
			||||||
	if ok {
 | 
					 | 
				
			||||||
		return true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *SSHConfig) exec(cmd string) error {
 | 
					 | 
				
			||||||
	if *debugFlag {
 | 
					 | 
				
			||||||
		log.Printf("SSHConfig.exec : Start %s on %s", cmd, s.name)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	session, err := s.client.NewSession()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if *debugFlag {
 | 
					 | 
				
			||||||
			log.Printf("SSHConfig.exec : client(%s).NewSession(%s) : %s", s.name, cmd, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = session.Run(cmd)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		if *debugFlag {
 | 
					 | 
				
			||||||
			log.Printf("SSHConfig.exec : session(%s).Run(%s) : %s", s.name, cmd, err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	session.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (s *SSHConfig) createZFS(path string) error {
 | 
					 | 
				
			||||||
	if *debugFlag {
 | 
					 | 
				
			||||||
		log.Printf("SSHConfig.createZFS : Start %s:%s", s.name, path)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if len(s.zfs) == 0 {
 | 
					 | 
				
			||||||
		err := s.getZFSList()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			if *debugFlag {
 | 
					 | 
				
			||||||
				log.Printf("SSHConfig.createZFS : s.getZFSList(%s) : %s", s.name, err)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	p := strings.Split(path, `/`)
 | 
					 | 
				
			||||||
	var base string
 | 
					 | 
				
			||||||
	for _, d := range p {
 | 
					 | 
				
			||||||
		if base == "" {
 | 
					 | 
				
			||||||
			base = d
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			base = base + `/` + d
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if _, ok := s.zfs[base]; !ok {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if *debugFlag {
 | 
					 | 
				
			||||||
				log.Printf("SSHConfig.createZFS : Creating %s:%s", s.name, base)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			err := s.exec("/sbin/zfs create -o mountpoint=none " + base)
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				if *debugFlag {
 | 
					 | 
				
			||||||
					log.Printf("SSHConfig.createZFS : s.exec(%s) : %s", s.name, err)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			s.zfs[base] = "none"
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (a AppConfig) RunAppSchedule(schedule string) error {
 | 
					 | 
				
			||||||
	if *debugFlag {
 | 
					 | 
				
			||||||
		log.Printf("RunAppSchedule(%s) : %s : Start", a.Name, schedule)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, src := range a.Sources {
 | 
					 | 
				
			||||||
		if !cfg.ssh[src.Box()].isZFS(src.Path()) {
 | 
					 | 
				
			||||||
			return fmt.Errorf("No path %s on source", string(src))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		for _, dest := range a.Destinations {
 | 
					 | 
				
			||||||
			if !cfg.ssh[dest.Box()].isZFS(dest.Path() + "/" + src.Box() + "/" + src.Path()) {
 | 
					 | 
				
			||||||
				err := cfg.ssh[dest.Box()].createZFS(dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					if *debugFlag {
 | 
					 | 
				
			||||||
						log.Printf("RunAppSchedule(%s) : %s : Error creating %s on %s", a.Name, schedule, dest.Path()+"/"+src.Box()+"/"+src.Path(), dest.Box())
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					return err
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for k, v := range a.Before {
 | 
					 | 
				
			||||||
		re := regexp.MustCompile(k)
 | 
					 | 
				
			||||||
		if re.MatchString(schedule) {
 | 
					 | 
				
			||||||
			err := cfg.ssh[v.Box()].exec(v.Path())
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				if *debugFlag {
 | 
					 | 
				
			||||||
					log.Printf("RunAppSchedule(%s) : %s : Error executing %s", a.Name, schedule, string(v))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	refreshSnapshot := make(map[string]bool)
 | 
					 | 
				
			||||||
	takeSnapshot := make(map[string]string)
 | 
					 | 
				
			||||||
	delSnapshot := make(map[string]string)
 | 
					 | 
				
			||||||
	for _, v := range a.Sources {
 | 
					 | 
				
			||||||
		takeSnapshot[v.Box()] = takeSnapshot[v.Box()] + " " + v.Path()
 | 
					 | 
				
			||||||
		refreshSnapshot[v.Box()] = true
 | 
					 | 
				
			||||||
		for _, v2 := range a.Destinations {
 | 
					 | 
				
			||||||
			delSnapshot[v2.Box()] = delSnapshot[v2.Box()] + " " + v2.Path() + "/" + v.Box() + "/" + v.Path()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for k, v := range takeSnapshot {
 | 
					 | 
				
			||||||
		if *debugFlag {
 | 
					 | 
				
			||||||
			log.Printf("RunAppSchedule(%s) : %s : taking snapshot on %s for %s", a.Name, schedule, k, v)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		err := cfg.ssh[k].exec("/usr/sbin/zfsnap snapshot -a " + cfg.Zfsnap[schedule] + v)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			if *debugFlag {
 | 
					 | 
				
			||||||
				log.Printf("RunAppSchedule(%s) : %s : Error executing zfsnap on %s", a.Name, schedule, k)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, v := range a.Destinations {
 | 
					 | 
				
			||||||
		refreshSnapshot[v.Box()] = true
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for k, _ := range refreshSnapshot {
 | 
					 | 
				
			||||||
		if *debugFlag {
 | 
					 | 
				
			||||||
			log.Printf("RunAppSchedule(%s) : %s : refreshing snapshots for source %s", a.Name, schedule, k)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		err := cfg.ssh[k].getSnapshotList()
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			if *debugFlag {
 | 
					 | 
				
			||||||
				log.Printf("RunAppSchedule(%s) : %s : Error getting snapshots on %s", a.Name, schedule, k)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for _, src := range a.Sources {
 | 
					 | 
				
			||||||
		for _, dest := range a.Destinations {
 | 
					 | 
				
			||||||
			if *debugFlag {
 | 
					 | 
				
			||||||
				log.Printf("RunAppSchedule(%s) : %s : Sending snapshots from %s to %s", a.Name, schedule, string(src), string(dest))
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			dLastSnapshot, err := cfg.ssh[dest.Box()].getLastSnapshot(dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				if *debugFlag {
 | 
					 | 
				
			||||||
					log.Printf("RunAppSchedule(%s) : %s : No snapshot for %s on %s", a.Name, schedule, string(src), dest.Box())
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				sFirstSnapshot, err := cfg.ssh[src.Box()].getFirstSnapshot(src.Path())
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					if *debugFlag {
 | 
					 | 
				
			||||||
						log.Printf("RunAppSchedule(%s) : %s : No snapshot for %s", a.Name, schedule, string(src))
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					return err
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if *debugFlag {
 | 
					 | 
				
			||||||
					log.Printf("RunAppSchedule(%s) : %s : Initializing snapshot on %s from %s", a.Name, schedule, dest.Box(), string(sFirstSnapshot))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				err = cfg.ssh[dest.Box()].exec("/usr/bin/ssh root@" + src.Box() + " /sbin/zfs send " + string(sFirstSnapshot) + " | /sbin/zfs recv -F " + dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
					 | 
				
			||||||
				if err != nil {
 | 
					 | 
				
			||||||
					if *debugFlag {
 | 
					 | 
				
			||||||
						log.Printf("RunAppSchedule(%s) : %s : Initializing snapshot on %s from %s failed (%s)", a.Name, schedule, dest.Box(), string(sFirstSnapshot), err)
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					return err
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				var sCurrSnapshot Snapshot
 | 
					 | 
				
			||||||
				sNextSnapshot := sFirstSnapshot
 | 
					 | 
				
			||||||
				for !cfg.ssh[src.Box()].isLastSnapshot(sNextSnapshot) {
 | 
					 | 
				
			||||||
					sCurrSnapshot = sNextSnapshot
 | 
					 | 
				
			||||||
					sNextSnapshot, err = cfg.ssh[src.Box()].getNextSnapshot(sNextSnapshot)
 | 
					 | 
				
			||||||
					if *debugFlag {
 | 
					 | 
				
			||||||
						log.Printf("RunAppSchedule(%s) : %s : Sending incrementally %s to %s", a.Name, schedule, string(sNextSnapshot), dest.Box())
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					err = cfg.ssh[dest.Box()].exec("/usr/bin/ssh root@" + src.Box() + " /sbin/zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | /sbin/zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						if *debugFlag {
 | 
					 | 
				
			||||||
							log.Printf("RunAppSchedule(%s) : %s : Sending snapshot on %s from %s failed (%s)", a.Name, schedule, dest.Box(), string(sNextSnapshot), err)
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						return err
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if *debugFlag {
 | 
					 | 
				
			||||||
					log.Printf("RunAppSchedule(%s) : %s : All snapshots sent for %s", a.Name, schedule, string(src))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				if *debugFlag {
 | 
					 | 
				
			||||||
					log.Printf("RunAppSchedule(%s) : %s : Last snapshot on %s is %s", a.Name, schedule, dest.Box(), string(dLastSnapshot))
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				var sCurrSnapshot Snapshot
 | 
					 | 
				
			||||||
				sNextSnapshot := Snapshot(string(dLastSnapshot)[len(string(dest))+2:])
 | 
					 | 
				
			||||||
				for !cfg.ssh[src.Box()].isLastSnapshot(sNextSnapshot) {
 | 
					 | 
				
			||||||
					sCurrSnapshot = sNextSnapshot
 | 
					 | 
				
			||||||
					sNextSnapshot, err = cfg.ssh[src.Box()].getNextSnapshot(sNextSnapshot)
 | 
					 | 
				
			||||||
					if *debugFlag {
 | 
					 | 
				
			||||||
						log.Printf("RunAppSchedule(%s) : %s : Sending incrementally %s to %s", a.Name, schedule, string(sNextSnapshot), dest.Box())
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					err = cfg.ssh[dest.Box()].exec("/usr/bin/ssh root@" + src.Box() + " /sbin/zfs send -I " + string(sCurrSnapshot) + " " + string(sNextSnapshot) + " | /sbin/zfs recv " + dest.Path() + "/" + src.Box() + "/" + src.Path())
 | 
					 | 
				
			||||||
					if err != nil {
 | 
					 | 
				
			||||||
						if *debugFlag {
 | 
					 | 
				
			||||||
							log.Printf("RunAppSchedule(%s) : %s : Sending snapshot on %s from %s failed (%s)", a.Name, schedule, dest.Box(), string(sNextSnapshot), err)
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						return err
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for k, v := range takeSnapshot {
 | 
					 | 
				
			||||||
		if *debugFlag {
 | 
					 | 
				
			||||||
			log.Printf("RunAppSchedule(%s) : %s : cleaning snapshot on %s for %s", a.Name, schedule, k, v)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		err := cfg.ssh[k].exec("/usr/sbin/zfsnap destroy" + v)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			if *debugFlag {
 | 
					 | 
				
			||||||
				log.Printf("RunAppSchedule(%s) : %s : Error executing zfsnap on %s", a.Name, schedule, k)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for k, v := range delSnapshot {
 | 
					 | 
				
			||||||
		if *debugFlag {
 | 
					 | 
				
			||||||
			log.Printf("RunAppSchedule(%s) : %s : cleaning snapshot on %s for %s", a.Name, schedule, k, v)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		err := cfg.ssh[k].exec("/usr/sbin/zfsnap destroy" + v)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			if *debugFlag {
 | 
					 | 
				
			||||||
				log.Printf("RunAppSchedule(%s) : %s : Error executing zfsnap on %s", a.Name, schedule, k)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return err
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	for k, v := range a.After {
 | 
					 | 
				
			||||||
		re := regexp.MustCompile(k)
 | 
					 | 
				
			||||||
		if re.MatchString(schedule) {
 | 
					 | 
				
			||||||
			err := cfg.ssh[v.Box()].exec(v.Path())
 | 
					 | 
				
			||||||
			if err != nil {
 | 
					 | 
				
			||||||
				if *debugFlag {
 | 
					 | 
				
			||||||
					log.Printf("RunAppSchedule(%s) : %s : Error executing %s on %s", a.Name, schedule, v.Path(), v.Box())
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
	flag.Parse()
 | 
						flag.Parse()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -689,50 +203,28 @@ func main() {
 | 
				
			|||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	schedule := *schedFlag
 | 
						err = RunBackup()
 | 
				
			||||||
	if schedule == "" {
 | 
					 | 
				
			||||||
		log.Printf("Main : Finding out schedule.")
 | 
					 | 
				
			||||||
		now := time.Now()
 | 
					 | 
				
			||||||
		if now.Day() == 1 && int(now.Month()) == 1 {
 | 
					 | 
				
			||||||
			schedule = "yearly"
 | 
					 | 
				
			||||||
		} else if now.Day() == 1 {
 | 
					 | 
				
			||||||
			schedule = "monthly"
 | 
					 | 
				
			||||||
		} else if now.Weekday().String() == "Monday" {
 | 
					 | 
				
			||||||
			schedule = "weekly"
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			schedule = "daily"
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	err = RunSchedule(schedule)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Printf("Cannot run schedule (%s)", err)
 | 
							log.Printf("Cannot run schedule (%s)", err)
 | 
				
			||||||
		os.Exit(1)
 | 
							os.Exit(1)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//RunSchedule run all backup targets where schedule is registered
 | 
					//RunBackup run all backup targets where schedule is registered
 | 
				
			||||||
func RunSchedule(schedule string) error {
 | 
					func RunBackup() error {
 | 
				
			||||||
	if *debugFlag {
 | 
						if *debugFlag {
 | 
				
			||||||
		log.Printf("RunSchedule(%s) : Start", schedule)
 | 
							log.Printf("RunBackup() : Start")
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if _, ok := cfg.Zfsnap[schedule]; !ok {
 | 
					 | 
				
			||||||
		return fmt.Errorf("No retention defined for %s schedule", schedule)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, app := range cfg.Apps {
 | 
						for _, app := range cfg.Apps {
 | 
				
			||||||
		for _, schedName := range app.Schedule {
 | 
							err := app.RunAppBackup()
 | 
				
			||||||
			if schedName == schedule {
 | 
					 | 
				
			||||||
				err := app.RunAppSchedule(schedule)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if *debugFlag {
 | 
								if *debugFlag {
 | 
				
			||||||
						log.Printf("RunSchedule(%s) : Error running %s", schedule, app.Name)
 | 
									log.Printf("RunBackup() : Error running %s", app.Name)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										20
									
								
								location.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								location.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Location string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l Location) Box() string {
 | 
				
			||||||
 | 
						s := strings.Split(string(l), `:`)
 | 
				
			||||||
 | 
						return s[0]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l Location) Path() string {
 | 
				
			||||||
 | 
						s := strings.Split(string(l), `:`)
 | 
				
			||||||
 | 
						return s[1]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (l Location) Valid() bool {
 | 
				
			||||||
 | 
						s := strings.Split(string(l), `:`)
 | 
				
			||||||
 | 
						return len(s) == 2
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										20
									
								
								snapshot.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								snapshot.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Snapshot string
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Snapshot) Path() string {
 | 
				
			||||||
 | 
						s2 := strings.Split(string(s), `@`)
 | 
				
			||||||
 | 
						return s2[0]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Snapshot) Name() string {
 | 
				
			||||||
 | 
						s2 := strings.Split(string(s), `@`)
 | 
				
			||||||
 | 
						return s2[1]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s Snapshot) Append(path string) Snapshot {
 | 
				
			||||||
 | 
						s2 := strings.Split(string(s), `@`)
 | 
				
			||||||
 | 
						return Snapshot(s2[0] + "/" + path + "@" + s2[1])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										331
									
								
								ssh.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										331
									
								
								ssh.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,331 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"encoding/csv"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/crypto/ssh"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type SSHConfig struct {
 | 
				
			||||||
 | 
						signer   ssh.Signer
 | 
				
			||||||
 | 
						config   *ssh.ClientConfig
 | 
				
			||||||
 | 
						client   *ssh.Client
 | 
				
			||||||
 | 
						logged   bool
 | 
				
			||||||
 | 
						name     string
 | 
				
			||||||
 | 
						zfs      map[string]string
 | 
				
			||||||
 | 
						snapshot []Snapshot
 | 
				
			||||||
 | 
						now      time.Time
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
								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 + "\" /usr/sbin/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) getZFSList() error {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("SSHConfig.getZFSList : %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.getZFSList : %s : client.NewSession() : %s", s.name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var b bytes.Buffer
 | 
				
			||||||
 | 
						session.Stdout = &b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = session.Run("TZ=\"" + cfg.Timezone + "\" /sbin/zfs list -H -o name,mountpoint")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("SSHConfig.getZFSList : %s : session.Run() : %s", s.name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s.zfs = make(map[string]string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						csvReader := csv.NewReader(&b)
 | 
				
			||||||
 | 
						csvReader.Comma = '\t'
 | 
				
			||||||
 | 
						csvReader.FieldsPerRecord = 2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						csvData, err := csvReader.ReadAll()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("SSHConfig.getZFSList : %s : csvReader.ReadAll() : %s", s.name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, rec := range csvData {
 | 
				
			||||||
 | 
							s.zfs[rec[0]] = rec[1]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("SSHConfig.getZFSList : %s : read %d zfs file systems", s.name, len(s.zfs))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SSHConfig) isZFS(path string) bool {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("SSHConfig.isZFS : Start %s:%s", s.name, path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(s.zfs) == 0 {
 | 
				
			||||||
 | 
							err := s.getZFSList()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("SSHConfig.isZFS : s.getZFSList(%s) : %s", s.name, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_, ok := s.zfs[path]
 | 
				
			||||||
 | 
						if ok {
 | 
				
			||||||
 | 
							return true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SSHConfig) getTime() error {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("SSHConfig.getTime : %s : Start", s.name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session, err := s.client.NewSession()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("SSHConfig.getTime :  %s : client.NewSession() : %s", s.name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var b bytes.Buffer
 | 
				
			||||||
 | 
						session.Stdout = &b
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = session.Run("TZ=\"" + cfg.Timezone + "\" /usr/bin/date +\"%F %T\"")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("SSHConfig.getTime : %s : session.Run() : %s", s.name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						s.now, err = time.Parse("2006-01-02 15:04:05\n", b.String())
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("SSHConfig.getTime : %s : time.Parse() : %s", s.name, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("SSHConfig.getTime : %s : now is %s", s.name, s.now.String())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SSHConfig) exec(cmd string) error {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("SSHConfig.exec : %s : Start %s", s.name, cmd)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session, err := s.client.NewSession()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("SSHConfig.exec : %s : client().NewSession(%s) : %s", s.name, cmd, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = session.Run("TZ=\"" + cfg.Timezone + "\" " + cmd)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if *debugFlag {
 | 
				
			||||||
 | 
								log.Printf("SSHConfig.exec : session(%s).Run(%s) : %s", s.name, cmd, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						session.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *SSHConfig) createZFS(path string) error {
 | 
				
			||||||
 | 
						if *debugFlag {
 | 
				
			||||||
 | 
							log.Printf("SSHConfig.createZFS : Start %s:%s", s.name, path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(s.zfs) == 0 {
 | 
				
			||||||
 | 
							err := s.getZFSList()
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("SSHConfig.createZFS : s.getZFSList(%s) : %s", s.name, err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								return err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						p := strings.Split(path, `/`)
 | 
				
			||||||
 | 
						var base string
 | 
				
			||||||
 | 
						for _, d := range p {
 | 
				
			||||||
 | 
							if base == "" {
 | 
				
			||||||
 | 
								base = d
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								base = base + `/` + d
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if _, ok := s.zfs[base]; !ok {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if *debugFlag {
 | 
				
			||||||
 | 
									log.Printf("SSHConfig.createZFS : Creating %s:%s", s.name, base)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								err := s.exec("/sbin/zfs create -o mountpoint=none " + base)
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									if *debugFlag {
 | 
				
			||||||
 | 
										log.Printf("SSHConfig.createZFS : s.exec(%s) : %s", s.name, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								s.zfs[base] = "none"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user