]>
Commit | Line | Data |
---|---|---|
3f7cacff DM |
1 | package PVE::ReplicationState; |
2 | ||
3 | use warnings; | |
4 | use strict; | |
5 | use JSON; | |
6 | ||
7 | use PVE::Tools; | |
90c07bf7 | 8 | use PVE::GuestHelpers; |
3f7cacff DM |
9 | use PVE::ReplicationConfig; |
10 | ||
3f7cacff DM |
11 | # Note: regression tests can overwrite $state_path for testing |
12 | our $state_path = "/var/lib/pve-manager/pve-replication-state.json"; | |
13 | our $state_lock = "/var/lib/pve-manager/pve-replication-state.lck"; | |
14 | ||
15 | # Note: We use PVE::Tools::file_set_contents to write state file atomically, | |
16 | # so read_state() always returns an consistent copy (even when not locked). | |
17 | ||
18 | sub read_state { | |
19 | ||
20 | return {} if ! -e $state_path; | |
21 | ||
22 | my $raw = PVE::Tools::file_get_contents($state_path); | |
23 | ||
24 | return {} if $raw eq ''; | |
25 | ||
26 | # untaint $raw | |
27 | if ($raw =~ m/^({.*})$/) { | |
28 | return decode_json($1); | |
29 | } | |
30 | ||
31 | die "invalid json data in '$state_path'\n"; | |
32 | } | |
33 | ||
34 | sub extract_job_state { | |
35 | my ($stateobj, $jobcfg) = @_; | |
36 | ||
37 | my $plugin = PVE::ReplicationConfig->lookup($jobcfg->{type}); | |
38 | ||
39 | my $vmid = $jobcfg->{guest}; | |
40 | my $tid = $plugin->get_unique_target_id($jobcfg); | |
41 | my $state = $stateobj->{$vmid}->{$tid}; | |
42 | ||
43 | $state = {} if !$state; | |
44 | ||
45 | $state->{last_iteration} //= 0; | |
46 | $state->{last_try} //= 0; # last sync start time | |
47 | $state->{last_sync} //= 0; # last successful sync start time | |
48 | $state->{fail_count} //= 0; | |
49 | ||
50 | return $state; | |
51 | } | |
52 | ||
53 | sub read_job_state { | |
54 | my ($jobcfg) = @_; | |
55 | ||
56 | my $stateobj = read_state(); | |
57 | return extract_job_state($stateobj, $jobcfg); | |
58 | } | |
59 | ||
60 | sub write_job_state { | |
61 | my ($jobcfg, $state) = @_; | |
62 | ||
63 | my $plugin = PVE::ReplicationConfig->lookup($jobcfg->{type}); | |
64 | ||
65 | my $vmid = $jobcfg->{guest}; | |
66 | my $tid = $plugin->get_unique_target_id($jobcfg); | |
67 | ||
90c07bf7 | 68 | my $update = sub { |
3f7cacff DM |
69 | |
70 | my $stateobj = read_state(); | |
71 | # Note: tuple ($vmid, $tid) is unique | |
72 | $stateobj->{$vmid}->{$tid} = $state; | |
73 | ||
74 | PVE::Tools::file_set_contents($state_path, encode_json($stateobj)); | |
75 | }; | |
76 | ||
90c07bf7 DM |
77 | my $code = sub { |
78 | PVE::Tools::lock_file($state_lock, 10, $update); | |
79 | die $@ if $@; | |
80 | }; | |
81 | ||
82 | # make sure we have guest_migration_lock during update | |
83 | PVE::GuestHelpers::guest_migration_lock($vmid, undef, $code); | |
3f7cacff DM |
84 | } |
85 | ||
86 | 1; |