]> git.proxmox.com Git - pve-manager.git/blob - www/manager6/Parser.js
ui: eslint: fix trailing comma and comma related whitespaces errors
[pve-manager.git] / www / manager6 / Parser.js
1 // Some configuration values are complex strings -
2 // so we need parsers/generators for them.
3
4 Ext.define('PVE.Parser', { statics: {
5
6 // this class only contains static functions
7
8 printACME: function(value) {
9 if (Ext.isArray(value.domains)) {
10 value.domains = value.domains.join(';');
11 }
12 return PVE.Parser.printPropertyString(value);
13 },
14
15 parseACME: function(value) {
16 if (!value) {
17 return {};
18 }
19
20 var res = {};
21 var error;
22
23 Ext.Array.each(value.split(','), function(p) {
24 var kv = p.split('=', 2);
25 if (Ext.isDefined(kv[1])) {
26 res[kv[0]] = kv[1];
27 } else {
28 error = 'Failed to parse key-value pair: '+p;
29 return false;
30 }
31 });
32
33 if (error !== undefined) {
34 console.error(error);
35 return;
36 }
37
38 if (res.domains !== undefined) {
39 res.domains = res.domains.split(/;/);
40 }
41
42 return res;
43 },
44
45 parseBoolean: function(value, default_value) {
46 if (!Ext.isDefined(value)) {
47 return default_value;
48 }
49 value = value.toLowerCase();
50 return value === '1' ||
51 value === 'on' ||
52 value === 'yes' ||
53 value === 'true';
54 },
55
56 parsePropertyString: function(value, defaultKey) {
57 var res = {},
58 error;
59
60 if (typeof value !== 'string' || value === '') {
61 return res;
62 }
63
64 Ext.Array.each(value.split(','), function(p) {
65 var kv = p.split('=', 2);
66 if (Ext.isDefined(kv[1])) {
67 res[kv[0]] = kv[1];
68 } else if (Ext.isDefined(defaultKey)) {
69 if (Ext.isDefined(res[defaultKey])) {
70 error = 'defaultKey may be only defined once in propertyString';
71 return false; // break
72 }
73 res[defaultKey] = kv[0];
74 } else {
75 error = 'invalid propertyString, not a key=value pair and no defaultKey defined';
76 return false; // break
77 }
78 });
79
80 if (error !== undefined) {
81 console.error(error);
82 return;
83 }
84
85 return res;
86 },
87
88 printPropertyString: function(data, defaultKey) {
89 var stringparts = [],
90 gotDefaultKeyVal = false,
91 defaultKeyVal;
92
93 Ext.Object.each(data, function(key, value) {
94 if (defaultKey !== undefined && key === defaultKey) {
95 gotDefaultKeyVal = true;
96 defaultKeyVal = value;
97 } else if (value !== '') {
98 stringparts.push(key + '=' + value);
99 }
100 });
101
102 stringparts = stringparts.sort();
103 if (gotDefaultKeyVal) {
104 stringparts.unshift(defaultKeyVal);
105 }
106
107 return stringparts.join(',');
108 },
109
110 parseQemuNetwork: function(key, value) {
111 if (!(key && value)) {
112 return;
113 }
114
115 var res = {};
116
117 var errors = false;
118 Ext.Array.each(value.split(','), function(p) {
119 if (!p || p.match(/^\s*$/)) {
120 return; // continue
121 }
122
123 var match_res;
124
125 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) {
126 res.model = match_res[1].toLowerCase();
127 if (match_res[3]) {
128 res.macaddr = match_res[3];
129 }
130 } else if ((match_res = p.match(/^bridge=(\S+)$/)) !== null) {
131 res.bridge = match_res[1];
132 } else if ((match_res = p.match(/^rate=(\d+(\.\d+)?)$/)) !== null) {
133 res.rate = match_res[1];
134 } else if ((match_res = p.match(/^tag=(\d+(\.\d+)?)$/)) !== null) {
135 res.tag = match_res[1];
136 } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) {
137 res.firewall = match_res[1];
138 } else if ((match_res = p.match(/^link_down=(\d+)$/)) !== null) {
139 res.disconnect = match_res[1];
140 } else if ((match_res = p.match(/^queues=(\d+)$/)) !== null) {
141 res.queues = match_res[1];
142 } else if ((match_res = p.match(/^trunks=(\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*)$/)) !== null) {
143 res.trunks = match_res[1];
144 } else {
145 errors = true;
146 return false; // break
147 }
148 });
149
150 if (errors || !res.model) {
151 return;
152 }
153
154 return res;
155 },
156
157 printQemuNetwork: function(net) {
158
159 var netstr = net.model;
160 if (net.macaddr) {
161 netstr += "=" + net.macaddr;
162 }
163 if (net.bridge) {
164 netstr += ",bridge=" + net.bridge;
165 if (net.tag) {
166 netstr += ",tag=" + net.tag;
167 }
168 if (net.firewall) {
169 netstr += ",firewall=" + net.firewall;
170 }
171 }
172 if (net.rate) {
173 netstr += ",rate=" + net.rate;
174 }
175 if (net.queues) {
176 netstr += ",queues=" + net.queues;
177 }
178 if (net.disconnect) {
179 netstr += ",link_down=" + net.disconnect;
180 }
181 if (net.trunks) {
182 netstr += ",trunks=" + net.trunks;
183 }
184 return netstr;
185 },
186
187 parseQemuDrive: function(key, value) {
188 if (!(key && value)) {
189 return;
190 }
191
192 var res = {};
193
194 var match_res = key.match(/^([a-z]+)(\d+)$/);
195 if (!match_res) {
196 return;
197 }
198 res['interface'] = match_res[1];
199 res.index = match_res[2];
200
201 var errors = false;
202 Ext.Array.each(value.split(','), function(p) {
203 if (!p || p.match(/^\s*$/)) {
204 return; // continue
205 }
206 var match_res = p.match(/^([a-z_]+)=(\S+)$/);
207 if (!match_res) {
208 if (!p.match(/\=/)) {
209 res.file = p;
210 return; // continue
211 }
212 errors = true;
213 return false; // break
214 }
215 var k = match_res[1];
216 if (k === 'volume') {
217 k = 'file';
218 }
219
220 if (Ext.isDefined(res[k])) {
221 errors = true;
222 return false; // break
223 }
224
225 var v = match_res[2];
226
227 if (k === 'cache' && v === 'off') {
228 v = 'none';
229 }
230
231 res[k] = v;
232 });
233
234 if (errors || !res.file) {
235 return;
236 }
237
238 return res;
239 },
240
241 printQemuDrive: function(drive) {
242
243 var drivestr = drive.file;
244
245 Ext.Object.each(drive, function(key, value) {
246 if (!Ext.isDefined(value) || key === 'file' ||
247 key === 'index' || key === 'interface') {
248 return; // continue
249 }
250 drivestr += ',' + key + '=' + value;
251 });
252
253 return drivestr;
254 },
255
256 parseIPConfig: function(key, value) {
257 if (!(key && value)) {
258 return;
259 }
260
261 var res = {};
262
263 var errors = false;
264 Ext.Array.each(value.split(','), function(p) {
265 if (!p || p.match(/^\s*$/)) {
266 return; // continue
267 }
268
269 var match_res;
270 if ((match_res = p.match(/^ip=(\S+)$/)) !== null) {
271 res.ip = match_res[1];
272 } else if ((match_res = p.match(/^gw=(\S+)$/)) !== null) {
273 res.gw = match_res[1];
274 } else if ((match_res = p.match(/^ip6=(\S+)$/)) !== null) {
275 res.ip6 = match_res[1];
276 } else if ((match_res = p.match(/^gw6=(\S+)$/)) !== null) {
277 res.gw6 = match_res[1];
278 } else {
279 errors = true;
280 return false; // break
281 }
282 });
283
284 if (errors) {
285 return;
286 }
287
288 return res;
289 },
290
291 printIPConfig: function(cfg) {
292 var c = "";
293 var str = "";
294 if (cfg.ip) {
295 str += "ip=" + cfg.ip;
296 c = ",";
297 }
298 if (cfg.gw) {
299 str += c + "gw=" + cfg.gw;
300 c = ",";
301 }
302 if (cfg.ip6) {
303 str += c + "ip6=" + cfg.ip6;
304 c = ",";
305 }
306 if (cfg.gw6) {
307 str += c + "gw6=" + cfg.gw6;
308 c = ",";
309 }
310 return str;
311 },
312
313 parseOpenVZNetIf: function(value) {
314 if (!value) {
315 return;
316 }
317
318 var res = {};
319
320 var errors = false;
321 Ext.Array.each(value.split(';'), function(item) {
322 if (!item || item.match(/^\s*$/)) {
323 return; // continue
324 }
325
326 var data = {};
327 Ext.Array.each(item.split(','), function(p) {
328 if (!p || p.match(/^\s*$/)) {
329 return; // continue
330 }
331 var match_res = p.match(/^(ifname|mac|bridge|host_ifname|host_mac|mac_filter)=(\S+)$/);
332 if (!match_res) {
333 errors = true;
334 return false; // break
335 }
336 if (match_res[1] === 'bridge'){
337 var bridgevlanf = match_res[2];
338 var bridge_res = bridgevlanf.match(/^(vmbr(\d+))(v(\d+))?(f)?$/);
339 if (!bridge_res) {
340 errors = true;
341 return false; // break
342 }
343 data.bridge = bridge_res[1];
344 data.tag = bridge_res[4];
345 data.firewall = bridge_res[5] ? 1 : 0;
346 } else {
347 data[match_res[1]] = match_res[2];
348 }
349 });
350
351 if (errors || !data.ifname) {
352 errors = true;
353 return false; // break
354 }
355
356 data.raw = item;
357
358 res[data.ifname] = data;
359 });
360
361 return errors ? undefined: res;
362 },
363
364 printOpenVZNetIf: function(netif) {
365 var netarray = [];
366
367 Ext.Object.each(netif, function(iface, data) {
368 var tmparray = [];
369 Ext.Array.each(['ifname', 'mac', 'bridge', 'host_ifname', 'host_mac', 'mac_filter', 'tag', 'firewall'], function(key) {
370 var value = data[key];
371 if (key === 'bridge'){
372 if(data.tag){
373 value = value + 'v' + data.tag;
374 }
375 if (data.firewall){
376 value = value + 'f';
377 }
378 }
379 if (value) {
380 tmparray.push(key + '=' + value);
381 }
382
383 });
384 netarray.push(tmparray.join(','));
385 });
386
387 return netarray.join(';');
388 },
389
390 parseLxcNetwork: function(value) {
391 if (!value) {
392 return;
393 }
394
395 var data = {};
396 Ext.Array.each(value.split(','), function(p) {
397 if (!p || p.match(/^\s*$/)) {
398 return; // continue
399 }
400 var match_res = p.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|tag|rate)=(\S+)$/);
401 if (match_res) {
402 data[match_res[1]] = match_res[2];
403 } else if ((match_res = p.match(/^firewall=(\d+)$/)) !== null) {
404 data.firewall = PVE.Parser.parseBoolean(match_res[1]);
405 } else {
406 // todo: simply ignore errors ?
407 return; // continue
408 }
409 });
410
411 return data;
412 },
413
414 printLxcNetwork: function(data) {
415 var tmparray = [];
416 Ext.Array.each(['bridge', 'hwaddr', 'mtu', 'name', 'ip',
417 'gw', 'ip6', 'gw6', 'firewall', 'tag'], function(key) {
418 var value = data[key];
419 if (value) {
420 tmparray.push(key + '=' + value);
421 }
422 });
423
424 if (data.rate > 0) {
425 tmparray.push('rate=' + data.rate);
426 }
427 return tmparray.join(',');
428 },
429
430 parseLxcMountPoint: function(value) {
431 if (!value) {
432 return;
433 }
434
435 var res = {};
436
437 var errors = false;
438 Ext.Array.each(value.split(','), function(p) {
439 if (!p || p.match(/^\s*$/)) {
440 return; // continue
441 }
442 var match_res = p.match(/^([a-z_]+)=(.+)$/);
443 if (!match_res) {
444 if (!p.match(/\=/)) {
445 res.file = p;
446 return; // continue
447 }
448 errors = true;
449 return false; // break
450 }
451 var k = match_res[1];
452 if (k === 'volume') {
453 k = 'file';
454 }
455
456 if (Ext.isDefined(res[k])) {
457 errors = true;
458 return false; // break
459 }
460
461 var v = match_res[2];
462
463 res[k] = v;
464 });
465
466 if (errors || !res.file) {
467 return;
468 }
469
470 var m = res.file.match(/^([a-z][a-z0-9\-\_\.]*[a-z0-9]):/i);
471 if (m) {
472 res.storage = m[1];
473 res.type = 'volume';
474 } else if (res.file.match(/^\/dev\//)) {
475 res.type = 'device';
476 } else {
477 res.type = 'bind';
478 }
479
480 return res;
481 },
482
483 printLxcMountPoint: function(mp) {
484 var drivestr = mp.file;
485
486 Ext.Object.each(mp, function(key, value) {
487 if (!Ext.isDefined(value) || key === 'file' ||
488 key === 'type' || key === 'storage') {
489 return; // continue
490 }
491 drivestr += ',' + key + '=' + value;
492 });
493
494 return drivestr;
495 },
496
497 parseStartup: function(value) {
498 if (value === undefined) {
499 return;
500 }
501
502 var res = {};
503
504 var errors = false;
505 Ext.Array.each(value.split(','), function(p) {
506 if (!p || p.match(/^\s*$/)) {
507 return; // continue
508 }
509
510 var match_res;
511
512 if ((match_res = p.match(/^(order)?=(\d+)$/)) !== null) {
513 res.order = match_res[2];
514 } else if ((match_res = p.match(/^up=(\d+)$/)) !== null) {
515 res.up = match_res[1];
516 } else if ((match_res = p.match(/^down=(\d+)$/)) !== null) {
517 res.down = match_res[1];
518 } else {
519 errors = true;
520 return false; // break
521 }
522 });
523
524 if (errors) {
525 return;
526 }
527
528 return res;
529 },
530
531 printStartup: function(startup) {
532 var arr = [];
533 if (startup.order !== undefined && startup.order !== '') {
534 arr.push('order=' + startup.order);
535 }
536 if (startup.up !== undefined && startup.up !== '') {
537 arr.push('up=' + startup.up);
538 }
539 if (startup.down !== undefined && startup.down !== '') {
540 arr.push('down=' + startup.down);
541 }
542
543 return arr.join(',');
544 },
545
546 parseQemuSmbios1: function(value) {
547 var res = value.split(',').reduce(function (accumulator, currentValue) {
548 var splitted = currentValue.split(new RegExp("=(.+)"));
549 accumulator[splitted[0]] = splitted[1];
550 return accumulator;
551 }, {});
552
553 if (PVE.Parser.parseBoolean(res.base64, false)) {
554 Ext.Object.each(res, function(key, value) {
555 if (key === 'uuid') { return; }
556 res[key] = Ext.util.Base64.decode(value);
557 });
558 }
559
560 return res;
561 },
562
563 printQemuSmbios1: function(data) {
564
565 var datastr = '';
566 var base64 = false;
567 Ext.Object.each(data, function(key, value) {
568 if (value === '') { return; }
569 if (key === 'uuid') {
570 datastr += (datastr !== '' ? ',' : '') + key + '=' + value;
571 } else {
572 // values should be base64 encoded from now on, mark config strings correspondingly
573 if (!base64) {
574 base64 = true;
575 datastr += (datastr !== '' ? ',' : '') + 'base64=1';
576 }
577 datastr += (datastr !== '' ? ',' : '') + key + '=' + Ext.util.Base64.encode(value);
578 }
579 });
580
581 return datastr;
582 },
583
584 parseTfaConfig: function(value) {
585 var res = {};
586
587 Ext.Array.each(value.split(','), function(p) {
588 var kva = p.split('=', 2);
589 res[kva[0]] = kva[1];
590 });
591
592 return res;
593 },
594
595 parseTfaType: function(value) {
596 var match;
597 if (!value || !value.length) {
598 return undefined;
599 } else if (value === 'x!oath') {
600 return 'totp';
601 } else if (!!(match = value.match(/^x!(.+)$/))) {
602 return match[1];
603 } else {
604 return 1;
605 }
606 },
607
608 parseQemuCpu: function(value) {
609 if (!value) {
610 return {};
611 }
612
613 var res = {};
614
615 var errors = false;
616 Ext.Array.each(value.split(','), function(p) {
617 if (!p || p.match(/^\s*$/)) {
618 return; // continue
619 }
620
621 if (!p.match(/\=/)) {
622 if (Ext.isDefined(res.cpu)) {
623 errors = true;
624 return false; // break
625 }
626 res.cputype = p;
627 return; // continue
628 }
629
630 var match_res = p.match(/^([a-z_]+)=(\S+)$/);
631 if (!match_res) {
632 errors = true;
633 return false; // break
634 }
635
636 var k = match_res[1];
637 if (Ext.isDefined(res[k])) {
638 errors = true;
639 return false; // break
640 }
641
642 res[k] = match_res[2];
643 });
644
645 if (errors || !res.cputype) {
646 return;
647 }
648
649 return res;
650 },
651
652 printQemuCpu: function(cpu) {
653 var cpustr = cpu.cputype;
654 var optstr = '';
655
656 Ext.Object.each(cpu, function(key, value) {
657 if (!Ext.isDefined(value) || key === 'cputype') {
658 return; // continue
659 }
660 optstr += ',' + key + '=' + value;
661 });
662
663 if (!cpustr) {
664 if (optstr) {
665 return 'kvm64' + optstr;
666 }
667 return;
668 }
669
670 return cpustr + optstr;
671 },
672
673 parseSSHKey: function(key) {
674 // |--- options can have quotes--| type key comment
675 var keyre = /^(?:((?:[^\s"]|\"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/;
676 var typere = /^(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)$/;
677
678 var m = key.match(keyre);
679 if (!m) {
680 return null;
681 }
682 if (m.length < 3 || !m[2]) { // [2] is always either type or key
683 return null;
684 }
685 if (m[1] && m[1].match(typere)) {
686 return {
687 type: m[1],
688 key: m[2],
689 comment: m[3],
690 };
691 }
692 if (m[2].match(typere)) {
693 return {
694 options: m[1],
695 type: m[2],
696 key: m[3],
697 comment: m[4],
698 };
699 }
700 return null;
701 },
702
703 parseACMEPluginData: function(data) {
704 let res = {};
705 let extradata = [];
706 data.split('\n').forEach((line) => {
707 // capture everything after the first = as value
708 let [key, value] = line.split(/=(.+)/);
709 if (value !== undefined) {
710 res[key] = value;
711 } else {
712 extradata.push(line);
713 }
714 });
715 return [res, extradata];
716 },
717 }});