From mboxrd@z Thu Jan  1 00:00:00 1970
Return-Path: <pve-devel-bounces@lists.proxmox.com>
Received: from firstgate.proxmox.com (firstgate.proxmox.com [IPv6:2a01:7e0:0:424::9])
	by lore.proxmox.com (Postfix) with ESMTPS id 7C2F81FF187
	for <inbox@lore.proxmox.com>; Wed,  7 May 2025 11:17:17 +0200 (CEST)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
	by firstgate.proxmox.com (Proxmox) with ESMTP id BCC65376DC;
	Wed,  7 May 2025 11:17:34 +0200 (CEST)
To: <h.duerr@proxmox.com>, <pve-devel@lists.proxmox.com>
Date: Wed, 7 May 2025 09:17:22 +0000
References: <20250506090041.53080-1-h.duerr@proxmox.com>
In-Reply-To: <20250506090041.53080-1-h.duerr@proxmox.com>
MIME-Version: 1.0
Message-ID: <mailman.230.1746609454.394.pve-devel@lists.proxmox.com>
List-Id: Proxmox VE development discussion <pve-devel.lists.proxmox.com>
List-Post: <mailto:pve-devel@lists.proxmox.com>
From: Lou Lecrivain via pve-devel <pve-devel@lists.proxmox.com>
Precedence: list
Cc: Lou.Lecrivain@wdz.de
X-Mailman-Version: 2.1.29
X-BeenThere: pve-devel@lists.proxmox.com
List-Subscribe: <https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel>, 
 <mailto:pve-devel-request@lists.proxmox.com?subject=subscribe>
List-Unsubscribe: <https://lists.proxmox.com/cgi-bin/mailman/options/pve-devel>, 
 <mailto:pve-devel-request@lists.proxmox.com?subject=unsubscribe>
List-Archive: <http://lists.proxmox.com/pipermail/pve-devel/>
Reply-To: Proxmox VE development discussion <pve-devel@lists.proxmox.com>
List-Help: <mailto:pve-devel-request@lists.proxmox.com?subject=help>
Subject: [pve-devel] [PATCH pve-network v4 1/2] ipam: add Nautobot plugin -
 range workaround questions
Content-Type: multipart/mixed; boundary="===============5260875589913889970=="
Errors-To: pve-devel-bounces@lists.proxmox.com
Sender: "pve-devel" <pve-devel-bounces@lists.proxmox.com>

--===============5260875589913889970==
Content-Type: message/rfc822
Content-Disposition: inline

Return-Path: <Lou.Lecrivain@wdz.de>
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 EFE8BCC583
	for <pve-devel@lists.proxmox.com>; Wed,  7 May 2025 11:17:33 +0200 (CEST)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
	by firstgate.proxmox.com (Proxmox) with ESMTP id D959D376B8
	for <pve-devel@lists.proxmox.com>; Wed,  7 May 2025 11:17:33 +0200 (CEST)
Received: from FR6P281CU001.outbound.protection.outlook.com (mail-germanywestcentralazlp170100001.outbound.protection.outlook.com [IPv6:2a01:111:f403:c20c::1])
	(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
	 key-exchange ECDHE (secp384r1) server-signature RSA-PSS (2048 bits) server-digest SHA256)
	(No client certificate requested)
	by firstgate.proxmox.com (Proxmox) with ESMTPS
	for <pve-devel@lists.proxmox.com>; Wed,  7 May 2025 11:17:32 +0200 (CEST)
ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;
 b=engCmGUe8VYl3cfohQmXaAO5x75povWsEpSbyz46QcP+SKXqcj/sXihm5XqnKNvV5O0ajPCERhK3gTWUstcEBIYWCCajZtayuylDv/9BJe5LypH9rHfXjIGoVPcihScNdd7Wbn+JmhQOSVGCTp7FwYzUmZvUS3mP1Qjh3NURsDoDwbpc4+eoQzxteN9C+s+uWDE5Yb+S8856uIpRVQKTsue5WFKBf8mo8UQWdzR8Nt8BdJE6V+mJ8iwPrHusQlEvojfsH9tr/l+RkI5ivEMm44kW1whHjnp+VutjcIhXVHDFxmsow+EESlKlkMVH7dxPqFMe+VE4X+I0JKrNv+1Xvg==
ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com;
 s=arcselector10001;
 h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1;
 bh=/4tyvfxkMrBybi4ryAQ0y+6brNEtfB3qkynEY623cZw=;
 b=SWzsVh1qjKQ4jS67LFZ749ra0GqKaS18Z4uVBVGq5ERABygLbUFCEGzXVaCuBtS4hbScbrOdE+I6jDe0PxP6l6SwkvXtU98+oO76uVsFn3Hwuu8OoXt/otgWeo+cbKyEajgRkPRy+fiAeLq09H9K885K52ftqMnc/yO/ekRM/hFvFqfR0PwqlRHEV5ZPHPR952+Lxw7PF5isY1MGa98MrH10wZeEsmz0girkFKVHktxt7qOxb9VeK1CqnBBeqHA6magkvSkxW1ePVZiqQnhc6UEsCVFhctfbJi9nbk8NsmubAAnYdiMvegVIrf3ignNoz3snNH2W22SQIioE1CvI4A==
ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass
 smtp.mailfrom=wdz.de; dmarc=pass action=none header.from=wdz.de; dkim=pass
 header.d=wdz.de; arc=none
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
 d=lswnetz.onmicrosoft.com; s=selector1-lswnetz-onmicrosoft-com;
 h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck;
 bh=/4tyvfxkMrBybi4ryAQ0y+6brNEtfB3qkynEY623cZw=;
 b=Ra66RyyDgqfu/2RDWzuyaKzWwtmcnIAhQBdqhhKTnkuJkC3ebvtlUAIgBy7KUc68Og/bEA08L+UXP3Gy4jo1Hq1f8LCBnAt50NMcCPygk4CP8Jjq93vjLNqwQBQkXYxOExkUoOD6WmSgqurBRFiAHIJenWOQmxaSvivSSUEuUrk=
Received: from FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d18:2::2e)
 by FR3P281MB1568.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d10:7b::12) with
 Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8699.26; Wed, 7 May
 2025 09:17:23 +0000
