1 package PVE
::HA
::Config
;
9 use PVE
::Cluster
qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
10 use PVE
::HA
::Resources
;
12 my $manager_status_filename = "ha/manager_status";
13 my $ha_groups_config = "ha/groups.cfg";
14 my $ha_resources_config = "ha/resources.cfg";
15 my $crm_commands_filename = "ha/crm_commands";
16 my $ha_fence_config = "ha/fence.cfg";
18 cfs_register_file
($crm_commands_filename,
19 sub { my ($fn, $raw) = @_; return defined($raw) ?
$raw : ''; },
20 sub { my ($fn, $raw) = @_; return $raw; });
21 cfs_register_file
($ha_groups_config,
22 sub { PVE
::HA
::Groups-
>parse_config(@_); },
23 sub { PVE
::HA
::Groups-
>write_config(@_); });
24 cfs_register_file
($ha_resources_config,
25 sub { PVE
::HA
::Resources-
>parse_config(@_); },
26 sub { PVE
::HA
::Resources-
>write_config(@_); });
27 cfs_register_file
($manager_status_filename,
30 cfs_register_file
($ha_fence_config,
31 \
&PVE
::HA
::FenceConfig
::parse_config
,
32 \
&PVE
::HA
::FenceConfig
::write_config
);
35 my ($filename, $data) = @_;
37 return defined($data) && length($data) > 0 ? decode_json
($data) : {};
41 my ($filename, $data) = @_;
43 return encode_json
($data);
49 die "undefined node" if !defined($node);
51 my $cfs_path = "nodes/$node/lrm_status";
53 my $raw = PVE
::Cluster
::get_config
($cfs_path);
55 # ENOENT -> possible deleted node, don't die here as it breaks our node
57 warn "unable to read file '/etc/pve/$cfs_path'\n";
58 # unkown mode set explicitly as 'active' is assumed as default..
59 return { mode
=> 'unknown' } if ! -e
"/etc/pve/$cfs_path";
62 return json_reader
(undef, $raw);
65 sub write_lrm_status
{
66 my ($node, $status_obj) = @_;
68 die "undefined node" if !defined($node);
70 my $filename = "/etc/pve/nodes/$node/lrm_status";
72 PVE
::HA
::Tools
::write_json_to_file
($filename, $status_obj);
75 sub parse_groups_config
{
76 my ($filename, $raw) = @_;
78 return PVE
::HA
::Groups-
>parse_config($filename, $raw);
81 sub parse_resources_config
{
82 my ($filename, $raw) = @_;
84 return PVE
::HA
::Resources-
>parse_config($filename, $raw);
87 sub read_resources_config
{
89 return cfs_read_file
($ha_resources_config);
92 # checks if resource exists and sets defaults for unset values
93 sub read_and_check_resources_config
{
95 my $res = cfs_read_file
($ha_resources_config);
97 my $vmlist = PVE
::Cluster
::get_vmlist
();
100 foreach my $sid (keys %{$res->{ids
}}) {
101 my $d = $res->{ids
}->{$sid};
102 my (undef, undef, $name) = parse_sid
($sid);
103 $d->{state} = 'started' if !defined($d->{state});
104 $d->{state} = 'started' if $d->{state} eq 'enabled'; # backward compatibility
105 $d->{max_restart
} = 1 if !defined($d->{max_restart
});
106 $d->{max_relocate
} = 1 if !defined($d->{max_relocate
});
107 if (PVE
::HA
::Resources-
>lookup($d->{type
})) {
108 if (my $vmd = $vmlist->{ids
}->{$name}) {
109 $d->{node
} = $vmd->{node
};
112 # undef $d->{node} is handled in get_verbose_service_state and
113 # status API, don't spam logs or ignore it; allow to delete it!
122 sub update_resources_config
{
123 my ($sid, $param, $delete, $digest) = @_;
127 my $cfg = read_resources_config
();
128 ($sid, my $type, my $name) = parse_sid
($sid);
130 PVE
::SectionConfig
::assert_if_modified
($cfg, $digest);
132 my $scfg = $cfg->{ids
}->{$sid} ||
133 die "no such resource '$sid'\n";
135 my $plugin = PVE
::HA
::Resources-
>lookup($scfg->{type
});
136 my $opts = $plugin->check_config($sid, $param, 0, 1);
138 foreach my $k (%$opts) {
139 $scfg->{$k} = $opts->{$k};
143 my $options = $plugin->private()->{options
}->{$type};
144 foreach my $k (PVE
::Tools
::split_list
($delete)) {
145 my $d = $options->{$k} ||
146 die "no such option '$k'\n";
147 die "unable to delete required option '$k'\n"
149 die "unable to delete fixed option '$k'\n"
155 write_resources_config
($cfg);
156 }, "update resources config failed");
164 if ($sid =~ m/^(\d+)$/) {
167 my $vmlist = PVE
::Cluster
::get_vmlist
();
168 if (defined($vmlist->{ids
}->{$name})) {
169 my $vm_type = $vmlist->{ids
}->{$name}->{type
};
170 if ($vm_type eq 'lxc') {
172 } elsif ($vm_type eq 'qemu') {
175 die "internal error";
177 $sid = "$type:$name";
180 die "unable do detect SID from VMID - VM/CT $1 does not exist\n";
182 } elsif ($sid =~m/^(\S+):(\S+)$/) {
186 die "unable to parse service id '$sid'\n";
189 return wantarray ?
($sid, $type, $name) : $sid;
192 sub read_group_config
{
194 return cfs_read_file
($ha_groups_config);
197 sub write_group_config
{
200 cfs_write_file
($ha_groups_config, $cfg);
203 sub write_resources_config
{
206 cfs_write_file
($ha_resources_config, $cfg);
209 sub read_manager_status
{
212 return cfs_read_file
($manager_status_filename);
215 sub write_manager_status
{
216 my ($status_obj) = @_;
218 cfs_write_file
($manager_status_filename, $status_obj);
221 sub read_fence_config
{
224 cfs_read_file
($ha_fence_config);
227 sub write_fence_config
{
230 cfs_write_file
($ha_fence_config, $cfg);
234 my ($code, $errmsg) = @_;
236 my $res = PVE
::Cluster
::cfs_lock_domain
("ha", undef, $code);
239 $errmsg ?
die "$errmsg: $err" : die $err;
244 sub queue_crm_commands
{
250 my $data = cfs_read_file
($crm_commands_filename);
252 cfs_write_file
($crm_commands_filename, $data);
255 return lock_ha_domain
($code);
258 sub read_crm_commands
{
261 my $data = cfs_read_file
($crm_commands_filename);
262 cfs_write_file
($crm_commands_filename, '');
266 return lock_ha_domain
($code);
269 my $service_check_ha_state = sub {
270 my ($conf, $sid, $has_state) = @_;
272 if (my $d = $conf->{ids
}->{$sid}) {
273 if (!defined($has_state)) {
274 # ignored service behave as if they were not managed by HA
275 return 0 if defined($d->{state}) && $d->{state} eq 'ignored';
279 # backward compatibility
280 $has_state = 'started' if $has_state eq 'enabled';
282 $d->{state} = 'started' if !defined($d->{state}) ||
283 ($d->{state} eq 'enabled');
285 return 1 if $d->{state} eq $has_state;
291 sub vm_is_ha_managed
{
292 my ($vmid, $has_state) = @_;
294 my $conf = cfs_read_file
($ha_resources_config);
296 my $types = PVE
::HA
::Resources-
>lookup_types();
297 foreach my $type ('vm', 'ct') {
298 return 1 if &$service_check_ha_state($conf, "$type:$vmid", $has_state);
304 sub service_is_ha_managed
{
305 my ($sid, $has_state, $noerr) = @_;
307 my $conf = cfs_read_file
($ha_resources_config);
309 return 1 if &$service_check_ha_state($conf, $sid, $has_state);
311 die "resource '$sid' is not HA managed\n" if !$noerr;
316 sub get_service_status
{
319 my $status = { managed
=> 0 };
321 my $conf = cfs_read_file
($ha_resources_config);
323 if (&$service_check_ha_state($conf, $sid)) {
324 my $manager_status = cfs_read_file
($manager_status_filename);
326 $status->{managed
} = 1;
327 $status->{group
} = $conf->{ids
}->{$sid}->{group
};
328 $status->{state} = $manager_status->{service_status
}->{$sid}->{state};