]> git.proxmox.com Git - pve-manager.git/blame - www/manager6/Parser.js
ui: restore: allow override of some settings
[pve-manager.git] / www / manager6 / Parser.js
CommitLineData
b85ef5ab 1// Some configuration values are complex strings - so we need parsers/generators for them.
8058410f
TL
2Ext.define('PVE.Parser', {
3 statics: {
fcb64fe4
DM
4
5 // this class only contains static functions
6
899c4f45
DC
7 printACME: function(value) {
8 if (Ext.isArray(value.domains)) {
9 value.domains = value.domains.join(';');
10 }
11 return PVE.Parser.printPropertyString(value);
12 },
13
488be4c2
DC
14 parseACME: function(value) {
15 if (!value) {
6ac64c3a 16 return {};
488be4c2
DC
17 }
18
a3aef0f5
TL
19 let res = {};
20 try {
21 value.split(',').forEach(property => {
22 let [k, v] = property.split('=', 2);
23 if (Ext.isDefined(v)) {
24 res[k] = v;
25 } else {
26 throw `Failed to parse key-value pair: ${property}`;
27 }
28 });
29 } catch (err) {
30 console.warn(err);
31 return undefined;
488be4c2
DC
32 }
33
08a61c7b
FG
34 if (res.domains !== undefined) {
35 res.domains = res.domains.split(/;/);
36 }
37
488be4c2
DC
38 return res;
39 },
40
4c1c0d5d 41 parseBoolean: function(value, default_value) {
84de645d 42 if (!Ext.isDefined(value)) {
4c1c0d5d 43 return default_value;
84de645d 44 }
4c1c0d5d 45 value = value.toLowerCase();
ec0bd652 46 return value === '1' ||
4c1c0d5d
EK
47 value === 'on' ||
48 value === 'yes' ||
49 value === 'true';
50 },
51
3d6189d3 52 parsePropertyString: function(value, defaultKey) {
a3aef0f5 53 let res = {};
3d6189d3 54
4a7248d4
TL
55 if (typeof value !== 'string' || value === '') {
56 return res;
57 }
58
a3aef0f5
TL
59 try {
60 value.split(',').forEach(property => {
61 let [k, v] = property.split('=', 2);
62 if (Ext.isDefined(v)) {
63 res[k] = v;
64 } else if (Ext.isDefined(defaultKey)) {
65 if (Ext.isDefined(res[defaultKey])) {
66 throw 'defaultKey may be only defined once in propertyString';
67 }
68 res[defaultKey] = k; // k ist the value in this case
69 } else {
70 throw `Failed to parse key-value pair: ${property}`;
3d6189d3 71 }
a3aef0f5
TL
72 });
73 } catch (err) {
74 console.warn(err);
75 return undefined;
3d6189d3
SI
76 }
77
78 return res;
79 },
80
81 printPropertyString: function(data, defaultKey) {
6de2baee
TL
82 var stringparts = [],
83 gotDefaultKeyVal = false,
84 defaultKeyVal;
3d6189d3
SI
85
86 Ext.Object.each(data, function(key, value) {
8007aaaf 87 if (defaultKey !== undefined && key === defaultKey) {
6de2baee
TL
88 gotDefaultKeyVal = true;
89 defaultKeyVal = value;
08e38f03 90 } else if (value !== '') {
8007aaaf 91 stringparts.push(key + '=' + value);
3d6189d3 92 }
3d6189d3
SI
93 });
94
6de2baee
TL
95 stringparts = stringparts.sort();
96 if (gotDefaultKeyVal) {
97 stringparts.unshift(defaultKeyVal);
98 }
99
3d6189d3
SI
100 return stringparts.join(',');
101 },
102
fcb64fe4
DM
103 parseQemuNetwork: function(key, value) {
104 if (!(key && value)) {
a3aef0f5 105 return undefined;
fcb64fe4
DM
106 }
107
a3aef0f5
TL
108 let res = {},
109 errors = false;
fcb64fe4
DM
110 Ext.Array.each(value.split(','), function(p) {
111 if (!p || p.match(/^\s*$/)) {
a3aef0f5 112 return undefined; // continue
fcb64fe4
DM
113 }
114
a3aef0f5 115 let match_res;
fcb64fe4
DM
116
117 if ((match_res = p.match(/^(ne2k_pci|e1000|e1000-82540em|e1000-82544gc|e1000-82545em|vmxnet3|rtl8139|pcnet|virtio|ne2k_isa|i82551|i82557b|i82559er)(=([0-9a-f]{2}(:[0-9a-f]{2}){5}))?$/i)) !== null) {
118 res.model = match_res[1].toLowerCase();
119 if (match_res[3]) {
120 res.macaddr = match_res[3];
121 }
122 } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) {
123 res.bridge = match_res[1];
124 } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) {
125 res.rate = match_res[1];
126 } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) {
63e62a6f 127 res.tag = match_res[1];
fcb64fe4 128 } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) {
63e62a6f 129 res.firewall = match_res[1];
fcb64fe4 130 } else if ((match_res = p.match(/^link_down=(\d+)$/)) !== null) {
63e62a6f 131 res.disconnect = match_res[1];
fcb64fe4 132 } else if ((match_res = p.match(/^queues=(\d+)$/)) !== null) {
63e62a6f 133 res.queues = match_res[1];
a2ed0697
WB
134 } else if ((match_res = p.match(/^trunks=(\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*)$/)) !== null) {
135 res.trunks = match_res[1];
ced67201
DC
136 } else if ((match_res = p.match(/^mtu=(\d+)$/)) !== null) {
137 res.mtu = match_res[1];
fcb64fe4
DM
138 } else {
139 errors = true;
140 return false; // break
141 }
a3aef0f5 142 return undefined; // continue
fcb64fe4
DM
143 });
144
145 if (errors || !res.model) {
a3aef0f5 146 return undefined;
fcb64fe4
DM
147 }
148
149 return res;
150 },
151
152 printQemuNetwork: function(net) {
fcb64fe4
DM
153 var netstr = net.model;
154 if (net.macaddr) {
155 netstr += "=" + net.macaddr;
156 }
157 if (net.bridge) {
158 netstr += ",bridge=" + net.bridge;
159 if (net.tag) {
160 netstr += ",tag=" + net.tag;
161 }
162 if (net.firewall) {
163 netstr += ",firewall=" + net.firewall;
164 }
165 }
166 if (net.rate) {
167 netstr += ",rate=" + net.rate;
168 }
169 if (net.queues) {
170 netstr += ",queues=" + net.queues;
171 }
172 if (net.disconnect) {
173 netstr += ",link_down=" + net.disconnect;
174 }
a2ed0697
WB
175 if (net.trunks) {
176 netstr += ",trunks=" + net.trunks;
177 }
ced67201
DC
178 if (net.mtu) {
179 netstr += ",mtu=" + net.mtu;
180 }
fcb64fe4
DM
181 return netstr;
182 },
183
184 parseQemuDrive: function(key, value) {
185 if (!(key && value)) {
a3aef0f5 186 return undefined;
fcb64fe4
DM
187 }
188
a3aef0f5
TL
189 const [, bus, index] = key.match(/^([a-z]+)(\d+)$/);
190 if (!bus) {
191 return undefined;
fcb64fe4 192 }
a3aef0f5
TL
193 let res = {
194 'interface': bus,
195 index,
196 };
fcb64fe4
DM
197
198 var errors = false;
199 Ext.Array.each(value.split(','), function(p) {
200 if (!p || p.match(/^\s*$/)) {
a3aef0f5 201 return undefined; // continue
fcb64fe4 202 }
a3aef0f5
TL
203 let match = p.match(/^([a-z_]+)=(\S+)$/);
204 if (!match) {
205 if (!p.match(/[=]/)) {
fcb64fe4 206 res.file = p;
a3aef0f5 207 return undefined; // continue
fcb64fe4
DM
208 }
209 errors = true;
210 return false; // break
211 }
a3aef0f5 212 let [, k, v] = match;
fcb64fe4
DM
213 if (k === 'volume') {
214 k = 'file';
215 }
216
217 if (Ext.isDefined(res[k])) {
218 errors = true;
219 return false; // break
220 }
221
fcb64fe4
DM
222 if (k === 'cache' && v === 'off') {
223 v = 'none';
224 }
225
226 res[k] = v;
a3aef0f5
TL
227
228 return undefined; // continue
fcb64fe4
DM
229 });
230
231 if (errors || !res.file) {
a3aef0f5 232 return undefined;
fcb64fe4
DM
233 }
234
235 return res;
236 },
237
238 printQemuDrive: function(drive) {
fcb64fe4
DM
239 var drivestr = drive.file;
240
241 Ext.Object.each(drive, function(key, value) {
242 if (!Ext.isDefined(value) || key === 'file' ||
243 key === 'index' || key === 'interface') {
244 return; // continue
245 }
246 drivestr += ',' + key + '=' + value;
247 });
248
249 return drivestr;
250 },
251
01e02121
DC
252 parseIPConfig: function(key, value) {
253 if (!(key && value)) {
a3aef0f5 254 return undefined; // continue
01e02121
DC
255 }
256
a3aef0f5
TL
257 let res = {};
258 try {
259 value.split(',').forEach(p => {
260 if (!p || p.match(/^\s*$/)) {
261 return; // continue
262 }
01e02121 263
a3aef0f5
TL
264 const match = p.match(/^(ip|gw|ip6|gw6)=(\S+)$/);
265 if (!match) {
266 throw `could not parse as IP config: ${p}`;
267 }
268 let [, k, v] = match;
269 res[k] = v;
270 });
271 } catch (err) {
272 console.warn(err);
273 return undefined; // continue
01e02121
DC
274 }
275
276 return res;
277 },
278
279 printIPConfig: function(cfg) {
a3aef0f5
TL
280 return Object.entries(cfg)
281 .filter(([k, v]) => v && k.match(/^(ip|gw|ip6|gw6)$/))
282 .map(([k, v]) => `${k}=${v}`)
283 .join(',');
01e02121
DC
284 },
285
fcb64fe4
DM
286 parseLxcNetwork: function(value) {
287 if (!value) {
a3aef0f5 288 return undefined;
fcb64fe4
DM
289 }
290
a3aef0f5
TL
291 let data = {};
292 value.split(',').forEach(p => {
fcb64fe4
DM
293 if (!p || p.match(/^\s*$/)) {
294 return; // continue
295 }
a3aef0f5 296 let match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|tag|rate)=(\S+)$/);
95e77b22
TL
297 if (match_res) {
298 data[match_res[1]] = match_res[2];
299 } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) {
805a7cb8 300 data.firewall = PVE.Parser.parseBoolean(match_res[1]);
a3aef0f5
TL
301 } else if (!p.match(/^type=\S+$/)) {
302 console.warn(`could not parse LXC network string ${p}`);
fcb64fe4 303 }
fcb64fe4
DM
304 });
305
306 return data;
307 },
308
a3aef0f5
TL
309 printLxcNetwork: function(config) {
310 let knownKeys = {
311 bridge: 1,
312 firewall: 1,
313 gw6: 1,
314 gw: 1,
315 hwaddr: 1,
316 ip6: 1,
317 ip: 1,
318 mtu: 1,
319 name: 1,
320 rate: 1,
321 tag: 1,
322 };
323 return Object.entries(config)
f155d934 324 .filter(([k, v]) => v !== undefined && v !== '' && knownKeys[k])
a3aef0f5
TL
325 .map(([k, v]) => `${k}=${v}`)
326 .join(',');
fcb64fe4
DM
327 },
328
4c1c0d5d
EK
329 parseLxcMountPoint: function(value) {
330 if (!value) {
a3aef0f5 331 return undefined;
4c1c0d5d
EK
332 }
333
a3aef0f5
TL
334 let res = {};
335 let errors = false;
4c1c0d5d
EK
336 Ext.Array.each(value.split(','), function(p) {
337 if (!p || p.match(/^\s*$/)) {
a3aef0f5 338 return undefined; // continue
4c1c0d5d 339 }
a3aef0f5
TL
340 let match = p.match(/^([a-z_]+)=(.+)$/);
341 if (!match) {
342 if (!p.match(/[=]/)) {
4c1c0d5d 343 res.file = p;
a3aef0f5 344 return undefined; // continue
4c1c0d5d
EK
345 }
346 errors = true;
347 return false; // break
348 }
a3aef0f5 349 let [, k, v] = match;
4c1c0d5d
EK
350 if (k === 'volume') {
351 k = 'file';
352 }
353
354 if (Ext.isDefined(res[k])) {
355 errors = true;
356 return false; // break
357 }
358
4c1c0d5d 359 res[k] = v;
a3aef0f5
TL
360
361 return undefined;
4c1c0d5d
EK
362 });
363
364 if (errors || !res.file) {
a3aef0f5 365 return undefined;
4c1c0d5d
EK
366 }
367
308d9f36
DC
368 const match = res.file.match(/^([a-z][a-z0-9\-_.]*[a-z0-9]):/i);
369 if (match) {
370 res.storage = match[1];
4c1c0d5d
EK
371 res.type = 'volume';
372 } else if (res.file.match(/^\/dev\//)) {
373 res.type = 'device';
374 } else {
375 res.type = 'bind';
376 }
377
378 return res;
379 },
380
381 printLxcMountPoint: function(mp) {
a3aef0f5 382 let drivestr = mp.file;
c57ff36d 383 for (const [key, value] of Object.entries(mp)) {
a3aef0f5
TL
384 if (!Ext.isDefined(value) || key === 'file' || key === 'type' || key === 'storage') {
385 continue;
4c1c0d5d 386 }
a3aef0f5
TL
387 drivestr += `,${key}=${value}`;
388 }
4c1c0d5d
EK
389 return drivestr;
390 },
391
fcb64fe4
DM
392 parseStartup: function(value) {
393 if (value === undefined) {
a3aef0f5 394 return undefined;
fcb64fe4
DM
395 }
396
a3aef0f5
TL
397 let res = {};
398 try {
399 value.split(',').forEach(p => {
400 if (!p || p.match(/^\s*$/)) {
401 return; // continue
402 }
fcb64fe4 403
a3aef0f5
TL
404 let match_res;
405 if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) {
406 res.order = match_res[2];
407 } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) {
408 res.up = match_res[1];
409 } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) {
410 res.down = match_res[1];
411 } else {
412 throw `could not parse startup config ${p}`;
413 }
414 });
415 } catch (err) {
416 console.warn(err);
417 return undefined;
fcb64fe4
DM
418 }
419
420 return res;
421 },
422
423 printStartup: function(startup) {
a3aef0f5 424 let arr = [];
fcb64fe4
DM
425 if (startup.order !== undefined && startup.order !== '') {
426 arr.push('order=' + startup.order);
427 }
428 if (startup.up !== undefined && startup.up !== '') {
429 arr.push('up=' + startup.up);
430 }
431 if (startup.down !== undefined && startup.down !== '') {
432 arr.push('down=' + startup.down);
433 }
434
435 return arr.join(',');
436 },
437
438 parseQemuSmbios1: function(value) {
a3aef0f5
TL
439 let res = value.split(',').reduce((acc, currentValue) => {
440 const [k, v] = currentValue.split(/[=](.+)/);
441 acc[k] = v;
442 return acc;
150ad74a
CE
443 }, {});
444
445 if (PVE.Parser.parseBoolean(res.base64, false)) {
a3aef0f5
TL
446 for (const [k, v] of Object.entries(res)) {
447 if (k !== 'uuid') {
448 res[k] = Ext.util.Base64.decode(v);
449 }
450 }
150ad74a 451 }
fcb64fe4
DM
452
453 return res;
454 },
455
456 printQemuSmbios1: function(data) {
a3aef0f5
TL
457 let base64 = false;
458 let datastr = Object.entries(data)
459 .map(([key, value]) => {
460 if (value === '') {
461 return undefined;
150ad74a 462 }
a3aef0f5
TL
463 if (key !== 'uuid') {
464 base64 = true; // smbios values can be arbitrary, so encode and mark config as such
465 value = Ext.util.Base64.encode(value);
466 }
467 return `${key}=${value}`;
468 })
469 .filter(v => v !== undefined)
470 .join(',');
fcb64fe4 471
a3aef0f5
TL
472 if (base64) {
473 datastr += ',base64=1';
474 }
fcb64fe4
DM
475 return datastr;
476 },
477
478 parseTfaConfig: function(value) {
a3aef0f5
TL
479 let res = {};
480 value.split(',').forEach(p => {
481 const [k, v] = p.split('=', 2);
482 res[k] = v;
fcb64fe4
DM
483 });
484
485 return res;
4c1c0d5d
EK
486 },
487
1cdb49c1 488 parseTfaType: function(value) {
a3aef0f5 489 let match;
1cdb49c1
WB
490 if (!value || !value.length) {
491 return undefined;
492 } else if (value === 'x!oath') {
493 return 'totp';
a3aef0f5 494 } else if ((match = value.match(/^x!(.+)$/)) !== null) {
1cdb49c1
WB
495 return match[1];
496 } else {
497 return 1;
498 }
499 },
500
4c1c0d5d
EK
501 parseQemuCpu: function(value) {
502 if (!value) {
503 return {};
504 }
505
a3aef0f5
TL
506 let res = {};
507 let errors = false;
4c1c0d5d
EK
508 Ext.Array.each(value.split(','), function(p) {
509 if (!p || p.match(/^\s*$/)) {
a3aef0f5 510 return undefined; // continue
4c1c0d5d 511 }
fcb64fe4 512
a3aef0f5 513 if (!p.match(/[=]/)) {
ec0bd652 514 if (Ext.isDefined(res.cpu)) {
4c1c0d5d
EK
515 errors = true;
516 return false; // break
517 }
518 res.cputype = p;
a3aef0f5 519 return undefined; // continue
4c1c0d5d
EK
520 }
521
a3aef0f5
TL
522 let match = p.match(/^([a-z_]+)=(\S+)$/);
523 if (!match || Ext.isDefined(res[match[1]])) {
4c1c0d5d
EK
524 errors = true;
525 return false; // break
526 }
527
a3aef0f5
TL
528 let [, k, v] = match;
529 res[k] = v;
4c1c0d5d 530
a3aef0f5 531 return undefined;
4c1c0d5d
EK
532 });
533
534 if (errors || !res.cputype) {
a3aef0f5 535 return undefined;
4c1c0d5d
EK
536 }
537
538 return res;
539 },
540
541 printQemuCpu: function(cpu) {
a3aef0f5
TL
542 let cpustr = cpu.cputype;
543 let optstr = '';
4c1c0d5d
EK
544
545 Ext.Object.each(cpu, function(key, value) {
546 if (!Ext.isDefined(value) || key === 'cputype') {
547 return; // continue
548 }
549 optstr += ',' + key + '=' + value;
550 });
551
552 if (!cpustr) {
84de645d 553 if (optstr) {
4c1c0d5d 554 return 'kvm64' + optstr;
a3aef0f5
TL
555 } else {
556 return undefined;
84de645d 557 }
4c1c0d5d
EK
558 }
559
560 return cpustr + optstr;
b1339314
WB
561 },
562
563 parseSSHKey: function(key) {
564 // |--- options can have quotes--| type key comment
a3aef0f5 565 let keyre = /^(?:((?:[^\s"]|"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/;
2cec9697 566 let typere = /^(?:(?:sk-)?(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)(?:@(?:[a-z0-9_-]+\.)+[a-z]{2,})?)$/;
b1339314 567
a3aef0f5
TL
568 let m = key.match(keyre);
569 if (!m || m.length < 3 || !m[2]) { // [2] is always either type or key
b1339314
WB
570 return null;
571 }
572 if (m[1] && m[1].match(typere)) {
573 return {
574 type: m[1],
575 key: m[2],
f6710aac 576 comment: m[3],
b1339314
WB
577 };
578 }
579 if (m[2].match(typere)) {
580 return {
581 options: m[1],
582 type: m[2],
583 key: m[3],
f6710aac 584 comment: m[4],
b1339314
WB
585 };
586 }
587 return null;
45708891
DC
588 },
589
590 parseACMEPluginData: function(data) {
591 let res = {};
592 let extradata = [];
593 data.split('\n').forEach((line) => {
594 // capture everything after the first = as value
a3aef0f5 595 let [key, value] = line.split(/[=](.+)/);
45708891
DC
596 if (value !== undefined) {
597 res[key] = value;
598 } else {
599 extradata.push(line);
600 }
601 });
602 return [res, extradata];
603 },
fa8d3971 604},
8058410f 605});