]>
git.proxmox.com Git - pve-manager.git/blob - www/manager6/Parser.js
1 // Some configuration values are complex strings - so we need parsers/generators for them.
2 Ext
.define('PVE.Parser', {
5 // this class only contains static functions
7 printACME: function(value
) {
8 if (Ext
.isArray(value
.domains
)) {
9 value
.domains
= value
.domains
.join(';');
11 return PVE
.Parser
.printPropertyString(value
);
14 parseACME: function(value
) {
21 value
.split(',').forEach(property
=> {
22 let [k
, v
] = property
.split('=', 2);
23 if (Ext
.isDefined(v
)) {
26 throw `Failed to parse key-value pair: ${property}`;
34 if (res
.domains
!== undefined) {
35 res
.domains
= res
.domains
.split(/;/);
41 parseBoolean: function(value
, default_value
) {
42 if (!Ext
.isDefined(value
)) {
45 value
= value
.toLowerCase();
46 return value
=== '1' ||
52 parsePropertyString: function(value
, defaultKey
) {
55 if (typeof value
!== 'string' || value
=== '') {
60 value
.split(',').forEach(property
=> {
61 let [k
, v
] = property
.split('=', 2);
62 if (Ext
.isDefined(v
)) {
64 } else if (Ext
.isDefined(defaultKey
)) {
65 if (Ext
.isDefined(res
[defaultKey
])) {
66 throw 'defaultKey may be only defined once in propertyString';
68 res
[defaultKey
] = k
; // k ist the value in this case
70 throw `Failed to parse key-value pair: ${property}`;
81 printPropertyString: function(data
, defaultKey
) {
83 gotDefaultKeyVal
= false,
86 Ext
.Object
.each(data
, function(key
, value
) {
87 if (defaultKey
!== undefined && key
=== defaultKey
) {
88 gotDefaultKeyVal
= true;
89 defaultKeyVal
= value
;
90 } else if (value
!== '') {
91 stringparts
.push(key
+ '=' + value
);
95 stringparts
= stringparts
.sort();
96 if (gotDefaultKeyVal
) {
97 stringparts
.unshift(defaultKeyVal
);
100 return stringparts
.join(',');
103 parseQemuNetwork: function(key
, value
) {
104 if (!(key
&& value
)) {
110 Ext
.Array
.each(value
.split(','), function(p
) {
111 if (!p
|| p
.match(/^\s*$/)) {
112 return undefined; // continue
117 if ((match_res
= p
.match(/^(ne2k_pci|e1000e?|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();
120 res
.macaddr
= match_res
[3];
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+)?|\.\d+)$/)) !== null) {
125 res
.rate
= match_res
[1];
126 } else if ((match_res
= p
.match(/^tag=(\d+(\.\d+)?)$/)) !== null) {
127 res
.tag
= match_res
[1];
128 } else if ((match_res
= p
.match(/^firewall=(\d+)$/)) !== null) {
129 res
.firewall
= match_res
[1];
130 } else if ((match_res
= p
.match(/^link_down=(\d+)$/)) !== null) {
131 res
.disconnect
= match_res
[1];
132 } else if ((match_res
= p
.match(/^queues=(\d+)$/)) !== null) {
133 res
.queues
= match_res
[1];
134 } else if ((match_res
= p
.match(/^trunks=(\d+(?:-\d+)?(?:;\d+(?:-\d+)?)*)$/)) !== null) {
135 res
.trunks
= match_res
[1];
136 } else if ((match_res
= p
.match(/^mtu=(\d+)$/)) !== null) {
137 res
.mtu
= match_res
[1];
140 return false; // break
142 return undefined; // continue
145 if (errors
|| !res
.model
) {
152 printQemuNetwork: function(net
) {
153 var netstr
= net
.model
;
155 netstr
+= "=" + net
.macaddr
;
158 netstr
+= ",bridge=" + net
.bridge
;
160 netstr
+= ",tag=" + net
.tag
;
163 netstr
+= ",firewall=" + net
.firewall
;
167 netstr
+= ",rate=" + net
.rate
;
170 netstr
+= ",queues=" + net
.queues
;
172 if (net
.disconnect
) {
173 netstr
+= ",link_down=" + net
.disconnect
;
176 netstr
+= ",trunks=" + net
.trunks
;
179 netstr
+= ",mtu=" + net
.mtu
;
184 parseQemuDrive: function(key
, value
) {
185 if (!(key
&& value
)) {
189 const [, bus
, index
] = key
.match(/^([a-z]+)(\d+)$/);
199 Ext
.Array
.each(value
.split(','), function(p
) {
200 if (!p
|| p
.match(/^\s*$/)) {
201 return undefined; // continue
203 let match
= p
.match(/^([a-z_]+)=(\S+)$/);
205 if (!p
.match(/[=]/)) {
207 return undefined; // continue
210 return false; // break
212 let [, k
, v
] = match
;
213 if (k
=== 'volume') {
217 if (Ext
.isDefined(res
[k
])) {
219 return false; // break
222 if (k
=== 'cache' && v
=== 'off') {
228 return undefined; // continue
231 if (errors
|| !res
.file
) {
238 printQemuDrive: function(drive
) {
239 var drivestr
= drive
.file
;
241 Ext
.Object
.each(drive
, function(key
, value
) {
242 if (!Ext
.isDefined(value
) || key
=== 'file' ||
243 key
=== 'index' || key
=== 'interface') {
246 drivestr
+= ',' + key
+ '=' + value
;
252 parseIPConfig: function(key
, value
) {
253 if (!(key
&& value
)) {
254 return undefined; // continue
259 value
.split(',').forEach(p
=> {
260 if (!p
|| p
.match(/^\s*$/)) {
264 const match
= p
.match(/^(ip|gw|ip6|gw6)=(\S+)$/);
266 throw `could not parse as IP config: ${p}`;
268 let [, k
, v
] = match
;
273 return undefined; // continue
279 printIPConfig: function(cfg
) {
280 return Object
.entries(cfg
)
281 .filter(([k
, v
]) => v
&& k
.match(/^(ip|gw|ip6|gw6)$/))
282 .map(([k
, v
]) => `${k}=${v}`)
286 parseLxcNetwork: function(value
) {
292 value
.split(',').forEach(p
=> {
293 if (!p
|| p
.match(/^\s*$/)) {
296 let match_res
= p
.match(/^(bridge|hwaddr|mtu|name|ip|ip6|gw|gw6|tag|rate)=(\S+)$/);
298 data
[match_res
[1]] = match_res
[2];
299 } else if ((match_res
= p
.match(/^firewall=(\d+)$/)) !== null) {
300 data
.firewall
= PVE
.Parser
.parseBoolean(match_res
[1]);
301 } else if ((match_res
= p
.match(/^link_down=(\d+)$/)) !== null) {
302 data
.link_down
= PVE
.Parser
.parseBoolean(match_res
[1]);
303 } else if (!p
.match(/^type=\S+$/)) {
304 console
.warn(`could not parse LXC network string ${p}`);
311 printLxcNetwork: function(config
) {
326 return Object
.entries(config
)
327 .filter(([k
, v
]) => v
!== undefined && v
!== '' && knownKeys
[k
])
328 .map(([k
, v
]) => `${k}=${v}`)
332 parseLxcMountPoint: function(value
) {
339 Ext
.Array
.each(value
.split(','), function(p
) {
340 if (!p
|| p
.match(/^\s*$/)) {
341 return undefined; // continue
343 let match
= p
.match(/^([a-z_]+)=(.+)$/);
345 if (!p
.match(/[=]/)) {
347 return undefined; // continue
350 return false; // break
352 let [, k
, v
] = match
;
353 if (k
=== 'volume') {
357 if (Ext
.isDefined(res
[k
])) {
359 return false; // break
367 if (errors
|| !res
.file
) {
371 const match
= res
.file
.match(/^([a-z][a-z0-9\-_.]*[a-z0-9]):/i);
373 res
.storage
= match
[1];
375 } else if (res
.file
.match(/^\/dev\//)) {
384 printLxcMountPoint: function(mp
) {
385 let drivestr
= mp
.file
;
386 for (const [key
, value
] of Object
.entries(mp
)) {
387 if (!Ext
.isDefined(value
) || key
=== 'file' || key
=== 'type' || key
=== 'storage') {
390 drivestr
+= `,${key}=${value}`;
395 parseStartup: function(value
) {
396 if (value
=== undefined) {
402 value
.split(',').forEach(p
=> {
403 if (!p
|| p
.match(/^\s*$/)) {
408 if ((match_res
= p
.match(/^(order)?=(\d+)$/)) !== null) {
409 res
.order
= match_res
[2];
410 } else if ((match_res
= p
.match(/^up=(\d+)$/)) !== null) {
411 res
.up
= match_res
[1];
412 } else if ((match_res
= p
.match(/^down=(\d+)$/)) !== null) {
413 res
.down
= match_res
[1];
415 throw `could not parse startup config ${p}`;
426 printStartup: function(startup
) {
428 if (startup
.order
!== undefined && startup
.order
!== '') {
429 arr
.push('order=' + startup
.order
);
431 if (startup
.up
!== undefined && startup
.up
!== '') {
432 arr
.push('up=' + startup
.up
);
434 if (startup
.down
!== undefined && startup
.down
!== '') {
435 arr
.push('down=' + startup
.down
);
438 return arr
.join(',');
441 parseQemuSmbios1: function(value
) {
442 let res
= value
.split(',').reduce((acc
, currentValue
) => {
443 const [k
, v
] = currentValue
.split(/[=](.+)/);
448 if (PVE
.Parser
.parseBoolean(res
.base64
, false)) {
449 for (const [k
, v
] of Object
.entries(res
)) {
451 res
[k
] = Ext
.util
.Base64
.decode(v
);
459 printQemuSmbios1: function(data
) {
461 let datastr
= Object
.entries(data
)
462 .map(([key
, value
]) => {
466 if (key
!== 'uuid') {
467 base64
= true; // smbios values can be arbitrary, so encode and mark config as such
468 value
= Ext
.util
.Base64
.encode(value
);
470 return `${key}=${value}`;
472 .filter(v
=> v
!== undefined)
476 datastr
+= ',base64=1';
481 parseTfaConfig: function(value
) {
483 value
.split(',').forEach(p
=> {
484 const [k
, v
] = p
.split('=', 2);
491 parseTfaType: function(value
) {
493 if (!value
|| !value
.length
) {
495 } else if (value
=== 'x!oath') {
497 } else if ((match
= value
.match(/^x!(.+)$/)) !== null) {
504 parseQemuCpu: function(value
) {
511 Ext
.Array
.each(value
.split(','), function(p
) {
512 if (!p
|| p
.match(/^\s*$/)) {
513 return undefined; // continue
516 if (!p
.match(/[=]/)) {
517 if (Ext
.isDefined(res
.cpu
)) {
519 return false; // break
522 return undefined; // continue
525 let match
= p
.match(/^([a-z_]+)=(\S+)$/);
526 if (!match
|| Ext
.isDefined(res
[match
[1]])) {
528 return false; // break
531 let [, k
, v
] = match
;
537 if (errors
|| !res
.cputype
) {
544 printQemuCpu: function(cpu
) {
545 let cpustr
= cpu
.cputype
;
548 Ext
.Object
.each(cpu
, function(key
, value
) {
549 if (!Ext
.isDefined(value
) || key
=== 'cputype') {
552 optstr
+= ',' + key
+ '=' + value
;
557 return 'kvm64' + optstr
;
563 return cpustr
+ optstr
;
566 parseSSHKey: function(key
) {
567 // |--- options can have quotes--| type key comment
568 let keyre
= /^(?:((?:[^\s"]|"(?:\\.|[^"\\])*")+)\s+)?(\S+)\s+(\S+)(?:\s+(.*))?$/;
569 let typere
= /^(?:(?:sk-)?(?:ssh-(?:dss|rsa|ed25519)|ecdsa-sha2-nistp\d+)(?:@(?:[a-z0-9_-]+\.)+[a-z]{2,})?)$/;
571 let m
= key
.match(keyre
);
572 if (!m
|| m
.length
< 3 || !m
[2]) { // [2] is always either type or key
575 if (m
[1] && m
[1].match(typere
)) {
582 if (m
[2].match(typere
)) {
593 parseACMEPluginData: function(data
) {
596 data
.split('\n').forEach((line
) => {
597 // capture everything after the first = as value
598 let [key
, value
] = line
.split(/[=](.+)/);
599 if (value
!== undefined) {
602 extradata
.push(line
);
605 return [res
, extradata
];
608 filterPropertyStringList: function(list
, filterFn
, defaultKey
) {
609 return list
.filter((entry
) => filterFn(PVE
.Parser
.parsePropertyString(entry
, defaultKey
)));