]> git.proxmox.com Git - proxmox-widget-toolkit.git/blame - src/panel/DiskList.js
ui: disks: add 'mounted' column
[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;
170 data.leaf = true;
171 data.expanded = true;
172 data.children = [];
173 data.iconCls = 'fa fa-fw fa-hdd-o x-fa-tree';
174 if (!data.parent) {
175 disks[data.devpath] = data;
176 }
177 }
178 for (const item of records) {
179 let data = item.data;
180 if (data.parent) {
181 disks[data.parent].leaf = false;
182 disks[data.parent].children.push(data);
183 }
184 }
185
186 let children = [];
187 for (const [_, device] of Object.entries(disks)) {
188 children.push(device);
189 }
190
191 view.setRootNode({
192 expanded: true,
193 children: children,
194 });
195
196 Proxmox.Utils.setErrorMask(view, false);
402991a7 197 },
402991a7
DC
198 },
199
e2ecec94
FE
200 renderDiskType: function(v) {
201 if (v === undefined) return Proxmox.Utils.unknownText;
202 switch (v) {
203 case 'ssd': return 'SSD';
204 case 'hdd': return 'Hard Disk';
205 case 'usb': return 'USB';
206 default: return v;
207 }
208 },
209
210 renderDiskUsage: function(v, metaData, rec) {
211 let extendedInfo = '';
212 if (rec) {
213 let types = [];
214 if (rec.data.osdid !== undefined && rec.data.osdid >= 0) {
215 types.push(`OSD.${rec.data.osdid.toString()}`);
216 }
217 if (rec.data.journals > 0) {
218 types.push('Journal');
219 }
220 if (rec.data.db > 0) {
221 types.push('DB');
222 }
223 if (rec.data.wal > 0) {
224 types.push('WAL');
225 }
226 if (types.length > 0) {
227 extendedInfo = `, Ceph (${types.join(', ')})`;
228 }
229 }
230 return v ? `${v}${extendedInfo}` : Proxmox.Utils.noText;
231 },
232
402991a7
DC
233 columns: [
234 {
d9e5b671 235 xtype: 'treecolumn',
402991a7
DC
236 header: gettext('Device'),
237 width: 150,
238 sortable: true,
239 dataIndex: 'devpath',
240 },
241 {
242 header: gettext('Type'),
243 width: 80,
244 sortable: true,
245 dataIndex: 'disk-type',
246 renderer: function(v) {
e2ecec94
FE
247 let me = this;
248 return me.renderDiskType(v);
402991a7
DC
249 },
250 },
251 {
252 header: gettext('Usage'),
253 width: 150,
254 sortable: false,
938a7b9f 255 renderer: function(v, metaData, rec) {
e2ecec94
FE
256 let me = this;
257 return me.renderDiskUsage(v, metaData, rec);
938a7b9f 258 },
402991a7
DC
259 dataIndex: 'used',
260 },
261 {
262 header: gettext('Size'),
263 width: 100,
264 align: 'right',
265 sortable: true,
266 renderer: Proxmox.Utils.format_size,
267 dataIndex: 'size',
268 },
269 {
270 header: 'GPT',
271 width: 60,
272 align: 'right',
273 renderer: Proxmox.Utils.format_boolean,
274 dataIndex: 'gpt',
275 },
276 {
277 header: gettext('Vendor'),
278 width: 100,
279 sortable: true,
280 hidden: true,
281 renderer: Ext.String.htmlEncode,
282 dataIndex: 'vendor',
283 },
284 {
285 header: gettext('Model'),
286 width: 200,
287 sortable: true,
288 renderer: Ext.String.htmlEncode,
289 dataIndex: 'model',
290 },
291 {
292 header: gettext('Serial'),
293 width: 200,
294 sortable: true,
295 renderer: Ext.String.htmlEncode,
296 dataIndex: 'serial',
297 },
298 {
299 header: 'S.M.A.R.T.',
300 width: 100,
301 sortable: true,
302 renderer: Ext.String.htmlEncode,
303 dataIndex: 'status',
304 },
b7e91380
HL
305 {
306 header: 'Mounted',
307 width: 60,
308 align: 'right',
309 renderer: Proxmox.Utils.format_boolean,
310 dataIndex: 'mounted',
311 },
402991a7
DC
312 {
313 header: 'Wearout',
314 width: 90,
315 sortable: true,
316 align: 'right',
317 dataIndex: 'wearout',
318 renderer: function(value) {
319 if (Ext.isNumeric(value)) {
320 return (100 - value).toString() + '%';
321 }
322 return 'N/A';
323 },
324 },
325 ],
326
327 listeners: {
328 itemdblclick: 'openSmartWindow',
329 },
e3a5040c
FE
330
331 initComponent: function() {
332 let me = this;
333
334 let tbar = [
335 {
336 text: gettext('Reload'),
337 handler: 'reload',
338 },
339 {
340 xtype: 'proxmoxButton',
341 text: gettext('Show S.M.A.R.T. values'),
342 parentXType: 'treepanel',
343 disabled: true,
344 enableFn: function(rec) {
345 if (!rec || rec.data.parent) {
346 return false;
347 } else {
348 return true;
349 }
350 },
351 handler: 'openSmartWindow',
352 },
353 {
354 xtype: 'proxmoxButton',
355 text: gettext('Initialize Disk with GPT'),
356 parentXType: 'treepanel',
357 disabled: true,
358 enableFn: function(rec) {
359 if (!rec || rec.data.parent ||
360 (rec.data.used && rec.data.used !== 'unused')) {
361 return false;
362 } else {
363 return true;
364 }
365 },
366 handler: 'initGPT',
367 },
368 ];
369
d453438a
FE
370 if (me.supportsWipeDisk) {
371 tbar.push('-');
372 tbar.push({
373 xtype: 'proxmoxButton',
374 text: gettext('Wipe Disk'),
375 parentXType: 'treepanel',
376 dangerous: true,
377 confirmMsg: function(rec) {
378 const data = rec.data;
379
380 let mainMessage = Ext.String.format(
381 gettext('Are you sure you want to wipe {0}?'),
382 data.devpath,
383 );
384 mainMessage += `<br> ${gettext('All data on the device will be lost!')}`;
385
386 const type = me.renderDiskType(data["disk-type"]);
387
388 let usage;
389 if (data.children.length > 0) {
390 const partitionUsage = data.children.map(
391 partition => me.renderDiskUsage(partition.used),
392 ).join(', ');
393 usage = `${gettext('Partitions')} (${partitionUsage})`;
394 } else {
395 usage = me.renderDiskUsage(data.used, undefined, rec);
396 }
397
398 const size = Proxmox.Utils.format_size(data.size);
399 const serial = Ext.String.htmlEncode(data.serial);
400
401 let additionalInfo = `${gettext('Type')}: ${type}<br>`;
402 additionalInfo += `${gettext('Usage')}: ${usage}<br>`;
403 additionalInfo += `${gettext('Size')}: ${size}<br>`;
404 additionalInfo += `${gettext('Serial')}: ${serial}`;
405
406 return `${mainMessage}<br><br>${additionalInfo}`;
407 },
408 disabled: true,
d453438a
FE
409 handler: 'wipeDisk',
410 });
411 }
412
e3a5040c
FE
413 me.tbar = tbar;
414
415 me.callParent();
416 },
402991a7 417});