[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