]>
git.proxmox.com Git - pve-manager.git/blob - PVE/NodeConfig.pm
9dc85b22349b53f83927df01c5c9c6a64e403fbd
1 package PVE
::NodeConfig
;
7 use PVE
::JSONSchema
qw(get_standard_option);
8 use PVE
::Tools
qw(file_get_contents file_set_contents lock_file);
11 # register up to 20 domain names
14 my $node_config_lock = '/var/lock/pvenode.lock';
16 PVE
::JSONSchema
::register_format
('pve-acme-domain', sub {
17 my ($domain, $noerr) = @_;
19 my $label = qr/[a-z0-9][a-z0-9_-]*/i;
21 return $domain if $domain =~ /^$label(?:\.$label)+$/;
22 return undef if $noerr;
23 die "value does not look like a valid domain name";
29 return "/etc/pve/nodes/${node}/config";
35 my $filename = config_file
($node);
36 my $raw = eval { PVE
::Tools
::file_get_contents
($filename); };
39 return parse_node_config
($raw);
43 my ($node, $conf) = @_;
45 my $filename = config_file
($node);
47 my $raw = write_node_config
($conf);
49 PVE
::Tools
::file_set_contents
($filename, $raw);
53 my ($node, $code, @param) = @_;
55 my $res = lock_file
($node_config_lock, 10, $code, @param);
65 description
=> 'Node description/comment.',
70 description
=> 'MAC address for wake on LAN',
74 'startall-onboot-delay' => {
75 description
=> 'Initial delay in seconds, before starting all the Virtual Guests with on-boot enabled.',
84 my $acme_domain_desc = {
87 format
=> 'pve-acme-domain',
88 format_description
=> 'domain',
89 description
=> 'domain for this node\'s ACME certificate',
94 format
=> 'pve-configid',
95 description
=> 'The ACME plugin ID',
96 format_description
=> 'name of the plugin configuration',
98 default => 'standalone',
102 format
=> 'pve-acme-domain',
103 format_description
=> 'domain',
104 description
=> 'Alias for the Domain to verify ACME Challenge over DNS',
110 account
=> get_standard_option
('pve-acme-account-name'),
113 format
=> 'pve-acme-domain-list',
114 format_description
=> 'domain[;domain;...]',
115 description
=> 'List of domains for this node\'s ACME certificate',
120 $confdesc->{acme
} = {
122 description
=> 'Node specific ACME settings.',
127 for my $i (0..$MAXDOMAINS) {
128 $confdesc->{"acmedomain$i"} = {
130 description
=> 'ACME domain and validation plugin',
131 format
=> $acme_domain_desc,
137 my ($key, $value) = @_;
139 die "unknown setting '$key'\n" if !$confdesc->{$key};
141 my $type = $confdesc->{$key}->{type
};
143 if (!defined($value)) {
144 die "got undefined value\n";
147 if ($value =~ m/[\n\r]/) {
148 die "property contains a line feed\n";
151 if ($type eq 'boolean') {
152 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
153 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
154 die "type check ('boolean') failed - got '$value'\n";
155 } elsif ($type eq 'integer') {
156 return int($1) if $value =~ m/^(\d+)$/;
157 die "type check ('integer') failed - got '$value'\n";
158 } elsif ($type eq 'number') {
159 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
160 die "type check ('number') failed - got '$value'\n";
161 } elsif ($type eq 'string') {
162 if (my $fmt = $confdesc->{$key}->{format
}) {
163 PVE
::JSONSchema
::check_format
($fmt, $value);
165 } elsif (my $pattern = $confdesc->{$key}->{pattern
}) {
166 if ($value !~ m/^$pattern$/) {
167 die "value does not match the regex pattern\n";
176 sub parse_node_config
{
179 return undef if !defined($content);
182 digest
=> Digest
::SHA
::sha1_hex
($content),
186 my @lines = split(/\n/, $content);
187 foreach my $line (@lines) {
188 if ($line =~ /^\#(.*)\s*$/ || $line =~ /^description:\s*(.*\S)\s*$/) {
189 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
192 if ($line =~ /^([a-z][a-z-_]*\d*):\s*(\S.*)\s*$/) {
195 $value = eval { check_type
($key, $value) };
196 die "cannot parse value of '$key' in node config: $@" if $@;
197 $conf->{$key} = $value;
199 warn "cannot parse line '$line' in node config\n";
203 $conf->{description
} = $descr if $descr;
208 sub write_node_config
{
212 # add description as comment to top of file
213 my $descr = $conf->{description
} || '';
214 foreach my $cl (split(/\n/, $descr)) {
215 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
218 for my $key (sort keys %$conf) {
219 next if ($key eq 'description');
220 next if ($key eq 'digest');
222 my $value = $conf->{$key};
223 die "detected invalid newline inside property '$key'\n"
225 $raw .= "$key: $value\n";
232 my ($node_conf, $noerr) = @_;
237 if (defined($node_conf->{acme
})) {
239 PVE
::JSONSchema
::parse_property_string
($acmedesc, $node_conf->{acme
})
242 return undef if $noerr;
245 my $standalone_domains = delete($res->{domains
}) // '';
246 for my $domain (split(";", $standalone_domains)) {
247 $res->{domains
}->{$domain}->{plugin
} = 'standalone';
251 $res->{account
} //= 'default';
253 for my $index (0..$MAXDOMAINS) {
254 my $domain_rec = $node_conf->{"acmedomain$index"};
255 next if !defined($domain_rec);
258 PVE
::JSONSchema
::parse_property_string
($acme_domain_desc, $domain_rec)
261 return undef if $noerr;
264 my $domain = delete $parsed->{domain
};
265 if ($res->{domains
}->{$domain}) {
266 return undef if $noerr;
267 die "duplicate ACME config for domain '$domain'\n";
269 $parsed->{plugin
} //= 'standalone';
270 $res->{domains
}->{$domain} = $parsed;
276 sub get_nodeconfig_schema
{