]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/qemu/CloudInit.js
ui: cloudinit: match backend privilege checks
[pve-manager.git] / www / manager6 / qemu / CloudInit.js
CommitLineData
4c507192
DC
1Ext.define('PVE.qemu.CloudInit', {
2 extend: 'Proxmox.grid.PendingObjectGrid',
3 xtype: 'pveCiPanel',
4
16dedd0f
DC
5 onlineHelp: 'qm_cloud_init',
6
4c507192
DC
7 tbar: [
8 {
9 xtype: 'proxmoxButton',
10 disabled: true,
11 dangerous: true,
12 confirmMsg: function(rec) {
46d9a35c 13 let view = this.up('grid');
4c507192
DC
14 var warn = gettext('Are you sure you want to remove entry {0}');
15
16 var entry = rec.data.key;
17 var msg = Ext.String.format(warn, "'"
46d9a35c 18 + view.renderKey(entry, {}, rec) + "'");
4c507192
DC
19
20 return msg;
21 },
22 enableFn: function(record) {
46d9a35c 23 let view = this.up('grid');
4c507192 24 var caps = Ext.state.Manager.get('GuiCap');
d1c7fa02
FG
25 let caps_ci = caps.vms['VM.Config.Network'] || caps.vms['VM.Config.Cloudinit'];
26 if (view.rows[record.data.key].never_delete || !caps_ci) {
4c507192
DC
27 return false;
28 }
2ed6dceb
DC
29
30 if (record.data.key === 'cipassword' && !record.data.value) {
31 return false;
32 }
4c507192
DC
33 return true;
34 },
35 handler: function() {
46d9a35c
DJ
36 let view = this.up('grid');
37 let records = view.getSelection();
8058410f 38 if (!records || !records.length) {
2ed6dceb
DC
39 return;
40 }
41
42 var id = records[0].data.key;
43 var match = id.match(/^net(\d+)$/);
44 if (match) {
45 id = 'ipconfig' + match[1];
46 }
47
48 var params = {};
399ffa76 49 params.delete = id;
2ed6dceb 50 Proxmox.Utils.API2Request({
46d9a35c
DJ
51 url: view.baseurl + '/config',
52 waitMsgTarget: view,
2ed6dceb
DC
53 method: 'PUT',
54 params: params,
55 failure: function(response, opts) {
56 Ext.Msg.alert('Error', response.htmlStatus);
57 },
58 callback: function() {
46d9a35c 59 view.reload();
f6710aac 60 },
2ed6dceb 61 });
4c507192 62 },
f6710aac 63 text: gettext('Remove'),
4c507192
DC
64 },
65 {
66 xtype: 'proxmoxButton',
67 disabled: true,
d790a1b4 68 enableFn: function(rec) {
46d9a35c
DJ
69 let view = this.up('pveCiPanel');
70 return !!view.rows[rec.data.key].editor;
d790a1b4 71 },
4c507192 72 handler: function() {
46d9a35c
DJ
73 let view = this.up('grid');
74 view.run_editor();
4c507192 75 },
f6710aac 76 text: gettext('Edit'),
4c507192
DC
77 },
78 '-',
79 {
80 xtype: 'button',
81 itemId: 'savebtn',
82 text: gettext('Regenerate Image'),
83 handler: function() {
46d9a35c 84 let view = this.up('grid');
4c507192
DC
85 var eject_params = {};
86 var insert_params = {};
46d9a35c 87 let disk = PVE.Parser.parseQemuDrive(view.ciDriveId, view.ciDrive);
4c507192 88 var storage = '';
f1374bff 89 var stormatch = disk.file.match(/^([^:]+):/);
4c507192
DC
90 if (stormatch) {
91 storage = stormatch[1];
92 }
46d9a35c
DJ
93 eject_params[view.ciDriveId] = 'none,media=cdrom';
94 insert_params[view.ciDriveId] = storage + ':cloudinit';
4c507192
DC
95
96 var failure = function(response, opts) {
97 Ext.Msg.alert('Error', response.htmlStatus);
98 };
99
100 Proxmox.Utils.API2Request({
46d9a35c
DJ
101 url: view.baseurl + '/config',
102 waitMsgTarget: view,
4c507192
DC
103 method: 'PUT',
104 params: eject_params,
105 failure: failure,
106 callback: function() {
107 Proxmox.Utils.API2Request({
46d9a35c
DJ
108 url: view.baseurl + '/config',
109 waitMsgTarget: view,
4c507192
DC
110 method: 'PUT',
111 params: insert_params,
112 failure: failure,
113 callback: function() {
46d9a35c 114 view.reload();
f6710aac 115 },
4c507192 116 });
f6710aac 117 },
4c507192 118 });
f6710aac
TL
119 },
120 },
4c507192
DC
121 ],
122
123 border: false,
124
125 set_button_status: function(rstore, records, success) {
126 if (!success || records.length < 1) {
127 return;
128 }
129 var me = this;
130 var found;
131 records.forEach(function(record) {
132 if (found) {
133 return;
134 }
135 var id = record.data.key;
136 var value = record.data.value;
137 var ciregex = new RegExp("vm-" + me.pveSelNode.data.vmid + "-cloudinit");
138 if (id.match(/^(ide|scsi|sata)\d+$/) && ciregex.test(value)) {
139 found = id;
140 me.ciDriveId = found;
141 me.ciDrive = value;
142 }
143 });
144
145 me.down('#savebtn').setDisabled(!found);
146 me.setDisabled(!found);
147 if (!found) {
148 me.getView().mask(gettext('No CloudInit Drive found'), ['pve-static-mask']);
149 } else {
150 me.getView().unmask();
151 }
152 },
153
154 renderKey: function(key, metaData, rec, rowIndex, colIndex, store) {
155 var me = this;
156 var rows = me.rows;
157 var rowdef = rows[key] || {};
158
159 var icon = "";
160 if (rowdef.iconCls) {
161 icon = '<i class="' + rowdef.iconCls + '"></i> ';
162 }
163 return icon + (rowdef.header || key);
164 },
165
166 listeners: {
8058410f 167 activate: function() {
4c507192
DC
168 var me = this;
169 me.rstore.startUpdate();
170 },
171 itemdblclick: function() {
172 var me = this;
173 me.run_editor();
f6710aac 174 },
4c507192
DC
175 },
176
177 initComponent: function() {
178 var me = this;
179
180 var nodename = me.pveSelNode.data.node;
181 if (!nodename) {
182 throw "no node name specified";
183 }
184
185 var vmid = me.pveSelNode.data.vmid;
186 if (!vmid) {
187 throw "no VM ID specified";
188 }
189 var caps = Ext.state.Manager.get('GuiCap');
190 me.baseurl = '/api2/extjs/nodes/' + nodename + '/qemu/' + vmid;
8058410f 191 me.url = me.baseurl + '/pending';
4c507192
DC
192 me.editorConfig.url = me.baseurl + '/config';
193 me.editorConfig.pveSelNode = me.pveSelNode;
194
c081336c 195 let caps_ci = caps.vms['VM.Config.Cloudinit'] || caps.vms['VM.Config.Network'];
4c507192
DC
196 /* editor is string and object */
197 me.rows = {
198 ciuser: {
199 header: gettext('User'),
200 iconCls: 'fa fa-user',
201 never_delete: true,
202 defaultValue: '',
044b08f2 203 editor: caps_ci ? {
4c507192
DC
204 xtype: 'proxmoxWindowEdit',
205 subject: gettext('User'),
206 items: [
207 {
208 xtype: 'proxmoxtextfield',
209 deleteEmpty: true,
210 emptyText: Proxmox.Utils.defaultText,
211 fieldLabel: gettext('User'),
f6710aac
TL
212 name: 'ciuser',
213 },
214 ],
4c507192
DC
215 } : undefined,
216 renderer: function(value) {
217 return value || Proxmox.Utils.defaultText;
f6710aac 218 },
4c507192
DC
219 },
220 cipassword: {
221 header: gettext('Password'),
222 iconCls: 'fa fa-unlock',
4c507192 223 defaultValue: '',
044b08f2 224 editor: caps_ci ? {
4c507192
DC
225 xtype: 'proxmoxWindowEdit',
226 subject: gettext('Password'),
227 items: [
228 {
229 xtype: 'proxmoxtextfield',
230 inputType: 'password',
231 deleteEmpty: true,
232 emptyText: Proxmox.Utils.noneText,
233 fieldLabel: gettext('Password'),
f6710aac
TL
234 name: 'cipassword',
235 },
236 ],
4c507192
DC
237 } : undefined,
238 renderer: function(value) {
239 return value || Proxmox.Utils.noneText;
f6710aac 240 },
4c507192
DC
241 },
242 searchdomain: {
243 header: gettext('DNS domain'),
244 iconCls: 'fa fa-globe',
d1c7fa02 245 editor: caps_ci ? 'PVE.lxc.DNSEdit' : undefined,
4c507192 246 never_delete: true,
f6710aac 247 defaultValue: gettext('use host settings'),
4c507192
DC
248 },
249 nameserver: {
250 header: gettext('DNS servers'),
251 iconCls: 'fa fa-globe',
d1c7fa02 252 editor: caps_ci ? 'PVE.lxc.DNSEdit' : undefined,
4c507192 253 never_delete: true,
f6710aac 254 defaultValue: gettext('use host settings'),
4c507192
DC
255 },
256 sshkeys: {
257 header: gettext('SSH public key'),
258 iconCls: 'fa fa-key',
044b08f2 259 editor: caps_ci ? 'PVE.qemu.SSHKeyEdit' : undefined,
4c507192
DC
260 never_delete: true,
261 renderer: function(value) {
262 value = decodeURIComponent(value);
263 var keys = value.split('\n');
264 var text = [];
265 keys.forEach(function(key) {
266 if (key.length) {
963bb5c9 267 let res = PVE.Parser.parseSSHKey(key);
4c507192 268 if (res) {
963bb5c9
LS
269 key = Ext.String.htmlEncode(res.comment);
270 if (res.options) {
4c507192
DC
271 key += ' <span style="color:gray">(' + gettext('with options') + ')</span>';
272 }
273 text.push(key);
274 return;
275 }
276 // Most likely invalid at this point, so just stick to
277 // the old value.
278 text.push(Ext.String.htmlEncode(key));
279 }
280 });
281 if (text.length) {
282 return text.join('<br>');
283 } else {
284 return Proxmox.Utils.noneText;
285 }
286 },
f6710aac
TL
287 defaultValue: '',
288 },
4c507192
DC
289 };
290 var i;
291 var ipconfig_renderer = function(value, md, record, ri, ci, store, pending) {
292 var id = record.data.key;
293 var match = id.match(/^net(\d+)$/);
294 var val = '';
295 if (match) {
296 val = me.getObjectValue('ipconfig'+match[1], '', pending);
297 }
298 return val;
299 };
300 for (i = 0; i < 32; i++) {
301 // we want to show an entry for every network device
302 // even if it is empty
303 me.rows['net' + i.toString()] = {
304 multiKey: ['ipconfig' + i.toString(), 'net' + i.toString()],
305 header: gettext('IP Config') + ' (net' + i.toString() +')',
d1c7fa02 306 editor: caps_ci ? 'PVE.qemu.IPConfigEdit' : undefined,
4c507192 307 iconCls: 'fa fa-exchange',
f6710aac 308 renderer: ipconfig_renderer,
4c507192
DC
309 };
310 me.rows['ipconfig' + i.toString()] = {
f6710aac 311 visible: false,
4c507192
DC
312 };
313 }
4c507192
DC
314
315 PVE.Utils.forEachBus(['ide', 'scsi', 'sata'], function(type, id) {
316 me.rows[type+id] = {
f6710aac 317 visible: false,
4c507192
DC
318 };
319 });
320 me.callParent();
321 me.mon(me.rstore, 'load', me.set_button_status, me);
f6710aac 322 },
4c507192 323});