]> git.proxmox.com Git - proxmox-widget-toolkit.git/blame - src/panel/DiskList.js
DiskList: render osdid-list if present
[proxmox-widget-toolkit.git] / src / panel / DiskList.js
CommitLineData
402991a7
DC
1Ext.define('pmx-disk-list', {
2 extend: 'Ext.data.Model',
3 fields: [
4 'devpath', 'used',
5 { name: 'size', type: 'number' },
938a7b9f 6 { name: 'osdid', type: 'number', defaultValue: -1 },
402991a7
DC
7 {
8 name: 'status',
9 convert: function(value, rec) {
10 if (value) return value;
11 if (rec.data.health) {
12 return rec.data.health;
13 }
bbcc3b3c
DC
14
15 if (rec.data.type === 'partition') {
16 return "";
17 }
18
402991a7
DC
19 return Proxmox.Utils.unknownText;
20 },
21 },
22 {
23 name: 'name',
24 convert: function(value, rec) {
25 if (value) return value;
26 if (rec.data.devpath) return rec.data.devpath;
27 return undefined;
28 },
29 },
30 {
31 name: 'disk-type',
32 convert: function(value, rec) {
33 if (value) return value;
34 if (rec.data.type) return rec.data.type;
35 return undefined;
36 },
37 },
b7e91380 38 'vendor', 'model', 'serial', 'rpm', 'type', 'wearout', 'health', 'mounted',
402991a7
DC
39 ],
40 idProperty: 'devpath',
41});
42
43Ext.define('Proxmox.DiskList', {
d9e5b671 44 extend: 'Ext.tree.Panel',
402991a7
DC
45 alias: 'widget.pmxDiskList',
46
d453438a
FE
47 supportsWipeDisk: false,
48
d9e5b671
FE
49 rootVisible: false,
50
402991a7
DC
51 emptyText: gettext('No Disks found'),
52
53 stateful: true,
d9e5b671 54 stateId: 'tree-node-disks',
402991a7
DC
55
56 controller: {
57 xclass: 'Ext.app.ViewController',
58
59 reload: function() {
60 let me = this;
d9e5b671
FE
61 let view = me.getView();
62
63 let extraParams = {};
64 if (view.includePartitions) {
65 extraParams['include-partitions'] = 1;
66 }
67
68 let url = `${view.baseurl}/list`;
69 me.store.setProxy({
70 type: 'proxmox',
71 extraParams: extraParams,
72 url: url,
73 });
74 me.store.load();
402991a7
DC
75 },
76
77 openSmartWindow: function() {
78 let me = this;
79 let view = me.getView();
80 let selection = view.getSelection();
81 if (!selection || selection.length < 1) return;
82
83 let rec = selection[0];
84 Ext.create('Proxmox.window.DiskSmart', {
85 baseurl: view.baseurl,
86 dev: rec.data.name,
87 }).show();
88 },
89
90 initGPT: function() {
91 let me = this;
92 let view = me.getView();
93 let selection = view.getSelection();
94 if (!selection || selection.length < 1) return;
95
96 let rec = selection[0];
97 Proxmox.Utils.API2Request({
abd4706a 98 url: `${view.exturl}/initgpt`,
402991a7
DC
99 waitMsgTarget: view,
100 method: 'POST',
abd4706a 101 params: { disk: rec.data.name },
4957686b 102 failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
402991a7 103 success: function(response, options) {
4957686b
TL
104 Ext.create('Proxmox.window.TaskProgress', {
105 upid: response.result.data,
abd4706a 106 taskDone: function() {
3824e269
TL
107 me.reload();
108 },
4957686b 109 autoShow: true,
402991a7 110 });
402991a7
DC
111 },
112 });
113 },
114
d453438a
FE
115 wipeDisk: function() {
116 let me = this;
117 let view = me.getView();
118 let selection = view.getSelection();
119 if (!selection || selection.length < 1) return;
120
121 let rec = selection[0];
122 Proxmox.Utils.API2Request({
123 url: `${view.exturl}/wipedisk`,
124 waitMsgTarget: view,
125 method: 'PUT',
126 params: { disk: rec.data.name },
4957686b 127 failure: response => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
d453438a 128 success: function(response, options) {
4957686b
TL
129 Ext.create('Proxmox.window.TaskProgress', {
130 upid: response.result.data,
d453438a
FE
131 taskDone: function() {
132 me.reload();
133 },
4957686b 134 autoShow: true,
d453438a 135 });
d453438a
FE
136 },
137 });
138 },
139
402991a7 140 init: function(view) {
402991a7
DC
141 let nodename = view.nodename || 'localhost';
142 view.baseurl = `/api2/json/nodes/${nodename}/disks`;
abd4706a 143 view.exturl = `/api2/extjs/nodes/${nodename}/disks`;
d9e5b671
FE
144
145 this.store = Ext.create('Ext.data.Store', {
146 model: 'pmx-disk-list',
147 });
148 this.store.on('load', this.onLoad, this);
149
150 Proxmox.Utils.monStoreErrors(view, this.store);
151 this.reload();
402991a7 152 },
402991a7 153
d9e5b671
FE
154 onLoad: function(store, records, success, operation) {
155 let me = this;
156 let view = this.getView();
157
158 if (!success) {
159 Proxmox.Utils.setErrorMask(
160 view,
161 Proxmox.Utils.getResponseErrorMessage(operation.getError()),
162 );
163 return;
164 }
165
166 let disks = {};
167
168 for (const item of records) {
169 let data = item.data;
d9e5b671 170 data.expanded = true;
59551419
HL
171 data.children = data.partitions ?? [];
172 for (let p of data.children) {
173 p['disk-type'] = 'partition';
174 p.iconCls = 'fa fa-fw fa-hdd-o x-fa-tree';
175 p.used = p.used === 'filesystem' ? p.filesystem : p.used;
176 p.parent = data.devpath;
177 p.children = [];
178 p.leaf = true;
179 }
d9e5b671 180 data.iconCls = 'fa fa-fw fa-hdd-o x-fa-tree';
59551419
HL
181 data.leaf = data.children.length === 0;
182
d9e5b671
FE
183 if (!data.parent) {
184 disks[data.devpath] = data;
185 }
186 }
187 for (const item of records) {
188 let data = item.data;
189 if (data.parent) {
190 disks[data.parent].leaf = false;
191 disks[data.parent].children.push(data);
192 }
193 }
194
195 let children = [];
196 for (const [_, device] of Object.entries(disks)) {
197 children.push(device);
198 }
199
200 view.setRootNode({
201 expanded: true,
202 children: children,
203 });
204
205 Proxmox.Utils.setErrorMask(view, false);
402991a7 206 },
402991a7
DC
207 },
208
e2ecec94
FE
209 renderDiskType: function(v) {
210 if (v === undefined) return Proxmox.Utils.unknownText;
211 switch (v) {
212 case 'ssd': return 'SSD';
213 case 'hdd': return 'Hard Disk';
214 case 'usb': return 'USB';
215 default: return v;
216 }
217 },
218
219 renderDiskUsage: function(v, metaData, rec) {
220 let extendedInfo = '';
221 if (rec) {
222 let types = [];
d981e33b
AL
223 if (rec.data['osdid-list'] && rec.data['osdid-list'].length > 0) {
224 for (const id of rec.data['osdid-list'].sort()) {
225 types.push(`OSD.${id.toString()}`);
226 }
227 } else if (rec.data.osdid !== undefined && rec.data.osdid >= 0) {
e2ecec94
FE
228 types.push(`OSD.${rec.data.osdid.toString()}`);
229 }
230 if (rec.data.journals > 0) {
231 types.push('Journal');
232 }
233 if (rec.data.db > 0) {
234 types.push('DB');
235 }
236 if (rec.data.wal > 0) {
237 types.push('WAL');
238 }
239 if (types.length > 0) {
240 extendedInfo = `, Ceph (${types.join(', ')})`;
241 }
242 }
59551419
HL
243 const formatMap = {
244 'bios': 'BIOS boot',
245 'zfsreserved': 'ZFS reserved',
246 'efi': 'EFI',
247 'lvm': 'LVM',
248 'zfs': 'ZFS',
249 };
250
251 v = formatMap[v] || v;
e2ecec94
FE
252 return v ? `${v}${extendedInfo}` : Proxmox.Utils.noText;
253 },
254
402991a7
DC
255 columns: [
256 {
d9e5b671 257 xtype: 'treecolumn',
402991a7
DC
258 header: gettext('Device'),
259 width: 150,
260 sortable: true,
261 dataIndex: 'devpath',
262 },
263 {
264 header: gettext('Type'),
265 width: 80,
266 sortable: true,
267 dataIndex: 'disk-type',
268 renderer: function(v) {
e2ecec94
FE
269 let me = this;
270 return me.renderDiskType(v);
402991a7
DC
271 },
272 },
273 {
274 header: gettext('Usage'),
275 width: 150,
276 sortable: false,
938a7b9f 277 renderer: function(v, metaData, rec) {
e2ecec94
FE
278 let me = this;
279 return me.renderDiskUsage(v, metaData, rec);
938a7b9f 280 },
402991a7
DC
281 dataIndex: 'used',
282 },
283 {
284 header: gettext('Size'),
285 width: 100,
286 align: 'right',
287 sortable: true,
288 renderer: Proxmox.Utils.format_size,
289 dataIndex: 'size',
290 },
291 {
292 header: 'GPT',
293 width: 60,
294 align: 'right',
295 renderer: Proxmox.Utils.format_boolean,
296 dataIndex: 'gpt',
297 },
298 {
299 header: gettext('Vendor'),
300 width: 100,
301 sortable: true,
302 hidden: true,
303 renderer: Ext.String.htmlEncode,
304 dataIndex: 'vendor',
305 },
306 {
307 header: gettext('Model'),
308 width: 200,
309 sortable: true,
310 renderer: Ext.String.htmlEncode,
311 dataIndex: 'model',
312 },
313 {
314 header: gettext('Serial'),
315 width: 200,
316 sortable: true,
317 renderer: Ext.String.htmlEncode,
318 dataIndex: 'serial',
319 },
320 {
321 header: 'S.M.A.R.T.',
322 width: 100,
323 sortable: true,
324 renderer: Ext.String.htmlEncode,
325 dataIndex: 'status',
326 },
b7e91380
HL
327 {
328 header: 'Mounted',
329 width: 60,
330 align: 'right',
331 renderer: Proxmox.Utils.format_boolean,
332 dataIndex: 'mounted',
333 },
402991a7
DC
334 {
335 header: 'Wearout',
336 width: 90,
337 sortable: true,
338 align: 'right',
339 dataIndex: 'wearout',
340 renderer: function(value) {
341 if (Ext.isNumeric(value)) {
342 return (100 - value).toString() + '%';
343 }
344 return 'N/A';
345 },
346 },
347 ],
348
349 listeners: {
350 itemdblclick: 'openSmartWindow',
351 },
e3a5040c
FE
352
353 initComponent: function() {
354 let me = this;
355
356 let tbar = [
357 {
358 text: gettext('Reload'),
359 handler: 'reload',
360 },
361 {
362 xtype: 'proxmoxButton',
363 text: gettext('Show S.M.A.R.T. values'),
364 parentXType: 'treepanel',
365 disabled: true,
366 enableFn: function(rec) {
367 if (!rec || rec.data.parent) {
368 return false;
369 } else {
370 return true;
371 }
372 },
373 handler: 'openSmartWindow',
374 },
375 {
376 xtype: 'proxmoxButton',
377 text: gettext('Initialize Disk with GPT'),
378 parentXType: 'treepanel',
379 disabled: true,
380 enableFn: function(rec) {
381 if (!rec || rec.data.parent ||
382 (rec.data.used && rec.data.used !== 'unused')) {
383 return false;
384 } else {
385 return true;
386 }
387 },
388 handler: 'initGPT',
389 },
390 ];
391
d453438a
FE
392 if (me.supportsWipeDisk) {
393 tbar.push('-');
394 tbar.push({
395 xtype: 'proxmoxButton',
396 text: gettext('Wipe Disk'),
397 parentXType: 'treepanel',
398 dangerous: true,
399 confirmMsg: function(rec) {
400 const data = rec.data;
401
402 let mainMessage = Ext.String.format(
403 gettext('Are you sure you want to wipe {0}?'),
404 data.devpath,
405 );
406 mainMessage += `<br> ${gettext('All data on the device will be lost!')}`;
407
408 const type = me.renderDiskType(data["disk-type"]);
409
410 let usage;
411 if (data.children.length > 0) {
412 const partitionUsage = data.children.map(
413 partition => me.renderDiskUsage(partition.used),
414 ).join(', ');
415 usage = `${gettext('Partitions')} (${partitionUsage})`;
416 } else {
417 usage = me.renderDiskUsage(data.used, undefined, rec);
418 }
419
420 const size = Proxmox.Utils.format_size(data.size);
421 const serial = Ext.String.htmlEncode(data.serial);
422
423 let additionalInfo = `${gettext('Type')}: ${type}<br>`;
424 additionalInfo += `${gettext('Usage')}: ${usage}<br>`;
425 additionalInfo += `${gettext('Size')}: ${size}<br>`;
426 additionalInfo += `${gettext('Serial')}: ${serial}`;
427
428 return `${mainMessage}<br><br>${additionalInfo}`;
429 },
430 disabled: true,
d453438a
FE
431 handler: 'wipeDisk',
432 });
433 }
434
e3a5040c
FE
435 me.tbar = tbar;
436
437 me.callParent();
438 },
402991a7 439});