]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/panel/DiskList.js
move DiskList.js from grid/ to panel/
[proxmox-widget-toolkit.git] / src / panel / DiskList.js
1 Ext.define('pmx-disk-list', {
2 extend: 'Ext.data.Model',
3 fields: [
4 'devpath', 'used',
5 { name: 'size', type: 'number' },
6 { name: 'osdid', type: 'number', defaultValue: -1 },
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
38 Ext.define('Proxmox.DiskList', {
39 extend: 'Ext.tree.Panel',
40 alias: 'widget.pmxDiskList',
41
42 rootVisible: false,
43
44 emptyText: gettext('No Disks found'),
45
46 stateful: true,
47 stateId: 'tree-node-disks',
48
49 controller: {
50 xclass: 'Ext.app.ViewController',
51
52 reload: function() {
53 let me = this;
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();
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({
91 url: `${view.exturl}/initgpt`,
92 waitMsgTarget: view,
93 method: 'POST',
94 params: { disk: rec.data.name },
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,
102 taskDone: function() {
103 me.reload();
104 },
105 });
106 win.show();
107 },
108 });
109 },
110
111 init: function(view) {
112 let nodename = view.nodename || 'localhost';
113 view.baseurl = `/api2/json/nodes/${nodename}/disks`;
114 view.exturl = `/api2/extjs/nodes/${nodename}/disks`;
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();
123 },
124
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);
168 },
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'),
179 parentXType: 'treepanel',
180 disabled: true,
181 enableFn: function(rec) {
182 if (!rec || rec.data.parent) {
183 return false;
184 } else {
185 return true;
186 }
187 },
188 handler: 'openSmartWindow',
189 },
190 {
191 xtype: 'proxmoxButton',
192 text: gettext('Initialize Disk with GPT'),
193 parentXType: 'treepanel',
194 disabled: true,
195 enableFn: function(rec) {
196 if (!rec || rec.data.parent ||
197 (rec.data.used && rec.data.used !== 'unused')) {
198 return false;
199 } else {
200 return true;
201 }
202 },
203 handler: 'initGPT',
204 },
205 ],
206
207 columns: [
208 {
209 xtype: 'treecolumn',
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,
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 },
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 });