Received: from FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM
 ([fe80::61d6:5538:7b39:dea1]) by FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM
 ([fe80::61d6:5538:7b39:dea1%5]) with mapi id 15.20.8699.026; Wed, 7 May 2025
 09:17:23 +0000
From: <Lou.Lecrivain@wdz.de>
To: <h.duerr@proxmox.com>, <pve-devel@lists.proxmox.com>
Subject: [PATCH pve-network v4 1/2] ipam: add Nautobot plugin - range
 workaround questions
Thread-Topic: [PATCH pve-network v4 1/2] ipam: add Nautobot plugin - range
 workaround questions
Thread-Index: AQHbvzDX9DivMuFErk+F8/u1jerq8A==
Date: Wed, 7 May 2025 09:17:22 +0000
Message-ID:  <FR2PPF45D006270C594040B87F6E418E7548588A@FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM>
References: <20250506090041.53080-1-h.duerr@proxmox.com>
In-Reply-To: <20250506090041.53080-1-h.duerr@proxmox.com>
Accept-Language: fr-FR, en-US
Content-Language: fr-FR
X-MS-Has-Attach: 
X-MS-TNEF-Correlator: 
msip_labels: 
authentication-results: dkim=none (message not signed)
 header.d=none;dmarc=none action=none header.from=wdz.de;
x-ms-publictraffictype: Email
x-ms-traffictypediagnostic: FR2PPF45D006270:EE_|FR3P281MB1568:EE_
x-ms-office365-filtering-correlation-id: 9aa0abaa-1b5a-4f2d-ed9c-08dd8d47f9d5
x-ms-exchange-senderadcheck: 1
x-ms-exchange-antispam-relay: 0
x-microsoft-antispam:  BCL:0;ARA:13230040|376014|366016|1800799024|10070799003|38070700018;
x-microsoft-antispam-message-info:  =?iso-8859-1?Q?pbihxu0TLRf44k5BPHQEfHLARGoPj7dOU+prvY0MgggWBe/RVUW18Z7AkA?=
 =?iso-8859-1?Q?s06qwaz+YSrKLBuEcit61NVNh5VcZjwuXxfvCRVu6aLhteisKRKZHi0GTb?=
 =?iso-8859-1?Q?pab2p1sGY9ahCknAcct9Tf5r3nO639473VnJjztYsmRfyXss5+kF3Y1oHy?=
 =?iso-8859-1?Q?Rc4GgJMCcM+Nqn/i/VIJAw1KqI0sT+VOOuP8C+KBb3vK3S/bJOyLRqs7Xi?=
 =?iso-8859-1?Q?ZHLcWFFz0ruUxZe0IH+Jwr8x2AkKeLG0c8w5YjmCK5RCt8fALX/8rTAaTh?=
 =?iso-8859-1?Q?8mxA56fO3WiR3ZLBkzrtyOCbIVKMsogO4vLYjsgj1KSf9BlwpUQ6mTNO91?=
 =?iso-8859-1?Q?jfB3zCbJOwdU7aQwaijq5YlfV9/KGJpgCjAS89PPFicTM2fiIyZcMDrc31?=
 =?iso-8859-1?Q?TxWzSeI2niM2/DwRiYrE4M2PcPNyzpHXDPit7haUTxIIXzpE72bscEMxew?=
 =?iso-8859-1?Q?qedKu5s6ib68zfwWuYaYbBy3fsgCviOwxBWV+9QlOr6ZmkxX3HHhPRNREm?=
 =?iso-8859-1?Q?60/MAw/pweQWAaLL6sJpIeBic2I/L7giV9r7GueZHFhLWNGgrMZ4VjcZ4+?=
 =?iso-8859-1?Q?nR/FOrx2o52Ov/a20jHZBlZEbzdD6Rz5PeNfuy6jMdlj84kDTuT0raF6yD?=
 =?iso-8859-1?Q?lez+CRcvVUSjnQ3muIC+b4JU9vD08/yNbpaiS8xgCRIAEzHmgNdHl3Ep1P?=
 =?iso-8859-1?Q?Xh933JL35eTftKSzQBcjOhPpANZ8oLRJ6c9typY0FMzZ8Vlo91cfqQt8nX?=
 =?iso-8859-1?Q?yPEX8Oec15m+pNI2tx1/sJDe3yd/QEeQ1HE6TErLhjS+4lfjDxA8s6qJ6Q?=
 =?iso-8859-1?Q?xx1l4gclgHJD3TF2PYnZS338LDANajhzxukn7foHnnDBt/VcHP0vNe2vQ6?=
 =?iso-8859-1?Q?vTyjXhiihm2f4tag/SIWYuPNhRqCrCdfCjDFE1QQpax/RL/9j5rxPjoVQJ?=
 =?iso-8859-1?Q?6hVMllrxPqR/QMRI1M6Qy3J2vaJ95KdI83fjSL2U8BAgaDcT4ZaR5SvrzV?=
 =?iso-8859-1?Q?Uh74uA57iQJBFSAJSRmownm1liGFnURwfxII3Cl46ev9/x78S0fGrsq6oQ?=
 =?iso-8859-1?Q?7m+OvDBI3SMB2QVIGh6+dsbsT9rMGU1nRdUhDnUHxa0FSGP/YazKQwu9bE?=
 =?iso-8859-1?Q?XFpJjesrmsA3GuhOaUW5e3R25sneEyfSIz+qDeHYtdpPbiUyjRig5tFWRt?=
 =?iso-8859-1?Q?/trSNf9+Uez5bCu1Uvhj2ZBXX2btAv9Sg8eSOnnoCAlgIY+mTDJNRpUvXX?=
 =?iso-8859-1?Q?CukItzaDr+k0oO0DYgEUidiIL7HbmAB+BSPY82TeoDg1SQz1BWVIsJ3Mku?=
 =?iso-8859-1?Q?lis6L+qFcLTastGLS33gFpNObSHpGWg4PxU5wea7/M0ZT4LRkVT/cqwF+9?=
 =?iso-8859-1?Q?AVajXgtM72cggbYOSMyv1RMrNwC4u2Vq9EueOCjwVgIbtKRtAvpD44HsYm?=
 =?iso-8859-1?Q?n6QCjASfB+eu3WVT8Vm8hNKrzeiPdQetWyNv/MpBV5FLGTKWGzZ+A7ZkK6?=
 =?iso-8859-1?Q?c=3D?=
