]>
git.proxmox.com Git - pve-manager.git/blob - PVE/NodeConfig.pm
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_additional_desc = {
87 format
=> 'pve-acme-domain',
88 format_description
=> 'domain',
89 description
=> 'domain for this node\'s ACME certificate',
93 format
=> 'pve-configid',
94 description
=> 'The plugin ID, default is standalone http',
95 format_description
=> 'name of the plugin configuration',
99 format
=> 'pve-acme-domain',
100 format_description
=> 'domain',
101 description
=> 'Alias for the Domain to verify ACME Challenge over DNS',
105 PVE
::JSONSchema
::register_format
('pve-acme-additional-node-conf', $acme_additional_desc);
108 account
=> get_standard_option
('pve-acme-account-name'),
111 format
=> 'pve-acme-domain-list',
112 format_description
=> 'domain[;domain;...]',
113 description
=> 'List of domains for this node\'s ACME certificate',
117 PVE
::JSONSchema
::register_format
('pve-acme-node-conf', $acmedesc);
119 $confdesc->{acme
} = {
121 description
=> 'Node specific ACME settings.',
126 for my $i (0..$MAXDOMAINS) {
127 $confdesc->{"acme_additional_domain$i"} = {
129 description
=> 'ACME additional Domain',
130 format
=> $acme_additional_desc,
136 my ($key, $value) = @_;
138 die "unknown setting '$key'\n" if !$confdesc->{$key};
140 my $type = $confdesc->{$key}->{type
};
142 if (!defined($value)) {
143 die "got undefined value\n";
146 if ($value =~ m/[\n\r]/) {
147 die "property contains a line feed\n";
150 if ($type eq 'boolean') {
151 return 1 if ($value eq '1') || ($value =~ m/^(on|yes|true)$/i);
152 return 0 if ($value eq '0') || ($value =~ m/^(off|no|false)$/i);
153 die "type check ('boolean') failed - got '$value'\n";
154 } elsif ($type eq 'integer') {
155 return int($1) if $value =~ m/^(\d+)$/;
156 die "type check ('integer') failed - got '$value'\n";
157 } elsif ($type eq 'number') {
158 return $value if $value =~ m/^(\d+)(\.\d+)?$/;
159 die "type check ('number') failed - got '$value'\n";
160 } elsif ($type eq 'string') {
161 if (my $fmt = $confdesc->{$key}->{format
}) {
162 PVE
::JSONSchema
::check_format
($fmt, $value);
164 } elsif (my $pattern = $confdesc->{$key}->{pattern
}) {
165 if ($value !~ m/^$pattern$/) {
166 die "value does not match the regex pattern\n";
175 sub parse_node_config
{
178 return undef if !defined($content);
181 digest
=> Digest
::SHA
::sha1_hex
($content),
185 my @lines = split(/\n/, $content);
186 foreach my $line (@lines) {
187 if ($line =~ /^\#(.*)\s*$/ || $line =~ /^description:\s*(.*\S)\s*$/) {
188 $descr .= PVE
::Tools
::decode_text
($1) . "\n";
191 if ($line =~ /^([a-z][a-z-_]*\d*):\s*(\S.*)\s*$/) {
194 eval { $value = check_type
($key, $value); };
195 warn "cannot parse value of '$key' in node config: $@" if $@;
196 $conf->{$key} = $value;
198 warn "cannot parse line '$line' in node config\n";
202 $conf->{description
} = $descr if $descr;
207 sub write_node_config
{
211 # add description as comment to top of file
212 my $descr = $conf->{description
} || '';
213 foreach my $cl (split(/\n/, $descr)) {
214 $raw .= '#' . PVE
::Tools
::encode_text
($cl) . "\n";
217 for my $key (sort keys %$conf) {
218 next if ($key eq 'description');
219 next if ($key eq 'digest');
221 my $value = $conf->{$key};
222 die "detected invalid newline inside property '$key'\n"
224 $raw .= "$key: $value\n";
231 my ($data, $noerr) = @_;
237 if (defined($data->{acme
})) {
239 PVE
::JSONSchema
::parse_property_string
($acmedesc, $data->{acme
});
242 return undef if $noerr;
246 $res->{0}->{account
} = $res->{0}->{account
} // "default";
249 for my $index (0..$MAXDOMAINS) {
250 my $domain_rec = $data->{"acme_additional_domain$index"};
251 next if !defined($domain_rec);
253 # index = 0 is used by acme see above
254 $res->{($index+1)} = eval {
255 PVE
::JSONSchema
::parse_property_string
(
256 $acme_additional_desc,
260 return undef if $noerr;
263 push @$domainlist, $res->{($index+1)}->{domain
};
266 # If additional domain are used it is not allowed
267 # to have a domain(list) at acme entry
268 my @domains = split(";", $res->{0}->{domains
})
269 if $res->{0}->{domains
};
270 die "Mutual exclusion of setting domains in acme and additional domains\n"
271 if (0 < @domains && defined(@$domainlist[0]));
273 $res->{"domains"} = @domains ? \
@domains : $domainlist;
278 sub get_nodeconfig_schema
{