]>
Commit | Line | Data |
---|---|---|
09358a73 DM |
1 | Ext.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 | }); |