1 package PVE
::API2
::HA
::Status
;
10 use PVE
::JSONSchema
qw(get_standard_option);
11 use PVE
::RPCEnvironment
;
15 use base
qw(PVE::RESTHandler);
17 my $nodename = PVE
::INotify
::nodename
();
19 my $timestamp_to_status = sub {
20 my ($ctime, $timestamp) = @_;
22 my $tdiff = $ctime - $timestamp;
24 return "old timestamp - dead?";
25 } elsif ($tdiff < -2) {
26 return "detected time drift!";
32 __PACKAGE__-
>register_method ({
36 permissions
=> { user
=> 'all' },
37 description
=> "Directory index.",
39 additionalProperties
=> 0,
48 links
=> [ { rel
=> 'child', href
=> "{name}" } ],
54 { name
=> 'current' },
55 { name
=> 'manager_status' },
61 __PACKAGE__-
>register_method ({
65 description
=> "Get HA manger status.",
67 check
=> ['perm', '/', [ 'Sys.Audit' ]],
70 additionalProperties
=> 0,
73 returns
=> { type
=> 'array' },
79 my $quorate = PVE
::Cluster
::check_cfs_quorum
(1);
81 push @$res, { id
=> 'quorum', type
=> 'quorum',
82 node
=> $nodename, status
=> "OK", quorate
=> 1 };
84 push @$res, { id
=> 'quorum', type
=> 'quorum', node
=> $nodename,
85 status
=> "No quorum on node '$nodename'!", quorate
=> 0 };
88 my $status = PVE
::HA
::Config
::read_manager_status
();
90 my $service_config = PVE
::HA
::Config
::read_and_check_resources_config
();
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}) {
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
} };
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
}}++;
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)"};
127 my $status_str = &$timestamp_to_status($ctime, $lrm_status->{timestamp
});
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";
134 if ($lrm_state eq 'wait_for_agent_lock' && !$active_count->{$node}) {
135 $status_str = 'idle';
137 $status_str = $lrm_state;
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
} };
149 my $add_service = sub {
150 my ($sid, $sc, $ss) = @_;
152 my $data = { id
=> "service:$sid", type
=> 'service', sid
=> $sid };
155 my $req = $sc->{state} || 'ignore';
156 my $cur = $ss->{state};
159 # give fast feedback to the user
160 if ($cur eq 'stopped') {
161 if ($req eq 'started') {
163 } elsif ($req eq 'disabled') {
166 } elsif ($cur eq 'started') {
167 if ($req eq 'stopped' || $req eq 'disabled') {
170 $state = 'starting' if !$ss->{running
};
171 } elsif ($cur eq 'error') {
172 if ($req eq 'disabled') {
173 $state = 'clearing error flag';
177 $data->{node
} = $ss->{node
};
178 $data->{status
} = "$sid ($ss->{node}, $state)"; # backward compatibility
179 $data->{state} = $state;
180 $data->{crm_state
} = $ss->{state};
182 $data->{node
} = $sc->{node
};
183 $data->{state} = 'queued';
184 $data->{status
} = "$sid ($sc->{node}, queued)"; # backward compatibility
187 # also return common resource attributes
189 $data->{request_state
} = $sc->{state};
190 foreach my $key (qw(group max_restart max_relocate comment)) {
191 $data->{$key} = $sc->{$key} if defined($sc->{$key});
198 foreach my $sid (sort keys %{$status->{service_status
}}) {
199 my $sc = $service_config->{$sid};
200 my $ss = $status->{service_status
}->{$sid};
201 $add_service->($sid, $sc, $ss);
204 # show also service which aren't yet processed by the CRM
205 foreach my $sid (sort keys %$service_config) {
206 next if $status->{service_status
}->{$sid};
207 my $sc = $service_config->{$sid};
208 $add_service->($sid, $sc);
214 __PACKAGE__-
>register_method ({
215 name
=> 'manager_status',
216 path
=> 'manager_status',
218 description
=> "Get full HA manger status, including LRM status.",
220 check
=> ['perm', '/', [ 'Sys.Audit' ]],
223 additionalProperties
=> 0,
226 returns
=> { type
=> 'object' },
230 my $status = PVE
::HA
::Config
::read_manager_status
();
232 my $data = { manager_status
=> $status };
236 quorate
=> PVE
::Cluster
::check_cfs_quorum
(1),
239 foreach my $node (sort keys %{$status->{node_status
}}) {
240 my $lrm_status = PVE
::HA
::Config
::read_lrm_status
($node);
241 $data->{lrm_status
}->{$node} = $lrm_status;