]>
Commit | Line | Data |
---|---|---|
c33dd818 AD |
1 | package PVE::Network::SDN::Subnets; |
2 | ||
3 | use strict; | |
4 | use warnings; | |
5 | ||
70b03506 | 6 | use Net::Subnet qw(subnet_matcher); |
ee4f339e | 7 | use Net::IP; |
aba0731c | 8 | use NetAddr::IP qw(:lower); |
c33dd818 | 9 | |
d1ab9bdb | 10 | use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file); |
ee4f339e | 11 | use PVE::Network::SDN::Dns; |
d1ab9bdb TL |
12 | use PVE::Network::SDN::Ipams; |
13 | ||
c33dd818 AD |
14 | use PVE::Network::SDN::SubnetPlugin; |
15 | PVE::Network::SDN::SubnetPlugin->register(); | |
16 | PVE::Network::SDN::SubnetPlugin->init(); | |
17 | ||
18 | sub sdn_subnets_config { | |
19 | my ($cfg, $id, $noerr) = @_; | |
20 | ||
21 | die "no sdn subnet ID specified\n" if !$id; | |
22 | ||
23 | my $scfg = $cfg->{ids}->{$id}; | |
24 | die "sdn subnet '$id' does not exist\n" if (!$noerr && !$scfg); | |
25 | ||
e8736dac AD |
26 | if($scfg) { |
27 | my ($zone, $network, $mask) = split(/-/, $id); | |
28 | $scfg->{cidr} = "$network/$mask"; | |
29 | $scfg->{zone} = $zone; | |
30 | $scfg->{network} = $network; | |
31 | $scfg->{mask} = $mask; | |
32 | } | |
33 | ||
c33dd818 AD |
34 | return $scfg; |
35 | } | |
36 | ||
37 | sub config { | |
38 | my $config = cfs_read_file("sdn/subnets.cfg"); | |
39 | } | |
40 | ||
41 | sub write_config { | |
42 | my ($cfg) = @_; | |
43 | ||
44 | cfs_write_file("sdn/subnets.cfg", $cfg); | |
45 | } | |
46 | ||
47 | sub sdn_subnets_ids { | |
48 | my ($cfg) = @_; | |
49 | ||
b184ebc3 | 50 | return sort keys %{$cfg->{ids}}; |
c33dd818 AD |
51 | } |
52 | ||
53 | sub complete_sdn_subnet { | |
54 | my ($cmdname, $pname, $cvalue) = @_; | |
55 | ||
56 | my $cfg = PVE::Network::SDN::Subnets::config(); | |
57 | ||
58 | return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Subnets::sdn_subnets_ids($cfg) ]; | |
59 | } | |
60 | ||
61 | sub get_subnet { | |
5d3e0248 AD |
62 | my ($subnetid, $running) = @_; |
63 | ||
64 | my $cfg = {}; | |
65 | if($running) { | |
d73c7c36 | 66 | my $cfg = PVE::Network::SDN::running_config(); |
5d3e0248 AD |
67 | $cfg = $cfg->{subnets}; |
68 | } else { | |
69 | $cfg = PVE::Network::SDN::Subnets::config(); | |
70 | } | |
c33dd818 | 71 | |
c33dd818 AD |
72 | my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1); |
73 | return $subnet; | |
74 | } | |
75 | ||
70b03506 | 76 | sub find_ip_subnet { |
e8736dac | 77 | my ($ip, $mask, $subnets) = @_; |
70b03506 AD |
78 | |
79 | my $subnet = undef; | |
80 | my $subnetid = undef; | |
81 | ||
e612faf6 | 82 | foreach my $id (sort keys %{$subnets}) { |
e8736dac AD |
83 | |
84 | next if $mask ne $subnets->{$id}->{mask}; | |
85 | my $cidr = $subnets->{$id}->{cidr}; | |
e612faf6 AD |
86 | my $subnet_matcher = subnet_matcher($cidr); |
87 | next if !$subnet_matcher->($ip); | |
88 | $subnet = $subnets->{$id}; | |
89 | $subnetid = $id; | |
90 | last; | |
70b03506 AD |
91 | } |
92 | die "can't find any subnet for ip $ip" if !$subnet; | |
93 | ||
94 | return ($subnetid, $subnet); | |
95 | } | |
96 | ||
b61e93a5 | 97 | sub verify_dns_zone { |
ee4f339e AD |
98 | my ($zone, $dns) = @_; |
99 | ||
100 | return if !$zone || !$dns; | |
101 | ||
102 | my $dns_cfg = PVE::Network::SDN::Dns::config(); | |
103 | my $plugin_config = $dns_cfg->{ids}->{$dns}; | |
104 | my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type}); | |
105 | $plugin->verify_zone($plugin_config, $zone); | |
b61e93a5 | 106 | } |
ee4f339e | 107 | |
b61e93a5 | 108 | sub get_reversedns_zone { |
e8736dac | 109 | my ($subnetid, $subnet, $dns, $ip) = @_; |
4ad78442 AD |
110 | |
111 | return if !$subnetid || !$dns || !$ip; | |
112 | ||
113 | my $dns_cfg = PVE::Network::SDN::Dns::config(); | |
114 | my $plugin_config = $dns_cfg->{ids}->{$dns}; | |
115 | my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type}); | |
e8736dac | 116 | $plugin->get_reversedns_zone($plugin_config, $subnetid, $subnet, $ip); |
b61e93a5 | 117 | } |
4ad78442 | 118 | |
b61e93a5 | 119 | sub add_dns_record { |
ceb972a9 | 120 | my ($zone, $dns, $hostname, $ip) = @_; |
ee4f339e AD |
121 | return if !$zone || !$dns || !$hostname || !$ip; |
122 | ||
ee4f339e AD |
123 | my $dns_cfg = PVE::Network::SDN::Dns::config(); |
124 | my $plugin_config = $dns_cfg->{ids}->{$dns}; | |
125 | my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type}); | |
126 | $plugin->add_a_record($plugin_config, $zone, $hostname, $ip); | |
127 | ||
b61e93a5 | 128 | } |
ee4f339e | 129 | |
b61e93a5 | 130 | sub add_dns_ptr_record { |
ceb972a9 | 131 | my ($reversezone, $zone, $dns, $hostname, $ip) = @_; |
ee4f339e AD |
132 | |
133 | return if !$zone || !$reversezone || !$dns || !$hostname || !$ip; | |
134 | ||
ee4f339e AD |
135 | $hostname .= ".$zone"; |
136 | my $dns_cfg = PVE::Network::SDN::Dns::config(); | |
137 | my $plugin_config = $dns_cfg->{ids}->{$dns}; | |
138 | my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type}); | |
139 | $plugin->add_ptr_record($plugin_config, $reversezone, $hostname, $ip); | |
b61e93a5 | 140 | } |
ee4f339e | 141 | |
b61e93a5 | 142 | sub del_dns_record { |
ceb972a9 | 143 | my ($zone, $dns, $hostname, $ip) = @_; |
ee4f339e AD |
144 | |
145 | return if !$zone || !$dns || !$hostname || !$ip; | |
146 | ||
ee4f339e AD |
147 | my $dns_cfg = PVE::Network::SDN::Dns::config(); |
148 | my $plugin_config = $dns_cfg->{ids}->{$dns}; | |
149 | my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type}); | |
150 | $plugin->del_a_record($plugin_config, $zone, $hostname, $ip); | |
b61e93a5 | 151 | } |
ee4f339e | 152 | |
b61e93a5 | 153 | sub del_dns_ptr_record { |
ee4f339e AD |
154 | my ($reversezone, $dns, $ip) = @_; |
155 | ||
156 | return if !$reversezone || !$dns || !$ip; | |
157 | ||
158 | my $dns_cfg = PVE::Network::SDN::Dns::config(); | |
159 | my $plugin_config = $dns_cfg->{ids}->{$dns}; | |
160 | my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type}); | |
161 | $plugin->del_ptr_record($plugin_config, $reversezone, $ip); | |
b61e93a5 | 162 | } |
ee4f339e | 163 | |
77ec7eb2 AD |
164 | sub add_subnet { |
165 | my ($zone, $subnetid, $subnet) = @_; | |
166 | ||
167 | my $ipam = $zone->{ipam}; | |
d6557a2d | 168 | return if !$ipam; |
77ec7eb2 AD |
169 | my $ipam_cfg = PVE::Network::SDN::Ipams::config(); |
170 | my $plugin_config = $ipam_cfg->{ids}->{$ipam}; | |
171 | my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); | |
172 | $plugin->add_subnet($plugin_config, $subnetid, $subnet); | |
173 | } | |
174 | ||
175 | sub del_subnet { | |
176 | my ($zone, $subnetid, $subnet) = @_; | |
177 | ||
178 | my $ipam = $zone->{ipam}; | |
d6557a2d | 179 | return if !$ipam; |
77ec7eb2 AD |
180 | my $ipam_cfg = PVE::Network::SDN::Ipams::config(); |
181 | my $plugin_config = $ipam_cfg->{ids}->{$ipam}; | |
182 | my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); | |
183 | $plugin->del_subnet($plugin_config, $subnetid, $subnet); | |
184 | } | |
185 | ||
70b03506 | 186 | sub next_free_ip { |
83dcfd57 | 187 | my ($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns) = @_; |
ee4f339e AD |
188 | |
189 | my $cidr = undef; | |
190 | my $ip = undef; | |
ceb972a9 | 191 | $description = '' if !$description; |
70b03506 | 192 | |
331e2330 | 193 | my $ipamid = $zone->{ipam}; |
4ad78442 AD |
194 | my $dns = $zone->{dns}; |
195 | my $dnszone = $zone->{dnszone}; | |
196 | my $reversedns = $zone->{reversedns}; | |
ee4f339e AD |
197 | my $dnszoneprefix = $subnet->{dnszoneprefix}; |
198 | ||
ceb972a9 AD |
199 | $hostname .= ".$dnszoneprefix" if $dnszoneprefix; |
200 | ||
ee4f339e | 201 | #verify dns zones before ipam |
83dcfd57 | 202 | verify_dns_zone($dnszone, $dns) if !$skipdns; |
ee4f339e AD |
203 | |
204 | if($ipamid) { | |
205 | my $ipam_cfg = PVE::Network::SDN::Ipams::config(); | |
206 | my $plugin_config = $ipam_cfg->{ids}->{$ipamid}; | |
207 | my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); | |
e612faf6 | 208 | eval { |
e9365ab0 | 209 | $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $description); |
e612faf6 AD |
210 | ($ip, undef) = split(/\//, $cidr); |
211 | }; | |
212 | die $@ if $@; | |
ee4f339e | 213 | } |
70b03506 | 214 | |
ee4f339e | 215 | eval { |
b61e93a5 | 216 | my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip); |
4ad78442 | 217 | |
83dcfd57 AD |
218 | if(!$skipdns) { |
219 | #add dns | |
220 | add_dns_record($dnszone, $dns, $hostname, $ip); | |
221 | #add reverse dns | |
222 | add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip); | |
223 | } | |
ee4f339e AD |
224 | }; |
225 | if ($@) { | |
226 | #rollback | |
227 | my $err = $@; | |
228 | eval { | |
0720c17e | 229 | PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname) |
ee4f339e AD |
230 | }; |
231 | die $err; | |
232 | } | |
233 | return $cidr; | |
70b03506 AD |
234 | } |
235 | ||
236 | sub add_ip { | |
83dcfd57 | 237 | my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $skipdns) = @_; |
70b03506 | 238 | |
b184ebc3 | 239 | return if !$subnet || !$ip; |
e612faf6 | 240 | |
533eb3d4 | 241 | my $ipaddr = NetAddr::IP->new($ip); |
aba0731c AD |
242 | $ip = $ipaddr->canon(); |
243 | ||
331e2330 | 244 | my $ipamid = $zone->{ipam}; |
4ad78442 AD |
245 | my $dns = $zone->{dns}; |
246 | my $dnszone = $zone->{dnszone}; | |
247 | my $reversedns = $zone->{reversedns}; | |
b61e93a5 | 248 | my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip); |
ee4f339e AD |
249 | my $dnszoneprefix = $subnet->{dnszoneprefix}; |
250 | ||
ceb972a9 AD |
251 | $hostname .= ".$dnszoneprefix" if $dnszoneprefix; |
252 | ||
ee4f339e | 253 | #verify dns zones before ipam |
83dcfd57 AD |
254 | if(!$skipdns) { |
255 | verify_dns_zone($dnszone, $dns); | |
256 | verify_dns_zone($reversednszone, $reversedns); | |
257 | } | |
ee4f339e AD |
258 | |
259 | if ($ipamid) { | |
5221635a | 260 | |
ee4f339e AD |
261 | my $ipam_cfg = PVE::Network::SDN::Ipams::config(); |
262 | my $plugin_config = $ipam_cfg->{ids}->{$ipamid}; | |
263 | my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); | |
5221635a | 264 | |
e612faf6 | 265 | eval { |
34c4c6d7 | 266 | $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway); |
e612faf6 AD |
267 | }; |
268 | die $@ if $@; | |
ee4f339e | 269 | } |
70b03506 | 270 | |
ee4f339e | 271 | eval { |
83dcfd57 AD |
272 | if(!$skipdns) { |
273 | #add dns | |
274 | add_dns_record($dnszone, $dns, $hostname, $ip); | |
275 | #add reverse dns | |
276 | add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip); | |
277 | } | |
ee4f339e AD |
278 | }; |
279 | if ($@) { | |
280 | #rollback | |
281 | my $err = $@; | |
282 | eval { | |
0720c17e | 283 | PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname) |
ee4f339e AD |
284 | }; |
285 | die $err; | |
286 | } | |
70b03506 AD |
287 | } |
288 | ||
dd54b5a3 | 289 | sub update_ip { |
83dcfd57 | 290 | my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns) = @_; |
dd54b5a3 AD |
291 | |
292 | return if !$subnet || !$ip; | |
293 | ||
533eb3d4 | 294 | my $ipaddr = NetAddr::IP->new($ip); |
dd54b5a3 AD |
295 | $ip = $ipaddr->canon(); |
296 | ||
297 | my $ipamid = $zone->{ipam}; | |
298 | my $dns = $zone->{dns}; | |
299 | my $dnszone = $zone->{dnszone}; | |
300 | my $reversedns = $zone->{reversedns}; | |
b61e93a5 | 301 | my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip); |
dd54b5a3 AD |
302 | my $dnszoneprefix = $subnet->{dnszoneprefix}; |
303 | ||
304 | $hostname .= ".$dnszoneprefix" if $dnszoneprefix; | |
305 | ||
306 | #verify dns zones before ipam | |
83dcfd57 AD |
307 | if(!$skipdns) { |
308 | verify_dns_zone($dnszone, $dns); | |
309 | verify_dns_zone($reversednszone, $reversedns); | |
310 | } | |
dd54b5a3 AD |
311 | |
312 | if ($ipamid) { | |
313 | my $ipam_cfg = PVE::Network::SDN::Ipams::config(); | |
314 | my $plugin_config = $ipam_cfg->{ids}->{$ipamid}; | |
315 | my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); | |
316 | eval { | |
317 | $plugin->update_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description); | |
318 | }; | |
319 | die $@ if $@; | |
320 | } | |
321 | ||
0d2396b0 AD |
322 | return if $hostname eq $oldhostname; |
323 | ||
dd54b5a3 | 324 | eval { |
83dcfd57 AD |
325 | if(!$skipdns) { |
326 | #add dns | |
327 | del_dns_record($dnszone, $dns, $oldhostname, $ip); | |
328 | add_dns_record($dnszone, $dns, $hostname, $ip); | |
329 | #add reverse dns | |
330 | del_dns_ptr_record($reversednszone, $reversedns, $ip); | |
331 | add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip); | |
332 | } | |
dd54b5a3 AD |
333 | }; |
334 | } | |
335 | ||
70b03506 | 336 | sub del_ip { |
83dcfd57 | 337 | my ($zone, $subnetid, $subnet, $ip, $hostname, $skipdns) = @_; |
70b03506 | 338 | |
aba0731c AD |
339 | return if !$subnet || !$ip; |
340 | ||
533eb3d4 | 341 | my $ipaddr = NetAddr::IP->new($ip); |
aba0731c | 342 | $ip = $ipaddr->canon(); |
e612faf6 | 343 | |
331e2330 | 344 | my $ipamid = $zone->{ipam}; |
4ad78442 AD |
345 | my $dns = $zone->{dns}; |
346 | my $dnszone = $zone->{dnszone}; | |
347 | my $reversedns = $zone->{reversedns}; | |
b61e93a5 | 348 | my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip); |
ee4f339e | 349 | my $dnszoneprefix = $subnet->{dnszoneprefix}; |
ceb972a9 AD |
350 | $hostname .= ".$dnszoneprefix" if $dnszoneprefix; |
351 | ||
83dcfd57 AD |
352 | if(!$skipdns) { |
353 | verify_dns_zone($dnszone, $dns); | |
354 | verify_dns_zone($reversednszone, $reversedns); | |
355 | } | |
ee4f339e AD |
356 | |
357 | if ($ipamid) { | |
358 | my $ipam_cfg = PVE::Network::SDN::Ipams::config(); | |
359 | my $plugin_config = $ipam_cfg->{ids}->{$ipamid}; | |
360 | my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type}); | |
e8736dac | 361 | $plugin->del_ip($plugin_config, $subnetid, $subnet, $ip); |
ee4f339e | 362 | } |
70b03506 | 363 | |
ee4f339e | 364 | eval { |
83dcfd57 AD |
365 | if(!$skipdns) { |
366 | del_dns_record($dnszone, $dns, $hostname, $ip); | |
367 | del_dns_ptr_record($reversednszone, $reversedns, $ip); | |
368 | } | |
ee4f339e AD |
369 | }; |
370 | if ($@) { | |
371 | warn $@; | |
372 | } | |
70b03506 AD |
373 | } |
374 | ||
c33dd818 | 375 | 1; |