blob: ac4d83f296017415b960c8f5f0e279c357788421 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ceph cluster info</title>
{% include 'common_styles.j2' %}
{% include 'common_scripts.j2' %}
<style>
table.cluster_nodes {
width: 100%;
margin-left: 1%;
margin-right: 1%;
}
.barcontent {
margin: auto;
width: 1350px;
padding: 10px;
}
.bar-centered {
float: none;
transform: translate(25%);
}
/* Node rows*/
.node {
font-family: "LaoSangamMN", Monaco, monospace;
font-size: 0.8em;
display: flex;
background-color: white;
align-items: center;
}
.collapsable {
font-family: "LaoSangamMN", Monaco, monospace;
font-size: 0.8em;
display: none;
background-color: white;
visibility: hidden;
}
.collapsable.in {
visibility: visible;
display: inline-block;
}
.row_button {
background-color: #468;
color: #fff;
cursor: pointer;
padding: 5px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 13px;
}
.row_button:after {
content: '\02795'; /* Unicode character for "plus" sign (+) */
font-size: 13px;
color: white;
float: left;
margin-left: 5px;
}
.row_active:after {
content: "\2796"; /* Unicode character for "minus" sign (-) */
color: white
}
.row_active, .row_button:hover {
background-color: #68a;
color: white
}
.cell_button {
color: darkgreen;
cursor: pointer;
padding: 5px;
width: 100%;
border: none;
text-align: center;
outline: none;
}
.cell_button:hover {
background-color: gray;
}
.row_content {
font-family: "LaoSangamMN", Monaco, monospace;
font-size: 0.8em;
padding: 0 18px;
background-color: white;
max-height: 0;
overflow: hidden;
transition: max-height 0.2s ease-out;
border-width: 1px;
border-color: #68a;
border-style: solid;
}
div.services > .collapsable.in {
display: table-row;
}
tr:nth-child(even) {
background-color: #eee;
}
tr:nth-child(odd) {
background-color: #fff;
}
tr.node > td, tr.collapsable > td {
display: block;
float: left;
padding: 1px;
margin: 2px;
}
td > .osd_group {
display: grid;
grid-template-columns: 40px 25px 25px 70px;
padding-left: 0px;
padding-right: 0px;
margin: 1px;
}
td > .pg_group {
display: grid;
grid-template-columns: 50px 40px 60px 65px 60px 65px 65px;;
padding-left: 0px;
padding-right: 0px;
margin: 1px;
}
td > .bench_group {
display: grid;
grid-template-columns: 80px 80px 75px 75px;
padding-left: 0px;
padding-right: 0px;
margin: 1px;
}
td > .meta_group {
display: inline-block;
grid-template-columns: repeat(4, auto);
padding-left: 0px;
padding-right: 0px;
margin: 1px;
}
td > .osdconf_group {
display: inline-block;
grid-template-columns: repeat(3, auto);
padding-left: 0px;
padding-right: 0px;
margin: 1px;
}
.item {
display: inline-grid;
border-width: 1px;
border-style: solid;
margin: 1px 1px 1px 1px;
padding: 0px 1px 0px 1px;
}
.spacer { border-radius: 2px; width: 20px;}
.status { border-radius: 10px; width: 120px; text-align: center;}
.health_ok { background-color: #393; color: white;}
.health_error { background-color: #933; color: white;}
.health_warn { background-color: #eb3; color: #333;}
.checks_code { border-radius: 2px; width: 20%; background-color: transparent; color: darkred;}
.head { height: 18px; background-color: transparent; border-color: transparent; border: 0px;}
.centered { text-align: center;}
.right { text-align: right;}
.col_shortmessage { min-width: 300px; }
.col_longmessage { width: auto; }
.col_properties { width: auto;}
.srv_name { width: 300px }
.srv_path { width: 250px }
.srv_timestamp { width: 250px }
.srv_addr { width: 450px }
.id { width: 30px }
.bucket_name { width: 365px }
.bucket_type { width: 50px }
.bucket_params { width: 200px }
.bucket_items { width: 630px }
.df_name { width: 300px }
.df_total { width: 150px }
.df_avail { width: 150px }
.df_used { width: 150px }
.df_used_raw { width: 150px }
.df_used_raw_rate { width: 150px }
.rdf_name { width: 200px; }
.rdf_obj { width: 75px; }
.rdf_total { width: 100px; }
.rdf_used { width: 100px; }
.rdf_bench { width: 100px; }
.dev_name { width: 300px; }
.dev_param { width: 100px; }
.mon_name { width: 100px }
.mon_url { width: 500px }
.meters {
display: inline-block;
margin: 1px;
}
.meters > .meter {
display: block;
float: left;
border-width: 1px;
border-style: solid;
margin: 0px 1px 0px 1px;
padding: 0px 1px 0px 1px;
}
.meters > .warn {
border-color: #d3a200;
background-color: rgb(255, 216, 133);
}
.meters > .fail {
border-color: #bb0000;
background-color: rgb(250, 135, 135);
}
.osd { border-color: #a0c0a0; background-color: rgb(252, 248, 248); text-align: center;}
.pg { border-color: #c0c0a0; background-color: rgb(255, 255, 251); text-align: right; }
.bench { border-color: #a0c0c0; background-color: rgb(255, 250, 250); text-align: right; }
.lat_commit { border-color: #a0c0c0; background-color: rgb(255, 250, 250); text-align: right; width: 45px}
.lat_apply { border-color: #a0c0c0; background-color: rgb(255, 250, 250); text-align: left; width: 35px}
.meta_name { border-color: #c4b890; background-color: #e7dbb6; text-align: left; width: 150px;}
.meta_value { border-color: #c6c3ba;background-color: #d4d4d4; text-align: left; width: 480px;}
.conf_name { border-color: #c4b890; background-color: #e7dbb6; text-align: left; width: 295px; word-break: break-all;}
.conf_value { border-color: #c6c3ba;background-color: #d4d4d4; text-align: left; width: 280px; word-break: break-all;}
.conf_source { border-color: #c6c3ba;background-color: #a4a4a4; text-align: left; width: 50px;}
.map_grid {
display: grid;
grid-template-columns: auto auto auto auto auto auto auto auto auto auto;
grid-column-gap: 20px;
padding-left: 0px;
padding-right: 0px;
margin: 1px;
margin-left: 20px;
}
.map_item {
display: inline-grid;
border-width: 0px;
border-style: solid;
margin: 1px 1px 1px 1px;
padding: 0px 1px 0px 1px;
}
.map_grid > .ok {
color: #80a080;
}
.map_grid > .warn {
color: #d3a200;
}
.map_grid > .fail {
color: #bb0000;
}
.modules {
font-family: "LaoSangamMN", Monaco, monospace;
font-size: 0.8em;
background-color: white;
}
.module_node {
margin-bottom: 2px;
display: flex;
}
.module_name, .node_name {
text-align: center;
border-width: 0px;
border-style: solid;
margin: 1px 1px 1px 1px;
padding: 0px 1px 0px 1px;
min-width: 250px;
border-radius: 10px;
}
.node_name {
background-color: #ddd;
}
.module_grid {
display: grid;
grid-template-columns: repeat(8, 100px);
grid-template-rows: repeat(6, auto);
grid-auto-flow: column;
grid-column-gap: 10px;
padding-left: 0px;
padding-right: 0px;
margin: 1px;
margin-left: 20px;
}
.module {
display: inline-grid;
text-align: center;
border-width: 0px;
border-style: solid;
margin: 1px 1px 1px 1px;
padding: 0px 1px 0px 1px;
min-width: 100px;
border-radius: 10px;
}
.module_grid > .on, .service_node > .ok {
background-color: #8c8;
}
.module_grid > .off, .service_node > .off{
background-color: #9aa;
}
.module_grid > .fail, .service_node > .fail {
background-color: #a33;
}
.module_grid > .always, .service_node > .fail {
background-color: #282;
}
.tooltiptext {
transform: translate(100px);
}
.console {
background-color: black;
font-family: "Lucida Console", Monaco, monospace;
font-size: 0.5em;
width: auto;
color: #fff;
border-radius: 6px;
padding: 5px 5px;
}
</style>
</head>
<body onload="init()">
<div class="header">
<div class="label">Ceph version:</div>
<div class="text">{{ ceph_version }}</div>
<div class="label">Image:</div>
<div class="text">{{ cluster.image }}</div>
<div class="label date">generated on: {{ gen_date }}</div>
</div>
<div class="bar">
<div class="bar-centered">
<button class="bar-item" onclick="openBar(event, 'status')">Status</button>
<button class="bar-item" onclick="openBar(event, 'latency')">Latency</button>
<button class="bar-item" onclick="openBar(event, 'crush')">CRUSH Map</button>
<button class="bar-item" onclick="openBar(event, 'osdconf')">OSD Conf</button>
<button class="bar-item" onclick="openBar(event, 'mondump')">Monitors</button>
<button class="bar-item" onclick="openBar(event, 'df')">Pools</button>
<button class="bar-item" onclick="openBar(event, 'dfrados')">Rados</button>
<button class="bar-item" onclick="openBar(event, 'auth')">Auth list</button>
<button class="bar-item" onclick="openBar(event, 'dhealth')">Device Health</button>
</div>
</div>
{% macro status_page(info, id_label) %}
<div id="{{ id_label }}" class="barcontent">
<h5>{{ caller() }}</h5>
<hr>
<table class="ceph_status">
<tr class="node">
<td class="status">Cluster status</td>
<td class="col_shortmessage">Status summary</td>
<td class="col_osd">
<div class="osd_group">
<div class="item osd">OSDs</div>
<div class="item osd">Up</div>
<div class="item osd">In</div>
<div class="item osd">Remap PGs</div>
</div>
</td>
<td class="col_pgs">
<div class="pg_group">
<div class="item pg">PGs</div>
<div class="item pg">Pools</div>
<div class="item pg">Objects</div>
<div class="item pg">Data, GB</div>
<div class="item pg">Used, GB</div>
<div class="item pg">Avail, GB</div>
<div class="item pg">Total, GB</div>
</div>
</td>
<td class="col_bench">
<div class="bench_group">
<div class="item bench">Read, MB/sec</div>
<div class="item bench">Write, MB/sec</div>
<div class="item bench">Read, op/sec</div>
<div class="item bench">Write, op/sec</div>
</div>
</td>
</tr>
{% set hdetail = info["health_detail"]["data"] %}
{% set cs = info["cluster_status"]["data"] %}
{% set osdmap = cs | get_osdmap %}
<tr class="node" onclick="toggleClassByID('health_data')" id="health_data_button">
<td class="status {{ hdetail["status"] | lower }}">{{ hdetail["status"] }}</td>
<td class="col_shortmessage">
{% for code,dt in hdetail["checks"].items() %}
{{ dt["summary"]["message"] }}<br>
{% endfor %}
</td>
<!-- background: linear-gradient(to right, gray 0% 20%, transparent 20% 100%); -->
<td class="col_osd">
<div class="osd_group">
<div class="item osd">{{ osdmap["num_osds"] }}</div>
<div class="item osd">{{ osdmap["num_up_osds"] }}</div>
<div class="item osd">{{ osdmap["num_in_osds"] }}</div>
<div class="item osd">{{ osdmap["num_remapped_pgs"] }}</div>
</div>
</td>
{% set pgmap = cs["pgmap"] %}
<td class="col_pgs">
<div class="pg_group">
<div class="item pg">{{ pgmap["num_pgs"] }}</div>
<div class="item pg">{{ pgmap["num_pools"] }}</div>
<div class="item pg">{{ pgmap["num_objects"] }}</div>
<div class="item pg">{{ pgmap["data_bytes"] | to_gb }}</div>
<div class="item pg">{{ pgmap["bytes_used"] | to_gb }}</div>
<div class="item pg">{{ pgmap["bytes_avail"] | to_gb }}</div>
<div class="item pg">{{ pgmap["bytes_total"] | to_gb }}</div>
</div>
</td>
<td class="col_bench">
<div class="bench_group">
{% if "read_bytes_sec" in pgmap %}
<div class="item bench">{{ pgmap["read_bytes_sec"] | to_mb }}</div>
{% else %}
<div class="item bench">0</div>
{% endif %}
{% if "write_bytes_sec" in pgmap %}
<div class="item bench">{{ pgmap["write_bytes_sec"] | to_mb }}</div>
{% else %}
<div class="item bench">0</div>
{% endif %}
{% if "read_op_per_sec" in pgmap %}
<div class="item bench">{{ pgmap["read_op_per_sec"] }}</div>
{% else %}
<div class="item bench">0</div>
{% endif %}
{% if "write_op_per_sec" in pgmap %}
<div class="item bench">{{ pgmap["write_op_per_sec"] }}</div>
{% else %}
<div class="item bench">0</div>
{% endif %}
</div>
</td>
</tr>
<tr class="collapsable in" id="health_data"><td colspan=3>
<table><tbody>
{% for code,dt in hdetail["checks"].items() %}
<tr>
<td class="spacer"></td>
<td class="status {{ dt["severity"] | lower }}">{{ dt["severity"] }}</td>
<td class="checks_code">{{ code }}</td>
<td class="col_longmessage">
<table><tbody>
{% for detail in dt["detail"] %}
<tr><td>{{ detail["message"] }}</td></tr>
{% endfor %}
</tbody></table>
</td>
</tr>
{% endfor %}
</tbody></table>
</td></tr>
</table>
<hr>
<!-- Services -->
{% set sm = info["cluster_status"]["data"]["servicemap"] %}
<h5>Services: {{ sm["services"] | count }} running. Last modification: {{ sm["modified"] }}</h5>
<table class="ceph_status">
<tr class="node">
<td class="srv_name">Name</td>
<td class="srv_path">Subpath</td>
<td class="srv_timestamp">Start time</td>
<td class="srv_addr">Address</td>
</tr>
{% for name, d1 in sm["services"].items() %}
{% if "daemons" in d1 %}
{% set d2 = d1["daemons"] %}
{% for key, d3 in d2.items() %}
{% if key.startswith("rgw.store") %}
<tr class="node" onclick="toggleClassByID('{{ name }}_service_data')" id="{{ name }}_service_data_button">
<td class="srv_name">{{ name }} ({{ d3["gid"] }})</td>
<td class="srv_path">daemons:{{ key }}</td>
<td class="srv_timestamp">{{ d3["start_stamp"] }}</td>
<td class="srv_addr">{{ d3["addr"] }}</td>
</tr>
<tr class="collapsable in" id="{{ name}}_service_data"><td colspan=4>
<table><tbody>
<tr><td class="metadata">
{% for mname, mvalue in d3["metadata"].items() %}
<div class="meta_group">
<div class="item meta_name">{{ mname }}</div>
<div class="item meta_value">{{ mvalue }}</div>
</div>
{% endfor %}
</td></tr>
</tbody></table>
</td></tr>
{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
</table>
<hr>
<!-- Modules -->
{% set mgrmap = info["cluster_status"]["data"]["mgrmap"] %}
{% set mods = mgrmap["modules"] %}
{% set avail = mgrmap["available_modules"] %}
{% if "always_on_modules" in mgrmap %}
{% set always_on = mgrmap["always_on_modules"].values() | list %}
{% set always_on = always_on[0] %}
{% else %}
{% set always_on = [] %}
{% endif %}
<h5>Modules: {{ mods | count}} active. {{ always_on | count }} always on. {{ avail | count }} available.</h5>
<div class="modules">
<div class="module_grid">
{% for mod in avail %}
{% if mod["name"] in always_on %}
<div class="module always">{{ mod["name"] }}</div>
{% elif mod["name"] in mods %}
<div class="module on">{{ mod["name"] }}</div>
{% elif not mod["can_run"] %}
<div class="module fail tooltip">
<div class="module fail">{{ mod["name"] }}</div>
<pre class="tooltiptext">{{ mod["error_string"] | linebreaks }}</pre>
</div>
{% else %}
<div class="module">{{ mod["name"] }}</div>
{% endif %}
{% endfor %}
</div>
</div>
<hr>
</div>
{% endmacro %}
<!-- CRUSH MAP -->
{% macro crush_page(info, id_label) %}
<div id="{{ id_label }}" class="barcontent">
<h5>{{ caller() }}</h5>
<hr>
{% set cmap = info["crushmap_json"]["data"] %}
<button type="button" class="row_button">{{ cmap["tunables"] | length }} tunable parameters</button>
<div class="row_content">
<table class="ceph_status"><tbody>
<tr><td class="metadata">
{% for tname, tvalue in cmap["tunables"].items() %}
<div class="meta_group">
<div class="item meta_name">{{ tname }}</div>
<div class="item meta_value">{{ tvalue }}</div>
</div>
{% endfor %}
</td></tr>
</tbody></table>
</div>
<button type="button" class="row_button">{{ cmap["devices"] | length }} devices</button>
<div class="row_content">
<table class="ceph_status"><tbody>
<tr><td class="metadata">
{% for dev in cmap["devices"] %}
<div class="meta_group">
<div class="item meta_name">{{ dev["name"] }}</div>
<div class="item meta_value">id: {{ dev["id"] }}, class: {{ dev["class"] }}</div>
</div>
{% endfor %}
</td></tr>
</tbody></table>
</div>
<button type="button" class="row_button">{{ cmap["types"] | length }} types</button>
<div class="row_content">
<table class="ceph_status"><tbody>
<tr><td class="metadata">
{% for dtyp in cmap["types"] %}
<div class="meta_group">
<div class="item meta_name">type_id: {{ dtyp["type_id"] }}</div>
<div class="item meta_value">{{ dtyp["name"] }}</div>
</div>
{% endfor %}
</td></tr>
</tbody></table>
</div>
<button type="button" class="row_button">{{ cmap["buckets"] | length }} buckets</button>
<div class="row_content">
<table class="ceph_status"><tbody>
<tr class="node">
<td class="id">ID</td>
<td class="bucket_name">Bucket name</td>
<td class="bucket_type">Type</td>
<td class="bucket_params">Weight, algorithm, hash</td>
<td class="bucket_items">Items</td>
</tr>
{% for buck in cmap["buckets"] %}
<tr class="node">
<td class="id">{{ buck["id"] }}</td>
<td class="bucket_name">{{ buck["name"] }}</td>
<td class="bucket_type">{{ buck["type_name"] }}</td>
<td class="bucket_params">{{ buck["weight"] }}, {{ buck["alg"] }}, {{ buck["hash"] }}</td>
<td class="bucket_items">
{% for bitem in buck["items"] %}
{{ bitem["pos"] }}: {{ bitem["id"] | get_bucket_item_name(cmap) }}, weight {{ bitem["weight"] }}<br>
{% endfor %}
</td>
</tr>
{% endfor %}
</td></tr>
</tbody></table>
</div>
<button type="button" class="row_button">{{ cmap["rules"] | length }} rules</button>
<div class="row_content">
<table class="ceph_status"><tbody>
<tr class="node">
<td class="id">ID</td>
<td class="bucket_name">Rule name</td>
<td class="bucket_type">Type</td>
<td class="bucket_params">Min/Max Size</td>
<td class="bucket_items">Steps</td>
</tr>
{% for rule in cmap["rules"] %}
<tr class="node">
<td class="id">{{ rule["rule_id"] }}</td>
<td class="bucket_name">{{ rule["rule_name"] }}</td>
<td class="bucket_type">{{ rule["type"] }}</td>
<td class="bucket_params">{{ rule["min_size"] }}/{{ rule["max_size"] }}</td>
<td class="bucket_items">
{% for step in rule["steps"] | get_rule_steps %}
{{ step }}<br>
{% endfor %}
</td>
</tr>
{% endfor %}
</td></tr>
</tbody></table>
</div>
</div>
{% endmacro %}
<!-- OSD Configuration -->
{% macro osdconf_page(info, id_label) %}
<div id="{{ id_label }}" class="barcontent">
<h5>{{ caller() }}</h5>
<hr>
{% set cbase = info["osd_config_data"]["data"]["common"] %}
{% set cuniq = info["osd_config_data"]["data"]["uniq"] %}
<button type="button" class="row_button">{{ cbase | length }} common configuration values</button>
<div class="row_content">
<table class="ceph_status"><tbody>
<tr><td class="metadata">
{% for tname, tdata in cbase.items() %}
<div class="osdconf_group">
<div class="item conf_name">{{ tname }}</div>
<div class="item conf_value">{{ tdata["value"] }}</div>
<div class="item conf_source">{{ tdata["source"] }}</div>
</div>
{% endfor %}
</td></tr>
</tbody></table>
</div>
{% for osdname in cuniq.keys() | sort %}
<button type="button" class="row_button">{{ osdname }}: {{ cuniq[osdname] | length }} uniq values</button>
<div class="row_content">
<table class="ceph_status"><tbody>
<tr><td class="metadata">
{% for tname, tdata in cuniq[osdname].items() %}
<div class="osdconf_group">
<div class="item conf_name">{{ tname }}</div>
<div class="item conf_value">{{ tdata["value"] }}</div>
<div class="item conf_source">{{ tdata["source"] }}</div>
</div>
{% endfor %}
</td></tr>
</tbody></table>
</div>
{% endfor %}
</div>
{% endmacro %}
<!-- Latency -->
{% macro latency_page(lat, id_label) %}
<div id="{{ id_label }}" class="barcontent">
{% set lat = info["osd_latency_data"]["data"] %}
{% set ldelay = lat["delay"] %}
{% set ltotal = lat["total"] %}
{% set ldata = lat["data"] %}
{% set ltable = info["osd_latency_data"]["table"] %}
<h5>{{ caller() }}: {{ ltotal }} iterations, {{ ldelay }} delay between iterations</h5>
<hr>
<table class="ceph_status">
{% for osd, llist in ltable.items() %}
<tr class="node">
{% if osd == "<dev>" %}
<td class="status">ODS node (ms)</td>
<td class="col_latency">
<div class="meters">
{% for ii in llist %}
<div class="meter lat_commit">Commit</div>
<div class="meter lat_apply">Apply</div>
{% endfor %}
</div>
</td>
{% else %}
<td class="status">{{ osd }}</td>
<td class="col_latency">
<div class="meters">
{% for ii in llist %}
<div class="meter lat_commit">{{ ii["commit_latency_ms"] }}</div>
<div class="meter lat_apply">{{ ii["apply_latency_ms"] }}</div>
{% endfor %}
</div>
</td>
{% endif %}
</tr>
{% endfor %}
</table>
</div>
{% endmacro %}
<!-- Mon Dump -->
{% macro mondump_page(mondump, id_label) %}
<div id="{{ id_label }}" class="barcontent">
{% set mons = info["monmap"]["data"] %}
<h5>{{ caller() }} : v{{ mons["min_mon_release"] }}/{{ mons["min_mon_release_name"] }} and higher</h5>
<div class="note">Persistent: {{ mons["features"]["persistent"] | join(", ") }}</div>
{% if mons["features"]["optional"] | length > 0 %}
<div class="note">Optional: {{ mons["features"]["optional"] | join(", ") }}</div>
{% else %}
<div class="note">Optional: no</div>
{% endif %}
<hr>
<table class="ceph_status">
<tr class="node">
<td class="id centered">Rank</td>
<td class="mon_name">Name</td>
<td class="mon_url">Address</td>
<td class="mon_url">Public address</td>
</tr>
{% for mon in mons["mons"] %}
<tr class="node">
<td class="id centered">{{ mon["rank"] }}</td>
<td class="mon_name">{{ mon["name"] }}</td>
<td class="mon_url">{{ mon["addr"] }}</td>
<td class="mon_url">{{ mon["public_addr"] }}</td>
</tr>
{% endfor %}
</table>
</div>
{% endmacro %}
<!-- DF -->
{% macro df_page(info, id_label) %}
<div id="{{ id_label }}" class="barcontent">
{% set df = info["ceph_df"]["data"] %}
<h5>{{ caller() }}</h5>
<div class="note">{{ df["stats"]["num_osds"] }} OSD nodes, {{ df["stats"]["num_per_pool_osds"] }} per pool</div>
<hr>
<table class="ceph_status">
<tr class="node">
<td class="df_name">Scope</td>
<td class="df_total right">Total, GB</td>
<td class="df_avail right">Available, GB</td>
<td class="df_used right">Used, GB</td>
<td class="df_used_raw right">Used raw, GB</td>
<td class="df_used_raw_rate right">Raw ratio</td>
</tr>
<tr class="node">
<td class="df_name">All</td>
<td class="df_total right">{{ df["stats"]["total_bytes"] | to_gb }}</td>
<td class="df_avail right">{{ df["stats"]["total_avail_bytes"] | to_gb }}</td>
<td class="df_used right">{{ df["stats"]["total_used_bytes"] | to_gb }}</td>
<td class="df_used_raw right">{{ df["stats"]["total_used_raw_bytes"] | to_gb }}</td>
<td class="df_used_raw_rate right">{{ "%0.4f" | format(df["stats"]["total_used_raw_ratio"]|float) }}</td>
</tr>
{% for class, stat in df["stats_by_class"].items() %}
<tr class="node">
<td class="df_name">{{ class }}</td>
<td class="df_total right">{{ stat["total_bytes"] | to_gb }}</td>
<td class="df_avail right">{{ stat["total_avail_bytes"] | to_gb }}</td>
<td class="df_used right">{{ stat["total_used_bytes"] | to_gb }}</td>
<td class="df_used_raw right">{{ stat["total_used_raw_bytes"] | to_gb }}</td>
<td class="df_used_raw_rate right">{{ "%0.4f" | format(stat["total_used_raw_ratio"]|float) }}</td>
</tr>
{% endfor %}
</table>
<hr>
<table class="ceph_status">
<tr class="node">
<td class="id centered">ID</td>
<td class="df_name">Name</td>
<td class="df_total centered">Objects</td>
<td class="df_avail right">Stored, GB</td>
<td class="df_used right">Used, GB</td>
<td class="df_used_raw centered">Used, %</td>
<td class="df_used_raw_rate right">Max Available, GB</td>
<td class="df_total centered">Placement Groups</td>
</tr>
{% for pool in df["pools"] %}
{% set pool_stats = pool["id"] | get_pool_stats(info["ceph_pg_dump"]["data"]) %}
<tr class="node">
<td class="id centered">{{ pool["id"] }}</td>
<td class="df_name">{{ pool["name"] }}</td>
<td class="df_total centered">{{ pool["stats"]["objects"] }}</td>
<td class="df_avail right">{{ pool["stats"]["stored"] | to_gb }}</td>
<td class="df_used right">{{ pool["stats"]["bytes_used"] | to_gb }}</td>
<td class="df_used_raw centered">{{ "%0.2f" | format(pool["stats"]["percent_used"]|float) }}</td>
<td class="df_used_raw_rate right">{{ pool["stats"]["max_avail"] | to_gb }}</td>
<td class="df_total centered">{{ pool_stats["num_pg"] }}</td>
</tr>
{% endfor %}
</table>
</div>
{% endmacro %}
<!-- RADOS DF -->
{% macro dfrados_page(info, id_label) %}
<div id="{{ id_label }}" class="barcontent">
{% set rdf = info["rados_df"]["data"] %}
<h5>{{ caller() }}</h5>
<hr>
<table class="ceph_status">
<tr class="node">
<td class="df_name">Stats</td>
<td class="df_total right">Total objects</td>
<td class="df_avail right">Total used, GB</td>
<td class="df_used right">Total Available, GB</td>
<td class="df_used_raw right">Total space, GB</td>
</tr>
<tr class="node">
<td class="df_name">Rados DF</td>
<td class="df_total right">{{ rdf["total_objects"] }}</td>
<td class="df_avail right">{{ rdf["total_used"] | to_gb }}</td>
<td class="df_used right">{{ rdf["total_avail"] | to_gb }}</td>
<td class="df_used_raw right">{{ rdf["total_space"] | to_gb }}</td>
</tr>
</table>
<hr>
<table class="ceph_status">
<tr class="node">
<td class="id centered">ID</td>
<td class="rdf_name">Name</td>
<td class="rdf_obj centered">Objects</td>
<td class="rdf_obj centered">Clones</td>
<td class="rdf_obj centered">Copies</td>
<td class="rdf_obj centered">Unfound</td>
<td class="rdf_obj centered">Degraded</td>
<td class="rdf_obj centered">Missing</td>
<td class="rdf_used right">Size, GB</td>
<td class="rdf_used right">Compressed, GB</td>
<td class="rdf_used right">Real, GB</td>
<td class="rdf_bench right">Read, MB/s</td>
<td class="rdf_bench right">Read, IOPS</td>
<td class="rdf_bench right">Write, MB/s</td>
<td class="rdf_bench right">Write, IOPS</td>
</tr>
{% for pool in rdf["pools"] | sort(attribute='id') %}
<tr class="node">
<td class="id centered">{{ pool["id"] }}</td>
<td class="rdf_name">{{ pool["name"] }}</td>
<td class="rdf_obj centered">{{ pool["num_objects"] }}</td>
<td class="rdf_obj centered">{{ pool["num_object_clones"] }}</td>
<td class="rdf_obj centered">{{ pool["num_object_copies"] }}</td>
<td class="rdf_obj centered">{{ pool["num_objects_unfound"] }}</td>
<td class="rdf_obj centered">{{ pool["num_objects_degraded"] }}</td>
<td class="rdf_obj centered">{{ pool["num_objects_missing_on_primary"] }}</td>
<td class="rdf_total right">{{ pool["size_bytes"] | to_gb }}</td>
<td class="rdf_used right">{{ pool["compress_bytes_used"] | to_gb }}</td>
<td class="rdf_used right">{{ pool["compress_under_bytes"] | to_gb }}</td>
<td class="rdf_bench right">{{ pool["read_bytes"] | to_mb }}</td>
<td class="rdf_bench right">{{ pool["read_ops"] }}</td>
<td class="rdf_bench right">{{ pool["write_bytes"] | to_mb }}</td>
<td class="rdf_bench right">{{ pool["write_ops"] }}</td>
</tr>
{% endfor %}
</table>
<hr>
</div>
{% endmacro %}
<!-- Auth ls -->
{% macro auth_page(info, id_label) %}
<div id="{{ id_label }}" class="barcontent">
{% set auth = info["ceph_auth_ls"]["data"] %}
<h5>{{ caller() }}</h5>
<hr>
<table class="ceph_status">
<tr class="node">
<td class="bucket_name">Entity</td>
<td class="bucket_items">Caps</td>
</tr>
{% for ath in auth["auth_dump"] | sort(attribute='entity') %}
<tr class="node">
<td class="bucket_name">{{ ath["entity"] }}</td>
<td class="bucket_items">
{% for scope, value in ath["caps"].items() %}
{{ scope }}: {{ value }}<br>
{% endfor %}
</td>
</tr>
{% endfor %}
</table>
<hr>
</div>
{% endmacro %}
<!-- Device Health -->
{% macro dhealth_page(info, id_label) %}
<div id="{{ id_label }}" class="barcontent">
{% set dh = info["ceph_health"]["latest"] %}
<h5>{{ caller() }}: {{ dh | count }} devices</h5>
<div class="note">Data collection timestamp is '{{ info["ceph_health"]['date'] }}'</div>
<hr>
<table class="ceph_status">
<tr class="node">
<td class="dev_name">Name</td>
<td class="dev_param centered">Device</td>
<td class="dev_param centered">Protocol</td>
<td class="dev_param centered">Firmware</td>
<td class="dev_param centered">Speed<div class='note'>current/max</div></td>
<td class="dev_param centered">Block size<div class='note'>physical/logical</div></td>
<td class="dev_param centered">Power cycles</td>
<td class="dev_param centered">Temperature</td>
<td class="dev_param centered">Smart Status</td>
<td class="dev_param centered">Smart data</td>
</tr>
{% for _d, _p in dh.items() | sort(attribute='0') if _p %}
<tr class="node">
<td class="dev_name">
<div class="text">{{ _p['model_name'] }}, {{ _p['serial_number'] }}</div><br>
{% if "model_family" in _p %}
<div class="note">{{ _p['model_family'] }}</div>
{% endif %}
<div class="note">{{ _p['node_name'] }}:{{ _p['osd_name'] }}</div>
</td>
<td class="dev_param centered">{{ _p['device']['info_name'] }}</td>
<td class="dev_param centered">{{ _p['device']['protocol'] }}</td>
<td class="dev_param centered">{{ _p['firmware_version'] }}</td>
{% if "interface_speed" in _p %}
<td class="dev_param centered">{{ _p['interface_speed']['current']['string'] }} / {{ _p['interface_speed']['max']['string'] }}</td>
{% else %}
<td class="dev_param centered">- / -</td>
{% endif%}
{% if "physical_block_size" in _p %}
<td class="dev_param centered">{{ _p['physical_block_size'] }} / {{ _p['logical_block_size'] }}</td>
{% else %}
<td class="dev_param centered">- / {{ _p['logical_block_size'] }}</td>
{% endif %}
<td class="dev_param centered">{{ _p['power_cycle_count'] }}</td>
<td class="dev_param centered">{{ _p['temperature']['current'] }}</td>
{% if _p['smart_status']['passed'] %}
<td class="dev_param centered" style="color: green">Passed</td>
{% else %}
<td class="dev_param centered" style="color: red;">Failed</td>
{% endif %}
<td class="dev_param centered">
<div class="cell_button" onclick="toggleClassByID('{{ _d }}_smart_output')" id="{{ _d }}_smart_output_button">Show/Hide</div>
</td>
</tr>
<tr class="collapsable" id="{{ _d }}_smart_output"><td colspan=10>
<div class="console">
<pre>
{% for line in _p['smartctl']['output'] %}
{{ line }}
{% endfor %}
</pre>
</div>
</td></tr>
{% endfor %}
</table>
<hr>
</div>
{% endmacro %}
<!-- ================================= -->
<!-- Cluster nodes page -->
{% call status_page(info, "status") %}
Cluster status
{% endcall %}
{% call crush_page(info, "crush") %}
CRUSH map
{% endcall %}
{% call osdconf_page(info, "osdconf") %}
OSD configs
{% endcall %}
{% call latency_page(info['osd_latency_data'], "latency") %}
Quick latency check for all OSDs
{% endcall %}
{% call mondump_page(info['monmap'], "mondump") %}
Ceph monitors
{% endcall %}
{% call df_page(info, "df") %}
Pool list with additional data
{% endcall %}
{% call dfrados_page(info, "dfrados") %}
Rados pools list with additional details
{% endcall %}
{% call auth_page(info, "auth") %}
Anonymized auth list
{% endcall %}
{% call dhealth_page(info, "dhealth") %}
Device health status and S.M.A.R.T. outputs
{% endcall %}
</body>
</html>