[pve-devel] [PATCH widget-toolkit 2/2] add DiskSmart window and DiskList from PVE

Dominik Csapak d.csapak at proxmox.com
Tue Jun 16 11:06:34 CEST 2020


for use with other produts.

the models are now all prefixed with 'pmx' instead of pve, so they
should not conflict

includes some changes to the model for remapping some fields and
some small refactors (change to controller for the DiskList,
some cleanup of the initComponent of the DiskSmart window)

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 src/Makefile            |   2 +
 src/grid/DiskList.js    | 233 ++++++++++++++++++++++++++++++++++++++++
 src/window/DiskSmart.js | 133 +++++++++++++++++++++++
 3 files changed, 368 insertions(+)
 create mode 100644 src/grid/DiskList.js
 create mode 100644 src/window/DiskSmart.js

diff --git a/src/Makefile b/src/Makefile
index 2696103..3311bbe 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -34,6 +34,7 @@ JSSRC=					\
 	button/HelpButton.js		\
 	grid/ObjectGrid.js		\
 	grid/PendingObjectGrid.js	\
+	grid/DiskList.js		\
 	panel/InputPanel.js		\
 	panel/LogView.js		\
 	panel/JournalView.js		\
@@ -43,6 +44,7 @@ JSSRC=					\
 	window/PasswordEdit.js		\
 	window/TaskViewer.js		\
 	window/LanguageEdit.js		\
+	window/DiskSmart.js		\
 	node/APT.js			\
 	node/NetworkEdit.js		\
 	node/NetworkView.js		\
