]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/lxc/Resources.js
update shipped appliance info index
[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;
c7ee0c11
DM
31
32 var nodename = me.pveSelNode.data.node;
2a4971d8 33 if (!nodename) {
c7ee0c11
DM
34 throw "no node name specified";
35 }
36
37 var vmid = me.pveSelNode.data.vmid;
38 if (!vmid) {
39 throw "no VM ID specified";
40 }
41
42 var caps = Ext.state.Manager.get('GuiCap');
d35b5b2a 43 var diskCap = caps.vms['VM.Config.Disk'];
c7ee0c11
DM
44
45 var mpeditor = caps.vms['VM.Config.Disk'] ? 'PVE.lxc.MountPointEdit' : undefined;
46
5a2e333c
FE
47 const nodeInfo = PVE.data.ResourceStore.getNodes().find(node => node.node === nodename);
48 let cpuEditor = {
49 xtype: 'pveLxcCPUEdit',
50 cgroupMode: nodeInfo['cgroup-mode'],
51 };
52
c7ee0c11
DM
53 var rows = {
54 memory: {
55 header: gettext('Memory'),
56 editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined,
c7ee0c11 57 defaultValue: 512,
a8ea1b68 58 tdCls: 'pmx-itype-icon-memory',
1a2fdf62 59 group: 1,
c7ee0c11 60 renderer: function(value) {
e7ade592 61 return Proxmox.Utils.format_size(value*1024*1024);
f6710aac 62 },
c7ee0c11
DM
63 },
64 swap: {
65 header: gettext('Swap'),
66 editor: caps.vms['VM.Config.Memory'] ? 'PVE.lxc.MemoryEdit' : undefined,
c7ee0c11 67 defaultValue: 512,
809f6b6e 68 iconCls: 'refresh',
1a2fdf62 69 group: 2,
c7ee0c11 70 renderer: function(value) {
e7ade592 71 return Proxmox.Utils.format_size(value*1024*1024);
f6710aac 72 },
c7ee0c11 73 },
92b5029f
DM
74 cores: {
75 header: gettext('Cores'),
5a2e333c 76 editor: caps.vms['VM.Config.CPU'] ? cpuEditor : undefined,
92b5029f 77 defaultValue: '',
a8ea1b68 78 tdCls: 'pmx-itype-icon-processor',
1a2fdf62 79 group: 3,
92b5029f 80 renderer: function(value) {
3e4b78dc
DC
81 var cpulimit = me.getObjectValue('cpulimit');
82 var cpuunits = me.getObjectValue('cpuunits');
83 var res;
84 if (value) {
85 res = value;
86 } else {
87 res = gettext('unlimited');
88 }
89
90 if (cpulimit) {
91 res += ' [cpulimit=' + cpulimit + ']';
92 }
93
94 if (cpuunits) {
95 res += ' [cpuunits=' + cpuunits + ']';
96 }
97 return res;
f6710aac 98 },
92b5029f 99 },
c7ee0c11
DM
100 rootfs: {
101 header: gettext('Root Disk'),
e7ade592 102 defaultValue: Proxmox.Utils.noneText,
c7ee0c11 103 editor: mpeditor,
809f6b6e 104 iconCls: 'hdd-o',
f6710aac 105 group: 4,
c15e9cd5 106 },
3e4b78dc 107 cpulimit: {
f6710aac 108 visible: false,
3e4b78dc
DC
109 },
110 cpuunits: {
f6710aac 111 visible: false,
3e4b78dc 112 },
c15e9cd5 113 unprivileged: {
f6710aac
TL
114 visible: false,
115 },
c7ee0c11
DM
116 };
117
8543ed33 118 PVE.Utils.forEachLxcMP(function(bus, i, confid) {
1a2fdf62
DL
119 var group = 5;
120 var header;
14a845bc
DC
121 if (bus === 'mp') {
122 header = gettext('Mount Point') + ' (' + confid + ')';
123 } else {
124 header = gettext('Unused Disk') + ' ' + i;
1a2fdf62 125 group += 1;
14a845bc 126 }
c7ee0c11 127 rows[confid] = {
1a2fdf62
DL
128 group: group,
129 order: i,
c7ee0c11
DM
130 tdCls: 'pve-itype-icon-storage',
131 editor: mpeditor,
f6710aac 132 header: header,
c7ee0c11 133 };
14a845bc 134 }, true);
c7ee0c11 135
4c406fed
FS
136 let deveditor = Proxmox.UserName === 'root@pam' ? 'PVE.lxc.DeviceEdit' : undefined;
137
8543ed33 138 PVE.Utils.forEachLxcDev(function(i, confid) {
4c406fed
FS
139 rows[confid] = {
140 group: 7,
141 order: i,
142 tdCls: 'pve-itype-icon-pci',
143 editor: deveditor,
144 header: gettext('Device') + ' (' + confid + ')',
145 };
146 });
147
c7ee0c11
DM
148 var baseurl = 'nodes/' + nodename + '/lxc/' + vmid + '/config';
149
14dd743b 150 me.selModel = Ext.create('Ext.selection.RowModel', {});
c7ee0c11
DM
151
152 var run_resize = function() {
14dd743b 153 var rec = me.selModel.getSelection()[0];
c7ee0c11
DM
154 if (!rec) {
155 return;
156 }
157
158 var win = Ext.create('PVE.window.MPResize', {
159 disk: rec.data.key,
160 nodename: nodename,
f6710aac 161 vmid: vmid,
c7ee0c11
DM
162 });
163
164 win.show();
c7ee0c11
DM
165 };
166
167 var run_remove = function(b, e, rec) {
e7ade592 168 Proxmox.Utils.API2Request({
c7ee0c11
DM
169 url: '/api2/extjs/' + baseurl,
170 waitMsgTarget: me,
171 method: 'PUT',
172 params: {
f6710aac 173 'delete': rec.data.key,
c7ee0c11 174 },
8058410f 175 failure: function(response, opts) {
c7ee0c11 176 Ext.Msg.alert('Error', response.htmlStatus);
f6710aac 177 },
c7ee0c11
DM
178 });
179 };
180
a8d854af
AL
181 let run_move = function() {
182 let rec = me.selModel.getSelection()[0];
c7164db7
DC
183 if (!rec) {
184 return;
185 }
186
187 var win = Ext.create('PVE.window.HDMove', {
188 disk: rec.data.key,
189 nodename: nodename,
190 vmid: vmid,
f6710aac 191 type: 'lxc',
c7164db7
DC
192 });
193
194 win.show();
195
196 win.on('destroy', me.reload, me);
197 };
198
a8d854af
AL
199 let run_reassign = function() {
200 let rec = me.selModel.getSelection()[0];
201 if (!rec) {
202 return;
203 }
204
3bde324f 205 Ext.create('PVE.window.GuestDiskReassign', {
a8d854af
AL
206 disk: rec.data.key,
207 nodename: nodename,
208 autoShow: true,
209 vmid: vmid,
210 type: 'lxc',
211 listeners: {
212 destroy: () => me.reload(),
213 },
214 });
215 };
216
5720fafa 217 var edit_btn = new Proxmox.button.Button({
c7ee0c11 218 text: gettext('Edit'),
14dd743b 219 selModel: me.selModel,
c7ee0c11
DM
220 disabled: true,
221 enableFn: function(rec) {
222 if (!rec) {
223 return false;
224 }
225 var rowdef = rows[rec.data.key];
226 return !!rowdef.editor;
227 },
f6710aac 228 handler: function() { me.run_editor(); },
c7ee0c11
DM
229 });
230
5720fafa 231 var remove_btn = new Proxmox.button.Button({
c7ee0c11 232 text: gettext('Remove'),
986ccfe4
AL
233 defaultText: gettext('Remove'),
234 altText: gettext('Detach'),
14dd743b 235 selModel: me.selModel,
c7ee0c11
DM
236 disabled: true,
237 dangerous: true,
238 confirmMsg: function(rec) {
986ccfe4
AL
239 let warn = Ext.String.format(gettext('Are you sure you want to remove entry {0}'));
240 if (this.text === this.altText) {
241 warn = gettext('Are you sure you want to detach entry {0}');
242 }
cc80f765
TL
243 let rendered = me.renderKey(rec.data.key, {}, rec);
244 let msg = Ext.String.format(warn, `'${rendered}'`);
986ccfe4 245
c7ee0c11 246 if (rec.data.key.match(/^unused\d+$/)) {
16152937 247 msg += " " + gettext('This will permanently erase all data.');
c7ee0c11 248 }
c7ee0c11
DM
249 return msg;
250 },
f6710aac 251 handler: run_remove,
986ccfe4
AL
252 listeners: {
253 render: function(btn) {
254 // hack: calculate the max button width on first display to prevent the whole
255 // toolbar to move when we switch between the "Remove" and "Detach" labels
256 let def = btn.getSize().width;
257
258 btn.setText(btn.altText);
259 let alt = btn.getSize().width;
260
261 btn.setText(btn.defaultText);
262
263 let optimal = alt > def ? alt : def;
264 btn.setSize({ width: optimal });
265 },
266 },
c7ee0c11
DM
267 });
268
a8d854af
AL
269 let move_menuitem = new Ext.menu.Item({
270 text: gettext('Move Storage'),
271 tooltip: gettext('Move volume to another storage'),
272 iconCls: 'fa fa-database',
c7164db7 273 selModel: me.selModel,
f6710aac 274 handler: run_move,
c7164db7
DC
275 });
276
a8d854af
AL
277 let reassign_menuitem = new Ext.menu.Item({
278 text: gettext('Reassign Owner'),
279 tooltip: gettext('Reassign volume to another CT'),
280 iconCls: 'fa fa-cube',
281 handler: run_reassign,
282 reference: 'reassing_item',
283 });
284
285 let resize_menuitem = new Ext.menu.Item({
286 text: gettext('Resize'),
287 iconCls: 'fa fa-plus',
288 selModel: me.selModel,
289 handler: run_resize,
290 });
291
292 let volumeaction_btn = new Proxmox.button.Button({
293 text: gettext('Volume Action'),
294 disabled: true,
295 menu: {
296 items: [
297 move_menuitem,
298 reassign_menuitem,
299 resize_menuitem,
300 ],
301 },
302 });
303
716c3043 304 let revert_btn = new PVE.button.PendingRevert();
273b5ce3 305
716c3043
TL
306 let set_button_status = function() {
307 let rec = me.selModel.getSelection()[0];
c7ee0c11
DM
308
309 if (!rec) {
310 edit_btn.disable();
311 remove_btn.disable();
a8d854af 312 volumeaction_btn.disable();
273b5ce3 313 revert_btn.disable();
c7ee0c11
DM
314 return;
315 }
716c3043
TL
316 let { key, value, 'delete': isDelete } = rec.data;
317 let rowdef = rows[key];
c7ee0c11 318
716c3043 319 let pending = isDelete || me.hasPendingChanges(key);
a8d854af
AL
320 let isRootFS = key === 'rootfs';
321 let isDisk = isRootFS || key.match(/^(mp|unused)\d+/);
716c3043
TL
322 let isUnusedDisk = key.match(/^unused\d+/);
323 let isUsedDisk = isDisk && !isUnusedDisk;
4c406fed 324 let isDevice = key.match(/^dev\d+/);
c7ee0c11 325
716c3043 326 let noedit = isDelete || !rowdef.editor;
35a04562 327 if (!noedit && Proxmox.UserName !== 'root@pam' && key.match(/^mp\d+$/)) {
716c3043 328 let mp = PVE.Parser.parseLxcMountPoint(value);
c7ee0c11
DM
329 if (mp.type !== 'volume') {
330 noedit = true;
331 }
332 }
333 edit_btn.setDisabled(noedit);
334
a8d854af
AL
335 volumeaction_btn.setDisabled(!isDisk || !diskCap);
336 move_menuitem.setDisabled(isUnusedDisk);
337 reassign_menuitem.setDisabled(isRootFS);
338 resize_menuitem.setDisabled(isUnusedDisk);
339
4c406fed 340 remove_btn.setDisabled(!(isDisk || isDevice) || isRootFS || !diskCap || pending);
273b5ce3 341 revert_btn.setDisabled(!pending);
986ccfe4
AL
342
343 remove_btn.setText(isUsedDisk ? remove_btn.altText : remove_btn.defaultText);
c7ee0c11 344 };
2a4971d8 345
716c3043
TL
346 let sorterFn = function(rec1, rec2) {
347 let v1 = rec1.data.key, v2 = rec2.data.key;
1a2fdf62 348
716c3043 349 let g1 = rows[v1].group || 0, g2 = rows[v2].group || 0;
53e3ea84 350 if (g1 - g2 !== 0) {
1a2fdf62
DL
351 return g1 - g2;
352 }
353
716c3043 354 let order1 = rows[v1].order || 0, order2 = rows[v2].order || 0;
53e3ea84 355 if (order1 - order2 !== 0) {
1a2fdf62
DL
356 return order1 - order2;
357 }
358
359 if (v1 > v2) {
360 return 1;
361 } else if (v1 < v2) {
362 return -1;
363 } else {
364 return 0;
365 }
93bd0d75 366 };
1a2fdf62 367
f7993618 368 Ext.apply(me, {
716c3043 369 url: `/api2/json/nodes/${nodename}/lxc/${vmid}/pending`,
14dd743b
TL
370 selModel: me.selModel,
371 interval: 2000,
c7ee0c11
DM
372 cwidth1: 170,
373 tbar: [
374 {
375 text: gettext('Add'),
376 menu: new Ext.menu.Menu({
377 items: [
378 {
379 text: gettext('Mount Point'),
809f6b6e 380 iconCls: 'fa fa-fw fa-hdd-o black',
c7ee0c11
DM
381 disabled: !caps.vms['VM.Config.Disk'],
382 handler: function() {
163d9f17
TL
383 Ext.create('PVE.lxc.MountPointEdit', {
384 autoShow: true,
385 url: `/api2/extjs/${baseurl}`,
c15e9cd5 386 unprivileged: me.getObjectValue('unprivileged'),
f6710aac 387 pveSelNode: me.pveSelNode,
163d9f17
TL
388 listeners: {
389 destroy: () => me.reload(),
390 },
c7ee0c11 391 });
f6710aac
TL
392 },
393 },
4c406fed
FS
394 {
395 text: gettext('Device Passthrough'),
396 iconCls: 'pve-itype-icon-pci',
397 disabled: Proxmox.UserName !== 'root@pam',
398 handler: function() {
399 Ext.create('PVE.lxc.DeviceEdit', {
400 autoShow: true,
401 url: `/api2/extjs/${baseurl}`,
402 pveSelNode: me.pveSelNode,
403 listeners: {
404 destroy: () => me.reload(),
405 },
406 });
407 },
408 },
f6710aac
TL
409 ],
410 }),
c7ee0c11
DM
411 },
412 edit_btn,
413 remove_btn,
a8d854af 414 volumeaction_btn,
f6710aac 415 revert_btn,
c7ee0c11
DM
416 ],
417 rows: rows,
1a2fdf62 418 sorterFn: sorterFn,
14dd743b
TL
419 editorConfig: {
420 pveSelNode: me.pveSelNode,
f6710aac 421 url: '/api2/extjs/' + baseurl,
14dd743b 422 },
c7ee0c11 423 listeners: {
14dd743b 424 itemdblclick: me.run_editor,
f6710aac
TL
425 selectionchange: set_button_status,
426 },
c7ee0c11
DM
427 });
428
429 me.callParent();
14dd743b
TL
430
431 me.on('activate', me.rstore.startUpdate);
432 me.on('destroy', me.rstore.stopUpdate);
433 me.on('deactivate', me.rstore.stopUpdate);
434
69f36699
DC
435 me.mon(me.getStore(), 'datachanged', function() {
436 set_button_status();
437 });
438
14dd743b 439 Ext.apply(me.editorConfig, { unprivileged: me.getObjectValue('unprivileged') });
f6710aac 440 },
c7ee0c11 441});