From 7e26567b8b1add1451e2dfee130953a933b7ccdb Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 23 Nov 2014 12:05:43 +0100 Subject: [PATCH] Pretty status for backup --- cmd/khepri/cmd_backup.go | 73 ++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/cmd/khepri/cmd_backup.go b/cmd/khepri/cmd_backup.go index 82cab39aa..97b50243f 100644 --- a/cmd/khepri/cmd_backup.go +++ b/cmd/khepri/cmd_backup.go @@ -17,18 +17,30 @@ func format_bytes(c uint64) string { switch { case c > 1<<40: - return fmt.Sprintf("%.3f TiB", b/(1<<40)) + return fmt.Sprintf("%.3fTiB", b/(1<<40)) case c > 1<<30: - return fmt.Sprintf("%.3f GiB", b/(1<<30)) + return fmt.Sprintf("%.3fGiB", b/(1<<30)) case c > 1<<20: - return fmt.Sprintf("%.3f MiB", b/(1<<20)) + return fmt.Sprintf("%.3fMiB", b/(1<<20)) case c > 1<<10: - return fmt.Sprintf("%.3f KiB", b/(1<<10)) + return fmt.Sprintf("%.3fKiB", b/(1<<10)) default: - return fmt.Sprintf("%d B", c) + return fmt.Sprintf("%dB", c) } } +func format_duration(sec uint64) string { + hours := sec / 3600 + sec -= hours * 3600 + min := sec / 60 + sec -= min * 60 + if hours > 0 { + return fmt.Sprintf("%d:%02d:%02d", hours, min, sec) + } + + return fmt.Sprintf("%d:%02d", min, sec) +} + func print_tree2(indent int, t *khepri.Tree) { for _, node := range *t { if node.Tree != nil { @@ -60,7 +72,7 @@ func commandBackup(be backend.Server, key *khepri.Key, args []string) error { fmt.Printf("scanning %s\n", target) if terminal.IsTerminal(int(os.Stdout.Fd())) { - ch := make(chan khepri.Stats, 5) + ch := make(chan khepri.Stats, 20) arch.ScannerStats = ch go func(ch <-chan khepri.Stats) { @@ -86,27 +98,52 @@ func commandBackup(be backend.Server, key *khepri.Key, args []string) error { fmt.Printf("\r%6d directories, %6d files, %14s\n", arch.Stats.Directories, arch.Stats.Files, format_bytes(arch.Stats.Bytes)) stats := khepri.Stats{} + start := time.Now() if terminal.IsTerminal(int(os.Stdout.Fd())) { - ch := make(chan khepri.Stats, 5) + ch := make(chan khepri.Stats, 20) arch.SaveStats = ch - go func(ch <-chan khepri.Stats) { - for s := range ch { - stats.Files += s.Files - stats.Directories += s.Directories - stats.Other += s.Other - stats.Bytes += s.Bytes + ticker := time.NewTicker(time.Second) + var eta, bps uint64 - fmt.Printf("\r%3.2f%% %d/%d directories, %d/%d files, %s/%s", + go func(ch <-chan khepri.Stats) { + + status := func(d time.Duration) { + fmt.Printf("\r[%s] %3.2f%% %s/s %s / %s ETA %s", + format_duration(uint64(d/time.Second)), float64(stats.Bytes)/float64(arch.Stats.Bytes)*100, - stats.Directories, arch.Stats.Directories, - stats.Files, arch.Stats.Files, - format_bytes(stats.Bytes), format_bytes(arch.Stats.Bytes)) + format_bytes(bps), + format_bytes(stats.Bytes), format_bytes(arch.Stats.Bytes), + format_duration(eta)) + } + + defer ticker.Stop() + for { + select { + case s, ok := <-ch: + if !ok { + return + } + stats.Files += s.Files + stats.Directories += s.Directories + stats.Other += s.Other + stats.Bytes += s.Bytes + + status(time.Since(start)) + case <-ticker.C: + d := time.Since(start) + bps = stats.Bytes * uint64(time.Second) / uint64(d) + + if bps > 0 { + eta = (arch.Stats.Bytes - stats.Bytes) / bps + } + + status(d) + } } }(ch) } - start := time.Now() sn, id, err := arch.Snapshot(target, t) if err != nil { fmt.Fprintf(os.Stderr, "error: %v\n", err)