]>
git.proxmox.com Git - pve-network.git/blob - src/PVE/Network/SDN/Ipams/PVEPlugin.pm
1 package PVE
::Network
::SDN
::Ipams
::PVEPlugin
;
6 use PVE
::Cluster
qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file);
9 use NetAddr
::IP
qw(:lower);
14 use base
('PVE::Network::SDN::Ipams::Plugin');
17 my $ipamdb_file = "priv/ipam.db";
19 PVE
::Cluster
::cfs_register_file
($ipamdb_file,
20 sub { PVE
::Network
::SDN
::Ipams
::PVEPlugin-
>parse_config(@_); },
21 sub { PVE
::Network
::SDN
::Ipams
::PVEPlugin-
>write_config(@_); });
33 # Plugin implementation
36 my ($class, $plugin_config, $subnetid, $subnet) = @_;
38 my $cidr = $subnet->{cidr
};
39 my $zone = $subnet->{zone
};
40 my $gateway = $subnet->{gateway
};
43 cfs_lock_file
($ipamdb_file, undef, sub {
47 $db->{zones
}->{$zone} = {} if !$db->{zones
}->{$zone};
48 my $zonedb = $db->{zones
}->{$zone};
50 if(!$zonedb->{subnets
}->{$cidr}) {
52 $zonedb->{subnets
}->{$cidr}->{ips
} = {};
59 sub only_gateway_remains
{
62 if (keys %{$ips} == 1 &&
63 (values %{$ips})[0]->{gateway
} == 1) {
70 my ($class, $plugin_config, $subnetid, $subnet) = @_;
72 my $cidr = $subnet->{cidr
};
73 my $zone = $subnet->{zone
};
75 cfs_lock_file
($ipamdb_file, undef, sub {
79 my $dbzone = $db->{zones
}->{$zone};
80 die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
81 my $dbsubnet = $dbzone->{subnets
}->{$cidr};
82 die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
84 my $ips = $dbsubnet->{ips
};
86 if (keys %{$ips} > 0 && !only_gateway_remains
($ips)) {
87 die "cannot delete subnet '$cidr', not empty\n";
90 delete $dbzone->{subnets
}->{$cidr};
99 my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway) = @_;
101 my $cidr = $subnet->{cidr
};
102 my $zone = $subnet->{zone
};
104 cfs_lock_file
($ipamdb_file, undef, sub {
107 my $dbzone = $db->{zones
}->{$zone};
108 die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
109 my $dbsubnet = $dbzone->{subnets
}->{$cidr};
110 die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
112 die "IP '$ip' already exist\n" if (!$is_gateway && defined($dbsubnet->{ips
}->{$ip})) || ($is_gateway && defined($dbsubnet->{ips
}->{$ip}) && !defined($dbsubnet->{ips
}->{$ip}->{gateway
}));
116 $data->{gateway
} = 1;
118 $data->{vmid
} = $vmid if $vmid;
119 $data->{hostname
} = $hostname if $hostname;
120 $data->{mac
} = $mac if $mac;
123 $dbsubnet->{ips
}->{$ip} = $data;
131 my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway) = @_;
135 sub add_next_freeip
{
136 my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid, $noerr) = @_;
138 my $cidr = $subnet->{cidr
};
139 my $network = $subnet->{network
};
140 my $zone = $subnet->{zone
};
141 my $mask = $subnet->{mask
};
144 cfs_lock_file
($ipamdb_file, undef, sub {
147 my $dbzone = $db->{zones
}->{$zone};
148 die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
149 my $dbsubnet = $dbzone->{subnets
}->{$cidr};
150 die "subnet '$cidr' doesn't exist in IPAM DB" if !$dbsubnet;
152 if (Net
::IP
::ip_is_ipv4
($network) && $mask == 32) {
153 die "cannot find free IP in subnet '$cidr'\n" if defined($dbsubnet->{ips
}->{$network});
156 my $iplist = NetAddr
::IP-
>new($cidr);
157 my $lastip = $iplist->last()->canon();
158 $iplist++ if Net
::IP
::ip_is_ipv4
($network); #skip network address for ipv4
160 my $ip = $iplist->canon();
161 if (defined($dbsubnet->{ips
}->{$ip})) {
162 last if $ip eq $lastip;
171 die "can't find free ip in subnet '$cidr'\n" if !$freeip;
173 $dbsubnet->{ips
}->{$freeip} = {};
182 sub add_range_next_freeip
{
183 my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
185 my $cidr = $subnet->{cidr
};
186 my $zone = $subnet->{zone
};
188 cfs_lock_file
($ipamdb_file, undef, sub {
191 my $dbzone = $db->{zones
}->{$zone};
192 die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
194 my $dbsubnet = $dbzone->{subnets
}->{$cidr};
195 die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
197 my $ip = new Net
::IP
("$range->{'start-address'} - $range->{'end-address'}")
198 or die "Invalid IP address(es) in Range!\n";
199 my $mac = $data->{mac
};
202 my $ip_address = $ip->version() == 6 ?
$ip->short() : $ip->ip();
203 if (!$dbsubnet->{ips
}->{$ip_address}) {
204 $dbsubnet->{ips
}->{$ip_address} = $data;
211 die "No free IP left in Range $range->{'start-address'}:$range->{'end-address'}}\n";
216 my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
218 my $cidr = $subnet->{cidr
};
219 my $zone = $subnet->{zone
};
221 cfs_lock_file
($ipamdb_file, undef, sub {
224 die "zone $zone don't exist in ipam db" if !$db->{zones
}->{$zone};
225 my $dbzone = $db->{zones
}->{$zone};
226 die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets
}->{$cidr};
227 my $dbsubnet = $dbzone->{subnets
}->{$cidr};
229 die "IP '$ip' does not exist in IPAM DB\n" if !defined($dbsubnet->{ips
}->{$ip});
230 delete $dbsubnet->{ips
}->{$ip};
236 sub get_ips_from_mac
{
237 my ($class, $plugin_config, $mac, $zoneid) = @_;
239 #just in case, as this should already be cached in local macs.db
245 die "zone $zoneid don't exist in ipam db" if !$db->{zones
}->{$zoneid};
246 my $dbzone = $db->{zones
}->{$zoneid};
247 my $subnets = $dbzone->{subnets
};
249 for my $subnet ( keys %$subnets) {
250 next if Net
::IP
::ip_is_ipv4
($subnet) && $ip4;
252 my $ips = $subnets->{$subnet}->{ips
};
253 for my $ip (keys %$ips) {
254 my $ipobject = $ips->{$ip};
255 if ($ipobject->{mac
} && $ipobject->{mac
} eq $mac) {
256 if (Net
::IP
::ip_is_ipv4
($ip)) {
263 last if $ip4 && $ip6;
271 my $db = cfs_read_file
($ipamdb_file);
278 my $json = to_json
($cfg);
279 cfs_write_file
($ipamdb_file, $json);
283 my ($class, $filename, $cfg) = @_;
289 my ($class, $filename, $raw) = @_;
291 $raw = '{}' if !defined($raw) ||$raw eq '';
292 my $cfg = from_json
($raw);