]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/lxc/Resources.js
ui: lxc: resources: consider rootfs as a disk again
[pve-manager.git] / www / manager6 / lxc / Resources.js
CommitLineData
c7ee0c11 1Ext.define('PVE.lxc.RessourceView', {
9fd8d73e 2 extend: 'Proxmox.grid.PendingObjectGrid',
c7ee0c11
DM
3 alias: ['widget.pveLxcRessourceView'],
4
ba93a9c6
DC
5 onlineHelp: 'pct_configuration',
6
c7ee0c11 7 renderKey: function(key, metaData, rec, rowIndex, colIndex, store) {
02ecbc98
TL
8 let me = this;
9 let rowdef = me.rows[key] || {};
10
11 let txt = rowdef.header || key;
12 let icon = '';
c7ee0c11
DM
13
14 metaData.tdAttr = "valign=middle";
c7ee0c11
DM
15 if (rowdef.tdCls) {
16 metaData.tdCls = rowdef.tdCls;
02ecbc98
TL
17 } else if (rowdef.iconCls) {
18 icon = `<i class='pve-grid-fa fa fa-fw fa-${rowdef.iconCls}'></i>`;
19 metaData.tdCls += " pve-itype-fa";
20 }
21 // only return icons in grid but not remove dialog
22 if (rowIndex !== undefined) {
23 return icon + txt;
24 } else {
25 return txt;
c7ee0c11 26 }
c7ee0c11
DM
27 },
28
8058410f 29 initComponent: function() {
c7ee0c11 30 var me = this;
55ee6ba1 31 let confid;
c7ee0c11
DM
32
33 var nodename = me.pveSelNode.data.node;
2a4971d8 34 if (!nodename) {
c7ee0c11
DM
35 throw "no node name specified";
36 }
37
38 var vmid = me.pveSelNode.data.vmid;
39 if (!vmid) {
40 throw "no VM ID specified";
41 }
42
43 var caps = Ext.state.Manager.get('GuiCap');
d35b5b2a 44 var diskCap = caps.vms['VM.Config.Disk'];
c7ee0c11
DM
45
46 var mpeditor = caps.vms['VM.Config.Disk'] ? 'PVE.lxc.MountPointEdit' : undefined;
47
48 var rows = {
49 memory: {
50 header: gettext('Memory'),
51 editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined,
c7ee0c11 52 defaultValue: 512,
a8ea1b68 53 tdCls: 'pmx-itype-icon-memory',
1a2fdf62 54 group: 1,
c7ee0c11 55 renderer: function(value) {
e7ade592 56 return Proxmox.Utils.format_size(value*1024*1024);
f6710aac 57 },
c7ee0c11
DM
58 },
59 swap: {
60 header: gettext('Swap'),
61 editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined,
c7ee0c11 62 defaultValue: 512,
809f6b6e 63 iconCls: 'refresh',
1a2fdf62 64 group: 2,
c7ee0c11 65 renderer: function(value) {
e7ade592 66 return Proxmox.Utils.format_size(value*1024*1024);
f6710aac 67 },
c7ee0c11 68 },
92b5029f
DM
69 cores: {
70 header: gettext('Cores'),
92b5029f
DM
71 editor: caps.vms['VM.Config.CPU'] ? 'PVE.lxc.CPUEdit' : undefined,
72 defaultValue: '',
a8ea1b68 73 tdCls: 'pmx-itype-icon-processor',
1a2fdf62 74 group: 3,
92b5029f 75 renderer: function(value) {
3e4b78dc
DC
76 var cpulimit = me.getObjectValue('cpulimit');
77 var cpuunits = me.getObjectValue('cpuunits');
78 var res;
79 if (value) {
80 res = value;
81 } else {
82 res = gettext('unlimited');
83 }
84
85 if (cpulimit) {
86 res += ' [cpulimit=' + cpulimit + ']';
87 }
88
89 if (cpuunits) {
90 res += ' [cpuunits=' + cpuunits + ']';
91 }
92 return res;
f6710aac 93 },
92b5029f 94 },
c7ee0c11
DM
95 rootfs: {
96 header: gettext('Root Disk'),
e7ade592 97 defaultValue: Proxmox.Utils.noneText,
c7ee0c11 98 editor: mpeditor,
809f6b6e 99 iconCls: 'hdd-o',
f6710aac 100 group: 4,
c15e9cd5 101 },
3e4b78dc 102 cpulimit: {
f6710aac 103 visible: false,
3e4b78dc
DC
104 },
105 cpuunits: {
f6710aac 106 visible: false,
3e4b78dc 107 },
c15e9cd5 108 unprivileged: {
f6710aac
TL
109 visible: false,
110 },
c7ee0c11
DM
111 };
112
14a845bc
DC
113 PVE.Utils.forEachMP(function(bus, i) {
114 confid = bus + i;
1a2fdf62
DL
115 var group = 5;
116 var header;
14a845bc
DC
117 if (bus === 'mp') {
118 header = gettext('Mount Point') + ' (' + confid + ')';
119 } else {
120 header = gettext('Unused Disk') + ' ' + i;
1a2fdf62 121 group += 1;
14a845bc 122 }
c7ee0c11 123 rows[confid] = {
1a2fdf62
DL
124 group: group,
125 order: i,
c7ee0c11
DM
126 tdCls: 'pve-itype-icon-storage',
127 editor: mpeditor,
f6710aac 128 header: header,
c7ee0c11 129 };
14a845bc 130 }, true);
c7ee0c11 131
c7ee0c11
DM
132 var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config';
133
14dd743b 134 me.selModel = Ext.create('Ext.selection.RowModel', {});
c7ee0c11
DM
135
136 var run_resize = function() {
14dd743b 137 var rec = me.selModel.getSelection()[0];
c7ee0c11
DM
138 if (!rec) {
139 return;
140 }
141
142 var win = Ext.create('PVE.window.MPResize', {
143 disk: rec.data.key,
144 nodename: nodename,
f6710aac 145 vmid: vmid,
c7ee0c11
DM
146 });
147
148 win.show();
c7ee0c11
DM
149 };
150
151 var run_remove = function(b, e, rec) {
e7ade592 152 Proxmox.Utils.API2Request({
c7ee0c11
DM
153 url: '/api2/extjs/' + baseurl,
154 waitMsgTarget: me,
155 method: 'PUT',
156 params: {
f6710aac 157 'delete': rec.data.key,
c7ee0c11 158 },
8058410f 159 failure: function(response, opts) {
c7ee0c11 160 Ext.Msg.alert('Error', response.htmlStatus);
f6710aac 161 },
c7ee0c11
DM
162 });
163 };
164
c7164db7
DC
165 var run_move = function(b, e, rec) {
166 if (!rec) {
167 return;
168 }
169
170 var win = Ext.create('PVE.window.HDMove', {
171 disk: rec.data.key,
172 nodename: nodename,
173 vmid: vmid,
f6710aac 174 type: 'lxc',
c7164db7
DC
175 });
176
177 win.show();
178
179 win.on('destroy', me.reload, me);
180 };
181
5720fafa 182 var edit_btn = new Proxmox.button.Button({
c7ee0c11 183 text: gettext('Edit'),
14dd743b 184 selModel: me.selModel,
c7ee0c11
DM
185 disabled: true,
186 enableFn: function(rec) {
187 if (!rec) {
188 return false;
189 }
190 var rowdef = rows[rec.data.key];
191 return !!rowdef.editor;
192 },
f6710aac 193 handler: function() { me.run_editor(); },
c7ee0c11
DM
194 });
195
5720fafa 196 var resize_btn = new Proxmox.button.Button({
c7ee0c11 197 text: gettext('Resize disk'),
14dd743b 198 selModel: me.selModel,
c7ee0c11 199 disabled: true,
f6710aac 200 handler: run_resize,
c7ee0c11
DM
201 });
202
5720fafa 203 var remove_btn = new Proxmox.button.Button({
c7ee0c11 204 text: gettext('Remove'),
986ccfe4
AL
205 defaultText: gettext('Remove'),
206 altText: gettext('Detach'),
14dd743b 207 selModel: me.selModel,
c7ee0c11
DM
208 disabled: true,
209 dangerous: true,
210 confirmMsg: function(rec) {
986ccfe4
AL
211 let warn = Ext.String.format(gettext('Are you sure you want to remove entry {0}'));
212 if (this.text === this.altText) {
213 warn = gettext('Are you sure you want to detach entry {0}');
214 }
cc80f765
TL
215 let rendered = me.renderKey(rec.data.key, {}, rec);
216 let msg = Ext.String.format(warn, `'${rendered}'`);
986ccfe4 217
c7ee0c11 218 if (rec.data.key.match(/^unused\d+$/)) {
16152937 219 msg += " " + gettext('This will permanently erase all data.');
c7ee0c11 220 }
c7ee0c11
DM
221 return msg;
222 },
f6710aac 223 handler: run_remove,
986ccfe4
AL
224 listeners: {
225 render: function(btn) {
226 // hack: calculate the max button width on first display to prevent the whole
227 // toolbar to move when we switch between the "Remove" and "Detach" labels
228 let def = btn.getSize().width;
229
230 btn.setText(btn.altText);
231 let alt = btn.getSize().width;
232
233 btn.setText(btn.defaultText);
234
235 let optimal = alt > def ? alt : def;
236 btn.setSize({ width: optimal });
237 },
238 },
c7ee0c11
DM
239 });
240
c7164db7
DC
241 var move_btn = new Proxmox.button.Button({
242 text: gettext('Move Volume'),
243 selModel: me.selModel,
244 disabled: true,
245 dangerous: true,
f6710aac 246 handler: run_move,
c7164db7
DC
247 });
248
417f904f 249 var revert_btn = new PVE.button.PendingRevert();
273b5ce3 250
c7ee0c11 251 var set_button_status = function() {
14dd743b 252 var rec = me.selModel.getSelection()[0];
c7ee0c11
DM
253
254 if (!rec) {
255 edit_btn.disable();
256 remove_btn.disable();
257 resize_btn.disable();
273b5ce3 258 revert_btn.disable();
c7ee0c11
DM
259 return;
260 }
261 var key = rec.data.key;
262 var value = rec.data.value;
263 var rowdef = rows[key];
264
399ffa76 265 var pending = rec.data.delete || me.hasPendingChanges(key);
4ad3a7cc 266 let isDisk = key === 'rootfs' || key.match(/^(mp|unused)\d+/);
5ef877b9 267 var isUnusedDisk = key.match(/^unused\d+/);
986ccfe4 268 var isUsedDisk = isDisk && !isUnusedDisk;
c7ee0c11 269
399ffa76 270 var noedit = rec.data.delete || !rowdef.editor;
35a04562 271 if (!noedit && Proxmox.UserName !== 'root@pam' && key.match(/^mp\d+$/)) {
c7ee0c11
DM
272 var mp = PVE.Parser.parseLxcMountPoint(value);
273 if (mp.type !== 'volume') {
274 noedit = true;
275 }
276 }
277 edit_btn.setDisabled(noedit);
278
cef9b653 279 remove_btn.setDisabled(!isDisk || rec.data.key === 'rootfs' || !diskCap || pending);
5ef877b9 280 resize_btn.setDisabled(!isDisk || !diskCap || isUnusedDisk);
d35b5b2a 281 move_btn.setDisabled(!isDisk || !diskCap);
273b5ce3 282 revert_btn.setDisabled(!pending);
986ccfe4
AL
283
284 remove_btn.setText(isUsedDisk ? remove_btn.altText : remove_btn.defaultText);
c7ee0c11 285 };
2a4971d8 286
1a2fdf62
DL
287 var sorterFn = function(rec1, rec2) {
288 var v1 = rec1.data.key;
289 var v2 = rec2.data.key;
290 var g1 = rows[v1].group || 0;
291 var g2 = rows[v2].group || 0;
292 var order1 = rows[v1].order || 0;
293 var order2 = rows[v2].order || 0;
294
53e3ea84 295 if (g1 - g2 !== 0) {
1a2fdf62
DL
296 return g1 - g2;
297 }
298
53e3ea84 299 if (order1 - order2 !== 0) {
1a2fdf62
DL
300 return order1 - order2;
301 }
302
303 if (v1 > v2) {
304 return 1;
305 } else if (v1 < v2) {
306 return -1;
307 } else {
308 return 0;
309 }
93bd0d75 310 };
1a2fdf62 311
f7993618 312 Ext.apply(me, {
9fd8d73e 313 url: "/api2/json/nodes/" + nodename + "/lxc/" + vmid + "/pending",
14dd743b
TL
314 selModel: me.selModel,
315 interval: 2000,
c7ee0c11
DM
316 cwidth1: 170,
317 tbar: [
318 {
319 text: gettext('Add'),
320 menu: new Ext.menu.Menu({
321 items: [
322 {
323 text: gettext('Mount Point'),
809f6b6e 324 iconCls: 'fa fa-fw fa-hdd-o black',
c7ee0c11
DM
325 disabled: !caps.vms['VM.Config.Disk'],
326 handler: function() {
163d9f17
TL
327 Ext.create('PVE.lxc.MountPointEdit', {
328 autoShow: true,
329 url: `/api2/extjs/${baseurl}`,
c15e9cd5 330 unprivileged: me.getObjectValue('unprivileged'),
f6710aac 331 pveSelNode: me.pveSelNode,
163d9f17
TL
332 listeners: {
333 destroy: () => me.reload(),
334 },
c7ee0c11 335 });
f6710aac
TL
336 },
337 },
338 ],
339 }),
c7ee0c11
DM
340 },
341 edit_btn,
342 remove_btn,
c7164db7 343 resize_btn,
273b5ce3 344 move_btn,
f6710aac 345 revert_btn,
c7ee0c11
DM
346 ],
347 rows: rows,
1a2fdf62 348 sorterFn: sorterFn,
14dd743b
TL
349 editorConfig: {
350 pveSelNode: me.pveSelNode,
f6710aac 351 url: '/api2/extjs/' + baseurl,
14dd743b 352 },
c7ee0c11 353 listeners: {
14dd743b 354 itemdblclick: me.run_editor,
f6710aac
TL
355 selectionchange: set_button_status,
356 },
c7ee0c11
DM
357 });
358
359 me.callParent();
14dd743b
TL
360
361 me.on('activate', me.rstore.startUpdate);
362 me.on('destroy', me.rstore.stopUpdate);
363 me.on('deactivate', me.rstore.stopUpdate);
364
69f36699
DC
365 me.mon(me.getStore(), 'datachanged', function() {
366 set_button_status();
367 });
368
14dd743b 369 Ext.apply(me.editorConfig, { unprivileged: me.getObjectValue('unprivileged') });
f6710aac 370 },
c7ee0c11 371});