package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "log" "regexp" "time" "golang.org/x/crypto/ssh" ) type Config struct { Zfsnap map[string]string `json:"zfsnap"` Box map[string]*Box `json:"box"` Email EmailConfig `json:"email"` Apps []AppConfig `json:"apps"` Timezone string `json:"timezone"` Now time.Time `json:"-"` } //Load config from file func (c *Config) Load() error { if *debugFlag { log.Printf("SSHConfig.Load : Start") } b, err := ioutil.ReadFile(*cfgFile) if err != nil { if *debugFlag { log.Printf("Config.Load : ioutil.ReadFile(%s) : %s", *cfgFile, err) } return err } err = json.Unmarshal(b, &c) if err != nil { if *debugFlag { log.Printf("Config.Load : json.Unmarshal : %s", err) } return err } if *debugFlag { log.Printf("Config.Load :\r\n%v", cfg) } l, err := time.LoadLocation(cfg.Timezone) if err != nil { if *debugFlag { log.Printf("Config.Load : time.LoadLocation : %s", err) } return err } if len(cfg.Email.SmtpHost) == 0 { if *debugFlag { log.Printf("Config.Load : no smtp") } return fmt.Errorf("no smtp") } if len(cfg.Email.FromEmail) == 0 { if *debugFlag { log.Printf("Config.Load : no email from") } return fmt.Errorf("no email from") } if len(cfg.Email.ToEmail) == 0 { if *debugFlag { log.Printf("Config.Load : no email to") } return fmt.Errorf("no email to") } c.Now = time.Now().In(l) for k, v := range c.Box { v.Name = k v.online = false v.zfs = NewZFSConfig() s := &SSHConfig{ logged: false, name: k, } v.ssh = s keyRaw, err := ioutil.ReadFile(v.Key) if err != nil { if *debugFlag { log.Printf("Config.Load : ioutil.ReadFile(%s) : %s", k, err) } return err } key, err := ssh.ParseRawPrivateKey(keyRaw) if err != nil { if *debugFlag { log.Printf("Config.Load : ssh.ParseRawPrivateKey(%s) : %s", k, err) } return err } s.signer, err = ssh.NewSignerFromKey(key) if err != nil { if *debugFlag { log.Printf("Config.Load : ssh.NewSignerFromKey(%s) : %s", k, err) } return err } s.config = &ssh.ClientConfig{ User: v.User, Auth: []ssh.AuthMethod{ ssh.PublicKeys(s.signer), }, HostKeyCallback: ssh.InsecureIgnoreHostKey(), Timeout: 5 * time.Second, } s.client, err = ssh.Dial("tcp", v.Addr, s.config) if err != nil { if *debugFlag { log.Printf("Config.Load : ssh.Dial(%s) : %s", k, err) } } else { v.online = true session, err := s.client.NewSession() if err != nil { if *debugFlag { log.Printf("Config.Load : client.NewSession(%s) : %s", k, err) } return err } var b bytes.Buffer session.Stdout = &b err = session.Run("TZ=\"" + cfg.Timezone + "\" zfsnap --version") if err != nil { if *debugFlag { log.Printf("Config.Load : client.NewSession(%s) : %s", k, err) } return err } if *debugFlag { log.Printf("Config.Load : logged into %s : %s", k, b.String()) } session.Close() s.logged = true } } for _, app := range c.Apps { for _, src := range app.Sources { if !src.Valid() { return fmt.Errorf("Source not valid : %s", string(src)) } if _, ok := cfg.Box[src.Box()]; !ok { return fmt.Errorf("No box defined for source : %s", string(src)) } if !cfg.Box[src.Box()].online { email.items = append(email.items, fmt.Sprintf("Source box offline for app : %s", app.Name)) } } var allOffline bool = true for _, dest := range app.Destinations { if !dest.Valid() { return fmt.Errorf("Destination not valid : %s", string(dest)) } if _, ok := cfg.Box[dest.Box()]; !ok { return fmt.Errorf("No box defined for destination : %s", string(dest)) } if cfg.Box[dest.Box()].online { allOffline = false } } if allOffline { email.items = append(email.items, fmt.Sprintf("No online destination box for app : %s", app.Name)) } for val, before := range app.Before { _, err = regexp.Compile(val) if err != nil { if *debugFlag { log.Printf("Config.Load : invalid regex : %s", val) } return err } if !before.Valid() { return fmt.Errorf("Before not valid : %s", string(before)) } if _, ok := cfg.Box[before.Box()]; !ok { return fmt.Errorf("No box defined for before : %s", string(before)) } if !cfg.Box[before.Box()].online { email.items = append(email.items, fmt.Sprintf("Before box offline for app : %s", app.Name)) } } for val, after := range app.After { _, err = regexp.Compile(val) if err != nil { if *debugFlag { log.Printf("Config.Load : invalid regex : %s", val) } return err } if !after.Valid() { return fmt.Errorf("After not valid : %s", string(after)) } if _, ok := cfg.Box[after.Box()]; !ok { return fmt.Errorf("No box defined for after : %s", string(after)) } if !cfg.Box[after.Box()].online { email.items = append(email.items, fmt.Sprintf("After box offline for app : %s", app.Name)) } } } return nil } //Close config func (c *Config) Close() error { return nil }