]> git.proxmox.com Git - pve-ha-manager.git/blob - src/PVE/API2/HA/Status.pm
api/status: extra handling of maintenance mode
[pve-ha-manager.git] / src / PVE / API2 / HA / Status.pm
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;
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
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
61 __PACKAGE__->register_method ({
62 name => 'status',
63 path => 'current',
64 method => 'GET',
65 description => "Get HA manger status.",
66 permissions => {
67 check => ['perm', '/', [ 'Sys.Audit' ]],
68 },
69 parameters => {
70 additionalProperties => 0,
71 properties => {},
72 },
73 returns => { type => 'array' },
74 code => sub {
75 my ($param) = @_;
76
77 my $res = [];
78
79 my $quorate = PVE::Cluster::check_cfs_quorum(1);
80 if ($quorate) {
81 push @$res, { id => 'quorum', type => 'quorum',
82 node => $nodename, status => "OK", quorate => 1 };
83 } else {
84 push @$res, { id => 'quorum', type => 'quorum', node => $nodename,
85 status => "No quorum on node '$nodename'!", quorate => 0 };
86 }
87
88 my $status = PVE::HA::Config::read_manager_status();
89
90 my $service_config = PVE::HA::Config::read_and_check_resources_config();
91
92 my $ctime = time();
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});
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 }
101 my $time_str = localtime($status->{timestamp});
102 my $status_text = "$master ($status_str, $time_str)";
103 push @$res, { id => 'master', type => 'master', node => $master,
104 status => $status_text, timestamp => $status->{timestamp} };
105 }
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 }
119
120 foreach my $node (sort keys %{$status->{node_status}}) {
121 my $lrm_status = PVE::HA::Config::read_lrm_status($node);
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});
128 my $lrm_mode = $lrm_status->{mode};
129
130 if ($status_str eq 'active') {
131 $lrm_mode ||= 'active';
132 my $lrm_state = $lrm_status->{state} || 'unknown';
133 if ($lrm_mode ne 'active') {
134 $status_str = "$lrm_mode mode";
135 } else {
136 if ($lrm_state eq 'wait_for_agent_lock' && !$active_count->{$node}) {
137 $status_str = 'idle';
138 } else {
139 $status_str = $lrm_state;
140 }
141 }
142 } elsif ($lrm_mode && $lrm_mode eq 'maintenance') {
143 $status_str = "$lrm_mode mode";
144 }
145
146 my $time_str = localtime($lrm_status->{timestamp});
147 my $status_text = "$node ($status_str, $time_str)";
148 push @$res, { id => $id, type => 'lrm', node => $node,
149 status => $status_text, timestamp => $lrm_status->{timestamp} };
150 }
151 }
152
153 my $add_service = sub {
154 my ($sid, $sc, $ss) = @_;
155
156 my $data = { id => "service:$sid", type => 'service', sid => $sid };
157
158 if ($ss) {
159 $data->{node} = $ss->{node};
160 $data->{crm_state} = $ss->{state};
161 } elsif ($sc) {
162 $data->{node} = $sc->{node};
163 }
164 my $node = $data->{node} // '---'; # to be save against manual tinkering
165
166 $data->{state} = PVE::HA::Tools::get_verbose_service_state($ss, $sc);
167 $data->{status} = "$sid ($node, $data->{state})"; # backward compat. and CLI
168
169 # also return common resource attributes
170 if (defined($sc)) {
171 $data->{request_state} = $sc->{state};
172 foreach my $key (qw(group max_restart max_relocate comment)) {
173 $data->{$key} = $sc->{$key} if defined($sc->{$key});
174 }
175 }
176
177 push @$res, $data;
178 };
179
180 foreach my $sid (sort keys %{$status->{service_status}}) {
181 my $sc = $service_config->{$sid};
182 my $ss = $status->{service_status}->{$sid};
183 $add_service->($sid, $sc, $ss);
184 }
185
186 # show also service which aren't yet processed by the CRM
187 foreach my $sid (sort keys %$service_config) {
188 next if $status->{service_status}->{$sid};
189 my $sc = $service_config->{$sid};
190 $add_service->($sid, $sc);
191 }
192
193 return $res;
194 }});
195
196 __PACKAGE__->register_method ({
197 name => 'manager_status',
198 path => 'manager_status',
199 method => 'GET',
200 description => "Get full HA manger status, including LRM status.",
201 permissions => {
202 check => ['perm', '/', [ 'Sys.Audit' ]],
203 },
204 parameters => {
205 additionalProperties => 0,
206 properties => {},
207 },
208 returns => { type => 'object' },
209 code => sub {
210 my ($param) = @_;
211
212 my $status = PVE::HA::Config::read_manager_status();
213
214 my $data = { manager_status => $status };
215
216 $data->{quorum} = {
217 node => $nodename,
218 quorate => PVE::Cluster::check_cfs_quorum(1),
219 };
220
221 foreach my $node (sort keys %{$status->{node_status}}) {
222 my $lrm_status = PVE::HA::Config::read_lrm_status($node);
223 $data->{lrm_status}->{$node} = $lrm_status;
224 }
225
226 return $data;
227 }});
228
229 1;