diff options
Diffstat (limited to 'src/PVE/Network/SDN/Ipams/PVEPlugin.pm')
| -rw-r--r-- | src/PVE/Network/SDN/Ipams/PVEPlugin.pm | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/PVE/Network/SDN/Ipams/PVEPlugin.pm b/src/PVE/Network/SDN/Ipams/PVEPlugin.pm new file mode 100644 index 0000000..3e8ffc5 --- /dev/null +++ b/src/PVE/Network/SDN/Ipams/PVEPlugin.pm @@ -0,0 +1,210 @@ +package PVE::Network::SDN::Ipams::PVEPlugin; + +use strict; +use warnings; +use PVE::INotify; +use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file); +use PVE::Tools; +use JSON; +use NetAddr::IP qw(:lower); + +use Net::IP; +use Digest::SHA; + +use base('PVE::Network::SDN::Ipams::Plugin'); + + +my $ipamdb_file = "priv/ipam.db"; + +PVE::Cluster::cfs_register_file($ipamdb_file, + sub { PVE::Network::SDN::Ipams::PVEPlugin->parse_config(@_); }, + sub { PVE::Network::SDN::Ipams::PVEPlugin->write_config(@_); }); + +sub type { + return 'pve'; +} + +sub properties { +} + +sub options { +} + +# Plugin implementation + +sub add_subnet { + my ($class, $plugin_config, $subnetid, $subnet) = @_; + + my $cidr = $subnet->{cidr}; + my $zone = $subnet->{zone}; + my $gateway = $subnet->{gateway}; + + + cfs_lock_file($ipamdb_file, undef, sub { + my $db = {}; + $db = read_db(); + + $db->{zones}->{$zone} = {} if !$db->{zones}->{$zone}; + my $zonedb = $db->{zones}->{$zone}; + + if(!$zonedb->{subnets}->{$cidr}) { + #create subnet + $zonedb->{subnets}->{$cidr}->{ips} = {}; + write_db($db); + } + }); + die "$@" if $@; +} + +sub del_subnet { + my ($class, $plugin_config, $subnetid, $subnet) = @_; + + my $cidr = $subnet->{cidr}; + my $zone = $subnet->{zone}; + + cfs_lock_file($ipamdb_file, undef, sub { + + my $db = read_db(); + + my $dbzone = $db->{zones}->{$zone}; + die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone; + my $dbsubnet = $dbzone->{subnets}->{$cidr}; + die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet; + + die "cannot delete subnet '$cidr', not empty\n" if keys %{$dbsubnet->{ips}} > 0; + + delete $dbzone->{subnets}->{$cidr}; + + write_db($db); + }); + die "$@" if $@; + +} + +sub add_ip { + my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_; + + my $cidr = $subnet->{cidr}; + my $zone = $subnet->{zone}; + + cfs_lock_file($ipamdb_file, undef, sub { + + my $db = read_db(); + my $dbzone = $db->{zones}->{$zone}; + die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone; + my $dbsubnet = $dbzone->{subnets}->{$cidr}; + die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet; + + die "IP '$ip' already exist\n" if (!$is_gateway && defined($dbsubnet->{ips}->{$ip})) || ($is_gateway && defined($dbsubnet->{ips}->{$ip}) && !defined($dbsubnet->{ips}->{$ip}->{gateway})); + $dbsubnet->{ips}->{$ip} = {}; + $dbsubnet->{ips}->{$ip} = {gateway => 1} if $is_gateway; + + write_db($db); + }); + die "$@" if $@; +} + +sub update_ip { + my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_; + return; +} + +sub add_next_freeip { + my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description) = @_; + + my $cidr = $subnet->{cidr}; + my $network = $subnet->{network}; + my $zone = $subnet->{zone}; + my $mask = $subnet->{mask}; + my $freeip = undef; + + cfs_lock_file($ipamdb_file, undef, sub { + + my $db = read_db(); + my $dbzone = $db->{zones}->{$zone}; + die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone; + my $dbsubnet = $dbzone->{subnets}->{$cidr}; + die "subnet '$cidr' doesn't exist in IPAM DB" if !$dbsubnet; + + if (Net::IP::ip_is_ipv4($network) && $mask == 32) { + die "cannot find free IP in subnet '$cidr'\n" if defined($dbsubnet->{ips}->{$network}); + $freeip = $network; + } else { + my $iplist = NetAddr::IP->new($cidr); + my $lastip = $iplist->last()->canon(); + $iplist++ if Net::IP::ip_is_ipv4($network); #skip network address for ipv4 + while(1) { + my $ip = $iplist->canon(); + if (defined($dbsubnet->{ips}->{$ip})) { + last if $ip eq $lastip; + $iplist++; + next; + } + $freeip = $ip; + last; + } + } + + die "can't find free ip in subnet '$cidr'\n" if !$freeip; + + $dbsubnet->{ips}->{$freeip} = {}; + + write_db($db); + }); + die "$@" if $@; + + return "$freeip/$mask"; +} + +sub del_ip { + my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_; + + my $cidr = $subnet->{cidr}; + my $zone = $subnet->{zone}; + + cfs_lock_file($ipamdb_file, undef, sub { + + my $db = read_db(); + die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone}; + my $dbzone = $db->{zones}->{$zone}; + die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr}; + my $dbsubnet = $dbzone->{subnets}->{$cidr}; + + die "IP '$ip' does not exist in IPAM DB\n" if !defined($dbsubnet->{ips}->{$ip}); + delete $dbsubnet->{ips}->{$ip}; + + write_db($db); + }); + die "$@" if $@; +} + +#helpers + +sub read_db { + my $db = cfs_read_file($ipamdb_file); + return $db; +} + +sub write_db { + my ($cfg) = @_; + + my $json = to_json($cfg); + cfs_write_file($ipamdb_file, $json); +} + +sub write_config { + my ($class, $filename, $cfg) = @_; + + return $cfg; +} + +sub parse_config { + my ($class, $filename, $raw) = @_; + + $raw = '{}' if !defined($raw) ||$raw eq ''; + my $cfg = from_json($raw); + + return $cfg; +} + +1; |
