]>
Commit | Line | Data |
---|---|---|
ab966729 FG |
1 | package PVE::DataCenterConfig; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
731f4e15 | 6 | use PVE::JSONSchema qw(get_standard_option parse_property_string register_standard_option); |
ab966729 FG |
7 | use PVE::Tools; |
8 | use PVE::Cluster; | |
9 | ||
e6c91e00 TL |
10 | # MA-L (large) assigned by IEEE |
11 | my $PROXMOX_OUI = 'BC:24:11'; | |
12 | ||
b466e489 FE |
13 | my $crs_format = { |
14 | ha => { | |
15 | type => 'string', | |
16 | enum => ['basic', 'static'], | |
c008170e | 17 | optional => 1, |
b466e489 | 18 | default => 'basic', |
c008170e | 19 | description => "Use this resource scheduler mode for HA.", |
b466e489 FE |
20 | verbose_description => "Configures how the HA manager should select nodes to start or ". |
21 | "recover services. With 'basic', only the number of services is used, with 'static', ". | |
22 | "static CPU and memory configuration of services is considered.", | |
23 | }, | |
1a75f4db TL |
24 | 'ha-rebalance-on-start' => { |
25 | type => 'boolean', | |
26 | optional => 1, | |
27 | default => 0, | |
28 | description => "Set to use CRS for selecting a suited node when a HA services request-state" | |
29 | ." changes from stop to start.", | |
30 | } | |
b466e489 FE |
31 | }; |
32 | ||
ab966729 FG |
33 | my $migration_format = { |
34 | type => { | |
35 | default_key => 1, | |
36 | type => 'string', | |
37 | enum => ['secure', 'insecure'], | |
38 | description => "Migration traffic is encrypted using an SSH tunnel by " . | |
39 | "default. On secure, completely private networks this can be " . | |
40 | "disabled to increase performance.", | |
41 | default => 'secure', | |
42 | }, | |
43 | network => { | |
44 | optional => 1, | |
45 | type => 'string', format => 'CIDR', | |
46 | format_description => 'CIDR', | |
47 | description => "CIDR of the (sub) network that is used for migration." | |
48 | }, | |
49 | }; | |
50 | ||
63e5cd09 TL |
51 | my $notification_format = { |
52 | 'package-updates' => { | |
53 | type => 'string', | |
54 | enum => ['auto', 'always', 'never'], | |
996e1e7a LW |
55 | description => "DEPRECATED: Use datacenter notification settings instead." |
56 | ." Control when the daily update job should send out notifications.", | |
57 | verbose_description => "DEPRECATED: Use datacenter notification settings instead.\n" | |
58 | ."Control how often the daily update job should send out notifications:\n" | |
63e5cd09 TL |
59 | ."* 'auto' daily for systems with a valid subscription, as those are assumed to be " |
60 | ." production-ready and thus should know about pending updates.\n" | |
61 | ."* 'always' every update, if there are new pending updates.\n" | |
62 | ."* 'never' never send a notification for new pending updates.\n", | |
63 | default => 'auto', | |
d91e09cf LW |
64 | optional => 1, |
65 | }, | |
996e1e7a LW |
66 | # TODO: These are the left-overs of the first version of the overhauled notification system. |
67 | # It was already available on pvetest, thus we should keep these entries here for a while to | |
68 | # avoid any breakage. | |
d91e09cf LW |
69 | 'target-package-updates' => { |
70 | type => 'string', | |
71 | format_description => 'TARGET', | |
996e1e7a | 72 | description => "UNUSED - Use datacenter notification settings instead.", |
d91e09cf LW |
73 | optional => 1, |
74 | }, | |
75 | 'fencing' => { | |
76 | type => 'string', | |
77 | enum => ['always', 'never'], | |
996e1e7a | 78 | description => "UNUSED - Use datacenter notification settings instead.", |
d91e09cf LW |
79 | optional => 1, |
80 | }, | |
81 | 'target-fencing' => { | |
82 | type => 'string', | |
83 | format_description => 'TARGET', | |
996e1e7a | 84 | description => "UNUSED - Use datacenter notification settings instead.", |
d91e09cf LW |
85 | optional => 1, |
86 | }, | |
87 | 'replication' => { | |
88 | type => 'string', | |
89 | enum => ['always', 'never'], | |
996e1e7a | 90 | description => "UNUSED - Use datacenter notification settings instead.", |
d91e09cf LW |
91 | optional => 1, |
92 | }, | |
93 | 'target-replication' => { | |
94 | type => 'string', | |
95 | format_description => 'TARGET', | |
996e1e7a | 96 | description => "UNUSED - Use datacenter notification settings instead.", |
d91e09cf | 97 | optional => 1, |
63e5cd09 TL |
98 | }, |
99 | }; | |
100 | ||
731f4e15 FE |
101 | register_standard_option('pve-ha-shutdown-policy', { |
102 | type => 'string', | |
103 | enum => ['freeze', 'failover', 'conditional', 'migrate'], | |
104 | description => "The policy for HA services on node shutdown. 'freeze' disables ". | |
105 | "auto-recovery, 'failover' ensures recovery, 'conditional' recovers on ". | |
106 | "poweroff and freezes on reboot. 'migrate' will migrate running services ". | |
107 | "to other nodes, if possible. With 'freeze' or 'failover', HA Services will ". | |
108 | "always get stopped first on shutdown.", | |
109 | verbose_description => "Describes the policy for handling HA services on poweroff ". | |
110 | "or reboot of a node. Freeze will always freeze services which are still located ". | |
111 | "on the node on shutdown, those services won't be recovered by the HA manager. ". | |
112 | "Failover will not mark the services as frozen and thus the services will get ". | |
113 | "recovered to other nodes, if the shutdown node does not come up again quickly ". | |
114 | "(< 1min). 'conditional' chooses automatically depending on the type of shutdown, ". | |
115 | "i.e., on a reboot the service will be frozen but on a poweroff the service will ". | |
116 | "stay as is, and thus get recovered after about 2 minutes. ". | |
117 | "Migrate will try to move all running services to another node when a reboot or ". | |
118 | "shutdown was triggered. The poweroff process will only continue once no running services ". | |
119 | "are located on the node anymore. If the node comes up again, the service will ". | |
120 | "be moved back to the previously powered-off node, at least if no other migration, ". | |
121 | "reloaction or recovery took place.", | |
122 | default => 'conditional', | |
123 | }); | |
124 | ||
ab966729 | 125 | my $ha_format = { |
731f4e15 | 126 | shutdown_policy => get_standard_option('pve-ha-shutdown-policy'), |
ab966729 FG |
127 | }; |
128 | ||
75e5d02e TL |
129 | my $next_id_format = { |
130 | lower => { | |
131 | type => 'integer', | |
132 | description => "Lower, inclusive boundary for free next-id API range.", | |
133 | min => 100, | |
134 | max => 1000 * 1000 * 1000 - 1, | |
135 | default => 100, | |
136 | optional => 1, | |
137 | }, | |
138 | upper => { | |
139 | type => 'integer', | |
8ca3c4bb | 140 | description => "Upper, exclusive boundary for free next-id API range.", |
75e5d02e | 141 | min => 100, |
8ca3c4bb | 142 | max => 1000 * 1000 * 1000, |
75e5d02e TL |
143 | default => 1000 * 1000, # lower than the maximum on purpose |
144 | optional => 1, | |
145 | }, | |
146 | }; | |
147 | ||
bcfa5ac1 | 148 | my $u2f_format = { |
ab966729 FG |
149 | appid => { |
150 | type => 'string', | |
151 | description => "U2F AppId URL override. Defaults to the origin.", | |
152 | format_description => 'APPID', | |
153 | optional => 1, | |
154 | }, | |
155 | origin => { | |
156 | type => 'string', | |
157 | description => "U2F Origin override. Mostly useful for single nodes with a single URL.", | |
158 | format_description => 'URL', | |
159 | optional => 1, | |
160 | }, | |
161 | }; | |
162 | ||
8545a705 WB |
163 | my $webauthn_format = { |
164 | rp => { | |
165 | type => 'string', | |
166 | description => | |
167 | 'Relying party name. Any text identifier.' | |
168 | .' Changing this *may* break existing credentials.', | |
169 | format_description => 'RELYING_PARTY', | |
170 | optional => 1, | |
171 | }, | |
172 | origin => { | |
173 | type => 'string', | |
174 | description => | |
175 | 'Site origin. Must be a `https://` URL (or `http://localhost`).' | |
176 | .' Should contain the address users type in their browsers to access' | |
177 | .' the web interface.' | |
178 | .' Changing this *may* break existing credentials.', | |
179 | format_description => 'URL', | |
180 | optional => 1, | |
181 | }, | |
182 | id => { | |
183 | type => 'string', | |
184 | description => | |
3b0214b6 | 185 | 'Relying party ID. Must be the domain name without protocol, port or location.' |
8545a705 WB |
186 | .' Changing this *will* break existing credentials.', |
187 | format_description => 'DOMAINNAME', | |
188 | optional => 1, | |
189 | }, | |
3b0214b6 WB |
190 | 'allow-subdomains' => { |
191 | type => 'boolean', | |
192 | description => 'Whether to allow the origin to be a subdomain, rather than the exact URL.', | |
193 | optional => 1, | |
194 | default => 1, | |
195 | }, | |
8545a705 | 196 | }; |
ab966729 FG |
197 | |
198 | PVE::JSONSchema::register_format('mac-prefix', \&pve_verify_mac_prefix); | |
199 | sub pve_verify_mac_prefix { | |
200 | my ($mac_prefix, $noerr) = @_; | |
201 | ||
202 | if ($mac_prefix !~ m/^[a-f0-9][02468ace](?::[a-f0-9]{2}){0,2}:?$/i) { | |
203 | return undef if $noerr; | |
204 | die "value is not a valid unicast MAC address prefix\n"; | |
205 | } | |
206 | return $mac_prefix; | |
207 | } | |
208 | ||
af234c4b DC |
209 | my $COLOR_RE = '[0-9a-fA-F]{6}'; |
210 | my $TAG_COLOR_OVERRIDE_RE = "(?:${PVE::JSONSchema::PVE_TAG_RE}:${COLOR_RE}(?:\:${COLOR_RE})?)"; | |
211 | ||
212 | my $tag_style_format = { | |
213 | 'shape' => { | |
214 | optional => 1, | |
215 | type => 'string', | |
216 | enum => ['full', 'circle', 'dense', 'none'], | |
217 | default => 'circle', | |
218 | description => "Tag shape for the web ui tree. 'full' draws the full tag. " | |
219 | ."'circle' draws only a circle with the background color. " | |
220 | ."'dense' only draws a small rectancle (useful when many tags are assigned to each guest)." | |
221 | ."'none' disables showing the tags.", | |
222 | }, | |
223 | 'color-map' => { | |
224 | optional => 1, | |
225 | type => 'string', | |
226 | pattern => "${TAG_COLOR_OVERRIDE_RE}(?:\;$TAG_COLOR_OVERRIDE_RE)*", | |
227 | typetext => '<tag>:<hex-color>[:<hex-color-for-text>][;<tag>=...]', | |
228 | description => "Manual color mapping for tags (semicolon separated).", | |
229 | }, | |
8456e52e DC |
230 | ordering => { |
231 | optional => 1, | |
232 | type => 'string', | |
233 | enum => ['config', 'alphabetical'], | |
234 | default => 'alphabetical', | |
2453d13f TL |
235 | description => 'Controls the sorting of the tags in the web-interface and the API update.', |
236 | }, | |
237 | 'case-sensitive' => { | |
238 | type => 'boolean', | |
239 | description => 'Controls if filtering for unique tags on update should check case-sensitive.', | |
240 | optional => 1, | |
241 | default => 0, | |
242 | }, | |
af234c4b DC |
243 | }; |
244 | ||
c17e397b DC |
245 | my $user_tag_privs_format = { |
246 | 'user-allow' => { | |
247 | optional => 1, | |
248 | type => 'string', | |
249 | enum => ['none', 'list', 'existing', 'free'], | |
250 | default => 'free', | |
c274026b TL |
251 | description => "Controls tag usage for users without `Sys.Modify` on `/` by either" |
252 | ." allowing `none`, a `list`, already `existing` or anything (`free`).", | |
253 | verbose_description => "Controls which tags can be set or deleted on resources a user" | |
254 | ." controls (such as guests). Users with the `Sys.Modify` privilege on `/` are always" | |
255 | ."unrestricted.\n" | |
256 | ."* 'none' no tags are usable.\n" | |
257 | ."* 'list' tags from 'user-allow-list' are usable.\n" | |
258 | ."* 'existing' like list, but already existing tags of resources are also usable.\n" | |
259 | ."* 'free' no tag restrictions.\n", | |
c17e397b DC |
260 | }, |
261 | 'user-allow-list' => { | |
262 | optional => 1, | |
263 | type => 'string', | |
264 | pattern => "${PVE::JSONSchema::PVE_TAG_RE}(?:\;${PVE::JSONSchema::PVE_TAG_RE})*", | |
265 | typetext => "<tag>[;<tag>...]", | |
266 | description => "List of tags users are allowed to set and delete (semicolon separated) " | |
267 | ."for 'user-allow' values 'list' and 'existing'.", | |
268 | }, | |
269 | }; | |
270 | ||
ab966729 FG |
271 | my $datacenter_schema = { |
272 | type => "object", | |
273 | additionalProperties => 0, | |
274 | properties => { | |
b466e489 FE |
275 | crs => { |
276 | optional => 1, | |
277 | type => 'string', format => $crs_format, | |
278 | description => "Cluster resource scheduling settings.", | |
279 | }, | |
ab966729 FG |
280 | keyboard => { |
281 | optional => 1, | |
282 | type => 'string', | |
283 | description => "Default keybord layout for vnc server.", | |
284 | enum => PVE::Tools::kvmkeymaplist(), | |
285 | }, | |
286 | language => { | |
287 | optional => 1, | |
288 | type => 'string', | |
289 | description => "Default GUI language.", | |
290 | enum => [ | |
a21d7e22 TL |
291 | 'ar', # Arabic |
292 | 'ca', # Catalan | |
293 | 'da', # Danish | |
294 | 'de', # German | |
295 | 'en', # English | |
296 | 'es', # Spanish | |
297 | 'eu', # Euskera (Basque) | |
298 | 'fa', # Persian (Farsi) | |
299 | 'fr', # French | |
300 | 'hr', # Croatian | |
301 | 'he', # Hebrew | |
302 | 'it', # Italian | |
303 | 'ja', # Japanese | |
304 | 'ka', # Georgian | |
305 | 'kr', # Korean | |
306 | 'nb', # Norwegian (Bokmal) | |
307 | 'nl', # Dutch | |
308 | 'nn', # Norwegian (Nynorsk) | |
309 | 'pl', # Polish | |
310 | 'pt_BR', # Portuguese (Brazil) | |
311 | 'ru', # Russian | |
312 | 'sl', # Slovenian | |
313 | 'sv', # Swedish | |
314 | 'tr', # Turkish | |
315 | 'ukr', # Ukrainian | |
316 | 'zh_CN', # Chinese (Simplified) | |
317 | 'zh_TW', # Chinese (Traditional) | |
ab966729 FG |
318 | ], |
319 | }, | |
320 | http_proxy => { | |
321 | optional => 1, | |
322 | type => 'string', | |
323 | description => "Specify external http proxy which is used for downloads (example: 'http://username:password\@host:port/')", | |
324 | pattern => "http://.*", | |
325 | }, | |
0cc6e737 | 326 | # FIXME: remove with 8.0 (add check to pve7to8!), merged into "migration" since 4.3 |
ab966729 FG |
327 | migration_unsecure => { |
328 | optional => 1, | |
329 | type => 'boolean', | |
330 | description => "Migration is secure using SSH tunnel by default. " . | |
331 | "For secure private networks you can disable it to speed up " . | |
332 | "migration. Deprecated, use the 'migration' property instead!", | |
333 | }, | |
75e5d02e TL |
334 | 'next-id' => { |
335 | optional => 1, | |
336 | type => 'string', | |
337 | format => $next_id_format, | |
338 | description => "Control the range for the free VMID auto-selection pool.", | |
339 | }, | |
ab966729 FG |
340 | migration => { |
341 | optional => 1, | |
342 | type => 'string', format => $migration_format, | |
343 | description => "For cluster wide migration settings.", | |
344 | }, | |
345 | console => { | |
346 | optional => 1, | |
347 | type => 'string', | |
0cc6e737 TL |
348 | description => "Select the default Console viewer. You can either use the builtin java" |
349 | ." applet (VNC; deprecated and maps to html5), an external virt-viewer comtatible application (SPICE), an HTML5 based vnc viewer (noVNC), or an HTML5 based console client (xtermjs). If the selected viewer is not available (e.g. SPICE not activated for the VM), the fallback is noVNC.", | |
044f5dc6 | 350 | # FIXME: remove 'applet' with 9.0 (add pve8to9 check!) |
ab966729 FG |
351 | enum => ['applet', 'vv', 'html5', 'xtermjs'], |
352 | }, | |
353 | email_from => { | |
354 | optional => 1, | |
355 | type => 'string', | |
356 | format => 'email-opt', | |
357 | description => "Specify email address to send notification from (default is root@\$hostname)", | |
358 | }, | |
359 | max_workers => { | |
360 | optional => 1, | |
361 | type => 'integer', | |
362 | minimum => 1, | |
363 | description => "Defines how many workers (per node) are maximal started ". | |
364 | " on actions like 'stopall VMs' or task from the ha-manager.", | |
365 | }, | |
366 | fencing => { | |
367 | optional => 1, | |
368 | type => 'string', | |
369 | default => 'watchdog', | |
370 | enum => [ 'watchdog', 'hardware', 'both' ], | |
371 | description => "Set the fencing mode of the HA cluster. Hardware mode " . | |
372 | "needs a valid configuration of fence devices in /etc/pve/ha/fence.cfg." . | |
373 | " With both all two modes are used." . | |
374 | "\n\nWARNING: 'hardware' and 'both' are EXPERIMENTAL & WIP", | |
375 | }, | |
376 | ha => { | |
377 | optional => 1, | |
378 | type => 'string', format => $ha_format, | |
379 | description => "Cluster wide HA settings.", | |
380 | }, | |
381 | mac_prefix => { | |
382 | optional => 1, | |
383 | type => 'string', | |
384 | format => 'mac-prefix', | |
e6c91e00 | 385 | default => $PROXMOX_OUI, |
f40a6d9c | 386 | description => "Prefix for the auto-generated MAC addresses of virtual guests. The" |
e6c91e00 | 387 | ." default '$PROXMOX_OUI' is the OUI assigned by the IEEE to Proxmox Server Solutions" |
f40a6d9c TL |
388 | ." GmbH for a 24-bit large MAC block. You're allowed to use this in local networks," |
389 | ." i.e., those not directly reachable by the public (e.g., in a LAN or behind NAT)." | |
390 | , | |
391 | verbose_description => "Prefix for the auto-generated MAC addresses of virtual guests." | |
392 | ." The default `BC:24:11` is the Organizationally Unique Identifier (OUI) assigned" | |
393 | ." by the IEEE to Proxmox Server Solutions GmbH for a MAC Address Block Large (MA-L)." | |
394 | ." You're allowed to use this in local networks, i.e., those not directly reachable" | |
395 | ." by the public (e.g., in a LAN or NAT/Masquerading).\n" | |
396 | ." \nNote that when you run multiple cluster that (partially) share the networks of" | |
397 | ." their virtual guests, it's highly recommended that you extend the default MAC" | |
398 | ." prefix, or generate a custom (valid) one, to reduce the chance of MAC collisions." | |
399 | ." For example, add a separate extra hexadecimal to the Proxmox OUI for each cluster," | |
e6c91e00 | 400 | ." like `$PROXMOX_OUI:0` for the first, `$PROXMOX_OUI:1` for the second, and so on.\n" |
f40a6d9c TL |
401 | ." Alternatively, you can also separate the networks of the guests logically, e.g.," |
402 | ." by using VLANs.\n\nFor publicly accessible guests it's recommended that you get" | |
403 | ." your own https://standards.ieee.org/products-programs/regauth/[OUI from the IEEE]" | |
404 | ." registered or coordinate with your, or your hosting providers, network admins." | |
405 | , | |
ab966729 | 406 | }, |
63e5cd09 TL |
407 | notify => { |
408 | optional => 1, | |
409 | type => 'string', format => $notification_format, | |
410 | description => "Cluster-wide notification settings.", | |
411 | }, | |
ab966729 FG |
412 | bwlimit => PVE::JSONSchema::get_standard_option('bwlimit'), |
413 | u2f => { | |
414 | optional => 1, | |
415 | type => 'string', | |
416 | format => $u2f_format, | |
417 | description => 'u2f', | |
418 | }, | |
8545a705 WB |
419 | webauthn => { |
420 | optional => 1, | |
421 | type => 'string', | |
422 | format => $webauthn_format, | |
423 | description => 'webauthn configuration', | |
424 | }, | |
2ae1c0bb DJ |
425 | description => { |
426 | type => 'string', | |
427 | description => "Datacenter description. Shown in the web-interface datacenter notes panel." | |
428 | ." This is saved as comment inside the configuration file.", | |
429 | maxLength => 64 * 1024, | |
430 | optional => 1, | |
431 | }, | |
af234c4b DC |
432 | 'tag-style' => { |
433 | optional => 1, | |
434 | type => 'string', | |
435 | description => "Tag style options.", | |
436 | format => $tag_style_format, | |
437 | }, | |
c17e397b DC |
438 | 'user-tag-access' => { |
439 | optional => 1, | |
440 | type => 'string', | |
441 | description => "Privilege options for user-settable tags", | |
442 | format => $user_tag_privs_format, | |
443 | }, | |
444 | 'registered-tags' => { | |
445 | optional => 1, | |
446 | type => 'string', | |
447 | description => "A list of tags that require a `Sys.Modify` on '/' to set and delete. " | |
448 | ."Tags set here that are also in 'user-tag-access' also require `Sys.Modify`.", | |
449 | pattern => "(?:${PVE::JSONSchema::PVE_TAG_RE};)*${PVE::JSONSchema::PVE_TAG_RE}", | |
450 | typetext => "<tag>[;<tag>...]", | |
451 | }, | |
ab966729 FG |
452 | }, |
453 | }; | |
454 | ||
455 | # make schema accessible from outside (for documentation) | |
456 | sub get_datacenter_schema { return $datacenter_schema }; | |
457 | ||
458 | sub parse_datacenter_config { | |
459 | my ($filename, $raw) = @_; | |
460 | ||
b5e2b244 TL |
461 | $raw = '' if !defined($raw); |
462 | ||
2ae1c0bb DJ |
463 | # description may be comment or key-value pair (or both) |
464 | my $comment = ''; | |
459f6084 | 465 | for my $line (split(/\n/, $raw)) { |
09b99550 | 466 | if ($line =~ /^\#(.*)$/) { |
2ae1c0bb DJ |
467 | $comment .= PVE::Tools::decode_text($1) . "\n"; |
468 | } | |
469 | } | |
470 | ||
471 | # parse_config ignores lines with # => use $raw | |
b5e2b244 | 472 | my $res = PVE::JSONSchema::parse_config($datacenter_schema, $filename, $raw); |
ab966729 | 473 | |
2ae1c0bb DJ |
474 | $res->{description} = $comment; |
475 | ||
f40a6d9c TL |
476 | # it could be better to track that this is the default, and not explicitly set, but having |
477 | # no MAC prefix is really not ideal, and overriding that here centrally catches all call sites | |
e6c91e00 | 478 | $res->{mac_prefix} = $PROXMOX_OUI if !defined($res->{mac_prefix}); |
f40a6d9c | 479 | |
b466e489 FE |
480 | if (my $crs = $res->{crs}) { |
481 | $res->{crs} = parse_property_string($crs_format, $crs); | |
482 | } | |
483 | ||
ab966729 | 484 | if (my $migration = $res->{migration}) { |
51866274 | 485 | $res->{migration} = parse_property_string($migration_format, $migration); |
ab966729 FG |
486 | } |
487 | ||
75e5d02e TL |
488 | if (my $next_id = $res->{'next-id'}) { |
489 | $res->{'next-id'} = parse_property_string($next_id_format, $next_id); | |
490 | } | |
491 | ||
ab966729 | 492 | if (my $ha = $res->{ha}) { |
51866274 | 493 | $res->{ha} = parse_property_string($ha_format, $ha); |
ab966729 | 494 | } |
63e5cd09 TL |
495 | if (my $notify = $res->{notify}) { |
496 | $res->{notify} = parse_property_string($notification_format, $notify); | |
497 | } | |
ab966729 | 498 | |
bcfa5ac1 | 499 | if (my $u2f = $res->{u2f}) { |
51866274 | 500 | $res->{u2f} = parse_property_string($u2f_format, $u2f); |
bcfa5ac1 FG |
501 | } |
502 | ||
8545a705 | 503 | if (my $webauthn = $res->{webauthn}) { |
51866274 | 504 | $res->{webauthn} = parse_property_string($webauthn_format, $webauthn); |
8545a705 WB |
505 | } |
506 | ||
af234c4b DC |
507 | if (my $tag_style = $res->{'tag-style'}) { |
508 | $res->{'tag-style'} = parse_property_string($tag_style_format, $tag_style); | |
509 | } | |
510 | ||
c17e397b DC |
511 | if (my $user_tag_privs = $res->{'user-tag-access'}) { |
512 | $res->{'user-tag-access'} = | |
513 | parse_property_string($user_tag_privs_format, $user_tag_privs); | |
514 | ||
515 | if (my $user_tags = $res->{'user-tag-access'}->{'user-allow-list'}) { | |
516 | $res->{'user-tag-access'}->{'user-allow-list'} = [split(';', $user_tags)]; | |
517 | } | |
518 | } | |
519 | ||
520 | if (my $admin_tags = $res->{'registered-tags'}) { | |
521 | $res->{'registered-tags'} = [split(';', $admin_tags)]; | |
522 | } | |
523 | ||
ab966729 FG |
524 | # for backwards compatibility only, new migration property has precedence |
525 | if (defined($res->{migration_unsecure})) { | |
526 | if (defined($res->{migration}->{type})) { | |
527 | warn "deprecated setting 'migration_unsecure' and new 'migration: type' " . | |
528 | "set at same time! Ignore 'migration_unsecure'\n"; | |
529 | } else { | |
530 | $res->{migration}->{type} = ($res->{migration_unsecure}) ? 'insecure' : 'secure'; | |
531 | } | |
532 | } | |
533 | ||
534 | # for backwards compatibility only, applet maps to html5 | |
535 | if (defined($res->{console}) && $res->{console} eq 'applet') { | |
536 | $res->{console} = 'html5'; | |
537 | } | |
538 | ||
539 | return $res; | |
540 | } | |
541 | ||
542 | sub write_datacenter_config { | |
543 | my ($filename, $cfg) = @_; | |
544 | ||
545 | # map deprecated setting to new one | |
546 | if (defined($cfg->{migration_unsecure}) && !defined($cfg->{migration})) { | |
547 | my $migration_unsecure = delete $cfg->{migration_unsecure}; | |
548 | $cfg->{migration}->{type} = ($migration_unsecure) ? 'insecure' : 'secure'; | |
549 | } | |
550 | ||
551 | # map deprecated applet setting to html5 | |
552 | if (defined($cfg->{console}) && $cfg->{console} eq 'applet') { | |
553 | $cfg->{console} = 'html5'; | |
554 | } | |
555 | ||
e6c91e00 TL |
556 | if (defined($cfg->{mac_prefix}) && uc($cfg->{mac_prefix}) eq $PROXMOX_OUI) { |
557 | delete $cfg->{mac_prefix}; # avoid writing out default prefix | |
558 | } | |
559 | ||
b466e489 FE |
560 | if (ref(my $crs = $cfg->{crs})) { |
561 | $cfg->{crs} = PVE::JSONSchema::print_property_string($crs, $crs_format); | |
562 | } | |
563 | ||
5b576e26 | 564 | if (ref(my $migration = $cfg->{migration})) { |
ab966729 FG |
565 | $cfg->{migration} = PVE::JSONSchema::print_property_string($migration, $migration_format); |
566 | } | |
567 | ||
75e5d02e TL |
568 | if (defined(my $next_id = $cfg->{'next-id'})) { |
569 | $next_id = parse_property_string($next_id_format, $next_id) if !ref($next_id); | |
570 | ||
571 | my $lower = int($next_id->{lower} // $next_id_format->{lower}->{default}); | |
572 | my $upper = int($next_id->{upper} // $next_id_format->{upper}->{default}); | |
573 | ||
fe2bc758 | 574 | die "lower ($lower) <= upper ($upper) boundary rule broken\n" if $lower > $upper; |
75e5d02e TL |
575 | |
576 | $cfg->{'next-id'} = PVE::JSONSchema::print_property_string($next_id, $next_id_format); | |
577 | } | |
578 | ||
5b576e26 | 579 | if (ref(my $ha = $cfg->{ha})) { |
ab966729 FG |
580 | $cfg->{ha} = PVE::JSONSchema::print_property_string($ha, $ha_format); |
581 | } | |
63e5cd09 TL |
582 | if (ref(my $notify = $cfg->{notify})) { |
583 | $cfg->{notify} = PVE::JSONSchema::print_property_string($notify, $notification_format); | |
584 | } | |
ab966729 | 585 | |
5b576e26 | 586 | if (ref(my $u2f = $cfg->{u2f})) { |
bcfa5ac1 FG |
587 | $cfg->{u2f} = PVE::JSONSchema::print_property_string($u2f, $u2f_format); |
588 | } | |
589 | ||
5b576e26 | 590 | if (ref(my $webauthn = $cfg->{webauthn})) { |
8545a705 WB |
591 | $cfg->{webauthn} = PVE::JSONSchema::print_property_string($webauthn, $webauthn_format); |
592 | } | |
593 | ||
af234c4b DC |
594 | if (ref(my $tag_style = $cfg->{'tag-style'})) { |
595 | $cfg->{'tag-style'} = PVE::JSONSchema::print_property_string($tag_style, $tag_style_format); | |
596 | } | |
597 | ||
c17e397b DC |
598 | if (ref(my $user_tag_privs = $cfg->{'user-tag-access'})) { |
599 | if (my $user_tags = $user_tag_privs->{'user-allow-list'}) { | |
600 | $user_tag_privs->{'user-allow-list'} = join(';', sort $user_tags->@*); | |
601 | } | |
602 | $cfg->{'user-tag-access'} = | |
603 | PVE::JSONSchema::print_property_string($user_tag_privs, $user_tag_privs_format); | |
604 | } | |
605 | ||
606 | if (ref(my $admin_tags = $cfg->{'registered-tags'})) { | |
607 | $cfg->{'registered-tags'} = join(';', sort $admin_tags->@*); | |
608 | } | |
609 | ||
2ae1c0bb DJ |
610 | my $comment = ''; |
611 | # add description as comment to top of file | |
612 | my $description = $cfg->{description} || ''; | |
613 | foreach my $line (split(/\n/, $description)) { | |
614 | $comment .= '#' . PVE::Tools::encode_text($line) . "\n"; | |
615 | } | |
616 | delete $cfg->{description}; # add only as comment, no additional key-value pair | |
617 | my $dump = PVE::JSONSchema::dump_config($datacenter_schema, $filename, $cfg); | |
618 | ||
619 | return $comment . "\n" . $dump; | |
ab966729 FG |
620 | } |
621 | ||
02ce710f TL |
622 | PVE::Cluster::cfs_register_file( |
623 | 'datacenter.cfg', | |
624 | \&parse_datacenter_config, | |
625 | \&write_datacenter_config, | |
626 | ); | |
ab966729 FG |
627 | |
628 | 1; |