diff --git a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm index 18089b7..56a1787 100644 --- a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm +++ b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm @@ -25,6 +25,21 @@ sub options { }; } +sub netbox_api_request { + my ($config, $method, $path, $params) = @_; + + return PVE::Network::SDN::api_request( + $method, + "$config->{url}${path}", + [ + 'Content-Type' => 'application/json; charset=UTF-8', + 'Authorization' => "token $config->{token}" + ], + $params, + $config->{fingerprint}, + ); +} + # Plugin implementation sub add_subnet { @@ -32,62 +47,42 @@ sub add_subnet { my $cidr = $subnet->{cidr}; my $gateway = $subnet->{gateway}; - my $url = $plugin_config->{url}; - my $token = $plugin_config->{token}; - my $fingerprint = $plugin_config->{fingerprint}; - my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; - my $internalid = get_prefix_id($url, $cidr, $headers, $fingerprint); - - #create subnet - if (!$internalid) { - - my $params = { prefix => $cidr }; - - eval { - my $result = PVE::Network::SDN::api_request( - "POST", "$url/ipam/prefixes/", $headers, $params, $fingerprint ); - }; - if ($@) { - die "error add subnet to ipam: $@" if !$noerr; - } + if (get_prefix_id($plugin_config, $cidr, $noerr)) { + return if $noerr; + die "prefix $cidr already exists in netbox"; + } + + eval { + netbox_api_request($plugin_config, "POST", "/ipam/prefixes/", { + prefix => $cidr + }); + }; + if ($@) { + return if $noerr; + die "error adding subnet to ipam: $@"; } - } sub del_subnet { my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_; my $cidr = $subnet->{cidr}; - my $url = $plugin_config->{url}; - my $token = $plugin_config->{token}; - my $gateway = $subnet->{gateway}; - my $fingerprint = $plugin_config->{fingerprint}; - my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; - my $internalid = get_prefix_id($url, $cidr, $headers, $fingerprint); - return if !$internalid; + my $internalid = get_prefix_id($plugin_config, $cidr, $noerr); return; #fixme: check that prefix is empty exluding gateway, before delete eval { - PVE::Network::SDN::api_request( - "DELETE", "$url/ipam/prefixes/$internalid/", $headers, undef, $fingerprint); + netbox_api_request($plugin_config, "DELETE", "/ipam/prefixes/$internalid/"); }; - if ($@) { - die "error deleting subnet from ipam: $@" if !$noerr; - } - + die "error deleting subnet from ipam: $@" if $@ && !$noerr; } sub add_ip { my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_; my $mask = $subnet->{mask}; - my $url = $plugin_config->{url}; - my $token = $plugin_config->{token}; - my $fingerprint = $plugin_config->{fingerprint}; - my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; my $description = undef; if ($is_gateway) { @@ -96,18 +91,18 @@ sub add_ip { $description = "mac:$mac"; } - my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description }; - eval { - PVE::Network::SDN::api_request( - "POST", "$url/ipam/ip-addresses/", $headers, $params, $fingerprint); + netbox_api_request($plugin_config, "POST", "/ipam/ip-addresses/", { + address => "$ip/$mask", + dns_name => $hostname, + description => $description, + }); }; if ($@) { if ($is_gateway) { - if (!is_ip_gateway($url, $ip, $headers, $fingerprint) && !$noerr) { - die "error add subnet ip to ipam: ip $ip already exist: $@"; - } + die "error add subnet ip to ipam: ip $ip already exist: $@" + if !is_ip_gateway($plugin_config, $ip, $noerr); } elsif (!$noerr) { die "error add subnet ip to ipam: ip already exist: $@"; } @@ -118,11 +113,6 @@ sub update_ip { my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_; my $mask = $subnet->{mask}; - my $url = $plugin_config->{url}; - my $token = $plugin_config->{token}; - my $section = $plugin_config->{section}; - my $fingerprint = $plugin_config->{fingerprint}; - my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; my $description = undef; if ($is_gateway) { @@ -131,14 +121,20 @@ sub update_ip { $description = "mac:$mac"; } - my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description }; + my $ip_id = get_ip_id($plugin_config, $ip, $noerr); - my $ip_id = get_ip_id($url, $ip, $headers, $fingerprint); - die "can't find ip $ip in ipam" if !$ip_id; + # definedness check, because ID could be 0 + if (!defined($ip_id)) { + return if $noerr; + die "could not find id for ip address $ip"; + } eval { - PVE::Network::SDN::api_request( - "PATCH", "$url/ipam/ip-addresses/$ip_id/", $headers, $params, $fingerprint); + netbox_api_request($plugin_config, "PATCH", "/ipam/ip-addresses/$ip_id/", { + address => "$ip/$mask", + dns_name => $hostname, + description => $description, + }); }; if ($@) { die "error update ip $ip : $@" if !$noerr; @@ -150,20 +146,22 @@ sub add_next_freeip { my $cidr = $subnet->{cidr}; - my $url = $plugin_config->{url}; - my $token = $plugin_config->{token}; - my $fingerprint = $plugin_config->{fingerprint}; - my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; + my $internalid = get_prefix_id($plugin_config, $cidr, $noerr); - my $internalid = get_prefix_id($url, $cidr, $headers, $fingerprint); + # definedness check, because ID could be 0 + if (!defined($internalid)) { + return if $noerr; + die "could not find id for prefix $cidr"; + } my $description = "mac:$mac" if $mac; - my $params = { dns_name => $hostname, description => $description }; - eval { - my $result = PVE::Network::SDN::api_request( - "POST", "$url/ipam/prefixes/$internalid/available-ips/", $headers, $params, $fingerprint); + my $result = netbox_api_request($plugin_config, "POST", "/ipam/prefixes/$internalid/available-ips/", { + dns_name => $hostname, + description => $description, + }); + my ($ip, undef) = split(/\//, $result->{address}); return $ip; }; @@ -176,19 +174,22 @@ sub add_next_freeip { sub add_range_next_freeip { my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_; - my $url = $plugin_config->{url}; - my $token = $plugin_config->{token}; - my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; - my $fingerprint = $plugin_config->{fingerprint}; + my $internalid = get_iprange_id($plugin_config, $range, $noerr); + + # definedness check, because ID could be 0 + if (!defined($internalid)) { + return if $noerr; + die "could not find id for ip range $range->{'start-address'}:$range->{'end-address'}"; + } - my $internalid = get_iprange_id($url, $range, $headers, $fingerprint); my $description = "mac:$data->{mac}" if $data->{mac}; - my $params = { dns_name => $data->{hostname}, description => $description }; - eval { - my $result = PVE::Network::SDN::api_request( - "POST", "$url/ipam/ip-ranges/$internalid/available-ips/", $headers, $params, $fingerprint); + my $result = netbox_api_request($plugin_config, "POST", "/ipam/ip-ranges/$internalid/available-ips/", { + dns_name => $data->{hostname}, + description => $description, + }); + my ($ip, undef) = split(/\//, $result->{address}); print "found ip free $ip in range $range->{'start-address'}-$range->{'end-address'}\n" if $ip; return $ip; @@ -204,16 +205,14 @@ sub del_ip { return if !$ip; - my $url = $plugin_config->{url}; - my $token = $plugin_config->{token}; - my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; - my $fingerprint = $plugin_config->{fingerprint}; - - my $ip_id = get_ip_id($url, $ip, $headers, $fingerprint); - die "can't find ip $ip in ipam" if !$ip_id; + my $ip_id = get_ip_id($plugin_config, $ip, $noerr); + if (!defined($ip_id)) { + warn "could not find id for ip $ip"; + return; + } eval { - PVE::Network::SDN::api_request("DELETE", "$url/ipam/ip-addresses/$ip_id/", $headers, undef, $fingerprint); + netbox_api_request($plugin_config, "DELETE", "/ipam/ip-addresses/$ip_id/"); }; if ($@) { die "error delete ip $ip : $@" if !$noerr; @@ -221,18 +220,14 @@ sub del_ip { } sub get_ips_from_mac { - my ($class, $plugin_config, $mac, $zoneid) = @_; - - my $url = $plugin_config->{url}; - my $token = $plugin_config->{token}; - my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; - my $fingerprint = $plugin_config->{fingerprint}; + my ($class, $plugin_config, $mac, $zoneid, $noerr) = @_; my $ip4 = undef; my $ip6 = undef; - my $data = PVE::Network::SDN::api_request( - "GET", "$url/ipam/ip-addresses/?description__ic=$mac", $headers, undef, $fingerprint); + my $data = eval { + netbox_api_request($plugin_config, "GET", "/ipam/ip-addresses/?description__ic=$mac"); + }; for my $ip (@{$data->{results}}) { if ($ip->{family}->{value} == 4 && !$ip4) { @@ -247,18 +242,10 @@ sub get_ips_from_mac { return ($ip4, $ip6); } - sub verify_api { my ($class, $plugin_config) = @_; - my $url = $plugin_config->{url}; - my $token = $plugin_config->{token}; - my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"]; - my $fingerprint = $plugin_config->{fingerprint}; - - eval { - PVE::Network::SDN::api_request("GET", "$url/ipam/aggregates/", $headers, undef, $fingerprint); - }; + eval { netbox_api_request($plugin_config, "GET", "/ipam/aggregates/"); }; if ($@) { die "Can't connect to netbox api: $@"; } @@ -266,47 +253,71 @@ sub verify_api { sub on_update_hook { my ($class, $plugin_config) = @_; - - PVE::Network::SDN::Ipams::NetboxPlugin::verify_api($class, $plugin_config); + verify_api($class, $plugin_config); } -#helpers - +# helpers sub get_prefix_id { - my ($url, $cidr, $headers, $fingerprint) = @_; - my $result = PVE::Network::SDN::api_request( - "GET", "$url/ipam/prefixes/?q=$cidr", $headers, undef, $fingerprint); + my ($config, $cidr, $noerr) = @_; + + # we need to supply any IP inside the prefix, without supplying the mask, so + # just take the one from the cidr + my ($ip, undef) = split(/\//, $cidr); + + my $result = eval { netbox_api_request($config, "GET", "/ipam/prefixes/?q=$ip") }; + if ($@) { + return if $noerr; + die "could not obtain ID for prefix $cidr: $@"; + } + my $data = @{$result->{results}}[0]; my $internalid = $data->{id}; return $internalid; } sub get_iprange_id { - my ($url, $range, $headers, $fingerprint) = @_; - my $result = PVE::Network::SDN::api_request( - "GET", - "$url/ipam/ip-ranges/?start_address=$range->{'start-address'}&end_address=$range->{'end-address'}", - $headers, - undef, - $fingerprint - ); + my ($config, $range, $noerr) = @_; + + my $result = eval { + netbox_api_request( + $config, + "GET", + "/ipam/ip-ranges/?start_address=$range->{'start-address'}&end_address=$range->{'end-address'}", + ); + }; + if ($@) { + return if $noerr; + die "could not obtain ID for IP range $range->{'start-address'}:$range->{'end-address'}: $@"; + } + my $data = @{$result->{results}}[0]; my $internalid = $data->{id}; return $internalid; } sub get_ip_id { - my ($url, $ip, $headers, $fingerprint) = @_; - my $result = PVE::Network::SDN::api_request( - "GET", "$url/ipam/ip-addresses/?q=$ip", $headers, undef, $fingerprint); + my ($config, $ip, $noerr) = @_; + + my $result = eval { netbox_api_request($config, "GET", "/ipam/ip-addresses/?q=$ip") }; + if ($@) { + return if $noerr; + die "could not obtain ID for IP $ip: $@"; + } + my $data = @{$result->{results}}[0]; my $ip_id = $data->{id}; return $ip_id; } sub is_ip_gateway { - my ($url, $ip, $headers, $fingerprint) = @_; - my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers, undef, $fingerprint); + my ($config, $ip, $noerr) = @_; + + my $result = eval { netbox_api_request($config, "GET", "/ipam/ip-addresses/?q=$ip") }; + if ($@) { + return if $noerr; + die "could not obtain ipam entry for address $ip: $@"; + } + my $data = @{$result->{data}}[0]; my $description = $data->{description}; my $is_gateway = 1 if $description eq 'gateway';