]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Services.pm
api: backup: update: turn delete into a hash
[pve-manager.git] / PVE / API2 / Services.pm
1 package PVE::API2::Services;
2
3 use strict;
4 use warnings;
5
6 use PVE::Tools;
7 use PVE::SafeSyslog;
8 use PVE::Cluster;
9 use PVE::INotify;
10 use PVE::Exception qw(raise_param_exc);
11 use PVE::RESTHandler;
12 use PVE::RPCEnvironment;
13 use PVE::JSONSchema qw(get_standard_option);
14 use PVE::AccessControl;
15 use IO::File;
16
17 use base qw(PVE::RESTHandler);
18
19 my $service_name_list = [
20 'chrony',
21 'corosync',
22 'cron',
23 'ksmtuned',
24 'postfix',
25 'pve-cluster',
26 'pve-firewall',
27 'pve-ha-crm',
28 'pve-ha-lrm',
29 'pvedaemon',
30 'pvefw-logger',
31 'pveproxy',
32 'pvescheduler',
33 'pvestatd',
34 'spiceproxy',
35 'sshd',
36 'syslog',
37 'systemd-journald',
38 'systemd-timesyncd',
39 ];
40 my $essential_services = {
41 pveproxy => 1,
42 pvedaemon => 1,
43 'pve-cluster' => 1,
44 };
45
46 # since postfix package 3.1.0-3.1 the postfix unit is only here to
47 # manage subinstances, of which the default is called "-".
48 # This is where we look for the daemon status
49 my $unit_extra_names = {
50 postfix => 'postfix@-'
51 };
52
53 my $get_full_service_state = sub {
54 my ($service) = @_;
55 $service = $unit_extra_names->{$service} if $unit_extra_names->{$service};
56 my $res;
57
58 my $parser = sub {
59 my $line = shift;
60 if ($line =~ m/^([^=\s]+)=(.*)$/) {
61 $res->{$1} = $2;
62 }
63 };
64
65 PVE::Tools::run_command(['systemctl', 'show', $service], outfunc => $parser);
66
67 return $res;
68 };
69
70 my $static_service_list;
71
72 sub get_service_list {
73
74 return $static_service_list if $static_service_list;
75
76 my $list = {};
77 foreach my $name (@$service_name_list) {
78 my $ss = eval { $get_full_service_state->($name) };
79 warn $@ if $@;
80 next if !$ss;
81 next if !defined($ss->{Description});
82 $list->{$name} = { name => $name, desc => $ss->{Description} };
83 }
84
85 $static_service_list = $list;
86
87 return $static_service_list;
88 }
89
90
91 my $service_prop_desc = {
92 description => "Service ID",
93 type => 'string',
94 enum => $service_name_list,
95 };
96
97 my $service_cmd = sub {
98 my ($service, $cmd) = @_;
99
100 my $initd_cmd;
101
102 die "unknown service command '$cmd'\n" if $cmd !~ m/^(start|stop|restart|reload|try-reload-or-restart)$/;
103
104 if ($essential_services->{$service} && $cmd eq 'stop') {
105 die "invalid service cmd '$service $cmd': refusing to stop essential service!\n";
106 }
107
108 PVE::Tools::run_command(['systemctl', $cmd, $service]);
109 };
110
111 my $service_state = sub {
112 my ($service) = @_;
113
114 my $res = { state => 'unknown' };
115
116 my $ss = eval { $get_full_service_state->($service) };
117 if (my $err = $@) {
118 return $res;
119 }
120 my $state = $ss->{SubState} || 'unknown';
121 if ($state eq 'dead' && $ss->{Type} && $ss->{Type} eq 'oneshot' && $ss->{Result}) {
122 $res->{state} = $ss->{Result};
123 } else {
124 $res->{state} = $ss->{SubState} || 'unknown';
125 }
126
127 if ($ss->{LoadState} eq 'not-found') {
128 $res->{'unit-state'} = 'not-found'; # not installed
129 } else {
130 $res->{'unit-state'} = $ss->{UnitFileState} || 'unknown';
131 }
132 $res->{'active-state'} = $ss->{ActiveState} || 'unknown';
133
134 return $res;
135 };
136
137 __PACKAGE__->register_method ({
138 name => 'index',
139 path => '',
140 method => 'GET',
141 permissions => {
142 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
143 },
144 description => "Service list.",
145 proxyto => 'node',
146 protected => 1,
147 parameters => {
148 additionalProperties => 0,
149 properties => {
150 node => get_standard_option('pve-node'),
151 },
152 },
153 returns => {
154 type => 'array',
155 items => {
156 type => "object",
157 properties => {},
158 },
159 links => [ { rel => 'child', href => "{service}" } ],
160 },
161 code => sub {
162 my ($param) = @_;
163
164 my $service_list = get_service_list();
165
166 my $res = [];
167 for my $id (sort keys %{$service_list}) {
168 my $state = $service_state->($id);
169 push @$res, {
170 service => $id,
171 name => $service_list->{$id}->{name},
172 desc => $service_list->{$id}->{desc},
173 %$state,
174 };
175 }
176
177 return $res;
178 }});
179
180 __PACKAGE__->register_method({
181 name => 'srvcmdidx',
182 path => '{service}',
183 method => 'GET',
184 description => "Directory index",
185 permissions => {
186 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
187 },
188 parameters => {
189 additionalProperties => 0,
190 properties => {
191 node => get_standard_option('pve-node'),
192 service => $service_prop_desc,
193 },
194 },
195 returns => {
196 type => 'array',
197 items => {
198 type => "object",
199 properties => {
200 subdir => { type => 'string' },
201 },
202 },
203 links => [ { rel => 'child', href => "{subdir}" } ],
204 },
205 code => sub {
206 my ($param) = @_;
207
208 my $res = [
209 { subdir => 'state' },
210 { subdir => 'start' },
211 { subdir => 'stop' },
212 { subdir => 'restart' },
213 { subdir => 'reload' },
214 ];
215
216 return $res;
217 }});
218
219 __PACKAGE__->register_method ({
220 name => 'service_state',
221 path => '{service}/state',
222 method => 'GET',
223 permissions => {
224 check => ['perm', '/nodes/{node}', [ 'Sys.Audit' ]],
225 },
226 description => "Read service properties",
227 proxyto => 'node',
228 protected => 1,
229 parameters => {
230 additionalProperties => 0,
231 properties => {
232 node => get_standard_option('pve-node'),
233 service => $service_prop_desc,
234 },
235 },
236 returns => {
237 type => "object",
238 properties => {},
239 },
240 code => sub {
241 my ($param) = @_;
242
243 my $id = $param->{service};
244
245 my $service_list = get_service_list();
246
247 my $si = $service_list->{$id};
248
249 my $state = $service_state->($id);
250
251 return {
252 service => $param->{service},
253 name => $si->{name},
254 desc => $si->{desc},
255 %$state,
256 };
257 }});
258
259 __PACKAGE__->register_method ({
260 name => 'service_start',
261 path => '{service}/start',
262 method => 'POST',
263 description => "Start service.",
264 permissions => {
265 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
266 },
267 proxyto => 'node',
268 protected => 1,
269 parameters => {
270 additionalProperties => 0,
271 properties => {
272 node => get_standard_option('pve-node'),
273 service => $service_prop_desc,
274 },
275 },
276 returns => {
277 type => 'string',
278 },
279 code => sub {
280 my ($param) = @_;
281
282 my $rpcenv = PVE::RPCEnvironment::get();
283
284 my $user = $rpcenv->get_user();
285
286 my $realcmd = sub {
287 my $upid = shift;
288
289 syslog('info', "starting service $param->{service}: $upid\n");
290
291 $service_cmd->($param->{service}, 'start');
292
293 };
294
295 return $rpcenv->fork_worker('srvstart', $param->{service}, $user, $realcmd);
296 }});
297
298 __PACKAGE__->register_method ({
299 name => 'service_stop',
300 path => '{service}/stop',
301 method => 'POST',
302 description => "Stop service.",
303 permissions => {
304 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
305 },
306 proxyto => 'node',
307 protected => 1,
308 parameters => {
309 additionalProperties => 0,
310 properties => {
311 node => get_standard_option('pve-node'),
312 service => $service_prop_desc,
313 },
314 },
315 returns => {
316 type => 'string',
317 },
318 code => sub {
319 my ($param) = @_;
320
321 my $rpcenv = PVE::RPCEnvironment::get();
322
323 my $user = $rpcenv->get_user();
324
325 my $realcmd = sub {
326 my $upid = shift;
327
328 syslog('info', "stopping service $param->{service}: $upid\n");
329
330 $service_cmd->($param->{service}, 'stop');
331
332 };
333
334 return $rpcenv->fork_worker('srvstop', $param->{service}, $user, $realcmd);
335 }});
336
337 __PACKAGE__->register_method ({
338 name => 'service_restart',
339 path => '{service}/restart',
340 method => 'POST',
341 description => "Hard restart service. Use reload if you want to reduce interruptions.",
342 permissions => {
343 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
344 },
345 proxyto => 'node',
346 protected => 1,
347 parameters => {
348 additionalProperties => 0,
349 properties => {
350 node => get_standard_option('pve-node'),
351 service => $service_prop_desc,
352 },
353 },
354 returns => {
355 type => 'string',
356 },
357 code => sub {
358 my ($param) = @_;
359
360 my $rpcenv = PVE::RPCEnvironment::get();
361 my $user = $rpcenv->get_user();
362
363 my $realcmd = sub {
364 my $upid = shift;
365 syslog('info', "re-starting service $param->{service}: $upid\n");
366
367 $service_cmd->($param->{service}, 'restart');
368 };
369
370 return $rpcenv->fork_worker('srvrestart', $param->{service}, $user, $realcmd);
371 }});
372
373 __PACKAGE__->register_method ({
374 name => 'service_reload',
375 path => '{service}/reload',
376 method => 'POST',
377 description => "Reload service. Falls back to restart if service cannot be reloaded.",
378 permissions => {
379 check => ['perm', '/nodes/{node}', [ 'Sys.Modify' ]],
380 },
381 proxyto => 'node',
382 protected => 1,
383 parameters => {
384 additionalProperties => 0,
385 properties => {
386 node => get_standard_option('pve-node'),
387 service => $service_prop_desc,
388 },
389 },
390 returns => {
391 type => 'string',
392 },
393 code => sub {
394 my ($param) = @_;
395
396 my $rpcenv = PVE::RPCEnvironment::get();
397 my $user = $rpcenv->get_user();
398
399 my $realcmd = sub {
400 my $upid = shift;
401 syslog('info', "reloading service $param->{service}: $upid\n");
402
403 $service_cmd->($param->{service}, 'try-reload-or-restart');
404
405 };
406
407 return $rpcenv->fork_worker('srvreload', $param->{service}, $user, $realcmd);
408 }});