]> git.proxmox.com Git - pve-manager.git/blob - PVE/API2/Replication.pm
change to ReplicaState
[pve-manager.git] / PVE / API2 / Replication.pm
1 package PVE::API2::Replication;
2
3 use warnings;
4 use strict;
5
6 use PVE::JSONSchema qw(get_standard_option);
7 use PVE::RPCEnvironment;
8 use PVE::ProcFSTools;
9 use PVE::ReplicationConfig;
10 use PVE::ReplicationState;
11 use PVE::Replication;
12 use PVE::QemuConfig;
13 use PVE::QemuServer;
14 use PVE::LXC::Config;
15 use PVE::LXC;
16
17 use PVE::RESTHandler;
18
19 use base qw(PVE::RESTHandler);
20
21 my $pvesr_lock_path = "/var/lock/pvesr.lck";
22
23 my $lookup_guest_class = sub {
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
36 sub 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
63 $jobcfg->{id} = $jobid;
64
65 $jobcfg->{vmtype} = $vms->{ids}->{$vmid}->{type};
66
67 my $guest_class = $lookup_guest_class->($jobcfg->{vmtype});
68 PVE::Replication::run_replication($guest_class, $jobcfg, $now, $now, $logfunc);
69 };
70
71 my $res = PVE::Tools::lock_file($pvesr_lock_path, 60, $code);
72 die $@ if $@;
73 }
74
75 # passing $now is useful for regression testing
76 sub run_jobs {
77 my ($now, $logfunc) = @_;
78
79 my $iteration = $now // time();
80
81 my $code = sub {
82 my $start_time = $now // time();
83
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);
87 $start_time = $now // time();
88 }
89 };
90
91 my $res = PVE::Tools::lock_file($pvesr_lock_path, 60, $code);
92 die $@ if $@;
93 }
94
95 my $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
117 __PACKAGE__->register_method ({
118 name => 'status',
119 path => '',
120 method => 'GET',
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
154 my $jobs = PVE::ReplicationState::job_status();
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',
172 permissions => { user => 'all' },
173 description => "Directory index.",
174 parameters => {
175 additionalProperties => 0,
176 properties => {
177 id => get_standard_option('pve-replication-id'),
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 [
193 { name => 'log' },
194 { name => 'status' },
195 ];
196 }});
197
198
199 __PACKAGE__->register_method ({
200 name => 'job_status',
201 path => '{id}/status',
202 method => 'GET',
203 description => "Get replication job status.",
204 permissions => {
205 description => "Requires the VM.Audit permission on /vms/<vmid>.",
206 user => 'all',
207 },
208 protected => 1,
209 proxyto => 'node',
210 parameters => {
211 additionalProperties => 0,
212 properties => {
213 id => get_standard_option('pve-replication-id'),
214 node => get_standard_option('pve-node'),
215 },
216 },
217 returns => {
218 type => "object",
219 properties => {},
220 },
221 code => sub {
222 my ($param) = @_;
223
224 my $rpcenv = PVE::RPCEnvironment::get();
225 my $authuser = $rpcenv->get_user();
226
227 my $jobs = PVE::ReplicationState::job_status();
228 my $jobid = $param->{id};
229 my $jobcfg = $jobs->{$jobid};
230
231 die "no such replication job '$jobid'\n" if !defined($jobcfg);
232
233 my $data = $extract_job_status->($jobcfg, $jobid);
234 my $guest = $data->{guest};
235
236 raise_perm_exc() if !$rpcenv->check($authuser, "/vms/$guest", [ 'VM.Audit' ]);
237
238 return $data;
239 }});
240
241 __PACKAGE__->register_method({
242 name => 'read_job_log',
243 path => '{id}/log',
244 method => 'GET',
245 permissions => {
246 description => "Requires the VM.Audit permission on /vms/<vmid>, or 'Sys.Audit' on '/nodes/<node>'",
247 user => 'all',
248 },
249 protected => 1,
250 description => "Read replication job log.",
251 proxyto => 'node',
252 parameters => {
253 additionalProperties => 0,
254 properties => {
255 id => get_standard_option('pve-replication-id'),
256 node => get_standard_option('pve-node'),
257 start => {
258 type => 'integer',
259 minimum => 0,
260 optional => 1,
261 },
262 limit => {
263 type => 'integer',
264 minimum => 0,
265 optional => 1,
266 },
267 },
268 },
269 returns => {
270 type => 'array',
271 items => {
272 type => "object",
273 properties => {
274 n => {
275 description=> "Line number",
276 type=> 'integer',
277 },
278 t => {
279 description=> "Line text",
280 type => 'string',
281 }
282 }
283 }
284 },
285 code => sub {
286 my ($param) = @_;
287
288 my $rpcenv = PVE::RPCEnvironment::get();
289 my $authuser = $rpcenv->get_user();
290
291 my $jobid = $param->{id};
292 my $filename = PVE::ReplicationState::job_logfile_name($jobid);
293
294 my $cfg = PVE::ReplicationConfig->new();
295 my $data = $cfg->{ids}->{$jobid};
296
297 die "no such replication job '$jobid'\n" if !defined($data);
298
299 my $node = $param->{node};
300
301 my $vmid = $data->{guest};
302 raise_perm_exc() if (!($rpcenv->check($authuser, "/vms/$vmid", [ 'VM.Audit' ]) ||
303 $rpcenv->check($authuser, "/nodes/$node", [ 'Sys.Audit' ])));
304
305 my ($count, $lines) = PVE::Tools::dump_logfile($filename, $param->{start}, $param->{limit});
306
307 $rpcenv->set_result_attrib('total', $count);
308
309 return $lines;
310 }});
311
312 1;