2021-11-14 03:53:13 +01:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"log"
|
|
|
|
"regexp"
|
2021-11-14 05:21:22 +01:00
|
|
|
"time"
|
2021-11-14 03:53:13 +01:00
|
|
|
|
|
|
|
"golang.org/x/crypto/ssh"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Config struct {
|
|
|
|
Zfsnap map[string]string `json:"zfsnap"`
|
|
|
|
Box map[string]*Box `json:"box"`
|
2022-06-19 06:27:25 +02:00
|
|
|
Email EmailConfig `json:"email"`
|
2022-06-17 14:54:14 +02:00
|
|
|
Apps []AppConfig `json:"apps"`
|
2021-11-14 03:53:13 +01:00
|
|
|
Timezone string `json:"timezone"`
|
2021-11-14 05:21:22 +01:00
|
|
|
Now time.Time `json:"-"`
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
//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
|
|
|
|
}
|
|
|
|
|
2021-11-14 05:21:22 +01:00
|
|
|
l, err := time.LoadLocation(cfg.Timezone)
|
|
|
|
if err != nil {
|
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("Config.Load : time.LoadLocation : %s", err)
|
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2022-06-19 06:27:25 +02:00
|
|
|
if len(cfg.Email.smtpHost) == 0 || len(cfg.Email.fromEmail) == 0 || len(cfg.Email.toEmail) == 0 {
|
|
|
|
if *debugFlag {
|
|
|
|
log.Printf("Config.Load : no email")
|
|
|
|
}
|
|
|
|
return fmt.Errorf("no email")
|
|
|
|
}
|
|
|
|
|
2021-11-14 05:21:22 +01:00
|
|
|
c.Now = time.Now().In(l)
|
|
|
|
|
2021-11-14 03:53:13 +01:00
|
|
|
for k, v := range c.Box {
|
2021-11-14 05:21:22 +01:00
|
|
|
v.Name = k
|
2022-04-16 14:57:45 +02:00
|
|
|
v.online = false
|
2021-11-14 08:58:47 +01:00
|
|
|
v.zfs = NewZFSConfig()
|
2021-11-14 03:53:13 +01:00
|
|
|
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(),
|
2022-04-16 15:09:06 +02:00
|
|
|
Timeout: 5 * time.Second,
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
}
|
2022-04-16 14:57:45 +02:00
|
|
|
} 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
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
|
2022-04-16 14:57:45 +02:00
|
|
|
var b bytes.Buffer
|
|
|
|
session.Stdout = &b
|
2021-11-14 03:53:13 +01:00
|
|
|
|
2022-04-16 14:57:45 +02:00
|
|
|
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
|
|
|
|
}
|
2021-11-14 03:53:13 +01:00
|
|
|
if *debugFlag {
|
2022-04-16 14:57:45 +02:00
|
|
|
log.Printf("Config.Load : logged into %s : %s", k, b.String())
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
2022-04-16 14:57:45 +02:00
|
|
|
session.Close()
|
|
|
|
s.logged = true
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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))
|
|
|
|
}
|
2022-04-16 14:57:45 +02:00
|
|
|
if !cfg.Box[src.Box()].online {
|
2022-06-17 14:54:14 +02:00
|
|
|
email.items = append(email.items, fmt.Sprintf("Source box offline for app : %s", app.Name))
|
2022-04-16 14:57:45 +02:00
|
|
|
}
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
2022-04-16 14:57:45 +02:00
|
|
|
var allOffline bool = true
|
2021-11-14 03:53:13 +01:00
|
|
|
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))
|
|
|
|
}
|
2022-04-16 14:57:45 +02:00
|
|
|
if cfg.Box[dest.Box()].online {
|
|
|
|
allOffline = false
|
|
|
|
}
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
2022-04-16 14:57:45 +02:00
|
|
|
if allOffline {
|
2022-06-17 14:54:14 +02:00
|
|
|
email.items = append(email.items, fmt.Sprintf("No online destination box for app : %s", app.Name))
|
2022-04-16 14:57:45 +02:00
|
|
|
}
|
|
|
|
|
2021-11-14 03:53:13 +01:00
|
|
|
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))
|
|
|
|
}
|
2022-04-16 15:09:06 +02:00
|
|
|
if !cfg.Box[before.Box()].online {
|
2022-06-17 14:54:14 +02:00
|
|
|
email.items = append(email.items, fmt.Sprintf("Before box offline for app : %s", app.Name))
|
2022-04-16 15:09:06 +02:00
|
|
|
}
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
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))
|
|
|
|
}
|
2022-04-16 15:09:06 +02:00
|
|
|
if !cfg.Box[after.Box()].online {
|
2022-06-17 14:54:14 +02:00
|
|
|
email.items = append(email.items, fmt.Sprintf("After box offline for app : %s", app.Name))
|
2022-04-16 15:09:06 +02:00
|
|
|
}
|
2021-11-14 03:53:13 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
//Close config
|
|
|
|
func (c *Config) Close() error {
|
|
|
|
return nil
|
|
|
|
}
|