]> git.proxmox.com Git - pve-manager.git/blame - PVE/API2/Replication.pm
Indentation cleanup.
[pve-manager.git] / PVE / API2 / Replication.pm
CommitLineData
892821fd
DM
1package PVE::API2::Replication;
2
3use warnings;
4use strict;
5
6use PVE::JSONSchema qw(get_standard_option);
7use PVE::RPCEnvironment;
483f89dd 8use PVE::ProcFSTools;
892821fd 9use PVE::ReplicationConfig;
d092dc4f 10use PVE::ReplicationState;
892821fd 11use PVE::Replication;
810c6776
DM
12use PVE::QemuConfig;
13use PVE::QemuServer;
14use PVE::LXC::Config;
15use PVE::LXC;
892821fd
DM
16
17use PVE::RESTHandler;
18
19use base qw(PVE::RESTHandler);
20
5b358450 21our $pvesr_lock_path = "/var/lock/pvesr.lck";
810c6776 22
25420507 23our $lookup_guest_class = sub {
810c6776
DM
24 my ($vmtype) = @_;
25
26 if ($vmtype eq 'qemu') {
27 return 'PVE::QemuConfig';
28 } elsif ($vmtype eq 'lxc') {
29 return 'PVE::LXC::Config';
30 } else {
31 die "unknown guest type '$vmtype' - internal error";
32 }
33};
34
35# passing $now is useful for regression testing
36sub run_single_job {
37 my ($jobid, $now, $logfunc) = @_;
38
39 my $local_node = PVE::INotify::nodename();
40
41 my $code = sub {
42 $now //= time();
43
44 my $cfg = PVE::ReplicationConfig->new();
45
46 my $jobcfg = $cfg->{ids}->{$jobid};
47 die "no such job '$jobid'\n" if !$jobcfg;
48
49 die "internal error - not implemented" if $jobcfg->{type} ne 'local';
50
51 die "job '$jobid' is disabled\n" if $jobcfg->{disable};
52
53 my $vms = PVE::Cluster::get_vmlist();
54 my $vmid = $jobcfg->{guest};
55
56 die "no such guest '$vmid'\n" if !$vms->{ids}->{$vmid};
57
58 die "guest '$vmid' is not on local node\n"
59 if $vms->{ids}->{$vmid}->{node} ne $local_node;
60
61 die "unable to sync to local node\n" if $jobcfg->{target} eq $local_node;
62
a4beaa94 63 my $vmtype = $vms->{ids}->{$vmid}->{type};
810c6776 64
a4beaa94 65 my $guest_class = $lookup_guest_class->($vmtype);
810c6776
DM
66 PVE::Replication::run_replication($guest_class, $jobcfg, $now, $now, $logfunc);
67 };
68
69 my $res = PVE::Tools::lock_file($pvesr_lock_path, 60, $code);
70 die $@ if $@;
71}
72
2aa02957 73# passing $now and $verbose is useful for regression testing
810c6776 74sub run_jobs {
2aa02957 75 my ($now, $logfunc, $verbose) = @_;
810c6776
DM
76
77 my $iteration = $now // time();
78
79 my $code = sub {
c8742096 80 my $start_time = $now // time();
810c6776 81
c8742096 82 PVE::ReplicationState::purge_old_states();
5a26b006 83
c8742096
WL
84 while (my $jobcfg = PVE::ReplicationState::get_next_job($iteration, $start_time)) {
85 my $guest_class = $lookup_guest_class->($jobcfg->{vmtype});
86 PVE::Replication::run_replication($guest_class, $jobcfg, $iteration, $start_time, $logfunc, 1, $verbose);
87 $start_time = $now // time();
88 }
810c6776
DM
89 };
90
91 my $res = PVE::Tools::lock_file($pvesr_lock_path, 60, $code);
92 die $@ if $@;
93}
94
fc527b4d
DM
95my $extract_job_status = sub {
96 my ($jobcfg, $jobid) = @_;
97
98 # Note: we modify $jobcfg
99 my $state = delete $jobcfg->{state};
100 my $data = $jobcfg;
101
102 $data->{id} = $jobid;
103
104 foreach my $k (qw(last_sync last_try fail_count error duration)) {
105 $data->{$k} = $state->{$k} if defined($state->{$k});
106 }
107
108 if ($state->{pid} && $state->{ptime}) {
109 if (PVE::ProcFSTools::check_process_running($state->{pid}, $state->{ptime})) {
110 $data->{pid} = $state->{pid};
111 }
112 }
113
114 return $data;
115};
116
892821fd 117__PACKAGE__->register_method ({
fc527b4d 118 name => 'status',
892821fd
DM
119 path => '',
120 method => 'GET',
fc527b4d
DM
121 description => "List status of all replication jobs on this node.",
122 permissions => {
123 description => "Requires the VM.Audit permission on /vms/<vmid>.",
124 user => 'all',
125 },
126 protected => 1,
127 proxyto => 'node',
128 parameters => {
129 additionalProperties => 0,
130 properties => {
131 node => get_standard_option('pve-node'),
132 guest => get_standard_option('pve-vmid', {
133 optional => 1,
134 description => "Only list replication jobs for this guest.",
135 }),
136 },
137 },
138 returns => {
139 type => 'array',
140 items => {
141 type => "object",
142 properties => {
143 id => { type => 'string' },
144 },
145 },
146 links => [ { rel => 'child', href => "{id}" } ],
147 },
148 code => sub {
149 my ($param) = @_;
150
151 my $rpcenv = PVE::RPCEnvironment::get();
152 my $authuser = $rpcenv->get_user();
153
959f37af 154 my $jobs = PVE::ReplicationState::job_status(1);
fc527b4d
DM
155
156 my $res = [];
157 foreach my $id (sort keys %$jobs) {
158 my $data = $extract_job_status->($jobs->{$id}, $id);
159 my $guest = $data->{guest};
160 next if defined($param->{guest}) && $guest != $param->{guest};
161 next if !$rpcenv->check($authuser, "/vms/$guest", [ 'VM.Audit' ]);
162 push @$res, $data;
163 }
164
165 return $res;
166 }});
167
168__PACKAGE__->register_method ({
169 name => 'index',
170 path => '{id}',
171 method => 'GET',
892821fd
DM
172 permissions => { user => 'all' },
173 description => "Directory index.",
174 parameters => {
175 additionalProperties => 0,
176 properties => {
fc527b4d 177 id => get_standard_option('pve-replication-id'),
892821fd
DM
178 node => get_standard_option('pve-node'),
179 },
180 },
181 returns => {
182 type => 'array',
183 items => {
184 type => "object",
185 properties => {},
186 },
187 links => [ { rel => 'child', href => "{name}" } ],
188 },
189 code => sub {
190 my ($param) = @_;
191
192 return [
88ea8e67 193 { name => 'schedule_now' },
fc527b4d 194 { name => 'log' },
892821fd 195 { name => 'status' },
fc527b4d 196 ];
892821fd
DM
197 }});
198
199
200__PACKAGE__->register_method ({
fc527b4d
DM
201 name => 'job_status',
202 path => '{id}/status',
892821fd 203 method => 'GET',
fc527b4d 204 description => "Get replication job status.",
892821fd
DM
205 permissions => {
206 description => "Requires the VM.Audit permission on /vms/<vmid>.",
207 user => 'all',
208 },
209 protected => 1,
210 proxyto => 'node',
211 parameters => {
212 additionalProperties => 0,
213 properties => {
fc527b4d 214 id => get_standard_option('pve-replication-id'),
892821fd
DM
215 node => get_standard_option('pve-node'),
216 },
217 },
218 returns => {
fc527b4d
DM
219 type => "object",
220 properties => {},
892821fd
DM
221 },
222 code => sub {
223 my ($param) = @_;
224
225 my $rpcenv = PVE::RPCEnvironment::get();
226 my $authuser = $rpcenv->get_user();
227
d092dc4f 228 my $jobs = PVE::ReplicationState::job_status();
fc527b4d
DM
229 my $jobid = $param->{id};
230 my $jobcfg = $jobs->{$jobid};
892821fd 231
fc527b4d
DM
232 die "no such replication job '$jobid'\n" if !defined($jobcfg);
233
234 my $data = $extract_job_status->($jobcfg, $jobid);
235 my $guest = $data->{guest};
236
237 raise_perm_exc() if !$rpcenv->check($authuser, "/vms/$guest", [ 'VM.Audit' ]);
238
239 return $data;
240 }});
241
242__PACKAGE__->register_method({
243 name => 'read_job_log',
244 path => '{id}/log',
245 method => 'GET',
246 permissions => {
247 description => "Requires the VM.Audit permission on /vms/<vmid>, or 'Sys.Audit' on '/nodes/<node>'",
248 user => 'all',
249 },
250 protected => 1,
251 description => "Read replication job log.",
252 proxyto => 'node',
253 parameters => {
254 additionalProperties => 0,
255 properties => {
256 id => get_standard_option('pve-replication-id'),
257 node => get_standard_option('pve-node'),
258 start => {
259 type => 'integer',
260 minimum => 0,
261 optional => 1,
262 },
263 limit => {
264 type => 'integer',
265 minimum => 0,
266 optional => 1,
267 },
268 },
269 },
270 returns => {
271 type => 'array',
272 items => {
273 type => "object",
274 properties => {
275 n => {
276 description=> "Line number",
277 type=> 'integer',
278 },
279 t => {
280 description=> "Line text",
281 type => 'string',
483f89dd
DM
282 }
283 }
892821fd 284 }
fc527b4d
DM
285 },
286 code => sub {
287 my ($param) = @_;
892821fd 288
fc527b4d
DM
289 my $rpcenv = PVE::RPCEnvironment::get();
290 my $authuser = $rpcenv->get_user();
291
292 my $jobid = $param->{id};
d09c076a 293 my $filename = PVE::ReplicationState::job_logfile_name($jobid);
fc527b4d
DM
294
295 my $cfg = PVE::ReplicationConfig->new();
296 my $data = $cfg->{ids}->{$jobid};
297
298 die "no such replication job '$jobid'\n" if !defined($data);
299
300 my $node = $param->{node};
301
302 my $vmid = $data->{guest};
303 raise_perm_exc() if (!($rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ]) ||
304 $rpcenv->check($authuser, "/nodes/$node", [ 'Sys.Audit' ])));
305
306 my ($count, $lines) = PVE::Tools::dump_logfile($filename, $param->{start}, $param->{limit});
307
308 $rpcenv->set_result_attrib('total', $count);
309
310 return $lines;
892821fd
DM
311 }});
312
88ea8e67
DM
313__PACKAGE__->register_method ({
314 name => 'schedule_now',
315 path => '{id}/schedule_now',
316 method => 'POST',
317 description => "Schedule replication job to start as soon as possible.",
318 proxyto => 'node',
319 protected => 1,
320 permissions => {
321 check => ['perm', '/storage', ['Datastore.Allocate']],
322 },
323 parameters => {
324 additionalProperties => 0,
325 properties => {
326 id => get_standard_option('pve-replication-id'),
327 node => get_standard_option('pve-node'),
328 },
329 },
330 returns => {
331 type => 'string',
332 },
333 code => sub {
334 my ($param) = @_;
335
336 my $jobid = $param->{id};
337
338 my $cfg = PVE::ReplicationConfig->new();
339 my $jobcfg = $cfg->{ids}->{$jobid};
340
341 die "no such replication job '$jobid'\n" if !defined($jobcfg);
342
343 PVE::ReplicationState::schedule_job_now($jobcfg);
344
345 }});
346
892821fd 3471;