]> git.proxmox.com Git - pve-guest-common.git/blob - PVE/ReplicationConfig.pm
PVE/ReplicationConfig.pm - new configuratin file for replication
[pve-guest-common.git] / PVE / ReplicationConfig.pm
1 package PVE::ReplicationConfig;
2
3 use strict;
4 use warnings;
5 use Data::Dumper;
6
7 use PVE::Tools;
8 use PVE::JSONSchema qw(get_standard_option);
9 use PVE::INotify;
10 use PVE::SectionConfig;
11 use PVE::CalendarEvent;
12
13 use PVE::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
14
15 use base qw(PVE::SectionConfig);
16
17 my $replication_cfg_filename = 'replication.cfg';
18
19 cfs_register_file($replication_cfg_filename,
20 sub { __PACKAGE__->parse_config(@_); },
21 sub { __PACKAGE__->write_config(@_); });
22
23 PVE::JSONSchema::register_standard_option('pve-replication-id', {
24 description => "Replication Job ID.",
25 type => 'string', format => 'pve-configid',
26 maxLength => 32, # keep short to reduce snapshot name length
27 });
28
29 my $defaultData = {
30 propertyList => {
31 type => { description => "Section type." },
32 id => get_standard_option('pve-replication-id'),
33 disable => {
34 description => "Flag to disable/deactivate the entry.",
35 type => 'boolean',
36 optional => 1,
37 },
38 comment => {
39 description => "Description.",
40 type => 'string',
41 optional => 1,
42 maxLength => 4096,
43 },
44 guest => get_standard_option('pve-vmid', {
45 optional => 1,
46 completion => \&PVE::Cluster::complete_vmid }),
47 rate => {
48 description => "Rate limit in mbps (megabytes per second) as floating point number.",
49 type => 'number',
50 minimum => 1,
51 optional => 1,
52 },
53 schedule => {
54 description => "Storage replication schedule. The format is a subset of `systemd` calender events.",
55 type => 'string', format => 'pve-calendar-event',
56 maxLength => 128,
57 default => '*/15',
58 optional => 1,
59 },
60 },
61 };
62
63 sub private {
64 return $defaultData;
65 }
66
67 sub parse_section_header {
68 my ($class, $line) = @_;
69
70 if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
71 my ($type, $id) = (lc($1), $2);
72 my $errmsg = undef; # set if you want to skip whole section
73 eval { PVE::JSONSchema::pve_verify_configid($id); };
74 $errmsg = $@ if $@;
75 my $config = {};
76 return ($type, $id, $errmsg, $config);
77 }
78 return undef;
79 }
80
81 # Note: We want only one replication job per target to
82 # avoid confusion. This method should return a string
83 # which uniquely identifies the target.
84 sub get_unique_target_id {
85 my ($class, $data) = @_;
86
87 die "please overwrite in subclass";
88 }
89
90 sub parse_config {
91 my ($class, $filename, $raw) = @_;
92
93 my $cfg = $class->SUPER::parse_config($filename, $raw);
94
95 my $target_hash = {};
96
97 foreach my $id (sort keys %{$cfg->{ids}}) {
98 my $data = $cfg->{ids}->{$id};
99
100 $data->{comment} = PVE::Tools::decode_text($data->{comment})
101 if defined($data->{comment});
102
103 my $plugin = $class->lookup($data->{type});
104 my $tid = $plugin->get_unique_target_id($data);
105 my $vmid = $data->{guest};
106
107 # should not happen, but we want to be sure
108 if (defined($target_hash->{$vmid}->{$tid})) {
109 warn "delete job $id: replication job for guest '$vmid' to target '$tid' already exists\n";
110 delete $cfg->{ids}->{$id};
111 }
112 $target_hash->{$vmid}->{$tid} = 1;
113 }
114
115 return $cfg;
116 }
117
118 sub write_config {
119 my ($class, $filename, $cfg) = @_;
120
121 my $target_hash = {};
122
123 foreach my $id (keys %{$cfg->{ids}}) {
124 my $data = $cfg->{ids}->{$id};
125
126 my $plugin = $class->lookup($data->{type});
127 my $tid = $plugin->get_unique_target_id($data);
128 my $vmid = $data->{guest};
129
130 die "replication job for guest '$vmid' to target '$tid' already exists\n"
131 if defined($target_hash->{$vmid}->{$tid});
132 $target_hash->{$vmid}->{$tid} = 1;
133
134 $data->{comment} = PVE::Tools::encode_text($data->{comment})
135 if defined($data->{comment});
136 }
137
138 $class->SUPER::write_config($filename, $cfg);
139 }
140
141 sub new {
142 my ($type) = @_;
143
144 my $class = ref($type) || $type;
145
146 my $cfg = cfs_read_file($replication_cfg_filename);
147
148 return bless $cfg, $class;
149 }
150
151 sub write {
152 my ($cfg) = @_;
153
154 cfs_write_file($replication_cfg_filename, $cfg);
155 }
156
157 sub lock {
158 my ($code, $errmsg) = @_;
159
160 cfs_lock_file($replication_cfg_filename, undef, $code);
161 my $err = $@;
162 if ($err) {
163 $errmsg ? die "$errmsg: $err" : die $err;
164 }
165 }
166
167
168 package PVE::ReplicationConfig::Cluster;
169
170 use base qw(PVE::ReplicationConfig);
171
172 sub type {
173 return 'local';
174 }
175
176 sub properties {
177 return {
178 target => {
179 description => "Target node.",
180 type => 'string', format => 'pve-node',
181 },
182 };
183 }
184
185 sub options {
186 return {
187 guest => { fixed => 1, optional => 0 },
188 target => { fixed => 1, optional => 0 },
189 disable => { optional => 1 },
190 comment => { optional => 1 },
191 rate => { optional => 1 },
192 schedule => { optional => 1 },
193 };
194 }
195
196 sub get_unique_target_id {
197 my ($class, $data) = @_;
198
199 return "local/$data->{target}";
200 }
201
202 PVE::ReplicationConfig::Cluster->register();
203 PVE::ReplicationConfig->init();
204
205 1;