]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/lxc/CreateWizard.js
ui: lxc: add edit window for device passthrough
[pve-manager.git] / www / manager6 / lxc / CreateWizard.js
CommitLineData
09358a73
DM
1Ext.define('PVE.lxc.CreateWizard', {
2 extend: 'PVE.window.Wizard',
7adbc4a4 3 mixins: ['Proxmox.Mixin.CBind'],
09358a73 4
7adbc4a4
TL
5 viewModel: {
6 data: {
7 nodename: '',
8 storage: '',
f6710aac
TL
9 unprivileged: true,
10 },
5a2e333c
FE
11 formulas: {
12 cgroupMode: function(get) {
13 const nodeInfo = PVE.data.ResourceStore.getNodes().find(
14 node => node.node === get('nodename'),
15 );
16 return nodeInfo ? nodeInfo['cgroup-mode'] : 2;
17 },
18 },
7adbc4a4 19 },
b1339314 20
7adbc4a4 21 cbindData: {
f6710aac 22 nodename: undefined,
7adbc4a4 23 },
b1339314 24
7adbc4a4 25 subject: gettext('LXC Container'),
b1339314 26
7adbc4a4
TL
27 items: [
28 {
29 xtype: 'inputpanel',
30 title: gettext('General'),
31 onlineHelp: 'pct_general',
32 column1: [
09358a73 33 {
7adbc4a4
TL
34 xtype: 'pveNodeSelector',
35 name: 'nodename',
36 cbind: {
37 selectCurNode: '{!nodename}',
f6710aac 38 preferredValue: '{nodename}',
7adbc4a4
TL
39 },
40 bind: {
f6710aac 41 value: '{nodename}',
7adbc4a4
TL
42 },
43 fieldLabel: gettext('Node'),
44 allowBlank: false,
f6710aac 45 onlineValidator: true,
7adbc4a4
TL
46 },
47 {
48 xtype: 'pveGuestIDSelector',
49 name: 'vmid', // backend only knows vmid
50 guestType: 'lxc',
51 value: '',
52 loadNextFreeID: true,
f6710aac 53 validateExists: false,
09358a73
DM
54 },
55 {
7adbc4a4
TL
56 xtype: 'proxmoxtextfield',
57 name: 'hostname',
58 vtype: 'DnsName',
59 value: '',
60 fieldLabel: gettext('Hostname'),
61 skipEmptyText: true,
f6710aac 62 allowBlank: true,
09358a73 63 },
09358a73 64 {
7adbc4a4
TL
65 xtype: 'proxmoxcheckbox',
66 name: 'unprivileged',
02307282 67 value: true,
7adbc4a4 68 bind: {
f6710aac 69 value: '{unprivileged}',
7adbc4a4 70 },
f6710aac
TL
71 fieldLabel: gettext('Unprivileged container'),
72 },
75b0b132
DC
73 {
74 xtype: 'proxmoxcheckbox',
75 name: 'features',
76 inputValue: 'nesting=1',
77 value: true,
78 bind: {
79 disabled: '{!unprivileged}',
80 },
81 fieldLabel: gettext('Nesting'),
82 },
7adbc4a4
TL
83 ],
84 column2: [
85 {
86 xtype: 'pvePoolSelector',
87 fieldLabel: gettext('Resource Pool'),
88 name: 'pool',
7adbc4a4 89 value: '',
f6710aac 90 allowBlank: true,
09358a73
DM
91 },
92 {
7adbc4a4
TL
93 xtype: 'textfield',
94 inputType: 'password',
95 name: 'password',
96 value: '',
97 fieldLabel: gettext('Password'),
98 allowBlank: false,
99 minLength: 5,
100 change: function(f, value) {
101 if (f.rendered) {
102 f.up().down('field[name=confirmpw]').validate();
103 }
f6710aac 104 },
09358a73 105 },
09358a73 106 {
7adbc4a4
TL
107 xtype: 'textfield',
108 inputType: 'password',
109 name: 'confirmpw',
110 value: '',
111 fieldLabel: gettext('Confirm password'),
112 allowBlank: true,
113 submitValue: false,
114 validator: function(value) {
115 var pw = this.up().down('field[name=password]').getValue();
116 if (pw !== value) {
117 return "Passwords do not match!";
118 }
119 return true;
f6710aac 120 },
09358a73
DM
121 },
122 {
ba7002f5 123 xtype: 'textarea',
7adbc4a4
TL
124 name: 'ssh-public-keys',
125 value: '',
ba7002f5 126 fieldLabel: gettext('SSH public key(s)'),
7adbc4a4
TL
127 allowBlank: true,
128 validator: function(value) {
4fd034b1 129 let pwfield = this.up().down('field[name=password]');
7adbc4a4 130 if (value.length) {
ba7002f5
DC
131 let keys = value.indexOf('\n') !== -1 ? value.split('\n') : [value];
132 if (keys.some(key => key !== '' && !PVE.Parser.parseSSHKey(key))) {
7adbc4a4
TL
133 return "Failed to recognize ssh key";
134 }
135 pwfield.allowBlank = true;
136 } else {
137 pwfield.allowBlank = false;
138 }
139 pwfield.validate();
140 return true;
141 },
142 afterRender: function() {
143 if (!window.FileReader) {
4fd034b1 144 return; // No FileReader support in this browser
09358a73 145 }
4fd034b1 146 let cancelEvent = ev => {
7adbc4a4
TL
147 ev = ev.event;
148 if (ev.preventDefault) {
149 ev.preventDefault();
150 }
151 };
4fd034b1
TL
152 this.inputEl.on('dragover', cancelEvent);
153 this.inputEl.on('dragenter', cancelEvent);
154 this.inputEl.on('drop', ev => {
155 cancelEvent(ev);
156 let files = ev.event.dataTransfer.files;
157 PVE.Utils.loadSSHKeyFromFile(files[0], v => this.setValue(v));
7adbc4a4 158 });
f6710aac 159 },
7adbc4a4
TL
160 },
161 {
ba7002f5 162 xtype: 'pveMultiFileButton',
7adbc4a4
TL
163 name: 'file',
164 hidden: !window.FileReader,
165 text: gettext('Load SSH Key File'),
09358a73 166 listeners: {
7adbc4a4
TL
167 change: function(btn, e, value) {
168 e = e.event;
ba7002f5
DC
169 let field = this.up().down('textarea[name=ssh-public-keys]');
170 for (const file of e?.target?.files ?? []) {
171 PVE.Utils.loadSSHKeyFromFile(file, v => {
172 let oldValue = field.getValue();
173 field.setValue(oldValue ? `${oldValue}\n${v.trim()}` : v.trim());
174 });
175 }
7adbc4a4 176 btn.reset();
f6710aac
TL
177 },
178 },
179 },
180 ],
ca97f630
DC
181 advancedColumnB: [
182 {
183 xtype: 'pveTagFieldSet',
184 name: 'tags',
185 maxHeight: 150,
186 },
187 ],
7adbc4a4
TL
188 },
189 {
190 xtype: 'inputpanel',
191 title: gettext('Template'),
192 onlineHelp: 'pct_container_images',
193 column1: [
194 {
195 xtype: 'pveStorageSelector',
196 name: 'tmplstorage',
197 fieldLabel: gettext('Storage'),
198 storageContent: 'vztmpl',
199 autoSelect: true,
200 allowBlank: false,
201 bind: {
202 value: '{storage}',
f6710aac
TL
203 nodename: '{nodename}',
204 },
7adbc4a4
TL
205 },
206 {
207 xtype: 'pveFileSelector',
208 name: 'ostemplate',
209 storageContent: 'vztmpl',
210 fieldLabel: gettext('Template'),
211 bind: {
212 storage: '{storage}',
f6710aac 213 nodename: '{nodename}',
7adbc4a4 214 },
f6710aac
TL
215 allowBlank: false,
216 },
217 ],
7adbc4a4
TL
218 },
219 {
af07d3d3
DC
220 xtype: 'pveMultiMPPanel',
221 title: gettext('Disks'),
7adbc4a4
TL
222 insideWizard: true,
223 isCreate: true,
224 unused: false,
f6710aac 225 confid: 'rootfs',
7adbc4a4
TL
226 },
227 {
228 xtype: 'pveLxcCPUInputPanel',
229 title: gettext('CPU'),
f6710aac 230 insideWizard: true,
7adbc4a4
TL
231 },
232 {
233 xtype: 'pveLxcMemoryInputPanel',
234 title: gettext('Memory'),
f6710aac 235 insideWizard: true,
7adbc4a4
TL
236 },
237 {
238 xtype: 'pveLxcNetworkInputPanel',
239 title: gettext('Network'),
240 insideWizard: true,
241 bind: {
f6710aac 242 nodename: '{nodename}',
7adbc4a4 243 },
f6710aac 244 isCreate: true,
7adbc4a4
TL
245 },
246 {
247 xtype: 'pveLxcDNSInputPanel',
248 title: gettext('DNS'),
f6710aac 249 insideWizard: true,
7adbc4a4
TL
250 },
251 {
252 title: gettext('Confirm'),
253 layout: 'fit',
254 items: [
255 {
256 xtype: 'grid',
257 store: {
258 model: 'KeyValue',
259 sorters: [{
8058410f 260 property: 'key',
f6710aac
TL
261 direction: 'ASC',
262 }],
09358a73 263 },
7adbc4a4 264 columns: [
8058410f
TL
265 { header: 'Key', width: 150, dataIndex: 'key' },
266 { header: 'Value', flex: 1, dataIndex: 'value' },
f6710aac
TL
267 ],
268 },
7adbc4a4 269 ],
fe3d3afc
TL
270 dockedItems: [
271 {
272 xtype: 'proxmoxcheckbox',
273 name: 'start',
274 dock: 'bottom',
275 margin: '5 0 0 0',
f6710aac
TL
276 boxLabel: gettext('Start after created'),
277 },
fe3d3afc 278 ],
7adbc4a4
TL
279 listeners: {
280 show: function(panel) {
4fd034b1
TL
281 let wizard = this.up('window');
282 let kv = wizard.getValues();
283 let data = [];
7adbc4a4
TL
284 Ext.Object.each(kv, function(key, value) {
285 if (key === 'delete' || key === 'tmplstorage') { // ignore
286 return;
287 }
288 if (key === 'password') { // don't show pw
289 return;
290 }
7adbc4a4
TL
291 data.push({ key: key, value: value });
292 });
09358a73 293
4fd034b1
TL
294 let summaryStore = panel.down('grid').getStore();
295 summaryStore.suspendEvents();
296 summaryStore.removeAll();
297 summaryStore.add(data);
298 summaryStore.sort();
299 summaryStore.resumeEvents();
300 summaryStore.fireEvent('refresh');
f6710aac 301 },
7adbc4a4
TL
302 },
303 onSubmit: function() {
4fd034b1
TL
304 let wizard = this.up('window');
305 let kv = wizard.getValues();
399ffa76 306 delete kv.delete;
09358a73 307
4fd034b1 308 let nodename = kv.nodename;
7adbc4a4
TL
309 delete kv.nodename;
310 delete kv.tmplstorage;
b1339314 311
4998ae6f
AN
312 if (!kv.pool.length) {
313 delete kv.pool;
314 }
7adbc4a4
TL
315 if (!kv.password.length && kv['ssh-public-keys']) {
316 delete kv.password;
317 }
fd776fca 318
7adbc4a4 319 Proxmox.Utils.API2Request({
4fd034b1 320 url: `/nodes/${nodename}/lxc`,
7adbc4a4
TL
321 waitMsgTarget: wizard,
322 method: 'POST',
323 params: kv,
8058410f 324 success: function(response, opts) {
4fd034b1
TL
325 Ext.create('Proxmox.window.TaskViewer', {
326 autoShow: true,
327 upid: response.result.data,
09358a73 328 });
7adbc4a4
TL
329 wizard.close();
330 },
4fd034b1 331 failure: (response, opts) => Ext.Msg.alert(gettext('Error'), response.htmlStatus),
7adbc4a4 332 });
f6710aac
TL
333 },
334 },
335 ],
09358a73 336});