]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/lxc/MPEdit.js
Merge branch 'taskset' into taskset-merged
[pve-manager.git] / www / manager6 / lxc / MPEdit.js
CommitLineData
342225bf 1Ext.define('PVE.lxc.MountPointInputPanel', {
ef4ef788 2 extend: 'Proxmox.panel.InputPanel',
34c235be 3 xtype: 'pveLxcMountPointInputPanel',
342225bf 4
342225bf
DC
5 onlineHelp: 'pct_container_storage',
6
7aaa492b 7 insideWizard: false,
342225bf 8
7aaa492b 9 unused: false, // add unused disk imaged
342225bf
DC
10 unprivileged: false,
11
12 vmconfig: {}, // used to select unused disks
13
658bf725 14 setUnprivileged: function(unprivileged) {
34c235be
DC
15 var me = this;
16 var vm = me.getViewModel();
17 me.unprivileged = unprivileged;
18 vm.set('unpriv', unprivileged);
658bf725
TL
19 },
20
342225bf
DC
21 onGetValues: function(values) {
22 var me = this;
23
85e6b368 24 var confid = me.confid || "mp"+values.mpid;
f9b63366 25 me.mp.file = me.down('field[name=file]').getValue();
342225bf
DC
26
27 if (me.unused) {
85e6b368 28 confid = "mp"+values.mpid;
342225bf 29 } else if (me.isCreate) {
f9b63366 30 me.mp.file = values.hdstorage + ':' + values.disksize;
342225bf
DC
31 }
32
34c235be 33 // delete unnecessary fields
85e6b368 34 delete values.mpid;
34c235be
DC
35 delete values.hdstorage;
36 delete values.disksize;
37 delete values.diskformat;
342225bf 38
7aaa492b
TL
39 let setMPOpt = (k, src, v) => PVE.Utils.propertyStringSet(me.mp, src, k, v);
40
41 setMPOpt('mp', values.mp);
42 let mountOpts = (values.mountoptions || []).join(';');
43 setMPOpt('mountoptions', values.mountoptions, mountOpts);
44 setMPOpt('mp', values.mp);
45 setMPOpt('backup', values.backup);
46 setMPOpt('quota', values.quota);
47 setMPOpt('ro', values.ro);
48 setMPOpt('acl', values.acl);
49 setMPOpt('replicate', values.replicate);
f9b63366 50
7aaa492b 51 let res = {};
f9b63366 52 res[confid] = PVE.Parser.printLxcMountPoint(me.mp);
342225bf
DC
53 return res;
54 },
55
56 setMountPoint: function(mp) {
7aaa492b
TL
57 let me = this;
58 let vm = me.getViewModel();
34c235be 59 vm.set('mptype', mp.type);
c115473d
OB
60 if (mp.mountoptions) {
61 mp.mountoptions = mp.mountoptions.split(';');
62 }
f9b63366 63 me.mp = mp;
7aaa492b
TL
64 me.filterMountOptions();
65 me.setValues(mp);
66 },
1780a644 67
7aaa492b
TL
68 filterMountOptions: function() {
69 let me = this;
70 if (me.confid === 'rootfs') {
71 let field = me.down('field[name=mountoptions]');
72 let exclude = ['nodev', 'noexec'];
73 let filtered = field.comboItems.filter(v => !exclude.includes(v[0]));
1780a644
OB
74 field.setComboItems(filtered);
75 }
342225bf
DC
76 },
77
09e7ac8b 78 updateVMConfig: function(vmconfig) {
7aaa492b
TL
79 let me = this;
80 let vm = me.getViewModel();
342225bf 81 me.vmconfig = vmconfig;
34c235be 82 vm.set('unpriv', vmconfig.unprivileged);
09e7ac8b
DC
83 me.down('field[name=mpid]').validate();
84 },
85
86 setVMConfig: function(vmconfig) {
87 let me = this;
34c235be 88
09e7ac8b 89 me.updateVMConfig(vmconfig);
7aaa492b
TL
90 PVE.Utils.forEachMP((bus, i) => {
91 let name = "mp" + i.toString();
34c235be 92 if (!Ext.isDefined(vmconfig[name])) {
85e6b368 93 me.down('field[name=mpid]').setValue(i);
34c235be 94 return false;
342225bf 95 }
7aaa492b 96 return undefined;
34c235be 97 });
342225bf
DC
98 },
99
100 setNodename: function(nodename) {
7aaa492b
TL
101 let me = this;
102 let vm = me.getViewModel();
34c235be 103 vm.set('node', nodename);
34c235be 104 me.down('#diskstorage').setNodename(nodename);
342225bf
DC
105 },
106
8058410f 107 controller: {
34c235be 108 xclass: 'Ext.app.ViewController',
342225bf 109
34c235be 110 control: {
85e6b368 111 'field[name=mpid]': {
34c235be 112 change: function(field, value) {
143a5071
DC
113 let me = this;
114 let view = this.getView();
115 if (view.confid !== 'rootfs') {
116 view.fireEvent('diskidchange', view, `mp${value}`);
117 }
34c235be 118 field.validate();
f6710aac 119 },
34c235be
DC
120 },
121 '#hdstorage': {
122 change: function(field, newValue) {
7aaa492b 123 let me = this;
34c235be 124 if (!newValue) {
342225bf
DC
125 return;
126 }
34c235be 127
7aaa492b 128 let rec = field.store.getById(newValue);
34c235be
DC
129 if (!rec) {
130 return;
342225bf 131 }
7aaa492b 132 me.getViewModel().set('type', rec.data.type);
f6710aac
TL
133 },
134 },
34c235be 135 },
34c235be 136 init: function(view) {
7aaa492b
TL
137 let me = this;
138 let vm = this.getViewModel();
f9b63366 139 view.mp = {};
34c235be
DC
140 vm.set('confid', view.confid);
141 vm.set('unused', view.unused);
142 vm.set('node', view.nodename);
143 vm.set('unpriv', view.unprivileged);
144 vm.set('hideStorSelector', view.unused || !view.isCreate);
2b4c02c0 145
7aaa492b 146 if (view.isCreate) { // can be array if created from unused disk
2b4c02c0 147 vm.set('isIncludedInBackup', true);
7aaa492b
TL
148 if (view.insideWizard) {
149 view.filterMountOptions();
150 }
2b4c02c0 151 }
87161abf
DC
152 if (view.selectFree) {
153 view.setVMConfig(view.vmconfig);
154 }
f6710aac 155 },
34c235be
DC
156 },
157
158 viewModel: {
159 data: {
160 unpriv: false,
161 unused: false,
162 showStorageSelector: false,
163 mptype: '',
164 type: '',
165 confid: '',
f6710aac 166 node: '',
34c235be
DC
167 },
168
169 formulas: {
170 quota: function(get) {
171 return !(get('type') === 'zfs' ||
172 get('type') === 'zfspool' ||
173 get('unpriv') ||
174 get('isBind'));
175 },
176 hasMP: function(get) {
177 return !!get('confid') && !get('unused');
178 },
179 isRoot: function(get) {
180 return get('confid') === 'rootfs';
181 },
182 isBind: function(get) {
183 return get('mptype') === 'bind';
184 },
185 isBindOrRoot: function(get) {
186 return get('isBind') || get('isRoot');
f6710aac
TL
187 },
188 },
34c235be 189 },
342225bf 190
34c235be
DC
191 column1: [
192 {
85e6b368
FG
193 xtype: 'proxmoxintegerfield',
194 name: 'mpid',
195 fieldLabel: gettext('Mount Point ID'),
196 minValue: 0,
5747fef3 197 maxValue: PVE.Utils.mp_counts.mp - 1,
34c235be
DC
198 hidden: true,
199 allowBlank: false,
6c442961 200 disabled: true,
34c235be
DC
201 bind: {
202 hidden: '{hasMP}',
f6710aac 203 disabled: '{hasMP}',
34c235be 204 },
34c235be 205 validator: function(value) {
46d9a35c
DJ
206 let view = this.up('inputpanel');
207 if (!view.rendered) {
7aaa492b 208 return undefined;
34c235be 209 }
46d9a35c 210 if (Ext.isDefined(view.vmconfig["mp"+value])) {
34c235be
DC
211 return "Mount point is already in use.";
212 }
34c235be 213 return true;
f6710aac 214 },
34c235be
DC
215 },
216 {
234d2e37 217 xtype: 'pveDiskStorageSelector',
34c235be 218 itemId: 'diskstorage',
342225bf 219 storageContent: 'rootdir',
34c235be 220 hidden: true,
342225bf 221 autoSelect: true,
34c235be 222 selectformat: false,
c6c8e2fc 223 defaultSize: 8,
34c235be
DC
224 bind: {
225 hidden: '{hideStorSelector}',
226 disabled: '{hideStorSelector}',
f6710aac
TL
227 nodename: '{node}',
228 },
34c235be
DC
229 },
230 {
231 xtype: 'textfield',
232 disabled: true,
233 submitValue: false,
234 fieldLabel: gettext('Disk image'),
235 name: 'file',
236 bind: {
f6710aac
TL
237 hidden: '{!hideStorSelector}',
238 },
239 },
34c235be
DC
240 ],
241
242 column2: [
243 {
244 xtype: 'textfield',
245 name: 'mp',
246 value: '',
8058410f 247 emptyText: gettext('/some/path'),
34c235be 248 allowBlank: false,
6c442961 249 disabled: true,
34c235be
DC
250 fieldLabel: gettext('Path'),
251 bind: {
252 hidden: '{isRoot}',
f6710aac
TL
253 disabled: '{isRoot}',
254 },
34c235be
DC
255 },
256 {
257 xtype: 'proxmoxcheckbox',
258 name: 'backup',
0bead056
TL
259 fieldLabel: gettext('Backup'),
260 autoEl: {
261 tag: 'div',
262 'data-qtip': gettext('Include volume in backup job'),
263 },
34c235be
DC
264 bind: {
265 hidden: '{isRoot}',
2b4c02c0 266 disabled: '{isBindOrRoot}',
f6710aac
TL
267 value: '{isIncludedInBackup}',
268 },
269 },
34c235be 270 ],
342225bf 271
34c235be
DC
272 advancedColumn1: [
273 {
274 xtype: 'proxmoxcheckbox',
342225bf
DC
275 name: 'quota',
276 defaultValue: 0,
34c235be 277 bind: {
f6710aac 278 disabled: '{!quota}',
34c235be 279 },
658bf725
TL
280 fieldLabel: gettext('Enable quota'),
281 listeners: {
282 disable: function() {
283 this.reset();
f6710aac
TL
284 },
285 },
34c235be
DC
286 },
287 {
288 xtype: 'proxmoxcheckbox',
289 name: 'ro',
290 defaultValue: 0,
291 bind: {
292 hidden: '{isRoot}',
f6710aac 293 disabled: '{isRoot}',
34c235be 294 },
f6710aac 295 fieldLabel: gettext('Read-only'),
7b6c7881
TL
296 },
297 {
298 xtype: 'proxmoxKVComboBox',
299 name: 'mountoptions',
300 fieldLabel: gettext('Mount options'),
301 deleteEmpty: false,
302 comboItems: [
0ffa230e 303 ['lazytime', 'lazytime'],
7b6c7881
TL
304 ['noatime', 'noatime'],
305 ['nodev', 'nodev'],
306 ['noexec', 'noexec'],
f6710aac 307 ['nosuid', 'nosuid'],
7b6c7881
TL
308 ],
309 multiSelect: true,
310 value: [],
f6710aac 311 allowBlank: true,
7b6c7881 312 },
34c235be 313 ],
342225bf 314
34c235be
DC
315 advancedColumn2: [
316 {
317 xtype: 'proxmoxKVComboBox',
318 name: 'acl',
319 fieldLabel: 'ACLs',
320 deleteEmpty: false,
321 comboItems: [
322 ['__default__', Proxmox.Utils.defaultText],
323 ['1', Proxmox.Utils.enabledText],
f6710aac 324 ['0', Proxmox.Utils.disabledText],
34c235be
DC
325 ],
326 value: '__default__',
327 bind: {
f6710aac 328 disabled: '{isBind}',
34c235be 329 },
f6710aac 330 allowBlank: true,
34c235be
DC
331 },
332 {
333 xtype: 'proxmoxcheckbox',
334 inputValue: '0', // reverses the logic
335 name: 'replicate',
f6710aac
TL
336 fieldLabel: gettext('Skip replication'),
337 },
338 ],
342225bf
DC
339});
340
341Ext.define('PVE.lxc.MountPointEdit', {
9fccc702 342 extend: 'Proxmox.window.Edit',
342225bf
DC
343
344 unprivileged: false,
345
8058410f 346 initComponent: function() {
7aaa492b 347 let me = this;
342225bf 348
7aaa492b 349 let nodename = me.pveSelNode.data.node;
342225bf
DC
350 if (!nodename) {
351 throw "no node name specified";
352 }
353
7aaa492b 354 let unused = me.confid && me.confid.match(/^unused\d+$/);
342225bf
DC
355
356 me.isCreate = me.confid ? unused : true;
357
7aaa492b 358 let ipanel = Ext.create('PVE.lxc.MountPointInputPanel', {
342225bf
DC
359 confid: me.confid,
360 nodename: nodename,
361 unused: unused,
362 unprivileged: me.unprivileged,
f6710aac 363 isCreate: me.isCreate,
342225bf
DC
364 });
365
7aaa492b 366 let subject;
342225bf
DC
367 if (unused) {
368 subject = gettext('Unused Disk');
369 } else if (me.isCreate) {
370 subject = gettext('Mount Point');
371 } else {
372 subject = gettext('Mount Point') + ' (' + me.confid + ')';
373 }
374
375 Ext.apply(me, {
376 subject: subject,
dc683ca0 377 defaultFocus: me.confid !== 'rootfs' ? 'textfield[name=mp]' : 'tool',
f6710aac 378 items: ipanel,
342225bf
DC
379 });
380
381 me.callParent();
382
383 me.load({
384 success: function(response, options) {
385 ipanel.setVMConfig(response.result.data);
342225bf 386
7aaa492b
TL
387 if (me.confid) {
388 let value = response.result.data[me.confid];
389 let mp = PVE.Parser.parseLxcMountPoint(value);
342225bf
DC
390 if (!mp) {
391 Ext.Msg.alert(gettext('Error'), 'Unable to parse mount point options');
392 me.close();
393 return;
394 }
342225bf
DC
395 ipanel.setMountPoint(mp);
396 me.isValid(); // trigger validation
397 }
f6710aac 398 },
342225bf 399 });
f6710aac 400 },
342225bf 401});