diff --git a/cluster/cluster.go b/cluster/cluster.go index aef0c5a07..5107cbc57 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -172,6 +172,7 @@ type Cluster struct { tunnel *ssh.Client `json:"-"` QueryRules map[uint32]config.QueryRule `json:"-"` Backups []v3.Backup `json:"-"` + BackupStat v3.BackupStat `json:"backupStat"` SLAHistory []state.Sla `json:"slaHistory"` APIUsers map[string]APIUser `json:"apiUsers"` Schedule map[string]cron.Entry `json:"-"` diff --git a/cluster/cluster_bck.go b/cluster/cluster_bck.go index a3a0b8c87..ac6fe43b1 100644 --- a/cluster/cluster_bck.go +++ b/cluster/cluster_bck.go @@ -183,7 +183,60 @@ func (cluster *Cluster) ResticFetchRepo() error { } } cluster.Backups = filterRepo + + cluster.ResticFetchRepoStat() + } + + return nil +} + +func (cluster *Cluster) ResticFetchRepoStat() error { + + // defer func() { cluster.canResticFetchRepo = true }() + // if cluster.Conf.BackupRestic && cluster.canResticFetchRepo { + cluster.canResticFetchRepo = false + // var stdout, stderr []byte + var stdoutBuf, stderrBuf bytes.Buffer + var errStdout, errStderr error + resticcmd := exec.Command(cluster.Conf.BackupResticBinaryPath, "stats", "--mode", "raw-data", "--json") + stdoutIn, _ := resticcmd.StdoutPipe() + stderrIn, _ := resticcmd.StderrPipe() + stdout := io.MultiWriter(os.Stdout, &stdoutBuf) + stderr := io.MultiWriter(os.Stderr, &stderrBuf) + + resticcmd.Env = cluster.ResticGetEnv() + if err := resticcmd.Start(); err != nil { + cluster.SetState("WARN0094", state.State{ErrType: "WARNING", ErrDesc: fmt.Sprintf(clusterError["WARN0094"], resticcmd.Path, err, ""), ErrFrom: "BACKUP"}) + return err + } + var wg sync.WaitGroup + wg.Add(1) + go func() { + _, errStdout = io.Copy(stdout, stdoutIn) + wg.Done() + }() + + _, errStderr = io.Copy(stderr, stderrIn) + wg.Wait() + + err := resticcmd.Wait() + if err != nil { + cluster.StateMachine.AddState("WARN0093", state.State{ErrType: "WARNING", ErrDesc: fmt.Sprintf(clusterError["WARN0093"], err, string(stdoutBuf.Bytes()), string(stderrBuf.Bytes())), ErrFrom: "CHECK"}) + cluster.ResticInitRepo() + return err + } + if errStdout != nil || errStderr != nil { + return errors.New("failed to capture stdout or stderr\n") + } + + var repostat v3.BackupStat + err = json.Unmarshal(stdoutBuf.Bytes(), &repostat) + if err != nil { + cluster.LogModulePrintf(cluster.Conf.Verbose, config.ConstLogModGeneral, config.LvlInfo, "Error unmarshal backups %s", err) + return err } + cluster.BackupStat = repostat + // } return nil } diff --git a/repmanv3/messages.pb.go b/repmanv3/messages.pb.go index 2d55ddf6f..034329a9e 100644 --- a/repmanv3/messages.pb.go +++ b/repmanv3/messages.pb.go @@ -1461,6 +1461,12 @@ func (x *Certificate) GetAuthority() string { return "" } +type BackupStat struct { + TotalSize int64 `protobuf:"varint,1,opt,name=total_size,proto3" json:"total_size"` + TotalFileCount int64 `protobuf:"varint,2,opt,name=total_file_count,proto3" json:"total_file_count"` + TotalBlobCount int64 `protobuf:"varint,3,opt,name=total_blob_count,proto3" json:"total_blob_count"` +} + type Backup struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache diff --git a/share/dashboard/app/dashboard.js b/share/dashboard/app/dashboard.js index c346117b0..9e21573d8 100644 --- a/share/dashboard/app/dashboard.js +++ b/share/dashboard/app/dashboard.js @@ -240,6 +240,11 @@ app.controller('DashboardController', function ( { id: '6', name: 'SAT' }, ]; + $scope.humanFileSize = function(size) { + var i = size == 0 ? 0 : Math.floor(Math.log(size) / Math.log(1024)); + return +((size / Math.pow(1024, i)).toFixed(2)) * 1 + ' ' + ['B', 'kB', 'MB', 'GB', 'TB'][i]; +} + var getClusterUrl = function () { return AppService.getClusterUrl($scope.selectedClusterName); }; diff --git a/share/dashboard/static/tab-cluster-backups.html b/share/dashboard/static/tab-cluster-backups.html index fe316963f..3eefecc6e 100644 --- a/share/dashboard/static/tab-cluster-backups.html +++ b/share/dashboard/static/tab-cluster-backups.html @@ -1,24 +1,49 @@ - - - -

Backups

-
- - - - - - - - - - - - - - - -
IdTimePathHostnameTags
{{bck.short_id}}{{bck.time}}{{bck.paths[0]}}{{bck.hostname}}{{bck.tags.join(', ')}}
-
-
+ + +

Backups

+
+ + + + + + + + + + + + + + + + + +
Total SizeTotal File CountTotal Blob Count
{{humanFileSize(selectedCluster.backupStat.total_size)}}{{selectedCluster.backupStat.total_file_count}}{{selectedCluster.backupStat.total_blob_count}}
+
+ + + + + + + + + + + + + + + + + + + + + +
IdTimePathHostnameTags
{{bck.short_id}}{{bck.time}}{{bck.paths[0]}}{{bck.hostname}}{{bck.tags.join(', ')}}
+
+
+ \ No newline at end of file