-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Web Dashboard: added a page for displaying Czar status
- Loading branch information
1 parent
7170258
commit 166a21f
Showing
5 changed files
with
272 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#fwk-qserv-czar-stats-controls label { | ||
font-weight: bold; | ||
} | ||
table#fwk-qserv-czar-stats-status { | ||
margin:0; | ||
} | ||
table.fwk-qserv-czar-stats caption { | ||
caption-side: top; | ||
text-align: right; | ||
padding-top: 0; | ||
} | ||
table.fwk-qserv-czar-stats caption.updating { | ||
background-color: #ffeeba; | ||
} | ||
table.fwk-qserv-czar-stats > thead > tr > th.sticky { | ||
position:sticky; | ||
top:80px; | ||
z-index:2; | ||
} | ||
table.fwk-qserv-czar-stats tbody th, | ||
table.fwk-qserv-czar-stats tbody td { | ||
vertical-align:middle; | ||
} | ||
table.fwk-qserv-czar-stats pre { | ||
padding: 0; | ||
margin: 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
define([ | ||
'webfwk/CSSLoader', | ||
'webfwk/Fwk', | ||
'webfwk/FwkApplication', | ||
'qserv/Common', | ||
'underscore'], | ||
|
||
function(CSSLoader, | ||
Fwk, | ||
FwkApplication, | ||
Common, | ||
_) { | ||
|
||
CSSLoader.load('qserv/css/QservCzarStatistics.css'); | ||
|
||
class QservCzarStatistics extends FwkApplication { | ||
|
||
constructor(name) { | ||
super(name); | ||
} | ||
fwk_app_on_show() { | ||
console.log('show: ' + this.fwk_app_name); | ||
this.fwk_app_on_update(); | ||
} | ||
fwk_app_on_hide() { | ||
console.log('hide: ' + this.fwk_app_name); | ||
} | ||
fwk_app_on_update() { | ||
if (this.fwk_app_visible) { | ||
this._init(); | ||
if (this._prev_update_sec === undefined) { | ||
this._prev_update_sec = 0; | ||
} | ||
let now_sec = Fwk.now().sec; | ||
if (now_sec - this._prev_update_sec > this._update_interval_sec()) { | ||
this._prev_update_sec = now_sec; | ||
this._init(); | ||
this._load(); | ||
} | ||
} | ||
} | ||
static _counters = ['queryRespConcurrentSetupCount', | ||
'queryRespConcurrentWaitCount', | ||
'queryRespConcurrentProcessingCount', | ||
'numQueries', | ||
'numJobs']; | ||
static _qdisppool_columns = ['priority', 'running', 'size']; | ||
|
||
_init() { | ||
if (this._initialized === undefined) this._initialized = false; | ||
if (this._initialized) return; | ||
this._initialized = true; | ||
let html = ` | ||
<div class="row" id="fwk-qserv-czar-stats-controls"> | ||
<div class="col"> | ||
<div class="form-row"> | ||
<div class="form-group col-md-1"> | ||
${Common.html_update_ival('update-interval', 10)} | ||
</div> | ||
<div class="form-group col-md-1"> | ||
<label for="reset-form"> </label> | ||
<button id="reset-form" class="btn btn-primary form-control">Reset</button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
<div class="row"> | ||
<div class="col"> | ||
<table class="table table-sm fwk-qserv-czar-stats" id="fwk-qserv-czar-stats-status"> | ||
<caption class="updating">Loading...</caption> | ||
</table> | ||
</div> | ||
</div> | ||
<div class="row"> | ||
<div class="col"> | ||
<h4>Counters</h4> | ||
<table class="table table-sm table-hover fwk-qserv-czar-stats" id="fwk-qserv-czar-stats-counters"> | ||
<tbody>` + _.reduce(QservCzarStatistics._counters, function(html, counter) { return html + ` | ||
<tr> | ||
<td style="text-align:left" scope="row"><pre>${counter}</pre></td> | ||
<td style="text-align:left"><pre id="${counter}">Loading...</pre></td> | ||
</tr>`; }, '') + ` | ||
</tbody> | ||
</table> | ||
</div> | ||
<div class="col"> | ||
<h4>QdispPool</h4> | ||
<table class="table table-sm table-hover table-bordered fwk-qserv-czar-stats" id="fwk-qserv-czar-stats-qdisppool"> | ||
<thead class="thead-light"> | ||
<tr>` + _.reduce(QservCzarStatistics._qdisppool_columns, function(html, column) { return html + ` | ||
<th class="sticky" style="text-align:right;">${column}</th>`; }, '') + ` | ||
</tr> | ||
</thead> | ||
<tbody></tbody> | ||
</table> | ||
</div> | ||
</div> | ||
<div class="row"> | ||
<div class="col"> | ||
<h4>Timing Histograms</h4> | ||
<table class="table table-sm table-hover table-bordered fwk-qserv-czar-stats" id="fwk-qserv-czar-stats-timing"></table> | ||
</div> | ||
</div> | ||
<div class="row"> | ||
<div class="col"> | ||
<h4>Data Rates Histograms</h4> | ||
<table class="table table-sm table-hover table-bordered fwk-qserv-czar-stats" id="fwk-qserv-czar-stats-data"></table> | ||
</div> | ||
</div> | ||
`; | ||
let cont = this.fwk_app_container.html(html); | ||
cont.find(".form-control-selector").change(() => { | ||
this._load(); | ||
}); | ||
cont.find("button#reset-form").click(() => { | ||
this._set_update_interval_sec(10); | ||
this._load(); | ||
}); | ||
} | ||
_form_control(elem_type, id) { | ||
if (this._form_control_obj === undefined) this._form_control_obj = {}; | ||
if (!_.has(this._form_control_obj, id)) { | ||
this._form_control_obj[id] = this.fwk_app_container.find(elem_type + '#' + id); | ||
} | ||
return this._form_control_obj[id]; | ||
} | ||
_update_interval_sec() { return this._form_control('select', 'update-interval').val(); } | ||
_set_update_interval_sec(val) { this._form_control('select', 'update-interval').val(val); } | ||
_table(name) { | ||
if (_.isUndefined(this._table_obj)) this._table_obj = {}; | ||
if (!_.has(this._table_obj, name)) { | ||
this._table_obj[name] = this.fwk_app_container.find('table#fwk-qserv-czar-stats-' + name); | ||
} | ||
return this._table_obj[name]; | ||
} | ||
_status() { | ||
if (_.isUndefined(this._status_obj)) { | ||
this._status_obj = this._table('status').children('caption'); | ||
} | ||
return this._status_obj; | ||
} | ||
_set_counter(name, val) { | ||
if (_.isUndefined(this._counters_obj)) this._counters_obj = {}; | ||
if (!_.has(this._counters_obj, name)) { | ||
this._counters_obj[name] = this._table('counters').children('tbody').find('#' + name); | ||
} | ||
this._counters_obj[name].text(val); | ||
} | ||
_load() { | ||
if (this._loading === undefined) this._loading = false; | ||
if (this._loading) return; | ||
this._loading = true; | ||
this._status().addClass('updating'); | ||
Fwk.web_service_GET( | ||
"/replication/qserv/master/status", | ||
{version: Common.RestAPIVersion}, | ||
(data) => { | ||
if (data.success) { | ||
this._display(data.status); | ||
Fwk.setLastUpdate(this._status()); | ||
} else { | ||
console.log('request failed', this.fwk_app_name, data.error); | ||
this._status().html('<span style="color:maroon">' + data.error + '</span>'); | ||
} | ||
this._status().removeClass('updating'); | ||
this._loading = false; | ||
}, | ||
(msg) => { | ||
console.log('request failed', this.fwk_app_name, msg); | ||
this._status().html('<span style="color:maroon">No Response</span>'); | ||
this._status().removeClass('updating'); | ||
this._loading = false; | ||
} | ||
); | ||
} | ||
_display(data) { | ||
let tbody = this._table('qdisppool').children('tbody'); | ||
if (_.isEmpty(data) || _.isEmpty(data.qdisp_stats) || _.isEmpty(data.qdisp_stats.QdispPool)) { | ||
tbody.html(''); | ||
return; | ||
} | ||
let that = this; | ||
_.each(QservCzarStatistics._counters, function(counter) { | ||
that._set_counter(counter, data.qdisp_stats[counter]); | ||
}); | ||
let html = ''; | ||
_.each(data.qdisp_stats.QdispPool, function (row) { | ||
html += ` | ||
<tr>` + _.reduce(QservCzarStatistics._qdisppool_columns, function (html, column) { return html + ` | ||
<td style="text-align:right;"><pre>${row[column]}</pre></td>`; }, '') + ` | ||
</tr>`; | ||
}); | ||
this._table('qdisppool').children('tbody').html(html); | ||
|
||
// Locate and display histograms nested in the top-level objects | ||
this._table('timing').html(this._htmlgen_histograms( | ||
_.reduce(data.qdisp_stats, function (histograms, e) { | ||
if (_.isObject(e) && _.has(e, 'HistogramId')) histograms.push(e); | ||
return histograms; | ||
}, []) | ||
)); | ||
this._table('data').html(this._htmlgen_histograms( | ||
_.reduce(data.transmit_stats, function (histograms, e) { | ||
if (_.isObject(e) && _.has(e, 'HistogramId')) histograms.push(e); | ||
return histograms; | ||
}, []) | ||
)); | ||
} | ||
_htmlgen_histograms(histograms) { | ||
return _.reduce(histograms, function (html, histogram) { | ||
if (html == '') { | ||
let idx = 0; | ||
html = ` | ||
<thead class="thead-light"> | ||
<tr> | ||
<th class="sticky" style="text-align:left;">id</th> | ||
<th class="sticky" style="text-align:right;">total</th> | ||
<th class="sticky" style="text-align:right;">totalCount</th> | ||
<th class="sticky" style="text-align:right;">avg</th>` + _.reduce(histogram.buckets, function (html, bucket) { return html + ` | ||
<th class="sticky" style="text-align:right;">${(idx++) == 0 ? "≤ " : ""}${bucket.maxVal}</th>`; }, '') + ` | ||
</tr> | ||
</thead> | ||
<tbody>`; | ||
|
||
} | ||
html += ` | ||
<tr> | ||
<th style="text-align:left;">${histogram.HistogramId}</th> | ||
<td style="text-align:right;"><pre>${histogram.total.toFixed(3)}</pre></td> | ||
<td style="text-align:right;"><pre>${histogram.totalCount}</pre></td> | ||
<td style="text-align:right;"><pre>${histogram.avg.toFixed(3)}</pre></td>` + _.reduce(histogram.buckets, function (html, bucket) { return html + ` | ||
<td style="text-align:right;"><pre>${bucket.count}</pre></td>`; }, '') + ` | ||
</tr>`; | ||
return html; | ||
}, '') + ` | ||
</tbody>`; | ||
} | ||
} | ||
return QservCzarStatistics; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters