]> git.proxmox.com Git - proxmox-widget-toolkit.git/commitdiff
add DiskSmart window and DiskList from PVE
authorDominik Csapak <d.csapak@proxmox.com>
Tue, 16 Jun 2020 09:06:34 +0000 (11:06 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Tue, 16 Jun 2020 09:34:37 +0000 (11:34 +0200)
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@proxmox.com>
src/Makefile
src/grid/DiskList.js [new file with mode: 0644]
src/window/DiskSmart.js [new file with mode: 0644]

index 26961030968b250ce3dfa1070d6e7f15cea3e1f3..3311bbefb226519aa2723454772506e86008831e 100644 (file)
@@ -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 (file)
index 0000000..03d6725
--- /dev/null
@@ -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 (file)
index 0000000..e41d798
--- /dev/null
@@ -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',
+    });
+});