From 402991a79dcc6859ff099a01f9b83b4a3a440527 Mon Sep 17 00:00:00 2001 From: Dominik Csapak Date: Tue, 16 Jun 2020 11:06:34 +0200 Subject: [PATCH] add DiskSmart window and DiskList from PVE 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 --- 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.39.2