]> git.proxmox.com Git - pve-ha-manager.git/blob - src/PVE/API2/HA/Status.pm
api: status: add CRS info to manager if not set to default
[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::Cluster qw(cfs_read_file);
7 use PVE::DataCenterConfig;
8 use PVE::INotify;
9 use PVE::JSONSchema qw(get_standard_option);
10 use PVE::RPCEnvironment;
11 use PVE::SafeSyslog;
12
13 use PVE::HA::Config;
14
15 use PVE::RESTHandler;
16 use base qw(PVE::RESTHandler);
17
18 my $nodename = PVE::INotify::nodename();
19
20 my $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
33 __PACKAGE__->register_method ({
34 name => 'index',
35 path => '',
36 method => 'GET',
37 permissions => { user => 'all' },
38 description => "Directory index.",
39 parameters => {
40 additionalProperties => 0,
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) = @_;
53
54 my $result = [
55 { name => 'current' },
56 { name => 'manager_status' },
57 ];
58
59 return $result;
60 }});
61
62 __PACKAGE__->register_method ({
63 name => 'status',
64 path => 'current',
65 method => 'GET',
66 description => "Get HA manger status.",
67 permissions => {
68 check => ['perm', '/', [ 'Sys.Audit' ]],
69 },
70 parameters => {
71 additionalProperties => 0,
72 properties => {},
73 },
74 returns => { type => 'array' },
75 code => sub {
76 my ($param) = @_;
77
78 my $res = [];
79
80 my $quorate = PVE::Cluster::check_cfs_quorum(1);
81 push @$res, {
82 id => 'quorum', type => 'quorum',
83 node => $nodename,
84 status => $quorate ? 'OK' : "No quorum on node '$nodename'!",
85 quorate => $quorate ? 1 : 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
102 my $extra_status = '';
103
104 my $datacenter_config = eval { cfs_read_file('datacenter.cfg') } // {};
105 if (my $crs = $datacenter_config->{crs}) {
106 $extra_status = " - $crs->{ha} load CRS" if $crs->{ha} && $crs->{ha} ne 'basic';
107 }
108 my $time_str = localtime($status->{timestamp});
109 my $status_text = "$master ($status_str, $time_str)$extra_status";
110 push @$res, {
111 id => 'master', type => 'master',
112 node => $master,
113 status => $status_text,
114 timestamp => $status->{timestamp},
115 };
116 }
117
118 # compute active services for all nodes
119 my $active_count = {};
120 foreach my $sid (sort keys %{$status->{service_status}}) {
121 my $sd = $status->{service_status}->{$sid};
122 next if !$sd->{node};
123 $active_count->{$sd->{node}} = 0 if !defined($active_count->{$sd->{node}});
124 my $req_state = $sd->{state};
125 next if !defined($req_state);
126 next if $req_state eq 'stopped';
127 next if $req_state eq 'freeze';
128 $active_count->{$sd->{node}}++;
129 }
130
131 foreach my $node (sort keys %{$status->{node_status}}) {
132 my $lrm_status = PVE::HA::Config::read_lrm_status($node);
133 my $id = "lrm:$node";
134 if (!$lrm_status->{timestamp}) {
135 push @$res, { id => $id, type => 'lrm', node => $node,
136 status => "$node (unable to read lrm status)"};
137 } else {
138 my $status_str = &$timestamp_to_status($ctime, $lrm_status->{timestamp});
139 my $lrm_mode = $lrm_status->{mode};
140
141 if ($status_str eq 'active') {
142 $lrm_mode ||= 'active';
143 my $lrm_state = $lrm_status->{state} || 'unknown';
144 if ($lrm_mode ne 'active') {
145 $status_str = "$lrm_mode mode";
146 } else {
147 if ($lrm_state eq 'wait_for_agent_lock' && !$active_count->{$node}) {
148 $status_str = 'idle';
149 } else {
150 $status_str = $lrm_state;
151 }
152 }
153 } elsif ($lrm_mode && $lrm_mode eq 'maintenance') {
154 $status_str = "$lrm_mode mode";
155 }
156
157 my $time_str = localtime($lrm_status->{timestamp});
158 my $status_text = "$node ($status_str, $time_str)";
159 push @$res, {
160 id => $id, type => 'lrm',
161 node => $node,
162 status => $status_text,
163 timestamp => $lrm_status->{timestamp},
164 };
165 }
166 }
167
168 my $add_service = sub {
169 my ($sid, $sc, $ss) = @_;
170
171 my $data = { id => "service:$sid", type => 'service', sid => $sid };
172
173 if ($ss) {
174 $data->{node} = $ss->{node};
175 $data->{crm_state} = $ss->{state};
176 } elsif ($sc) {
177 $data->{node} = $sc->{node};
178 }
179 my $node = $data->{node} // '---'; # to be save against manual tinkering
180
181 $data->{state} = PVE::HA::Tools::get_verbose_service_state($ss, $sc);
182 $data->{status} = "$sid ($node, $data->{state})"; # backward compat. and CLI
183
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 }
190 }
191
192 push @$res, $data;
193 };
194
195 foreach my $sid (sort keys %{$status->{service_status}}) {
196 my $sc = $service_config->{$sid};
197 my $ss = $status->{service_status}->{$sid};
198 $add_service->($sid, $sc, $ss);
199 }
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};
204 my $sc = $service_config->{$sid};
205 $add_service->($sid, $sc);
206 }
207
208 return $res;
209 }});
210
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.",
216 permissions => {
217 check => ['perm', '/', [ 'Sys.Audit' ]],
218 },
219 parameters => {
220 additionalProperties => 0,
221 properties => {},
222 },
223 returns => { type => 'object' },
224 code => sub {
225 my ($param) = @_;
226
227 my $status = PVE::HA::Config::read_manager_status();
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}}) {
237 my $lrm_status = PVE::HA::Config::read_lrm_status($node);
238 $data->{lrm_status}->{$node} = $lrm_status;
239 }
240
241 return $data;
242 }});
243
244 1;