[pve-devel] [PATCH manager 2/5] add DiskList panel
Dominik Csapak
d.csapak at proxmox.com
Tue Aug 23 14:49:14 CEST 2016
this patch adds a disk list panel which:
lists the disks with typical columns
(type, vendor, serial, smart, wearout, etc.)
and with a doubleclick you can show the smart attributes
and with a click on init disk you can initialize a disk
with a gpt table
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
www/manager6/Makefile | 1 +
www/manager6/Utils.js | 1 +
www/manager6/node/Disks.js | 322 +++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 324 insertions(+)
create mode 100644 www/manager6/node/Disks.js
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index 41c564f..670eac6 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -106,6 +106,7 @@ JSSRC= \
ceph/Crush.js \
ceph/Status.js \
ceph/Config.js \
+ node/Disks.js \
node/DNSEdit.js \
node/DNSView.js \
node/TimeView.js \
diff --git a/www/manager6/Utils.js b/www/manager6/Utils.js
index 7ec140d..a31beb9 100644
--- a/www/manager6/Utils.js
+++ b/www/manager6/Utils.js
@@ -541,6 +541,7 @@ Ext.define('PVE.Utils', { statics: {
},
task_desc_table: {
+ diskinit: [ 'Disk', gettext('Initialize GPT') ],
vncproxy: [ 'VM/CT', gettext('Console') ],
spiceproxy: [ 'VM/CT', gettext('Console') + ' (Spice)' ],
vncshell: [ '', gettext('Shell') ],
diff --git a/www/manager6/node/Disks.js b/www/manager6/node/Disks.js
new file mode 100644
index 0000000..bf6d0e2
--- /dev/null
+++ b/www/manager6/node/Disks.js
@@ -0,0 +1,322 @@
+Ext.define('PVE.node.DiskList', {
+ extend: 'Ext.grid.GridPanel',
+ alias: 'widget.pveNodeDiskList',
+ emptyText: gettext('No Disks found'),
+ columns: [
+ {
+ header: gettext('Device'),
+ width: 100,
+ sortable: true,
+ dataIndex: 'devpath'
+ },
+ {
+ header: gettext('Type'),
+ width: 80,
+ sortable: true,
+ dataIndex: 'type',
+ renderer: function(v) {
+ if (v === 'ssd') {
+ return 'SSD';
+ } else if (v === 'hdd') {
+ return 'Hard Disk';
+ } else if (v === 'usb'){
+ return 'USB';
+ } else {
+ return gettext('Unknown');
+ }
+ }
+ },
+ {
+ header: gettext('Usage'),
+ width: 80,
+ sortable: false,
+ renderer: function(v, metaData, rec) {
+ if (rec && (rec.data.osdid >= 0)) {
+ return "osd." + rec.data.osdid.toString();
+ }
+ return v || PVE.Utils.noText;
+ },
+ dataIndex: 'used'
+ },
+ {
+ header: gettext('Size'),
+ width: 100,
+ align: 'right',
+ sortable: true,
+ renderer: PVE.Utils.format_size,
+ dataIndex: 'size'
+ },
+ {
+ header: gettext('GPT'),
+ width: 60,
+ align: 'right',
+ renderer: function(value) {
+ if (value) {
+ return PVE.Utils.yesText;
+ } else {
+ return PVE.Utils.noText;
+ }
+ },
+ dataIndex: 'gpt'
+ },
+ {
+ header: gettext('Vendor'),
+ width: 100,
+ sortable: true,
+ dataIndex: 'vendor'
+ },
+ {
+ header: gettext('Model'),
+ width: 200,
+ sortable: true,
+ dataIndex: 'model'
+ },
+ {
+ header: gettext('Serial'),
+ width: 200,
+ sortable: true,
+ dataIndex: 'serial'
+ },
+ {
+ header: 'S.M.A.R.T.',
+ width: 100,
+ sortable: true,
+ dataIndex: 'health'
+ },
+ {
+ header: 'Wearout',
+ width: 100,
+ sortable: true,
+ dataIndex: 'wearout',
+ renderer: function(value) {
+ if (Ext.isNumeric(value)) {
+ return (100 - value).toString() + '%';
+ }
+ return 'N/A';
+ }
+ }
+ ],
+
+ initComponent: function() {
+ /*jslint confusion: true */
+ var me = this;
+
+ var nodename = me.pveSelNode.data.node;
+ if (!nodename) {
+ throw "no node name specified";
+ }
+
+ var sm = Ext.create('Ext.selection.RowModel', {});
+
+ var store = Ext.create('Ext.data.Store', {
+ storeid: 'node-disk-list' + nodename,
+ model: 'node-disk-list',
+ proxy: {
+ type: 'pve',
+ url: "/api2/json/nodes/" + nodename + "/disks/list"
+ },
+ sorters: [
+ {
+ property : 'dev',
+ direction: 'ASC'
+ }
+ ]
+ });
+
+ var reloadButton = Ext.create('PVE.button.Button', {
+ text: gettext('Reload'),
+ handler: function() {
+ me.store.load();
+ }
+ });
+
+ var smartButton = Ext.create('PVE.button.Button', {
+ text: gettext('Show S.M.A.R.T. values'),
+ selModel: sm,
+ enableFn: function() {
+ return !!sm.getSelection().length;
+ },
+ disabled: true,
+ handler: function() {
+ var rec = sm.getSelection()[0];
+
+ var win = Ext.create('PVE.DiskSmartWindow', {
+ nodename: nodename,
+ dev: rec.data.devpath
+ });
+ win.show();
+ }
+ });
+
+ var initButton = Ext.create('PVE.button.Button', {
+ text: gettext('Initialize Disk with GPT'),
+ selModel: sm,
+ enableFn: function() {
+ var selection = sm.getSelection();
+
+ if (!selection.length || selection[0].data.used) {
+ return false;
+ } else {
+ return true;
+ }
+ },
+ disabled: true,
+
+ handler: function() {
+ var rec = sm.getSelection()[0];
+ PVE.Utils.API2Request({
+ url: '/api2/extjs/nodes/' + nodename + '/disks/initgpt',
+ waitMsgTarget: me,
+ 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('PVE.window.TaskProgress', {
+ upid: upid
+ });
+ win.show();
+ }
+ });
+ }
+ });
+
+ Ext.apply(me, {
+ emptyText: gettext('No Disk found'),
+ store: store,
+ selModel: sm,
+ stateful: false,
+ tbar: [ reloadButton, smartButton, initButton ],
+ listeners: {
+ itemdblclick: function() {
+ var rec = sm.getSelection()[0];
+
+ var win = Ext.create('PVE.DiskSmartWindow', {
+ nodename: nodename,
+ dev: rec.data.devpath
+ });
+ win.show();
+ }
+ }
+ });
+
+
+ me.callParent();
+ me.store.load();
+ }
+}, function() {
+
+ Ext.define('node-disk-list', {
+ extend: 'Ext.data.Model',
+ fields: [ 'devpath', 'used', { name: 'size', type: 'number'},
+ {name: 'osdid', type: 'number'},
+ 'vendor', 'model', 'serial', 'rpm', 'type', 'health', 'wearout' ],
+ idProperty: 'devpath'
+ });
+});
+
+Ext.define('PVE.DiskSmartWindow', {
+ extend: 'Ext.window.Window',
+ alias: 'widget.pveSmartWindow',
+
+ 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' },
+ { text: gettext('Value'), dataIndex: 'raw'},
+ { 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'}
+ ]
+ }
+ ],
+
+ 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;
+
+ var nodename = me.nodename;
+ if (!nodename) {
+ throw "no node name specified";
+ }
+
+ var dev = me.dev;
+ if (!dev) {
+ throw "no device specified";
+ }
+
+ me.store = Ext.create('Ext.data.Store', {
+ model: 'smart-attribute',
+ proxy: {
+ type: 'pve',
+ root: 'data.attributes',
+ url: "/api2/json/nodes/" + nodename + "/disks/smart?disk=" + dev
+ }
+ });
+
+ me.callParent();
+ me.down('#smarts').setStore(me.store);
+ me.store.load();
+ }
+}, function() {
+
+ Ext.define('disk-smart', {
+ extend: 'Ext.data.Model',
+ fields: [
+ { name:'health'}
+ ],
+ hasMany: {model: 'smart-attribute', name: 'attributes'}
+ });
+ Ext.define('smart-attribute', {
+ extend: 'Ext.data.Model',
+ fields: [
+ { name:'id', type:'number' }, 'name', 'value', 'worst', 'threshold', 'flags', 'fail', 'raw'
+ ],
+ belongsTo: 'disk-smart'
+ });
+});
--
2.1.4
More information about the pve-devel
mailing list