diff --git a/docs/release-notes/version-3.3.md b/docs/release-notes/version-3.3.md index 0e3cfd35f..92401c217 100644 --- a/docs/release-notes/version-3.3.md +++ b/docs/release-notes/version-3.3.md @@ -25,6 +25,7 @@ * [#5303](https://github.com/netbox-community/netbox/issues/5303) - A virtual machine may be assigned to a site and/or cluster * [#7120](https://github.com/netbox-community/netbox/issues/7120) - Add `termination_date` field to Circuit * [#7744](https://github.com/netbox-community/netbox/issues/7744) - Add `status` field to Location +* [#8171](https://github.com/netbox-community/netbox/issues/8171) - Populate next available address when cloning an IP * [#8222](https://github.com/netbox-community/netbox/issues/8222) - Enable the assignment of a VM to a specific host device within a cluster * [#8471](https://github.com/netbox-community/netbox/issues/8471) - Add `status` field to Cluster * [#8495](https://github.com/netbox-community/netbox/issues/8495) - Enable custom field grouping diff --git a/netbox/ipam/models/ip.py b/netbox/ipam/models/ip.py index db662f49c..0bc0e2364 100644 --- a/netbox/ipam/models/ip.py +++ b/netbox/ipam/models/ip.py @@ -857,6 +857,25 @@ class IPAddress(NetBoxModel): address__net_host=str(self.address.ip) ).exclude(pk=self.pk) + def get_next_available_ip(self): + """ + Return the next available IP address within this IP's network (if any) + """ + if self.address and self.address.broadcast: + start_ip = self.address.ip + 1 + end_ip = self.address.broadcast - 1 + if start_ip <= end_ip: + available_ips = netaddr.IPSet(netaddr.IPRange(start_ip, end_ip)) + available_ips -= netaddr.IPSet([ + address.ip for address in IPAddress.objects.filter( + vrf=self.vrf, + address__gt=self.address, + address__net_contained_or_equal=self.address.cidr + ).values_list('address', flat=True) + ]) + if available_ips: + return next(iter(available_ips)) + def clean(self): super().clean() @@ -907,6 +926,15 @@ class IPAddress(NetBoxModel): super().save(*args, **kwargs) + def clone(self): + attrs = super().clone() + + # Populate the address field with the next available IP (if any) + if next_available_ip := self.get_next_available_ip(): + attrs['address'] = next_available_ip + + return attrs + def to_objectchange(self, action): objectchange = super().to_objectchange(action) objectchange.related_object = self.assigned_object