]> git.proxmox.com Git - pve-ha-manager.git/blob - src/PVE/API2/HA/Status.pm
status: show added but not yet active services
[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 use PVE::HA::Env::PVE2;
13
14 use PVE::RESTHandler;
15
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 if ($quorate) {
82 push @$res, { id => 'quorum', type => 'quorum',
83 node => $nodename, status => "OK", quorate => 1 };
84 } else {
85 push @$res, { id => 'quorum', type => 'quorum', node => $nodename,
86 status => "No quorum on node '$nodename'!", quorate => 0 };
87 }
88
89 my $haenv = PVE::HA::Env::PVE2->new($nodename);
90
91 my $status = $haenv->read_manager_status();
92
93 my $service_config = $haenv->read_service_config();
94
95 my $ctime = $haenv->get_time();
96
97 if (defined($status->{master_node}) && defined($status->{timestamp})) {
98 my $master = $status->{master_node};
99 my $status_str = &$timestamp_to_status($ctime, $status->{timestamp});
100 # mark crm idle if it has no service configured and is not active
101 if ($quorate && $status_str ne 'active' && !keys %{$service_config}) {
102 $status_str = 'idle';
103 }
104 my $time_str = localtime($status->{timestamp});
105 my $status_text = "$master ($status_str, $time_str)";
106 push @$res, { id => 'master', type => 'master', node => $master,
107 status => $status_text, timestamp => $status->{timestamp} };
108 }
109
110 # compute active services for all nodes
111 my $active_count = {};
112 foreach my $sid (sort keys %{$status->{service_status}}) {
113 my $sd = $status->{service_status}->{$sid};
114 next if !$sd->{node};
115 $active_count->{$sd->{node}} = 0 if !defined($active_count->{$sd->{node}});
116 my $req_state = $sd->{state};
117 next if !defined($req_state);
118 next if $req_state eq 'stopped';
119 next if $req_state eq 'freeze';
120 $active_count->{$sd->{node}}++;
121 }
122
123 foreach my $node (sort keys %{$status->{node_status}}) {
124 my $lrm_status = $haenv->read_lrm_status($node);
125 my $id = "lrm:$node";
126 if (!$lrm_status->{timestamp}) {
127 push @$res, { id => $id, type => 'lrm', node => $node,
128 status => "$node (unable to read lrm status)"};
129 } else {
130 my $status_str = &$timestamp_to_status($ctime, $lrm_status->{timestamp});
131 if ($status_str eq 'active') {
132 my $lrm_mode = $lrm_status->{mode} || 'active';
133 my $lrm_state = $lrm_status->{state} || 'unknown';
134 if ($lrm_mode ne 'active') {
135 $status_str = "$lrm_mode mode";
136 } else {
137 if ($lrm_state eq 'wait_for_agent_lock' && !$active_count->{$node}) {
138 $status_str = 'idle';
139 } else {
140 $status_str = $lrm_state;
141 }
142 }
143 }
144
145 my $time_str = localtime($lrm_status->{timestamp});
146 my $status_text = "$node ($status_str, $time_str)";
147 push @$res, { id => $id, type => 'lrm', node => $node,
148 status => $status_text, timestamp => $lrm_status->{timestamp} };
149 }
150 }
151
152 foreach my $sid (sort keys %{$status->{service_status}}) {
153 my $d = $status->{service_status}->{$sid};
154 push @$res, { id => "service:$sid", type => 'service', sid => $sid,
155 node => $d->{node}, status => "$sid ($d->{node}, $d->{state})" };
156 }
157
158 # show also service which aren't yet processed by the CRM
159 foreach my $sid (sort keys %$service_config) {
160 next if $status->{service_status}->{$sid};
161 my $d = $service_config->{$sid};
162 push @$res, { id => "service:$sid", type => 'service', sid => $sid,
163 status => "$sid ($d->{node}, queued)",
164 node => $d->{node} };
165 }
166
167 return $res;
168 }});
169
170 __PACKAGE__->register_method ({
171 name => 'manager_status',
172 path => 'manager_status',
173 method => 'GET',
174 description => "Get full HA manger status, including LRM status.",
175 permissions => {
176 check => ['perm', '/', [ 'Sys.Audit' ]],
177 },
178 parameters => {
179 additionalProperties => 0,
180 properties => {},
181 },
182 returns => { type => 'object' },
183 code => sub {
184 my ($param) = @_;
185
186 my $haenv = PVE::HA::Env::PVE2->new($nodename);
187
188 my $status = $haenv->read_manager_status();
189
190 my $data = { manager_status => $status };
191
192 $data->{quorum} = {
193 node => $nodename,
194 quorate => PVE::Cluster::check_cfs_quorum(1),
195 };
196
197 foreach my $node (sort keys %{$status->{node_status}}) {
198 my $lrm_status = $haenv->read_lrm_status($node);
199 $data->{lrm_status}->{$node} = $lrm_status;
200 }
201
202 return $data;
203 }});
204
205 1;