blob: ac4d83f296017415b960c8f5f0e279c357788421 [file] [log] [blame]
Alexdcb792f2021-10-04 14:24:21 -05001<!DOCTYPE html>
2<html lang="en">
3<head>
4 <meta charset="UTF-8">
5 <title>Ceph cluster info</title>
6 {% include 'common_styles.j2' %}
7 {% include 'common_scripts.j2' %}
8 <style>
9 table.cluster_nodes {
10 width: 100%;
11 margin-left: 1%;
12 margin-right: 1%;
13 }
14 .barcontent {
15 margin: auto;
16 width: 1350px;
17 padding: 10px;
18 }
19 .bar-centered {
20 float: none;
21 transform: translate(25%);
22 }
23
24 /* Node rows*/
25 .node {
26 font-family: "LaoSangamMN", Monaco, monospace;
27 font-size: 0.8em;
28 display: flex;
29 background-color: white;
30 align-items: center;
31 }
32 .collapsable {
33 font-family: "LaoSangamMN", Monaco, monospace;
34 font-size: 0.8em;
35 display: none;
36 background-color: white;
37 visibility: hidden;
38 }
39 .collapsable.in {
40 visibility: visible;
41 display: inline-block;
42 }
43
44 .row_button {
45 background-color: #468;
46 color: #fff;
47 cursor: pointer;
48 padding: 5px;
49 width: 100%;
50 border: none;
51 text-align: left;
52 outline: none;
53 font-size: 13px;
54 }
55 .row_button:after {
56 content: '\02795'; /* Unicode character for "plus" sign (+) */
57 font-size: 13px;
58 color: white;
59 float: left;
60 margin-left: 5px;
61 }
62
63 .row_active:after {
64 content: "\2796"; /* Unicode character for "minus" sign (-) */
65 color: white
66 }
67
68 .row_active, .row_button:hover {
69 background-color: #68a;
70 color: white
71 }
72
73 .cell_button {
74 color: darkgreen;
75 cursor: pointer;
76 padding: 5px;
77 width: 100%;
78 border: none;
79 text-align: center;
80 outline: none;
81 }
82 .cell_button:hover {
83 background-color: gray;
84 }
85
86 .row_content {
Alex41dd0cc2022-02-09 17:33:23 -060087 font-family: "LaoSangamMN", Monaco, monospace;
88 font-size: 0.8em;
Alexdcb792f2021-10-04 14:24:21 -050089 padding: 0 18px;
90 background-color: white;
91 max-height: 0;
92 overflow: hidden;
93 transition: max-height 0.2s ease-out;
94 border-width: 1px;
95 border-color: #68a;
96 border-style: solid;
97 }
98
99 div.services > .collapsable.in {
100 display: table-row;
101 }
102 tr:nth-child(even) {
103 background-color: #eee;
104 }
105 tr:nth-child(odd) {
106 background-color: #fff;
107 }
108
109 tr.node > td, tr.collapsable > td {
110 display: block;
111 float: left;
112 padding: 1px;
113 margin: 2px;
114 }
115 td > .osd_group {
116 display: grid;
117 grid-template-columns: 40px 25px 25px 70px;
118 padding-left: 0px;
119 padding-right: 0px;
120 margin: 1px;
121 }
122 td > .pg_group {
123 display: grid;
124 grid-template-columns: 50px 40px 60px 65px 60px 65px 65px;;
125 padding-left: 0px;
126 padding-right: 0px;
127 margin: 1px;
128 }
129 td > .bench_group {
130 display: grid;
131 grid-template-columns: 80px 80px 75px 75px;
132 padding-left: 0px;
133 padding-right: 0px;
134 margin: 1px;
135 }
136 td > .meta_group {
137 display: inline-block;
138 grid-template-columns: repeat(4, auto);
139 padding-left: 0px;
140 padding-right: 0px;
141 margin: 1px;
142 }
Alex41dd0cc2022-02-09 17:33:23 -0600143 td > .osdconf_group {
144 display: inline-block;
145 grid-template-columns: repeat(3, auto);
146 padding-left: 0px;
147 padding-right: 0px;
148 margin: 1px;
149 }
Alexdcb792f2021-10-04 14:24:21 -0500150 .item {
151 display: inline-grid;
152 border-width: 1px;
153 border-style: solid;
154 margin: 1px 1px 1px 1px;
155 padding: 0px 1px 0px 1px;
156 }
157
158 .spacer { border-radius: 2px; width: 20px;}
159 .status { border-radius: 10px; width: 120px; text-align: center;}
160 .health_ok { background-color: #393; color: white;}
161 .health_error { background-color: #933; color: white;}
162 .health_warn { background-color: #eb3; color: #333;}
163 .checks_code { border-radius: 2px; width: 20%; background-color: transparent; color: darkred;}
164
165 .head { height: 18px; background-color: transparent; border-color: transparent; border: 0px;}
166 .centered { text-align: center;}
167 .right { text-align: right;}
168 .col_shortmessage { min-width: 300px; }
169 .col_longmessage { width: auto; }
170 .col_properties { width: auto;}
171
172 .srv_name { width: 300px }
173 .srv_path { width: 250px }
174 .srv_timestamp { width: 250px }
175 .srv_addr { width: 450px }
176
177 .id { width: 30px }
178 .bucket_name { width: 365px }
179 .bucket_type { width: 50px }
180 .bucket_params { width: 200px }
181 .bucket_items { width: 630px }
182
183 .df_name { width: 300px }
184 .df_total { width: 150px }
185 .df_avail { width: 150px }
186 .df_used { width: 150px }
187 .df_used_raw { width: 150px }
188 .df_used_raw_rate { width: 150px }
189
190 .rdf_name { width: 200px; }
191 .rdf_obj { width: 75px; }
192 .rdf_total { width: 100px; }
193 .rdf_used { width: 100px; }
194 .rdf_bench { width: 100px; }
195
196 .dev_name { width: 300px; }
197 .dev_param { width: 100px; }
198
199 .mon_name { width: 100px }
200 .mon_url { width: 500px }
201
202 .meters {
203 display: inline-block;
204 margin: 1px;
205 }
206 .meters > .meter {
207 display: block;
208 float: left;
209 border-width: 1px;
210 border-style: solid;
211 margin: 0px 1px 0px 1px;
212 padding: 0px 1px 0px 1px;
213
214 }
215 .meters > .warn {
216 border-color: #d3a200;
217 background-color: rgb(255, 216, 133);
218 }
219 .meters > .fail {
220 border-color: #bb0000;
221 background-color: rgb(250, 135, 135);
222 }
223 .osd { border-color: #a0c0a0; background-color: rgb(252, 248, 248); text-align: center;}
224 .pg { border-color: #c0c0a0; background-color: rgb(255, 255, 251); text-align: right; }
225 .bench { border-color: #a0c0c0; background-color: rgb(255, 250, 250); text-align: right; }
226 .lat_commit { border-color: #a0c0c0; background-color: rgb(255, 250, 250); text-align: right; width: 45px}
227 .lat_apply { border-color: #a0c0c0; background-color: rgb(255, 250, 250); text-align: left; width: 35px}
228 .meta_name { border-color: #c4b890; background-color: #e7dbb6; text-align: left; width: 150px;}
229 .meta_value { border-color: #c6c3ba;background-color: #d4d4d4; text-align: left; width: 480px;}
Alex41dd0cc2022-02-09 17:33:23 -0600230 .conf_name { border-color: #c4b890; background-color: #e7dbb6; text-align: left; width: 295px; word-break: break-all;}
231 .conf_value { border-color: #c6c3ba;background-color: #d4d4d4; text-align: left; width: 280px; word-break: break-all;}
232 .conf_source { border-color: #c6c3ba;background-color: #a4a4a4; text-align: left; width: 50px;}
Alexdcb792f2021-10-04 14:24:21 -0500233
234 .map_grid {
235 display: grid;
236 grid-template-columns: auto auto auto auto auto auto auto auto auto auto;
237 grid-column-gap: 20px;
238 padding-left: 0px;
239 padding-right: 0px;
240 margin: 1px;
241 margin-left: 20px;
242
243 }
244 .map_item {
245 display: inline-grid;
246 border-width: 0px;
247 border-style: solid;
248 margin: 1px 1px 1px 1px;
249 padding: 0px 1px 0px 1px;
250 }
251
252 .map_grid > .ok {
253 color: #80a080;
254 }
255 .map_grid > .warn {
256 color: #d3a200;
257 }
258 .map_grid > .fail {
259 color: #bb0000;
260 }
261
262 .modules {
263 font-family: "LaoSangamMN", Monaco, monospace;
264 font-size: 0.8em;
265 background-color: white;
266 }
267 .module_node {
268 margin-bottom: 2px;
269 display: flex;
270 }
271 .module_name, .node_name {
272 text-align: center;
273 border-width: 0px;
274 border-style: solid;
275 margin: 1px 1px 1px 1px;
276 padding: 0px 1px 0px 1px;
277 min-width: 250px;
278 border-radius: 10px;
279 }
280 .node_name {
281 background-color: #ddd;
282 }
283 .module_grid {
284 display: grid;
285 grid-template-columns: repeat(8, 100px);
286 grid-template-rows: repeat(6, auto);
287 grid-auto-flow: column;
288 grid-column-gap: 10px;
289 padding-left: 0px;
290 padding-right: 0px;
291 margin: 1px;
292 margin-left: 20px;
293 }
294 .module {
295 display: inline-grid;
296 text-align: center;
297 border-width: 0px;
298 border-style: solid;
299 margin: 1px 1px 1px 1px;
300 padding: 0px 1px 0px 1px;
301 min-width: 100px;
302 border-radius: 10px;
303 }
304
305 .module_grid > .on, .service_node > .ok {
306 background-color: #8c8;
307 }
308 .module_grid > .off, .service_node > .off{
309 background-color: #9aa;
310 }
311 .module_grid > .fail, .service_node > .fail {
312 background-color: #a33;
313 }
314 .module_grid > .always, .service_node > .fail {
315 background-color: #282;
316 }
317
318 .tooltiptext {
319 transform: translate(100px);
320 }
321
322 .console {
323 background-color: black;
324 font-family: "Lucida Console", Monaco, monospace;
325 font-size: 0.5em;
326 width: auto;
327 color: #fff;
328 border-radius: 6px;
329 padding: 5px 5px;
330 }
331
332 </style>
333</head>
334<body onload="init()">
335
336<div class="header">
337 <div class="label">Ceph version:</div>
338 <div class="text">{{ ceph_version }}</div>
339 <div class="label">Image:</div>
340 <div class="text">{{ cluster.image }}</div>
341 <div class="label date">generated on: {{ gen_date }}</div>
342</div>
343
344<div class="bar">
345 <div class="bar-centered">
346 <button class="bar-item" onclick="openBar(event, 'status')">Status</button>
347 <button class="bar-item" onclick="openBar(event, 'latency')">Latency</button>
348 <button class="bar-item" onclick="openBar(event, 'crush')">CRUSH Map</button>
Alex41dd0cc2022-02-09 17:33:23 -0600349 <button class="bar-item" onclick="openBar(event, 'osdconf')">OSD Conf</button>
Alexdcb792f2021-10-04 14:24:21 -0500350 <button class="bar-item" onclick="openBar(event, 'mondump')">Monitors</button>
351 <button class="bar-item" onclick="openBar(event, 'df')">Pools</button>
352 <button class="bar-item" onclick="openBar(event, 'dfrados')">Rados</button>
353 <button class="bar-item" onclick="openBar(event, 'auth')">Auth list</button>
354 <button class="bar-item" onclick="openBar(event, 'dhealth')">Device Health</button>
355 </div>
356</div>
357
358{% macro status_page(info, id_label) %}
359<div id="{{ id_label }}" class="barcontent">
360 <h5>{{ caller() }}</h5>
361 <hr>
362 <table class="ceph_status">
363 <tr class="node">
364 <td class="status">Cluster status</td>
365 <td class="col_shortmessage">Status summary</td>
366 <td class="col_osd">
367 <div class="osd_group">
368 <div class="item osd">OSDs</div>
369 <div class="item osd">Up</div>
370 <div class="item osd">In</div>
371 <div class="item osd">Remap PGs</div>
372 </div>
373 </td>
374 <td class="col_pgs">
375 <div class="pg_group">
376 <div class="item pg">PGs</div>
377 <div class="item pg">Pools</div>
378 <div class="item pg">Objects</div>
379 <div class="item pg">Data, GB</div>
380 <div class="item pg">Used, GB</div>
381 <div class="item pg">Avail, GB</div>
382 <div class="item pg">Total, GB</div>
383 </div>
384 </td>
385 <td class="col_bench">
386 <div class="bench_group">
387 <div class="item bench">Read, MB/sec</div>
388 <div class="item bench">Write, MB/sec</div>
389 <div class="item bench">Read, op/sec</div>
390 <div class="item bench">Write, op/sec</div>
391 </div>
392 </td>
393 </tr>
394 {% set hdetail = info["health_detail"]["data"] %}
395 {% set cs = info["cluster_status"]["data"] %}
396 {% set osdmap = cs | get_osdmap %}
397 <tr class="node" onclick="toggleClassByID('health_data')" id="health_data_button">
398 <td class="status {{ hdetail["status"] | lower }}">{{ hdetail["status"] }}</td>
399 <td class="col_shortmessage">
400 {% for code,dt in hdetail["checks"].items() %}
401 {{ dt["summary"]["message"] }}<br>
402 {% endfor %}
403 </td>
404 <!-- background: linear-gradient(to right, gray 0% 20%, transparent 20% 100%); -->
405 <td class="col_osd">
406 <div class="osd_group">
407 <div class="item osd">{{ osdmap["num_osds"] }}</div>
408 <div class="item osd">{{ osdmap["num_up_osds"] }}</div>
409 <div class="item osd">{{ osdmap["num_in_osds"] }}</div>
410 <div class="item osd">{{ osdmap["num_remapped_pgs"] }}</div>
411 </div>
412 </td>
413 {% set pgmap = cs["pgmap"] %}
414 <td class="col_pgs">
415 <div class="pg_group">
416 <div class="item pg">{{ pgmap["num_pgs"] }}</div>
417 <div class="item pg">{{ pgmap["num_pools"] }}</div>
418 <div class="item pg">{{ pgmap["num_objects"] }}</div>
419 <div class="item pg">{{ pgmap["data_bytes"] | to_gb }}</div>
420 <div class="item pg">{{ pgmap["bytes_used"] | to_gb }}</div>
421 <div class="item pg">{{ pgmap["bytes_avail"] | to_gb }}</div>
422 <div class="item pg">{{ pgmap["bytes_total"] | to_gb }}</div>
423 </div>
424 </td>
425 <td class="col_bench">
426 <div class="bench_group">
427 {% if "read_bytes_sec" in pgmap %}
428 <div class="item bench">{{ pgmap["read_bytes_sec"] | to_mb }}</div>
429 {% else %}
430 <div class="item bench">0</div>
431 {% endif %}
432 {% if "write_bytes_sec" in pgmap %}
433 <div class="item bench">{{ pgmap["write_bytes_sec"] | to_mb }}</div>
434 {% else %}
435 <div class="item bench">0</div>
436 {% endif %}
437 {% if "read_op_per_sec" in pgmap %}
438 <div class="item bench">{{ pgmap["read_op_per_sec"] }}</div>
439 {% else %}
440 <div class="item bench">0</div>
441 {% endif %}
442 {% if "write_op_per_sec" in pgmap %}
443 <div class="item bench">{{ pgmap["write_op_per_sec"] }}</div>
444 {% else %}
445 <div class="item bench">0</div>
446 {% endif %}
447 </div>
448 </td>
449 </tr>
450 <tr class="collapsable in" id="health_data"><td colspan=3>
451 <table><tbody>
452 {% for code,dt in hdetail["checks"].items() %}
453 <tr>
454 <td class="spacer"></td>
455 <td class="status {{ dt["severity"] | lower }}">{{ dt["severity"] }}</td>
456 <td class="checks_code">{{ code }}</td>
457 <td class="col_longmessage">
458 <table><tbody>
459 {% for detail in dt["detail"] %}
460 <tr><td>{{ detail["message"] }}</td></tr>
461 {% endfor %}
462 </tbody></table>
463 </td>
464 </tr>
465 {% endfor %}
466 </tbody></table>
467 </td></tr>
468 </table>
469 <hr>
470 <!-- Services -->
471 {% set sm = info["cluster_status"]["data"]["servicemap"] %}
472 <h5>Services: {{ sm["services"] | count }} running. Last modification: {{ sm["modified"] }}</h5>
473 <table class="ceph_status">
474 <tr class="node">
475 <td class="srv_name">Name</td>
476 <td class="srv_path">Subpath</td>
477 <td class="srv_timestamp">Start time</td>
478 <td class="srv_addr">Address</td>
479 </tr>
480 {% for name, d1 in sm["services"].items() %}
481 {% if "daemons" in d1 %}
482 {% set d2 = d1["daemons"] %}
483 {% for key, d3 in d2.items() %}
484 {% if key.startswith("rgw.store") %}
485 <tr class="node" onclick="toggleClassByID('{{ name }}_service_data')" id="{{ name }}_service_data_button">
486 <td class="srv_name">{{ name }} ({{ d3["gid"] }})</td>
487 <td class="srv_path">daemons:{{ key }}</td>
488 <td class="srv_timestamp">{{ d3["start_stamp"] }}</td>
489 <td class="srv_addr">{{ d3["addr"] }}</td>
490 </tr>
491 <tr class="collapsable in" id="{{ name}}_service_data"><td colspan=4>
492 <table><tbody>
493 <tr><td class="metadata">
494 {% for mname, mvalue in d3["metadata"].items() %}
495 <div class="meta_group">
496 <div class="item meta_name">{{ mname }}</div>
497 <div class="item meta_value">{{ mvalue }}</div>
498 </div>
499 {% endfor %}
500 </td></tr>
501 </tbody></table>
502 </td></tr>
503 {% endif %}
504 {% endfor %}
505 {% endif %}
506 {% endfor %}
507 </table>
508 <hr>
509 <!-- Modules -->
510 {% set mgrmap = info["cluster_status"]["data"]["mgrmap"] %}
511 {% set mods = mgrmap["modules"] %}
512 {% set avail = mgrmap["available_modules"] %}
513 {% if "always_on_modules" in mgrmap %}
514 {% set always_on = mgrmap["always_on_modules"].values() | list %}
515 {% set always_on = always_on[0] %}
516 {% else %}
517 {% set always_on = [] %}
518 {% endif %}
519 <h5>Modules: {{ mods | count}} active. {{ always_on | count }} always on. {{ avail | count }} available.</h5>
520 <div class="modules">
521 <div class="module_grid">
522 {% for mod in avail %}
523 {% if mod["name"] in always_on %}
524 <div class="module always">{{ mod["name"] }}</div>
525 {% elif mod["name"] in mods %}
526 <div class="module on">{{ mod["name"] }}</div>
527 {% elif not mod["can_run"] %}
528 <div class="module fail tooltip">
529 <div class="module fail">{{ mod["name"] }}</div>
530 <pre class="tooltiptext">{{ mod["error_string"] | linebreaks }}</pre>
531 </div>
532 {% else %}
533 <div class="module">{{ mod["name"] }}</div>
534 {% endif %}
535 {% endfor %}
536 </div>
537 </div>
538 <hr>
539</div>
540{% endmacro %}
541
542<!-- CRUSH MAP -->
543{% macro crush_page(info, id_label) %}
544<div id="{{ id_label }}" class="barcontent">
545 <h5>{{ caller() }}</h5>
546 <hr>
547 {% set cmap = info["crushmap_json"]["data"] %}
548 <button type="button" class="row_button">{{ cmap["tunables"] | length }} tunable parameters</button>
549 <div class="row_content">
550 <table class="ceph_status"><tbody>
551 <tr><td class="metadata">
552 {% for tname, tvalue in cmap["tunables"].items() %}
553 <div class="meta_group">
554 <div class="item meta_name">{{ tname }}</div>
555 <div class="item meta_value">{{ tvalue }}</div>
556 </div>
557 {% endfor %}
558 </td></tr>
559 </tbody></table>
560 </div>
561
562 <button type="button" class="row_button">{{ cmap["devices"] | length }} devices</button>
563 <div class="row_content">
564 <table class="ceph_status"><tbody>
565 <tr><td class="metadata">
566 {% for dev in cmap["devices"] %}
567 <div class="meta_group">
568 <div class="item meta_name">{{ dev["name"] }}</div>
569 <div class="item meta_value">id: {{ dev["id"] }}, class: {{ dev["class"] }}</div>
570 </div>
571 {% endfor %}
572 </td></tr>
573 </tbody></table>
574 </div>
575
576 <button type="button" class="row_button">{{ cmap["types"] | length }} types</button>
577 <div class="row_content">
578 <table class="ceph_status"><tbody>
579 <tr><td class="metadata">
580 {% for dtyp in cmap["types"] %}
581 <div class="meta_group">
582 <div class="item meta_name">type_id: {{ dtyp["type_id"] }}</div>
583 <div class="item meta_value">{{ dtyp["name"] }}</div>
584 </div>
585 {% endfor %}
586 </td></tr>
587 </tbody></table>
588 </div>
589
590 <button type="button" class="row_button">{{ cmap["buckets"] | length }} buckets</button>
591 <div class="row_content">
592 <table class="ceph_status"><tbody>
593 <tr class="node">
594 <td class="id">ID</td>
595 <td class="bucket_name">Bucket name</td>
596 <td class="bucket_type">Type</td>
597 <td class="bucket_params">Weight, algorithm, hash</td>
598 <td class="bucket_items">Items</td>
599 </tr>
600 {% for buck in cmap["buckets"] %}
601 <tr class="node">
602 <td class="id">{{ buck["id"] }}</td>
603 <td class="bucket_name">{{ buck["name"] }}</td>
604 <td class="bucket_type">{{ buck["type_name"] }}</td>
605 <td class="bucket_params">{{ buck["weight"] }}, {{ buck["alg"] }}, {{ buck["hash"] }}</td>
606 <td class="bucket_items">
607 {% for bitem in buck["items"] %}
608 {{ bitem["pos"] }}: {{ bitem["id"] | get_bucket_item_name(cmap) }}, weight {{ bitem["weight"] }}<br>
609 {% endfor %}
610 </td>
611 </tr>
612 {% endfor %}
613 </td></tr>
614 </tbody></table>
615 </div>
616
617 <button type="button" class="row_button">{{ cmap["rules"] | length }} rules</button>
618 <div class="row_content">
619 <table class="ceph_status"><tbody>
620 <tr class="node">
621 <td class="id">ID</td>
622 <td class="bucket_name">Rule name</td>
623 <td class="bucket_type">Type</td>
624 <td class="bucket_params">Min/Max Size</td>
625 <td class="bucket_items">Steps</td>
626 </tr>
627 {% for rule in cmap["rules"] %}
628 <tr class="node">
629 <td class="id">{{ rule["rule_id"] }}</td>
630 <td class="bucket_name">{{ rule["rule_name"] }}</td>
631 <td class="bucket_type">{{ rule["type"] }}</td>
632 <td class="bucket_params">{{ rule["min_size"] }}/{{ rule["max_size"] }}</td>
633 <td class="bucket_items">
634 {% for step in rule["steps"] | get_rule_steps %}
635 {{ step }}<br>
636 {% endfor %}
637 </td>
638 </tr>
639 {% endfor %}
640 </td></tr>
641 </tbody></table>
642 </div>
643</div>
644{% endmacro %}
645
Alex41dd0cc2022-02-09 17:33:23 -0600646<!-- OSD Configuration -->
647{% macro osdconf_page(info, id_label) %}
648<div id="{{ id_label }}" class="barcontent">
649 <h5>{{ caller() }}</h5>
650 <hr>
651 {% set cbase = info["osd_config_data"]["data"]["common"] %}
652 {% set cuniq = info["osd_config_data"]["data"]["uniq"] %}
653 <button type="button" class="row_button">{{ cbase | length }} common configuration values</button>
654 <div class="row_content">
655 <table class="ceph_status"><tbody>
656 <tr><td class="metadata">
657 {% for tname, tdata in cbase.items() %}
658 <div class="osdconf_group">
659 <div class="item conf_name">{{ tname }}</div>
660 <div class="item conf_value">{{ tdata["value"] }}</div>
661 <div class="item conf_source">{{ tdata["source"] }}</div>
662 </div>
663 {% endfor %}
664 </td></tr>
665 </tbody></table>
666 </div>
667 {% for osdname in cuniq.keys() | sort %}
668 <button type="button" class="row_button">{{ osdname }}: {{ cuniq[osdname] | length }} uniq values</button>
669 <div class="row_content">
670 <table class="ceph_status"><tbody>
671 <tr><td class="metadata">
672 {% for tname, tdata in cuniq[osdname].items() %}
673 <div class="osdconf_group">
674 <div class="item conf_name">{{ tname }}</div>
675 <div class="item conf_value">{{ tdata["value"] }}</div>
676 <div class="item conf_source">{{ tdata["source"] }}</div>
677 </div>
678 {% endfor %}
679 </td></tr>
680 </tbody></table>
681 </div>
682 {% endfor %}
683</div>
684{% endmacro %}
685
Alexdcb792f2021-10-04 14:24:21 -0500686<!-- Latency -->
687{% macro latency_page(lat, id_label) %}
688<div id="{{ id_label }}" class="barcontent">
689 {% set lat = info["osd_latency_data"]["data"] %}
690 {% set ldelay = lat["delay"] %}
691 {% set ltotal = lat["total"] %}
692 {% set ldata = lat["data"] %}
693 {% set ltable = info["osd_latency_data"]["table"] %}
694 <h5>{{ caller() }}: {{ ltotal }} iterations, {{ ldelay }} delay between iterations</h5>
695 <hr>
696 <table class="ceph_status">
697 {% for osd, llist in ltable.items() %}
698 <tr class="node">
699 {% if osd == "<dev>" %}
700 <td class="status">ODS node (ms)</td>
701 <td class="col_latency">
702 <div class="meters">
703 {% for ii in llist %}
704 <div class="meter lat_commit">Commit</div>
705 <div class="meter lat_apply">Apply</div>
706 {% endfor %}
707 </div>
708 </td>
709 {% else %}
710 <td class="status">{{ osd }}</td>
711 <td class="col_latency">
712 <div class="meters">
713 {% for ii in llist %}
714 <div class="meter lat_commit">{{ ii["commit_latency_ms"] }}</div>
715 <div class="meter lat_apply">{{ ii["apply_latency_ms"] }}</div>
716 {% endfor %}
717 </div>
718 </td>
719 {% endif %}
720 </tr>
721 {% endfor %}
722 </table>
723</div>
724{% endmacro %}
725
726<!-- Mon Dump -->
727{% macro mondump_page(mondump, id_label) %}
728<div id="{{ id_label }}" class="barcontent">
729 {% set mons = info["monmap"]["data"] %}
730 <h5>{{ caller() }} : v{{ mons["min_mon_release"] }}/{{ mons["min_mon_release_name"] }} and higher</h5>
731 <div class="note">Persistent: {{ mons["features"]["persistent"] | join(", ") }}</div>
732 {% if mons["features"]["optional"] | length > 0 %}
733 <div class="note">Optional: {{ mons["features"]["optional"] | join(", ") }}</div>
734 {% else %}
735 <div class="note">Optional: no</div>
736 {% endif %}
737 <hr>
738 <table class="ceph_status">
739 <tr class="node">
740 <td class="id centered">Rank</td>
741 <td class="mon_name">Name</td>
742 <td class="mon_url">Address</td>
743 <td class="mon_url">Public address</td>
744 </tr>
745 {% for mon in mons["mons"] %}
746 <tr class="node">
747 <td class="id centered">{{ mon["rank"] }}</td>
748 <td class="mon_name">{{ mon["name"] }}</td>
749 <td class="mon_url">{{ mon["addr"] }}</td>
750 <td class="mon_url">{{ mon["public_addr"] }}</td>
751 </tr>
752 {% endfor %}
753 </table>
754</div>
755{% endmacro %}
756
757<!-- DF -->
758{% macro df_page(info, id_label) %}
759<div id="{{ id_label }}" class="barcontent">
760 {% set df = info["ceph_df"]["data"] %}
761 <h5>{{ caller() }}</h5>
762 <div class="note">{{ df["stats"]["num_osds"] }} OSD nodes, {{ df["stats"]["num_per_pool_osds"] }} per pool</div>
763 <hr>
764 <table class="ceph_status">
765 <tr class="node">
766 <td class="df_name">Scope</td>
767 <td class="df_total right">Total, GB</td>
768 <td class="df_avail right">Available, GB</td>
769 <td class="df_used right">Used, GB</td>
770 <td class="df_used_raw right">Used raw, GB</td>
771 <td class="df_used_raw_rate right">Raw ratio</td>
772 </tr>
773 <tr class="node">
774 <td class="df_name">All</td>
775 <td class="df_total right">{{ df["stats"]["total_bytes"] | to_gb }}</td>
776 <td class="df_avail right">{{ df["stats"]["total_avail_bytes"] | to_gb }}</td>
777 <td class="df_used right">{{ df["stats"]["total_used_bytes"] | to_gb }}</td>
778 <td class="df_used_raw right">{{ df["stats"]["total_used_raw_bytes"] | to_gb }}</td>
779 <td class="df_used_raw_rate right">{{ "%0.4f" | format(df["stats"]["total_used_raw_ratio"]|float) }}</td>
780 </tr>
781 {% for class, stat in df["stats_by_class"].items() %}
782 <tr class="node">
783 <td class="df_name">{{ class }}</td>
784 <td class="df_total right">{{ stat["total_bytes"] | to_gb }}</td>
785 <td class="df_avail right">{{ stat["total_avail_bytes"] | to_gb }}</td>
786 <td class="df_used right">{{ stat["total_used_bytes"] | to_gb }}</td>
787 <td class="df_used_raw right">{{ stat["total_used_raw_bytes"] | to_gb }}</td>
788 <td class="df_used_raw_rate right">{{ "%0.4f" | format(stat["total_used_raw_ratio"]|float) }}</td>
789 </tr>
790 {% endfor %}
791 </table>
792 <hr>
793 <table class="ceph_status">
794 <tr class="node">
795 <td class="id centered">ID</td>
796 <td class="df_name">Name</td>
797 <td class="df_total centered">Objects</td>
798 <td class="df_avail right">Stored, GB</td>
799 <td class="df_used right">Used, GB</td>
800 <td class="df_used_raw centered">Used, %</td>
801 <td class="df_used_raw_rate right">Max Available, GB</td>
802 <td class="df_total centered">Placement Groups</td>
803 </tr>
804
805 {% for pool in df["pools"] %}
806 {% set pool_stats = pool["id"] | get_pool_stats(info["ceph_pg_dump"]["data"]) %}
807 <tr class="node">
808 <td class="id centered">{{ pool["id"] }}</td>
809 <td class="df_name">{{ pool["name"] }}</td>
810 <td class="df_total centered">{{ pool["stats"]["objects"] }}</td>
811 <td class="df_avail right">{{ pool["stats"]["stored"] | to_gb }}</td>
812 <td class="df_used right">{{ pool["stats"]["bytes_used"] | to_gb }}</td>
813 <td class="df_used_raw centered">{{ "%0.2f" | format(pool["stats"]["percent_used"]|float) }}</td>
814 <td class="df_used_raw_rate right">{{ pool["stats"]["max_avail"] | to_gb }}</td>
815 <td class="df_total centered">{{ pool_stats["num_pg"] }}</td>
816 </tr>
817 {% endfor %}
818 </table>
819
820</div>
821{% endmacro %}
822
823<!-- RADOS DF -->
824{% macro dfrados_page(info, id_label) %}
825<div id="{{ id_label }}" class="barcontent">
826 {% set rdf = info["rados_df"]["data"] %}
827 <h5>{{ caller() }}</h5>
828 <hr>
829 <table class="ceph_status">
830 <tr class="node">
831 <td class="df_name">Stats</td>
832 <td class="df_total right">Total objects</td>
833 <td class="df_avail right">Total used, GB</td>
834 <td class="df_used right">Total Available, GB</td>
835 <td class="df_used_raw right">Total space, GB</td>
836 </tr>
837 <tr class="node">
838 <td class="df_name">Rados DF</td>
839 <td class="df_total right">{{ rdf["total_objects"] }}</td>
840 <td class="df_avail right">{{ rdf["total_used"] | to_gb }}</td>
841 <td class="df_used right">{{ rdf["total_avail"] | to_gb }}</td>
842 <td class="df_used_raw right">{{ rdf["total_space"] | to_gb }}</td>
843 </tr>
844 </table>
845 <hr>
846 <table class="ceph_status">
847 <tr class="node">
848 <td class="id centered">ID</td>
849 <td class="rdf_name">Name</td>
850 <td class="rdf_obj centered">Objects</td>
851 <td class="rdf_obj centered">Clones</td>
852 <td class="rdf_obj centered">Copies</td>
853 <td class="rdf_obj centered">Unfound</td>
854 <td class="rdf_obj centered">Degraded</td>
855 <td class="rdf_obj centered">Missing</td>
856
857 <td class="rdf_used right">Size, GB</td>
858 <td class="rdf_used right">Compressed, GB</td>
859 <td class="rdf_used right">Real, GB</td>
860
861 <td class="rdf_bench right">Read, MB/s</td>
862 <td class="rdf_bench right">Read, IOPS</td>
863
864 <td class="rdf_bench right">Write, MB/s</td>
865 <td class="rdf_bench right">Write, IOPS</td>
866 </tr>
867 {% for pool in rdf["pools"] | sort(attribute='id') %}
868 <tr class="node">
869 <td class="id centered">{{ pool["id"] }}</td>
870 <td class="rdf_name">{{ pool["name"] }}</td>
871 <td class="rdf_obj centered">{{ pool["num_objects"] }}</td>
872 <td class="rdf_obj centered">{{ pool["num_object_clones"] }}</td>
873 <td class="rdf_obj centered">{{ pool["num_object_copies"] }}</td>
874 <td class="rdf_obj centered">{{ pool["num_objects_unfound"] }}</td>
875 <td class="rdf_obj centered">{{ pool["num_objects_degraded"] }}</td>
876 <td class="rdf_obj centered">{{ pool["num_objects_missing_on_primary"] }}</td>
877
878 <td class="rdf_total right">{{ pool["size_bytes"] | to_gb }}</td>
879 <td class="rdf_used right">{{ pool["compress_bytes_used"] | to_gb }}</td>
880 <td class="rdf_used right">{{ pool["compress_under_bytes"] | to_gb }}</td>
881
882 <td class="rdf_bench right">{{ pool["read_bytes"] | to_mb }}</td>
883 <td class="rdf_bench right">{{ pool["read_ops"] }}</td>
884
885 <td class="rdf_bench right">{{ pool["write_bytes"] | to_mb }}</td>
886 <td class="rdf_bench right">{{ pool["write_ops"] }}</td>
887 </tr>
888 {% endfor %}
889 </table>
890 <hr>
891</div>
892{% endmacro %}
893
894<!-- Auth ls -->
895{% macro auth_page(info, id_label) %}
896<div id="{{ id_label }}" class="barcontent">
897 {% set auth = info["ceph_auth_ls"]["data"] %}
898 <h5>{{ caller() }}</h5>
899 <hr>
900 <table class="ceph_status">
901 <tr class="node">
902 <td class="bucket_name">Entity</td>
903 <td class="bucket_items">Caps</td>
904 </tr>
905 {% for ath in auth["auth_dump"] | sort(attribute='entity') %}
906 <tr class="node">
907 <td class="bucket_name">{{ ath["entity"] }}</td>
908 <td class="bucket_items">
909 {% for scope, value in ath["caps"].items() %}
910 {{ scope }}: {{ value }}<br>
911 {% endfor %}
912 </td>
913 </tr>
914 {% endfor %}
915 </table>
916 <hr>
917</div>
918{% endmacro %}
919
920<!-- Device Health -->
921{% macro dhealth_page(info, id_label) %}
922<div id="{{ id_label }}" class="barcontent">
923 {% set dh = info["ceph_health"]["latest"] %}
924 <h5>{{ caller() }}: {{ dh | count }} devices</h5>
925 <div class="note">Data collection timestamp is '{{ info["ceph_health"]['date'] }}'</div>
926 <hr>
927 <table class="ceph_status">
928 <tr class="node">
929 <td class="dev_name">Name</td>
930 <td class="dev_param centered">Device</td>
931 <td class="dev_param centered">Protocol</td>
932 <td class="dev_param centered">Firmware</td>
933 <td class="dev_param centered">Speed<div class='note'>current/max</div></td>
934 <td class="dev_param centered">Block size<div class='note'>physical/logical</div></td>
935 <td class="dev_param centered">Power cycles</td>
936 <td class="dev_param centered">Temperature</td>
937 <td class="dev_param centered">Smart Status</td>
938 <td class="dev_param centered">Smart data</td>
939 </tr>
Alex90ac1532021-12-09 11:13:14 -0600940 {% for _d, _p in dh.items() | sort(attribute='0') if _p %}
Alexdcb792f2021-10-04 14:24:21 -0500941 <tr class="node">
942 <td class="dev_name">
943 <div class="text">{{ _p['model_name'] }}, {{ _p['serial_number'] }}</div><br>
Alexdf9cc3a2021-10-12 14:37:28 -0500944 {% if "model_family" in _p %}
945 <div class="note">{{ _p['model_family'] }}</div>
946 {% endif %}
947 <div class="note">{{ _p['node_name'] }}:{{ _p['osd_name'] }}</div>
Alexdcb792f2021-10-04 14:24:21 -0500948 </td>
949 <td class="dev_param centered">{{ _p['device']['info_name'] }}</td>
950 <td class="dev_param centered">{{ _p['device']['protocol'] }}</td>
951 <td class="dev_param centered">{{ _p['firmware_version'] }}</td>
Alexdf9cc3a2021-10-12 14:37:28 -0500952 {% if "interface_speed" in _p %}
Alexdcb792f2021-10-04 14:24:21 -0500953 <td class="dev_param centered">{{ _p['interface_speed']['current']['string'] }} / {{ _p['interface_speed']['max']['string'] }}</td>
Alexdf9cc3a2021-10-12 14:37:28 -0500954 {% else %}
955 <td class="dev_param centered">- / -</td>
956 {% endif%}
957 {% if "physical_block_size" in _p %}
Alexdcb792f2021-10-04 14:24:21 -0500958 <td class="dev_param centered">{{ _p['physical_block_size'] }} / {{ _p['logical_block_size'] }}</td>
Alexdf9cc3a2021-10-12 14:37:28 -0500959 {% else %}
960 <td class="dev_param centered">- / {{ _p['logical_block_size'] }}</td>
961 {% endif %}
Alexdcb792f2021-10-04 14:24:21 -0500962 <td class="dev_param centered">{{ _p['power_cycle_count'] }}</td>
963 <td class="dev_param centered">{{ _p['temperature']['current'] }}</td>
964 {% if _p['smart_status']['passed'] %}
965 <td class="dev_param centered" style="color: green">Passed</td>
966 {% else %}
967 <td class="dev_param centered" style="color: red;">Failed</td>
968 {% endif %}
969 <td class="dev_param centered">
970 <div class="cell_button" onclick="toggleClassByID('{{ _d }}_smart_output')" id="{{ _d }}_smart_output_button">Show/Hide</div>
971 </td>
972 </tr>
973 <tr class="collapsable" id="{{ _d }}_smart_output"><td colspan=10>
974 <div class="console">
975 <pre>
976 {% for line in _p['smartctl']['output'] %}
977 {{ line }}
978 {% endfor %}
979 </pre>
980 </div>
981 </td></tr>
982 {% endfor %}
983 </table>
984 <hr>
985</div>
986{% endmacro %}
987
988<!-- ================================= -->
989<!-- Cluster nodes page -->
990{% call status_page(info, "status") %}
991 Cluster status
992{% endcall %}
993
994{% call crush_page(info, "crush") %}
995 CRUSH map
996{% endcall %}
997
Alex41dd0cc2022-02-09 17:33:23 -0600998{% call osdconf_page(info, "osdconf") %}
999 OSD configs
1000{% endcall %}
1001
Alexdcb792f2021-10-04 14:24:21 -05001002{% call latency_page(info['osd_latency_data'], "latency") %}
1003 Quick latency check for all OSDs
1004{% endcall %}
1005
1006{% call mondump_page(info['monmap'], "mondump") %}
1007 Ceph monitors
1008{% endcall %}
1009
1010{% call df_page(info, "df") %}
1011 Pool list with additional data
1012{% endcall %}
1013
1014{% call dfrados_page(info, "dfrados") %}
1015 Rados pools list with additional details
1016{% endcall %}
1017
1018{% call auth_page(info, "auth") %}
1019 Anonymized auth list
1020{% endcall %}
1021
1022{% call dhealth_page(info, "dhealth") %}
1023 Device health status and S.M.A.R.T. outputs
1024{% endcall %}
1025</body>
1026</html>