x-forefront-antispam-report:  CIP:255.255.255.255;CTRY:;LANG:en;SCL:1;SRV:;IPV:NLI;SFV:NSPM;H:FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM;PTR:;CAT:NONE;SFS:(13230040)(376014)(366016)(1800799024)(10070799003)(38070700018);DIR:OUT;SFP:1102;
x-ms-exchange-antispam-messagedata-chunkcount: 1
x-ms-exchange-antispam-messagedata-0:  =?iso-8859-1?Q?uuIMtA8vcIWvlB3CtmaQRazIzAvHJlJk3gyMdNIhGigYrQ/Irqoi2e/hQx?=
 =?iso-8859-1?Q?8Ttu0YpIu2gaaIx7DBvJ7vI8j1SgYOAqdILh47eojeuoLo3TIzKb5RSH/U?=
 =?iso-8859-1?Q?UmiQUx7IhJUUIEw23Kfltu2147+U34hRWPO52Q9ZM6j9K4ztyjlA8V5FUq?=
 =?iso-8859-1?Q?rWh0c/cD9IbekCvXkDfqgFXV0n5dHBCTCj83QScNHQvdpx7DMXXvmu8Y2u?=
 =?iso-8859-1?Q?NJXPLh4ul+pvD3uvoc4nVybEIcAPPD3ZWtcxnr5IaNLJbvY9Y9OERjs/58?=
 =?iso-8859-1?Q?om1b0T1eAHzLx1U/8hxrXditEFWHhh51V+2t479hzWu5+7+J9WbD4EOnLM?=
 =?iso-8859-1?Q?citFl0ZkLCTWn7xeWBXYey1kyEczC9nkBHj9FyQ5zuX4k9pzqaX2B01ioa?=
 =?iso-8859-1?Q?5l4mSXmc9CCDiXabeCSnoReUJk/LqxpsaeebYa3lE+wXMXiOwNW60DZAxC?=
 =?iso-8859-1?Q?Ev7/jqfEHb0GUrf6vmd/FIMJUImG47YFxRCzMweTYo2gQkab3hUYRnHZMR?=
 =?iso-8859-1?Q?Gqyc1WzAQzK5I4WYrfw4Qrnd7wA28HivPISA+bDX6e1rWrpqalkdo8lNq9?=
 =?iso-8859-1?Q?i9CpIVxahBTmi5hs7gh28ecCXCXBetja4RTFtHROdzgRNedGxrU+2WRkbn?=
 =?iso-8859-1?Q?6B7Lr23ARBtzIY+ENgVmocvEJiw2cdnxMMcqs869di8tpcZA657hb9C/RH?=
 =?iso-8859-1?Q?U8I2UzzQQGYhf6jHah/Ol3MmkhPdGagFPZGu/Tw6O7TqL9/91N3RqgIwor?=
 =?iso-8859-1?Q?snAb5Oj9FF+EsLWvioXbcLLiqbhxzrJgYQCAphCTnW9UWE5ZfkXMe6agSm?=
 =?iso-8859-1?Q?mS6yytc07SM0l9ynRdOPxKxITn56jJCXtMH8vGO8yQFsqK90ipfWChb2hP?=
 =?iso-8859-1?Q?RnvDeIiFVl8otn4eRXol54gQVi2zDlIqfPnsdJHQMVPbOn59+h4cZThJas?=
 =?iso-8859-1?Q?FXwk3VIHXLc4MHn3WWFbr6t9mDlTtRUw/c6l9gC/375l4CZ4eZ7rjozda2?=
 =?iso-8859-1?Q?JxyK5wfbIUitPqeMishShE5+rIz4wJMrJtUgfQobmJcyDbD6reXwUk6WNb?=
 =?iso-8859-1?Q?23hvxXoH0A9NutnPqQC/fyJWyQ27CvIcuBHyG8O/yrznUC8YVKrv6h+9q1?=
 =?iso-8859-1?Q?+ZBnfzsM6sxAr6G3v/UaVRz6eucDcL8ZJOoJzD/LijP+IErdpwKcnkEeDO?=
 =?iso-8859-1?Q?X+/PXmQcKNk8xjD0cw/l+bUlRmVw2jpiKzvFabgimAvSAOd49ttPU78IOV?=
 =?iso-8859-1?Q?SMZinUGEV8UQ0M1Dw1CWlVsxhh+2MnlFPf1iK7Rf58cJ5NvKxYQSFjDrnA?=
 =?iso-8859-1?Q?NJlRPVlln3TMLK1fHJaaimOH14d31B/JstsMZl+w5aBeDhEtakgn2hBWId?=
 =?iso-8859-1?Q?vGxyRD+FluNLkBUBblCs3lM3d73Z2pEPlIxq9E14LonSON5Br2KnSux7Vx?=
 =?iso-8859-1?Q?52S/kNTPDDXCS6knOwOnHYLaPtxJA668Q02772qWh8RDnFWkCKK7gmG7ZZ?=
 =?iso-8859-1?Q?WijRZ8PJX6JDdk4jgToAtX/gjN2tTlRG889qX7XSn6J0dNJUjl2hzdiLw2?=
 =?iso-8859-1?Q?sH7OFHxF89gnzZU3CN1u90WPKJAhX15si6fbXxU/wk8fM/7oo8F77NLnFS?=
 =?iso-8859-1?Q?3OQfhw/5ctzWCcGDO2rh6LTSdMbm2yCH8KBrrcjIvoh4CbRl1P1ZZjEgBH?=
 =?iso-8859-1?Q?Mm+yZGwVA8Iz2uf7nqpkgpIb1obnobItsijH39n2?=
Content-Type: text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0
X-OriginatorOrg: wdz.de
X-MS-Exchange-CrossTenant-AuthAs: Internal
X-MS-Exchange-CrossTenant-AuthSource: FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM
X-MS-Exchange-CrossTenant-Network-Message-Id: 9aa0abaa-1b5a-4f2d-ed9c-08dd8d47f9d5
X-MS-Exchange-CrossTenant-originalarrivaltime: 07 May 2025 09:17:22.9654
 (UTC)
