]> git.proxmox.com Git - pve-ha-manager.git/blame - src/PVE/API2/HA/Status.pm
api: fix/add return description for status endpoint
[pve-ha-manager.git] / src / PVE / API2 / HA / Status.pm
CommitLineData
50306453
DM
1package PVE::API2::HA::Status;
2
3use strict;
4use warnings;
5
475f19fe
TL
6use PVE::Cluster qw(cfs_read_file);
7use PVE::DataCenterConfig;
d0625985 8use PVE::INotify;
50306453
DM
9use PVE::JSONSchema qw(get_standard_option);
10use PVE::RPCEnvironment;
d0625985 11use PVE::SafeSyslog;
50306453 12
d0625985 13use PVE::HA::Config;
50306453 14
d0625985 15use PVE::RESTHandler;
50306453
DM
16use base qw(PVE::RESTHandler);
17
18my $nodename = PVE::INotify::nodename();
19
20my $timestamp_to_status = sub {
21 my ($ctime, $timestamp) = @_;
22
23 my $tdiff = $ctime - $timestamp;
24 if ($tdiff > 30) {
25 return "old timestamp - dead?";
26 } elsif ($tdiff < -2) {
27 return "detected time drift!";
28 } else {
29 return "active";
30 }
31};
32
5c9d7bc9 33__PACKAGE__->register_method ({
289e4784
TL
34 name => 'index',
35 path => '',
5c9d7bc9
DM
36 method => 'GET',
37 permissions => { user => 'all' },
38 description => "Directory index.",
39 parameters => {
d0625985 40 additionalProperties => 0,
5c9d7bc9
DM
41 properties => {},
42 },
43 returns => {
44 type => 'array',
45 items => {
46 type => "object",
47 properties => {},
48 },
49 links => [ { rel => 'child', href => "{name}" } ],
50 },
51 code => sub {
52 my ($param) = @_;
289e4784 53
5c9d7bc9
DM
54 my $result = [
55 { name => 'current' },
56 { name => 'manager_status' },
d0625985 57 ];
5c9d7bc9
DM
58
59 return $result;
60 }});
61
50306453 62__PACKAGE__->register_method ({
289e4784 63 name => 'status',
5c9d7bc9 64 path => 'current',
50306453
DM
65 method => 'GET',
66 description => "Get HA manger status.",
0fcba7ab
TL
67 permissions => {
68 check => ['perm', '/', [ 'Sys.Audit' ]],
69 },
50306453 70 parameters => {
d0625985 71 additionalProperties => 0,
50306453
DM
72 properties => {},
73 },
afa1aa9c
FE
74 returns => {
75 type => 'array',
76 items => {
77 type => 'object',
78 properties => {
79 id => {
80 description => "Status entry ID (quorum, master, lrm:<node>, service:<sid>).",
81 type => "string",
82 },
83 node => {
84 description => "Node associated to status entry.",
85 type => "string",
86 },
87 status => {
88 description => "Status of the entry (value depends on type).",
89 type => "string",
90 },
91 type => {
92 description => "Type of status entry.",
93 enum => ["quorum", "master", "lrm", "service"],
94 },
95 quorate => {
96 description => "For type 'quorum'. Whether the cluster is quorate or not.",
97 type => "boolean",
98 optional => 1,
99 },
100 timestamp => {
101 description => "For type 'lrm','master'. Timestamp of the status information.",
102 type => "integer",
103 optional => 1,
104 },
105 crm_state => {
106 description => "For type 'service'. Service state as seen by the CRM.",
107 type => "string",
108 optional => 1,
109 },
110 max_relocate => {
111 description => "For type 'service'.",
112 type => "integer",
113 optional => 1,
114 },
115 max_restart => {
116 description => "For type 'service'.",
117 type => "integer",
118 optional => 1,
119 },
120 request_state => {
121 description => "For type 'service'. Requested service state.",
122 type => "string",
123 optional => 1,
124 },
125 sid => {
126 description => "For type 'service'. Service ID.",
127 type => "string",
128 optional => 1,
129 },
130 state => {
131 description => "For type 'service'. Verbose service state.",
132 type => "string",
133 optional => 1,
134 },
135 },
136 },
137 },
50306453
DM
138 code => sub {
139 my ($param) = @_;
140
141 my $res = [];
322d3550
TL
142
143 my $quorate = PVE::Cluster::check_cfs_quorum(1);
d0625985
TL
144 push @$res, {
145 id => 'quorum', type => 'quorum',
146 node => $nodename,
147 status => $quorate ? 'OK' : "No quorum on node '$nodename'!",
148 quorate => $quorate ? 1 : 0,
149 };
322d3550 150
c12b9a1d 151 my $status = PVE::HA::Config::read_manager_status();
322d3550 152
c12b9a1d 153 my $service_config = PVE::HA::Config::read_and_check_resources_config();
50306453 154
c12b9a1d 155 my $ctime = time();
50306453
DM
156
157 if (defined($status->{master_node}) && defined($status->{timestamp})) {
158 my $master = $status->{master_node};
159 my $status_str = &$timestamp_to_status($ctime, $status->{timestamp});
322d3550
TL
160 # mark crm idle if it has no service configured and is not active
161 if ($quorate && $status_str ne 'active' && !keys %{$service_config}) {
162 $status_str = 'idle';
163 }
475f19fe
TL
164
165 my $extra_status = '';
166
167 my $datacenter_config = eval { cfs_read_file('datacenter.cfg') } // {};
168 if (my $crs = $datacenter_config->{crs}) {
169 $extra_status = " - $crs->{ha} load CRS" if $crs->{ha} && $crs->{ha} ne 'basic';
170 }
50306453 171 my $time_str = localtime($status->{timestamp});
475f19fe 172 my $status_text = "$master ($status_str, $time_str)$extra_status";
d0625985
TL
173 push @$res, {
174 id => 'master', type => 'master',
175 node => $master,
176 status => $status_text,
177 timestamp => $status->{timestamp},
178 };
322d3550 179 }
331a9f00
DM
180
181 # compute active services for all nodes
182 my $active_count = {};
183 foreach my $sid (sort keys %{$status->{service_status}}) {
184 my $sd = $status->{service_status}->{$sid};
185 next if !$sd->{node};
186 $active_count->{$sd->{node}} = 0 if !defined($active_count->{$sd->{node}});
187 my $req_state = $sd->{state};
188 next if !defined($req_state);
189 next if $req_state eq 'stopped';
190 next if $req_state eq 'freeze';
191 $active_count->{$sd->{node}}++;
192 }
289e4784 193
50306453 194 foreach my $node (sort keys %{$status->{node_status}}) {
c12b9a1d 195 my $lrm_status = PVE::HA::Config::read_lrm_status($node);
50306453
DM
196 my $id = "lrm:$node";
197 if (!$lrm_status->{timestamp}) {
289e4784
TL
198 push @$res, { id => $id, type => 'lrm', node => $node,
199 status => "$node (unable to read lrm status)"};
50306453
DM
200 } else {
201 my $status_str = &$timestamp_to_status($ctime, $lrm_status->{timestamp});
77bcb60a
TL
202 my $lrm_mode = $lrm_status->{mode};
203
331a9f00 204 if ($status_str eq 'active') {
77bcb60a 205 $lrm_mode ||= 'active';
331a9f00
DM
206 my $lrm_state = $lrm_status->{state} || 'unknown';
207 if ($lrm_mode ne 'active') {
208 $status_str = "$lrm_mode mode";
209 } else {
210 if ($lrm_state eq 'wait_for_agent_lock' && !$active_count->{$node}) {
211 $status_str = 'idle';
212 } else {
213 $status_str = $lrm_state;
214 }
215 }
77bcb60a
TL
216 } elsif ($lrm_mode && $lrm_mode eq 'maintenance') {
217 $status_str = "$lrm_mode mode";
331a9f00
DM
218 }
219
50306453
DM
220 my $time_str = localtime($lrm_status->{timestamp});
221 my $status_text = "$node ($status_str, $time_str)";
d0625985
TL
222 push @$res, {
223 id => $id, type => 'lrm',
224 node => $node,
225 status => $status_text,
226 timestamp => $lrm_status->{timestamp},
227 };
50306453
DM
228 }
229 }
230
30b36b89
DM
231 my $add_service = sub {
232 my ($sid, $sc, $ss) = @_;
233
98808a5a
DM
234 my $data = { id => "service:$sid", type => 'service', sid => $sid };
235
30b36b89 236 if ($ss) {
98808a5a 237 $data->{node} = $ss->{node};
98808a5a 238 $data->{crm_state} = $ss->{state};
c9b21b5a 239 } elsif ($sc) {
98808a5a 240 $data->{node} = $sc->{node};
98808a5a 241 }
fbda2658 242 my $node = $data->{node} // '---'; # to be save against manual tinkering
30b36b89 243
aa68337a 244 $data->{state} = PVE::HA::Tools::get_verbose_service_state($ss, $sc);
fbda2658 245 $data->{status} = "$sid ($node, $data->{state})"; # backward compat. and CLI
aa68337a 246
98808a5a
DM
247 # also return common resource attributes
248 if (defined($sc)) {
249 $data->{request_state} = $sc->{state};
250 foreach my $key (qw(group max_restart max_relocate comment)) {
251 $data->{$key} = $sc->{$key} if defined($sc->{$key});
252 }
30b36b89 253 }
98808a5a
DM
254
255 push @$res, $data;
30b36b89
DM
256 };
257
50306453 258 foreach my $sid (sort keys %{$status->{service_status}}) {
30b36b89
DM
259 my $sc = $service_config->{$sid};
260 my $ss = $status->{service_status}->{$sid};
261 $add_service->($sid, $sc, $ss);
50306453 262 }
2afff38a
TL
263
264 # show also service which aren't yet processed by the CRM
265 foreach my $sid (sort keys %$service_config) {
266 next if $status->{service_status}->{$sid};
30b36b89
DM
267 my $sc = $service_config->{$sid};
268 $add_service->($sid, $sc);
2afff38a
TL
269 }
270
50306453
DM
271 return $res;
272 }});
273
5c9d7bc9 274__PACKAGE__->register_method ({
289e4784 275 name => 'manager_status',
5c9d7bc9
DM
276 path => 'manager_status',
277 method => 'GET',
278 description => "Get full HA manger status, including LRM status.",
0fcba7ab
TL
279 permissions => {
280 check => ['perm', '/', [ 'Sys.Audit' ]],
281 },
5c9d7bc9 282 parameters => {
d0625985 283 additionalProperties => 0,
5c9d7bc9
DM
284 properties => {},
285 },
286 returns => { type => 'object' },
287 code => sub {
288 my ($param) = @_;
50306453 289
c12b9a1d 290 my $status = PVE::HA::Config::read_manager_status();
289e4784 291
5c9d7bc9
DM
292 my $data = { manager_status => $status };
293
294 $data->{quorum} = {
295 node => $nodename,
296 quorate => PVE::Cluster::check_cfs_quorum(1),
297 };
289e4784 298
5c9d7bc9 299 foreach my $node (sort keys %{$status->{node_status}}) {
c12b9a1d 300 my $lrm_status = PVE::HA::Config::read_lrm_status($node);
5c9d7bc9
DM
301 $data->{lrm_status}->{$node} = $lrm_status;
302 }
303
304 return $data;
305 }});
50306453
DM
306
3071;