]>
git.proxmox.com Git - pve-cluster.git/blob - src/PVE/DataCenterConfig.pm
1 package PVE
:: DataCenterConfig
;
6 use PVE
:: JSONSchema
qw(get_standard_option parse_property_string register_standard_option) ;
10 # MA-L (large) assigned by IEEE
11 my $PROXMOX_OUI = 'BC:24:11' ;
16 enum
=> [ 'basic' , 'static' ],
19 description
=> "Use this resource scheduler mode for HA." ,
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." ,
24 'ha-rebalance-on-start' => {
28 description
=> "Set to use CRS for selecting a suited node when a HA services request-state"
29 . " changes from stop to start." ,
33 my $migration_format = {
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." ,
45 type
=> 'string' , format
=> 'CIDR' ,
46 format_description
=> 'CIDR' ,
47 description
=> "CIDR of the (sub) network that is used for migration."
51 my $notification_format = {
52 'package-updates' => {
54 enum
=> [ 'auto' , 'always' , 'never' ],
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 "
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 " ,
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
69 'target-package-updates' => {
71 format_description
=> 'TARGET' ,
72 description
=> "UNUSED - Use datacenter notification settings instead." ,
77 enum
=> [ 'always' , 'never' ],
78 description
=> "UNUSED - Use datacenter notification settings instead." ,
83 format_description
=> 'TARGET' ,
84 description
=> "UNUSED - Use datacenter notification settings instead." ,
89 enum
=> [ 'always' , 'never' ],
90 description
=> "UNUSED - Use datacenter notification settings instead." ,
93 'target-replication' => {
95 format_description
=> 'TARGET' ,
96 description
=> "UNUSED - Use datacenter notification settings instead." ,
101 register_standard_option
( 'pve-ha-shutdown-policy' , {
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' ,
126 shutdown_policy
=> get_standard_option
( 'pve-ha-shutdown-policy' ),
129 my $next_id_format = {
132 description
=> "Lower, inclusive boundary for free next-id API range." ,
134 max
=> 1000 * 1000 * 1000 - 1 ,
140 description
=> "Upper, exclusive boundary for free next-id API range." ,
142 max
=> 1000 * 1000 * 1000 ,
143 default => 1000 * 1000 , # lower than the maximum on purpose
151 description
=> "U2F AppId URL override. Defaults to the origin." ,
152 format_description
=> 'APPID' ,
157 description
=> "U2F Origin override. Mostly useful for single nodes with a single URL." ,
158 format_description
=> 'URL' ,
163 my $webauthn_format = {
167 'Relying party name. Any text identifier.'
168 . ' Changing this *may* break existing credentials.' ,
169 format_description
=> 'RELYING_PARTY' ,
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' ,
185 'Relying party ID. Must be the domain name without protocol, port or location.'
186 . ' Changing this *will* break existing credentials.' ,
187 format_description
=> 'DOMAINNAME' ,
190 'allow-subdomains' => {
192 description
=> 'Whether to allow the origin to be a subdomain, rather than the exact URL.' ,
198 PVE
:: JSONSchema
:: register_format
( 'mac-prefix' , \
& pve_verify_mac_prefix
);
199 sub pve_verify_mac_prefix
{
200 my ( $mac_prefix, $noerr ) = @_ ;
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 " ;
209 my $COLOR_RE = '[0-9a-fA-F]{6}' ;
210 my $TAG_COLOR_OVERRIDE_RE = "(?:${PVE::JSONSchema::PVE_TAG_RE}:${COLOR_RE}(?:\:${COLOR_RE})?)" ;
212 my $tag_style_format = {
216 enum
=> [ 'full' , 'circle' , 'dense' , 'none' ],
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." ,
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)." ,
233 enum
=> [ 'config' , 'alphabetical' ],
234 default => 'alphabetical' ,
235 description
=> 'Controls the sorting of the tags in the web-interface and the API update.' ,
237 'case-sensitive' => {
239 description
=> 'Controls if filtering for unique tags on update should check case-sensitive.' ,
245 my $user_tag_privs_format = {
249 enum
=> [ 'none' , 'list' , 'existing' , 'free' ],
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"
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 " ,
261 'user-allow-list' => {
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'." ,
271 my $datacenter_schema = {
273 additionalProperties
=> 0 ,
277 type
=> 'string' , format
=> $crs_format,
278 description
=> "Cluster resource scheduling settings." ,
283 description
=> "Default keybord layout for vnc server." ,
284 enum
=> PVE
:: Tools
:: kvmkeymaplist
(),
289 description
=> "Default GUI language." ,
297 'eu' , # Euskera (Basque)
298 'fa' , # Persian (Farsi)
306 'nb' , # Norwegian (Bokmal)
308 'nn' , # Norwegian (Nynorsk)
310 'pt_BR' , # Portuguese (Brazil)
316 'zh_CN' , # Chinese (Simplified)
317 'zh_TW' , # Chinese (Traditional)
323 description
=> "Specify external http proxy which is used for downloads (example: 'http://username:password\ @host :port/')" ,
324 pattern
=> "http://.*" ,
326 # FIXME: remove with 8.0 (add check to pve7to8!), merged into "migration" since 4.3
327 migration_unsecure
=> {
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!" ,
337 format
=> $next_id_format,
338 description
=> "Control the range for the free VMID auto-selection pool." ,
342 type
=> 'string' , format
=> $migration_format,
343 description
=> "For cluster wide migration settings." ,
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." ,
350 # FIXME: remove 'applet' with 9.0 (add pve8to9 check!)
351 enum
=> [ 'applet' , 'vv' , 'html5' , 'xtermjs' ],
356 format
=> 'email-opt' ,
357 description
=> "Specify email address to send notification from (default is root @\$hostname )" ,
363 description
=> "Defines how many workers (per node) are maximal started " .
364 " on actions like 'stopall VMs' or task from the ha-manager." ,
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\n WARNING: 'hardware' and 'both' are EXPERIMENTAL & WIP" ,
378 type
=> 'string' , format
=> $ha_format,
379 description
=> "Cluster wide HA settings." ,
384 format
=> 'mac-prefix' ,
385 default => $PROXMOX_OUI,
386 description
=> "Prefix for the auto-generated MAC addresses of virtual guests. The"
387 . " default ' $PROXMOX_OUI ' is the OUI assigned by the IEEE to Proxmox Server Solutions"
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)."
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 . " \n Note 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,"
400 . " like ` $PROXMOX_OUI :0` for the first, ` $PROXMOX_OUI :1` for the second, and so on. \n "
401 . " Alternatively, you can also separate the networks of the guests logically, e.g.,"
402 . " by using VLANs. \n\n For 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."
409 type
=> 'string' , format
=> $notification_format,
410 description
=> "Cluster-wide notification settings." ,
412 bwlimit
=> PVE
:: JSONSchema
:: get_standard_option
( 'bwlimit' ),
416 format
=> $u2f_format,
417 description
=> 'u2f' ,
422 format
=> $webauthn_format,
423 description
=> 'webauthn configuration' ,
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 ,
435 description
=> "Tag style options." ,
436 format
=> $tag_style_format,
438 'user-tag-access' => {
441 description
=> "Privilege options for user-settable tags" ,
442 format
=> $user_tag_privs_format,
444 'registered-tags' => {
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>...]" ,
455 # make schema accessible from outside (for documentation)
456 sub get_datacenter_schema
{ return $datacenter_schema };
458 sub parse_datacenter_config
{
459 my ( $filename, $raw ) = @_ ;
461 $raw = '' if ! defined ( $raw );
463 # description may be comment or key-value pair (or both)
465 for my $line ( split ( /\n/ , $raw )) {
466 if ( $line =~ /^\#(.*)$/ ) {
467 $comment .= PVE
:: Tools
:: decode_text
( $1 ) . " \n " ;
471 # parse_config ignores lines with # => use $raw
472 my $res = PVE
:: JSONSchema
:: parse_config
( $datacenter_schema, $filename, $raw );
474 $res ->{ description
} = $comment ;
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
478 $res ->{ mac_prefix
} = $PROXMOX_OUI if ! defined ( $res ->{ mac_prefix
});
480 if ( my $crs = $res ->{ crs
}) {
481 $res ->{ crs
} = parse_property_string
( $crs_format, $crs );
484 if ( my $migration = $res ->{ migration
}) {
485 $res ->{ migration
} = parse_property_string
( $migration_format, $migration );
488 if ( my $next_id = $res ->{ 'next-id' }) {
489 $res ->{ 'next-id' } = parse_property_string
( $next_id_format, $next_id );
492 if ( my $ha = $res ->{ ha
}) {
493 $res ->{ ha
} = parse_property_string
( $ha_format, $ha );
495 if ( my $notify = $res ->{ notify
}) {
496 $res ->{ notify
} = parse_property_string
( $notification_format, $notify );
499 if ( my $u2f = $res ->{ u2f
}) {
500 $res ->{ u2f
} = parse_property_string
( $u2f_format, $u2f );
503 if ( my $webauthn = $res ->{ webauthn
}) {
504 $res ->{ webauthn
} = parse_property_string
( $webauthn_format, $webauthn );
507 if ( my $tag_style = $res ->{ 'tag-style' }) {
508 $res ->{ 'tag-style' } = parse_property_string
( $tag_style_format, $tag_style );
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 );
515 if ( my $user_tags = $res ->{ 'user-tag-access' }->{ 'user-allow-list' }) {
516 $res ->{ 'user-tag-access' }->{ 'user-allow-list' } = [ split ( ';' , $user_tags )];
520 if ( my $admin_tags = $res ->{ 'registered-tags' }) {
521 $res ->{ 'registered-tags' } = [ split ( ';' , $admin_tags )];
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 " ;
530 $res ->{ migration
}->{ type
} = ( $res ->{ migration_unsecure
}) ?
'insecure' : 'secure' ;
534 # for backwards compatibility only, applet maps to html5
535 if ( defined ( $res ->{ console
}) && $res ->{ console
} eq 'applet' ) {
536 $res ->{ console
} = 'html5' ;
542 sub write_datacenter_config
{
543 my ( $filename, $cfg ) = @_ ;
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' ;
551 # map deprecated applet setting to html5
552 if ( defined ( $cfg ->{ console
}) && $cfg ->{ console
} eq 'applet' ) {
553 $cfg ->{ console
} = 'html5' ;
556 if ( defined ( $cfg ->{ mac_prefix
}) && uc ( $cfg ->{ mac_prefix
}) eq $PROXMOX_OUI ) {
557 delete $cfg ->{ mac_prefix
}; # avoid writing out default prefix
560 if ( ref ( my $crs = $cfg ->{ crs
})) {
561 $cfg ->{ crs
} = PVE
:: JSONSchema
:: print_property_string
( $crs, $crs_format );
564 if ( ref ( my $migration = $cfg ->{ migration
})) {
565 $cfg ->{ migration
} = PVE
:: JSONSchema
:: print_property_string
( $migration, $migration_format );
568 if ( defined ( my $next_id = $cfg ->{ 'next-id' })) {
569 $next_id = parse_property_string
( $next_id_format, $next_id ) if ! ref ( $next_id );
571 my $lower = int ( $next_id ->{ lower
} // $next_id_format ->{ lower
}->{ default });
572 my $upper = int ( $next_id ->{ upper
} // $next_id_format ->{ upper
}->{ default });
574 die "lower ( $lower ) <= upper ( $upper ) boundary rule broken \n " if $lower > $upper ;
576 $cfg ->{ 'next-id' } = PVE
:: JSONSchema
:: print_property_string
( $next_id, $next_id_format );
579 if ( ref ( my $ha = $cfg ->{ ha
})) {
580 $cfg ->{ ha
} = PVE
:: JSONSchema
:: print_property_string
( $ha, $ha_format );
582 if ( ref ( my $notify = $cfg ->{ notify
})) {
583 $cfg ->{ notify
} = PVE
:: JSONSchema
:: print_property_string
( $notify, $notification_format );
586 if ( ref ( my $u2f = $cfg ->{ u2f
})) {
587 $cfg ->{ u2f
} = PVE
:: JSONSchema
:: print_property_string
( $u2f, $u2f_format );
590 if ( ref ( my $webauthn = $cfg ->{ webauthn
})) {
591 $cfg ->{ webauthn
} = PVE
:: JSONSchema
:: print_property_string
( $webauthn, $webauthn_format );
594 if ( ref ( my $tag_style = $cfg ->{ 'tag-style' })) {
595 $cfg ->{ 'tag-style' } = PVE
:: JSONSchema
:: print_property_string
( $tag_style, $tag_style_format );
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 -> @* );
602 $cfg ->{ 'user-tag-access' } =
603 PVE
:: JSONSchema
:: print_property_string
( $user_tag_privs, $user_tag_privs_format );
606 if ( ref ( my $admin_tags = $cfg ->{ 'registered-tags' })) {
607 $cfg ->{ 'registered-tags' } = join ( ';' , sort $admin_tags -> @* );
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 " ;
616 delete $cfg ->{ description
}; # add only as comment, no additional key-value pair
617 my $dump = PVE
:: JSONSchema
:: dump_config
( $datacenter_schema, $filename, $cfg );
619 return $comment . " \n " . $dump ;
622 PVE
:: Cluster
:: cfs_register_file
(
624 \
& parse_datacenter_config
,
625 \
& write_datacenter_config
,