Show current repository state (fixes #89)

This commit is contained in:
Jakob Borg 2014-04-14 09:58:17 +02:00
parent 5064f846fc
commit 48bfc2d9ed
6 changed files with 87 additions and 7 deletions

File diff suppressed because one or more lines are too long

View File

@ -88,6 +88,8 @@ func restGetModel(m *Model, w http.ResponseWriter, params martini.Params) {
res["inSyncFiles"], res["inSyncBytes"] = globalFiles-needFiles, globalBytes-needBytes res["inSyncFiles"], res["inSyncBytes"] = globalFiles-needFiles, globalBytes-needBytes
res["state"] = m.State(repo)
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(res) json.NewEncoder(w).Encode(res)
} }

View File

@ -20,11 +20,21 @@ import (
"github.com/calmh/syncthing/scanner" "github.com/calmh/syncthing/scanner"
) )
type repoState int
const (
RepoIdle repoState = iota
RepoScanning
RepoSyncing
RepoCleaning
)
type Model struct { type Model struct {
repoDirs map[string]string // repo -> dir repoDirs map[string]string // repo -> dir
repoFiles map[string]*files.Set // repo -> files repoFiles map[string]*files.Set // repo -> files
repoNodes map[string][]string // repo -> nodeIDs repoNodes map[string][]string // repo -> nodeIDs
nodeRepos map[string][]string // nodeID -> repos nodeRepos map[string][]string // nodeID -> repos
repoState map[string]repoState // repo -> state
rmut sync.RWMutex // protects the above rmut sync.RWMutex // protects the above
cm *cid.Map cm *cid.Map
@ -54,6 +64,7 @@ func NewModel(maxChangeBw int) *Model {
repoFiles: make(map[string]*files.Set), repoFiles: make(map[string]*files.Set),
repoNodes: make(map[string][]string), repoNodes: make(map[string][]string),
nodeRepos: make(map[string][]string), nodeRepos: make(map[string][]string),
repoState: make(map[string]repoState),
cm: cid.NewMap(), cm: cid.NewMap(),
protoConn: make(map[string]protocol.Connection), protoConn: make(map[string]protocol.Connection),
rawConn: make(map[string]io.Closer), rawConn: make(map[string]io.Closer),
@ -536,14 +547,20 @@ func (m *Model) AddRepo(id, dir string, nodes []NodeConfiguration) {
func (m *Model) ScanRepos() { func (m *Model) ScanRepos() {
m.rmut.RLock() m.rmut.RLock()
var repos = make([]string, 0, len(m.repoDirs))
for repo := range m.repoDirs { for repo := range m.repoDirs {
m.ScanRepo(repo) repos = append(repos, repo)
} }
m.rmut.RUnlock() m.rmut.RUnlock()
for _, repo := range repos {
m.ScanRepo(repo)
}
} }
func (m *Model) ScanRepo(repo string) { func (m *Model) ScanRepo(repo string) {
sup := &suppressor{threshold: int64(cfg.Options.MaxChangeKbps)} sup := &suppressor{threshold: int64(cfg.Options.MaxChangeKbps)}
m.rmut.Lock()
w := &scanner.Walker{ w := &scanner.Walker{
Dir: m.repoDirs[repo], Dir: m.repoDirs[repo],
IgnoreFile: ".stignore", IgnoreFile: ".stignore",
@ -552,8 +569,11 @@ func (m *Model) ScanRepo(repo string) {
Suppressor: sup, Suppressor: sup,
CurrentFiler: cFiler{m, repo}, CurrentFiler: cFiler{m, repo},
} }
m.rmut.Unlock()
m.setState(repo, RepoScanning)
fs, _ := w.Walk() fs, _ := w.Walk()
m.ReplaceLocal(repo, fs) m.ReplaceLocal(repo, fs)
m.setState(repo, RepoIdle)
} }
func (m *Model) SaveIndexes(dir string) { func (m *Model) SaveIndexes(dir string) {
@ -647,3 +667,27 @@ func (m *Model) clusterConfig(node string) protocol.ClusterConfigMessage {
return cm return cm
} }
func (m *Model) setState(repo string, state repoState) {
m.rmut.Lock()
m.repoState[repo] = state
m.rmut.Unlock()
}
func (m *Model) State(repo string) string {
m.rmut.Lock()
state := m.repoState[repo]
m.rmut.Unlock()
switch state {
case RepoIdle:
return "idle"
case RepoScanning:
return "scanning"
case RepoCleaning:
return "cleaning"
case RepoSyncing:
return "syncing"
default:
return "unknown"
}
}

View File

@ -127,11 +127,13 @@ func (p *puller) run() {
for { for {
select { select {
case res := <-p.requestResults: case res := <-p.requestResults:
p.model.setState(p.repo, RepoSyncing)
changed = true changed = true
p.requestSlots <- true p.requestSlots <- true
p.handleRequestResult(res) p.handleRequestResult(res)
case b := <-p.blocks: case b := <-p.blocks:
p.model.setState(p.repo, RepoSyncing)
changed = true changed = true
p.handleBlock(b) p.handleBlock(b)
@ -155,10 +157,13 @@ func (p *puller) run() {
} }
if changed { if changed {
p.model.setState(p.repo, RepoCleaning)
p.fixupDirectories() p.fixupDirectories()
changed = false changed = false
} }
p.model.setState(p.repo, RepoIdle)
// Do a rescan if it's time for it // Do a rescan if it's time for it
select { select {
case <-walkTicker: case <-walkTicker:

View File

@ -108,6 +108,36 @@ syncthing.controller('SyncthingCtrl', function ($scope, $http) {
}); });
}; };
$scope.repoStatus = function (repo) {
if (typeof $scope.model[repo] === 'undefined') {
return 'Unknown';
}
var state = '' + $scope.model[repo].state;
state = state[0].toUpperCase() + state.substr(1);
if (state == "Syncing" || state == "Idle") {
state += " (" + $scope.syncPercentage(repo) + "%)";
}
return state;
}
$scope.repoClass = function (repo) {
if (typeof $scope.model[repo] === 'undefined') {
return 'text-info';
}
var state = '' + $scope.model[repo].state;
if (state == 'idle') {
return 'text-success';
}
if (state == 'syncing') {
return 'text-primary';
}
return 'text-info';
}
$scope.syncPercentage = function (repo) { $scope.syncPercentage = function (repo) {
if (typeof $scope.model[repo] === 'undefined') { if (typeof $scope.model[repo] === 'undefined') {
return 100; return 100;

View File

@ -137,9 +137,8 @@
<span class="data">{{repo.ID}}</span> <span class="data">{{repo.ID}}</span>
</div> </div>
<div class="li-column"> <div class="li-column">
<span class="text-muted glyphicon glyphicon-home"></span> <span class="text-muted glyphicon glyphicon-comment"></span>
<span class="data text-success" ng-show="syncPercentage(repo.ID) == 100">In Sync</span> <span class="data" ng-class="repoClass(repo.ID)">{{repoStatus(repo.ID)}}</span>
<span class="data text-primary" ng-hide="syncPercentage(repo.ID) == 100">Syncing ({{syncPercentage(repo.ID)}}%)</span>
</div> </div>
</li> </li>
<li> <li>
@ -191,7 +190,7 @@
<span class="data">{{nodeAddr(nodeCfg)}}</span> <span class="data">{{nodeAddr(nodeCfg)}}</span>
</div> </div>
<div class="li-column"> <div class="li-column">
<span class="text-muted glyphicon glyphicon-dashboard"></span> <span class="text-muted glyphicon glyphicon-comment"></span>
<span class="data text-{{nodeClass(nodeCfg)}}">{{nodeStatus(nodeCfg)}}</span> <span class="data text-{{nodeClass(nodeCfg)}}">{{nodeStatus(nodeCfg)}}</span>
</div> </div>
</li> </li>