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 [212.224.123.68])
	by lore.proxmox.com (Postfix) with ESMTPS id 5F70D1FF17C
	for <inbox@lore.proxmox.com>; Wed, 14 May 2025 16:00:17 +0200 (CEST)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
	by firstgate.proxmox.com (Proxmox) with ESMTP id 3D089756D;
	Wed, 14 May 2025 16:00:38 +0200 (CEST)
To: <h.duerr@proxmox.com>, <pve-devel@lists.proxmox.com>
Date: Wed, 14 May 2025 14:00:25 +0000
References: <20250506090041.53080-1-h.duerr@proxmox.com>
 <FR2PPF45D006270C594040B87F6E418E7548588A@FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM>
 <93bc24cc-2465-4ed2-b62f-d12305d5add3@proxmox.com>
 <FR2PPF45D0062706987369A6C23E8E8DF74858AA@FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM>
 <cf8752be-f877-4832-9a06-1ad4f71993b6@proxmox.com>
In-Reply-To: <cf8752be-f877-4832-9a06-1ad4f71993b6@proxmox.com>
MIME-Version: 1.0
Message-ID: <mailman.416.1747231237.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, jonatan.crystall@gwdg.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: Re: [pve-devel] [!!ACHTUNG extern!!] - Re: SPAM: RE: [!!ACHTUNG
 extern!!] - Re: SPAM: [PATCH pve-network v4 1/2] ipam: add Nautobot plugin
 - range workaround questions
Content-Type: multipart/mixed; boundary="===============5940173037338585320=="
Errors-To: pve-devel-bounces@lists.proxmox.com
Sender: "pve-devel" <pve-devel-bounces@lists.proxmox.com>

--===============5940173037338585320==
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) server-digest SHA256)
	(No client certificate requested)
	by lists.proxmox.com (Postfix) with ESMTPS id 54B7DCE69F
	for <pve-devel@lists.proxmox.com>; Wed, 14 May 2025 16:00:37 +0200 (CEST)
Received: from firstgate.proxmox.com (localhost [127.0.0.1])
	by firstgate.proxmox.com (Proxmox) with ESMTP id 3B6C5753B
	for <pve-devel@lists.proxmox.com>; Wed, 14 May 2025 16:00:37 +0200 (CEST)
