]>
git.proxmox.com Git - pve-ha-manager.git/blob - PVE/HA/Manager.pm
1 package PVE
::HA
::Manager
;
5 use Digest
::MD5
qw(md5_base64);
9 use PVE
::HA
::NodeStatus
;
12 my ($this, $haenv) = @_;
14 my $class = ref($this) || $this;
16 my $ms = $haenv->read_manager_status();
18 $ms->{master_node
} = $haenv->nodename();
20 my $ns = PVE
::HA
::NodeStatus-
>new($haenv, $ms->{node_status
} || {});
22 # fixme: use separate class PVE::HA::ServiceStatus
23 my $ss = $ms->{service_status
} || {};
27 ms
=> $ms, # master status
28 ns
=> $ns, # PVE::HA::NodeStatus
29 ss
=> $ss, # service status
41 sub flush_master_status
{
44 my ($haenv, $ms, $ns, $ss) = ($self->{haenv
}, $self->{ms
}, $self->{ns
}, $self->{ss
});
46 $ms->{node_status
} = $ns->{status
};
47 $ms->{service_status
} = $ss;
49 $haenv->write_manager_status($ms);
52 # Attention: must be idempotent (alway return the same result for same input!)
53 sub select_service_node
{
54 my ($self, $service_conf) = @_;
58 my $pref_node = $service_conf->{node
};
60 return $pref_node if $ns->node_is_online($pref_node);
62 my $online_nodes = $ns->list_online_nodes();
64 return shift @$online_nodes;
69 my $change_service_state = sub {
70 my ($self, $sid, $new_state, %params) = @_;
72 my ($haenv, $ss) = ($self->{haenv
}, $self->{ss
});
74 my $sd = $ss->{$sid} || die "no such service '$sid";
76 my $old_state = $sd->{state};
78 die "no state change" if $old_state eq $new_state; # just to be sure
81 foreach my $k (keys %params) {
83 next if defined($sd->{$k}) && $sd->{$k} eq $v;
84 $changes .= ", " if $changes;
85 $changes .= "$k = $v";
89 $sd->{state} = $new_state;
91 $sd->{uid
} = md5_base64
($new_state . $$ . time() . $uid_counter);
93 # fixme: cleanup state (remove unused values)
95 $changes = " ($changes)" if $changes;
96 $haenv->log('info', "service '$sid': state changed to '$new_state' $changes\n");
99 # read LRM status for all nodes (even for offline nodes)
100 sub read_lrm_status
{
101 my ($self, $node_info) = @_;
103 my $haenv = $self->{haenv
};
107 foreach my $node (keys %$node_info) {
108 my $ls = $haenv->read_lrm_status($node);
109 foreach my $uid (keys %$ls) {
110 next if $res->{$uid}; # should not happen
111 $res->{$uid} = $ls->{$uid};
121 my ($haenv, $ms, $ns, $ss) = ($self->{haenv
}, $self->{ms
}, $self->{ns
}, $self->{ss
});
123 my ($node_info, $quorate) = $haenv->get_node_info();
124 $ns->update($node_info);
126 # fixme: what if $quorate is 0??
128 if (!$ns->node_is_online($haenv->nodename())) {
129 $haenv->log('info', "master seems offline\n");
133 my $lrm_status = $self->read_lrm_status($node_info);
135 my $sc = $haenv->read_service_config();
137 # compute new service status
140 foreach my $sid (keys %$sc) {
141 next if $ss->{$sid}; # already there
142 $haenv->log('info', "Adding new service '$sid'\n");
143 # assume we are running to avoid relocate running service at add
144 $ss->{$sid} = { state => 'started', node
=> $sc->{$sid}->{current_node
}};
150 foreach my $sid (keys %$ss) {
151 my $sd = $ss->{$sid};
152 my $cd = $sc->{$sid} || { state => 'disabled' };
154 my $last_state = $sd->{state};
156 if ($last_state eq 'stopped') {
158 if ($cd->{state} eq 'disabled') {
160 } elsif ($cd->{state} eq 'enabled') {
161 if (my $node = $self->select_service_node($cd)) {
162 &$change_service_state($self, $sid, 'started', node
=> $node);
167 # do nothing - todo: log something?
170 } elsif ($last_state eq 'started') {
172 if (!$ns->node_is_online($sd->{node
})) {
174 &$change_service_state($self, $sid, 'fence');
178 if ($cd->{state} eq 'disabled') {
179 &$change_service_state($self, $sid, 'request_stop');
180 } elsif ($cd->{state} eq 'enabled') {
181 my $node = $self->select_service_node($cd);
182 if ($node && ($sd->{node
} ne $node)) {
183 &$change_service_state($self, $sid, 'migrate');
188 # do nothing - todo: log something?
192 } elsif ($last_state eq 'migrate') {
196 } elsif ($last_state eq 'fence') {
198 # do nothing here - wait until fenced
200 } elsif ($last_state eq 'request_stop') {
202 #fixme: die "implement me";
206 die "unknown service state '$last_state'";
210 $repeat = 1 if $sd->{state} ne $last_state;
214 my $fenced_nodes = {};
215 foreach my $sid (keys %$ss) {
216 my $sd = $ss->{$sid};
217 next if $sd->{state} ne 'fence';
219 if (!defined($fenced_nodes->{$sd->{node
}})) {
220 $fenced_nodes->{$sd->{node
}} = $ns->fence_node($sd->{node
}) || 0;
223 next if !$fenced_nodes->{$sd->{node
}};
225 # node fence was sucessful - mark service as stopped
226 &$change_service_state($self, $sid, 'stopped');
232 # remove stale services
235 $self->flush_master_status();