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