From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9]) by lore.proxmox.com (Postfix) with ESMTPS id 3CDF91FF15C for ; Wed, 8 Jan 2025 13:10:16 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 4BCB7186D8; Wed, 8 Jan 2025 13:09:56 +0100 (CET) To: pve-devel@lists.proxmox.com Date: Wed, 8 Jan 2025 13:08:53 +0100 In-Reply-To: <20250108120903.5344-1-lou.lecrivain@wdz.de> References: <20250108120903.5344-1-lou.lecrivain@wdz.de> MIME-Version: 1.0 Message-ID: List-Id: Proxmox VE development discussion List-Post: From: Lou Lecrivain via pve-devel Precedence: list Cc: Lou Lecrivain X-Mailman-Version: 2.1.29 X-BeenThere: pve-devel@lists.proxmox.com List-Subscribe: , List-Unsubscribe: , List-Archive: Reply-To: Proxmox VE development discussion List-Help: Subject: [pve-devel] SPAM: [PATCH pve-network v2 1/7] ipam: nautobot support initial commit Content-Type: multipart/mixed; boundary="===============2320909529334645294==" Errors-To: pve-devel-bounces@lists.proxmox.com Sender: "pve-devel" --===============2320909529334645294== Content-Type: message/rfc822 Content-Disposition: inline Return-Path: X-Original-To: pve-devel@lists.proxmox.com Delivered-To: pve-devel@lists.proxmox.com Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 093BBCB022 for ; Wed, 8 Jan 2025 13:09:55 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id D7EC518375 for ; Wed, 8 Jan 2025 13:09:24 +0100 (CET) Received: from smtp.smtpout.orange.fr (smtp-78.smtpout.orange.fr [80.12.242.78]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS for ; Wed, 8 Jan 2025 13:09:23 +0100 (CET) Received: from localhost ([176.139.8.107]) by smtp.orange.fr with ESMTPA id VUs5tPuffJiySVUs8tUj4A; Wed, 08 Jan 2025 13:09:17 +0100 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=orange.fr; s=t20230301; t=1736338157; bh=Fsbn2tYhHOyZvUjCYbfRGhnmE4sO6lPm/3YliFVmzBI=; h=From:To:Subject:Date:Message-Id:MIME-Version; b=A8WjhzRQHYSXXklBxFKTrTwp+SnvIlREB4S/iqtBo9GSYaknPS0T483xhxhfzxVmY FSKCZe3MVJ2kv62L/yGZWJnkKI19xHEUPI1RVBN0kl0lVpVegwA5myRzS38Pk8/YVN dnISiI32840HqASjGox9NM2QvqddmW06iXXMQpolREHgd2yFtyaR92Eg3VSvd03eEP acSWEcUVRiVZUf99582c0kTRPXffwm5b+GnKT6v9xAaZ6YUVRNPTBRo7EyaL4LYNHU S1oZQi0LZDAgp0p4ZE6eDnH+0WrfpT7M2NMFubSeIq/QKPU5oMZDgZlJLVGBzP0uOD MZu2O6ulh+ziA== X-ME-Helo: localhost X-ME-Auth: bG91LmxlY3JpdmFpbkBvcmFuZ2UuZnI= X-ME-Date: Wed, 08 Jan 2025 13:09:17 +0100 X-ME-IP: 176.139.8.107 From: Lou Lecrivain To: pve-devel@lists.proxmox.com subject: SPAM: [PATCH pve-network v2 1/7] ipam: nautobot support initial commit Date: Wed, 8 Jan 2025 13:08:53 +0100 Message-Id: <20250108120903.5344-2-lou.lecrivain@wdz.de> X-Mailer: git-send-email 2.39.5 In-Reply-To: <20250108120903.5344-1-lou.lecrivain@wdz.de> References: <20250108120903.5344-1-lou.lecrivain@wdz.de> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 4 AWL -0.094 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DKIM_SIGNED 0.1 Message has a DKIM or DK signature, not necessarily valid DKIM_VALID -0.1 Message has at least one valid DKIM or DK signature DKIM_VALID_EF -0.1 Message has a valid DKIM or DK signature from envelope-from domain DMARC_REJECT 0.1 DMARC reject policy FREEMAIL_FORGED_FROMDOMAIN 0.001 2nd level domains in From and EnvelopeFrom freemail headers are different FREEMAIL_FROM 0.001 Sender email is commonly abused enduser mail provider HEADER_FROM_DIFFERENT_DOMAINS 0.24 From and EnvelopeFrom 2nd level mail domains are different KAM_DMARC_REJECT 6 DKIM has Failed or SPF has failed on the message and the domain has a DMARC reject policy RCVD_IN_DNSWL_NONE -0.0001 Sender listed at https://www.dnswl.org/, no trust SPF_HELO_PASS -0.001 SPF: HELO matches SPF record SPF_PASS -0.001 SPF: sender matches SPF record This is the initial Nautobot plugin, based on the Netbox plugin implementation. Signed-off-by: lou lecrivain --- src/PVE/API2/Network/SDN/Ipams.pm | 1 + src/PVE/Network/SDN/Ipams.pm | 3 + src/PVE/Network/SDN/Ipams/Makefile | 2 +- src/PVE/Network/SDN/Ipams/NautobotPlugin.pm | 195 ++++++++++++++++++++ 4 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 src/PVE/Network/SDN/Ipams/NautobotPlugin.pm diff --git a/src/PVE/API2/Network/SDN/Ipams.pm b/src/PVE/API2/Network/SDN/Ipams.pm index 27ead02..8074512 100644 --- a/src/PVE/API2/Network/SDN/Ipams.pm +++ b/src/PVE/API2/Network/SDN/Ipams.pm @@ -12,6 +12,7 @@ use PVE::Network::SDN::Ipams::Plugin; use PVE::Network::SDN::Ipams::PVEPlugin; use PVE::Network::SDN::Ipams::PhpIpamPlugin; use PVE::Network::SDN::Ipams::NetboxPlugin; +use PVE::Network::SDN::Ipams::NautobotPlugin; use PVE::Network::SDN::Dhcp; use PVE::Network::SDN::Vnets; use PVE::Network::SDN::Zones; diff --git a/src/PVE/Network/SDN/Ipams.pm b/src/PVE/Network/SDN/Ipams.pm index c689b8f..2ecb75e 100644 --- a/src/PVE/Network/SDN/Ipams.pm +++ b/src/PVE/Network/SDN/Ipams.pm @@ -12,11 +12,14 @@ use PVE::Network; use PVE::Network::SDN::Ipams::PVEPlugin; use PVE::Network::SDN::Ipams::NetboxPlugin; +use PVE::Network::SDN::Ipams::NautobotPlugin; use PVE::Network::SDN::Ipams::PhpIpamPlugin; use PVE::Network::SDN::Ipams::Plugin; + PVE::Network::SDN::Ipams::PVEPlugin->register(); PVE::Network::SDN::Ipams::NetboxPlugin->register(); +PVE::Network::SDN::Ipams::NautobotPlugin->register(); PVE::Network::SDN::Ipams::PhpIpamPlugin->register(); PVE::Network::SDN::Ipams::Plugin->init(); diff --git a/src/PVE/Network/SDN/Ipams/Makefile b/src/PVE/Network/SDN/Ipams/Makefile index 4e7d65f..75e5b9a 100644 --- a/src/PVE/Network/SDN/Ipams/Makefile +++ b/src/PVE/Network/SDN/Ipams/Makefile @@ -1,4 +1,4 @@ -SOURCES=Plugin.pm PhpIpamPlugin.pm NetboxPlugin.pm PVEPlugin.pm +SOURCES=Plugin.pm PhpIpamPlugin.pm NetboxPlugin.pm PVEPlugin.pm NautobotPlugin.pm PERL5DIR=${DESTDIR}/usr/share/perl5 diff --git a/src/PVE/Network/SDN/Ipams/NautobotPlugin.pm b/src/PVE/Network/SDN/Ipams/NautobotPlugin.pm new file mode 100644 index 0000000..69e7897 --- /dev/null +++ b/src/PVE/Network/SDN/Ipams/NautobotPlugin.pm @@ -0,0 +1,195 @@ +package PVE::Network::SDN::Ipams::NautobotPlugin; + +use strict; +use warnings; +use PVE::INotify; +use PVE::Cluster; +use PVE::Tools; + +use base('PVE::Network::SDN::Ipams::NetboxPlugin'); + +sub type { + return 'nautobot'; +} + +sub properties { + return { + namespace => { + type => 'string', + }, + }; +} + +sub options { + return { + url => { optional => 0 }, + token => { optional => 0 }, + namespace => { optional => 0 }, + }; +} + +sub default_ip_status { + return 'Active'; +} + +sub default_headers { + my ($plugin_config) = @_; + my $token = $plugin_config->{token}; + + return ['Content-Type' => "application/json", 'Authorization' => "token $token", 'Accept' => "application/json"]; +} + +# implem + +sub add_subnet { + my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_; + + my $cidr = $subnet->{cidr}; + my $gateway = $subnet->{gateway}; + my $url = $plugin_config->{url}; + my $namespace = $plugin_config->{namespace}; + my $headers = default_headers($plugin_config); + + my $internalid = PVE::Network::SDN::Ipams::NetboxPlugin::get_prefix_id($url, $cidr, $headers); + + #create subnet + if (!$internalid) { + my $params = { prefix => $cidr, namespace => $namespace, status => default_ip_status()}; + + eval { + my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/prefixes/", $headers, $params); + }; + if ($@) { + die "error adding subnet to 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 $namespace = $plugin_config->{namespace}; + my $headers = default_headers($plugin_config); + + my $description = undef; + if ($is_gateway) { + $description = 'gateway' + } elsif ($mac) { + $description = "mac:$mac"; + } + + my $params = { address => "$ip/$mask", type => "dhcp", dns_name => $hostname, description => $description, namespace => $namespace, status => default_ip_status()}; + + eval { + PVE::Network::SDN::api_request("POST", "$url/ipam/ip-addresses/", $headers, $params); + }; + + if ($@) { + if($is_gateway) { + die "error adding subnet ip to ipam: ip $ip already exists: $@" if !PVE::Network::SDN::Ipams::NetboxPlugin::is_ip_gateway($url, $ip, $headers) && !$noerr; + } else { + die "error adding subnet ip to ipam: ip $ip already exists: $@" if !$noerr; + } + } +} + + +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 $namespace = $plugin_config->{namespace}; + my $headers = default_headers($plugin_config); + + my $description = undef; + if ($is_gateway) { + $description = 'gateway' + } elsif ($mac) { + $description = "mac:$mac"; + } + + my $params = { address => "$ip/$mask", type => "dhcp", dns_name => $hostname, description => $description, namespace => $namespace, status => default_ip_status()}; + + my $ip_id = PVE::Network::SDN::Ipams::NetboxPlugin::get_ip_id($url, $ip, $headers); + die "can't find ip $ip in ipam" if !$ip_id; + + eval { + PVE::Network::SDN::api_request("PATCH", "$url/ipam/ip-addresses/$ip_id/", $headers, $params); + }; + if ($@) { + die "error updating ip $ip: $@" if !$noerr; + } +} + + +sub verify_api { + my ($class, $plugin_config) = @_; + + my $url = $plugin_config->{url}; + my $namespace = $plugin_config->{namespace}; + my $headers = default_headers($plugin_config); + + # check that the namespace exists AND that default IP active status + # exists AND that we have indeed API access + eval { + get_namespace_id($url, $namespace, $headers) // die "namespace $namespace does not exist"; + get_status_id($url, default_ip_status(), $headers) // die "default IP status ". default_ip_status() . " not found"; + }; + if ($@) { + die "Can't use nautobot api: $@"; + } +} + +sub get_ips_from_mac { + my ($class, $plugin_config, $mac, $zoneid) = @_; + + my $url = $plugin_config->{url}; + my $namespace = $plugin_config->{namespace}; + my $headers = default_headers($plugin_config); + + my $ip4 = undef; + my $ip6 = undef; + + my $data = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?q=$mac", $headers); + for my $ip (@{$data->{results}}) { + if ($ip->{ip_version} == 4 && !$ip4) { + ($ip4, undef) = split(/\//, $ip->{address}); + } + + if ($ip->{ip_version} == 6 && !$ip6) { + ($ip6, undef) = split(/\//, $ip->{address}); + } + } + + return ($ip4, $ip6); +} + +sub on_update_hook { + my ($class, $plugin_config) = @_; + + PVE::Network::SDN::Ipams::NautobotPlugin::verify_api($class, $plugin_config); +} + +# helpers +sub get_namespace_id { + my ($url, $namespace, $headers) = @_; + + my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/namespaces/?q=$namespace", $headers); + my $data = @{$result->{results}}[0]; + my $internalid = $data->{id}; + return $internalid; +} + +sub get_status_id { + my ($url, $status, $headers) = @_; + + my $result = PVE::Network::SDN::api_request("GET", "$url/extras/statuses/?q=$status", $headers); + my $data = @{$result->{results}}[0]; + my $internalid = $data->{id}; + return $internalid; +} + +1; -- 2.39.5 --===============2320909529334645294== Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ pve-devel mailing list pve-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel --===============2320909529334645294==--