X-MS-Exchange-CrossTenant-fromentityheader: Hosted
X-MS-Exchange-CrossTenant-id: d1b757c4-c391-4d47-b0de-c8365891bd6b
X-MS-Exchange-CrossTenant-mailboxtype: HOSTED
X-MS-Exchange-CrossTenant-userprincipalname: vCo3GS8t6/p5cQVFExJfh4BMux7EA8lHknvt/oQwQEQ5aIeKnfapcwMRcFwPCZ0pEOyGLhXSaORgNuHkEOk7gQ==
X-MS-Exchange-Transport-CrossTenantHeadersStamped: FR3P281MB1568
X-SPAM-LEVEL: Spam detection results:  0
	ARC_SIGNED              0.001 Message has a ARC signature
	ARC_VALID               0.001 Message has a valid ARC signature
	AWL                    -0.200 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
	DMARC_PASS               -0.1 DMARC pass policy
	FORGED_SPF_HELO             1 -
	KAM_MAILER                  2 Automated Mailer Tag Left in Email
	SPF_HELO_PASS          -0.001 SPF: HELO matches SPF record
	T_SPF_PERMERROR          0.01 SPF: test of record failed (permerror)
	URIBL_BLOCKED           0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked.  See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [networktocode.com,nautobotplugin.pm,phpipamplugin.pm,plugin.pm,ipams.pm,lswnetz.onmicrosoft.com,netboxplugin.pm,pveplugin.pm]

Hannes,=0A=
=0A=
Thank you for helping with getting the Netbox plugin in good shape, it's ap=
preciated, really :)=0A=
=0A=
I just have two questions regarding the lack of range support in Nautobot:=
=0A=
- Was there an issue with the original workaround code? I find this one way=
 more complex,=0A=
   but also I guess I could've missed some corner case(s).=0A=
- What happens if we use a prefix with a CIDR greater than /24 in IPv4 or /=
120 in IPv6?=0A=
   Does not being able to slice it means that ranges with small prefixes su=
ch as these=0A=
   would be forbidden/not working?=0A=
