--- /dev/null
+{% extends "base.html" %}
+
+{% block content %}
+
+<script>
+ $(document).ready(function(){
+ // Pre-populated initial data at page load
+ var content_data = {{ content_data }};
+
+ var refresh = function() {
+ $.get("{{ url_prefix }}/filesystem_data/" + content_data.fs_status.filesystem.id + "/", function(data) {
+ _.extend(content_data.fs_status, data);
+ setTimeout(refresh, 5000);
+ });
+ };
+
+ // Generate a "width: xx%" style string for use with a progress
+ // bar element showing a pool's used space.
+ rivets.formatters.pool_size_bar = function(pool){
+ var ratio = pool.used / pool.avail;
+
+ return "width: " + Math.round(ratio * 100).toString() + "%";
+ };
+
+ rivets.bind($("div#content"), content_data.fs_status);
+ setTimeout(refresh, 5000);
+
+ // Convert ceph-mgr's time series format (list of 2-tuples
+ // with seconds-since-epoch timestamps) into what chart.js
+ // can handle (list of objects with millisecs-since-epoch
+ // timestamps)
+ var convert_timeseries = function(source_series)
+ {
+ var data = [];
+ _.each(source_series, function(dp) {
+ data.push({
+ x: dp[0] * 1000,
+ y: dp[1]
+ });
+ });
+
+ return data;
+ };
+
+ var delta_timeseries = function(source_series)
+ {
+ var i;
+ var prev = source_series[0];
+ var result = [];
+ for (i = 1; i < source_series.length; i++) {
+ var cur = source_series[i];
+ var tdelta = cur[0] - prev[0];
+ var vdelta = cur[1] - prev[1];
+ var rate = vdelta / tdelta;
+
+ result.push({
+ x: cur[0] * 1000,
+ y: rate
+ });
+
+ prev = cur;
+ }
+ return result;
+ };
+
+ var charts = {};
+
+ var lhs_counter = 'mds.inodes';
+ var lhs_transform = convert_timeseries;
+ var rhs_counter = "mds_server.handle_client_request";
+ var rhs_transform = delta_timeseries;
+
+ var draw_chart = function() {
+ $.get("{{ url_prefix }}/mds_counters/" + content_data.fs_status.filesystem.id + "/", function(data) {
+ var top_chart = true;
+
+ // Cull any chart elements that correspond to MDSs no
+ // longer present in the data
+ var existing_mds_names = {};
+ _.each(data, function(mds_data, mds_name) {
+ existing_mds_names[mds_name] = true;
+ });
+
+ $("#mds_charts canvas").each(function(i, e) {
+ var el_mds_name = e.id.replace("mds_chart_", "");
+ if (existing_mds_names[el_mds_name] != true) {
+ e.remove();
+ }
+ });
+
+
+ _.each(data, function(mds_data, mds_name) {
+ if (! $("#mds_chart_" + mds_name).length) {
+ // Construct new chart
+ $("#mds_charts").append(
+ $("<canvas height='64' id='mds_chart_" + mds_name + "'></canvas>")
+ );
+
+ var ctx = $("#mds_chart_" + mds_name);
+ var lhs_data = lhs_transform(mds_data[lhs_counter]);
+ var rhs_data = rhs_transform(mds_data[rhs_counter]);
+
+ var chartInstance = new Chart(ctx, {
+ type: 'line',
+ data: {
+ datasets: [
+ {
+ label: lhs_counter,
+ yAxisID: 'LHS',
+ data: lhs_data,
+ tension: 0.1,
+ borderColor: "#dd2222",
+
+ },
+ {
+ label: rhs_counter,
+ yAxisID: 'RHS',
+ data: rhs_data,
+ tension: 0.1,
+ borderColor: "#2222dd",
+ },
+ ]
+ },
+ options: {
+ legend: {
+ position: 'top',
+ display: top_chart,
+ labels:{fontColor: "#ddd"}
+ },
+ scales: {
+ xAxes: [{
+ position: 'top',
+ type: 'time',
+ display: top_chart,
+ ticks: {fontColor:"#ddd"},
+ time: {
+ displayFormats: {
+ quarter: 'MMM YYYY'
+ }
+ }
+ }],
+ yAxes: [{
+ id: 'LHS',
+ type: 'linear',
+ position: 'left',
+ ticks: {fontColor:"#ddd"},
+ min: 0
+ }, {
+ id: 'RHS',
+ type: 'linear',
+ position: 'right',
+ ticks: {fontColor:"#ddd"},
+ min: 0
+ }]
+ }
+ }
+ });
+ charts[mds_name] = chartInstance;
+ } else {
+ // Update existing chart
+ var chart = charts[mds_name];
+ var lhs_data = lhs_transform(mds_data[lhs_counter]);
+ var rhs_data = rhs_transform(mds_data[rhs_counter]);
+ chart.data.datasets[0].data = lhs_data;
+ chart.data.datasets[1].data = rhs_data;
+ chart.update(0);
+ };
+
+ // FIXME: update the visibility of axes etc
+ // when charts come and go.
+ top_chart = false;
+ });
+
+ setTimeout(draw_chart, 5000);
+ });
+ };
+
+ // TODO periodic refresh
+ draw_chart();
+
+ });
+</script>
+
+
+<section class="content-header">
+ <h1>
+ Filesystem {filesystem.name}
+ </h1>
+</section>
+
+<section class="content">
+ <p style="font-size: 1.3em;">
+ <i class="fa fa-desktop"></i> Clients: <span style="font-weight:bold;">{filesystem.client_count}</span>
+ <a rv-href="filesystem.clients_url">Detail...</a>
+ </p>
+
+ <div class="row">
+ <div class="col-sm-6">
+ <div class="box">
+ <div class="box-header">
+ <h3 class="box-title">Ranks</h3>
+ </div>
+ <div class="box-body">
+ <table class="table table-condensed">
+ <thead>
+ <tr>
+ <th>Rank</th>
+ <th>State</th>
+ <th>Daemon</th>
+ <th>Activity</th>
+ <th>Dentries</th>
+ <th>Inodes</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr rv-each-rank="filesystem.ranks">
+ <td>{rank.rank}</td>
+ <td>{rank.state}</td>
+ <td>{rank.mds}</td>
+ <td>{rank.activity}</td>
+ <td>{rank.dns | dimless}</td>
+ <td>{rank.inos | dimless}</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+ <div class="col-sm-6">
+ <div class="box">
+ <div class="box-header">
+ <h3 class="box-title">Pools</h3>
+ </div>
+ <div class="box-body">
+ <table class="table table-condensed">
+ <thead>
+ <tr>
+ <th>Pool</th>
+ <th>Type</th>
+ <th>Used</th>
+ <th>Avail</th>
+ <th></th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr rv-each-pool="filesystem.pools">
+ <td>{pool.pool}</td>
+ <td>{pool.type}</td>
+ <td>{pool.used | dimless}</td>
+ <td>{pool.avail | dimless}</td>
+ <td>
+ <div class="progress"
+ style="width: 100px; height: 20px;">
+ <div class="progress-bar progress-bar-red"
+ rv-style="pool | pool_size_bar"></div>
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+ </div>
+
+ </div>
+ <div class="box">
+ <div class="box-header">
+ <h3 class="box-title">Standby daemons</h3>
+ </div>
+ <div class="box-body">
+ <table>
+ <thead>
+ <tr rv-show="standbys | length">
+ <th>Daemon</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr rv-each-standby="standbys">
+ <td>{standby.name}</td>
+ </tr>
+ <tr class="ceph-none-found" rv-hide="standbys | length">
+ <td>None found</td>
+ </tr>
+ </tbody>
+ </table>
+ </div>
+ </div>
+
+ <div id="mds_charts" style="color: #ddd;">
+ </div>
+
+</section>
+<!-- /.content -->
+
+{% endblock %}