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