| <!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> |