]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/node/Disks.js
8e79dc8420d773c360ae66324bcf1d744f9f8642
[pve-manager.git] / www / manager6 / node / Disks.js
1 Ext.define('PVE.node.DiskList', {
2 extend: 'Ext.grid.GridPanel',
3 alias: 'widget.pveNodeDiskList',
4 emptyText: gettext('No Disks found'),
5 stateful: true,
6 stateId: 'grid-node-disks',
7 columns: [
8 {
9 header: gettext('Device'),
10 width: 100,
11 sortable: true,
12 dataIndex: 'devpath'
13 },
14 {
15 header: gettext('Type'),
16 width: 80,
17 sortable: true,
18 dataIndex: 'type',
19 renderer: function(v) {
20 if (v === 'ssd') {
21 return 'SSD';
22 } else if (v === 'hdd') {
23 return 'Hard Disk';
24 } else if (v === 'usb'){
25 return 'USB';
26 } else {
27 return gettext('Unknown');
28 }
29 }
30 },
31 {
32 header: gettext('Usage'),
33 width: 80,
34 sortable: false,
35 renderer: function(v, metaData, rec) {
36 if (rec) {
37 if (rec.data.osdid >= 0) {
38 var bluestore = '';
39 if (rec.data.bluestore === 1) {
40 bluestore = ' (Bluestore)';
41 }
42 return "Ceph osd." + rec.data.osdid.toString() + bluestore;
43 }
44
45 var types = [];
46 if (rec.data.journals > 0) {
47 types.push('Journal');
48 }
49
50 if (rec.data.db > 0) {
51 types.push('DB');
52 }
53
54 if (rec.data.wal > 0) {
55 types.push('WAL');
56 }
57
58 if (types.length > 0) {
59 return 'Ceph (' + types.join(', ') + ')';
60 }
61 }
62
63 return v || Proxmox.Utils.noText;
64 },
65 dataIndex: 'used'
66 },
67 {
68 header: gettext('Size'),
69 width: 100,
70 align: 'right',
71 sortable: true,
72 renderer: Proxmox.Utils.format_size,
73 dataIndex: 'size'
74 },
75 {
76 header: 'GPT',
77 width: 60,
78 align: 'right',
79 renderer: function(value) {
80 if (value) {
81 return Proxmox.Utils.yesText;
82 } else {
83 return Proxmox.Utils.noText;
84 }
85 },
86 dataIndex: 'gpt'
87 },
88 {
89 header: gettext('Vendor'),
90 width: 100,
91 sortable: true,
92 renderer: Ext.String.htmlEncode,
93 dataIndex: 'vendor'
94 },
95 {
96 header: gettext('Model'),
97 width: 200,
98 sortable: true,
99 renderer: Ext.String.htmlEncode,
100 dataIndex: 'model'
101 },
102 {
103 header: gettext('Serial'),
104 width: 200,
105 sortable: true,
106 renderer: Ext.String.htmlEncode,
107 dataIndex: 'serial'
108 },
109 {
110 header: 'S.M.A.R.T.',
111 width: 100,
112 sortable: true,
113 renderer: Ext.String.htmlEncode,
114 dataIndex: 'health'
115 },
116 {
117 header: 'Wearout',
118 width: 100,
119 sortable: true,
120 dataIndex: 'wearout',
121 renderer: function(value) {
122 if (Ext.isNumeric(value)) {
123 return (100 - value).toString() + '%';
124 }
125 return 'N/A';
126 }
127 }
128 ],
129
130 initComponent: function() {
131 /*jslint confusion: true */
132 var me = this;
133
134 var nodename = me.pveSelNode.data.node;
135 if (!nodename) {
136 throw "no node name specified";
137 }
138
139 var sm = Ext.create('Ext.selection.RowModel', {});
140
141 var store = Ext.create('Ext.data.Store', {
142 storeid: 'node-disk-list' + nodename,
143 model: 'node-disk-list',
144 proxy: {
145 type: 'pve',
146 url: "/api2/json/nodes/" + nodename + "/disks/list"
147 },
148 sorters: [
149 {
150 property : 'dev',
151 direction: 'ASC'
152 }
153 ]
154 });
155
156 var reloadButton = Ext.create('Proxmox.button.Button', {
157 text: gettext('Reload'),
158 handler: function() {
159 me.store.load();
160 }
161 });
162
163 var smartButton = Ext.create('Proxmox.button.Button', {
164 text: gettext('Show S.M.A.R.T. values'),
165 selModel: sm,
166 enableFn: function() {
167 return !!sm.getSelection().length;
168 },
169 disabled: true,
170 handler: function() {
171 var rec = sm.getSelection()[0];
172
173 var win = Ext.create('PVE.DiskSmartWindow', {
174 nodename: nodename,
175 dev: rec.data.devpath
176 });
177 win.show();
178 }
179 });
180
181 var initButton = Ext.create('Proxmox.button.Button', {
182 text: gettext('Initialize Disk with GPT'),
183 selModel: sm,
184 enableFn: function() {
185 var selection = sm.getSelection();
186
187 if (!selection.length || selection[0].data.used) {
188 return false;
189 } else {
190 return true;
191 }
192 },
193 disabled: true,
194
195 handler: function() {
196 var rec = sm.getSelection()[0];
197 Proxmox.Utils.API2Request({
198 url: '/api2/extjs/nodes/' + nodename + '/disks/initgpt',
199 waitMsgTarget: me,
200 method: 'POST',
201 params: { disk: rec.data.devpath},
202 failure: function(response, options) {
203 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
204 },
205 success: function(response, options) {
206 var upid = response.result.data;
207 var win = Ext.create('Proxmox.window.TaskProgress', {
208 upid: upid
209 });
210 win.show();
211 }
212 });
213 }
214 });
215
216 me.loadCount = 1; // avoid duplicate loadmask
217 Proxmox.Utils.monStoreErrors(me, store);
218
219 Ext.apply(me, {
220 store: store,
221 selModel: sm,
222 tbar: [ reloadButton, smartButton, initButton ],
223 listeners: {
224 itemdblclick: function() {
225 var rec = sm.getSelection()[0];
226
227 var win = Ext.create('PVE.DiskSmartWindow', {
228 nodename: nodename,
229 dev: rec.data.devpath
230 });
231 win.show();
232 }
233 }
234 });
235
236
237 me.callParent();
238 me.store.load();
239 }
240 }, function() {
241
242 Ext.define('node-disk-list', {
243 extend: 'Ext.data.Model',
244 fields: [ 'devpath', 'used', { name: 'size', type: 'number'},
245 {name: 'osdid', type: 'number'},
246 'vendor', 'model', 'serial', 'rpm', 'type', 'health', 'wearout' ],
247 idProperty: 'devpath'
248 });
249 });
250
251 Ext.define('PVE.DiskSmartWindow', {
252 extend: 'Ext.window.Window',
253 alias: 'widget.pveSmartWindow',
254
255 modal: true,
256
257 items: [
258 {
259 xtype: 'gridpanel',
260 layout: {
261 type: 'fit'
262 },
263 emptyText: gettext('No S.M.A.R.T. Values'),
264 scrollable: true,
265 flex: 1,
266 itemId: 'smarts',
267 reserveScrollbar: true,
268 columns: [
269 { text: 'ID', dataIndex: 'id', width: 50 },
270 { text: gettext('Attribute'), flex: 1, dataIndex: 'name', renderer: Ext.String.htmlEncode },
271 { text: gettext('Value'), dataIndex: 'raw', renderer: Ext.String.htmlEncode },
272 { text: gettext('Normalized'), dataIndex: 'value', width: 60},
273 { text: gettext('Threshold'), dataIndex: 'threshold', width: 60},
274 { text: gettext('Worst'), dataIndex: 'worst', width: 60},
275 { text: gettext('Flags'), dataIndex: 'flags'},
276 { text: gettext('Failing'), dataIndex: 'fail', renderer: Ext.String.htmlEncode }
277 ]
278 },
279 {
280 xtype: 'component',
281 itemId: 'text',
282 layout: {
283 type: 'fit'
284 },
285 hidden: true,
286 style: {
287 'background-color': 'white',
288 'white-space': 'pre',
289 'font-family': 'monospace'
290 }
291 }
292 ],
293
294 buttons: [
295 {
296 text: gettext('Reload'),
297 name: 'reload',
298 handler: function() {
299 var me = this;
300 me.up('window').store.reload();
301 }
302 },
303 {
304 text: gettext('Close'),
305 name: 'close',
306 handler: function() {
307 var me = this;
308 me.up('window').close();
309 }
310 }
311 ],
312
313 layout: {
314 type: 'vbox',
315 align: 'stretch'
316 },
317 width: 800,
318 height: 500,
319 minWidth: 600,
320 minHeight: 400,
321 bodyPadding: 5,
322 title: gettext('S.M.A.R.T. Values'),
323
324 initComponent: function() {
325 var me = this;
326
327 var nodename = me.nodename;
328 if (!nodename) {
329 throw "no node name specified";
330 }
331
332 var dev = me.dev;
333 if (!dev) {
334 throw "no device specified";
335 }
336
337 me.store = Ext.create('Ext.data.Store', {
338 model: 'disk-smart',
339 proxy: {
340 type: 'pve',
341 url: "/api2/json/nodes/" + nodename + "/disks/smart?disk=" + dev
342 }
343 });
344
345 me.callParent();
346 var grid = me.down('#smarts');
347 var text = me.down('#text');
348
349 Proxmox.Utils.monStoreErrors(grid, me.store);
350 me.mon(me.store, 'load', function(s, records, success) {
351 if (success && records.length > 0) {
352 var rec = records[0];
353 switch (rec.data.type) {
354 case 'text':
355 grid.setVisible(false);
356 text.setVisible(true);
357 text.setHtml(Ext.String.htmlEncode(rec.data.text));
358 break;
359 default:
360 // includes 'ata'
361 // cannot use empty case because
362 // of jslint
363 grid.setVisible(true);
364 text.setVisible(false);
365 grid.setStore(rec.attributes());
366 break;
367 }
368 }
369 });
370
371 me.store.load();
372 }
373 }, function() {
374
375 Ext.define('disk-smart', {
376 extend: 'Ext.data.Model',
377 fields: [
378 { name:'health'},
379 { name:'type'},
380 { name:'text'}
381 ],
382 hasMany: {model: 'smart-attribute', name: 'attributes'}
383 });
384 Ext.define('smart-attribute', {
385 extend: 'Ext.data.Model',
386 fields: [
387 { name:'id', type:'number' }, 'name', 'value', 'worst', 'threshold', 'flags', 'fail', 'raw'
388 ]
389 });
390 });