]> git.proxmox.com Git - proxmox-widget-toolkit.git/blame - src/grid/DiskList.js
convert disk list to disk tree and conditionally include partitions
[proxmox-widget-toolkit.git] / src / grid / 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 }
14 return Proxmox.Utils.unknownText;
15 },
16 },
17 {
18 name: 'name',
19 convert: function(value, rec) {
20 if (value) return value;
21 if (rec.data.devpath) return rec.data.devpath;
22 return undefined;
23 },
24 },
25 {
26 name: 'disk-type',
27 convert: function(value, rec) {
28 if (value) return value;
29 if (rec.data.type) return rec.data.type;
30 return undefined;
31 },
32 },
33 'vendor', 'model', 'serial', 'rpm', 'type', 'wearout', 'health',
34 ],
35 idProperty: 'devpath',
36});
37
38Ext.define('Proxmox.DiskList', {
d9e5b671 39 extend: 'Ext.tree.Panel',
402991a7
DC
40 alias: 'widget.pmxDiskList',
41
d9e5b671
FE
42 rootVisible: false,
43
402991a7
DC
44 emptyText: gettext('No Disks found'),
45
46 stateful: true,
d9e5b671 47 stateId: 'tree-node-disks',
402991a7
DC
48
49 controller: {
50 xclass: 'Ext.app.ViewController',
51
52 reload: function() {
53 let me = this;
d9e5b671
FE
54 let view = me.getView();
55
56 let extraParams = {};
57 if (view.includePartitions) {
58 extraParams['include-partitions'] = 1;
59 }
60
61 let url = `${view.baseurl}/list`;
62 me.store.setProxy({
63 type: 'proxmox',
64 extraParams: extraParams,
65 url: url,
66 });
67 me.store.load();
402991a7
DC
68 },
69
70 openSmartWindow: function() {
71 let me = this;
72 let view = me.getView();
73 let selection = view.getSelection();
74 if (!selection || selection.length < 1) return;
75
76 let rec = selection[0];
77 Ext.create('Proxmox.window.DiskSmart', {
78 baseurl: view.baseurl,
79 dev: rec.data.name,
80 }).show();
81 },
82
83 initGPT: function() {
84 let me = this;
85 let view = me.getView();
86 let selection = view.getSelection();
87 if (!selection || selection.length < 1) return;
88
89 let rec = selection[0];
90 Proxmox.Utils.API2Request({
abd4706a 91 url: `${view.exturl}/initgpt`,
402991a7
DC
92 waitMsgTarget: view,
93 method: 'POST',
abd4706a 94 params: { disk: rec.data.name },
402991a7
DC
95 failure: function(response, options) {
96 Ext.Msg.alert(gettext('Error'), response.htmlStatus);
97 },
98 success: function(response, options) {
99 var upid = response.result.data;
100 var win = Ext.create('Proxmox.window.TaskProgress', {
101 upid: upid,
abd4706a 102 taskDone: function() {
3824e269
TL
103 me.reload();
104 },
402991a7
DC
105 });
106 win.show();
107 },
108 });
109 },
110
111 init: function(view) {
402991a7
DC
112 let nodename = view.nodename || 'localhost';
113 view.baseurl = `/api2/json/nodes/${nodename}/disks`;
abd4706a 114 view.exturl = `/api2/extjs/nodes/${nodename}/disks`;
d9e5b671
FE
115
116 this.store = Ext.create('Ext.data.Store', {
117 model: 'pmx-disk-list',
118 });
119 this.store.on('load', this.onLoad, this);
120
121 Proxmox.Utils.monStoreErrors(view, this.store);
122 this.reload();
402991a7 123 },
402991a7 124
d9e5b671
FE
125 onLoad: function(store, records, success, operation) {
126 let me = this;
127 let view = this.getView();
128
129 if (!success) {
130 Proxmox.Utils.setErrorMask(
131 view,
132 Proxmox.Utils.getResponseErrorMessage(operation.getError()),
133 );
134 return;
135 }
136
137 let disks = {};
138
139 for (const item of records) {
140 let data = item.data;
141 data.leaf = true;
142 data.expanded = true;
143 data.children = [];
144 data.iconCls = 'fa fa-fw fa-hdd-o x-fa-tree';
145 if (!data.parent) {
146 disks[data.devpath] = data;
147 }
148 }
149 for (const item of records) {
150 let data = item.data;
151 if (data.parent) {
152 disks[data.parent].leaf = false;
153 disks[data.parent].children.push(data);
154 }
155 }
156
157 let children = [];
158 for (const [_, device] of Object.entries(disks)) {
159 children.push(device);
160 }
161
162 view.setRootNode({
163 expanded: true,
164 children: children,
165 });
166
167 Proxmox.Utils.setErrorMask(view, false);
402991a7 168 },
402991a7
DC
169 },
170
171 tbar: [
172 {
173 text: gettext('Reload'),
174 handler: 'reload',
175 },
176 {
177 xtype: 'proxmoxButton',
178 text: gettext('Show S.M.A.R.T. values'),
d9e5b671 179 parentXType: 'treepanel',
402991a7 180 disabled: true,
d9e5b671
FE
181 enableFn: function(rec) {
182 if (!rec || rec.data.parent) {
183 return false;
184 } else {
185 return true;
186 }
187 },
402991a7
DC
188 handler: 'openSmartWindow',
189 },
190 {
191 xtype: 'proxmoxButton',
192 text: gettext('Initialize Disk with GPT'),
d9e5b671 193 parentXType: 'treepanel',
402991a7
DC
194 disabled: true,
195 enableFn: function(rec) {
d9e5b671
FE
196 if (!rec || rec.data.parent ||
197 (rec.data.used && rec.data.used !== 'unused')) {
402991a7
DC
198 return false;
199 } else {
200 return true;
201 }
202 },
203 handler: 'initGPT',
204 },
205 ],
206
207 columns: [
208 {
d9e5b671 209 xtype: 'treecolumn',
402991a7
DC
210 header: gettext('Device'),
211 width: 150,
212 sortable: true,
213 dataIndex: 'devpath',
214 },
215 {
216 header: gettext('Type'),
217 width: 80,
218 sortable: true,
219 dataIndex: 'disk-type',
220 renderer: function(v) {
221 if (v === undefined) return Proxmox.Utils.unknownText;
222 switch (v) {
223 case 'ssd': return 'SSD';
224 case 'hdd': return 'Hard Disk';
225 case 'usb': return 'USB';
226 default: return v;
227 }
228 },
229 },
230 {
231 header: gettext('Usage'),
232 width: 150,
233 sortable: false,
938a7b9f
DC
234 renderer: function(v, metaData, rec) {
235 let extendedInfo = ' ';
236 if (rec) {
237 let types = [];
238 if (rec.data.osdid !== undefined && rec.data.osdid >= 0) {
239 types.push(`OSD.${rec.data.osdid.toString()}`);
240 }
241 if (rec.data.journals > 0) {
242 types.push('Journal');
243 }
244 if (rec.data.db > 0) {
245 types.push('DB');
246 }
247 if (rec.data.wal > 0) {
248 types.push('WAL');
249 }
250 if (types.length > 0) {
251 extendedInfo = `, Ceph (${types.join(', ')})`;
252 }
253 }
254 return v ? `${v}${extendedInfo}` : Proxmox.Utils.noText;
255 },
402991a7
DC
256 dataIndex: 'used',
257 },
258 {
259 header: gettext('Size'),
260 width: 100,
261 align: 'right',
262 sortable: true,
263 renderer: Proxmox.Utils.format_size,
264 dataIndex: 'size',
265 },
266 {
267 header: 'GPT',
268 width: 60,
269 align: 'right',
270 renderer: Proxmox.Utils.format_boolean,
271 dataIndex: 'gpt',
272 },
273 {
274 header: gettext('Vendor'),
275 width: 100,
276 sortable: true,
277 hidden: true,
278 renderer: Ext.String.htmlEncode,
279 dataIndex: 'vendor',
280 },
281 {
282 header: gettext('Model'),
283 width: 200,
284 sortable: true,
285 renderer: Ext.String.htmlEncode,
286 dataIndex: 'model',
287 },
288 {
289 header: gettext('Serial'),
290 width: 200,
291 sortable: true,
292 renderer: Ext.String.htmlEncode,
293 dataIndex: 'serial',
294 },
295 {
296 header: 'S.M.A.R.T.',
297 width: 100,
298 sortable: true,
299 renderer: Ext.String.htmlEncode,
300 dataIndex: 'status',
301 },
302 {
303 header: 'Wearout',
304 width: 90,
305 sortable: true,
306 align: 'right',
307 dataIndex: 'wearout',
308 renderer: function(value) {
309 if (Ext.isNumeric(value)) {
310 return (100 - value).toString() + '%';
311 }
312 return 'N/A';
313 },
314 },
315 ],
316
317 listeners: {
318 itemdblclick: 'openSmartWindow',
319 },
320});