]>
Commit | Line | Data |
---|---|---|
50306453 DM |
1 | package PVE::API2::HA::Status; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
6 | use PVE::SafeSyslog; | |
7 | use PVE::INotify; | |
8 | use PVE::Cluster; | |
9 | use PVE::HA::Config; | |
10 | use PVE::JSONSchema qw(get_standard_option); | |
11 | use PVE::RPCEnvironment; | |
50306453 DM |
12 | |
13 | use PVE::RESTHandler; | |
14 | ||
15 | use base qw(PVE::RESTHandler); | |
16 | ||
17 | my $nodename = PVE::INotify::nodename(); | |
18 | ||
19 | my $timestamp_to_status = sub { | |
20 | my ($ctime, $timestamp) = @_; | |
21 | ||
22 | my $tdiff = $ctime - $timestamp; | |
23 | if ($tdiff > 30) { | |
24 | return "old timestamp - dead?"; | |
25 | } elsif ($tdiff < -2) { | |
26 | return "detected time drift!"; | |
27 | } else { | |
28 | return "active"; | |
29 | } | |
30 | }; | |
31 | ||
5c9d7bc9 DM |
32 | __PACKAGE__->register_method ({ |
33 | name => 'index', | |
34 | path => '', | |
35 | method => 'GET', | |
36 | permissions => { user => 'all' }, | |
37 | description => "Directory index.", | |
38 | parameters => { | |
39 | additionalProperties => 0, | |
40 | properties => {}, | |
41 | }, | |
42 | returns => { | |
43 | type => 'array', | |
44 | items => { | |
45 | type => "object", | |
46 | properties => {}, | |
47 | }, | |
48 | links => [ { rel => 'child', href => "{name}" } ], | |
49 | }, | |
50 | code => sub { | |
51 | my ($param) = @_; | |
52 | ||
53 | my $result = [ | |
54 | { name => 'current' }, | |
55 | { name => 'manager_status' }, | |
56 | ]; | |
57 | ||
58 | return $result; | |
59 | }}); | |
60 | ||
50306453 DM |
61 | __PACKAGE__->register_method ({ |
62 | name => 'status', | |
5c9d7bc9 | 63 | path => 'current', |
50306453 DM |
64 | method => 'GET', |
65 | description => "Get HA manger status.", | |
0fcba7ab TL |
66 | permissions => { |
67 | check => ['perm', '/', [ 'Sys.Audit' ]], | |
68 | }, | |
50306453 DM |
69 | parameters => { |
70 | additionalProperties => 0, | |
71 | properties => {}, | |
72 | }, | |
73 | returns => { type => 'array' }, | |
74 | code => sub { | |
75 | my ($param) = @_; | |
76 | ||
77 | my $res = []; | |
322d3550 TL |
78 | |
79 | my $quorate = PVE::Cluster::check_cfs_quorum(1); | |
80 | if ($quorate) { | |
81 | push @$res, { id => 'quorum', type => 'quorum', | |
50306453 DM |
82 | node => $nodename, status => "OK", quorate => 1 }; |
83 | } else { | |
322d3550 | 84 | push @$res, { id => 'quorum', type => 'quorum', node => $nodename, |
50306453 DM |
85 | status => "No quorum on node '$nodename'!", quorate => 0 }; |
86 | } | |
322d3550 | 87 | |
c12b9a1d | 88 | my $status = PVE::HA::Config::read_manager_status(); |
322d3550 | 89 | |
c12b9a1d | 90 | my $service_config = PVE::HA::Config::read_and_check_resources_config(); |
50306453 | 91 | |
c12b9a1d | 92 | my $ctime = time(); |
50306453 DM |
93 | |
94 | if (defined($status->{master_node}) && defined($status->{timestamp})) { | |
95 | my $master = $status->{master_node}; | |
96 | my $status_str = &$timestamp_to_status($ctime, $status->{timestamp}); | |
322d3550 TL |
97 | # mark crm idle if it has no service configured and is not active |
98 | if ($quorate && $status_str ne 'active' && !keys %{$service_config}) { | |
99 | $status_str = 'idle'; | |
100 | } | |
50306453 DM |
101 | my $time_str = localtime($status->{timestamp}); |
102 | my $status_text = "$master ($status_str, $time_str)"; | |
322d3550 | 103 | push @$res, { id => 'master', type => 'master', node => $master, |
50306453 | 104 | status => $status_text, timestamp => $status->{timestamp} }; |
322d3550 | 105 | } |
331a9f00 DM |
106 | |
107 | # compute active services for all nodes | |
108 | my $active_count = {}; | |
109 | foreach my $sid (sort keys %{$status->{service_status}}) { | |
110 | my $sd = $status->{service_status}->{$sid}; | |
111 | next if !$sd->{node}; | |
112 | $active_count->{$sd->{node}} = 0 if !defined($active_count->{$sd->{node}}); | |
113 | my $req_state = $sd->{state}; | |
114 | next if !defined($req_state); | |
115 | next if $req_state eq 'stopped'; | |
116 | next if $req_state eq 'freeze'; | |
117 | $active_count->{$sd->{node}}++; | |
118 | } | |
50306453 DM |
119 | |
120 | foreach my $node (sort keys %{$status->{node_status}}) { | |
c12b9a1d | 121 | my $lrm_status = PVE::HA::Config::read_lrm_status($node); |
50306453 DM |
122 | my $id = "lrm:$node"; |
123 | if (!$lrm_status->{timestamp}) { | |
124 | push @$res, { id => $id, type => 'lrm', node => $node, | |
125 | status => "$node (unable to read lrm status)"}; | |
126 | } else { | |
127 | my $status_str = &$timestamp_to_status($ctime, $lrm_status->{timestamp}); | |
331a9f00 DM |
128 | if ($status_str eq 'active') { |
129 | my $lrm_mode = $lrm_status->{mode} || 'active'; | |
130 | my $lrm_state = $lrm_status->{state} || 'unknown'; | |
131 | if ($lrm_mode ne 'active') { | |
132 | $status_str = "$lrm_mode mode"; | |
133 | } else { | |
134 | if ($lrm_state eq 'wait_for_agent_lock' && !$active_count->{$node}) { | |
135 | $status_str = 'idle'; | |
136 | } else { | |
137 | $status_str = $lrm_state; | |
138 | } | |
139 | } | |
140 | } | |
141 | ||
50306453 DM |
142 | my $time_str = localtime($lrm_status->{timestamp}); |
143 | my $status_text = "$node ($status_str, $time_str)"; | |
144 | push @$res, { id => $id, type => 'lrm', node => $node, | |
145 | status => $status_text, timestamp => $lrm_status->{timestamp} }; | |
146 | } | |
147 | } | |
148 | ||
30b36b89 DM |
149 | my $add_service = sub { |
150 | my ($sid, $sc, $ss) = @_; | |
151 | ||
98808a5a DM |
152 | my $data = { id => "service:$sid", type => 'service', sid => $sid }; |
153 | ||
30b36b89 DM |
154 | if ($ss) { |
155 | my $req = $sc->{state} || 'ignore'; | |
156 | my $cur = $ss->{state}; | |
157 | my $state = $cur; | |
158 | ||
159 | # give fast feedback to the user | |
160 | if ($cur eq 'stopped') { | |
161 | if ($req eq 'started') { | |
162 | $state = 'starting'; | |
163 | } | |
164 | } elsif ($cur eq 'started') { | |
165 | if ($req eq 'stopped' || $req eq 'disabled') { | |
166 | $state = 'stopping'; | |
167 | } | |
168 | } elsif ($cur eq 'error') { | |
169 | if ($req eq 'disabled') { | |
170 | $state = 'clearing error flag'; | |
171 | } | |
172 | } | |
173 | ||
98808a5a DM |
174 | $data->{node} = $ss->{node}; |
175 | $data->{status} = "$sid ($ss->{node}, $state)"; # backward compatibility | |
176 | $data->{state} = $state; | |
177 | $data->{crm_state} = $ss->{state}; | |
30b36b89 | 178 | } else { |
98808a5a DM |
179 | $data->{node} = $sc->{node}; |
180 | $data->{state} = 'queued'; | |
181 | $data->{status} = "$sid ($sc->{node}, queued)"; # backward compatibility | |
182 | } | |
30b36b89 | 183 | |
98808a5a DM |
184 | # also return common resource attributes |
185 | if (defined($sc)) { | |
186 | $data->{request_state} = $sc->{state}; | |
187 | foreach my $key (qw(group max_restart max_relocate comment)) { | |
188 | $data->{$key} = $sc->{$key} if defined($sc->{$key}); | |
189 | } | |
30b36b89 | 190 | } |
98808a5a DM |
191 | |
192 | push @$res, $data; | |
30b36b89 DM |
193 | }; |
194 | ||
50306453 | 195 | foreach my $sid (sort keys %{$status->{service_status}}) { |
30b36b89 DM |
196 | my $sc = $service_config->{$sid}; |
197 | my $ss = $status->{service_status}->{$sid}; | |
198 | $add_service->($sid, $sc, $ss); | |
50306453 | 199 | } |
2afff38a TL |
200 | |
201 | # show also service which aren't yet processed by the CRM | |
202 | foreach my $sid (sort keys %$service_config) { | |
203 | next if $status->{service_status}->{$sid}; | |
30b36b89 DM |
204 | my $sc = $service_config->{$sid}; |
205 | $add_service->($sid, $sc); | |
2afff38a TL |
206 | } |
207 | ||
50306453 DM |
208 | return $res; |
209 | }}); | |
210 | ||
5c9d7bc9 DM |
211 | __PACKAGE__->register_method ({ |
212 | name => 'manager_status', | |
213 | path => 'manager_status', | |
214 | method => 'GET', | |
215 | description => "Get full HA manger status, including LRM status.", | |
0fcba7ab TL |
216 | permissions => { |
217 | check => ['perm', '/', [ 'Sys.Audit' ]], | |
218 | }, | |
5c9d7bc9 DM |
219 | parameters => { |
220 | additionalProperties => 0, | |
221 | properties => {}, | |
222 | }, | |
223 | returns => { type => 'object' }, | |
224 | code => sub { | |
225 | my ($param) = @_; | |
50306453 | 226 | |
c12b9a1d | 227 | my $status = PVE::HA::Config::read_manager_status(); |
5c9d7bc9 DM |
228 | |
229 | my $data = { manager_status => $status }; | |
230 | ||
231 | $data->{quorum} = { | |
232 | node => $nodename, | |
233 | quorate => PVE::Cluster::check_cfs_quorum(1), | |
234 | }; | |
235 | ||
236 | foreach my $node (sort keys %{$status->{node_status}}) { | |
c12b9a1d | 237 | my $lrm_status = PVE::HA::Config::read_lrm_status($node); |
5c9d7bc9 DM |
238 | $data->{lrm_status}->{$node} = $lrm_status; |
239 | } | |
240 | ||
241 | return $data; | |
242 | }}); | |
50306453 DM |
243 | |
244 | 1; |