]> git.proxmox.com Git - pve-ha-manager.git/blame - src/PVE/API2/HA/Status.pm
ha status: include common service attributes
[pve-ha-manager.git] / src / PVE / API2 / HA / Status.pm
CommitLineData
50306453
DM
1package PVE::API2::HA::Status;
2
3use strict;
4use warnings;
5
6use PVE::SafeSyslog;
7use PVE::INotify;
8use PVE::Cluster;
9use PVE::HA::Config;
10use PVE::JSONSchema qw(get_standard_option);
11use PVE::RPCEnvironment;
50306453
DM
12
13use PVE::RESTHandler;
14
15use base qw(PVE::RESTHandler);
16
17my $nodename = PVE::INotify::nodename();
18
19my $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
2441;