Use SSH to list most recent snapshot age.

This commit is contained in:
Bill Thiede 2017-10-09 20:29:01 -07:00
parent f08671aee9
commit 10ab91179b

View File

@ -1,18 +1,19 @@
package main package main
import ( import (
"bufio"
"bytes"
"flag" "flag"
"fmt" "fmt"
"io" "io/ioutil"
"log"
"os" "os"
"strings" "path/filepath"
"regexp"
"time" "time"
"golang.org/x/crypto/ssh" "github.com/golang/glog"
"xinu.tv/thumbnailer/auth" "golang.org/x/crypto/ssh"
"xinu.tv/thumbnailer/scp"
) )
var ( var (
@ -20,51 +21,54 @@ var (
user = flag.String("user", os.Getenv("USER"), "ssh user") user = flag.String("user", os.Getenv("USER"), "ssh user")
) )
func checkFatal(err error) { // Example: @auto-20171001.1400-2w
if err != nil { var (
log.Fatal(err) snapshotPattern = regexp.MustCompile(`^([^@]+)@auto-(\d{8}\.\d{4})-\d+[mwy]$`)
} snapshotFormat = "20060102.1504"
} )
func progress(w io.Writer, r io.Reader, size int64) error { func newPublicKey() ([]ssh.AuthMethod, error) {
const ( var signers []ssh.AuthMethod
chunkSize = 1 << 10
width = 50
)
var err error
total, bLast := int64(0), int64(0)
tLast := time.Now()
defer fmt.Println()
for {
var n int64
n, err = io.CopyN(w, r, chunkSize)
total += n
bLast += n
if time.Since(tLast) > (300 * time.Millisecond) {
kbps := float64(bLast) / time.Since(tLast).Seconds() / 1024
frac := int(total * width / size)
fmt.Printf("\rDownloaded: %s>%s| %.2f Kb/s", strings.Repeat("=", frac),
strings.Repeat(" ", width-frac), kbps)
tLast = time.Now()
bLast = 0
}
for _, path := range []string{
filepath.Join(os.Getenv("HOME"), ".ssh", "id_dsa"),
filepath.Join(os.Getenv("HOME"), ".ssh", "id_rsa"),
} {
// A public key may be used to authenticate against the remote
// server by using an unencrypted PEM-encoded private key file.
//
// If you have an encrypted private key, the crypto/x509 package
// can be used to decrypt it.
key, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
break if os.IsNotExist(err) {
continue
}
return nil, fmt.Errorf("unable to read private key %q: %v", path, err)
} }
}
if err == io.EOF {
return nil
}
return err
}
var sink, _ = os.Create(os.DevNull) // Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
return nil, fmt.Errorf("unable to parse private key %q: %v", path, err)
}
signers = append(signers, ssh.PublicKeys(signer))
}
if len(signers) == 0 {
return nil, fmt.Errorf("no public keys configured")
}
return signers, nil
}
func main() { func main() {
flag.Parse() flag.Parse()
ams, err := auth.NewPublicKey() defer glog.Flush()
checkFatal(err)
ams, err := newPublicKey()
if err != nil {
glog.Exitf("Error fetching public keys: %v", err)
}
// An SSH client is represented with a ClientConn. // An SSH client is represented with a ClientConn.
// //
@ -77,27 +81,43 @@ func main() {
HostKeyCallback: ssh.InsecureIgnoreHostKey(), HostKeyCallback: ssh.InsecureIgnoreHostKey(),
} }
client, err := ssh.Dial("tcp", *host, config) client, err := ssh.Dial("tcp", *host, config)
checkFatal(err) if err != nil {
glog.Exitf("Error dialing %q: %v", *host, err)
}
for _, fn := range flag.Args() { // Each ClientConn can support multiple interactive sessions,
log.Println("Fetching", fn) // represented by a Session.
// Each ClientConn can support multiple interactive sessions, s, err := client.NewSession()
// represented by a Session. if err != nil {
session, err := client.NewSession() glog.Exitf("Error creating new session: %v", err)
checkFatal(err) }
cmd := "/sbin/zfs list -t snapshot -H -o name -s name"
f, err := scp.ScpFrom(session, fn) b, err := s.CombinedOutput(cmd)
if err != nil { if err != nil {
log.Println(err) glog.Exitf("Error running %q: %v", cmd, err)
continue }
scanner := bufio.NewScanner(bytes.NewReader(b))
snapshotAges := map[string]time.Time{}
for scanner.Scan() {
l := scanner.Text()
m := snapshotPattern.FindStringSubmatch(l)
if len(m) == 3 {
snapshot := m[1]
t, err := time.Parse(snapshotFormat, m[2])
if err != nil {
glog.Errorf("Malformed time in snapshot %q: %v", m[2], err)
continue
}
snapshotTime := snapshotAges[snapshot]
if snapshotTime.Before(t) {
snapshotAges[snapshot] = t
}
} }
}
log.Println(f.Name, f.Perm, f.Size) if err := scanner.Err(); err != nil {
glog.Exitf("Failed to scan response: %v", err)
err = progress(sink, f, f.Size) }
checkFatal(err) for snapshot, snapshotTime := range snapshotAges {
fmt.Println(snapshot, snapshotTime, time.Since(snapshotTime))
checkFatal(f.Close())
checkFatal(session.Close())
} }
} }