ui: osd: add details window

This new windows provides more detailes about an OSD such as:
* PID
* Memory usage
* various metadata that could be of interest
* list of phyiscal disks used for the main disk, db and wal with
  additional infos about the volumes for each

A new 'Details' button is added to the OSD overview and a double click
on an OSD will also open this new window.

The componend defines the items in the initComponent instead of
following a fully declarative approach. This is because we need to pass
the same store to multiple Proxmox.ObjectGrids.

Signed-off-by: Aaron Lauterer <a.lauterer@proxmox.com>
Reviewed-by: Dominik Csapak <d.csapak@proxmox.com>
Tested-by:  Dominik Csapak <d.csapak@proxmox.com>
This commit is contained in:
Aaron Lauterer 2022-12-12 13:14:51 +01:00 committed by Thomas Lamprecht
parent fe5c4f8156
commit 6757887a5d
3 changed files with 307 additions and 0 deletions

View File

@ -188,6 +188,7 @@ JSSRC= \
ceph/Log.js \
ceph/Monitor.js \
ceph/OSD.js \
ceph/OSDDetails.js \
ceph/Pool.js \
ceph/ServiceList.js \
ceph/Services.js \

View File

@ -583,6 +583,20 @@ Ext.define('PVE.node.CephOsdTree', {
}
},
run_details: function(view, rec) {
if (rec.data.host && rec.data.type === 'osd' && rec.data.id >= 0) {
this.details();
}
},
details: function() {
let vm = this.getViewModel();
Ext.create('PVE.CephOsdDetails', {
nodename: vm.get('osdhost'),
osdid: vm.get('osdid'),
}).show();
},
set_selection_status: function(tp, selection) {
if (selection.length < 1) {
return;
@ -695,6 +709,9 @@ Ext.define('PVE.node.CephOsdTree', {
stateId: 'grid-ceph-osd',
rootVisible: false,
useArrows: true,
listeners: {
itemdblclick: 'run_details',
},
columns: [
{
@ -842,6 +859,15 @@ Ext.define('PVE.node.CephOsdTree', {
'</tpl>',
],
},
{
text: gettext('Details'),
iconCls: 'fa fa-info-circle',
disabled: true,
bind: {
disabled: '{!isOsd}',
},
handler: 'details',
},
{
text: gettext('Start'),
iconCls: 'fa fa-play',

View File

@ -0,0 +1,280 @@
Ext.define('pve-osd-details-devices', {
extend: 'Ext.data.Model',
fields: ['device', 'type', 'physical_device', 'size', 'support_discard', 'dev_node'],
idProperty: 'device',
});
Ext.define('PVE.CephOsdDetails', {
extend: 'Ext.window.Window',
alias: ['widget.pveCephOsdDetails'],
mixins: ['Proxmox.Mixin.CBind'],
cbindData: function() {
let me = this;
me.baseUrl = `/nodes/${me.nodename}/ceph/osd/${me.osdid}`;
return {
title: `${gettext('Details')}: OSD ${me.osdid}`,
};
},
viewModel: {
data: {
device: '',
},
},
modal: true,
width: 650,
minHeight: 250,
resizable: true,
cbind: {
title: '{title}',
},
layout: {
type: 'vbox',
align: 'stretch',
},
defaults: {
layout: 'fit',
border: false,
},
controller: {
xclass: 'Ext.app.ViewController',
reload: function() {
let view = this.getView();
Proxmox.Utils.API2Request({
url: `${view.baseUrl}/metadata`,
waitMsgTarget: view.lookup('detailsTabs'),
method: 'GET',
failure: function(response, opts) {
Proxmox.Utils.setErrorMask(view.lookup('detailsTabs'), response.htmlStatus);
},
success: function(response, opts) {
let d = response.result.data;
let osdData = Object.keys(d.osd).sort().map(x => ({ key: x, value: d.osd[x] }));
view.osdStore.loadData(osdData);
let devices = view.lookup('devices');
let deviceStore = devices.getStore();
deviceStore.loadData(d.devices);
view.lookup('osdGeneral').rstore.fireEvent('load', view.osdStore, osdData, true);
view.lookup('osdNetwork').rstore.fireEvent('load', view.osdStore, osdData, true);
// select 'block' device automatically on first load
if (devices.getSelection().length === 0) {
devices.setSelection(deviceStore.findRecord('device', 'block'));
}
},
});
},
showDevInfo: function(grid, selected) {
let view = this.getView();
if (selected[0]) {
let device = selected[0].data.device;
this.getViewModel().set('device', device);
let detailStore = view.lookup('volumeDetails');
detailStore.rstore.getProxy().setUrl(`api2/json${view.baseUrl}/lv-info`);
detailStore.rstore.getProxy().setExtraParams({ 'type': device });
detailStore.setLoading();
detailStore.rstore.load({ callback: () => detailStore.setLoading(false) });
}
},
init: function() {
this.reload();
},
control: {
'grid[reference=devices]': {
selectionchange: 'showDevInfo',
},
},
},
tbar: [
{
text: gettext('Reload'),
iconCls: 'fa fa-refresh',
handler: 'reload',
},
],
initComponent: function() {
let me = this;
me.osdStore = Ext.create('Proxmox.data.ObjectStore');
Ext.applyIf(me, {
items: [
{
xtype: 'tabpanel',
reference: 'detailsTabs',
items: [
{
xtype: 'proxmoxObjectGrid',
reference: 'osdGeneral',
tooltip: gettext('Various information about the OSD'),
rstore: me.osdStore,
title: gettext('General'),
viewConfig: {
enableTextSelection: true,
},
gridRows: [
{
xtype: 'text',
name: 'version',
text: gettext('Version'),
},
{
xtype: 'text',
name: 'hostname',
text: gettext('Hostname'),
},
{
xtype: 'text',
name: 'osd_data',
text: gettext('OSD data path'),
},
{
xtype: 'text',
name: 'osd_objectstore',
text: gettext('OSD object store'),
},
{
xtype: 'text',
name: 'mem_usage',
text: gettext('Memory usage'),
renderer: Proxmox.Utils.render_size,
},
{
xtype: 'text',
name: 'pid',
text: `${gettext('Process ID')} (PID)`,
},
],
},
{
xtype: 'proxmoxObjectGrid',
reference: 'osdNetwork',
tooltip: gettext('Addresses and ports used by the OSD service'),
rstore: me.osdStore,
title: gettext('Network'),
viewConfig: {
enableTextSelection: true,
},
gridRows: [
{
xtype: 'text',
name: 'front_addr',
text: `${gettext('Front Address')}<br>(Client & Monitor)`,
renderer: PVE.Utils.render_ceph_osd_addr,
},
{
xtype: 'text',
name: 'hb_front_addr',
text: gettext('Heartbeat Front Address'),
renderer: PVE.Utils.render_ceph_osd_addr,
},
{
xtype: 'text',
name: 'back_addr',
text: `${gettext('Back Address')}<br>(OSD)`,
renderer: PVE.Utils.render_ceph_osd_addr,
},
{
xtype: 'text',
name: 'hb_back_addr',
text: gettext('Heartbeat Back Address'),
renderer: PVE.Utils.render_ceph_osd_addr,
},
],
},
{
xtype: 'panel',
title: 'Devices',
tooltip: gettext('Physical devices used by the OSD'),
items: [
{
xtype: 'grid',
border: false,
reference: 'devices',
store: {
model: 'pve-osd-details-devices',
},
columns: {
items: [
{ text: gettext('Device'), dataIndex: 'device' },
{ text: gettext('Type'), dataIndex: 'type' },
{
text: gettext('Physical Device'),
dataIndex: 'physical_device',
},
{
text: gettext('Size'),
dataIndex: 'size',
renderer: Proxmox.Utils.render_size,
},
{
text: 'Discard',
dataIndex: 'support_discard',
hidden: true,
},
{
text: gettext('Device node'),
dataIndex: 'dev_node',
hidden: true,
},
],
defaults: {
tdCls: 'pointer',
flex: 1,
},
},
},
{
xtype: 'proxmoxObjectGrid',
reference: 'volumeDetails',
maskOnLoad: true,
viewConfig: {
enableTextSelection: true,
},
bind: {
title: Ext.String.format(
gettext('Volume Details for {0}'),
'{device}',
),
},
rows: {
creation_time: {
header: gettext('Creation time'),
},
lv_name: {
header: gettext('LV Name'),
},
lv_path: {
header: gettext('LV Path'),
},
lv_uuid: {
header: gettext('LV UUID'),
},
vg_name: {
header: gettext('VG Name'),
},
},
url: 'nodes/', //placeholder will be set when device is selected
},
],
},
],
},
],
});
me.callParent();
},
});