=0A=
BR=0A=
Lou=0A=
________________________________________=0A=
De :=A0Hannes Duerr <h.duerr@proxmox.com>=0A=
Envoy=E9 :=A0mardi 6 mai 2025 11:00=0A=
=C0 :=A0pve-devel@lists.proxmox.com <pve-devel@lists.proxmox.com>=0A=
Cc=A0:=A0Hannes Duerr <h.duerr@proxmox.com>; Lecrivain, Lou (WDZ) <Lou.Lecr=
ivain@wdz.de>=0A=
Objet :=A0[!!ACHTUNG extern!!] - [PATCH pve-network v4 1/2] ipam: add Nauto=
bot plugin=0A=
=A0=0A=
Nautobot is a network source of truth [0] and as such this plugin offers=0A=
the possibility to automatically enter IP addresses and subnets=0A=
(prefixes in Nautobot jargon) into Nautobot as soon as they are created=0A=
in Proxmox VE.=0A=
=0A=
Limitations:=0A=
* The IPAM plugin currently does not recognize whether VMs/CTs are=0A=
=A0 active/online and initially sets the status to Active but when the=0A=
=A0 VMs/CTs are switched off the status is not deactivated.=0A=
* Nautobot does not support ranges that can be searched for free ip=0A=
=A0 addresses, so we map as many pool prefixes that we search until we=0A=
=A0 have searched the entire range for free ip addresses=0A=
* Nautobot has the possibility to map DNS names to IP addresses.=0A=
=A0 However, since we have no standardized way to set the DNS names in=0A=
=A0 Proxmox VE (for containers the container name in Proxmox VE is set as=
=0A=
=A0 the host name in the container, but this is not possible for VMs) we=0A=
=A0 refrain from setting a DNS name at the moment.=0A=
* Nautobot does not have a field in IP address objects for a MAC address=0A=
=A0 or to mark them as gateway, so we write this in the comment.=0A=
=0A=
[0] https://networktocode.com/nautobot/=0A=
=0A=
Co-authored-by: lou lecrivain <lou.lecrivain@wdz.de>=0A=
Signed-off-by: Hannes Duerr <h.duerr@proxmox.com>=0A=
---=0A=
=0A=
Notes:=0A=
=A0=A0=A0 Changes from v3 -> v4:=0A=
=A0=A0=A0 * Merge the check for empty prefixes/subnets into this commit=0A=
=A0=A0=A0 * Create nautobot_api_request to unify API calls=0A=
=A0=A0=A0 * add update_subnet function=0A=
=A0=A0=A0 * fix add_range_next_freeip=0A=
=A0=A0=A0 * fix and unify error handling=0A=
=A0=A0=A0 * Fix perl style=0A=
=0A=
=A0src/PVE/API2/Network/SDN/Ipams.pm=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 |=A0=A0 =
1 +=0A=
=A0src/PVE/Network/SDN/Ipams.pm=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=
=A0 |=A0=A0 3 +=0A=
=A0src/PVE/Network/SDN/Ipams/Makefile=A0=A0=A0=A0=A0=A0=A0=A0=A0 |=A0=A0 2 =
+-=0A=
=A0src/PVE/Network/SDN/Ipams/NautobotPlugin.pm | 513 ++++++++++++++++++++=
=0A=
=A04 files changed, 518 insertions(+), 1 deletion(-)=0A=
=A0create mode 100644 src/PVE/Network/SDN/Ipams/NautobotPlugin.pm=0A=
=0A=
diff --git a/src/PVE/API2/Network/SDN/Ipams.pm b/src/PVE/API2/Network/SDN/I=
pams.pm=0A=
index 27ead02..8074512 100644=0A=
--- a/src/PVE/API2/Network/SDN/Ipams.pm=0A=
+++ b/src/PVE/API2/Network/SDN/Ipams.pm=0A=
@@ -12,6 +12,7 @@ use PVE::Network::SDN::Ipams::Plugin;=0A=
=A0use PVE::Network::SDN::Ipams::PVEPlugin;=0A=
=A0use PVE::Network::SDN::Ipams::PhpIpamPlugin;=0A=
=A0use PVE::Network::SDN::Ipams::NetboxPlugin;=0A=
+use PVE::Network::SDN::Ipams::NautobotPlugin;=0A=
=A0use PVE::Network::SDN::Dhcp;=0A=
=A0use PVE::Network::SDN::Vnets;=0A=
=A0use PVE::Network::SDN::Zones;=0A=
diff --git a/src/PVE/Network/SDN/Ipams.pm b/src/PVE/Network/SDN/Ipams.pm=0A=
index c689b8f..2ecb75e 100644=0A=
--- a/src/PVE/Network/SDN/Ipams.pm=0A=
+++ b/src/PVE/Network/SDN/Ipams.pm=0A=
@@ -12,11 +12,14 @@ use PVE::Network;=0A=
=A0=0A=
=A0use PVE::Network::SDN::Ipams::PVEPlugin;=0A=
=A0use PVE::Network::SDN::Ipams::NetboxPlugin;=0A=
+use PVE::Network::SDN::Ipams::NautobotPlugin;=0A=
=A0use PVE::Network::SDN::Ipams::PhpIpamPlugin;=0A=
=A0use PVE::Network::SDN::Ipams::Plugin;=0A=
=A0=0A=
+=0A=
=A0PVE::Network::SDN::Ipams::PVEPlugin->register();=0A=
=A0PVE::Network::SDN::Ipams::NetboxPlugin->register();=0A=
+PVE::Network::SDN::Ipams::NautobotPlugin->register();=0A=
=A0PVE::Network::SDN::Ipams::PhpIpamPlugin->register();=0A=
=A0PVE::Network::SDN::Ipams::Plugin->init();=0A=
=A0=0A=
diff --git a/src/PVE/Network/SDN/Ipams/Makefile b/src/PVE/Network/SDN/Ipams=
/Makefile=0A=
index 4e7d65f..75e5b9a 100644=0A=
--- a/src/PVE/Network/SDN/Ipams/Makefile=0A=
+++ b/src/PVE/Network/SDN/Ipams/Makefile=0A=
@@ -1,4 +1,4 @@=0A=
-SOURCES=3DPlugin.pm PhpIpamPlugin.pm NetboxPlugin.pm PVEPlugin.pm=0A=
+SOURCES=3DPlugin.pm PhpIpamPlugin.pm NetboxPlugin.pm PVEPlugin.pm Nautobot=
Plugin.pm=0A=
=A0=0A=
=A0=0A=
=A0PERL5DIR=3D${DESTDIR}/usr/share/perl5=0A=
diff --git a/src/PVE/Network/SDN/Ipams/NautobotPlugin.pm b/src/PVE/Network/=
SDN/Ipams/NautobotPlugin.pm=0A=
new file mode 100644=0A=
index 0000000..2897025=0A=
--- /dev/null=0A=
+++ b/src/PVE/Network/SDN/Ipams/NautobotPlugin.pm=0A=
@@ -0,0 +1,513 @@=0A=
+package PVE::Network::SDN::Ipams::NautobotPlugin;=0A=
+=0A=
+use strict;=0A=
+use warnings;=0A=
+use PVE::INotify;=0A=
+use PVE::Cluster;=0A=
+use PVE::Tools;=0A=
+use NetAddr::IP;=0A=
+use Net::Subnet qw(subnet_matcher);=0A=
+=0A=
+use base('PVE::Network::SDN::Ipams::Plugin');=0A=
+=0A=
+sub type {=0A=
+=A0=A0=A0 return 'nautobot';=0A=
+}=0A=
+=0A=
+sub properties {=0A=
+=A0=A0=A0 return {=0A=
+=A0=A0=A0=A0=A0=A0 namespace =3D> {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 type =3D> 'string',=0A=
+=A0=A0=A0=A0=A0=A0 },=0A=
+=A0=A0=A0 };=0A=
+}=0A=
+=0A=
+sub options {=0A=
+=A0=A0=A0 return {=0A=
+=A0=A0=A0=A0=A0=A0 url =3D> { optional =3D> 0 },=0A=
+=A0=A0=A0=A0=A0=A0 token =3D> { optional =3D> 0 },=0A=
+=A0=A0=A0=A0=A0=A0 namespace =3D> { optional =3D> 0 },=0A=
+=A0=A0=A0=A0=A0=A0 fingerprint =3D> { optional =3D> 1 },=0A=
+=A0=A0=A0 };=0A=
+}=0A=
+=0A=
+sub default_ip_status {=0A=
+=A0=A0=A0 return 'Active';=0A=
+}=0A=
+=0A=
+sub nautobot_api_request {=0A=
+=A0=A0=A0 my ($config, $method, $path, $params) =3D @_;=0A=
+=0A=
+=A0=A0=A0 return PVE::Network::SDN::api_request(=0A=
+=A0=A0=A0=A0=A0=A0 $method,=0A=
+=A0=A0=A0=A0=A0=A0 "$config->{url}${path}",=0A=
+=A0=A0=A0=A0=A0=A0 [=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 'Content-Type' =3D> 'application/json; char=
set=3DUTF-8',=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 'Authorization' =3D> "token $config->{token=
}",=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 'Accept' =3D> "application/json"=0A=
+=A0=A0=A0=A0=A0=A0 ],=0A=
+=A0=A0=A0=A0=A0=A0 $params,=0A=
+=A0=A0=A0=A0=A0=A0 $config->{fingerprint},=0A=
+=A0=A0=A0 );=0A=
+}=0A=
+=0A=
+sub add_subnet {=0A=
+=A0=A0=A0 my ($class, $config, undef, $subnet, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $cidr =3D $subnet->{cidr};=0A=
+=A0=A0=A0 my $namespace =3D $config->{namespace};=0A=
+=0A=
+=A0=A0=A0 my $internalid =3D get_prefix_id($config, $cidr, $noerr);=0A=
+=A0=A0=A0 if ($internalid) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "could not add the subnet $subnet because it alread=
y exists in nautobot\n"=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $params =3D {=0A=
+=A0=A0=A0=A0=A0=A0 prefix =3D> $cidr,=0A=
+=A0=A0=A0=A0=A0=A0 namespace =3D> $namespace,=0A=
+=A0=A0=A0=A0=A0=A0 status =3D> default_ip_status()=0A=
+=A0=A0=A0 };=0A=
+=0A=
+=A0=A0=A0 eval {=0A=
+=A0=A0=A0=A0=A0=A0 nautobot_api_request($config, "POST", "/ipam/prefixes/"=
, $params);=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error adding the subnet $subnet to nautobot $@\n";=
=0A=
+=A0=A0=A0 }=0A=
+}=0A=
+=0A=
+sub update_subnet {=0A=
+=A0=A0=A0 my ($class, $plugin_config, $subnetid, $subnet, $old_subnet, $no=
err) =3D @_;=0A=
+=A0=A0=A0 # dhcp ranges are not supported in nautobot so we don't have to =
update them=0A=
+}=0A=
+=0A=
+sub del_subnet {=0A=
+=A0=A0=A0 my ($class, $config, $subnetid, $subnet, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $cidr =3D $subnet->{cidr};=0A=
+=0A=
+=A0=A0=A0 my $internalid =3D get_prefix_id($config, $cidr, $noerr);=0A=
+=A0=A0=A0 if (!$internalid) {=0A=
+=A0=A0=A0=A0=A0=A0 warn("could not find delete the subnet $cidr because it=
 does not exist in nautobot\n");=0A=
+=A0=A0=A0=A0=A0=A0 return;=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 if (!subnet_is_deletable($config, $subnetid, $subnet, $internali=
d, $noerr)) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "could not delete the subnet $cidr, it still contai=
ns ip addresses!\n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 # delete associated gateway IP addresses=0A=
+=A0=A0=A0 $class->empty_subnet( $config, $subnetid, $subnet, $internalid, =
$noerr);=0A=
+=0A=
+=A0=A0=A0 eval {=0A=
+=A0=A0=A0=A0=A0=A0 nautobot_api_request($config, "DELETE", "/ipam/prefixes=
/$internalid/");=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error deleting subnet from nautobot: $@\n";=0A=
+=A0=A0=A0 }=0A=
+=A0=A0=A0 return 1;=0A=
+}=0A=
+=0A=
+sub add_ip {=0A=
+=A0=A0=A0 my ($class, $config, undef, $subnet, $ip, $hostname, $mac, undef=
,=0A=
+=A0=A0=A0=A0=A0=A0 $is_gateway, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $mask =3D $subnet->{mask};=0A=
+=A0=A0=A0 my $namespace =3D $config->{namespace};=0A=
+=0A=
+=A0=A0=A0 my $description =3D undef;=0A=
+=A0=A0=A0 if ($is_gateway) {=0A=
+=A0=A0=A0=A0=A0=A0 $description =3D 'gateway';=0A=
+=A0=A0=A0 } elsif ($mac) {=0A=
+=A0=A0=A0=A0=A0=A0 $description =3D "mac:$mac";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $params =3D {=0A=
+=A0=A0=A0=A0=A0=A0 address =3D> "$ip/$mask",=0A=
+=A0=A0=A0=A0=A0=A0 type =3D> "dhcp",=0A=
+=A0=A0=A0=A0=A0=A0 description =3D> $description,=0A=
+=A0=A0=A0=A0=A0=A0 namespace =3D> $namespace,=0A=
+=A0=A0=A0=A0=A0=A0 status =3D> default_ip_status()=0A=
+=A0=A0=A0 };=0A=
+=0A=
+=A0=A0=A0 eval {=0A=
+=A0=A0=A0=A0=A0=A0 nautobot_api_request($config, "POST", "/ipam/ip-address=
es/", $params);=0A=
+=A0=A0=A0 };=0A=
+=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 if ($is_gateway) {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 die "error add subnet ip to ipam: ip $ip al=
ready exist: $@"=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 if !is_ip_gateway($config, $ip,=
 $noerr);=0A=
+=A0=A0=A0=A0=A0=A0 } elsif (!$noerr) {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 die "error add subnet ip to ipam: ip alread=
y exist: $@";=0A=
+=A0=A0=A0=A0=A0=A0 }=0A=
+=A0=A0=A0 }=0A=
+}=0A=
+=0A=
+sub add_next_freeip {=0A=
+=A0=A0=A0 my ($class, $config, undef, $subnet, $hostname, $mac, undef, $no=
err) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $cidr =3D $subnet->{cidr};=0A=
+=A0=A0=A0 my $namespace =3D $config->{namespace};=0A=
+=0A=
+=A0=A0=A0 my $internalid =3D get_prefix_id($config, $cidr, $noerr);=0A=
+=A0=A0=A0 if (!defined($internalid)) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "could not find prefix $cidr in nautobot\n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $description =3D undef;=0A=
+=A0=A0=A0 $description =3D "mac:$mac" if $mac;=0A=
+=0A=
+=A0=A0=A0 my $params =3D {=0A=
+=A0=A0=A0=A0=A0=A0 type =3D> "dhcp",=0A=
+=A0=A0=A0=A0=A0=A0 description =3D> $description,=0A=
+=A0=A0=A0=A0=A0=A0 namespace =3D> $namespace,=0A=
+=A0=A0=A0=A0=A0=A0 status =3D> default_ip_status()=0A=
+=A0=A0=A0 };=0A=
+=0A=
+=A0=A0=A0 my $response =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0 return nautobot_api_request(=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 $config,=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 "POST",=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 "/ipam/prefixes/$internalid/available-ips/"=
,=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 $params=0A=
+=A0=A0=A0=A0=A0=A0 );=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@ || !$response) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "could not allocate ip in subnet $cidr: $@\n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $ip =3D NetAddr::IP->new($response->{address});=0A=
+=0A=
+=A0=A0=A0 return $ip->addr;=0A=
+}=0A=
+=0A=
+sub add_range_next_freeip {=0A=
+=A0=A0=A0 my ($class, $config, $subnet, $range, $data, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $cidr =3D NetAddr::IP->new($subnet->{cidr});=0A=
+=A0=A0=A0 my $namespace =3D $config->{namespace};=0A=
+=0A=
+=A0=A0=A0 # Nautobot does not support IP ranges only prefixes.=0A=
+=A0=A0=A0 # We therefore divide the range into smaller pool-prefixes,=0A=
+=A0=A0=A0 # each containing 256 addresses, and search for available IPs in=
 them=0A=
+=A0=A0=A0 my $prefix_size =3D $cidr->version =3D=3D 4 ? 24 : 120;=0A=
+=A0=A0=A0 my $increment =3D 256;=0A=
+=A0=A0=A0 my $found_ip =3D undef;=0A=
+=0A=
+=A0=A0=A0 my $start_range =3D NetAddr::IP->new($range->{'start-address'}, =
$prefix_size);=0A=
+=A0=A0=A0 my $end_range =3D NetAddr::IP->new($range->{'end-address'}, $pre=
fix_size);=0A=
+=A0=A0=A0 my $matcher =3D subnet_matcher($end_range->cidr);=0A=
+=A0=A0=A0 my $current_ip =3D $start_range;=0A=
+=0A=
+=A0=A0=A0 while (1) {=0A=
+=A0=A0=A0=A0=A0=A0 my $current_cidr =3D $current_ip->addr . "/$prefix_size=
";=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 my $params =3D {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 prefix =3D> $current_cidr,=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 namespace =3D> $namespace,=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 status =3D> default_ip_status(),=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 type =3D> "pool"=0A=
+=A0=A0=A0=A0=A0=A0 };=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 # create temporary pool prefix=0A=
+=A0=A0=A0=A0=A0=A0 my $temp_prefix =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 return nautobot_api_request($config, "POST"=
, "/ipam/prefixes/", $params);=0A=
+=A0=A0=A0=A0=A0=A0 };=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 # skip if it is not possible to create it=0A=
+=A0=A0=A0=A0=A0=A0 next if $@;=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 my $temp_prefix_id =3D $temp_prefix->{id};=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 # Fetch available IPs from the temporary pool and find =
a matching IP=0A=
+=A0=A0=A0=A0=A0=A0 my $result =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 return nautobot_api_request(=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 $config,=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 "GET",=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 "/ipam/prefixes/$temp_prefix_id=
/available-ips/?limit=3D$increment"=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 );=0A=
+=A0=A0=A0=A0=A0=A0 };=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 # search list for IPs in actual range=0A=
+=A0=A0=A0=A0=A0=A0 if (!$@) {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 foreach my $entry (@$result) {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 my $ip =3D NetAddr::IP->new($en=
try->{address});=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 # comparison is only possible b=
ecause they are in the same subnet=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 if ($start_range <=3D $ip && $i=
p <=3D $end_range) {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 $found_ip =3D $ip->=
addr;=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 last;=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 }=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 }=0A=
+=A0=A0=A0=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 # Delete temporary prefix pool=0A=
+=A0=A0=A0=A0=A0=A0 eval {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 nautobot_api_request($config, "DELETE", "/i=
pam/prefixes/$temp_prefix_id/");=0A=
+=A0=A0=A0=A0=A0=A0 };=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 last if $found_ip;=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 # we searched the last pool prefix=0A=
+=A0=A0=A0=A0=A0=A0 last if $matcher->($current_ip->addr);=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 $current_ip =3D $current_ip->plus($increment);=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 if (!$found_ip) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "could not allocate ip in the range " . $start_rang=
e->addr . " - "=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 . $end_range->addr . ": $@\n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 $class->add_ip($config, undef, $subnet, $found_ip, $data->{hostn=
ame}, $data->{mac}, undef, 0, $noerr);=0A=
+=0A=
+=A0=A0=A0 return $found_ip;=0A=
+}=0A=
+=0A=
+sub update_ip {=0A=
+=A0=A0=A0 my ($class, $config, $subnetid, $subnet, $ip, $hostname, $mac,=
=0A=
+=A0=A0=A0=A0=A0=A0 undef, $is_gateway, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $mask =3D $subnet->{mask};=0A=
+=A0=A0=A0 my $namespace =3D $config->{namespace};=0A=
+=0A=
+=A0=A0=A0 my $description =3D undef;=0A=
+=A0=A0=A0 if ($is_gateway) {=0A=
+=A0=A0=A0=A0=A0=A0 $description =3D 'gateway'=0A=
+=A0=A0=A0 } elsif ($mac) {=0A=
+=A0=A0=A0=A0=A0=A0 $description =3D "mac:$mac";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $params =3D {=0A=
+=A0=A0=A0=A0=A0=A0 address =3D> "$ip/$mask",=0A=
+=A0=A0=A0=A0=A0=A0 type =3D> "dhcp",=0A=
+=A0=A0=A0=A0=A0=A0 description =3D> $description,=0A=
+=A0=A0=A0=A0=A0=A0 namespace =3D> $namespace,=0A=
+=A0=A0=A0=A0=A0=A0 status =3D> default_ip_status()=0A=
+=A0=A0=A0 };=0A=
+=0A=
+=A0=A0=A0 my $ip_id =3D get_ip_id($config, $ip, $noerr);=0A=
+=A0=A0=A0 if (!defined($ip_id)) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "could not find the ip $ip in nautobot\n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 eval {=0A=
+=A0=A0=A0=A0=A0=A0 nautobot_api_request($config, "PATCH", "/ipam/ip-addres=
ses/$ip_id/", $params);=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error updating ip $ip: $@";=0A=
+=A0=A0=A0 }=0A=
+}=0A=
+=0A=
+=0A=
+sub del_ip {=0A=
+=A0=A0=A0 my ($class, $config, undef, undef, $ip, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 return if !$ip;=0A=
+=0A=
+=A0=A0=A0 my $ip_id =3D get_ip_id($config, $ip, $noerr);=0A=
+=A0=A0=A0 if (!defined($ip_id)) {=0A=
+=A0=A0=A0=A0=A0=A0 warn("could not find the ip $ip in nautobot\n");=0A=
+=A0=A0=A0=A0=A0=A0 return;=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 eval {=0A=
+=A0=A0=A0=A0=A0=A0 nautobot_api_request($config, "DELETE", "/ipam/ip-addre=
sses/$ip_id/");=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error deleting ip $ip : $@\n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 return 1;=0A=
+}=0A=
+=0A=
+sub empty_subnet {=0A=
+=A0=A0=A0 my ($class, $config, $subnetid, $subnet, $subnetuuid, $noerr) =
=3D @_;=0A=
+=0A=
+=A0=A0=A0 my $namespace =3D $config->{namespace};=0A=
+=0A=
+=A0=A0=A0 my $response =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0 return nautobot_api_request(=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 $config,=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 "GET",=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 "/ipam/ip-addresses/?namespace=3D$namespace=
&parent=3D$subnetuuid"=0A=
+=A0=A0=A0=A0=A0=A0 );=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "could not find the subnet $subnet in nautobot: $@\=
n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 for my $ip (@{$response->{results}}) {=0A=
+=A0=A0=A0=A0=A0=A0 del_ip($class, $config, undef, undef, $ip->{host}, $noe=
rr);=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 return 1;=0A=
+}=0A=
+=0A=
+sub subnet_is_deletable {=0A=
+=A0=A0=A0 my ($config, $subnetid, $subnet, $subnetuuid, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $namespace =3D $config->{namespace};=0A=
+=0A=
+=A0=A0=A0 my $response =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0 return nautobot_api_request(=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 $config,=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 "GET",=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 "/ipam/ip-addresses/?namespace=3D$namespace=
&parent=3D$subnetuuid"=0A=
+=A0=A0=A0=A0=A0=A0 );=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error querying prefix $subnet: $@\n";=0A=
+=A0=A0=A0 }=0A=
+=A0=A0=A0 my $n_ips =3D scalar $response->{results}->@*;=0A=
+=0A=
+=A0=A0=A0 # least costly check operation 1st=0A=
+=A0=A0=A0 return 1 if ($n_ips =3D=3D 0);=0A=
+=0A=
+=A0=A0=A0 for my $ip (values $response->{results}->@*) {=0A=
+=A0=A0=A0=A0=A0=A0 if (!is_ip_gateway($config, $ip->{host}, $noerr)) {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 # some remaining IP is not a gateway so we =
can't delete the subnet=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 return 0;=0A=
+=A0=A0=A0=A0=A0=A0 }=0A=
+=A0=A0=A0 }=0A=
+=A0=A0=A0 #all remaining IPs are gateways=0A=
+=A0=A0=A0 return 1;=0A=
+}=0A=
+=0A=
+sub verify_api {=0A=
+=A0=A0=A0 my ($class, $config) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $namespace =3D $config->{namespace};=0A=
+=0A=
+=A0=A0=A0 # check if the namespace and the status "Active" exist=0A=
+=A0=A0=A0 eval {=0A=
+=A0=A0=A0=A0=A0=A0 get_namespace_id($config, $namespace)=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 // die "namespace $namespace does not exist=
";=0A=
+=A0=A0=A0=A0=A0=A0 get_status_id($config, default_ip_status())=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 // die "the status " . default_ip_status() =
. " does not exist";=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 die "could not use nautobot api: $@\n";=0A=
+=A0=A0=A0 }=0A=
+}=0A=
+=0A=
+sub get_ips_from_mac {=0A=
+=A0=A0=A0 my ($class, $config, $mac, $zone) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $ip4 =3D undef;=0A=
+=A0=A0=A0 my $ip6 =3D undef;=0A=
+=0A=
+=A0=A0=A0 my $data =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0 nautobot_api_request($config, "GET", "/ipam/ip-addresse=
s/?q=3D$mac");=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 die "could not query ip address entry for mac $mac: $@"=
;=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 for my $ip (@{$data->{results}}) {=0A=
+=A0=A0=A0=A0=A0=A0 if ($ip->{ip_version} =3D=3D 4 && !$ip4) {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 ($ip4, undef) =3D split(/\//, $ip->{address=
});=0A=
+=A0=A0=A0=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0=A0=A0=A0 if ($ip->{ip_version} =3D=3D 6 && !$ip6) {=0A=
+=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 ($ip6, undef) =3D split(/\//, $ip->{address=
});=0A=
+=A0=A0=A0=A0=A0=A0 }=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 return ($ip4, $ip6);=0A=
+}=0A=
+=0A=
+sub on_update_hook {=0A=
+=A0=A0=A0 my ($class, $config) =3D @_;=0A=
+=0A=
+=A0=A0=A0 PVE::Network::SDN::Ipams::NautobotPlugin::verify_api($class, $co=
nfig);=0A=
+}=0A=
+=0A=
+sub get_ip_id {=0A=
+=A0=A0=A0 my ($config, $ip, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $result =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0 return nautobot_api_request($config, "GET", "/ipam/ip-a=
ddresses/?address=3D$ip");=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error while querying for ip $ip id: $@\n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $data =3D @{$result->{results}}[0];=0A=
+=A0=A0=A0 return $data->{id};=0A=
+}=0A=
+=0A=
+sub get_prefix_id {=0A=
+=A0=A0=A0 my ($config, $cidr, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $result =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0 return nautobot_api_request($config, "GET", "/ipam/pref=
ixes/?prefix=3D$cidr");=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error while querying for cidr $cidr prefix id: $@\=
n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $data =3D @{$result->{results}}[0];=0A=
+=A0=A0=A0 return $data->{id};=0A=
+}=0A=
+=0A=
+sub get_namespace_id {=0A=
+=A0=A0=A0 my ($config, $namespace, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $result =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0=A0 return nautobot_api_request($config, "GET", "/ipam/n=
amespaces/?name=3D$namespace");=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error while querying for namespace $namespace id: =
$@\n";=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $data =3D @{$result->{results}}[0];=0A=
+=A0=A0=A0 return $data->{id};=0A=
+}=0A=
+=0A=
+sub get_status_id {=0A=
+=A0=A0=A0 my ($config, $status, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $result =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0 return nautobot_api_request($config, "GET", "/extras/st=
atuses/?name=3D$status");=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error while querying for status $status id: $@\n";=
=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $data =3D @{$result->{results}}[0];=0A=
+=A0=A0=A0 return $data->{id};=0A=
+}=0A=
+=0A=
+sub is_ip_gateway {=0A=
+=A0=A0=A0 my ($config, $ip, $noerr) =3D @_;=0A=
+=0A=
+=A0=A0=A0 my $result =3D eval {=0A=
+=A0=A0=A0=A0=A0=A0=A0 return nautobot_api_request($config, "GET", "/ipam/i=
p-addresses/?address=3D$ip");=0A=
+=A0=A0=A0 };=0A=
+=A0=A0=A0 if ($@) {=0A=
+=A0=A0=A0=A0=A0=A0 return if $noerr;=0A=
+=A0=A0=A0=A0=A0=A0 die "error while checking if $ip is a gateway: $@\n";=
=0A=
+=A0=A0=A0 }=0A=
+=0A=
+=A0=A0=A0 my $data =3D @{$result->{results}}[0];=0A=
+=A0=A0=A0 return $data->{description} eq 'gateway';=0A=
+}=0A=
+=0A=
+1;=0A=
--=0A=
2.39.5=0A=
=0A=


--===============5260875589913889970==
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

--===============5260875589913889970==--