diff --git a/src/grid/DiskList.js b/src/grid/DiskList.js
new file mode 100644
index 0000000..03d6725
--- /dev/null
+++ b/src/grid/DiskList.js
@@ -0,0 +1,233 @@
+Ext.define('pmx-disk-list', {
+    extend: 'Ext.data.Model',
+    fields: [
+	'devpath', 'used',
+	{ name: 'size', type: 'number' },
+	{ name: 'osdid', type: 'number' },
+	{
+	    name: 'status',
+	    convert: function(value, rec) {
+		if (value) return value;
+		if (rec.data.health) {
+		    return rec.data.health;
+		}
+		return Proxmox.Utils.unknownText;
+	    },
+	},
+	{
+	    name: 'name',
+	    convert: function(value, rec) {
+		if (value) return value;
+		if (rec.data.devpath) return rec.data.devpath;
+		return undefined;
+	    },
+	},
+	{
+	    name: 'disk-type',
+	    convert: function(value, rec) {
+		if (value) return value;
+		if (rec.data.type) return rec.data.type;
+		return undefined;
+	    },
+	},
+	'vendor', 'model', 'serial', 'rpm', 'type', 'wearout', 'health',
+    ],
+    idProperty: 'devpath',
+});
+
+Ext.define('Proxmox.DiskList', {
+    extend: 'Ext.grid.GridPanel',
+    alias: 'widget.pmxDiskList',
+
+    emptyText: gettext('No Disks found'),
+
+    stateful: true,
+    stateId: 'grid-node-disks',
+
+    controller: {
+	xclass: 'Ext.app.ViewController',
+
+	reload: function() {
+	    let me = this;
+	    me.getView().getStore().load();
+	},
+
+	openSmartWindow: function() {
+	    let me = this;
+	    let view = me.getView();
+	    let selection = view.getSelection();
+	    if (!selection || selection.length < 1) return;
+
+	    let rec = selection[0];
+	    Ext.create('Proxmox.window.DiskSmart', {
+		baseurl: view.baseurl,
+		dev: rec.data.name,
+	    }).show();
+	},
+
+	initGPT: function() {
+	    let me = this;
+	    let view = me.getView();
+	    let selection = view.getSelection();
+	    if (!selection || selection.length < 1) return;
+
+	    let rec = selection[0];
+	    Proxmox.Utils.API2Request({
+		url: `${view.baseurl}/initgpt`,
+		waitMsgTarget: view,
+		method: 'POST',
+		params: { disk: rec.data.devpath },
+		failure: function(response, options) {
+		    Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+		},
+		success: function(response, options) {
+		    var upid = response.result.data;
+		    var win = Ext.create('Proxmox.window.TaskProgress', {
+		        upid: upid,
+		    });
+		    win.show();
+		},
+	    });
+	},
+
+	init: function(view) {
+	    Proxmox.Utils.monStoreErrors(view, view.getStore(), true);
+
+	    let nodename = view.nodename || 'localhost';
+	    view.baseurl = `/api2/json/nodes/${nodename}/disks`;
+	    view.getStore().getProxy().setUrl(`${view.baseurl}/list`);
+	    view.getStore().load();
+	},
+    },
+
+    store: {
+	model: 'pmx-disk-list',
+	proxy: {
+	    type: 'proxmox',
+	},
+	sorters: [
+	    {
+		property: 'dev',
+		direction: 'ASC',
+	    },
+	],
+    },
+
+    tbar: [
+	{
+	    text: gettext('Reload'),
+	    handler: 'reload',
+	},
+	{
+	    xtype: 'proxmoxButton',
+	    text: gettext('Show S.M.A.R.T. values'),
+	    disabled: true,
+	    handler: 'openSmartWindow',
+	},
+	{
+	    xtype: 'proxmoxButton',
+	    text: gettext('Initialize Disk with GPT'),
+	    disabled: true,
+	    enableFn: function(rec) {
+		if (!rec || rec.data.used) {
+		    return false;
+		} else {
+		    return true;
+		}
+	    },
+	    handler: 'initGPT',
+	},
+    ],
+
+    columns: [
+	{
+	    header: gettext('Device'),
+	    width: 150,
+	    sortable: true,
+	    dataIndex: 'devpath',
+	},
+	{
+	    header: gettext('Type'),
+	    width: 80,
+	    sortable: true,
+	    dataIndex: 'disk-type',
+	    renderer: function(v) {
+		if (v === undefined) return Proxmox.Utils.unknownText;
+		switch (v) {
+		    case 'ssd': return 'SSD';
+		    case 'hdd': return 'Hard Disk';
+		    case 'usb': return 'USB';
+		    default: return v;
+		}
+	    },
+	},
+	{
+	    header: gettext('Usage'),
+	    width: 150,
+	    sortable: false,
+	    renderer: v => v || Proxmox.Utils.noText,
+	    dataIndex: 'used',
+	},
+	{
+	    header: gettext('Size'),
+	    width: 100,
+	    align: 'right',
+	    sortable: true,
+	    renderer: Proxmox.Utils.format_size,
+	    dataIndex: 'size',
+	},
+	{
+	    header: 'GPT',
+	    width: 60,
+	    align: 'right',
+	    renderer: Proxmox.Utils.format_boolean,
+	    dataIndex: 'gpt',
+	},
+	{
+	    header: gettext('Vendor'),
+	    width: 100,
+	    sortable: true,
+	    hidden: true,
+	    renderer: Ext.String.htmlEncode,
+	    dataIndex: 'vendor',
+	},
+	{
+	    header: gettext('Model'),
+	    width: 200,
+	    sortable: true,
+	    renderer: Ext.String.htmlEncode,
+	    dataIndex: 'model',
+	},
+	{
+	    header: gettext('Serial'),
+	    width: 200,
+	    sortable: true,
+	    renderer: Ext.String.htmlEncode,
+	    dataIndex: 'serial',
+	},
+	{
+	    header: 'S.M.A.R.T.',
+	    width: 100,
+	    sortable: true,
+	    renderer: Ext.String.htmlEncode,
+	    dataIndex: 'status',
+	},
+	{
+	    header: 'Wearout',
+	    width: 90,
+	    sortable: true,
+	    align: 'right',
+	    dataIndex: 'wearout',
+	    renderer: function(value) {
+		if (Ext.isNumeric(value)) {
+		    return (100 - value).toString() + '%';
+		}
+		return 'N/A';
+	    },
+	},
+    ],
+
+    listeners: {
+	itemdblclick: 'openSmartWindow',
+    },
+});
diff --git a/src/window/DiskSmart.js b/src/window/DiskSmart.js
new file mode 100644
index 0000000..e41d798
--- /dev/null
+++ b/src/window/DiskSmart.js
@@ -0,0 +1,133 @@
+Ext.define('Proxmox.window.DiskSmart', {
+    extend: 'Ext.window.Window',
+    alias: 'widget.pmxSmartWindow',
+
+    modal: true,
+
+    items: [
+	{
+	    xtype: 'gridpanel',
+	    layout: {
+		type: 'fit',
+	    },
+	    emptyText: gettext('No S.M.A.R.T. Values'),
+	    scrollable: true,
+	    flex: 1,
+	    itemId: 'smarts',
+	    reserveScrollbar: true,
+	    columns: [
+	    { text: 'ID', dataIndex: 'id', width: 50 },
+	    { text: gettext('Attribute'), flex: 1, dataIndex: 'name', renderer: Ext.String.htmlEncode },
+	    { text: gettext('Value'), dataIndex: 'raw', renderer: Ext.String.htmlEncode },
+	    { text: gettext('Normalized'), dataIndex: 'value', width: 60 },
+	    { text: gettext('Threshold'), dataIndex: 'threshold', width: 60 },
+	    { text: gettext('Worst'), dataIndex: 'worst', width: 60 },
+	    { text: gettext('Flags'), dataIndex: 'flags' },
+	    { text: gettext('Failing'), dataIndex: 'fail', renderer: Ext.String.htmlEncode },
+	    ],
+	},
+	{
+	    xtype: 'component',
+	    itemId: 'text',
+	    layout: {
+		type: 'fit',
+	    },
+	    hidden: true,
+	    style: {
+		'background-color': 'white',
+		'white-space': 'pre',
+		'font-family': 'monospace',
+	    },
+	},
+    ],
+
+    buttons: [
+	{
+	    text: gettext('Reload'),
+	    name: 'reload',
+	    handler: function() {
+		var me = this;
+		me.up('window').store.reload();
+	    },
+	},
+	{
+	    text: gettext('Close'),
+	    name: 'close',
+	    handler: function() {
+		var me = this;
+		me.up('window').close();
+	    },
+	},
+    ],
+
+    layout: {
+	type: 'vbox',
+	align: 'stretch',
+    },
+    width: 800,
+    height: 500,
+    minWidth: 600,
+    minHeight: 400,
+    bodyPadding: 5,
+    title: gettext('S.M.A.R.T. Values'),
+
+    initComponent: function() {
+	var me = this;
+
+	if (!me.baseurl) {
+	    throw "no baseurl specified";
+	}
+
+	var dev = me.dev;
+	if (!dev) {
+	    throw "no device specified";
+	}
+
+	me.store = Ext.create('Ext.data.Store', {
+	    model: 'pmx-disk-smart',
+	    proxy: {
+                type: 'proxmox',
+		url: `${me.baseurl}/smart?disk=${dev}`,
+	    },
+	});
+
+	me.callParent();
+	var grid = me.down('#smarts');
+	var text = me.down('#text');
+
+	Proxmox.Utils.monStoreErrors(grid, me.store);
+	me.mon(me.store, 'load', function(s, records, success) {
+	    if (success && records.length > 0) {
+		var rec = records[0];
+		if (rec.data.type === 'text') {
+		    grid.setVisible(false);
+		    text.setVisible(true);
+		    text.setHtml(Ext.String.htmlEncode(rec.data.text));
+		} else {
+		    grid.setVisible(true);
+		    text.setVisible(false);
+		    grid.setStore(rec.attributes());
+		}
+	    }
+	});
+
+	me.store.load();
+    },
+}, function() {
+    Ext.define('pmx-disk-smart', {
+	extend: 'Ext.data.Model',
+	fields: [
+	    { name: 'health' },
+	    { name: 'type' },
+	    { name: 'text' },
+	],
+	hasMany: { model: 'pmx-smart-attribute', name: 'attributes' },
+    });
+    Ext.define('pmx-smart-attribute', {
+	extend: 'Ext.data.Model',
+	fields: [
+	    { name: 'id', type: 'number' }, 'name', 'value', 'worst', 'threshold', 'flags', 'fail', 'raw',
+	],
+	idProperty: 'name',
+    });
+});
-- 
2.20.1





More information about the pve-devel mailing list