* [pve-devel] [PATCH v3 pve-container 1/1] add ipam support
2021-05-24 15:19 [pve-devel] [PATCH v3 pve-container 0/1] add ipam support Alexandre Derumier
@ 2021-05-24 15:19 ` Alexandre Derumier
0 siblings, 0 replies; 2+ messages in thread
From: Alexandre Derumier @ 2021-05-24 15:19 UTC (permalink / raw)
To: pve-devel
This add ipam support for nic using sdn vnets.
- if ips are specified manally, we verify that subnet exist on vnet, and we register ip in ipam
- if nic is on a vnet, but no ip is specified, we auto find the next available ips in this vnet subnet
- if a gateway is defined on the subnet, we override current vm nc gateway
- extra informations like mac address,hostname are registered in external ipam.
- if dns server exist in the zone, we register vm hostname in ipam for each ip address
- ips addresses are removed on ct destroy, nic destroy, ip change or vnet change
- snapshot rollback: if ip from snapshot is already used, we keep the current running ip
- backup restore to new vmid: if ip from backup is already used, we set ips has undef
---
src/PVE/LXC.pm | 144 ++++++++++++++++++
src/PVE/LXC/Config.pm | 58 +++++++
src/PVE/LXC/Create.pm | 33 +++-
src/test/Makefile | 5 +-
.../ipam.db | 18 +++
.../ipam.db.expected | 17 +++
.../ipam_config | 7 +
.../net | 7 +
.../net.expected | 7 +
.../oldnet | 7 +
.../sdn_config | 35 +++++
.../ipam.db | 18 +++
.../ipam.db.expected | 18 +++
.../ipam_config | 7 +
.../net | 7 +
.../net.expected | 7 +
.../oldnet | 7 +
.../sdn_config | 35 +++++
.../ipam.db | 18 +++
.../ipam.db.expected | 17 +++
.../ipam_config | 7 +
.../net | 7 +
.../net.expected | 7 +
.../oldnet | 7 +
.../sdn_config | 35 +++++
.../ipam.db | 18 +++
.../ipam.db.expected | 18 +++
.../ipam_config | 7 +
.../net | 6 +
.../net.expected | 7 +
.../oldnet | 7 +
.../sdn_config | 35 +++++
.../ipam.db | 18 +++
.../ipam.db.expected | 18 +++
.../ipam_config | 7 +
.../net | 6 +
.../net.expected | 7 +
.../oldnet | 7 +
.../sdn_config | 35 +++++
.../ipv4_changeip_samevnet_with_ipam/ipam.db | 18 +++
.../ipam.db.expected | 18 +++
.../ipam_config | 7 +
.../ipv4_changeip_samevnet_with_ipam/net | 7 +
.../net.expected | 7 +
.../ipv4_changeip_samevnet_with_ipam/oldnet | 7 +
.../sdn_config | 35 +++++
.../ipv4_changeip_vmbr0_to_ipamvnet/ipam.db | 17 +++
.../ipam.db.expected | 18 +++
.../ipam_config | 7 +
.../ipams/ipv4_changeip_vmbr0_to_ipamvnet/net | 7 +
.../net.expected | 7 +
.../ipv4_changeip_vmbr0_to_ipamvnet/oldnet | 7 +
.../sdn_config | 35 +++++
.../ipv4_changeip_vmbr0_to_noipamvnet/ipam.db | 17 +++
.../ipam.db.expected | 17 +++
.../ipam_config | 7 +
.../ipv4_changeip_vmbr0_to_noipamvnet/net | 7 +
.../net.expected | 7 +
.../ipv4_changeip_vmbr0_to_noipamvnet/oldnet | 7 +
.../sdn_config | 35 +++++
.../ipam.db | 18 +++
.../ipam.db.expected | 18 +++
.../ipam_config | 7 +
.../net | 7 +
.../net.expected | 8 +
.../oldnet | 7 +
.../sdn_config | 38 +++++
.../ipam.db | 18 +++
.../ipam.db.expected | 17 +++
.../ipam_config | 7 +
.../net | 8 +
.../net.expected | 8 +
.../oldnet | 7 +
.../sdn_config | 38 +++++
.../ipam.db | 18 +++
.../ipam.db.expected | 18 +++
.../ipam_config | 7 +
.../net | 7 +
.../net.expected | 7 +
.../oldnet | 8 +
.../sdn_config | 36 +++++
.../ipams/ipv4_updateipam_ipamvnet/ipam.db | 18 +++
.../ipv4_updateipam_ipamvnet/ipam.db.expected | 18 +++
.../ipv4_updateipam_ipamvnet/ipam_config | 7 +
src/test/ipams/ipv4_updateipam_ipamvnet/net | 7 +
.../ipv4_updateipam_ipamvnet/net.expected | 7 +
.../ipams/ipv4_updateipam_ipamvnet/oldnet | 7 +
.../ipams/ipv4_updateipam_ipamvnet/sdn_config | 35 +++++
.../ipam.db | 19 +++
.../ipam.db.expected | 19 +++
.../ipam_config | 7 +
.../ipv4v6_next_free_samevnet_with_ipam/net | 6 +
.../net.expected | 8 +
.../oldnet | 8 +
.../sdn_config | 39 +++++
.../ipam.db | 20 +++
.../ipam.db.expected | 20 +++
.../ipam_config | 7 +
.../net | 6 +
.../net.expected | 8 +
.../oldnet | 8 +
.../sdn_config | 39 +++++
.../ipams/ipv4v6_updateipam_ipamvnet/ipam.db | 19 +++
.../ipam.db.expected | 19 +++
.../ipv4v6_updateipam_ipamvnet/ipam_config | 7 +
src/test/ipams/ipv4v6_updateipam_ipamvnet/net | 8 +
.../ipv4v6_updateipam_ipamvnet/net.expected | 8 +
.../ipams/ipv4v6_updateipam_ipamvnet/oldnet | 8 +
.../ipv4v6_updateipam_ipamvnet/sdn_config | 39 +++++
.../ipam.db | 19 +++
.../ipam.db.expected | 19 +++
.../ipam_config | 7 +
.../ipv4v6_updateipam_ipamvnet_failingv6/net | 8 +
.../net.expected | 8 +
.../oldnet | 8 +
.../sdn_config | 35 +++++
.../ipam.db | 18 +++
.../ipam.db.expected | 18 +++
.../ipam_config | 7 +
.../net | 7 +
.../net.expected | 7 +
.../oldnet | 7 +
.../sdn_config | 35 +++++
.../ipam.db | 18 +++
.../ipam.db.expected | 18 +++
.../ipam_config | 7 +
.../net | 7 +
.../net.expected | 8 +
.../oldnet | 7 +
.../sdn_config | 37 +++++
.../ipams/ipv6_updateipam_ipamvnet/ipam.db | 14 ++
.../ipv6_updateipam_ipamvnet/ipam.db.expected | 14 ++
.../ipv6_updateipam_ipamvnet/ipam_config | 7 +
src/test/ipams/ipv6_updateipam_ipamvnet/net | 7 +
.../ipv6_updateipam_ipamvnet/net.expected | 7 +
.../ipams/ipv6_updateipam_ipamvnet/oldnet | 7 +
.../ipams/ipv6_updateipam_ipamvnet/sdn_config | 35 +++++
src/test/run_ipam_tests.pl | 126 +++++++++++++++
src/test/snapshot-input/sdn/subnets.cfg | 0
src/test/snapshot-test.pm | 1 -
140 files changed, 2266 insertions(+), 10 deletions(-)
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam.db
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam_config
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/net
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/net.expected
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/oldnet
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/sdn_config
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam.db
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam_config
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/net
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/net.expected
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/oldnet
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/sdn_config
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam.db
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam_config
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/net
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/net.expected
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/oldnet
create mode 100644 src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/sdn_config
create mode 100644 src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam.db
create mode 100644 src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam_config
create mode 100644 src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/net
create mode 100644 src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/net.expected
create mode 100644 src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/oldnet
create mode 100644 src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/sdn_config
create mode 100644 src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam.db
create mode 100644 src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam_config
create mode 100644 src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/net
create mode 100644 src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/net.expected
create mode 100644 src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/oldnet
create mode 100644 src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/sdn_config
create mode 100644 src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam.db
create mode 100644 src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam_config
create mode 100644 src/test/ipams/ipv4_changeip_samevnet_with_ipam/net
create mode 100644 src/test/ipams/ipv4_changeip_samevnet_with_ipam/net.expected
create mode 100644 src/test/ipams/ipv4_changeip_samevnet_with_ipam/oldnet
create mode 100644 src/test/ipams/ipv4_changeip_samevnet_with_ipam/sdn_config
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam.db
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam_config
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/net
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/net.expected
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/oldnet
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/sdn_config
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam.db
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam_config
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/net
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/net.expected
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/oldnet
create mode 100644 src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/sdn_config
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam_config
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net.expected
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/oldnet
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/sdn_config
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam.db
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam_config
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/net
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/net.expected
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/oldnet
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/sdn_config
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam.db
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam_config
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/net
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/net.expected
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/oldnet
create mode 100644 src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/sdn_config
create mode 100644 src/test/ipams/ipv4_updateipam_ipamvnet/ipam.db
create mode 100644 src/test/ipams/ipv4_updateipam_ipamvnet/ipam.db.expected
create mode 100644 src/test/ipams/ipv4_updateipam_ipamvnet/ipam_config
create mode 100644 src/test/ipams/ipv4_updateipam_ipamvnet/net
create mode 100644 src/test/ipams/ipv4_updateipam_ipamvnet/net.expected
create mode 100644 src/test/ipams/ipv4_updateipam_ipamvnet/oldnet
create mode 100644 src/test/ipams/ipv4_updateipam_ipamvnet/sdn_config
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam.db
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam_config
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/net
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/net.expected
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/oldnet
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/sdn_config
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam.db
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam.db.expected
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam_config
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/net
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/net.expected
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/oldnet
create mode 100644 src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/sdn_config
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam.db
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam.db.expected
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam_config
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet/net
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet/net.expected
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet/oldnet
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet/sdn_config
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam.db
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam.db.expected
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam_config
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/net
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/net.expected
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/oldnet
create mode 100644 src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/sdn_config
create mode 100644 src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam.db
create mode 100644 src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam_config
create mode 100644 src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/net
create mode 100644 src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/net.expected
create mode 100644 src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/oldnet
create mode 100644 src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/sdn_config
create mode 100644 src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db
create mode 100644 src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db.expected
create mode 100644 src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam_config
create mode 100644 src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net
create mode 100644 src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net.expected
create mode 100644 src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/oldnet
create mode 100644 src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/sdn_config
create mode 100644 src/test/ipams/ipv6_updateipam_ipamvnet/ipam.db
create mode 100644 src/test/ipams/ipv6_updateipam_ipamvnet/ipam.db.expected
create mode 100644 src/test/ipams/ipv6_updateipam_ipamvnet/ipam_config
create mode 100644 src/test/ipams/ipv6_updateipam_ipamvnet/net
create mode 100644 src/test/ipams/ipv6_updateipam_ipamvnet/net.expected
create mode 100644 src/test/ipams/ipv6_updateipam_ipamvnet/oldnet
create mode 100644 src/test/ipams/ipv6_updateipam_ipamvnet/sdn_config
create mode 100755 src/test/run_ipam_tests.pl
create mode 100644 src/test/snapshot-input/sdn/subnets.cfg
diff --git a/src/PVE/LXC.pm b/src/PVE/LXC.pm
index 7e6f378..a1eee0e 100644
--- a/src/PVE/LXC.pm
+++ b/src/PVE/LXC.pm
@@ -847,6 +847,9 @@ sub destroy_lxc_container {
rmdir "/var/lib/lxc/$vmid/rootfs";
unlink "/var/lib/lxc/$vmid/config";
rmdir "/var/lib/lxc/$vmid";
+
+ PVE::LXC::destroy_net_ip($conf);
+
if (defined $replacement_conf) {
PVE::LXC::Config->write_config($vmid, $replacement_conf);
} else {
@@ -1077,6 +1080,147 @@ sub update_ipconfig {
}
+sub is_static_ip {
+ my ($ip) = @_;
+
+ return 1 if $ip !~ m/(dhcp|manual|auto)$/;
+}
+
+sub ip_has_changed {
+ my ($oldip, $ip) = @_;
+
+ return 1 if !$oldip && $ip;
+ return 1 if !$ip && $oldip;
+ return 1 if $ip && $oldip && $ip ne $oldip;
+}
+
+my $add_net_ip = sub {
+ my ($version, $net, $oldnet, $hostname, $oldhostname, $description) = @_;
+
+ my $oldbridge = $oldnet->{bridge};
+ my $bridge = $net->{bridge};
+ my $mac = $net->{hwaddr};
+ my $ipfield = $version == 4 ? "ip" : "ip6";
+ my $ip = $net->{$ipfield};
+ my $oldip = $oldnet->{$ipfield};
+ my $subnets = PVE::Network::SDN::Vnets::get_subnets($bridge);
+ return if !keys %{$subnets};
+
+ eval {
+ if (!$ip) {
+ my $next_free_ip = PVE::Network::SDN::Vnets::get_next_free_cidr($bridge, $hostname, $mac, $description, $version);
+ $net->{$ipfield} = $next_free_ip if $next_free_ip;
+ } elsif (is_static_ip($ip)) {
+ if (!ip_has_changed($oldip, $ip)) {
+ #update ip attributes if no ip address change
+ PVE::Network::SDN::Vnets::update_cidr($bridge, $ip, $hostname, $oldhostname, $mac, $description);
+ } else {
+ PVE::Network::SDN::Vnets::add_cidr($bridge, $ip, $hostname, $mac, $description);
+ }
+ }
+ };
+ if ($@) {
+ die $@;
+ }
+};
+
+my $del_net_ip = sub {
+ my ($version, $oldnet, $net, $hostname, $description) = @_;
+
+ my $oldbridge = $oldnet->{bridge};
+ my $bridge = $net->{bridge};
+ my $ip = $version == 4 ? $net->{ip} : $net->{ip6};
+ my $oldip = $version == 4 ? $oldnet->{ip} : $oldnet->{ip6};
+
+ return if !$oldip || !is_static_ip($oldip);
+
+ my $subnets = PVE::Network::SDN::Vnets::get_subnets($oldbridge);
+ return if !keys %{$subnets};
+
+ eval {
+ PVE::Network::SDN::Vnets::del_cidr($oldbridge, $oldip, $hostname, $description) if !$bridge || $bridge ne $oldbridge || !$ip || $ip ne $oldip;
+ };
+ warn $@ if $@;
+};
+
+
+my $update_net_gateway = sub {
+ my ($version, $net) = @_;
+
+ my $bridge = $net->{bridge};
+ my $netip = $version == 4 ? $net->{ip} : $net->{ip6};
+ my $gwfield = $version == 4 ? "gw" : "gw6";
+
+ return if (!$netip || !is_static_ip($netip));
+
+ my $subnets = PVE::Network::SDN::Vnets::get_subnets($bridge);
+ return if !keys %{$subnets};
+
+ #update gateway
+ my ($ip, $mask) = split(/\//, $netip);
+ my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets);
+ my $gw = $subnet->{gateway} if $subnet->{gateway};
+ $net->{$gwfield} = $gw if $gw;
+
+};
+
+sub destroy_net_ip {
+ my ($conf) = @_;
+
+ return if !$have_sdn;
+
+ foreach my $opt (keys %$conf) {
+ next if $opt !~ m/^net(\d+)$/;
+ my $netid = $1;
+ my $oldnet = PVE::LXC::Config->parse_lxc_network($conf->{$opt});
+ my $hostname = $conf->{hostname};
+ my $description = '';
+ delete_net_ip($hostname, $oldnet, undef, $description);
+ }
+}
+
+sub update_net_ip {
+ my ($net, $oldnet, $hostname, $oldhostname, $description) = @_;
+
+ return if !$have_sdn;
+
+ eval {
+ &$add_net_ip(4, $net, $oldnet, $hostname, $oldhostname, $description);
+ };
+ if ($@) {
+ $net->{ip} = $oldnet->{ip};
+ die "can't change ip4: $@\n";
+ }
+
+ eval {
+ &$add_net_ip(6, $net, $oldnet, $hostname, $oldhostname, $description);
+ };
+ if ($@) {
+ my $err = $@;
+ #if error, delete previously added ipv4
+ eval {
+ PVE::Network::SDN::Vnets::del_cidr($net->{bridge}, $net->{ip}, $hostname, $description) if ip_has_changed($oldnet->{ip}, $net->{ip});
+ };
+ $net->{ip6} = $oldnet->{ip6};
+ $net->{ip} = $oldnet->{ip};
+ die "error change ipv6: $err\n";
+ }
+
+ delete_net_ip($oldhostname, $oldnet, $net, $description);
+
+ &$update_net_gateway(4, $net);
+ &$update_net_gateway(6, $net);
+}
+
+sub delete_net_ip {
+ my ($hostname, $oldnet, $net, $description) = @_;
+
+ return if !$have_sdn;
+
+ &$del_net_ip(4, $oldnet, $net, $hostname, $description);
+ &$del_net_ip(6, $oldnet, $net, $hostname, $description);
+}
+
my $open_namespace = sub {
my ($vmid, $pid, $kind) = @_;
sysopen my $fd, "/proc/$pid/ns/$kind", O_RDONLY
diff --git a/src/PVE/LXC/Config.pm b/src/PVE/LXC/Config.pm
index 7b82f65..da8cbfa 100644
--- a/src/PVE/LXC/Config.pm
+++ b/src/PVE/LXC/Config.pm
@@ -213,6 +213,43 @@ sub __snapshot_delete_vol_snapshot {
push @$unused, $mountpoint->{volume};
}
+sub __snapshot_rollback_hook {
+ my ($class, $vmid, $conf, $snap, $prepare, $data) = @_;
+
+ if($prepare) {
+ $data->{oldconf} = $conf;
+ } else {
+ my $noerr = 1; #if ip change and already use, we keep the current ip
+ my $oldconf = $data->{oldconf};
+ #update ip of current and new net interface
+ foreach my $opt (keys %$conf) {
+ next if $opt !~ m/^net(\d+)$/;
+ my $netid = $1;
+ my $oldnet = $class->parse_lxc_network($oldconf->{$opt});
+ my $net = $class->parse_lxc_network($conf->{$opt});
+ my $hostname = $conf->{hostname};
+ my $description = "vm:$vmid net:$netid";
+ eval {
+ PVE::LXC::update_net_ip($net, $oldnet, $hostname, $hostname, $description);
+ };
+ if ($@) {
+ warn "error: $@ : keep current ip configuration\n";
+ $conf->{$opt} = $class->print_lxc_network($net);
+ }
+ }
+
+ #remove ip of removed net interface
+ foreach my $opt (keys %$oldconf) {
+ next if $opt !~ m/^net(\d+)$/;
+ next if defined($conf->{$opt});
+ my $netid = $1;
+ my $oldnet = $class->parse_lxc_network($oldconf->{$opt});
+ my $hostname = $oldconf->{hostname};
+ my $description = "vm:$vmid net:$netid";
+ PVE::LXC::delete_net_ip($hostname, $oldnet, undef, "vm:$vmid net:$netid");
+ }
+ }
+}
sub __snapshot_rollback_vol_possible {
my ($class, $mountpoint, $snapname) = @_;
@@ -1044,6 +1081,10 @@ sub update_pct_config {
$class->check_protection($conf, "can't remove CT $vmid drive '$opt'");
} elsif ($opt eq 'unprivileged') {
die "unable to delete read-only option: '$opt'\n";
+ } elsif ($opt =~ m/^net(\d+)$/) {
+ my $netid = $1;
+ my $oldnet = $class->parse_lxc_network($conf->{$opt});
+ PVE::LXC::delete_net_ip($conf->{hostname}, $oldnet, undef, "vm:$vmid net:$netid");
}
$class->add_to_pending_delete($conf, $opt);
}
@@ -1071,7 +1112,23 @@ sub update_pct_config {
$value = PVE::LXC::verify_searchdomain_list($value);
} elsif ($opt eq 'unprivileged') {
die "unable to modify read-only option: '$opt'\n";
+ } elsif ($opt =~ m/^net(\d+)$/) {
+ my $netid = $1;
+ my $net = $class->parse_lxc_network($value);
+ my $oldnet = $class->parse_lxc_network($conf->{$opt});
+ my $hostname = $param->{hostname} ? $param->{hostname} : $conf->{hostname};
+ PVE::LXC::update_net_ip($net, $oldnet, $hostname, $hostname, "vm:$vmid net:$netid");
+ $value = $class->print_lxc_network($net);
+ } elsif ($opt eq 'hostname') {
+ #if hostname change, update ipam + dns for each ip
+ foreach my $netopt (sort keys %$conf) {
+ next if $netopt !~ m/^net(\d+)$/;
+ my $netid = $1;
+ my $net = $class->parse_lxc_network($conf->{$netopt});
+ PVE::LXC::update_net_ip($net, $net, $value, $conf->{hostname}, "vm:$vmid net:$netid");
+ }
}
+
$conf->{pending}->{$opt} = $value;
$class->remove_from_pending_delete($conf, $opt);
}
@@ -1663,4 +1720,5 @@ sub get_backup_volumes {
return $return_volumes;
}
+
1;
diff --git a/src/PVE/LXC/Create.pm b/src/PVE/LXC/Create.pm
index 82d7ad9..e43433b 100644
--- a/src/PVE/LXC/Create.pm
+++ b/src/PVE/LXC/Create.pm
@@ -278,7 +278,7 @@ sub restore_configuration_from_proxmox_backup {
my $oldconf = recover_config_from_proxmox_backup($storage_cfg, $archive, $vmid);
- sanitize_and_merge_config($conf, $oldconf, $restricted, $unique);
+ sanitize_and_merge_config($conf, $oldconf, $restricted, $unique, $vmid);
my $cmd = "files";
@@ -304,7 +304,7 @@ sub restore_configuration_from_proxmox_backup {
}
sub sanitize_and_merge_config {
- my ($conf, $oldconf, $restricted, $unique) = @_;
+ my ($conf, $oldconf, $restricted, $unique, $vmid) = @_;
foreach my $key (keys %$oldconf) {
next if $key eq 'digest' || $key eq 'rootfs' || $key eq 'snapshots' || $key eq 'unprivileged' || $key eq 'parent';
@@ -325,12 +325,29 @@ sub sanitize_and_merge_config {
next;
}
- if ($unique && $key =~ /^net\d+$/) {
+ if ($key =~ m/^net(\d+)$/) {
+ my $netid = $1;
my $net = PVE::LXC::Config->parse_lxc_network($oldconf->{$key});
- my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
- $net->{hwaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
- $conf->{$key} = PVE::LXC::Config->print_lxc_network($net);
- next;
+ my $hostname = $oldconf->{hostname};
+ my $description = "vm:$vmid net:$netid";
+
+ if ($unique) {
+ my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
+ $net->{hwaddr} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
+ $conf->{$key} = PVE::LXC::Config->print_lxc_network($net);
+ }
+
+ eval {
+ PVE::LXC::update_net_ip($net, undef, $hostname, $hostname, $description);
+ };
+ if ($@) {
+ warn "error: $@ : remove ipconfig from net$netid\n";
+ $net->{ip} = undef;
+ $net->{gw} = undef;
+ $net->{ip6} = undef;
+ $net->{gw6} = undef;
+ $conf->{$key} = PVE::LXC::Config->print_lxc_network($net);
+ }
}
$conf->{$key} = $oldconf->{$key} if !defined($conf->{$key});
}
@@ -348,7 +365,7 @@ sub restore_configuration_from_etc_vzdump {
my $raw = PVE::Tools::file_get_contents($pct_cfg_fn);
my $oldconf = PVE::LXC::Config::parse_pct_config("/lxc/$vmid.conf", $raw);
- sanitize_and_merge_config($conf, $oldconf, $restricted, $unique);
+ sanitize_and_merge_config($conf, $oldconf, $restricted, $unique, $vmid);
unlink($pct_cfg_fn);
diff --git a/src/test/Makefile b/src/test/Makefile
index 8734879..68133aa 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -2,7 +2,7 @@ RUN_USERNS := lxc-usernsexec -m "u:0:`id -u`:1" -m "g:0:`id -g`:1" --
all: test
-test: test_setup test_snapshot test_bindmount
+test: test_setup test_snapshot test_bindmount test_ipam
test_setup: run_setup_tests.pl
$(RUN_USERNS) ./run_setup_tests.pl
@@ -13,5 +13,8 @@ test_snapshot: run_snapshot_tests.pl
test_bindmount: bindmount_test.pl
$(RUN_USERNS) ./bindmount_test.pl
+test_ipam: run_ipam_tests.pl
+ ./run_ipam_tests.pl
+
clean:
rm -rf tmprootfs
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam.db b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam.db.expected b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam.db.expected
new file mode 100644
index 0000000..3d36a24
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam.db.expected
@@ -0,0 +1,17 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam_config b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/net b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/net
new file mode 100644
index 0000000..26740b4
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet3',
+ 'ip' => '192.168.2.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/net.expected b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/net.expected
new file mode 100644
index 0000000..26740b4
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet3',
+ 'ip' => '192.168.2.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/oldnet b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/oldnet
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/sdn_config b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnet_noipam/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam.db b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam.db.expected b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam.db.expected
new file mode 100644
index 0000000..f576915
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ "192.168.1.1" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam_config b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/net b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/net
new file mode 100644
index 0000000..4031d65
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip' => '192.168.1.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/net.expected b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/net.expected
new file mode 100644
index 0000000..4031d65
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip' => '192.168.1.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/oldnet b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/oldnet
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/sdn_config b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_othervnetipam/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam.db b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam.db.expected b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam.db.expected
new file mode 100644
index 0000000..3d36a24
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam.db.expected
@@ -0,0 +1,17 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam_config b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/net b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/net
new file mode 100644
index 0000000..908f573
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'vmbr0',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/net.expected b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/net.expected
new file mode 100644
index 0000000..908f573
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'vmbr0',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/oldnet b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/oldnet
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/sdn_config b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_ipamvnet_to_vmbr0_noipam/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam.db b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam.db.expected b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam.db.expected
new file mode 100644
index 0000000..f576915
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ "192.168.1.1" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam_config b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/net b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/net
new file mode 100644
index 0000000..a73e889
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/net
@@ -0,0 +1,6 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/net.expected b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/net.expected
new file mode 100644
index 0000000..4031d65
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip' => '192.168.1.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/oldnet b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/oldnet
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/sdn_config b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_othervnet_with_ipam/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam.db b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam.db.expected b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam.db.expected
new file mode 100644
index 0000000..2287b85
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.2" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam_config b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/net b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/net
new file mode 100644
index 0000000..b68d367
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/net
@@ -0,0 +1,6 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/net.expected b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/net.expected
new file mode 100644
index 0000000..97a3915
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.2/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/oldnet b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/oldnet
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/sdn_config b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_next_free_samevnet_with_ipam/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam.db b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam.db.expected b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam.db.expected
new file mode 100644
index 0000000..2287b85
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.2" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam_config b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_changeip_samevnet_with_ipam/net b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/net
new file mode 100644
index 0000000..97a3915
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.2/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_samevnet_with_ipam/net.expected b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/net.expected
new file mode 100644
index 0000000..97a3915
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.2/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_samevnet_with_ipam/oldnet b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/oldnet
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_samevnet_with_ipam/sdn_config b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_samevnet_with_ipam/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam.db b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam.db
new file mode 100644
index 0000000..3d36a24
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam.db
@@ -0,0 +1,17 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam.db.expected b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam.db.expected
new file mode 100644
index 0000000..e0906aa
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ '192.168.0.1' => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam_config b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/net b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/net
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/net.expected b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/net.expected
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/oldnet b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/oldnet
new file mode 100644
index 0000000..dbd70c4
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'vmbr0',
+ 'ip' => '10.0.0.1/24',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/sdn_config b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_ipamvnet/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam.db b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam.db
new file mode 100644
index 0000000..3d36a24
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam.db
@@ -0,0 +1,17 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam.db.expected b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam.db.expected
new file mode 100644
index 0000000..3d36a24
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam.db.expected
@@ -0,0 +1,17 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam_config b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/net b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/net
new file mode 100644
index 0000000..26740b4
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet3',
+ 'ip' => '192.168.2.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/net.expected b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/net.expected
new file mode 100644
index 0000000..26740b4
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet3',
+ 'ip' => '192.168.2.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/oldnet b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/oldnet
new file mode 100644
index 0000000..dbd70c4
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'vmbr0',
+ 'ip' => '10.0.0.1/24',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/sdn_config b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4_changeip_vmbr0_to_noipamvnet/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db.expected b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db.expected
new file mode 100644
index 0000000..f576915
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ "192.168.1.1" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam_config b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net
new file mode 100644
index 0000000..4031d65
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip' => '192.168.1.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net.expected b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net.expected
new file mode 100644
index 0000000..d79ce80
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net.expected
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip' => '192.168.1.1/30',
+ 'gw' => '192.168.1.2',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/oldnet b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/oldnet
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/sdn_config b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/sdn_config
new file mode 100644
index 0000000..da2eded
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetipam/sdn_config
@@ -0,0 +1,38 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '192.168.0.2'
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ 'gateway' => '192.168.1.2'
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ 'gateway' => '192.168.2.3'
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam.db b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam.db.expected b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam.db.expected
new file mode 100644
index 0000000..3d36a24
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam.db.expected
@@ -0,0 +1,17 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam_config b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/net b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/net
new file mode 100644
index 0000000..ae2b7bc
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/net
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet3',
+ 'ip' => '192.168.2.1/30',
+ 'gw' => '192.168.2.2',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/net.expected b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/net.expected
new file mode 100644
index 0000000..ae2b7bc
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/net.expected
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet3',
+ 'ip' => '192.168.2.1/30',
+ 'gw' => '192.168.2.2',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/oldnet b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/oldnet
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/sdn_config b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/sdn_config
new file mode 100644
index 0000000..56b17ae
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_gateway_othervnetnoipam/sdn_config
@@ -0,0 +1,38 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '192.168.0.2'
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ 'gateway' => '192.168.1.2'
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ 'gateway' => '192.168.2.2'
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam.db b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam.db.expected b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam.db.expected
new file mode 100644
index 0000000..f576915
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ "192.168.1.1" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam_config b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/net b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/net
new file mode 100644
index 0000000..4031d65
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip' => '192.168.1.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/net.expected b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/net.expected
new file mode 100644
index 0000000..4031d65
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip' => '192.168.1.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/oldnet b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/oldnet
new file mode 100644
index 0000000..e3e093c
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/oldnet
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'gw' => '192.168.0.2',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/sdn_config b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/sdn_config
new file mode 100644
index 0000000..20785d0
--- /dev/null
+++ b/src/test/ipams/ipv4_gateway_changeip_ipamvnet_to_nogateway_othervnetipam/sdn_config
@@ -0,0 +1,36 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '192.168.0.2'
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4_updateipam_ipamvnet/ipam.db b/src/test/ipams/ipv4_updateipam_ipamvnet/ipam.db
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_updateipam_ipamvnet/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_updateipam_ipamvnet/ipam.db.expected b/src/test/ipams/ipv4_updateipam_ipamvnet/ipam.db.expected
new file mode 100644
index 0000000..d03cc04
--- /dev/null
+++ b/src/test/ipams/ipv4_updateipam_ipamvnet/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "192.168.1.0/30" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4_updateipam_ipamvnet/ipam_config b/src/test/ipams/ipv4_updateipam_ipamvnet/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4_updateipam_ipamvnet/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4_updateipam_ipamvnet/net b/src/test/ipams/ipv4_updateipam_ipamvnet/net
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_updateipam_ipamvnet/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_updateipam_ipamvnet/net.expected b/src/test/ipams/ipv4_updateipam_ipamvnet/net.expected
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_updateipam_ipamvnet/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_updateipam_ipamvnet/oldnet b/src/test/ipams/ipv4_updateipam_ipamvnet/oldnet
new file mode 100644
index 0000000..fe003a3
--- /dev/null
+++ b/src/test/ipams/ipv4_updateipam_ipamvnet/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4_updateipam_ipamvnet/sdn_config b/src/test/ipams/ipv4_updateipam_ipamvnet/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4_updateipam_ipamvnet/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam.db b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam.db
new file mode 100644
index 0000000..252c95a
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam.db
@@ -0,0 +1,19 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7334" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam.db.expected b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam.db.expected
new file mode 100644
index 0000000..8dcf09b
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam.db.expected
@@ -0,0 +1,19 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.2" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7335" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam_config b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/net b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/net
new file mode 100644
index 0000000..b68d367
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/net
@@ -0,0 +1,6 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/net.expected b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/net.expected
new file mode 100644
index 0000000..3b6e3c5
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/net.expected
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.2/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7335/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/oldnet b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/oldnet
new file mode 100644
index 0000000..929f90d
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/oldnet
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/sdn_config b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/sdn_config
new file mode 100644
index 0000000..171cb08
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam/sdn_config
@@ -0,0 +1,39 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-2001:db8:85a3::8a2e:370:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam.db b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam.db
new file mode 100644
index 0000000..de920d7
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam.db
@@ -0,0 +1,20 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7334" => {},
+ "2001:db8:85a3::8a2e:370:7335" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam.db.expected b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam.db.expected
new file mode 100644
index 0000000..de920d7
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam.db.expected
@@ -0,0 +1,20 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7334" => {},
+ "2001:db8:85a3::8a2e:370:7335" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam_config b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/net b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/net
new file mode 100644
index 0000000..b68d367
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/net
@@ -0,0 +1,6 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/net.expected b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/net.expected
new file mode 100644
index 0000000..929f90d
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/net.expected
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/oldnet b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/oldnet
new file mode 100644
index 0000000..929f90d
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/oldnet
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/sdn_config b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/sdn_config
new file mode 100644
index 0000000..171cb08
--- /dev/null
+++ b/src/test/ipams/ipv4v6_next_free_samevnet_with_ipam_failingv6/sdn_config
@@ -0,0 +1,39 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-2001:db8:85a3::8a2e:370:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam.db b/src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam.db
new file mode 100644
index 0000000..252c95a
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam.db
@@ -0,0 +1,19 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7334" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam.db.expected b/src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam.db.expected
new file mode 100644
index 0000000..adb6fc7
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam.db.expected
@@ -0,0 +1,19 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7335" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam_config b/src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet/net b/src/test/ipams/ipv4v6_updateipam_ipamvnet/net
new file mode 100644
index 0000000..e7052bc
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet/net
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7335/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet/net.expected b/src/test/ipams/ipv4v6_updateipam_ipamvnet/net.expected
new file mode 100644
index 0000000..e7052bc
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet/net.expected
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7335/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet/oldnet b/src/test/ipams/ipv4v6_updateipam_ipamvnet/oldnet
new file mode 100644
index 0000000..929f90d
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet/oldnet
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet/sdn_config b/src/test/ipams/ipv4v6_updateipam_ipamvnet/sdn_config
new file mode 100644
index 0000000..171cb08
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet/sdn_config
@@ -0,0 +1,39 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-2001:db8:85a3::8a2e:370:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam.db b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam.db
new file mode 100644
index 0000000..252c95a
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam.db
@@ -0,0 +1,19 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7334" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam.db.expected b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam.db.expected
new file mode 100644
index 0000000..252c95a
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam.db.expected
@@ -0,0 +1,19 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "192.168.0.0/30" => {
+ "ips" =>{
+ "192.168.0.1" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7334" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam_config b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/net b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/net
new file mode 100644
index 0000000..872a354
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/net
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7336/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/net.expected b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/net.expected
new file mode 100644
index 0000000..929f90d
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/net.expected
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/oldnet b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/oldnet
new file mode 100644
index 0000000..929f90d
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/oldnet
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip' => '192.168.0.1/30',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/sdn_config b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/sdn_config
new file mode 100644
index 0000000..43e142f
--- /dev/null
+++ b/src/test/ipams/ipv4v6_updateipam_ipamvnet_failingv6/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-192.168.2.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam.db b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam.db
new file mode 100644
index 0000000..16c5af3
--- /dev/null
+++ b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7334" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:371:7334/127" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam.db.expected b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam.db.expected
new file mode 100644
index 0000000..4039793
--- /dev/null
+++ b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ }
+ },
+ "2001:db8:85a3::8a2e:371:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:371:7334" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam_config b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/net b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/net
new file mode 100644
index 0000000..b4bee3a
--- /dev/null
+++ b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip6' => '2001:db8:85a3::8a2e:371:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/net.expected b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/net.expected
new file mode 100644
index 0000000..b4bee3a
--- /dev/null
+++ b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip6' => '2001:db8:85a3::8a2e:371:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/oldnet b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/oldnet
new file mode 100644
index 0000000..b1c5472
--- /dev/null
+++ b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/sdn_config b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/sdn_config
new file mode 100644
index 0000000..5d48a4e
--- /dev/null
+++ b/src/test/ipams/ipv6_changeip_ipamvnet_to_othervnetipam/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-2001:db8:85a3::8a2e:370:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-2001:db8:85a3::8a2e:371:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-2001:db8:85a3::8a2e:372:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db
new file mode 100644
index 0000000..16c5af3
--- /dev/null
+++ b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7334" => {}
+ }
+ },
+ "2001:db8:85a3::8a2e:371:7334/127" => {
+ "ips" =>{
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db.expected b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db.expected
new file mode 100644
index 0000000..4039793
--- /dev/null
+++ b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam.db.expected
@@ -0,0 +1,18 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ }
+ },
+ "2001:db8:85a3::8a2e:371:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:371:7334" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam_config b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net
new file mode 100644
index 0000000..b4bee3a
--- /dev/null
+++ b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'ip6' => '2001:db8:85a3::8a2e:371:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net.expected b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net.expected
new file mode 100644
index 0000000..689f7cc
--- /dev/null
+++ b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/net.expected
@@ -0,0 +1,8 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet2',
+ 'gw6' => '2001:db8:85a3::8a2e:371:7335',
+ 'ip6' => '2001:db8:85a3::8a2e:371:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/oldnet b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/oldnet
new file mode 100644
index 0000000..b1c5472
--- /dev/null
+++ b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/sdn_config b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/sdn_config
new file mode 100644
index 0000000..3652eca
--- /dev/null
+++ b/src/test/ipams/ipv6_gateway_changeip_ipamvnet_to_gateway_othervnetipam/sdn_config
@@ -0,0 +1,37 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-2001:db8:85a3::8a2e:370:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '2001:db8:85a3::8a2e:370:7335'
+ },
+ 'myzone-2001:db8:85a3::8a2e:371:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ 'gateway' => '2001:db8:85a3::8a2e:371:7335'
+ },
+ 'myzone2-2001:db8:85a3::8a2e:372:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/ipams/ipv6_updateipam_ipamvnet/ipam.db b/src/test/ipams/ipv6_updateipam_ipamvnet/ipam.db
new file mode 100644
index 0000000..d8e3ce5
--- /dev/null
+++ b/src/test/ipams/ipv6_updateipam_ipamvnet/ipam.db
@@ -0,0 +1,14 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7334" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv6_updateipam_ipamvnet/ipam.db.expected b/src/test/ipams/ipv6_updateipam_ipamvnet/ipam.db.expected
new file mode 100644
index 0000000..89b6e8c
--- /dev/null
+++ b/src/test/ipams/ipv6_updateipam_ipamvnet/ipam.db.expected
@@ -0,0 +1,14 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ "2001:db8:85a3::8a2e:370:7335" => {}
+ }
+ },
+ }
+ },
+ }
+}
+
diff --git a/src/test/ipams/ipv6_updateipam_ipamvnet/ipam_config b/src/test/ipams/ipv6_updateipam_ipamvnet/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/ipams/ipv6_updateipam_ipamvnet/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/ipams/ipv6_updateipam_ipamvnet/net b/src/test/ipams/ipv6_updateipam_ipamvnet/net
new file mode 100644
index 0000000..4f83ab2
--- /dev/null
+++ b/src/test/ipams/ipv6_updateipam_ipamvnet/net
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7335/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv6_updateipam_ipamvnet/net.expected b/src/test/ipams/ipv6_updateipam_ipamvnet/net.expected
new file mode 100644
index 0000000..4f83ab2
--- /dev/null
+++ b/src/test/ipams/ipv6_updateipam_ipamvnet/net.expected
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7335/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv6_updateipam_ipamvnet/oldnet b/src/test/ipams/ipv6_updateipam_ipamvnet/oldnet
new file mode 100644
index 0000000..b1c5472
--- /dev/null
+++ b/src/test/ipams/ipv6_updateipam_ipamvnet/oldnet
@@ -0,0 +1,7 @@
+{
+ 'name' => 'eth0',
+ 'bridge' => 'myvnet',
+ 'ip6' => '2001:db8:85a3::8a2e:370:7334/127',
+ 'type' => 'veth',
+ 'hwaddr' => '8A:4C:75:11:58:1D'
+}
diff --git a/src/test/ipams/ipv6_updateipam_ipamvnet/sdn_config b/src/test/ipams/ipv6_updateipam_ipamvnet/sdn_config
new file mode 100644
index 0000000..5d48a4e
--- /dev/null
+++ b/src/test/ipams/ipv6_updateipam_ipamvnet/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ myvnet3 => { type => "vnet", zone => "myzone2" },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => { ipam => "pve", type =>"simple" },
+ myzone2 => { type =>"simple" },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-2001:db8:85a3::8a2e:370:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-2001:db8:85a3::8a2e:371:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ },
+ 'myzone2-2001:db8:85a3::8a2e:372:7334-127' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet3',
+ },
+ }
+
+ }
+}
diff --git a/src/test/run_ipam_tests.pl b/src/test/run_ipam_tests.pl
new file mode 100755
index 0000000..ab8c7ac
--- /dev/null
+++ b/src/test/run_ipam_tests.pl
@@ -0,0 +1,126 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib qw(..);
+use File::Slurp;
+use NetAddr::IP qw(:lower);
+
+use Test::More;
+use Test::MockModule;
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::INotify;
+use PVE::LXC;
+use JSON;
+
+use Data::Dumper qw(Dumper);
+$Data::Dumper::Sortkeys = 1;
+
+sub read_sdn_config {
+ my ($file) = @_;
+ # Read structure back in again
+ open my $in, '<', $file or die $!;
+ my $sdn_config;
+ {
+ local $/; # slurp mode
+ $sdn_config = eval <$in>;
+ }
+ close $in;
+ return $sdn_config;
+}
+
+
+my @plugins = read_dir( './ipams/', prefix => 1 ) ;
+
+foreach my $path (@plugins) {
+
+ my (undef, $testid) = split(/\//, $path);
+
+ print "test: $testid\n";
+ my $sdn_config = read_sdn_config ("$path/sdn_config");
+ my $pve_sdn_zones;
+ $pve_sdn_zones = Test::MockModule->new('PVE::Network::SDN::Zones');
+ $pve_sdn_zones->mock(
+ config => sub {
+ return $sdn_config->{zones};
+ },
+ );
+
+ my $pve_sdn_vnets;
+ $pve_sdn_vnets = Test::MockModule->new('PVE::Network::SDN::Vnets');
+ $pve_sdn_vnets->mock(
+ config => sub {
+ return $sdn_config->{vnets};
+ },
+ );
+
+ my $pve_sdn_subnets;
+ $pve_sdn_subnets = Test::MockModule->new('PVE::Network::SDN::Subnets');
+ $pve_sdn_subnets->mock(
+ config => sub {
+ return $sdn_config->{subnets};
+ },
+ verify_dns_zone => sub {
+ return;
+ },
+ add_dns_record => sub {
+ return;
+ }
+ );
+
+ my $js = JSON->new;
+ $js->canonical(1);
+
+ my $hostname = "myhostname";
+ my $mac = "da:65:8f:18:9b:6f";
+ my $description = "mydescription";
+ my $ipamdb = read_sdn_config ("$path/ipam.db");
+
+ my $plugin;
+ my $sdn_ipam_plugin;
+ $plugin = PVE::Network::SDN::Ipams::Plugin->lookup('pve');
+ $sdn_ipam_plugin = Test::MockModule->new($plugin);
+ $sdn_ipam_plugin->mock(
+ read_db => sub {
+ return $ipamdb;
+ },
+ write_db => sub {
+ my ($cfg) = @_;
+ $ipamdb = $cfg;
+ }
+ );
+
+ my $pve_sdn_ipams;
+ $pve_sdn_ipams = Test::MockModule->new('PVE::Network::SDN::Ipams');
+ $pve_sdn_ipams->mock(
+ config => sub {
+ my $ipam_config = read_sdn_config ("$path/ipam_config");
+ return $ipam_config;
+ },
+ );
+
+
+ ## add_ip
+ my $test = "update_net";
+ my $name = "$testid $test";
+ my $oldnet = read_sdn_config("$path/oldnet");
+ my $net = read_sdn_config("$path/net");
+ my $expected_net = read_sdn_config("$path/net.expected");
+ my $expected_ipamdb = read_sdn_config("$path/ipam.db.expected");
+
+ eval {
+ PVE::LXC::update_net_ip($net, $oldnet, $hostname, $hostname, "description");
+ };
+ is(Dumper($net), Dumper($expected_net), "verify net");
+ is(Dumper($ipamdb), Dumper($expected_ipamdb), "verify ipam.db");
+
+
+
+}
+
+done_testing();
+
+
diff --git a/src/test/snapshot-input/sdn/subnets.cfg b/src/test/snapshot-input/sdn/subnets.cfg
new file mode 100644
index 0000000..e69de29
diff --git a/src/test/snapshot-test.pm b/src/test/snapshot-test.pm
index 91a2af9..73a0532 100644
--- a/src/test/snapshot-test.pm
+++ b/src/test/snapshot-test.pm
@@ -281,7 +281,6 @@ sub mocked_write_config {
# END mocked PVE::LXC methods
-
PVE::Tools::run_command("rm -rf snapshot-working");
PVE::Tools::run_command("cp -a snapshot-input snapshot-working");
--
2.20.1
^ permalink raw reply [flat|nested] 2+ messages in thread