]>
Commit | Line | Data |
---|---|---|
a36deac4 DC |
1 | Ext.define('PVE.node.DiskList', { |
2 | extend: 'Ext.grid.GridPanel', | |
3 | alias: 'widget.pveNodeDiskList', | |
4 | emptyText: gettext('No Disks found'), | |
e19b93ec DC |
5 | stateful: true, |
6 | stateId: 'grid-node-disks', | |
a36deac4 DC |
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) { | |
6dd90c40 DC |
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 | } | |
a36deac4 | 61 | } |
6dd90c40 | 62 | |
e7ade592 | 63 | return v || Proxmox.Utils.noText; |
a36deac4 DC |
64 | }, |
65 | dataIndex: 'used' | |
66 | }, | |
67 | { | |
68 | header: gettext('Size'), | |
69 | width: 100, | |
70 | align: 'right', | |
71 | sortable: true, | |
e7ade592 | 72 | renderer: Proxmox.Utils.format_size, |
a36deac4 DC |
73 | dataIndex: 'size' |
74 | }, | |
75 | { | |
b9628aa5 | 76 | header: 'GPT', |
a36deac4 DC |
77 | width: 60, |
78 | align: 'right', | |
79 | renderer: function(value) { | |
80 | if (value) { | |
e7ade592 | 81 | return Proxmox.Utils.yesText; |
a36deac4 | 82 | } else { |
e7ade592 | 83 | return Proxmox.Utils.noText; |
a36deac4 DC |
84 | } |
85 | }, | |
86 | dataIndex: 'gpt' | |
87 | }, | |
88 | { | |
89 | header: gettext('Vendor'), | |
90 | width: 100, | |
91 | sortable: true, | |
23280184 | 92 | renderer: Ext.String.htmlEncode, |
a36deac4 DC |
93 | dataIndex: 'vendor' |
94 | }, | |
95 | { | |
96 | header: gettext('Model'), | |
97 | width: 200, | |
98 | sortable: true, | |
23280184 | 99 | renderer: Ext.String.htmlEncode, |
a36deac4 DC |
100 | dataIndex: 'model' |
101 | }, | |
102 | { | |
103 | header: gettext('Serial'), | |
104 | width: 200, | |
105 | sortable: true, | |
23280184 | 106 | renderer: Ext.String.htmlEncode, |
a36deac4 DC |
107 | dataIndex: 'serial' |
108 | }, | |
109 | { | |
110 | header: 'S.M.A.R.T.', | |
111 | width: 100, | |
112 | sortable: true, | |
23280184 | 113 | renderer: Ext.String.htmlEncode, |
a36deac4 DC |
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: { | |
56a353b9 | 145 | type: 'proxmox', |
a36deac4 DC |
146 | url: "/api2/json/nodes/" + nodename + "/disks/list" |
147 | }, | |
148 | sorters: [ | |
149 | { | |
150 | property : 'dev', | |
151 | direction: 'ASC' | |
152 | } | |
153 | ] | |
154 | }); | |
155 | ||
5720fafa | 156 | var reloadButton = Ext.create('Proxmox.button.Button', { |
a36deac4 DC |
157 | text: gettext('Reload'), |
158 | handler: function() { | |
159 | me.store.load(); | |
160 | } | |
161 | }); | |
162 | ||
5720fafa | 163 | var smartButton = Ext.create('Proxmox.button.Button', { |
a36deac4 DC |
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 | ||
5720fafa | 181 | var initButton = Ext.create('Proxmox.button.Button', { |
a36deac4 DC |
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]; | |
e7ade592 | 197 | Proxmox.Utils.API2Request({ |
a36deac4 DC |
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; | |
8cbc11a7 | 207 | var win = Ext.create('Proxmox.window.TaskProgress', { |
a36deac4 DC |
208 | upid: upid |
209 | }); | |
210 | win.show(); | |
211 | } | |
212 | }); | |
213 | } | |
214 | }); | |
215 | ||
2149db2a | 216 | me.loadCount = 1; // avoid duplicate loadmask |
e7ade592 | 217 | Proxmox.Utils.monStoreErrors(me, store); |
2149db2a | 218 | |
a36deac4 | 219 | Ext.apply(me, { |
a36deac4 DC |
220 | store: store, |
221 | selModel: sm, | |
a36deac4 DC |
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 }, | |
23280184 DC |
270 | { text: gettext('Attribute'), flex: 1, dataIndex: 'name', renderer: Ext.String.htmlEncode }, |
271 | { text: gettext('Value'), dataIndex: 'raw', renderer: Ext.String.htmlEncode }, | |
a36deac4 DC |
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'}, | |
23280184 | 276 | { text: gettext('Failing'), dataIndex: 'fail', renderer: Ext.String.htmlEncode } |
a36deac4 | 277 | ] |
d96ac973 DC |
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 | } | |
a36deac4 DC |
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', { | |
2149db2a | 338 | model: 'disk-smart', |
a36deac4 | 339 | proxy: { |
56a353b9 | 340 | type: 'proxmox', |
a36deac4 DC |
341 | url: "/api2/json/nodes/" + nodename + "/disks/smart?disk=" + dev |
342 | } | |
343 | }); | |
344 | ||
345 | me.callParent(); | |
2149db2a | 346 | var grid = me.down('#smarts'); |
d96ac973 | 347 | var text = me.down('#text'); |
2149db2a | 348 | |
e7ade592 | 349 | Proxmox.Utils.monStoreErrors(grid, me.store); |
2149db2a DC |
350 | me.mon(me.store, 'load', function(s, records, success) { |
351 | if (success && records.length > 0) { | |
352 | var rec = records[0]; | |
d96ac973 DC |
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 | } | |
2149db2a DC |
368 | } |
369 | }); | |
370 | ||
a36deac4 DC |
371 | me.store.load(); |
372 | } | |
373 | }, function() { | |
374 | ||
375 | Ext.define('disk-smart', { | |
376 | extend: 'Ext.data.Model', | |
377 | fields: [ | |
d96ac973 DC |
378 | { name:'health'}, |
379 | { name:'type'}, | |
380 | { name:'text'} | |
a36deac4 DC |
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' | |
e62526ea | 388 | ] |
a36deac4 DC |
389 | }); |
390 | }); |