upout: 0,
downin: 0,
downout: 0,
- oldosds: [],
+ oldOSD: [],
+ ghostOSD: [],
},
tpl: [
- '<h3>' + 'OSDs' + '</h3>',
+ '<h3>OSDs</h3>',
'<table class="osds">',
'<tr><td></td>',
'<td><i class="fa fa-fw good fa-circle"></i>',
gettext('Total'),
': {total}',
'</div><br />',
- '<tpl if="oldosds.length > 0">',
+ '<tpl if="oldOSD.length > 0">',
'<i class="fa fa-refresh warning"></i> ' + gettext('Outdated OSDs') + "<br>",
'<div class="osds">',
- '<tpl for="oldosds">',
+ '<tpl for="oldOSD">',
'<div class="left-aligned">osd.{id}:</div>',
'<div class="right-aligned">{version}</div><br />',
'<div style="clear:both"></div>',
'</tpl>',
'</div>',
'</tpl>',
+ '</div>',
+ '<tpl if="ghostOSD.length > 0">',
+ '<br />',
+ `<i class="fa fa-question-circle warning"></i> ${gettext('Ghost OSDs')}<br>`,
+ `<div data-qtip="${gettext('OSDs with no metadata, possibly left over from removal')}" class="osds">`,
+ '<tpl for="ghostOSD">',
+ '<div class="left-aligned">osd.{id}</div>',
+ '<div style="clear:both"></div>',
+ '</tpl>',
+ '</div>',
+ '</tpl>',
],
},
{
colors: [
'#CFCFCF',
'#21BF4B',
+ '#3892d4',
'#FFCC00',
'#FF6C59',
],
states: [],
},
tpl: [
- '<h3>' + 'PGs' + '</h3>',
+ '<h3>PGs</h3>',
'<tpl for="states">',
'<div class="left-aligned"><i class ="fa fa-circle {cls}"></i> {state_name}:</div>',
'<div class="right-aligned">{count}</div><br />',
clean: 1,
active: 1,
- // working
+ // busy
activating: 2,
backfill_wait: 2,
backfilling: 2,
creating: 2,
deep: 2,
- degraded: 2,
forced_backfill: 2,
forced_recovery: 2,
peered: 2,
snaptrim: 2,
snaptrim_wait: 2,
- // error
- backfill_toofull: 3,
- backfill_unfound: 3,
- down: 3,
- incomplete: 3,
- inconsistent: 3,
- recovery_toofull: 3,
- recovery_unfound: 3,
- snaptrim_error: 3,
- stale: 3,
+ // warning
+ degraded: 3,
undersized: 3,
+
+ // critical
+ backfill_toofull: 4,
+ backfill_unfound: 4,
+ down: 4,
+ incomplete: 4,
+ inconsistent: 4,
+ recovery_toofull: 4,
+ recovery_unfound: 4,
+ snaptrim_error: 4,
+ stale: 4,
},
statecategories: [
cls: 'good',
},
{
- text: gettext('Working'),
+ text: gettext('Busy'),
+ cls: 'pve-ceph-status-busy',
+ },
+ {
+ text: gettext('Warning'),
cls: 'warning',
},
{
- text: gettext('Error'),
+ text: gettext('Critical'),
cls: 'critical',
},
],
+ checkThemeColors: function() {
+ let me = this;
+ let rootStyle = getComputedStyle(document.documentElement);
+
+ // get color
+ let background = rootStyle.getPropertyValue("--pwt-panel-background").trim() || "#ffffff";
+
+ // set the colors
+ me.chart.setBackground(background);
+ me.chart.redraw();
+ },
+
updateAll: function(metadata, status) {
- var me = this;
+ let me = this;
me.suspendLayout = true;
- var maxversion = "0";
- Object.values(metadata.version || {}).forEach(function(version) {
- if (PVE.Utils.compare_ceph_versions(version, maxversion) > 0) {
- maxversion = version;
+ let maxversion = "0";
+ Object.values(metadata.node || {}).forEach(function(node) {
+ if (PVE.Utils.compare_ceph_versions(node?.version?.parts, maxversion) > 0) {
+ maxversion = node.version.parts;
}
});
- var oldosds = [];
-
- if (metadata.osd) {
- metadata.osd.forEach(function(osd) {
- var version = PVE.Utils.parse_ceph_version(osd);
- if (version != maxversion) {
- oldosds.push({
+ let oldOSD = [], ghostOSD = [];
+ metadata.osd?.forEach(osd => {
+ let version = PVE.Utils.parse_ceph_version(osd);
+ if (version !== undefined) {
+ if (PVE.Utils.compare_ceph_versions(version, maxversion) !== 0) {
+ oldOSD.push({
id: osd.id,
version: version,
});
}
- });
- }
+ } else {
+ if (Object.keys(osd).length > 1) {
+ console.warn('got OSD entry with no valid version but other keys', osd);
+ }
+ ghostOSD.push({
+ id: osd.id,
+ });
+ }
+ });
// update PGs sorted
- var pgmap = status.pgmap || {};
- var pgs_by_state = pgmap.pgs_by_state || [];
+ let pgmap = status.pgmap || {};
+ let pgs_by_state = pgmap.pgs_by_state || [];
pgs_by_state.sort(function(a, b) {
return a.state_name < b.state_name?-1:a.state_name === b.state_name?0:1;
});
});
pgs_by_state.forEach(function(state) {
- var i;
- var states = state.state_name.split(/[^a-z]+/);
- var result = 0;
- for (i = 0; i < states.length; i++) {
+ let states = state.state_name.split(/[^a-z]+/);
+ let result = 0;
+ for (let i = 0; i < states.length; i++) {
if (me.pgstates[states[i]] > result) {
result = me.pgstates[states[i]];
}
me.statecategories[result].states.push(state);
});
- me.getComponent('pgchart').getStore().setData(me.statecategories);
+ me.chart.getStore().setData(me.statecategories);
me.getComponent('pgs').update({ states: pgs_by_state });
- var downinregex = /(\d+) osds down/;
- var downin_osds = 0;
-
- var health = status.health || {};
+ let health = status.health || {};
// we collect monitor/osd information from the checks
+ const downinregex = /(\d+) osds down/;
+ let downin_osds = 0;
Ext.Object.each(health.checks, function(key, value, obj) {
var found = null;
if (key === 'OSD_DOWN') {
}
});
- var osdmap = status.osdmap || {};
- if (typeof osdmap.osdmap != "undefined") {
+ let osdmap = status.osdmap || {};
+ if (typeof osdmap.osdmap !== "undefined") {
osdmap = osdmap.osdmap;
}
- // update osds counts
- var total_osds = osdmap.num_osds || 0;
- var in_osds = osdmap.num_in_osds || 0;
- var up_osds = osdmap.num_up_osds || 0;
- var out_osds = total_osds - in_osds;
- var down_osds = total_osds - up_osds;
+ // update OSDs counts
+ let total_osds = osdmap.num_osds || 0;
+ let in_osds = osdmap.num_in_osds || 0;
+ let up_osds = osdmap.num_up_osds || 0;
+ let down_osds = total_osds - up_osds;
- var downout_osds = down_osds - downin_osds;
- var upin_osds = in_osds - downin_osds;
- var upout_osds = up_osds - upin_osds;
- var osds = {
+ let downout_osds = down_osds - downin_osds;
+ let upin_osds = in_osds - downin_osds;
+ let upout_osds = up_osds - upin_osds;
+
+ let osds = {
total: total_osds,
upin: upin_osds,
upout: upout_osds,
downin: downin_osds,
downout: downout_osds,
- oldosds: oldosds,
+ oldOSD: oldOSD,
+ ghostOSD,
};
- var osdcomponent = me.getComponent('osds');
+ let osdcomponent = me.getComponent('osds');
osdcomponent.update(Ext.apply(osdcomponent.data, osds));
me.suspendLayout = false;
me.updateLayout();
},
+
+ initComponent: function() {
+ var me = this;
+ me.callParent();
+
+ me.chart = me.getComponent('pgchart');
+ me.checkThemeColors();
+
+ // switch colors on media query changes
+ me.mediaQueryList = window.matchMedia("(prefers-color-scheme: dark)");
+ me.themeListener = (e) => { me.checkThemeColors(); };
+ me.mediaQueryList.addEventListener("change", me.themeListener);
+ },
+
+ doDestroy: function() {
+ let me = this;
+
+ me.mediaQueryList.removeEventListener("change", me.themeListener);
+
+ me.callParent();
+ },
});