blob: e847cb441bc0cc06a21c7d891e9f675905134258 [file] [log] [blame]
Alexb2129542021-11-23 15:49:42 -06001<!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 {% include 'bar_chart.j2' %}
9 <style>
10 table.cluster_nodes {
11 width: 100%;
12 margin-left: 1%;
13 margin-right: 1%;
14 }
15 .barcontent {
16 margin: auto;
17 width: 1350px;
18 padding: 10px;
19 }
20 .bar-centered {
21 float: none;
22 transform: translate(25%);
23 }
24
25 /* Node rows*/
26 .node {
27 font-family: "LaoSangamMN", Monaco, monospace;
28 font-size: 0.8em;
29 display: flex;
30 background-color: white;
31 align-items: center;
32 }
33 .collapsable {
34 font-family: "LaoSangamMN", Monaco, monospace;
35 font-size: 0.8em;
36 display: none;
37 background-color: white;
38 visibility: hidden;
39 }
40 .collapsable.in {
41 visibility: visible;
42 display: inline-block;
43 }
44
45 .row_button {
46 background-color: #468;
47 color: #fff;
48 cursor: pointer;
49 padding: 5px;
50 width: 100%;
51 border: none;
52 text-align: left;
53 outline: none;
54 font-size: 13px;
55 }
56 .row_button:after {
57 content: '\02795'; /* Unicode character for "plus" sign (+) */
58 font-size: 13px;
59 color: white;
60 float: left;
61 margin-left: 5px;
62 }
63
64 .row_active:after {
65 content: "\2796"; /* Unicode character for "minus" sign (-) */
66 color: white
67 }
68
69 .row_active, .row_button:hover {
70 background-color: #68a;
71 color: white
72 }
73
74 .cell_button {
75 color: darkgreen;
76 cursor: pointer;
77 padding: 5px;
78 width: 100%;
79 border: none;
80 text-align: center;
81 outline: none;
82 }
83 .cell_button:hover {
84 background-color: gray;
85 }
86
87 .row_content {
88 padding: 0 18px;
89 background-color: white;
90 max-height: 0;
91 overflow: hidden;
92 transition: max-height 0.2s ease-out;
93 border-width: 1px;
94 border-color: #68a;
95 border-style: solid;
96 }
97
98 div.services > .collapsable.in {
99 display: table-row;
100 }
101 tr:nth-child(even) {
102 background-color: #eee;
103 }
104 tr:nth-child(odd) {
105 background-color: #fff;
106 }
107
108 tr.node > td, tr.collapsable > td {
109 display: block;
110 float: left;
111 padding: 1px;
112 margin: 2px;
113 }
114 td > .osd_group {
115 display: grid;
116 grid-template-columns: 40px 25px 25px 70px;
117 padding-left: 0px;
118 padding-right: 0px;
119 margin: 1px;
120 }
121 td > .props_group {
122 display: grid;
123 grid-template-columns: 60px 60px 80px 35px 45px 95px 50px 60px 45px;
124 padding-left: 0px;
125 padding-right: 0px;
126 margin: 1px;
127 }
128 td > .pg_group {
129 display: grid;
130 grid-template-columns: 50px 40px 60px 65px 60px 65px 65px;;
131 padding-left: 0px;
132 padding-right: 0px;
133 margin: 1px;
134 }
135 td > .bench_run_group {
136 display: grid;
137 grid-template-columns: 80px 80px 80px 80px 75px 75px;
138 padding-left: 0px;
139 padding-right: 0px;
140 margin: 1px;
141 }
142 td > .bench_group {
143 display: grid;
144 grid-template-columns: 80px 80px 75px 75px;
145 padding-left: 0px;
146 padding-right: 0px;
147 margin: 1px;
148 }
149 td > .meta_group {
150 display: inline-block;
151 grid-template-columns: repeat(4, auto);
152 padding-left: 0px;
153 padding-right: 0px;
154 margin: 1px;
155 }
156 .item {
157 display: inline-grid;
158 border-width: 1px;
159 border-style: solid;
160 margin: 1px 1px 1px 1px;
161 padding: 0px 1px 0px 1px;
162 }
163
164 .spacer { border-radius: 2px; width: 20px;}
165 .status { border-radius: 10px; width: 120px; text-align: center;}
166 .health_ok { background-color: #393; color: white;}
167 .health_error { background-color: #933; color: white;}
168 .health_warn { background-color: #eb3; color: #333;}
169 .checks_code { border-radius: 2px; width: 20%; background-color: transparent; color: darkred;}
170
171 .head { height: 18px; background-color: transparent; border-color: transparent; border: 0px;}
172 .centered { text-align: center;}
173 .right { text-align: right;}
174 .col_shortmessage { min-width: 300px; }
175 .col_longmessage { width: auto; }
176 .col_properties { width: auto;}
177
178 .srv_name { width: 300px }
179 .srv_path { width: 250px }
180 .srv_timestamp { width: 250px }
181 .srv_addr { width: 450px }
182
183 .id { width: 30px }
184 .bucket_name { width: 365px }
185 .bucket_type { width: 50px }
186 .bucket_params { width: 200px }
187 .bucket_items { width: 630px }
188
189 .df_name { width: 300px }
190 .df_total { width: 150px }
191 .df_avail { width: 150px }
192 .df_used { width: 150px }
193 .df_used_raw { width: 150px }
194 .df_used_raw_rate { width: 150px }
195
196 .rdf_name { width: 200px; }
197 .rdf_obj { width: 75px; }
198 .rdf_total { width: 100px; }
199 .rdf_used { width: 100px; }
200 .rdf_bench { width: 100px; }
201
202 .dev_name { width: 300px; }
203 .dev_param { width: 100px; }
204
205 .mon_name { width: 100px }
206 .mon_url { width: 500px }
207
208 .meters {
209 display: inline-block;
210 margin: 1px;
211 }
212 .meters > .meter {
213 display: block;
214 float: left;
215 border-width: 1px;
216 border-style: solid;
217 margin: 0px 1px 0px 1px;
218 padding: 0px 1px 0px 1px;
219
220 }
221 .meters > .warn {
222 border-color: #d3a200;
223 background-color: rgb(255, 216, 133);
224 }
225 .meters > .fail {
226 border-color: #bb0000;
227 background-color: rgb(250, 135, 135);
228 }
229 .osd { border-color: #a0c0a0; background-color: rgb(252, 248, 248); text-align: center;}
230 .prop { border-color: #74c28b; background-color: rgb(252, 248, 248); text-align: center;}
231 .pg { border-color: #c0c0a0; background-color: rgb(255, 255, 251); text-align: right; }
232 .bench { border-color: #a0c0c0; background-color: rgb(255, 250, 250); text-align: right; }
233 .lat_commit { border-color: #a0c0c0; background-color: rgb(255, 250, 250); text-align: right; width: 45px}
234 .lat_apply { border-color: #a0c0c0; background-color: rgb(255, 250, 250); text-align: left; width: 35px}
235 .meta_name { border-color: #c4b890; background-color: #e7dbb6; text-align: left; width: 150px;}
236 .meta_value { border-color: #c6c3ba;background-color: #d4d4d4; text-align: left; width: 480px;}
237
238 .map_grid {
239 display: grid;
240 grid-template-columns: auto auto auto auto auto auto auto auto auto auto;
241 grid-column-gap: 20px;
242 padding-left: 0px;
243 padding-right: 0px;
244 margin: 1px;
245 margin-left: 20px;
246
247 }
248 .map_item {
249 display: inline-grid;
250 border-width: 0px;
251 border-style: solid;
252 margin: 1px 1px 1px 1px;
253 padding: 0px 1px 0px 1px;
254 }
255
256 .map_grid > .ok {
257 color: #80a080;
258 }
259 .map_grid > .warn {
260 color: #d3a200;
261 }
262 .map_grid > .fail {
263 color: #bb0000;
264 }
265
266 .modules {
267 font-family: "LaoSangamMN", Monaco, monospace;
268 font-size: 0.8em;
269 background-color: white;
270 }
271 .module_node {
272 margin-bottom: 2px;
273 display: flex;
274 }
275 .module_name, .node_name {
276 text-align: center;
277 border-width: 0px;
278 border-style: solid;
279 margin: 1px 1px 1px 1px;
280 padding: 0px 1px 0px 1px;
281 min-width: 250px;
282 border-radius: 10px;
283 }
284 .node_name {
285 background-color: #ddd;
286 }
287 .module_grid {
288 display: grid;
289 grid-template-columns: repeat(8, 100px);
290 grid-template-rows: repeat(6, auto);
291 grid-auto-flow: column;
292 grid-column-gap: 10px;
293 padding-left: 0px;
294 padding-right: 0px;
295 margin: 1px;
296 margin-left: 20px;
297 }
298 .module {
299 display: inline-grid;
300 text-align: center;
301 border-width: 0px;
302 border-style: solid;
303 margin: 1px 1px 1px 1px;
304 padding: 0px 1px 0px 1px;
305 min-width: 100px;
306 border-radius: 10px;
307 }
308
309 .module_grid > .on, .service_node > .ok {
310 background-color: #8c8;
311 }
312 .module_grid > .off, .service_node > .off{
313 background-color: #9aa;
314 }
315 .module_grid > .fail, .service_node > .fail {
316 background-color: #a33;
317 }
318 .module_grid > .always, .service_node > .fail {
319 background-color: #282;
320 }
321
322 .tooltiptext {
323 transform: translate(100px);
324 }
325
326 .console {
327 background-color: black;
328 font-family: "Lucida Console", Monaco, monospace;
329 font-size: 0.5em;
330 width: auto;
331 color: #fff;
332 border-radius: 6px;
333 padding: 5px 5px;
334 }
335
336 </style>
337</head>
338<body onload="init()">
339
340<div class="header">
341 <div class="label">Ceph version:</div>
342 <div class="text">{{ ceph_version }}</div>
343 <div class="label">Image:</div>
344 <div class="text">{{ cluster.image }}</div>
345 <div class="label date">generated on: {{ gen_date }}</div>
346</div>
347
348<div class="bar">
349 <div class="bar-centered">
350 <button class="bar-item" onclick="openBar(event, 'bench')">Benchmark Results</button>
351 <button class="bar-item" onclick="openBar(event, 'status')">Status</button>
352 <!-- <button class="bar-item" onclick="openBar(event, 'latency')">Latency</button> -->
353 </div>
354</div>
355
356<!-- Benchmarks -->
357{% macro bench_page(results, id_label) %}
358<div id="{{ id_label }}" class="barcontent">
359 <h5>{{ caller() }}</h5>
360 <hr>
361 <table class="ceph_status">
362 <tr class="node">
363 <td class="status">Time started</td>
364 <td class="status">Data point</td>
365 <td class="col_properties">
366 <div class="props_group">
367 <div class="item prop">Warmup</div>
368 <div class="item prop">Run Time</div>
369 <div class="item prop">Storage class</div>
370 <div class="item pg">PGs</div>
371 <div class="item prop">Engine</div>
372 <div class="item prop">Mode</div>
373 <div class="item prop">BS</div>
374 <div class="item prop">IOdepth</div>
375 <div class="item prop">Size</div>
376 </div>
377 </td>
378 <td class="col_bench">
379 <div class="bench_run_group">
380 <div class="item bench">Read, MB/s</div>
381 <div class="item bench">Avg lat, usec</div>
382 <div class="item bench">Read, op/s</div>
383 <div class="item bench">Write, MB/s</div>
384 <div class="item bench">Avg lat, usec</div>
385 <div class="item bench">Write, op/s</div>
386 </div>
387 </td>
388 </tr>
389 {% for time,dt in results.items() %}
390 {% set t = dt["totals"] %}
391 {% set o = dt["input_options"] %}
392 {% set tstripped = time | tstrip %}
393 <tr class="node" onclick="toggleClassByID('timing_{{ tstripped }}_data')" id="timing_{{ tstripped }}_button">
394 <td class="status">{{ time }}</td>
395 <td class="status">All agents</td>
396 <td class="col_properties">
397 <div class="props_group">
398 <div class="item prop">{{ o["ramp_time"] }}</div>
399 <div class="item prop">{{ o["runtime"] }}</div>
400 <div class="item prop">{{ t["storage_class"] }}</div>
401 <div class="item pg">{{ t["storage_class_stats"]["num_pg"] }}</div>
402 <div class="item prop">{{ o["ioengine"] }}</div>
403 <div class="item prop">{{ o["readwrite"] }} ({{ o["rwmixread"] }}/{{ 100-o["rwmixread"] }})</div>
404 <div class="item prop">{{ o["bs"] }}</div>
405 <div class="item prop">{{ o["iodepth"] }}</div>
406 <div class="item prop">{{ o["size"] }}</div>
407 </div>
408 </td>
409 <td class="col_bench">
410 <div class="bench_run_group">
411 <div class="item bench">{{ t["read_bw_bytes"] | to_mb }}</div>
412 <div class="item bench">{{ "%0.2f" | format(t["read_avg_lat_us"]|float) }}</div>
413 <div class="item bench">{{ "%0.2f" | format(t["read_iops"]|float) }}</div>
414 <div class="item bench">{{ t["write_bw_bytes"] | to_mb }}</div>
415 <div class="item bench">{{ "%0.2f" | format(t["write_avg_lat_us"]|float) }}</div>
416 <div class="item bench">{{ "%0.2f" | format(t["write_iops"]|float) }}</div>
417 </div>
418 </td>
419 </tr>
420 <tr class="collapsable" id="timing_{{ tstripped }}_data"><td colspan=3>
421 <div class="bc-wrap">
422 <div class="bctimecol">
423 <div class="bctime"><span class="bctimetext">110</span></div>
424 <div class="bctime"><span class="bctimetext">75</span></div>
425 <div class="bctime"><span class="bctimetext">50</span></div>
426 <div class="bctime"><span class="bctimetext">15</span></div>
427 </div>
428
429 <div class="bc-container">
430 <div class="bc">
431 <div class="bccol"><div class="bcbar" style="height: 75%;"></div><div class="bcfooter">2s</div></div>
432 <div class="bccol"><div class="bcbar" style="height: 25%;"></div><div class="bcfooter">4s</div></div>
433 <div class="bccol"><div class="bcbar" style="height: 55%;"></div><div class="bcfooter">6s</div></div>
434 <div class="bccol"><div class="bcbar" style="height: 65%;"></div><div class="bcfooter">8s</div></div>
435 <div class="bccol"><div class="bcbar" style="height: 15%;"></div><div class="bcfooter">10s</div></div>
436 <div class="bccol"><div class="bcbar" style="height: 16%;"></div><div class="bcfooter">12s</div></div>
437 <div class="bccol"><div class="bcbar" style="height: 17%;"></div><div class="bcfooter">14s</div></div>
438 <div class="bccol"><div class="bcbar" style="height: 18%;"></div><div class="bcfooter">16s</div></div>
439 <div class="bccol"><div class="bcbar" style="height: 19%;"></div><div class="bcfooter">18s</div></div>
440 <div class="bccol"><div class="bcbar" style="height: 20%;"></div><div class="bcfooter">20s</div></div>
441 <div class="bccol"><div class="bcbar" style="height: 21%;"></div><div class="bcfooter">22s</div></div>
442 </div>
443 </div>
444 </div>
445 <table style="table-layout: auto;"><tbody>
446 {% for agent,ag_result in dt["agents"].items() %}
447 {% set j = ag_result[time]["jobs"][0] %}
448 <tr>
449 <td class="status">{{ time }}</td>
450 <td class="status">{{ agent }}</td>
451 <td class="col_properties">
452 <div class="props_group">
453 <div class="item prop">{{ j["job options"]["ramp_time"] }}</div>
454 <div class="item prop">{{ j["job options"]["runtime"] }}</div>
455 <div class="item prop">{{ t["storage_class"] }}</div>
456 <div class="item pg">{{ t["storage_class_stats"]["num_pg"] }}</div>
457 <div class="item prop">{{ o["ioengine"] }}</div>
458 <div class="item prop">{{ o["readwrite"] }} ({{ o["rwmixread"] }}/{{ 100-o["rwmixread"] }})</div>
459 <div class="item prop">{{ j["job options"]["bs"] }}</div>
460 <div class="item prop">{{ o["iodepth"] }}</div>
461 <div class="item prop">{{ j["job options"]["size"] }}</div>
462 </div>
463 </td>
464 <td class="col_bench">
465 <div class="bench_run_group">
466 <div class="item bench">{{ j["read"]["bw_bytes"] | to_mb }}</div>
467 <div class="item bench">{{ "%0.2f" | format(j["read"]["lat_ns"]["mean"]|float / 1000) }}</div>
468 <div class="item bench">{{ "%0.2f" | format(j["read"]["iops"]|float) }}</div>
469 <div class="item bench">{{ j["write"]["bw_bytes"] | to_mb }}</div>
470 <div class="item bench">{{ "%0.2f" | format(j["write"]["lat_ns"]["mean"]|float / 1000) }}</div>
471 <div class="item bench">{{ "%0.2f" | format(j["write"]["iops"]|float) }}</div>
472 </div>
473 </td>
474 </tr>
475 </tr>
476 {% endfor %}
477 </tbody></table>
478 </td></tr>
479 {% endfor %}
480 </table>
481</div>
482{% endmacro %}
483
484<!-- Status page -->
485{% macro status_page(info, id_label) %}
486<div id="{{ id_label }}" class="barcontent">
487 <h5>{{ caller() }}</h5>
488 <hr>
489 <table class="ceph_status">
490 <tr class="node">
491 <td class="status">Cluster status</td>
492 <td class="col_shortmessage">Status summary</td>
493 <td class="col_osd">
494 <div class="osd_group">
495 <div class="item osd">OSDs</div>
496 <div class="item osd">Up</div>
497 <div class="item osd">In</div>
498 <div class="item osd">Remap PGs</div>
499 </div>
500 </td>
501 <td class="col_pgs">
502 <div class="pg_group">
503 <div class="item pg">PGs</div>
504 <div class="item pg">Pools</div>
505 <div class="item pg">Objects</div>
506 <div class="item pg">Data, GB</div>
507 <div class="item pg">Used, GB</div>
508 <div class="item pg">Avail, GB</div>
509 <div class="item pg">Total, GB</div>
510 </div>
511 </td>
512 <td class="col_bench">
513 <div class="bench_group">
514 <div class="item bench">Read, MB/sec</div>
515 <div class="item bench">Write, MB/sec</div>
516 <div class="item bench">Read, op/sec</div>
517 <div class="item bench">Write, op/sec</div>
518 </div>
519 </td>
520 </tr>
521 {% set cs = idle_status %}
522 {% set osdmap = cs | get_osdmap %}
523 <tr class="node" onclick="toggleClassByID('health_data')" id="health_data_button">
524 <td class="status {{ health_detail["status"] | lower }}">{{ health_detail["status"] }}</td>
525 <td class="col_shortmessage">
526 {% for code,dt in health_detail["checks"].items() %}
527 {{ dt["summary"]["message"] }}<br>
528 {% endfor %}
529 </td>
530 <!-- background: linear-gradient(to right, gray 0% 20%, transparent 20% 100%); -->
531 <td class="col_osd">
532 <div class="osd_group">
533 <div class="item osd">{{ osdmap["num_osds"] }}</div>
534 <div class="item osd">{{ osdmap["num_up_osds"] }}</div>
535 <div class="item osd">{{ osdmap["num_in_osds"] }}</div>
536 <div class="item osd">{{ osdmap["num_remapped_pgs"] }}</div>
537 </div>
538 </td>
539 {% set pgmap = cs["pgmap"] %}
540 <td class="col_pgs">
541 <div class="pg_group">
542 <div class="item pg">{{ pgmap["num_pgs"] }}</div>
543 <div class="item pg">{{ pgmap["num_pools"] }}</div>
544 <div class="item pg">{{ pgmap["num_objects"] }}</div>
545 <div class="item pg">{{ pgmap["data_bytes"] | to_gb }}</div>
546 <div class="item pg">{{ pgmap["bytes_used"] | to_gb }}</div>
547 <div class="item pg">{{ pgmap["bytes_avail"] | to_gb }}</div>
548 <div class="item pg">{{ pgmap["bytes_total"] | to_gb }}</div>
549 </div>
550 </td>
551 <td class="col_bench">
552 <div class="bench_group">
553 {% if "read_bytes_sec" in pgmap %}
554 <div class="item bench">{{ pgmap["read_bytes_sec"] | to_mb }}</div>
555 {% else %}
556 <div class="item bench">0</div>
557 {% endif %}
558 {% if "write_bytes_sec" in pgmap %}
559 <div class="item bench">{{ pgmap["write_bytes_sec"] | to_mb }}</div>
560 {% else %}
561 <div class="item bench">0</div>
562 {% endif %}
563 {% if "read_op_per_sec" in pgmap %}
564 <div class="item bench">{{ pgmap["read_op_per_sec"] }}</div>
565 {% else %}
566 <div class="item bench">0</div>
567 {% endif %}
568 {% if "write_op_per_sec" in pgmap %}
569 <div class="item bench">{{ pgmap["write_op_per_sec"] }}</div>
570 {% else %}
571 <div class="item bench">0</div>
572 {% endif %}
573 </div>
574 </td>
575 </tr>
576 <tr class="collapsable in" id="health_data"><td colspan=3>
577 <table><tbody>
578 {% for code,dt in health_detail["checks"].items() %}
579 <tr>
580 <td class="spacer"></td>
581 <td class="status {{ dt["severity"] | lower }}">{{ dt["severity"] }}</td>
582 <td class="checks_code">{{ code }}</td>
583 <td class="col_longmessage">
584 <table><tbody>
585 {% for detail in dt["detail"] %}
586 <tr><td>{{ detail["message"] }}</td></tr>
587 {% endfor %}
588 </tbody></table>
589 </td>
590 </tr>
591 {% endfor %}
592 </tbody></table>
593 </td></tr>
594 </table>
595 <hr>
596 <!-- Services -->
597 {% set sm = idle_status["servicemap"] %}
598 <h5>Services: {{ sm["services"] | count }} running. Last modification: {{ sm["modified"] }}</h5>
599 <table class="ceph_status">
600 <tr class="node">
601 <td class="srv_name">Name</td>
602 <td class="srv_path">Subpath</td>
603 <td class="srv_timestamp">Start time</td>
604 <td class="srv_addr">Address</td>
605 </tr>
606 {% for name, d1 in sm["services"].items() %}
607 {% if "daemons" in d1 %}
608 {% set d2 = d1["daemons"] %}
609 {% for key, d3 in d2.items() %}
610 {% if key.startswith("rgw.store") %}
611 <tr class="node" onclick="toggleClassByID('{{ name }}_service_data')" id="{{ name }}_service_data_button">
612 <td class="srv_name">{{ name }} ({{ d3["gid"] }})</td>
613 <td class="srv_path">daemons:{{ key }}</td>
614 <td class="srv_timestamp">{{ d3["start_stamp"] }}</td>
615 <td class="srv_addr">{{ d3["addr"] }}</td>
616 </tr>
617 <tr class="collapsable in" id="{{ name}}_service_data"><td colspan=4>
618 <table><tbody>
619 <tr><td class="metadata">
620 {% for mname, mvalue in d3["metadata"].items() %}
621 <div class="meta_group">
622 <div class="item meta_name">{{ mname }}</div>
623 <div class="item meta_value">{{ mvalue }}</div>
624 </div>
625 {% endfor %}
626 </td></tr>
627 </tbody></table>
628 </td></tr>
629 {% endif %}
630 {% endfor %}
631 {% endif %}
632 {% endfor %}
633 </table>
634 <hr>
635 <!-- Modules -->
636 {% set mgrmap = idle_status["mgrmap"] %}
637 {% set mods = mgrmap["modules"] %}
638 {% set avail = mgrmap["available_modules"] %}
639 {% if "always_on_modules" in mgrmap %}
640 {% set always_on = mgrmap["always_on_modules"].values() | list %}
641 {% set always_on = always_on[0] %}
642 {% else %}
643 {% set always_on = [] %}
644 {% endif %}
645 <h5>Modules: {{ mods | count}} active. {{ always_on | count }} always on. {{ avail | count }} available.</h5>
646 <div class="modules">
647 <div class="module_grid">
648 {% for mod in avail %}
649 {% if mod["name"] in always_on %}
650 <div class="module always">{{ mod["name"] }}</div>
651 {% elif mod["name"] in mods %}
652 <div class="module on">{{ mod["name"] }}</div>
653 {% elif not mod["can_run"] %}
654 <div class="module fail tooltip">
655 <div class="module fail">{{ mod["name"] }}</div>
656 <pre class="tooltiptext">{{ mod["error_string"] | linebreaks }}</pre>
657 </div>
658 {% else %}
659 <div class="module">{{ mod["name"] }}</div>
660 {% endif %}
661 {% endfor %}
662 </div>
663 </div>
664 <hr>
665</div>
666{% endmacro %}
667
668<!-- ================================= -->
669<!-- Cluster nodes page -->
670{% call bench_page(results, "bench") %}
671 Benchmark results
672{% endcall %}
673
674{% call status_page(info, "status") %}
675 Cluster status
676{% endcall %}
677
678</body>
679</html>