Received: from BEUP281CU002.outbound.protection.outlook.com (mail-germanynorthazlp170100001.outbound.protection.outlook.com [IPv6:2a01:111:f403:c20b::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, 14 May 2025 16:00:35 +0200 (CEST)
ARC-Seal: i=1; a=rsa-sha256; s=arcselector10001; d=microsoft.com; cv=none;
 b=VA0LTXV/5TEjqBwVk8CPTBOnKdbjTVAHqtlt2xXet8GGXAP0TsfyEFC8fjy7Vs8Og4hh6pRGd7ro6plxd+DrQWOZJdNIPER+y92+CH7A9fldEP/WLxOkccsQ1mWIWTZXJX0DByD7iDN3V7aW1Gh6V7u3RIudkTKNTm/gfyCmvob8+UAZzYpSp8C5zsDRp0s6lXtedCKY+VYl/xj0CDoH6fc3EvQfaZ5IsjBUuCCMqWHE1qeLSs0dx2KC44/YpGbNY6Fsrr4jOgWRdFCmWud+OHnYCacpxJ9TzNqcEHY9h6GJsVwch1s/clE364f8Ws1L8bIbCB6qONxd44kPTKg9FA==
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=szKJ4F6+YJd5BdfzTSv1YUxKUJJgfmG/XUHx5zdiQys=;
 b=azRbusU49jUm9LK6HVOyGEuDEMOW0oe9GiItQvCMShe8axlvzefZ2k2vV9zM75lZUi7PISVDUuqZStYL9rQcXcT2lBYe7KVawWo4WzcSLqxIcR/T08z09JR079GWD1/w8PZ9ew8RRMkQY4ldwkaR3RCkrrr85P/Gq9nKOfH42ALuvFjl0Ulj+P1TcnqQ70axGBVsmWHOTcBLKFImjU+ZGfRjJxUCGblaxYaoTR+6mFK5AFlEN5uNv+I5g94NgTRkJB1X7udyco2Do0aWn4r5CalGgrOgreTuicN9+HJDr1hp4tEB+0Wyjh/P5R54v0jchvkfMnBFl46McQw0ig43uw==
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=szKJ4F6+YJd5BdfzTSv1YUxKUJJgfmG/XUHx5zdiQys=;
 b=gvnhnjiOw+pR3//JxBef8VN4S56ZLVLVzftTGc5WMdAUSp7p9ccwSGMn94OJCO/D6w6ZY4EHxTcDqLo1GdR59Akuz1d3GwlrmX8UEnNtdDk2w5l5mjARIkNuRhBpwrI++TuntpPiJEGAZx50592G/+EZ4RBiYm8Mlh4QX8G/cWU=
Received: from FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d18:2::2e)
 by FR1PPFE06F30640.DEUP281.PROD.OUTLOOK.COM (2603:10a6:d18::f9f) with
 Microsoft SMTP Server (version=TLS1_2,
 cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.8722.31; Wed, 14 May
 2025 14:00:25 +0000
Received: from FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM
 ([fe80::61d6:5538:7b39:dea1]) by FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM
 ([fe80::61d6:5538:7b39:dea1%7]) with mapi id 15.20.8722.027; Wed, 14 May 2025
 14:00:25 +0000
From: <Lou.Lecrivain@wdz.de>
To: <h.duerr@proxmox.com>, <pve-devel@lists.proxmox.com>
CC: <jonatan.crystall@gwdg.de>
Subject: RE: [!!ACHTUNG extern!!] - Re: SPAM: RE: [!!ACHTUNG extern!!] - Re:
 SPAM: [PATCH pve-network v4 1/2] ipam: add Nautobot plugin - range workaround
 questions
Thread-Topic: [!!ACHTUNG extern!!] - Re: SPAM: RE: [!!ACHTUNG extern!!] - Re:
 SPAM: [PATCH pve-network v4 1/2] ipam: add Nautobot plugin - range workaround
 questions
Thread-Index: AQHbvzDX9DivMuFErk+F8/u1jerq8LPJ/zQAgAAMAouABKjcgIADd9GP
Date: Wed, 14 May 2025 14:00:25 +0000
Message-ID:  <FR2PPF45D00627026643A8C54C7A090EC6F8591A@FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM>
References: <20250506090041.53080-1-h.duerr@proxmox.com>
 <FR2PPF45D006270C594040B87F6E418E7548588A@FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM>
 <93bc24cc-2465-4ed2-b62f-d12305d5add3@proxmox.com>
 <FR2PPF45D0062706987369A6C23E8E8DF74858AA@FR2PPF45D006270.DEUP281.PROD.OUTLOOK.COM>
 <cf8752be-f877-4832-9a06-1ad4f71993b6@proxmox.com>
In-Reply-To: <cf8752be-f877-4832-9a06-1ad4f71993b6@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_|FR1PPFE06F30640:EE_
x-ms-office365-filtering-correlation-id: 1e9c4894-5005-461d-bf89-08dd92efad0c
x-ms-exchange-senderadcheck: 1
x-ms-exchange-antispam-relay: 0
x-microsoft-antispam:  BCL:0;ARA:13230040|10070799003|366016|1800799024|376014|38070700018;
x-microsoft-antispam-message-info:  =?iso-8859-1?Q?HkqcXnOiEMYDVEvkjyxa1ecwLJzn8kVznBdxGHfdAF0tAPfEqT9UTCnbzc?=
 =?iso-8859-1?Q?RarkpWVb6/U6JXAl3tZfEZHMtQawX7m5YQGccP9JdJHit3f8GnLW6HZ2Yq?=
 =?iso-8859-1?Q?0PJ+Q0Kvn87F2fKLKK27g6EF5QYCUKlw2i42psdT9JJRcnQHV3s3syNBBc?=
 =?iso-8859-1?Q?5CFcWP4tAht1Al3TbAplereVSvjFg0RgXG0YRCF0vQLARbKd/EuXzKCv+/?=
 =?iso-8859-1?Q?8rTLjnyjufSKfFT9kez9vDt8n336sqD4Ts5u6RulkDtzWr82Zw+JHjjzlk?=
 =?iso-8859-1?Q?vxt2ZA0rboCbVwxO67HHAwI/U1oTM/UT0hDFUdtUZLOKwCPee5dEYa9t9w?=
 =?iso-8859-1?Q?jRb+R4lJXYMyf4EOYa5/dkky15svOg+/KOAayFW+CBbuzOZJMV85ye0F6E?=
 =?iso-8859-1?Q?rukkjQYSutvGtbYcJjpY7Q4kbP9uw7hxYBnjsAaJkVOCmli/jSsjZ42nHC?=
 =?iso-8859-1?Q?l9DvrwNSZY73Q7rRazLIuDy8pzI+NHXuWsmFyXiMEK/h5LLXweyd+zIGro?=
 =?iso-8859-1?Q?EDVaiQ12uwQwkTpsacdMDopXtZ2Xpk/RnpSKIHKlHfZA7JzIqHcRkhRtbo?=
 =?iso-8859-1?Q?hX8/Tf0Fee0XvLIm/3fMY0PrFuRIiD5RqYFVmKlOiUAAByBeQx6ASyiTgF?=
 =?iso-8859-1?Q?Kp3X6MyuZFD+DNSv+/yZix9Q7q0U4F4i1lCJnAPnr/X9nyVwVr5oD/zYcc?=
 =?iso-8859-1?Q?lq2jU762wv+shmayV3ADh/7aR2wLUkSjtSOsi3iXtWLn/vIsoF0EhbIQUD?=
 =?iso-8859-1?Q?eZpVrX7A3spS4xyUY+4Qwbnq1aFkYws+MZLWulZEME7aL1dZnY7FCqCJZi?=
 =?iso-8859-1?Q?ksZrVItNs6OF3LWkYMIG1pDXbeJRYnBgJmGu/tcXk8ytcdYLaFYy/rI+Nx?=
 =?iso-8859-1?Q?W5Paib4BWKCrlgCeOBqJNfpl8mZcIIkGXDSCyvxUWHPejCZ71eDuENuKsc?=
 =?iso-8859-1?Q?nm1XCuNcAiDXuDlBpIcDgPQKkS6ZlB+AdTGBeM1ZOy6OQMoo6PLY/XuUGd?=
 =?iso-8859-1?Q?o/uJJKSGXzFmFr5iX3hytO4B59kamThr+61R2gsNBozNWYOtg1VUwpeVyO?=
 =?iso-8859-1?Q?uiILgWvhRp08jrWqOCNJvZI+22bE2fqd+Cofnnb8KJ6AcVCFP6GtJuMFkV?=
 =?iso-8859-1?Q?gWhNCrugTXZi1myy5s9zZvG0sFyyBMed2cHEiTeygXh60/3SOVoaN0KS3l?=
 =?iso-8859-1?Q?O7DPqKlyix/OYmdTu5rVPMMUP2AxVcLiKRvTtB5X4LSbqeHsh0rjXjWGt7?=
 =?iso-8859-1?Q?1tFv74OpMAgLnrppxtgPtebXXPe8h9+8j7ZMa4I7kNb6ZXqOweboJyf+5k?=
 =?iso-8859-1?Q?Zgzqq2n/fsa4oRAzcLSCp9+amrR7sRQIfT999lSy19c+jMyon/DYTOsO0P?=
 =?iso-8859-1?Q?jXf2kzv4fhvEivX1c8kSNtyuKraZ2mcfUPRAq05Hhd6NJH+yZ4Lnkh4701?=
 =?iso-8859-1?Q?+JkIti6QI6qPnBmOtn0gRoeJ/s3kk3+IFws5PMCFDrvO8X8xlZ/favC6wA?=
 =?iso-8859-1?Q?U=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)(10070799003)(366016)(1800799024)(376014)(38070700018);DIR:OUT;SFP:1102;
x-ms-exchange-antispam-messagedata-chunkcount: 1
x-ms-exchange-antispam-messagedata-0:  =?iso-8859-1?Q?WlLdEPodUxSzXjs6mfU+qh4Q0JElmtEMLG9l8M6K14BW+2mrsbxRruNJN2?=
 =?iso-8859-1?Q?txI1vkG6efzImCC02a+20qe+zFan2WTF0e601L8XmZsDAGGgOHVvK4hneI?=
 =?iso-8859-1?Q?KQKln3gwhlJuPQbApNSqOowz9HSz8xHDFaZKgAZTQBcnbAfKFqFISSyWl6?=
 =?iso-8859-1?Q?5l39NQidVuyjVuUB5P90Bbamx+twpat8oPmVTYEeu8VRYaa6S842LvepI2?=
 =?iso-8859-1?Q?LUJXyzVdVvrWlfRs8kv1+/lnqx8keq57u/DVC9CnKxcyCf3B4dRsgokZ3/?=
 =?iso-8859-1?Q?EhydtEzvXZUsjUlEie0ca8tJPq5ky/mncKawrQ6dxWlMZm9CJKMAcQbHVV?=
 =?iso-8859-1?Q?1WufURE9ssHarJXLOKoYudG3NuHdjM40cTi2sIYqZNaWCiW6WyjAUrvm23?=
 =?iso-8859-1?Q?QU9l6aN8xhu63NdiDusmDDJoKzsQKTL1siprBxbgd1VRLQOA920HnOno9B?=
 =?iso-8859-1?Q?YC68FQBktefxK726qdPoSh02eyGtIfPtc6mD0IV5ZZak0581uzPIl6QLVq?=
 =?iso-8859-1?Q?Vozla9MJ/3BsEbvw5R0f7WnJIaSx+zIEEBXUabd3w1DVKomvn/a7O8poZv?=
 =?iso-8859-1?Q?+Vji0Y8kb9EWUA7sn2AgpMUMtCI6GbSHwDBz/pe70p6O8iOJPBMTfbuCLR?=
 =?iso-8859-1?Q?hduuueejV7fS6IFSwfK83hFhh00qaQ1AIUsNDVCRmTcFfhmgiuVBji8CxT?=
 =?iso-8859-1?Q?9nmHIfk3i5WqSfwq437p7MwASDNtTg9JWvVNdOhkn8P2QBr3LWgWthQGG9?=
 =?iso-8859-1?Q?jKG1GFFK9KzRUMTn15sKUMTMzOHIc0cZvd0zvtY3p/n/wpzSlMZeaeDXen?=
 =?iso-8859-1?Q?ut40HmYN2ojmYMRR+v/ylU4ZxF14U7nToKt8hN8cKOQfwit/ECB/0tbSc+?=
 =?iso-8859-1?Q?FhE3uZSeHLXBik/R+GKgRrUJVkjSvPCTvHN84aKtxlLXj5t4+DhCkb7rD5?=
 =?iso-8859-1?Q?I07D0E47qqxsw3CKGWGcK/w3HCGdCzcgdE+87RztM6HG+d9ULf/flinwQ6?=
 =?iso-8859-1?Q?fUPDp5P/MXtiiK/mClXQaljTYsWIF3eCjVKstkdlG7pUziHDPjpoe3OSYq?=
 =?iso-8859-1?Q?koia0QmYB3ikIchWK3IQ0pclGZX2dg3hLGMw49N9+VGHeDhTAtI4X6d36G?=
 =?iso-8859-1?Q?Tisjgquqb/u/QHRPGOMc8omJ353YCe40QGDeHA3KX7xtxGZIb39bBxSSlD?=
 =?iso-8859-1?Q?5xhtNsT+6Hvb93iowwzQDne8/v2LhCqrV+SIWah+wMii8PruUrVRRBO/SG?=
 =?iso-8859-1?Q?N3PitDULGy/GnxLAugzPaiJPeuZiy3pAUbpQ389ZfFI/tbmjk7/QCNt4vm?=
 =?iso-8859-1?Q?5nWsgtnDexBnzbow8THntMFMZhv4WhTXy4nWGyQEAkICC1zHEwp3VYyOp4?=
 =?iso-8859-1?Q?kVi0y9hUudUTQm43Z2S3gUcZNd18kyrgV5SRon9nXy431cmSREFEjdjfU7?=
 =?iso-8859-1?Q?htnxwXeY+S0cRdWlOQ3RK08csxuGCBNiweFSYEcIJK4uPow5wfnePFRi/a?=
 =?iso-8859-1?Q?9OqM0YmRBBdv+dMAjleW29lfSyu44yLv83sbUVFRA3C2UV1FLxgqsy/QiX?=
 =?iso-8859-1?Q?5yMP1smwVFvN9ukfHep7YjngP8eyWTx9W0ZxYFF12coq8q2hcMZ4QzoIUA?=
 =?iso-8859-1?Q?i/WuJ06o+8bNkc4ibrhk4eJeBMenrHbKwu?=
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: 1e9c4894-5005-461d-bf89-08dd92efad0c
X-MS-Exchange-CrossTenant-originalarrivaltime: 14 May 2025 14:00:25.4019
 (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: ltNfGZiFgxnHdvzXJsM0adks1q8Odn8yaaP/z+NHH4kEleIJAjGT15XbYgW3tY+N8l9V70/plrGZfUdc3BxdwQ==
X-MS-Exchange-Transport-CrossTenantHeadersStamped: FR1PPFE06F30640
X-SPAM-LEVEL: Spam detection results:  2
	ARC_SIGNED              0.001 Message has a ARC signature
	ARC_VALID               0.001 Message has a valid ARC signature
	AWL                    -1.940 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_ASCII_DIVIDERS        0.8 Email that uses ascii formatting dividers and possible spam tricks
	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_BLACK                 3 Contains an URL listed in the URIBL blacklist [plugin.pm]

Re Hannes,=0A=
=0A=
I took some time today to test the new implementation, but I believe there =
are still some issues with it.=0A=
=0A=
Here's what I did:=0A=
1. Create IPAM entry with Nautobot plugin=0A=
2. Create a zone of type simple with Nautobot as an IPAM=0A=
3. Create a VNet in said zone, with a /28 IPv4 subnet and two DHCP ranges w=
ithin it=0A=
=0A=
Afterwards I created a /24 IPv4 subnet for which the /28 is in it (so, what=
 the plugin=0A=
would have created itself).=0A=
=0A=
This results in a 403 from there, because we cannot create the same prefix =
*twice*:=0A=
+       # create temporary pool prefix=0A=
+       my $temp_prefix =3D eval {=0A=
+           return nautobot_api_request($config, "POST", "/ipam/prefixes/",=
 $params);=0A=
+       };=0A=
+=0A=
+       # skip if it is not possible to create it=0A=
+       next if $@;=0A=
And then the plugin goes into an infinite loop, doing the same request agai=
n and again.=0A=
=0A=
I don't think this failure mode is necessarily linked to the prefix size or=
 anything.=0A=
=0A=
BR=0A=
________________________________________=0A=
De :=A0Hannes Duerr <h.duerr@proxmox.com>=0A=
Envoy=E9 :=A0lundi 12 mai 2025 10:38=0A=
=C0 :=A0Lecrivain, Lou (WDZ) <Lou.Lecrivain@wdz.de>; pve-devel@lists.proxmo=
x.com <pve-devel@lists.proxmox.com>=0A=
Cc=A0:=A0jonatan.crystall@gwdg.de <jonatan.crystall@gwdg.de>=0A=
Objet :=A0[!!ACHTUNG extern!!] - Re: SPAM: RE: [!!ACHTUNG extern!!] - Re: S=
PAM: [PATCH pve-network v4 1/2] ipam: add Nautobot plugin - range workaroun=
d questions=0A=
=A0=0A=
=0A=
On 5/9/25 11:34, Lou.Lecrivain@wdz.de wrote:=0A=
> Re,=0A=
>=0A=
> Ok, thanks for clarifying. Indeed the offset parameter is ineffective (I =
have looked at the Nautobot code this morning). Tbqh I don't really like ha=
ving to create then delete an object in Nautobot for this, but I guess ther=
e's no other way, given the limitations we're faced with.=0A=
>=0A=
> Alternatively as a middle ground (and I think this would be much quicker =
than implementing IP ranges in Nautobot), we could implement the offset par=
ameter in Nautobot and change the original workaround code to fit this, and=
 then require a minimal Nautobot version (for ranges or the whole plugin). =
But IMO this doesn't mean we can't merge this. We could change it afterward=
s.=0A=
yes I understand that it's not necessarily the nicest solution, but=0A=
intuitively I preferred to implement it this way and have no API=0A=
version restrictions than to implement the offset parameter. But I=0A=
wouldn't have any hard feelings about adapting the code if Nautobot=0A=
supports the offset parameter and people prefer it that way.=0A=
Have you already tested the new implementation?=0A=
> I have no other remarks. Thanks again for your work.=0A=
>=0A=
> BR=0A=
> Lou=0A=
> ________________________________________=0A=
> De :=A0Hannes Duerr <h.duerr@proxmox.com>=0A=
> Envoy=E9 :=A0vendredi 9 mai 2025 10:46=0A=
> =C0 :=A0Lecrivain, Lou (WDZ) <Lou.Lecrivain@wdz.de>; pve-devel@lists.prox=
mox.com <pve-devel@lists.proxmox.com>=0A=
> Objet :=A0[!!ACHTUNG extern!!] - Re: SPAM: [PATCH pve-network v4 1/2] ipa=
m: add Nautobot plugin - range workaround questions=0A=
>=A0=A0=0A=
>=0A=
> On 5/7/25 11:17, Lou.Lecrivain@wdz.de=A0wrote:=0A=
> Hannes,=0A=
>=0A=
> Thank you for helping with getting the Netbox plugin in good shape, it's =
appreciated, 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 w=
ay more complex,=0A=
>=A0=A0 =A0 but also I guess I could've missed some corner case(s).=0A=
> Yes, there was the problem that the old version would possibly output=0A=
> invalid free IP addresses if the IP range did not start directly at=0A=
> the beginning of the prefix. Assuming you have 192.168.0.0/16 as=0A=
> prefix, all IP addresses are still free and you want free ip addresses=0A=
> in the range 192.168.6.1/16 - 192.168.7.255/16 then the old=0A=
> implementation would still have received the addresses=0A=
> 192.168.0.[0-50]/24 from the API. This could have been remedied by an=0A=
> offset parameter, but this is not implemented for the API endpoint.=0A=
> Increasing the `limit` is also not an option, as there is a maximum=0A=
> limit of 1000 and you can still run into problems.=0A=
> I have therefore opted for the current implementation.=0A=
>=0A=
>=0A=
> - What happens if we use a prefix with a CIDR greater than /24 in IPv4 or=
 /120 in IPv6?=0A=
>=A0=A0 =A0 Does not being able to slice it means that ranges with small pr=
efixes such as these=0A=
>=A0=A0 =A0 would be forbidden/not working?=0A=
> No, all prefixes should work. For smaller prefixes such as /26, we=0A=
> still create a /24 pool and search it for free ips, but then check=0A=
> again whether the ip is actually in the desired range in /26.=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.Le=
crivain@wdz.de>=0A=
> Objet :=A0[!!ACHTUNG extern!!] - [PATCH pve-network v4 1/2] ipam: add Nau=
tobot plugin=0A=
>=A0=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 =A0 active/online and initially sets the status to Active but when the=
=0A=
>=A0 =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 =A0 addresses, so we map as many pool prefixes that we search until we=
=0A=
>=A0 =A0 have searched the entire range for free ip addresses=0A=
> * Nautobot has the possibility to map DNS names to IP addresses.=0A=
>=A0 =A0 However, since we have no standardized way to set the DNS names in=
=0A=
>=A0 =A0 Proxmox VE (for containers the container name in Proxmox VE is set=
 as=0A=
>=A0 =A0 the host name in the container, but this is not possible for VMs) =
we=0A=
>=A0 =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 =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=A0 Changes from v3 -> v4:=0A=
>=A0 =A0=A0=A0 * Merge the check for empty prefixes/subnets into this commi=
t=0A=
>=A0 =A0=A0=A0 * Create nautobot_api_request to unify API calls=0A=
>=A0 =A0=A0=A0 * add update_subnet function=0A=
>=A0 =A0=A0=A0 * fix add_range_next_freeip=0A=
>=A0 =A0=A0=A0 * fix and unify error handling=0A=
>=A0 =A0=A0=A0 * Fix perl style=0A=
>=0A=
>=A0 =A0src/PVE/API2/Network/SDN/Ipams.pm=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 |=
=A0=A0 1 +=0A=
>=A0 =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=
>=A0 =A0src/PVE/Network/SDN/Ipams/Makefile=A0=A0=A0=A0=A0=A0=A0=A0=A0 |=A0=
=A0 2 +-=0A=
>=A0 =A0src/PVE/Network/SDN/Ipams/NautobotPlugin.pm | 513 +++++++++++++++++=
+++=0A=
>=A0 =A04 files changed, 518 insertions(+), 1 deletion(-)=0A=
>=A0 =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=
/Ipams.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=
>=A0 =A0use PVE::Network::SDN::Ipams::PVEPlugin;=0A=
>=A0 =A0use PVE::Network::SDN::Ipams::PhpIpamPlugin;=0A=
>=A0 =A0use PVE::Network::SDN::Ipams::NetboxPlugin;=0A=
> +use PVE::Network::SDN::Ipams::NautobotPlugin;=0A=
>=A0 =A0use PVE::Network::SDN::Dhcp;=0A=
>=A0 =A0use PVE::Network::SDN::Vnets;=0A=
>=A0 =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=A0=0A=
>=A0 =A0use PVE::Network::SDN::Ipams::PVEPlugin;=0A=
>=A0 =A0use PVE::Network::SDN::Ipams::NetboxPlugin;=0A=
> +use PVE::Network::SDN::Ipams::NautobotPlugin;=0A=
>=A0 =A0use PVE::Network::SDN::Ipams::PhpIpamPlugin;=0A=
>=A0 =A0use PVE::Network::SDN::Ipams::Plugin;=0A=
>=A0=A0=0A=
> +=0A=
>=A0 =A0PVE::Network::SDN::Ipams::PVEPlugin->register();=0A=
>=A0 =A0PVE::Network::SDN::Ipams::NetboxPlugin->register();=0A=
> +PVE::Network::SDN::Ipams::NautobotPlugin->register();=0A=
>=A0 =A0PVE::Network::SDN::Ipams::PhpIpamPlugin->register();=0A=
>=A0 =A0PVE::Network::SDN::Ipams::Plugin->init();=0A=
>=A0=A0=0A=
> diff --git a/src/PVE/Network/SDN/Ipams/Makefile b/src/PVE/Network/SDN/Ipa=
ms/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 Nautob=
otPlugin.pm=0A=
>=A0=A0=0A=
>=A0=A0=0A=
>=A0 =A0PERL5DIR=3D${DESTDIR}/usr/share/perl5=0A=
> diff --git a/src/PVE/Network/SDN/Ipams/NautobotPlugin.pm b/src/PVE/Networ=
k/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; ch=
arset=3DUTF-8',=0A=
> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 'Authorization' =3D> "token $config->{tok=
en}",=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 alre=
ady 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, $=
noerr) =3D @_;=0A=
> +=A0=A0=A0 # dhcp ranges are not supported in nautobot so we don't have t=
o 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, $interna=
lid, $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 cont=
ains 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/prefix=
es/$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, und=
ef,=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-addre=
sses/", $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 =
already exist: $@"=0A=
> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 if !is_ip_gateway($config, $i=
p, $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 alre=
ady 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, $=
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 (!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'}, $p=
refix_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_si=
ze";=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, "POS=
T", "/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 fin=
d 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($=
entry->{address});=0A=
> +=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0=A0 # comparison is only possible=
 because 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 && =
$ip <=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", "=
/ipam/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_ra=
nge->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->{hos=
tname}, $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-addr=
esses/$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-add=
resses/$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$namespa=
ce&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}, $n=
oerr);=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$namespa=
ce&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 w=
e 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 exi=
st";=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-addres=
ses/?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->{addre=
ss});=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->{addre=
ss});=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, $=
config);=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=
-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 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/pr=
efixes/?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=
/namespaces/?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/=
statuses/?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=
/ip-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=
>=0A=
>=0A=


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

--===============5940173037338585320==--