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 <d.csapak@proxmox.com>
This commit is contained in:
Dominik Csapak 2020-06-16 11:06:34 +02:00 committed by Thomas Lamprecht
parent 4f0c076470
commit 402991a79d
3 changed files with 368 additions and 0 deletions

View 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 \

233
src/grid/DiskList.js Normal file
View File

@ -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',
},
});

133
src/window/DiskSmart.js Normal file
View File

@ -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',
});
});