summaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
authorThomas Lamprecht <t.lamprecht@proxmox.com>2023-05-25 18:10:14 +0200
committerThomas Lamprecht <t.lamprecht@proxmox.com>2023-05-25 18:18:57 +0200
commit6029cbb071c3722c717eebbafaf1b373f3edaadc (patch)
tree456d7aff44d2ae220d1671f77da7528174d53fe6 /src/test
parentcead0f28af4aceee83af6636d4f5ffb2d2f6c6b1 (diff)
separate packaging and source build system
like almost all of our repos do nowadays, modern git can detect such things on rebase so in development stuff should be hopefully not too much affected by this. Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
Diffstat (limited to 'src/test')
-rw-r--r--src/test/Makefile17
-rw-r--r--src/test/debug/documentation.txt102
-rw-r--r--src/test/debug/generateconfig.pl24
-rw-r--r--src/test/debug/statuscheck.pl9
-rw-r--r--src/test/dns/powerdns/dns_config10
-rw-r--r--src/test/dns/powerdns/expected.add_a_multiple_record.ipv413
-rw-r--r--src/test/dns/powerdns/expected.add_a_multiple_record.ipv613
-rw-r--r--src/test/dns/powerdns/expected.add_a_record.ipv412
-rw-r--r--src/test/dns/powerdns/expected.add_a_record.ipv613
-rw-r--r--src/test/dns/powerdns/expected.add_ptr_record.ipv413
-rw-r--r--src/test/dns/powerdns/expected.add_ptr_record.ipv612
-rw-r--r--src/test/dns/powerdns/expected.del_a_multiple_record.ipv413
-rw-r--r--src/test/dns/powerdns/expected.del_a_multiple_record.ipv612
-rw-r--r--src/test/dns/powerdns/expected.del_a_record.ipv413
-rw-r--r--src/test/dns/powerdns/expected.del_a_record.ipv612
-rw-r--r--src/test/dns/powerdns/expected.del_ptr_record.ipv412
-rw-r--r--src/test/dns/powerdns/expected.del_ptr_record.ipv613
-rw-r--r--src/test/dns/powerdns/expected.verify_zone12
-rw-r--r--src/test/dns/powerdns/sdn_config20
-rw-r--r--src/test/ipams/netbox/expected.add_ip9
-rw-r--r--src/test/ipams/netbox/expected.add_ip_notgateway9
-rw-r--r--src/test/ipams/netbox/expected.add_next_freeip9
-rw-r--r--src/test/ipams/netbox/expected.add_subnet9
-rw-r--r--src/test/ipams/netbox/expected.del_ip9
-rw-r--r--src/test/ipams/netbox/expected.del_subnet9
-rw-r--r--src/test/ipams/netbox/expected.update_ip9
-rw-r--r--src/test/ipams/netbox/ipam_config18
-rw-r--r--src/test/ipams/netbox/sdn_config20
-rw-r--r--src/test/ipams/phpipam/expected.add_ip12
-rw-r--r--src/test/ipams/phpipam/expected.add_ip_notgateway12
-rw-r--r--src/test/ipams/phpipam/expected.add_next_freeip12
-rw-r--r--src/test/ipams/phpipam/expected.add_subnet12
-rw-r--r--src/test/ipams/phpipam/expected.del_ip12
-rw-r--r--src/test/ipams/phpipam/expected.del_subnet12
-rw-r--r--src/test/ipams/phpipam/expected.update_ip12
-rw-r--r--src/test/ipams/phpipam/ipam_config18
-rw-r--r--src/test/ipams/phpipam/sdn_config20
-rwxr-xr-xsrc/test/run_test_dns.pl271
-rwxr-xr-xsrc/test/run_test_ipams.pl197
-rwxr-xr-xsrc/test/run_test_subnets.pl305
-rwxr-xr-xsrc/test/run_test_vnets.pl355
-rwxr-xr-xsrc/test/run_test_zones.pl123
-rw-r--r--src/test/subnets/ipv4/ipam_config18
-rw-r--r--src/test/subnets/ipv4/sdn_config20
-rw-r--r--src/test/subnets/ipv6/ipam_config18
-rw-r--r--src/test/subnets/ipv6/sdn_config20
-rw-r--r--src/test/subnets/noipam/ipam_config18
-rw-r--r--src/test/subnets/noipam/sdn_config20
-rw-r--r--src/test/vnets/ipv4/ipam.db17
-rw-r--r--src/test/vnets/ipv4/ipam_config7
-rw-r--r--src/test/vnets/ipv4/sdn_config26
-rw-r--r--src/test/vnets/ipv4noipam/ipam.db17
-rw-r--r--src/test/vnets/ipv4noipam/ipam_config7
-rw-r--r--src/test/vnets/ipv4noipam/sdn_config26
-rw-r--r--src/test/vnets/ipv6/ipam.db16
-rw-r--r--src/test/vnets/ipv6/ipam_config7
-rw-r--r--src/test/vnets/ipv6/sdn_config26
-rw-r--r--src/test/zones/evpn/advertise_subnets/expected_controller_config54
-rw-r--r--src/test/zones/evpn/advertise_subnets/expected_sdn_interfaces42
-rw-r--r--src/test/zones/evpn/advertise_subnets/interfaces7
-rw-r--r--src/test/zones/evpn/advertise_subnets/sdn_config26
-rw-r--r--src/test/zones/evpn/disable_arp_nd_suppression/expected_controller_config41
-rw-r--r--src/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces40
-rw-r--r--src/test/zones/evpn/disable_arp_nd_suppression/interfaces7
-rw-r--r--src/test/zones/evpn/disable_arp_nd_suppression/sdn_config26
-rw-r--r--src/test/zones/evpn/ebgp/expected_controller_config58
-rw-r--r--src/test/zones/evpn/ebgp/expected_sdn_interfaces41
-rw-r--r--src/test/zones/evpn/ebgp/interfaces7
-rw-r--r--src/test/zones/evpn/ebgp/sdn_config50
-rw-r--r--src/test/zones/evpn/ebgp_loopback/expected_controller_config69
-rw-r--r--src/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces41
-rw-r--r--src/test/zones/evpn/ebgp_loopback/interfaces13
-rw-r--r--src/test/zones/evpn/ebgp_loopback/sdn_config29
-rw-r--r--src/test/zones/evpn/exitnode/expected_controller_config66
-rw-r--r--src/test/zones/evpn/exitnode/expected_sdn_interfaces41
-rw-r--r--src/test/zones/evpn/exitnode/interfaces7
-rw-r--r--src/test/zones/evpn/exitnode/sdn_config26
-rw-r--r--src/test/zones/evpn/exitnode_local_routing/expected_controller_config51
-rw-r--r--src/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces56
-rw-r--r--src/test/zones/evpn/exitnode_local_routing/interfaces7
-rw-r--r--src/test/zones/evpn/exitnode_local_routing/sdn_config27
-rw-r--r--src/test/zones/evpn/exitnode_primary/expected_controller_config68
-rw-r--r--src/test/zones/evpn/exitnode_primary/expected_sdn_interfaces41
-rw-r--r--src/test/zones/evpn/exitnode_primary/interfaces7
-rw-r--r--src/test/zones/evpn/exitnode_primary/sdn_config26
-rw-r--r--src/test/zones/evpn/exitnode_snat/expected_controller_config66
-rw-r--r--src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces68
-rw-r--r--src/test/zones/evpn/exitnode_snat/interfaces7
-rw-r--r--src/test/zones/evpn/exitnode_snat/sdn_config35
-rw-r--r--src/test/zones/evpn/ipv4/expected_controller_config41
-rw-r--r--src/test/zones/evpn/ipv4/expected_sdn_interfaces42
-rw-r--r--src/test/zones/evpn/ipv4/interfaces7
-rw-r--r--src/test/zones/evpn/ipv4/sdn_config26
-rw-r--r--src/test/zones/evpn/ipv4ipv6/expected_controller_config41
-rw-r--r--src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces44
-rw-r--r--src/test/zones/evpn/ipv4ipv6/interfaces7
-rw-r--r--src/test/zones/evpn/ipv4ipv6/sdn_config32
-rw-r--r--src/test/zones/evpn/ipv4ipv6nogateway/expected_controller_config41
-rw-r--r--src/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces40
-rw-r--r--src/test/zones/evpn/ipv4ipv6nogateway/interfaces7
-rw-r--r--src/test/zones/evpn/ipv4ipv6nogateway/sdn_config30
-rw-r--r--src/test/zones/evpn/ipv6/expected_controller_config41
-rw-r--r--src/test/zones/evpn/ipv6/expected_sdn_interfaces42
-rw-r--r--src/test/zones/evpn/ipv6/interfaces7
-rw-r--r--src/test/zones/evpn/ipv6/sdn_config27
-rw-r--r--src/test/zones/evpn/multipath_relax/expected_controller_config53
-rw-r--r--src/test/zones/evpn/multipath_relax/expected_sdn_interfaces41
-rw-r--r--src/test/zones/evpn/multipath_relax/interfaces7
-rw-r--r--src/test/zones/evpn/multipath_relax/sdn_config49
-rw-r--r--src/test/zones/evpn/rt_import/expected_controller_config47
-rw-r--r--src/test/zones/evpn/rt_import/expected_sdn_interfaces42
-rw-r--r--src/test/zones/evpn/rt_import/interfaces7
-rw-r--r--src/test/zones/evpn/rt_import/sdn_config26
-rw-r--r--src/test/zones/qinq/bridge/expected_sdn_interfaces65
-rw-r--r--src/test/zones/qinq/bridge/interfaces5
-rw-r--r--src/test/zones/qinq/bridge/sdn_config16
-rw-r--r--src/test/zones/qinq/bridge_notagvnet/expected_sdn_interfaces36
-rw-r--r--src/test/zones/qinq/bridge_notagvnet/interfaces5
-rw-r--r--src/test/zones/qinq/bridge_notagvnet/sdn_config26
-rw-r--r--src/test/zones/qinq/bridge_vlanaware/expected_sdn_interfaces55
-rw-r--r--src/test/zones/qinq/bridge_vlanaware/interfaces7
-rw-r--r--src/test/zones/qinq/bridge_vlanaware/sdn_config16
-rw-r--r--src/test/zones/qinq/bridge_vlanaware_notagvnet/expected_sdn_interfaces27
-rw-r--r--src/test/zones/qinq/bridge_vlanaware_notagvnet/interfaces7
-rw-r--r--src/test/zones/qinq/bridge_vlanaware_notagvnet/sdn_config11
-rw-r--r--src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces27
-rw-r--r--src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/interfaces7
-rw-r--r--src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/sdn_config11
-rw-r--r--src/test/zones/qinq/bridge_vlanaware_vlanprotocol/expected_sdn_interfaces29
-rw-r--r--src/test/zones/qinq/bridge_vlanaware_vlanprotocol/interfaces7
-rw-r--r--src/test/zones/qinq/bridge_vlanaware_vlanprotocol/sdn_config11
-rw-r--r--src/test/zones/qinq/bridge_vlanawarevnet/expected_sdn_interfaces32
-rw-r--r--src/test/zones/qinq/bridge_vlanawarevnet/interfaces5
-rw-r--r--src/test/zones/qinq/bridge_vlanawarevnet/sdn_config11
-rw-r--r--src/test/zones/qinq/bridge_vlanprotocol/expected_sdn_interfaces31
-rw-r--r--src/test/zones/qinq/bridge_vlanprotocol/interfaces5
-rw-r--r--src/test/zones/qinq/bridge_vlanprotocol/sdn_config11
-rw-r--r--src/test/zones/qinq/ovs/expected_sdn_interfaces71
-rw-r--r--src/test/zones/qinq/ovs/interfaces9
-rw-r--r--src/test/zones/qinq/ovs/sdn_config16
-rw-r--r--src/test/zones/qinq/ovs_notagvnet/expected_sdn_interfaces37
-rw-r--r--src/test/zones/qinq/ovs_notagvnet/interfaces9
-rw-r--r--src/test/zones/qinq/ovs_notagvnet/sdn_config11
-rw-r--r--src/test/zones/qinq/ovs_vlanawarevnet/expected_sdn_interfaces37
-rw-r--r--src/test/zones/qinq/ovs_vlanawarevnet/interfaces9
-rw-r--r--src/test/zones/qinq/ovs_vlanawarevnet/sdn_config11
-rw-r--r--src/test/zones/qinq/ovs_vlanprotocol/expected_sdn_interfaces35
-rw-r--r--src/test/zones/qinq/ovs_vlanprotocol/interfaces9
-rw-r--r--src/test/zones/qinq/ovs_vlanprotocol/sdn_config11
-rw-r--r--src/test/zones/simple/basic/expected_sdn_interfaces7
-rw-r--r--src/test/zones/simple/basic/interfaces5
-rw-r--r--src/test/zones/simple/basic/sdn_config11
-rw-r--r--src/test/zones/simple/hetzner/expected_sdn_interfaces19
-rw-r--r--src/test/zones/simple/hetzner/interfaces6
-rw-r--r--src/test/zones/simple/hetzner/sdn_config34
-rw-r--r--src/test/zones/simple/ipv4/expected_sdn_interfaces9
-rw-r--r--src/test/zones/simple/ipv4/interfaces5
-rw-r--r--src/test/zones/simple/ipv4/sdn_config22
-rw-r--r--src/test/zones/simple/ipv4snat/expected_sdn_interfaces13
-rw-r--r--src/test/zones/simple/ipv4snat/interfaces7
-rw-r--r--src/test/zones/simple/ipv4snat/sdn_config23
-rw-r--r--src/test/zones/simple/ipv4v6/expected_sdn_interfaces11
-rw-r--r--src/test/zones/simple/ipv4v6/interfaces5
-rw-r--r--src/test/zones/simple/ipv4v6/sdn_config27
-rw-r--r--src/test/zones/simple/ipv4v6nogateway/expected_sdn_interfaces7
-rw-r--r--src/test/zones/simple/ipv4v6nogateway/interfaces5
-rw-r--r--src/test/zones/simple/ipv4v6nogateway/sdn_config25
-rw-r--r--src/test/zones/simple/ipv6snat/expected_sdn_interfaces13
-rw-r--r--src/test/zones/simple/ipv6snat/interfaces7
-rw-r--r--src/test/zones/simple/ipv6snat/sdn_config24
-rw-r--r--src/test/zones/vlan/bridge/expected_sdn_interfaces23
-rw-r--r--src/test/zones/vlan/bridge/interfaces5
-rw-r--r--src/test/zones/vlan/bridge/sdn_config11
-rw-r--r--src/test/zones/vlan/bridge_vlanaware/expected_sdn_interfaces7
-rw-r--r--src/test/zones/vlan/bridge_vlanaware/interfaces7
-rw-r--r--src/test/zones/vlan/bridge_vlanaware/sdn_config11
-rw-r--r--src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces9
-rw-r--r--src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/interfaces7
-rw-r--r--src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/sdn_config11
-rw-r--r--src/test/zones/vlan/ovs/expected_sdn_interfaces17
-rw-r--r--src/test/zones/vlan/ovs/interfaces9
-rw-r--r--src/test/zones/vlan/ovs/sdn_config11
-rw-r--r--src/test/zones/vlan/ovs_vlanware_vnet/expected_sdn_interfaces19
-rw-r--r--src/test/zones/vlan/ovs_vlanware_vnet/interfaces9
-rw-r--r--src/test/zones/vlan/ovs_vlanware_vnet/sdn_config11
-rw-r--r--src/test/zones/vxlan/basic/expected_sdn_interfaces15
-rw-r--r--src/test/zones/vxlan/basic/interfaces7
-rw-r--r--src/test/zones/vxlan/basic/sdn_config11
-rw-r--r--src/test/zones/vxlan/vlanawarevnet/expected_sdn_interfaces17
-rw-r--r--src/test/zones/vxlan/vlanawarevnet/interfaces7
-rw-r--r--src/test/zones/vxlan/vlanawarevnet/sdn_config11
191 files changed, 5227 insertions, 0 deletions
diff --git a/src/test/Makefile b/src/test/Makefile
new file mode 100644
index 0000000..eb59d5f
--- /dev/null
+++ b/src/test/Makefile
@@ -0,0 +1,17 @@
+all: test
+
+test: test_zones test_ipams test_dns test_subnets
+
+test_zones: run_test_zones.pl
+ ./run_test_zones.pl
+
+test_ipams: run_test_ipams.pl
+ ./run_test_ipams.pl
+
+test_dns: run_test_dns.pl
+ ./run_test_dns.pl
+
+test_subnets: run_test_subnets.pl
+ ./run_test_subnets.pl
+
+clean:
diff --git a/src/test/debug/documentation.txt b/src/test/debug/documentation.txt
new file mode 100644
index 0000000..6ee8ee6
--- /dev/null
+++ b/src/test/debug/documentation.txt
@@ -0,0 +1,102 @@
+Here a sample of command with pvesh to manage the sdn.
+
+
+#create a vlan transportzone
+pvesh create /cluster/sdn/zones/ --zone vlanzone --type vlan --ipam pve --bridge vmbr0
+#create a vnet on vlanzone
+pvesh create /cluster/sdn/vnets/ --vnet vnet100 --type vnet --zone vlanzone --tag 100
+#create a subnet on vlanzone
+pvesh create /cluster/sdn/vnets/vnet100/subnets/ --type subnet --subnet 192.168.0.0/24 --gateway 192.168.0.1
+
+
+#create a layer2 vxlan unicast transportzone
+pvesh create /cluster/sdn/zones/ --zone vxlanunicastzone --type vxlan --ipam pve --peers 192.168.0.1,192.168.0.2,192.168.0.3
+
+#create an evpn controller
+pvesh create /cluster/sdn/controllers/ --controller evpn1 --type evpn --peers 192.168.0.1,192.168.0.2,192.168.0.3 --asn 1234
+
+#add a ebgp peer
+pvesh create /cluster/sdn/controllers/ --controller bgp1 --type bgp --peers 192.168.0.253,192.168.0.254 --asn 1234 --ebgp --node pxnode1
+
+#create a layer2 vxlan bgpevpn transportzone
+pvesh create /cluster/sdn/zones/ --zone layer2evpnzone --type evpn --ipam pve --controller evpn1
+
+#create a layer3 routable vxlan bgpevpn transportzone + exit-nodes
+pvesh create /cluster/sdn/zones/ --zone layer3evpnzone --type evpn --ipam pve --controller evpn1 --vrf-vxlan 4000 --exit-nodes pxnode1,pxnode2
+
+
+
+#create a vnet in the transportzone
+pvesh create /cluster/sdn/vnets/ --vnet vnet10 --type vnet --zone vlanzone --tag 10
+
+#create a vnet in the transportzone with subnets for evpn routing
+pvesh create /cluster/sdn/vnets/ --vnet vnet11 --type vnet --zone layer3evpnzone --tag 11 --mac c8:1f:66:f8:62:8d
+pvesh create /cluster/sdn/vnets/vnet11/subnets/ --type subnet --subnet 10.0.0.0/24 --gateway 10.0.0.1
+pvesh create /cluster/sdn/vnets/ --vnet vnet12 --type vnet --zone layer3evpnzone --tag 12 --mac c8:1f:66:f8:62:8e
+pvesh create /cluster/sdn/vnets/vnet11/subnets/ --type subnet --subnet 10.0.1.0/24 --gateway 10.0.1.1
+
+#display running configuration
+pvesh get /cluster/sdn/vnets --running
+pvesh get /cluster/sdn/zones --running
+pvesh get /cluster/sdn/controllers --running
+pvesh get /cluster/sdn/vnets/vnetX/subnets --running
+
+
+#display pending configuration
+pvesh get /cluster/sdn/vnets --pending
+pvesh get /cluster/sdn/zones --pending
+pvesh get /cluster/sdn/controllers --pending
+pvesh get /cluster/sdn/vnets/vnetX/subnets --pending
+
+
+#apply changes from /etc/pve/sdn.cfg.new to /etc/pve/sdn.cfg
+pvesh set /cluster/sdn
+
+
+#generate local /etc/network/interfaces.d/sdn and reload (need to be called on each node)
+ pvesh set /nodes/<node>/network
+
+
+display transporzone status on all cluster nodes
+#pvesh get /cluster/resources
+┌────────────────────────────────────┬─────────┬───────┬───────────┬─────────┬───────┬────────┬─────────────┬────────────┬────────────┬───────────────┬──────┬───────────┬──────────────┬────────────────┐
+│ id │ type │ cpu │ disk │ hastate │ level │ maxcpu │ maxdisk │ maxmem │ mem │ node │ pool │ status │ storage │ uptime │
+│ sdn/node1/transportzone10 │ sdn │ │ │ │ │ │ │ │ │ kvmformation1 │ │ error │ │ │
+├────────────────────────────────────┼─────────┼───────┼───────────┼─────────┼───────┼────────┼─────────────┼────────────┼────────────┼───────────────┼──────┼───────────┼──────────────┼────────────────┤
+│ sdn/node1/zone1 │ sdn │ │ │ │ │ │ │ │ │ node1 │ │ available │ │ │
+├────────────────────────────────────┼─────────┼───────┼───────────┼─────────┼───────┼────────┼─────────────┼────────────┼────────────┼───────────────┼──────┼───────────┼──────────────┼────────────────┤
+│ sdn/node1/zone4 │ sdn │ │ │ │ │ │ │ │ │ node1 │ │ available │ │ │
+├────────────────────────────────────┼─────────┼───────┼───────────┼─────────┼───────┼────────┼─────────────┼────────────┼────────────┼───────────────┼──────┼───────────┼──────────────┼────────────────┤
+
+
+
+
+#list all transport zones of a node
+
+pvesh get /nodes/<node>/sdn/zones/
+ ┌─────────────────┬───────────┐
+ │ sdn │ status │
+ ├─────────────────┼───────────┤
+ │ transportzone10 │ error │
+ ├─────────────────┼───────────┤
+ │ zone1 │ available │
+ ├─────────────────┼───────────┤
+ │ zone4 │ available │
+ └─────────────────┴───────────┘
+
+
+#list all vnet status from a node transportzone
+
+pveset get /nodes/<node>/sdn/zones/<transportzone>/content
+
+ ┌─────────┬────────┐
+ │ vnet │ status │
+ ├─────────┼────────┤
+ │ vnet100 │ error │
+ ├─────────┼────────┤
+ │ vnet101 │ error │
+ └─────────┴────────┘
+
+
+
+
diff --git a/src/test/debug/generateconfig.pl b/src/test/debug/generateconfig.pl
new file mode 100644
index 0000000..250db43
--- /dev/null
+++ b/src/test/debug/generateconfig.pl
@@ -0,0 +1,24 @@
+use strict;
+use warnings;
+use File::Copy;
+use PVE::Cluster qw(cfs_read_file);
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Controllers;
+use Data::Dumper;
+
+PVE::Network::SDN::commit_config();
+my $network_config = PVE::Network::SDN::Zones::generate_etc_network_config();
+
+PVE::Network::SDN::Zones::write_etc_network_config($network_config);
+print "/etc/network/interfaces.d/sdn\n";
+print $network_config;
+print "\n";
+
+my $controller_config = PVE::Network::SDN::Controllers::generate_controller_config();
+
+if ($controller_config) {
+ print Dumper($controller_config);
+ PVE::Network::SDN::Controllers::write_controller_config($controller_config);
+}
diff --git a/src/test/debug/statuscheck.pl b/src/test/debug/statuscheck.pl
new file mode 100644
index 0000000..e43003b
--- /dev/null
+++ b/src/test/debug/statuscheck.pl
@@ -0,0 +1,9 @@
+use strict;
+use warnings;
+use PVE::Network::SDN;
+use Data::Dumper;
+
+my ($transport_status, $vnet_status) = PVE::Network::SDN::status();
+
+print Dumper($vnet_status);
+print Dumper($transport_status);
diff --git a/src/test/dns/powerdns/dns_config b/src/test/dns/powerdns/dns_config
new file mode 100644
index 0000000..6052366
--- /dev/null
+++ b/src/test/dns/powerdns/dns_config
@@ -0,0 +1,10 @@
+{
+ 'ids' => {
+ 'powerdns' => {
+ 'url' => 'http://localhost:8881/api/v1/servers/localhost',
+ 'type' => 'powerdns',
+ 'key' => '1234',
+ 'ttl' => '3600'
+ },
+ },
+}
diff --git a/src/test/dns/powerdns/expected.add_a_multiple_record.ipv4 b/src/test/dns/powerdns/expected.add_a_multiple_record.ipv4
new file mode 100644
index 0000000..0e5539f
--- /dev/null
+++ b/src/test/dns/powerdns/expected.add_a_multiple_record.ipv4
@@ -0,0 +1,13 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"REPLACE","name":"myhostname.domain.com.","records":[{"content":"127.0.0.1","disabled":false,"name":"myhostname.domain.com.","priority":0,"type":"A"},{"content":"10.0.0.1","disabled":false,"name":"myhostname.domain.com.","priority":0,"type":"A"}],"ttl":"3600","type":"A"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
+
diff --git a/src/test/dns/powerdns/expected.add_a_multiple_record.ipv6 b/src/test/dns/powerdns/expected.add_a_multiple_record.ipv6
new file mode 100644
index 0000000..e432e7b
--- /dev/null
+++ b/src/test/dns/powerdns/expected.add_a_multiple_record.ipv6
@@ -0,0 +1,13 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"REPLACE","name":"myhostname.domain.com.","records":[{"content":"2001:4860:4860::8844","disabled":false,"name":"myhostname.domain.com.","priority":0,"type":"AAAA"},{"content":"2001:4860:4860::8888","disabled":false,"name":"myhostname.domain.com.","priority":0,"type":"AAAA"}],"ttl":"3600","type":"AAAA"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
+
diff --git a/src/test/dns/powerdns/expected.add_a_record.ipv4 b/src/test/dns/powerdns/expected.add_a_record.ipv4
new file mode 100644
index 0000000..888d67f
--- /dev/null
+++ b/src/test/dns/powerdns/expected.add_a_record.ipv4
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"REPLACE","name":"myhostname.domain.com.","records":[{"content":"10.0.0.1","disabled":false,"name":"myhostname.domain.com.","priority":0,"type":"A"}],"ttl":"3600","type":"A"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' ); \ No newline at end of file
diff --git a/src/test/dns/powerdns/expected.add_a_record.ipv6 b/src/test/dns/powerdns/expected.add_a_record.ipv6
new file mode 100644
index 0000000..bfeeab7
--- /dev/null
+++ b/src/test/dns/powerdns/expected.add_a_record.ipv6
@@ -0,0 +1,13 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"REPLACE","name":"myhostname.domain.com.","records":[{"content":"2001:4860:4860::8888","disabled":false,"name":"myhostname.domain.com.","priority":0,"type":"AAAA"}],"ttl":"3600","type":"AAAA"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
+
diff --git a/src/test/dns/powerdns/expected.add_ptr_record.ipv4 b/src/test/dns/powerdns/expected.add_ptr_record.ipv4
new file mode 100644
index 0000000..6923971
--- /dev/null
+++ b/src/test/dns/powerdns/expected.add_ptr_record.ipv4
@@ -0,0 +1,13 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"REPLACE","name":"1.0.0.10.in-addr.arpa.","records":[{"content":"myhostname.","disabled":false,"name":"1.0.0.10.in-addr.arpa.","priority":0,"type":"PTR"}],"ttl":"3600","type":"PTR"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
+
diff --git a/src/test/dns/powerdns/expected.add_ptr_record.ipv6 b/src/test/dns/powerdns/expected.add_ptr_record.ipv6
new file mode 100644
index 0000000..1d8049f
--- /dev/null
+++ b/src/test/dns/powerdns/expected.add_ptr_record.ipv6
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"REPLACE","name":"8.8.8.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.6.8.4.0.6.8.4.1.0.0.2.ip6.arpa.","records":[{"content":"myhostname.","disabled":false,"name":"8.8.8.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.6.8.4.0.6.8.4.1.0.0.2.ip6.arpa.","priority":0,"type":"PTR"}],"ttl":"3600","type":"PTR"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/dns/powerdns/expected.del_a_multiple_record.ipv4 b/src/test/dns/powerdns/expected.del_a_multiple_record.ipv4
new file mode 100644
index 0000000..45d76c6
--- /dev/null
+++ b/src/test/dns/powerdns/expected.del_a_multiple_record.ipv4
@@ -0,0 +1,13 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"REPLACE","name":"myhostname.domain.com.","records":[{"content":"127.0.0.1","disabled":false,"name":"myhostname.domain.com.","priority":0,"type":"A"}],"ttl":"3600","type":"A"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
+
diff --git a/src/test/dns/powerdns/expected.del_a_multiple_record.ipv6 b/src/test/dns/powerdns/expected.del_a_multiple_record.ipv6
new file mode 100644
index 0000000..9b56abd
--- /dev/null
+++ b/src/test/dns/powerdns/expected.del_a_multiple_record.ipv6
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"REPLACE","name":"myhostname.domain.com.","records":[{"content":"2001:4860:4860::8844","disabled":false,"name":"myhostname.domain.com.","priority":0,"type":"AAAA"}],"ttl":"3600","type":"AAAA"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/dns/powerdns/expected.del_a_record.ipv4 b/src/test/dns/powerdns/expected.del_a_record.ipv4
new file mode 100644
index 0000000..7c0cf45
--- /dev/null
+++ b/src/test/dns/powerdns/expected.del_a_record.ipv4
@@ -0,0 +1,13 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"DELETE","name":"myhostname.domain.com.","records":[],"type":"A"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
+
diff --git a/src/test/dns/powerdns/expected.del_a_record.ipv6 b/src/test/dns/powerdns/expected.del_a_record.ipv6
new file mode 100644
index 0000000..9494c83
--- /dev/null
+++ b/src/test/dns/powerdns/expected.del_a_record.ipv6
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"DELETE","name":"myhostname.domain.com.","records":[],"type":"AAAA"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/dns/powerdns/expected.del_ptr_record.ipv4 b/src/test/dns/powerdns/expected.del_ptr_record.ipv4
new file mode 100644
index 0000000..120485b
--- /dev/null
+++ b/src/test/dns/powerdns/expected.del_ptr_record.ipv4
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"DELETE","name":"1.0.0.10.in-addr.arpa.","records":[],"type":"PTR"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/dns/powerdns/expected.del_ptr_record.ipv6 b/src/test/dns/powerdns/expected.del_ptr_record.ipv6
new file mode 100644
index 0000000..7948e78
--- /dev/null
+++ b/src/test/dns/powerdns/expected.del_ptr_record.ipv6
@@ -0,0 +1,13 @@
+bless( {
+ '_content' => '{"rrsets":[{"changetype":"DELETE","name":"8.8.8.8.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.6.8.4.0.6.8.4.1.0.0.2.ip6.arpa.","records":[],"type":"PTR"}]}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com')}, 'URI::http' )
+ }, 'HTTP::Request' );
+
diff --git a/src/test/dns/powerdns/expected.verify_zone b/src/test/dns/powerdns/expected.verify_zone
new file mode 100644
index 0000000..b476875
--- /dev/null
+++ b/src/test/dns/powerdns/expected.verify_zone
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'x-api-key' => 'X-API-Key'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'x-api-key' => '1234'
+ }, 'HTTP::Headers' ),
+ '_method' => 'GET',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8881/api/v1/servers/localhost/zones/domain.com?rrsets=false')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/dns/powerdns/sdn_config b/src/test/dns/powerdns/sdn_config
new file mode 100644
index 0000000..2087729
--- /dev/null
+++ b/src/test/dns/powerdns/sdn_config
@@ -0,0 +1,20 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type =>"simple", dns => "powerdns", reversedns => "powerdns", dnszone => "domain.com" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ }
+ }
+ }
+}
diff --git a/src/test/ipams/netbox/expected.add_ip b/src/test/ipams/netbox/expected.add_ip
new file mode 100644
index 0000000..ae876f2
--- /dev/null
+++ b/src/test/ipams/netbox/expected.add_ip
@@ -0,0 +1,9 @@
+bless( {
+ '_content' => '{"address":"10.0.0.1/24","description":"mydescription mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
+ '_headers' => bless( {
+ 'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
+ 'content-type' => 'application/json; charset=UTF-8'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8000/api/ipam/ip-addresses/')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/netbox/expected.add_ip_notgateway b/src/test/ipams/netbox/expected.add_ip_notgateway
new file mode 100644
index 0000000..ae876f2
--- /dev/null
+++ b/src/test/ipams/netbox/expected.add_ip_notgateway
@@ -0,0 +1,9 @@
+bless( {
+ '_content' => '{"address":"10.0.0.1/24","description":"mydescription mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
+ '_headers' => bless( {
+ 'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
+ 'content-type' => 'application/json; charset=UTF-8'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8000/api/ipam/ip-addresses/')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/netbox/expected.add_next_freeip b/src/test/ipams/netbox/expected.add_next_freeip
new file mode 100644
index 0000000..7f80f4c
--- /dev/null
+++ b/src/test/ipams/netbox/expected.add_next_freeip
@@ -0,0 +1,9 @@
+bless( {
+ '_content' => '{"description":"mydescription mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
+ '_headers' => bless( {
+ 'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
+ 'content-type' => 'application/json; charset=UTF-8'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8000/api/ipam/prefixes/1/available-ips/')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/netbox/expected.add_subnet b/src/test/ipams/netbox/expected.add_subnet
new file mode 100644
index 0000000..62ca823
--- /dev/null
+++ b/src/test/ipams/netbox/expected.add_subnet
@@ -0,0 +1,9 @@
+bless( {
+ '_content' => '{"prefix":"10.0.0.0/24"}',
+ '_headers' => bless( {
+ 'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
+ 'content-type' => 'application/json; charset=UTF-8'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8000/api/ipam/prefixes/')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/netbox/expected.del_ip b/src/test/ipams/netbox/expected.del_ip
new file mode 100644
index 0000000..3c41de4
--- /dev/null
+++ b/src/test/ipams/netbox/expected.del_ip
@@ -0,0 +1,9 @@
+bless( {
+ '_content' => '',
+ '_headers' => bless( {
+ 'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
+ 'content-type' => 'application/json; charset=UTF-8'
+ }, 'HTTP::Headers' ),
+ '_method' => 'DELETE',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8000/api/ipam/ip-addresses/1/')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/netbox/expected.del_subnet b/src/test/ipams/netbox/expected.del_subnet
new file mode 100644
index 0000000..bdadb71
--- /dev/null
+++ b/src/test/ipams/netbox/expected.del_subnet
@@ -0,0 +1,9 @@
+bless( {
+ '_content' => '{"address":"192.168.0.1/24","description":null,"dns_name":"toto"}',
+ '_headers' => bless( {
+ 'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
+ 'content-type' => 'application/json; charset=UTF-8'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8000/api/ipam/ip-addresses/')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/netbox/expected.update_ip b/src/test/ipams/netbox/expected.update_ip
new file mode 100644
index 0000000..a1202ad
--- /dev/null
+++ b/src/test/ipams/netbox/expected.update_ip
@@ -0,0 +1,9 @@
+bless( {
+ '_content' => '{"address":"10.0.0.1/24","description":"mydescription mac:da:65:8f:18:9b:6f","dns_name":"myhostname"}',
+ '_headers' => bless( {
+ 'authorization' => 'token 0123456789abcdef0123456789abcdef01234567',
+ 'content-type' => 'application/json; charset=UTF-8'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'http://localhost:8000/api/ipam/ip-addresses/1/')}, 'URI::http' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/netbox/ipam_config b/src/test/ipams/netbox/ipam_config
new file mode 100644
index 0000000..a33be30
--- /dev/null
+++ b/src/test/ipams/netbox/ipam_config
@@ -0,0 +1,18 @@
+{
+ 'ids' => {
+ 'phpipam' => {
+ 'url' => 'https://localhost/api/apiadmin',
+ 'type' => 'phpipam',
+ 'section' => 1,
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ },
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ 'netbox' => {
+ 'token' => '0123456789abcdef0123456789abcdef01234567',
+ 'type' => 'netbox',
+ 'url' => 'http://localhost:8000/api'
+ }
+ },
+}
diff --git a/src/test/ipams/netbox/sdn_config b/src/test/ipams/netbox/sdn_config
new file mode 100644
index 0000000..c31847b
--- /dev/null
+++ b/src/test/ipams/netbox/sdn_config
@@ -0,0 +1,20 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "netbox" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ }
+ }
+ }
+}
diff --git a/src/test/ipams/phpipam/expected.add_ip b/src/test/ipams/phpipam/expected.add_ip
new file mode 100644
index 0000000..50af460
--- /dev/null
+++ b/src/test/ipams/phpipam/expected.add_ip
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"description":"mydescription","hostname":"myhostname","ip":"10.0.0.1","is_gateway":1,"mac":"da:65:8f:18:9b:6f","subnetId":1}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'token' => 'Token'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'https://localhost/api/apiadmin/addresses/')}, 'URI::https' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/phpipam/expected.add_ip_notgateway b/src/test/ipams/phpipam/expected.add_ip_notgateway
new file mode 100644
index 0000000..7a91359
--- /dev/null
+++ b/src/test/ipams/phpipam/expected.add_ip_notgateway
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"description":"mydescription","hostname":"myhostname","ip":"10.0.0.1","mac":"da:65:8f:18:9b:6f","subnetId":1}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'token' => 'Token'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'https://localhost/api/apiadmin/addresses/')}, 'URI::https' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/phpipam/expected.add_next_freeip b/src/test/ipams/phpipam/expected.add_next_freeip
new file mode 100644
index 0000000..d72f94f
--- /dev/null
+++ b/src/test/ipams/phpipam/expected.add_next_freeip
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"description":"mydescription","hostname":"myhostname","mac":"da:65:8f:18:9b:6f"}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'token' => 'Token'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'https://localhost/api/apiadmin/addresses/first_free/1/')}, 'URI::https' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/phpipam/expected.add_subnet b/src/test/ipams/phpipam/expected.add_subnet
new file mode 100644
index 0000000..b10cc5a
--- /dev/null
+++ b/src/test/ipams/phpipam/expected.add_subnet
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"mask":"24","sectionId":1,"subnet":"10.0.0.0"}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'token' => 'Token'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'https://localhost/api/apiadmin/subnets/')}, 'URI::https' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/phpipam/expected.del_ip b/src/test/ipams/phpipam/expected.del_ip
new file mode 100644
index 0000000..72e83cb
--- /dev/null
+++ b/src/test/ipams/phpipam/expected.del_ip
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'token' => 'Token'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ }, 'HTTP::Headers' ),
+ '_method' => 'DELETE',
+ '_uri' => bless( do{\(my $o = 'https://localhost/api/apiadmin/addresses/1')}, 'URI::https' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/phpipam/expected.del_subnet b/src/test/ipams/phpipam/expected.del_subnet
new file mode 100644
index 0000000..349a34f
--- /dev/null
+++ b/src/test/ipams/phpipam/expected.del_subnet
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"description":null,"hostname":"toto","ip":"192.168.0.1","is_gateway":null,"subnetId":1}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'token' => 'Token'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ }, 'HTTP::Headers' ),
+ '_method' => 'POST',
+ '_uri' => bless( do{\(my $o = 'https://localhost/api/apiadmin/addresses/')}, 'URI::https' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/phpipam/expected.update_ip b/src/test/ipams/phpipam/expected.update_ip
new file mode 100644
index 0000000..96c219b
--- /dev/null
+++ b/src/test/ipams/phpipam/expected.update_ip
@@ -0,0 +1,12 @@
+bless( {
+ '_content' => '{"description":"mydescription","hostname":"myhostname","is_gateway":1,"mac":"da:65:8f:18:9b:6f"}',
+ '_headers' => bless( {
+ '::std_case' => {
+ 'token' => 'Token'
+ },
+ 'content-type' => 'application/json; charset=UTF-8',
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ }, 'HTTP::Headers' ),
+ '_method' => 'PATCH',
+ '_uri' => bless( do{\(my $o = 'https://localhost/api/apiadmin/addresses/1')}, 'URI::https' )
+ }, 'HTTP::Request' );
diff --git a/src/test/ipams/phpipam/ipam_config b/src/test/ipams/phpipam/ipam_config
new file mode 100644
index 0000000..a33be30
--- /dev/null
+++ b/src/test/ipams/phpipam/ipam_config
@@ -0,0 +1,18 @@
+{
+ 'ids' => {
+ 'phpipam' => {
+ 'url' => 'https://localhost/api/apiadmin',
+ 'type' => 'phpipam',
+ 'section' => 1,
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ },
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ 'netbox' => {
+ 'token' => '0123456789abcdef0123456789abcdef01234567',
+ 'type' => 'netbox',
+ 'url' => 'http://localhost:8000/api'
+ }
+ },
+}
diff --git a/src/test/ipams/phpipam/sdn_config b/src/test/ipams/phpipam/sdn_config
new file mode 100644
index 0000000..c774807
--- /dev/null
+++ b/src/test/ipams/phpipam/sdn_config
@@ -0,0 +1,20 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "phpipam" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ }
+ }
+ }
+}
diff --git a/src/test/run_test_dns.pl b/src/test/run_test_dns.pl
new file mode 100755
index 0000000..87e011e
--- /dev/null
+++ b/src/test/run_test_dns.pl
@@ -0,0 +1,271 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib qw(..);
+use File::Slurp;
+use Net::IP;
+
+use Test::More;
+use Test::MockModule;
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Controllers;
+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( './dns/', prefix => 1 ) ;
+
+foreach my $path (@plugins) {
+
+ my (undef, $dnsid) = split(/\//, $path);
+ my $sdn_config = read_sdn_config ("$path/sdn_config");
+
+
+ my $pve_sdn_dns;
+ $pve_sdn_dns = Test::MockModule->new('PVE::Network::SDN::Dns');
+ $pve_sdn_dns->mock(
+ config => sub {
+ my $dns_config = read_sdn_config ("$path/dns_config");
+ return $dns_config;
+ },
+ );
+
+ my $sdn_module = Test::MockModule->new("PVE::Network::SDN");
+ $sdn_module->mock(
+ config => sub {
+ return $sdn_config;
+ },
+ api_request => sub {
+ my ($method, $url, $headers, $data) = @_;
+
+ my $js = JSON->new;
+ $js->canonical(1);
+
+ my $encoded_data = $js->encode($data) if $data;
+ my $req = HTTP::Request->new($method,$url, $headers, $encoded_data);
+ die Dumper($req);
+ }
+ );
+
+
+
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
+ my $plugin_config = $dns_cfg->{ids}->{$dnsid};
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+
+ #test params;
+ my @ips = ("10.0.0.1", "2001:4860:4860::8888");
+ my $zone = "domain.com";
+ my $hostname = "myhostname";
+
+ foreach my $ip (@ips) {
+
+ my $ipversion = Net::IP::ip_is_ipv6($ip) ? "ipv6" : "ipv4";
+ my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
+ my $ip2 = $type eq 'AAAA' ? '2001:4860:4860::8844' : '127.0.0.1';
+ my $fqdn = $hostname.".".$zone.".";
+
+ my $sdn_dns_plugin = Test::MockModule->new($plugin);
+ $sdn_dns_plugin->mock(
+
+ get_zone_content => sub {
+ return undef;
+ },
+ get_zone_rrset => sub {
+ return undef;
+ }
+ );
+
+ ## add_a_record
+ my $test = "add_a_record";
+ my $expected = Dumper read_sdn_config("$path/expected.$test.$ipversion");
+ my $name = "$dnsid $test";
+
+ $plugin->add_a_record($plugin_config, $zone, $hostname, $ip, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+ ## add_ptr_record
+ $test = "add_ptr_record";
+ $expected = Dumper read_sdn_config("$path/expected.$test.$ipversion");
+ $name = "$dnsid $test";
+
+ $plugin->add_ptr_record($plugin_config, $zone, $hostname, $ip, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+
+ ## del_ptr_record
+ $test = "del_ptr_record";
+ $expected = Dumper read_sdn_config("$path/expected.$test.$ipversion");
+ $name = "$dnsid $test";
+
+ $plugin->del_ptr_record($plugin_config, $zone, $ip, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+
+ ## del_a_record
+
+ $sdn_dns_plugin->mock(
+
+ get_zone_content => sub {
+ return undef;
+ },
+ get_zone_rrset => sub {
+
+ my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
+ my $fqdn = $hostname.".".$zone.".";
+ my $record = { content => $ip,
+ disabled => JSON::false,
+ name => $fqdn,
+ type => $type,
+ priority => 0 };
+
+ my $rrset = { name => $fqdn,
+ type => $type,
+ ttl => '3600',
+ records => [ $record ] };
+ return $rrset;
+ }
+ );
+
+ $test = "del_a_record";
+ $expected = Dumper read_sdn_config("$path/expected.$test.$ipversion");
+ $name = "$dnsid $test";
+
+ $plugin->del_a_record($plugin_config, $zone, $hostname, $ip, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+ ## del_a_multiple_record
+
+ $sdn_dns_plugin->mock(
+
+ get_zone_content => sub {
+ return undef;
+ },
+ get_zone_rrset => sub {
+
+ my $record = { content => $ip,
+ disabled => JSON::false,
+ name => $fqdn,
+ type => $type,
+ priority => 0 };
+
+ my $record2 = { content => $ip2,
+ disabled => JSON::false,
+ name => $fqdn,
+ type => $type,
+ priority => 0 };
+
+ my $rrset = { name => $fqdn,
+ type => $type,
+ ttl => '3600',
+ records => [ $record, $record2 ] };
+ return $rrset;
+ }
+ );
+
+ $test = "del_a_multiple_record";
+ $expected = Dumper read_sdn_config("$path/expected.$test.$ipversion");
+ $name = "$dnsid $test";
+
+ $plugin->del_a_record($plugin_config, $zone, $hostname, $ip, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+ ## add_a_multiple_record
+
+ $sdn_dns_plugin->mock(
+
+ get_zone_content => sub {
+ return undef;
+ },
+ get_zone_rrset => sub {
+
+ my $record2 = { content => $ip2,
+ disabled => JSON::false,
+ name => $fqdn,
+ type => $type,
+ priority => 0 };
+
+ my $rrset = { name => $fqdn,
+ type => $type,
+ ttl => '3600',
+ records => [ $record2 ] };
+ return $rrset;
+ }
+ );
+
+ $test = "add_a_multiple_record";
+ $expected = Dumper read_sdn_config("$path/expected.$test.$ipversion");
+ $name = "$dnsid $test";
+
+ $plugin->add_a_record($plugin_config, $zone, $hostname, $ip, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+ }
+
+ ## verify_zone
+ my $test = "verify_zone";
+ my $expected = Dumper read_sdn_config("$path/expected.$test");
+ my $name = "$dnsid $test";
+
+ $plugin->verify_zone($plugin_config, $zone, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+}
+
+done_testing();
+
+
diff --git a/src/test/run_test_ipams.pl b/src/test/run_test_ipams.pl
new file mode 100755
index 0000000..27bd441
--- /dev/null
+++ b/src/test/run_test_ipams.pl
@@ -0,0 +1,197 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib qw(..);
+use File::Slurp;
+
+use Test::More;
+use Test::MockModule;
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Controllers;
+use PVE::INotify;
+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 = <./ipams/*>;
+my @plugins = read_dir( './ipams/', prefix => 1 ) ;
+
+foreach my $path (@plugins) {
+
+ my (undef, $ipamid) = split(/\//, $path);
+ my $sdn_config = read_sdn_config ("$path/sdn_config");
+
+
+ my $pve_sdn_subnets;
+ $pve_sdn_subnets = Test::MockModule->new('PVE::Network::SDN::Subnets');
+ $pve_sdn_subnets->mock(
+ config => sub {
+ return $sdn_config->{subnets};
+ },
+ );
+
+ my $pve_sdn_ipam;
+ $pve_sdn_subnets = Test::MockModule->new('PVE::Network::SDN::Ipams');
+ $pve_sdn_subnets->mock(
+ config => sub {
+ my $ipam_config = read_sdn_config ("$path/ipam_config");
+ return $ipam_config;
+ },
+ );
+
+ my $sdn_module = Test::MockModule->new("PVE::Network::SDN");
+ $sdn_module->mock(
+ config => sub {
+ return $sdn_config;
+ },
+ api_request => sub {
+ my ($method, $url, $headers, $data) = @_;
+
+ my $js = JSON->new;
+ $js->canonical(1);
+
+ my $encoded_data = $js->encode($data) if $data;
+ my $req = HTTP::Request->new($method,$url, $headers, $encoded_data);
+ die Dumper($req);
+ }
+ );
+
+
+
+ #test params;
+ my $subnetid = "myzone-10.0.0.0-24";
+ my $ip = "10.0.0.1";
+ my $hostname = "myhostname";
+ my $mac = "da:65:8f:18:9b:6f";
+ my $description = "mydescription";
+ my $is_gateway = 1;
+
+
+ my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($sdn_config->{subnets}, $subnetid, 1);
+
+ my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+ my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
+ my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+ my $sdn_ipam_plugin = Test::MockModule->new($plugin);
+ $sdn_ipam_plugin->mock(
+ get_prefix_id => sub {
+ return 1;
+ },
+ get_ip_id => sub {
+ return 1;
+ },
+ is_ip_gateway => sub {
+ return 1;
+ }
+ );
+
+ ## add_ip
+ my $test = "add_ip";
+ my $expected = Dumper read_sdn_config("$path/expected.$test");
+ my $name = "$ipamid $test";
+
+ $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+ ## add_next_freeip
+ $test = "add_next_freeip";
+ $expected = Dumper read_sdn_config("$path/expected.$test");
+ $name = "$ipamid $test";
+
+ $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $description, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+
+ ## del_ip
+ $test = "del_ip";
+ $expected = Dumper read_sdn_config("$path/expected.$test");
+ $name = "$ipamid $test";
+
+ $plugin->del_ip($plugin_config, $subnetid, $subnet, $ip, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+ ## update_ip
+ $test = "update_ip";
+ $expected = Dumper read_sdn_config("$path/expected.$test");
+ $name = "$ipamid $test";
+ $plugin->update_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+ ## add_ip_notgateway
+ $is_gateway = undef;
+ $test = "add_ip_notgateway";
+ $expected = Dumper read_sdn_config("$path/expected.$test");
+ $name = "$ipamid $test";
+
+ $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+ $sdn_ipam_plugin->mock(
+ get_prefix_id => sub {
+ return undef;
+ },
+ );
+
+ ## add_subnet
+ $test = "add_subnet";
+ $expected = Dumper read_sdn_config("$path/expected.$test");
+ $name = "$ipamid $test";
+
+ $plugin->add_subnet($plugin_config, $subnetid, $subnet, 1);
+
+ if ($@) {
+ is ($@, $expected, $name);
+ } else {
+ fail($name);
+ }
+
+}
+
+done_testing();
+
+
diff --git a/src/test/run_test_subnets.pl b/src/test/run_test_subnets.pl
new file mode 100755
index 0000000..f6564e1
--- /dev/null
+++ b/src/test/run_test_subnets.pl
@@ -0,0 +1,305 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib qw(..);
+use File::Slurp;
+
+use Test::More;
+use Test::MockModule;
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Controllers;
+use PVE::INotify;
+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( './subnets/', 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_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);
+
+
+ #test params;
+ my $subnets = $sdn_config->{subnets}->{ids};
+ my $subnetid = (keys %{$subnets})[0];
+ my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($sdn_config->{subnets}, $subnetid, 1);
+
+ my $subnet_cidr = $subnet->{cidr};
+ my $iplist = NetAddr::IP->new($subnet_cidr);
+ $iplist++ if Net::IP::ip_is_ipv4($iplist->canon()); #skip network address for ipv4
+ my $ip = $iplist->canon();
+ $iplist++;
+ my $ipnextfree = $iplist->canon();
+ $iplist++;
+ my $ip2 = $iplist->canon();
+
+ my $ip3 = undef;
+ my $hostname = "myhostname";
+ my $mac = "da:65:8f:18:9b:6f";
+ my $description = "mydescription";
+ my $is_gateway = 1;
+ my $ipamdb = {};
+
+ my $zone = $sdn_config->{zones}->{ids}->{"myzone"};
+ my $ipam = $zone->{ipam};
+
+ my $plugin;
+ my $sdn_ipam_plugin;
+ if($ipam) {
+ $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($ipam);
+ $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_subnet
+ my $test = "add_subnet $subnetid";
+ my $name = "$testid $test";
+ my $result = undef;
+ my $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{}}}}}}';
+
+ eval {
+ PVE::Network::SDN::Subnets::add_subnet($zone, $subnetid, $subnet);
+
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } elsif($ipam) {
+ $result = $js->encode($plugin->read_db());
+ is ($result, $expected, $name);
+ } else {
+ is (undef, undef, $name);
+ }
+
+ ## add_ip
+ $test = "add_ip $ip";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1}}}}}}}';
+
+ eval {
+ PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } elsif($ipam) {
+ $result = $js->encode($plugin->read_db());
+ is ($result, $expected, $name);
+ } else {
+ is (undef, undef, $name);
+ }
+
+ if($ipam) {
+ ## add_already_exist_ip
+ $test = "add_already_exist_ip $ip";
+ $name = "$testid $test";
+
+ eval {
+ PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ is (undef, undef, $name);
+ } else {
+ fail("$name : $@");
+ }
+ }
+
+ ## add_second_ip
+ $test = "add_second_ip $ip2";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ip2.'":{}}}}}}}';
+
+ eval {
+ PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip2, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } elsif($ipam) {
+ $result = $js->encode($plugin->read_db());
+ is ($result, $expected, $name);
+ } else {
+ is (undef, undef, $name);
+ }
+
+ ## add_next_free
+ $test = "find_next_freeip ($ipnextfree)";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
+
+ eval {
+ $ip3 = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } elsif($ipam) {
+ $result = $js->encode($plugin->read_db());
+ is ($result, $expected, $name);
+ }
+
+ ## del_ip
+ $test = "del_ip $ip";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
+
+ eval {
+ PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } elsif($ipam) {
+ $result = $js->encode($plugin->read_db());
+ is ($result, $expected, $name);
+ } else {
+ is (undef, undef, $name);
+ }
+
+ if($ipam){
+ ## del_subnet_not_empty
+ $test = "del_subnet_not_empty $subnetid";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = undef;
+
+ eval {
+ PVE::Network::SDN::Subnets::del_subnet($zone, $subnetid, $subnet);
+ };
+
+ if ($@) {
+ is ($result, $expected, $name);
+ } else {
+ fail("$name : $@");
+ }
+ }
+
+
+ ## add_ip_rollback_failing_dns
+ $test = "add_ip_rollback_failing_dns";
+
+ $pve_sdn_subnets->mock(
+ config => sub {
+ return $sdn_config->{subnets};
+ },
+ verify_dns_zone => sub {
+ return;
+ },
+ add_dns_record => sub {
+ die "error add dns record";
+ return;
+ }
+ );
+
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
+
+ eval {
+ PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ if($ipam) {
+ $result = $js->encode($plugin->read_db());
+ is ($result, $expected, $name);
+ } else {
+ is (undef, undef, $name);
+ }
+ } else {
+ fail("$name : $@");
+ }
+
+
+ ## del_empty_subnet
+ $test = "del_empty_subnet";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '{"zones":{"myzone":{"subnets":{}}}}';
+
+ PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip2, $hostname);
+ PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip3, $hostname);
+
+ eval {
+ PVE::Network::SDN::Subnets::del_subnet($zone, $subnetid, $subnet);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } elsif($ipam) {
+ $result = $js->encode($plugin->read_db());
+ is ($result, $expected, $name);
+ } else {
+ is (undef, undef, $name);
+ }
+
+}
+
+done_testing();
+
+
diff --git a/src/test/run_test_vnets.pl b/src/test/run_test_vnets.pl
new file mode 100755
index 0000000..5aeb676
--- /dev/null
+++ b/src/test/run_test_vnets.pl
@@ -0,0 +1,355 @@
+#!/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::Network::SDN::Controllers;
+use PVE::INotify;
+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( './vnets/', 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);
+
+ #test params;
+ #test params;
+ my $subnets = $sdn_config->{subnets}->{ids};
+
+ my $subnetid = (sort keys %{$subnets})[0];
+ my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($sdn_config->{subnets}, $subnetid, 1);
+ my $subnet_cidr = $subnet->{cidr};
+ my $iplist = NetAddr::IP->new($subnet_cidr);
+ my $mask = $iplist->masklen();
+ my $ipversion = undef;
+
+ if (Net::IP::ip_is_ipv4($iplist->canon())){
+ $iplist++; #skip network address for ipv4
+ $ipversion = 4;
+ } else {
+ $ipversion = 6;
+ }
+
+ my $cidr1 = $iplist->canon()."/$mask";
+ $iplist++;
+ my $cidr2 = $iplist->canon()."/$mask";
+ my $cidr_outofrange = '8.8.8.8/8';
+
+ my $subnetid2 = (sort keys %{$subnets})[1];
+ my $subnet2 = PVE::Network::SDN::Subnets::sdn_subnets_config($sdn_config->{subnets}, $subnetid2, 1);
+ my $subnet2_cidr = $subnet2->{cidr};
+ my $iplist2 = NetAddr::IP->new($subnet2_cidr);
+ $iplist2++;
+ my $cidr3 = $iplist2->canon()."/$mask";
+ $iplist2++;
+ my $cidr4 = $iplist2->canon()."/$mask";
+
+ my $hostname = "myhostname";
+ my $mac = "da:65:8f:18:9b:6f";
+ my $description = "mydescription";
+ my $ipamdb = read_sdn_config ("$path/ipam.db");
+
+ my $zone = $sdn_config->{zones}->{ids}->{"myzone"};
+ my $ipam = $zone->{ipam};
+
+ my $plugin;
+ my $sdn_ipam_plugin;
+ if($ipam) {
+ $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($ipam);
+ $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;
+ },
+ );
+
+ my $vnetid = "myvnet";
+
+ ## add_ip
+ my $test = "add_cidr $cidr1";
+ my $name = "$testid $test";
+ my $result = undef;
+ my $expected = '';
+
+ eval {
+ PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr1, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } else {
+ is (undef, undef, $name);
+ }
+
+ ## add_ip
+ $test = "add_already_exist_cidr $cidr1";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '';
+
+ eval {
+ PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr1, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ is (undef, undef, $name);
+ } elsif($ipam) {
+ fail("$name : $@");
+ } else {
+ is (undef, undef, $name);
+ }
+
+ ## add_ip
+ $test = "add_cidr $cidr2";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '';
+
+ eval {
+ PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr2, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } else {
+ is (undef, undef, $name);
+ }
+
+ ## add_ip
+ $test = "add_ip_out_of_range_subnets $cidr_outofrange";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '';
+
+ eval {
+ PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr_outofrange, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ is (undef, undef, $name);
+ } else {
+ fail("$name : $@");
+ }
+
+ ## add_ip
+ $test = "add_cidr $cidr4";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = '';
+
+ eval {
+ PVE::Network::SDN::Vnets::add_cidr($vnetid, $cidr4, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } else {
+ is (undef, undef, $name);
+ }
+
+
+ $test = "find_next_free_cidr_in_second_subnet ($cidr3)";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = $ipam ? $cidr3 : undef;
+
+ eval {
+ $result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } else {
+ is ($result, $expected, $name);
+ }
+
+
+ $test = "del_cidr $cidr1";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = undef;
+
+ eval {
+ $result = PVE::Network::SDN::Vnets::del_cidr($vnetid, $cidr1, $hostname);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } else {
+ is (undef, undef, $name);
+ }
+
+ $test = "del_cidr $cidr3";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = undef;
+
+ eval {
+ $result = PVE::Network::SDN::Vnets::del_cidr($vnetid, $cidr3, $hostname);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } else {
+ is (undef, undef, $name);
+ }
+
+ $test = "del_cidr not exist $cidr1";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = undef;
+
+ eval {
+ $result = PVE::Network::SDN::Vnets::del_cidr($vnetid, $cidr1, $hostname);
+ };
+
+ if ($@) {
+ is (undef, undef, $name);
+ } elsif($ipam) {
+ fail("$name : $@");
+ } else {
+ is (undef, undef, $name);
+ }
+
+ $test = "del_cidr outofrange $cidr_outofrange";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = undef;
+
+ eval {
+ $result = PVE::Network::SDN::Vnets::del_cidr($vnetid, $cidr_outofrange, $hostname);
+ };
+
+ if ($@) {
+ is (undef, undef, $name);
+ } else {
+ fail("$name : $@");
+ }
+
+ $test = "find_next_free_cidr_in_first_subnet ($cidr1)";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = $ipam ? $cidr1 : undef;
+
+ eval {
+ $result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } else {
+ is ($result, $expected, $name);
+ }
+
+ $test = "update_cidr $cidr1";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = undef;
+
+ eval {
+ $result = PVE::Network::SDN::Vnets::update_cidr($vnetid, $cidr1, $hostname, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } else {
+ is (undef, undef, $name);
+ }
+
+ $test = "update_cidr deleted $cidr3";
+ $name = "$testid $test";
+ $result = undef;
+ $expected = undef;
+
+ eval {
+ $result = PVE::Network::SDN::Vnets::update_cidr($vnetid, $cidr1, $hostname, $hostname, $mac, $description);
+ };
+
+ if ($@) {
+ fail("$name : $@");
+ } else {
+ is (undef, undef, $name);
+ }
+
+}
+
+done_testing();
+
+
diff --git a/src/test/run_test_zones.pl b/src/test/run_test_zones.pl
new file mode 100755
index 0000000..12e017a
--- /dev/null
+++ b/src/test/run_test_zones.pl
@@ -0,0 +1,123 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib qw(..);
+use File::Slurp;
+
+use Test::More;
+use Test::MockModule;
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Controllers;
+use PVE::INotify;
+
+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 @tests = grep { -d } glob './zones/*/*';
+
+foreach my $test (@tests) {
+
+ my $sdn_config = read_sdn_config ("./$test/sdn_config");
+
+ open my $fh1, '<', "./$test/interfaces" or die "can't read interfaces file";
+ my $interfaces_config = PVE::INotify::__read_etc_network_interfaces($fh1, undef, undef);
+ close $fh1;
+
+ my $pve_common_inotify;
+ $pve_common_inotify = Test::MockModule->new('PVE::INotify');
+ $pve_common_inotify->mock(
+ nodename => sub {
+ return 'localhost';
+ },
+ read_file => sub {
+ return $interfaces_config;
+ },
+ );
+
+ my $pve_sdn_subnets;
+ $pve_sdn_subnets = Test::MockModule->new('PVE::Network::SDN::Subnets');
+ $pve_sdn_subnets->mock(
+ config => sub {
+ return $sdn_config->{subnets};
+ },
+ );
+
+ my $pve_sdn_zones_plugin;
+ $pve_sdn_zones_plugin = Test::MockModule->new('PVE::Network::SDN::Zones::Plugin');
+ $pve_sdn_zones_plugin->mock(
+ get_local_route_ip => sub {
+ my $outiface = "vmbr0";
+ my $outip = $interfaces_config->{ifaces}->{$outiface}->{address};
+ return ($outip, $outiface);
+ },
+ is_vlanaware => sub {
+ return $interfaces_config->{ifaces}->{vmbr0}->{'bridge_vlan_aware'};
+ },
+ is_ovs => sub {
+ return 1 if $interfaces_config->{ifaces}->{vmbr0}->{'type'} eq 'OVSBridge';
+ },
+ get_bridge_ifaces => sub {
+ return ('eth0');
+ },
+ find_bridge => sub {
+ return;
+ }
+ );
+
+ my $sdn_module = Test::MockModule->new("PVE::Network::SDN");
+ $sdn_module->mock(
+ running_config => sub {
+ return $sdn_config;
+ },
+ );
+
+ my $name = $test;
+ my $expected = read_file("./$test/expected_sdn_interfaces");
+
+ my $result = "";
+ eval {
+ $result = PVE::Network::SDN::Zones::generate_etc_network_config();
+ };
+
+ if (my $err = $@) {
+ fail($name);
+ } else {
+ is ($result, $expected, $name);
+ }
+
+ if ($sdn_config->{controllers}) {
+ my $expected = read_file("./$test/expected_controller_config");
+ my $controller_rawconfig = "";
+
+ eval {
+ my $config = PVE::Network::SDN::Controllers::generate_controller_config();
+ $controller_rawconfig = PVE::Network::SDN::Controllers::generate_controller_rawconfig($config);
+ };
+ if (my $err = $@) {
+ fail($name);
+ } else {
+ is ($controller_rawconfig, $expected, $name);
+ }
+ }
+}
+
+done_testing();
+
+
diff --git a/src/test/subnets/ipv4/ipam_config b/src/test/subnets/ipv4/ipam_config
new file mode 100644
index 0000000..a33be30
--- /dev/null
+++ b/src/test/subnets/ipv4/ipam_config
@@ -0,0 +1,18 @@
+{
+ 'ids' => {
+ 'phpipam' => {
+ 'url' => 'https://localhost/api/apiadmin',
+ 'type' => 'phpipam',
+ 'section' => 1,
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ },
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ 'netbox' => {
+ 'token' => '0123456789abcdef0123456789abcdef01234567',
+ 'type' => 'netbox',
+ 'url' => 'http://localhost:8000/api'
+ }
+ },
+}
diff --git a/src/test/subnets/ipv4/sdn_config b/src/test/subnets/ipv4/sdn_config
new file mode 100644
index 0000000..72697d4
--- /dev/null
+++ b/src/test/subnets/ipv4/sdn_config
@@ -0,0 +1,20 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type =>"simple" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ }
+ }
+ }
+}
diff --git a/src/test/subnets/ipv6/ipam_config b/src/test/subnets/ipv6/ipam_config
new file mode 100644
index 0000000..a33be30
--- /dev/null
+++ b/src/test/subnets/ipv6/ipam_config
@@ -0,0 +1,18 @@
+{
+ 'ids' => {
+ 'phpipam' => {
+ 'url' => 'https://localhost/api/apiadmin',
+ 'type' => 'phpipam',
+ 'section' => 1,
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ },
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ 'netbox' => {
+ 'token' => '0123456789abcdef0123456789abcdef01234567',
+ 'type' => 'netbox',
+ 'url' => 'http://localhost:8000/api'
+ }
+ },
+}
diff --git a/src/test/subnets/ipv6/sdn_config b/src/test/subnets/ipv6/sdn_config
new file mode 100644
index 0000000..618f234
--- /dev/null
+++ b/src/test/subnets/ipv6/sdn_config
@@ -0,0 +1,20 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type =>"simple" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-2a0a:1580:2000::-56' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ }
+ }
+ }
+}
diff --git a/src/test/subnets/noipam/ipam_config b/src/test/subnets/noipam/ipam_config
new file mode 100644
index 0000000..a33be30
--- /dev/null
+++ b/src/test/subnets/noipam/ipam_config
@@ -0,0 +1,18 @@
+{
+ 'ids' => {
+ 'phpipam' => {
+ 'url' => 'https://localhost/api/apiadmin',
+ 'type' => 'phpipam',
+ 'section' => 1,
+ 'token' => 'JPHkPSLB4O_XL-GQz4qtEFmNpx-99Htw'
+ },
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ 'netbox' => {
+ 'token' => '0123456789abcdef0123456789abcdef01234567',
+ 'type' => 'netbox',
+ 'url' => 'http://localhost:8000/api'
+ }
+ },
+}
diff --git a/src/test/subnets/noipam/sdn_config b/src/test/subnets/noipam/sdn_config
new file mode 100644
index 0000000..55107d6
--- /dev/null
+++ b/src/test/subnets/noipam/sdn_config
@@ -0,0 +1,20 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { type =>"simple" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ }
+ }
+ }
+}
diff --git a/src/test/vnets/ipv4/ipam.db b/src/test/vnets/ipv4/ipam.db
new file mode 100644
index 0000000..ef3fa93
--- /dev/null
+++ b/src/test/vnets/ipv4/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/vnets/ipv4/ipam_config b/src/test/vnets/ipv4/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/vnets/ipv4/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/vnets/ipv4/sdn_config b/src/test/vnets/ipv4/sdn_config
new file mode 100644
index 0000000..ee11fd1
--- /dev/null
+++ b/src/test/vnets/ipv4/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type =>"simple" } },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ }
+
+ }
+}
diff --git a/src/test/vnets/ipv4noipam/ipam.db b/src/test/vnets/ipv4noipam/ipam.db
new file mode 100644
index 0000000..ef3fa93
--- /dev/null
+++ b/src/test/vnets/ipv4noipam/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/vnets/ipv4noipam/ipam_config b/src/test/vnets/ipv4noipam/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/vnets/ipv4noipam/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/vnets/ipv4noipam/sdn_config b/src/test/vnets/ipv4noipam/sdn_config
new file mode 100644
index 0000000..470c1ae
--- /dev/null
+++ b/src/test/vnets/ipv4noipam/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { type =>"simple" } },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-192.168.1.0-30' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ }
+
+ }
+}
diff --git a/src/test/vnets/ipv6/ipam.db b/src/test/vnets/ipv6/ipam.db
new file mode 100644
index 0000000..d3f2ce9
--- /dev/null
+++ b/src/test/vnets/ipv6/ipam.db
@@ -0,0 +1,16 @@
+{
+ "zones" => {
+ "myzone" => {
+ "subnets" => {
+ "2001:db8:85a3::8a2e:370:7334/127" => {
+ "ips" =>{
+ }
+ },
+ "2001:db8:85a3::8a2e:371:7334/127" => {
+ "ips" =>{
+ }
+ },
+ }
+ }
+ }
+}
diff --git a/src/test/vnets/ipv6/ipam_config b/src/test/vnets/ipv6/ipam_config
new file mode 100644
index 0000000..f5f36ad
--- /dev/null
+++ b/src/test/vnets/ipv6/ipam_config
@@ -0,0 +1,7 @@
+{
+ 'ids' => {
+ 'pve' => {
+ 'type' => 'pve'
+ },
+ },
+}
diff --git a/src/test/vnets/ipv6/sdn_config b/src/test/vnets/ipv6/sdn_config
new file mode 100644
index 0000000..231ca8a
--- /dev/null
+++ b/src/test/vnets/ipv6/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", 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' => 'myvnet',
+ },
+ }
+
+ }
+}
diff --git a/src/test/zones/evpn/advertise_subnets/expected_controller_config b/src/test/zones/evpn/advertise_subnets/expected_controller_config
new file mode 100644
index 0000000..82b06b4
--- /dev/null
+++ b/src/test/zones/evpn/advertise_subnets/expected_controller_config
@@ -0,0 +1,54 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ advertise ipv4 unicast
+ advertise ipv6 unicast
+ exit-address-family
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/advertise_subnets/expected_sdn_interfaces b/src/test/zones/evpn/advertise_subnets/expected_sdn_interfaces
new file mode 100644
index 0000000..9d1c64c
--- /dev/null
+++ b/src/test/zones/evpn/advertise_subnets/expected_sdn_interfaces
@@ -0,0 +1,42 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ hwaddress A2:1D:CB:1A:C0:8B
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/advertise_subnets/interfaces b/src/test/zones/evpn/advertise_subnets/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/advertise_subnets/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/advertise_subnets/sdn_config b/src/test/zones/evpn/advertise_subnets/sdn_config
new file mode 100644
index 0000000..76f16a1
--- /dev/null
+++ b/src/test/zones/evpn/advertise_subnets/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, 'mac' => 'A2:1D:CB:1A:C0:8B', 'advertise-subnets' => 1 } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/disable_arp_nd_suppression/expected_controller_config b/src/test/zones/evpn/disable_arp_nd_suppression/expected_controller_config
new file mode 100644
index 0000000..bd7830a
--- /dev/null
+++ b/src/test/zones/evpn/disable_arp_nd_suppression/expected_controller_config
@@ -0,0 +1,41 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces b/src/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces
new file mode 100644
index 0000000..bbde906
--- /dev/null
+++ b/src/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces
@@ -0,0 +1,40 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ hwaddress A2:1D:CB:1A:C0:8B
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ mtu 1450
diff --git a/src/test/zones/evpn/disable_arp_nd_suppression/interfaces b/src/test/zones/evpn/disable_arp_nd_suppression/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/disable_arp_nd_suppression/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/disable_arp_nd_suppression/sdn_config b/src/test/zones/evpn/disable_arp_nd_suppression/sdn_config
new file mode 100644
index 0000000..199596b
--- /dev/null
+++ b/src/test/zones/evpn/disable_arp_nd_suppression/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, 'mac' => 'A2:1D:CB:1A:C0:8B', 'disable-arp-nd-suppression' => 1 } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/ebgp/expected_controller_config b/src/test/zones/evpn/ebgp/expected_controller_config
new file mode 100644
index 0000000..ccc0b28
--- /dev/null
+++ b/src/test/zones/evpn/ebgp/expected_controller_config
@@ -0,0 +1,58 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65001
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as external
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ neighbor BGP peer-group
+ neighbor BGP remote-as external
+ neighbor BGP bfd
+ neighbor BGP ebgp-multihop 3
+ neighbor 192.168.0.252 peer-group BGP
+ neighbor 192.168.0.253 peer-group BGP
+ !
+ address-family ipv4 unicast
+ neighbor BGP activate
+ neighbor BGP soft-reconfiguration inbound
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ autort as 65000
+ exit-address-family
+exit
+!
+router bgp 65001 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+ !
+ address-family l2vpn evpn
+ route-target import 65000:1000
+ route-target export 65000:1000
+ exit-address-family
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/ebgp/expected_sdn_interfaces b/src/test/zones/evpn/ebgp/expected_sdn_interfaces
new file mode 100644
index 0000000..4cf13e0
--- /dev/null
+++ b/src/test/zones/evpn/ebgp/expected_sdn_interfaces
@@ -0,0 +1,41 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/ebgp/interfaces b/src/test/zones/evpn/ebgp/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/ebgp/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/ebgp/sdn_config b/src/test/zones/evpn/ebgp/sdn_config
new file mode 100644
index 0000000..6e9d116
--- /dev/null
+++ b/src/test/zones/evpn/ebgp/sdn_config
@@ -0,0 +1,50 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => {
+ tag => "100",
+ type => "vnet",
+ zone => "myzone",
+ },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => {
+ ipam => "pve",
+ type => "evpn",
+ controller => "evpnctl",
+ 'vrf-vxlan' => 1000,
+ },
+ },
+ },
+ controllers => {
+ ids => {
+ evpnctl => {
+ type => "evpn",
+ 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3',
+ asn => "65000",
+ },
+ localhost => {
+ type => "bgp",
+ 'peers' => '192.168.0.252,192.168.0.253',
+ ebgp => "1",
+ 'ebgp-multihop' => '3',
+ asn => "65001",
+ node => "localhost",
+ },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ },
+ },
+ },
+}
diff --git a/src/test/zones/evpn/ebgp_loopback/expected_controller_config b/src/test/zones/evpn/ebgp_loopback/expected_controller_config
new file mode 100644
index 0000000..548d532
--- /dev/null
+++ b/src/test/zones/evpn/ebgp_loopback/expected_controller_config
@@ -0,0 +1,69 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+ip protocol bgp route-map correct_src
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65001
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as external
+ neighbor VTEP bfd
+ neighbor VTEP ebgp-multihop 10
+ neighbor VTEP update-source dummy1
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ bgp disable-ebgp-connected-route-check
+ neighbor BGP peer-group
+ neighbor BGP remote-as external
+ neighbor BGP bfd
+ neighbor 172.16.0.254 peer-group BGP
+ neighbor 172.17.0.254 peer-group BGP
+ !
+ address-family ipv4 unicast
+ network 192.168.0.1/32
+ neighbor BGP activate
+ neighbor BGP soft-reconfiguration inbound
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ autort as 65000
+ exit-address-family
+exit
+!
+router bgp 65001 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+ !
+ address-family l2vpn evpn
+ route-target import 65000:1000
+ route-target export 65000:1000
+ exit-address-family
+exit
+!
+ip prefix-list loopbacks_ips seq 10 permit 0.0.0.0/0 le 32
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+route-map correct_src permit 1
+ match ip address prefix-list loopbacks_ips
+ set src 192.168.0.1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces b/src/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces
new file mode 100644
index 0000000..4cf13e0
--- /dev/null
+++ b/src/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces
@@ -0,0 +1,41 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/ebgp_loopback/interfaces b/src/test/zones/evpn/ebgp_loopback/interfaces
new file mode 100644
index 0000000..f6bc352
--- /dev/null
+++ b/src/test/zones/evpn/ebgp_loopback/interfaces
@@ -0,0 +1,13 @@
+auto eth0
+iface eth0 inet static
+ address 172.16.0.1/24
+
+auto eth1
+iface eth1 inet static
+ address 172.17.0.1/24
+
+auto dummy1
+iface dummy1 inet static
+ address 192.168.0.1/32
+ link-type dummy
+
diff --git a/src/test/zones/evpn/ebgp_loopback/sdn_config b/src/test/zones/evpn/ebgp_loopback/sdn_config
new file mode 100644
index 0000000..c8bc2e0
--- /dev/null
+++ b/src/test/zones/evpn/ebgp_loopback/sdn_config
@@ -0,0 +1,29 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000 } },
+ },
+ controllers => {
+ ids => {
+ evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" },
+ localhost => { type => "bgp", 'peers' => '172.16.0.254,172.17.0.254', ebgp => "1", asn => "65001", loopback => 'dummy1', node => "localhost" },
+ },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/exitnode/expected_controller_config b/src/test/zones/evpn/exitnode/expected_controller_config
new file mode 100644
index 0000000..48830a3
--- /dev/null
+++ b/src/test/zones/evpn/exitnode/expected_controller_config
@@ -0,0 +1,66 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family ipv4 unicast
+ import vrf vrf_myzone
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ import vrf vrf_myzone
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ default-originate ipv4
+ default-originate ipv6
+ exit-address-family
+exit
+!
+route-map MAP_VTEP_IN deny 1
+ match evpn route-type prefix
+exit
+!
+route-map MAP_VTEP_IN permit 2
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/exitnode/expected_sdn_interfaces b/src/test/zones/evpn/exitnode/expected_sdn_interfaces
new file mode 100644
index 0000000..5ab3084
--- /dev/null
+++ b/src/test/zones/evpn/exitnode/expected_sdn_interfaces
@@ -0,0 +1,41 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route del vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/exitnode/interfaces b/src/test/zones/evpn/exitnode/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/exitnode/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/exitnode/sdn_config b/src/test/zones/evpn/exitnode/sdn_config
new file mode 100644
index 0000000..fd81817
--- /dev/null
+++ b/src/test/zones/evpn/exitnode/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, exitnodes => { 'localhost' => 1 } } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/exitnode_local_routing/expected_controller_config b/src/test/zones/evpn/exitnode_local_routing/expected_controller_config
new file mode 100644
index 0000000..f671b63
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_local_routing/expected_controller_config
@@ -0,0 +1,51 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+ip route 10.0.0.0/24 10.255.255.2 xvrf_myzone
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+ !
+ address-family l2vpn evpn
+ default-originate ipv4
+ default-originate ipv6
+ exit-address-family
+exit
+!
+route-map MAP_VTEP_IN deny 1
+ match evpn route-type prefix
+exit
+!
+route-map MAP_VTEP_IN permit 2
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces
new file mode 100644
index 0000000..301f5b3
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces
@@ -0,0 +1,56 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route del vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto xvrf_myzone
+iface xvrf_myzone
+ link-type veth
+ address 10.255.255.1/30
+ veth-peer-name xvrfp_myzone
+ mtu 1500
+
+auto xvrfp_myzone
+iface xvrfp_myzone
+ link-type veth
+ address 10.255.255.2/30
+ veth-peer-name xvrf_myzone
+ vrf vrf_myzone
+ mtu 1500
diff --git a/src/test/zones/evpn/exitnode_local_routing/interfaces b/src/test/zones/evpn/exitnode_local_routing/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_local_routing/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/exitnode_local_routing/sdn_config b/src/test/zones/evpn/exitnode_local_routing/sdn_config
new file mode 100644
index 0000000..f5f7ca1
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_local_routing/sdn_config
@@ -0,0 +1,27 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, exitnodes => { 'localhost' => 1 }, 'exitnodes-local-routing' => 1 },
+ },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ },
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/exitnode_primary/expected_controller_config b/src/test/zones/evpn/exitnode_primary/expected_controller_config
new file mode 100644
index 0000000..e45b22c
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_primary/expected_controller_config
@@ -0,0 +1,68 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family ipv4 unicast
+ import vrf vrf_myzone
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ import vrf vrf_myzone
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ default-originate ipv4
+ default-originate ipv6
+ exit-address-family
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+ match evpn vni 1000
+ match evpn route-type prefix
+ set metric 200
+exit
+!
+route-map MAP_VTEP_OUT permit 2
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/exitnode_primary/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_primary/expected_sdn_interfaces
new file mode 100644
index 0000000..5ab3084
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_primary/expected_sdn_interfaces
@@ -0,0 +1,41 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route del vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/exitnode_primary/interfaces b/src/test/zones/evpn/exitnode_primary/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_primary/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/exitnode_primary/sdn_config b/src/test/zones/evpn/exitnode_primary/sdn_config
new file mode 100644
index 0000000..bfeafc5
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_primary/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, 'exitnodes-primary' => "othernode", exitnodes => { 'localhost' => 1 } } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/exitnode_snat/expected_controller_config b/src/test/zones/evpn/exitnode_snat/expected_controller_config
new file mode 100644
index 0000000..48830a3
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_snat/expected_controller_config
@@ -0,0 +1,66 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family ipv4 unicast
+ import vrf vrf_myzone
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ import vrf vrf_myzone
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+ !
+ address-family ipv4 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family ipv6 unicast
+ redistribute connected
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ default-originate ipv4
+ default-originate ipv6
+ exit-address-family
+exit
+!
+route-map MAP_VTEP_IN deny 1
+ match evpn route-type prefix
+exit
+!
+route-map MAP_VTEP_IN permit 2
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
new file mode 100644
index 0000000..47df77a
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
@@ -0,0 +1,68 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ post-up iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
+ post-down iptables -t nat -D POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
+ post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
+ post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto myvnet2
+iface myvnet2
+ address 2a08:2142:302:3::1/64
+ post-up ip6tables -t nat -A POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 192.168.0.1
+ post-down ip6tables -t nat -D POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 192.168.0.1
+ post-up ip6tables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
+ post-down ip6tables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
+ bridge_ports vxlan_myvnet2
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip6-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route del vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet2
+iface vxlan_myvnet2
+ vxlan-id 200
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/exitnode_snat/interfaces b/src/test/zones/evpn/exitnode_snat/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_snat/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/exitnode_snat/sdn_config b/src/test/zones/evpn/exitnode_snat/sdn_config
new file mode 100644
index 0000000..35cdf5d
--- /dev/null
+++ b/src/test/zones/evpn/exitnode_snat/sdn_config
@@ -0,0 +1,35 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ myvnet2 => { tag => "200", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, exitnodes => { 'localhost' => 1 } } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ 'snat' => 1
+ },
+ 'myzone-2a08:2142:302:3::-64' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ 'gateway' => '2a08:2142:302:3::1',
+ 'snat' => 1
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/ipv4/expected_controller_config b/src/test/zones/evpn/ipv4/expected_controller_config
new file mode 100644
index 0000000..bd7830a
--- /dev/null
+++ b/src/test/zones/evpn/ipv4/expected_controller_config
@@ -0,0 +1,41 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/ipv4/expected_sdn_interfaces b/src/test/zones/evpn/ipv4/expected_sdn_interfaces
new file mode 100644
index 0000000..9d1c64c
--- /dev/null
+++ b/src/test/zones/evpn/ipv4/expected_sdn_interfaces
@@ -0,0 +1,42 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ hwaddress A2:1D:CB:1A:C0:8B
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/ipv4/interfaces b/src/test/zones/evpn/ipv4/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/ipv4/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/ipv4/sdn_config b/src/test/zones/evpn/ipv4/sdn_config
new file mode 100644
index 0000000..dd73b5c
--- /dev/null
+++ b/src/test/zones/evpn/ipv4/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, 'mac' => 'A2:1D:CB:1A:C0:8B' } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/ipv4ipv6/expected_controller_config b/src/test/zones/evpn/ipv4ipv6/expected_controller_config
new file mode 100644
index 0000000..bd7830a
--- /dev/null
+++ b/src/test/zones/evpn/ipv4ipv6/expected_controller_config
@@ -0,0 +1,41 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces b/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
new file mode 100644
index 0000000..7a5d741
--- /dev/null
+++ b/src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
@@ -0,0 +1,44 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ address 2a08:2142:302:3::1/64
+ hwaddress A2:1D:CB:1A:C0:8B
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ ip6-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/ipv4ipv6/interfaces b/src/test/zones/evpn/ipv4ipv6/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/ipv4ipv6/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/ipv4ipv6/sdn_config b/src/test/zones/evpn/ipv4ipv6/sdn_config
new file mode 100644
index 0000000..4583818
--- /dev/null
+++ b/src/test/zones/evpn/ipv4ipv6/sdn_config
@@ -0,0 +1,32 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, 'mac' => 'A2:1D:CB:1A:C0:8B' } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ },
+ 'myzone-2a08:2142:302:3::-64' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '2a08:2142:302:3::1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/ipv4ipv6nogateway/expected_controller_config b/src/test/zones/evpn/ipv4ipv6nogateway/expected_controller_config
new file mode 100644
index 0000000..bd7830a
--- /dev/null
+++ b/src/test/zones/evpn/ipv4ipv6nogateway/expected_controller_config
@@ -0,0 +1,41 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces b/src/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces
new file mode 100644
index 0000000..378fa77
--- /dev/null
+++ b/src/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces
@@ -0,0 +1,40 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ hwaddress A2:1D:CB:1A:C0:8B
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/ipv4ipv6nogateway/interfaces b/src/test/zones/evpn/ipv4ipv6nogateway/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/ipv4ipv6nogateway/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/ipv4ipv6nogateway/sdn_config b/src/test/zones/evpn/ipv4ipv6nogateway/sdn_config
new file mode 100644
index 0000000..ab2273f
--- /dev/null
+++ b/src/test/zones/evpn/ipv4ipv6nogateway/sdn_config
@@ -0,0 +1,30 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, 'mac' => 'A2:1D:CB:1A:C0:8B' } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-2a08:2142:302:3::-64' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/ipv6/expected_controller_config b/src/test/zones/evpn/ipv6/expected_controller_config
new file mode 100644
index 0000000..bd7830a
--- /dev/null
+++ b/src/test/zones/evpn/ipv6/expected_controller_config
@@ -0,0 +1,41 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/ipv6/expected_sdn_interfaces b/src/test/zones/evpn/ipv6/expected_sdn_interfaces
new file mode 100644
index 0000000..b2bdbfe
--- /dev/null
+++ b/src/test/zones/evpn/ipv6/expected_sdn_interfaces
@@ -0,0 +1,42 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 2a08:2142:302:3::1/64
+ hwaddress A2:1D:CB:1A:C0:8B
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip6-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/ipv6/interfaces b/src/test/zones/evpn/ipv6/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/ipv6/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/ipv6/sdn_config b/src/test/zones/evpn/ipv6/sdn_config
new file mode 100644
index 0000000..949e886
--- /dev/null
+++ b/src/test/zones/evpn/ipv6/sdn_config
@@ -0,0 +1,27 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, 'mac' => 'A2:1D:CB:1A:C0:8B' } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-2a08:2142:302:3::-64' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '2a08:2142:302:3::1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/evpn/multipath_relax/expected_controller_config b/src/test/zones/evpn/multipath_relax/expected_controller_config
new file mode 100644
index 0000000..2d1ad44
--- /dev/null
+++ b/src/test/zones/evpn/multipath_relax/expected_controller_config
@@ -0,0 +1,53 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ bgp bestpath as-path multipath-relax
+ neighbor BGP peer-group
+ neighbor BGP remote-as 65000
+ neighbor BGP bfd
+ neighbor 192.168.0.1 peer-group BGP
+ neighbor 192.168.0.2 peer-group BGP
+ neighbor 192.168.0.3 peer-group BGP
+ !
+ address-family ipv4 unicast
+ neighbor BGP activate
+ neighbor BGP soft-reconfiguration inbound
+ exit-address-family
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/multipath_relax/expected_sdn_interfaces b/src/test/zones/evpn/multipath_relax/expected_sdn_interfaces
new file mode 100644
index 0000000..4cf13e0
--- /dev/null
+++ b/src/test/zones/evpn/multipath_relax/expected_sdn_interfaces
@@ -0,0 +1,41 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/multipath_relax/interfaces b/src/test/zones/evpn/multipath_relax/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/multipath_relax/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/multipath_relax/sdn_config b/src/test/zones/evpn/multipath_relax/sdn_config
new file mode 100644
index 0000000..5a1d8a7
--- /dev/null
+++ b/src/test/zones/evpn/multipath_relax/sdn_config
@@ -0,0 +1,49 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => {
+ tag => "100",
+ type => "vnet",
+ zone => "myzone",
+ },
+ },
+ },
+
+ zones => {
+ ids => {
+ myzone => {
+ ipam => "pve",
+ type => "evpn",
+ controller => "evpnctl",
+ 'vrf-vxlan' => 1000,
+ },
+ },
+ },
+ controllers => {
+ ids => {
+ evpnctl => {
+ type => "evpn",
+ 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3',
+ asn => "65000",
+ },
+ localhost => {
+ type => "bgp",
+ 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3',
+ 'bgp-multipath-as-path-relax' => "1",
+ asn => "65000",
+ node => "localhost",
+ },
+ },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ },
+ },
+ },
+}
diff --git a/src/test/zones/evpn/rt_import/expected_controller_config b/src/test/zones/evpn/rt_import/expected_controller_config
new file mode 100644
index 0000000..f4f28dd
--- /dev/null
+++ b/src/test/zones/evpn/rt_import/expected_controller_config
@@ -0,0 +1,47 @@
+frr version 8.2.2
+frr defaults datacenter
+hostname localhost
+log syslog informational
+service integrated-vtysh-config
+!
+!
+vrf vrf_myzone
+ vni 1000
+exit-vrf
+!
+router bgp 65000
+ bgp router-id 192.168.0.1
+ no bgp default ipv4-unicast
+ coalesce-time 1000
+ neighbor VTEP peer-group
+ neighbor VTEP remote-as 65000
+ neighbor VTEP bfd
+ neighbor 192.168.0.2 peer-group VTEP
+ neighbor 192.168.0.3 peer-group VTEP
+ !
+ address-family l2vpn evpn
+ neighbor VTEP route-map MAP_VTEP_IN in
+ neighbor VTEP route-map MAP_VTEP_OUT out
+ neighbor VTEP activate
+ advertise-all-vni
+ exit-address-family
+exit
+!
+router bgp 65000 vrf vrf_myzone
+ bgp router-id 192.168.0.1
+ !
+ address-family l2vpn evpn
+ route-target import 65001:1000
+ route-target import 65002:1000
+ route-target import 65003:1000
+ exit-address-family
+exit
+!
+route-map MAP_VTEP_IN permit 1
+exit
+!
+route-map MAP_VTEP_OUT permit 1
+exit
+!
+line vty
+! \ No newline at end of file
diff --git a/src/test/zones/evpn/rt_import/expected_sdn_interfaces b/src/test/zones/evpn/rt_import/expected_sdn_interfaces
new file mode 100644
index 0000000..9d1c64c
--- /dev/null
+++ b/src/test/zones/evpn/rt_import/expected_sdn_interfaces
@@ -0,0 +1,42 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ hwaddress A2:1D:CB:1A:C0:8B
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ ip-forward on
+ arp-accept on
+ vrf vrf_myzone
+
+auto vrf_myzone
+iface vrf_myzone
+ vrf-table auto
+ post-up ip route add vrf vrf_myzone unreachable default metric 4278198272
+
+auto vrfbr_myzone
+iface vrfbr_myzone
+ bridge-ports vrfvx_myzone
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+ vrf vrf_myzone
+
+auto vrfvx_myzone
+iface vrfvx_myzone
+ vxlan-id 1000
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan-local-tunnelip 192.168.0.1
+ bridge-learning off
+ bridge-arp-nd-suppress on
+ mtu 1450
diff --git a/src/test/zones/evpn/rt_import/interfaces b/src/test/zones/evpn/rt_import/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/evpn/rt_import/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/evpn/rt_import/sdn_config b/src/test/zones/evpn/rt_import/sdn_config
new file mode 100644
index 0000000..b62bb2e
--- /dev/null
+++ b/src/test/zones/evpn/rt_import/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", zone => "myzone" },
+ },
+ },
+
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "evpn", controller => "evpnctl", 'vrf-vxlan' => 1000, 'mac' => 'A2:1D:CB:1A:C0:8B', 'rt-import' => '65001:1000,65002:1000,65003:1000' } },
+ },
+ controllers => {
+ ids => { evpnctl => { type => "evpn", 'peers' => '192.168.0.1,192.168.0.2,192.168.0.3', asn => "65000" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/qinq/bridge/expected_sdn_interfaces b/src/test/zones/qinq/bridge/expected_sdn_interfaces
new file mode 100644
index 0000000..58a0e23
--- /dev/null
+++ b/src/test/zones/qinq/bridge/expected_sdn_interfaces
@@ -0,0 +1,65 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto ln_myzone2
+iface ln_myzone2
+ link-type veth
+ veth-peer-name pr_myzone2
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+
+auto myvnet2
+iface myvnet2
+ bridge_ports z_myzone.101
+ bridge_stp off
+ bridge_fd 0
+
+auto myvnet3
+iface myvnet3
+ bridge_ports z_myzone2.100
+ bridge_stp off
+ bridge_fd 0
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto pr_myzone2
+iface pr_myzone2
+ link-type veth
+ veth-peer-name ln_myzone2
+
+auto sv_myzone
+iface sv_myzone
+ vlan-raw-device eth0
+ vlan-id 10
+
+auto sv_myzone2
+iface sv_myzone2
+ vlan-raw-device eth0
+ vlan-id 20
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports sv_myzone ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+
+auto z_myzone2
+iface z_myzone2
+ bridge-stp off
+ bridge-ports sv_myzone2 ln_myzone2
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/bridge/interfaces b/src/test/zones/qinq/bridge/interfaces
new file mode 100644
index 0000000..68b6a88
--- /dev/null
+++ b/src/test/zones/qinq/bridge/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/qinq/bridge/sdn_config b/src/test/zones/qinq/bridge/sdn_config
new file mode 100644
index 0000000..6321603
--- /dev/null
+++ b/src/test/zones/qinq/bridge/sdn_config
@@ -0,0 +1,16 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ myvnet2 => { tag => 101, type => "vnet", zone => "myzone" },
+ myvnet3 => { tag => 100, type => "vnet", zone => "myzone2" },
+ },
+ },
+ zones => {
+ ids => {
+ myzone => { bridge => "vmbr0", tag => 10, ipam => "pve", type => "qinq" },
+ myzone2 => { bridge => "vmbr0", tag => 20, ipam => "pve", type => "qinq" },
+ },
+ },
+}
diff --git a/src/test/zones/qinq/bridge_notagvnet/expected_sdn_interfaces b/src/test/zones/qinq/bridge_notagvnet/expected_sdn_interfaces
new file mode 100644
index 0000000..cfa43a2
--- /dev/null
+++ b/src/test/zones/qinq/bridge_notagvnet/expected_sdn_interfaces
@@ -0,0 +1,36 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+
+auto myvnet2
+iface myvnet2
+ bridge_ports pr_myzone
+ bridge_stp off
+ bridge_fd 0
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto sv_myzone
+iface sv_myzone
+ vlan-raw-device eth0
+ vlan-id 10
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports sv_myzone ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/bridge_notagvnet/interfaces b/src/test/zones/qinq/bridge_notagvnet/interfaces
new file mode 100644
index 0000000..68b6a88
--- /dev/null
+++ b/src/test/zones/qinq/bridge_notagvnet/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/qinq/bridge_notagvnet/sdn_config b/src/test/zones/qinq/bridge_notagvnet/sdn_config
new file mode 100644
index 0000000..1f40369
--- /dev/null
+++ b/src/test/zones/qinq/bridge_notagvnet/sdn_config
@@ -0,0 +1,26 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => {
+ tag => 100,
+ type => "vnet",
+ zone => "myzone"
+ },
+ myvnet2 => {
+ type => "vnet",
+ zone => "myzone"
+ },
+ },
+ },
+ zones => {
+ ids => {
+ myzone => {
+ bridge => "vmbr0",
+ tag => 10,
+ ipam => "pve",
+ type => "qinq",
+ },
+ },
+ },
+}
diff --git a/src/test/zones/qinq/bridge_vlanaware/expected_sdn_interfaces b/src/test/zones/qinq/bridge_vlanaware/expected_sdn_interfaces
new file mode 100644
index 0000000..c325dec
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware/expected_sdn_interfaces
@@ -0,0 +1,55 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto ln_myzone2
+iface ln_myzone2
+ link-type veth
+ veth-peer-name pr_myzone2
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+
+auto myvnet2
+iface myvnet2
+ bridge_ports z_myzone.101
+ bridge_stp off
+ bridge_fd 0
+
+auto myvnet3
+iface myvnet3
+ bridge_ports z_myzone2.100
+ bridge_stp off
+ bridge_fd 0
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto pr_myzone2
+iface pr_myzone2
+ link-type veth
+ veth-peer-name ln_myzone2
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports vmbr0.10 ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+
+auto z_myzone2
+iface z_myzone2
+ bridge-stp off
+ bridge-ports vmbr0.20 ln_myzone2
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/bridge_vlanaware/interfaces b/src/test/zones/qinq/bridge_vlanaware/interfaces
new file mode 100644
index 0000000..cfdfafe
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
+ bridge-vids 2-4094
+ bridge-vlan-aware 1
diff --git a/src/test/zones/qinq/bridge_vlanaware/sdn_config b/src/test/zones/qinq/bridge_vlanaware/sdn_config
new file mode 100644
index 0000000..6321603
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware/sdn_config
@@ -0,0 +1,16 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ myvnet2 => { tag => 101, type => "vnet", zone => "myzone" },
+ myvnet3 => { tag => 100, type => "vnet", zone => "myzone2" },
+ },
+ },
+ zones => {
+ ids => {
+ myzone => { bridge => "vmbr0", tag => 10, ipam => "pve", type => "qinq" },
+ myzone2 => { bridge => "vmbr0", tag => 20, ipam => "pve", type => "qinq" },
+ },
+ },
+}
diff --git a/src/test/zones/qinq/bridge_vlanaware_notagvnet/expected_sdn_interfaces b/src/test/zones/qinq/bridge_vlanaware_notagvnet/expected_sdn_interfaces
new file mode 100644
index 0000000..cd87a3a
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware_notagvnet/expected_sdn_interfaces
@@ -0,0 +1,27 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto myvnet
+iface myvnet
+ bridge_ports pr_myzone
+ bridge_stp off
+ bridge_fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports vmbr0.10 ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/bridge_vlanaware_notagvnet/interfaces b/src/test/zones/qinq/bridge_vlanaware_notagvnet/interfaces
new file mode 100644
index 0000000..cfdfafe
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware_notagvnet/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
+ bridge-vids 2-4094
+ bridge-vlan-aware 1
diff --git a/src/test/zones/qinq/bridge_vlanaware_notagvnet/sdn_config b/src/test/zones/qinq/bridge_vlanaware_notagvnet/sdn_config
new file mode 100644
index 0000000..2382f4d
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware_notagvnet/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", vlanaware => "1", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", tag => 10, ipam => "pve", type => "qinq" } },
+ },
+}
diff --git a/src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces b/src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces
new file mode 100644
index 0000000..28d215b
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces
@@ -0,0 +1,27 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports vmbr0.10 ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/interfaces b/src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/interfaces
new file mode 100644
index 0000000..cfdfafe
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
+ bridge-vids 2-4094
+ bridge-vlan-aware 1
diff --git a/src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/sdn_config b/src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/sdn_config
new file mode 100644
index 0000000..c013176
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", vlanaware => "1", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", tag => 10, ipam => "pve", type => "qinq" } },
+ },
+}
diff --git a/src/test/zones/qinq/bridge_vlanaware_vlanprotocol/expected_sdn_interfaces b/src/test/zones/qinq/bridge_vlanaware_vlanprotocol/expected_sdn_interfaces
new file mode 100644
index 0000000..0bc301b
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware_vlanprotocol/expected_sdn_interfaces
@@ -0,0 +1,29 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto vmbr0
+iface vmbr0
+ bridge-vlan-protocol 802.1ad
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports vmbr0.10 ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/bridge_vlanaware_vlanprotocol/interfaces b/src/test/zones/qinq/bridge_vlanaware_vlanprotocol/interfaces
new file mode 100644
index 0000000..cfdfafe
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware_vlanprotocol/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
+ bridge-vids 2-4094
+ bridge-vlan-aware 1
diff --git a/src/test/zones/qinq/bridge_vlanaware_vlanprotocol/sdn_config b/src/test/zones/qinq/bridge_vlanaware_vlanprotocol/sdn_config
new file mode 100644
index 0000000..20a8a51
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanaware_vlanprotocol/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", tag => 10, 'vlan-protocol' => '802.1ad', ipam => "pve", type => "qinq" } },
+ },
+}
diff --git a/src/test/zones/qinq/bridge_vlanawarevnet/expected_sdn_interfaces b/src/test/zones/qinq/bridge_vlanawarevnet/expected_sdn_interfaces
new file mode 100644
index 0000000..bde23d9
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanawarevnet/expected_sdn_interfaces
@@ -0,0 +1,32 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto sv_myzone
+iface sv_myzone
+ vlan-raw-device eth0
+ vlan-id 10
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports sv_myzone ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/bridge_vlanawarevnet/interfaces b/src/test/zones/qinq/bridge_vlanawarevnet/interfaces
new file mode 100644
index 0000000..68b6a88
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanawarevnet/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/qinq/bridge_vlanawarevnet/sdn_config b/src/test/zones/qinq/bridge_vlanawarevnet/sdn_config
new file mode 100644
index 0000000..c013176
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanawarevnet/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", vlanaware => "1", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", tag => 10, ipam => "pve", type => "qinq" } },
+ },
+}
diff --git a/src/test/zones/qinq/bridge_vlanprotocol/expected_sdn_interfaces b/src/test/zones/qinq/bridge_vlanprotocol/expected_sdn_interfaces
new file mode 100644
index 0000000..6b59164
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanprotocol/expected_sdn_interfaces
@@ -0,0 +1,31 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto sv_myzone
+iface sv_myzone
+ vlan-raw-device eth0
+ vlan-id 10
+ vlan-protocol 802.1ad
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports sv_myzone ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/bridge_vlanprotocol/interfaces b/src/test/zones/qinq/bridge_vlanprotocol/interfaces
new file mode 100644
index 0000000..68b6a88
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanprotocol/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/qinq/bridge_vlanprotocol/sdn_config b/src/test/zones/qinq/bridge_vlanprotocol/sdn_config
new file mode 100644
index 0000000..20a8a51
--- /dev/null
+++ b/src/test/zones/qinq/bridge_vlanprotocol/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", tag => 10, 'vlan-protocol' => '802.1ad', ipam => "pve", type => "qinq" } },
+ },
+}
diff --git a/src/test/zones/qinq/ovs/expected_sdn_interfaces b/src/test/zones/qinq/ovs/expected_sdn_interfaces
new file mode 100644
index 0000000..d25b2a8
--- /dev/null
+++ b/src/test/zones/qinq/ovs/expected_sdn_interfaces
@@ -0,0 +1,71 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto ln_myzone2
+iface ln_myzone2
+ link-type veth
+ veth-peer-name pr_myzone2
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+
+auto myvnet2
+iface myvnet2
+ bridge_ports z_myzone.101
+ bridge_stp off
+ bridge_fd 0
+
+auto myvnet3
+iface myvnet3
+ bridge_ports z_myzone2.100
+ bridge_stp off
+ bridge_fd 0
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto pr_myzone2
+iface pr_myzone2
+ link-type veth
+ veth-peer-name ln_myzone2
+
+auto sv_myzone
+iface sv_myzone
+ ovs_type OVSIntPort
+ ovs_bridge vmbr0
+ ovs_options vlan_mode=dot1q-tunnel tag=10 other_config:qinq-ethtype=802.1q
+
+auto sv_myzone2
+iface sv_myzone2
+ ovs_type OVSIntPort
+ ovs_bridge vmbr0
+ ovs_options vlan_mode=dot1q-tunnel tag=20 other_config:qinq-ethtype=802.1q
+
+auto vmbr0
+iface vmbr0
+ ovs_ports sv_myzone sv_myzone2
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports sv_myzone ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+
+auto z_myzone2
+iface z_myzone2
+ bridge-stp off
+ bridge-ports sv_myzone2 ln_myzone2
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/ovs/interfaces b/src/test/zones/qinq/ovs/interfaces
new file mode 100644
index 0000000..14d2f1e
--- /dev/null
+++ b/src/test/zones/qinq/ovs/interfaces
@@ -0,0 +1,9 @@
+auto eth0
+iface eth0 inet manual
+ ovs_type OVSPort
+ ovs_bridge vmbr0
+
+auto vmbr0
+iface vmbr0 inet manual
+ ovs_type OVSBridge
+ ovs_ports eth0
diff --git a/src/test/zones/qinq/ovs/sdn_config b/src/test/zones/qinq/ovs/sdn_config
new file mode 100644
index 0000000..6321603
--- /dev/null
+++ b/src/test/zones/qinq/ovs/sdn_config
@@ -0,0 +1,16 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ myvnet2 => { tag => 101, type => "vnet", zone => "myzone" },
+ myvnet3 => { tag => 100, type => "vnet", zone => "myzone2" },
+ },
+ },
+ zones => {
+ ids => {
+ myzone => { bridge => "vmbr0", tag => 10, ipam => "pve", type => "qinq" },
+ myzone2 => { bridge => "vmbr0", tag => 20, ipam => "pve", type => "qinq" },
+ },
+ },
+}
diff --git a/src/test/zones/qinq/ovs_notagvnet/expected_sdn_interfaces b/src/test/zones/qinq/ovs_notagvnet/expected_sdn_interfaces
new file mode 100644
index 0000000..5f47b28
--- /dev/null
+++ b/src/test/zones/qinq/ovs_notagvnet/expected_sdn_interfaces
@@ -0,0 +1,37 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto myvnet
+iface myvnet
+ bridge_ports pr_myzone
+ bridge_stp off
+ bridge_fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto sv_myzone
+iface sv_myzone
+ ovs_type OVSIntPort
+ ovs_bridge vmbr0
+ ovs_options vlan_mode=dot1q-tunnel tag=10 other_config:qinq-ethtype=802.1q
+
+auto vmbr0
+iface vmbr0
+ ovs_ports sv_myzone
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports sv_myzone ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/ovs_notagvnet/interfaces b/src/test/zones/qinq/ovs_notagvnet/interfaces
new file mode 100644
index 0000000..14d2f1e
--- /dev/null
+++ b/src/test/zones/qinq/ovs_notagvnet/interfaces
@@ -0,0 +1,9 @@
+auto eth0
+iface eth0 inet manual
+ ovs_type OVSPort
+ ovs_bridge vmbr0
+
+auto vmbr0
+iface vmbr0 inet manual
+ ovs_type OVSBridge
+ ovs_ports eth0
diff --git a/src/test/zones/qinq/ovs_notagvnet/sdn_config b/src/test/zones/qinq/ovs_notagvnet/sdn_config
new file mode 100644
index 0000000..2382f4d
--- /dev/null
+++ b/src/test/zones/qinq/ovs_notagvnet/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", vlanaware => "1", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", tag => 10, ipam => "pve", type => "qinq" } },
+ },
+}
diff --git a/src/test/zones/qinq/ovs_vlanawarevnet/expected_sdn_interfaces b/src/test/zones/qinq/ovs_vlanawarevnet/expected_sdn_interfaces
new file mode 100644
index 0000000..d69d38c
--- /dev/null
+++ b/src/test/zones/qinq/ovs_vlanawarevnet/expected_sdn_interfaces
@@ -0,0 +1,37 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto sv_myzone
+iface sv_myzone
+ ovs_type OVSIntPort
+ ovs_bridge vmbr0
+ ovs_options vlan_mode=dot1q-tunnel tag=10 other_config:qinq-ethtype=802.1q
+
+auto vmbr0
+iface vmbr0
+ ovs_ports sv_myzone
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports sv_myzone ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/ovs_vlanawarevnet/interfaces b/src/test/zones/qinq/ovs_vlanawarevnet/interfaces
new file mode 100644
index 0000000..14d2f1e
--- /dev/null
+++ b/src/test/zones/qinq/ovs_vlanawarevnet/interfaces
@@ -0,0 +1,9 @@
+auto eth0
+iface eth0 inet manual
+ ovs_type OVSPort
+ ovs_bridge vmbr0
+
+auto vmbr0
+iface vmbr0 inet manual
+ ovs_type OVSBridge
+ ovs_ports eth0
diff --git a/src/test/zones/qinq/ovs_vlanawarevnet/sdn_config b/src/test/zones/qinq/ovs_vlanawarevnet/sdn_config
new file mode 100644
index 0000000..c013176
--- /dev/null
+++ b/src/test/zones/qinq/ovs_vlanawarevnet/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", vlanaware => "1", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", tag => 10, ipam => "pve", type => "qinq" } },
+ },
+}
diff --git a/src/test/zones/qinq/ovs_vlanprotocol/expected_sdn_interfaces b/src/test/zones/qinq/ovs_vlanprotocol/expected_sdn_interfaces
new file mode 100644
index 0000000..aeefec9
--- /dev/null
+++ b/src/test/zones/qinq/ovs_vlanprotocol/expected_sdn_interfaces
@@ -0,0 +1,35 @@
+#version:1
+
+auto ln_myzone
+iface ln_myzone
+ link-type veth
+ veth-peer-name pr_myzone
+
+auto myvnet
+iface myvnet
+ bridge_ports z_myzone.100
+ bridge_stp off
+ bridge_fd 0
+
+auto pr_myzone
+iface pr_myzone
+ link-type veth
+ veth-peer-name ln_myzone
+
+auto sv_myzone
+iface sv_myzone
+ ovs_type OVSIntPort
+ ovs_bridge vmbr0
+ ovs_options vlan_mode=dot1q-tunnel tag=10 other_config:qinq-ethtype=802.1ad
+
+auto vmbr0
+iface vmbr0
+ ovs_ports sv_myzone
+
+auto z_myzone
+iface z_myzone
+ bridge-stp off
+ bridge-ports sv_myzone ln_myzone
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/qinq/ovs_vlanprotocol/interfaces b/src/test/zones/qinq/ovs_vlanprotocol/interfaces
new file mode 100644
index 0000000..14d2f1e
--- /dev/null
+++ b/src/test/zones/qinq/ovs_vlanprotocol/interfaces
@@ -0,0 +1,9 @@
+auto eth0
+iface eth0 inet manual
+ ovs_type OVSPort
+ ovs_bridge vmbr0
+
+auto vmbr0
+iface vmbr0 inet manual
+ ovs_type OVSBridge
+ ovs_ports eth0
diff --git a/src/test/zones/qinq/ovs_vlanprotocol/sdn_config b/src/test/zones/qinq/ovs_vlanprotocol/sdn_config
new file mode 100644
index 0000000..20a8a51
--- /dev/null
+++ b/src/test/zones/qinq/ovs_vlanprotocol/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", tag => 10, 'vlan-protocol' => '802.1ad', ipam => "pve", type => "qinq" } },
+ },
+}
diff --git a/src/test/zones/simple/basic/expected_sdn_interfaces b/src/test/zones/simple/basic/expected_sdn_interfaces
new file mode 100644
index 0000000..1e0c2c7
--- /dev/null
+++ b/src/test/zones/simple/basic/expected_sdn_interfaces
@@ -0,0 +1,7 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ bridge_ports none
+ bridge_stp off
+ bridge_fd 0
diff --git a/src/test/zones/simple/basic/interfaces b/src/test/zones/simple/basic/interfaces
new file mode 100644
index 0000000..68b6a88
--- /dev/null
+++ b/src/test/zones/simple/basic/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/simple/basic/sdn_config b/src/test/zones/simple/basic/sdn_config
new file mode 100644
index 0000000..527dcba
--- /dev/null
+++ b/src/test/zones/simple/basic/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "simple" } },
+ },
+}
diff --git a/src/test/zones/simple/hetzner/expected_sdn_interfaces b/src/test/zones/simple/hetzner/expected_sdn_interfaces
new file mode 100644
index 0000000..f47ac53
--- /dev/null
+++ b/src/test/zones/simple/hetzner/expected_sdn_interfaces
@@ -0,0 +1,19 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 144.76.100.65/29
+ bridge_ports none
+ bridge_stp off
+ bridge_fd 0
+ ip-forward on
+
+auto myvnet2
+iface myvnet2
+ address 144.76.0.1/32
+ up ip route add 144.76.200.65/32 dev myvnet2
+ up ip route add 144.76.200.66/32 dev myvnet2
+ bridge_ports none
+ bridge_stp off
+ bridge_fd 0
+ ip-forward on
diff --git a/src/test/zones/simple/hetzner/interfaces b/src/test/zones/simple/hetzner/interfaces
new file mode 100644
index 0000000..5ab9635
--- /dev/null
+++ b/src/test/zones/simple/hetzner/interfaces
@@ -0,0 +1,6 @@
+auto eth0
+iface eth0 inet static
+ address 144.76.0.1
+ netmask 255.255.255.255
+ pointopoint 172.31.1.1
+ gateway 172.31.1.1 \ No newline at end of file
diff --git a/src/test/zones/simple/hetzner/sdn_config b/src/test/zones/simple/hetzner/sdn_config
new file mode 100644
index 0000000..30773ca
--- /dev/null
+++ b/src/test/zones/simple/hetzner/sdn_config
@@ -0,0 +1,34 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ myvnet2 => { type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "simple" } },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-144.76.100.64-29' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '144.76.100.65',
+ },
+ 'myzone-144.76.200.65-32' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ 'gateway' => '144.76.0.1',
+ },
+ 'myzone-144.76.200.66-32' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet2',
+ 'gateway' => '144.76.0.1',
+ },
+ }
+ }
+}
+
+
diff --git a/src/test/zones/simple/ipv4/expected_sdn_interfaces b/src/test/zones/simple/ipv4/expected_sdn_interfaces
new file mode 100644
index 0000000..06e43ad
--- /dev/null
+++ b/src/test/zones/simple/ipv4/expected_sdn_interfaces
@@ -0,0 +1,9 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 192.168.0.1/24
+ bridge_ports none
+ bridge_stp off
+ bridge_fd 0
+ ip-forward on
diff --git a/src/test/zones/simple/ipv4/interfaces b/src/test/zones/simple/ipv4/interfaces
new file mode 100644
index 0000000..68b6a88
--- /dev/null
+++ b/src/test/zones/simple/ipv4/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/simple/ipv4/sdn_config b/src/test/zones/simple/ipv4/sdn_config
new file mode 100644
index 0000000..dd77b75
--- /dev/null
+++ b/src/test/zones/simple/ipv4/sdn_config
@@ -0,0 +1,22 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "simple" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-192.168.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '192.168.0.1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/simple/ipv4snat/expected_sdn_interfaces b/src/test/zones/simple/ipv4snat/expected_sdn_interfaces
new file mode 100644
index 0000000..69d7986
--- /dev/null
+++ b/src/test/zones/simple/ipv4snat/expected_sdn_interfaces
@@ -0,0 +1,13 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 10.0.0.1/24
+ post-up iptables -t nat -A POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
+ post-down iptables -t nat -D POSTROUTING -s '10.0.0.0/24' -o vmbr0 -j SNAT --to-source 192.168.0.1
+ post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
+ post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
+ bridge_ports none
+ bridge_stp off
+ bridge_fd 0
+ ip-forward on
diff --git a/src/test/zones/simple/ipv4snat/interfaces b/src/test/zones/simple/ipv4snat/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/simple/ipv4snat/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/simple/ipv4snat/sdn_config b/src/test/zones/simple/ipv4snat/sdn_config
new file mode 100644
index 0000000..5936d7d
--- /dev/null
+++ b/src/test/zones/simple/ipv4snat/sdn_config
@@ -0,0 +1,23 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "simple" } },
+ },
+
+ subnets => {
+ ids => { 'myzone-10.0.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '10.0.0.1',
+ 'snat' => 1
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/simple/ipv4v6/expected_sdn_interfaces b/src/test/zones/simple/ipv4v6/expected_sdn_interfaces
new file mode 100644
index 0000000..34ed5db
--- /dev/null
+++ b/src/test/zones/simple/ipv4v6/expected_sdn_interfaces
@@ -0,0 +1,11 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 192.168.0.1/24
+ address 2a08:2142:302:3::1/64
+ bridge_ports none
+ bridge_stp off
+ bridge_fd 0
+ ip-forward on
+ ip6-forward on
diff --git a/src/test/zones/simple/ipv4v6/interfaces b/src/test/zones/simple/ipv4v6/interfaces
new file mode 100644
index 0000000..68b6a88
--- /dev/null
+++ b/src/test/zones/simple/ipv4v6/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/simple/ipv4v6/sdn_config b/src/test/zones/simple/ipv4v6/sdn_config
new file mode 100644
index 0000000..b8ed848
--- /dev/null
+++ b/src/test/zones/simple/ipv4v6/sdn_config
@@ -0,0 +1,27 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "simple" } },
+ },
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '192.168.0.1',
+ },
+ 'myzone-2a08:2142:302:3::-64' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '2a08:2142:302:3::1',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/simple/ipv4v6nogateway/expected_sdn_interfaces b/src/test/zones/simple/ipv4v6nogateway/expected_sdn_interfaces
new file mode 100644
index 0000000..1e0c2c7
--- /dev/null
+++ b/src/test/zones/simple/ipv4v6nogateway/expected_sdn_interfaces
@@ -0,0 +1,7 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ bridge_ports none
+ bridge_stp off
+ bridge_fd 0
diff --git a/src/test/zones/simple/ipv4v6nogateway/interfaces b/src/test/zones/simple/ipv4v6nogateway/interfaces
new file mode 100644
index 0000000..68b6a88
--- /dev/null
+++ b/src/test/zones/simple/ipv4v6nogateway/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/simple/ipv4v6nogateway/sdn_config b/src/test/zones/simple/ipv4v6nogateway/sdn_config
new file mode 100644
index 0000000..dbd75c9
--- /dev/null
+++ b/src/test/zones/simple/ipv4v6nogateway/sdn_config
@@ -0,0 +1,25 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "simple" } },
+ },
+ subnets => {
+ ids => {
+ 'myzone-192.168.0.0-24' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ },
+ 'myzone-2a08:2142:302:3::-64' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/simple/ipv6snat/expected_sdn_interfaces b/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
new file mode 100644
index 0000000..d3adc24
--- /dev/null
+++ b/src/test/zones/simple/ipv6snat/expected_sdn_interfaces
@@ -0,0 +1,13 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ address 2a08:2142:302:3::1/64
+ post-up ip6tables -t nat -A POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 192.168.0.1
+ post-down ip6tables -t nat -D POSTROUTING -s '2a08:2142:302:3::/64' -o vmbr0 -j SNAT --to-source 192.168.0.1
+ post-up ip6tables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1
+ post-down ip6tables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1
+ bridge_ports none
+ bridge_stp off
+ bridge_fd 0
+ ip6-forward on
diff --git a/src/test/zones/simple/ipv6snat/interfaces b/src/test/zones/simple/ipv6snat/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/simple/ipv6snat/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/simple/ipv6snat/sdn_config b/src/test/zones/simple/ipv6snat/sdn_config
new file mode 100644
index 0000000..bc38527
--- /dev/null
+++ b/src/test/zones/simple/ipv6snat/sdn_config
@@ -0,0 +1,24 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "simple" } },
+ },
+
+ subnets => {
+ ids => {
+ 'myzone-2a08:2142:302:3::-64' => {
+ 'type' => 'subnet',
+ 'vnet' => 'myvnet',
+ 'gateway' => '2a08:2142:302:3::1',
+ 'snat' => 1
+ }
+ }
+ }
+}
+
+
diff --git a/src/test/zones/vlan/bridge/expected_sdn_interfaces b/src/test/zones/vlan/bridge/expected_sdn_interfaces
new file mode 100644
index 0000000..f9e96d1
--- /dev/null
+++ b/src/test/zones/vlan/bridge/expected_sdn_interfaces
@@ -0,0 +1,23 @@
+#version:1
+
+auto ln_myvnet
+iface ln_myvnet
+ link-type veth
+ veth-peer-name pr_myvnet
+
+auto myvnet
+iface myvnet
+ bridge_ports ln_myvnet
+ bridge_stp off
+ bridge_fd 0
+
+auto pr_myvnet
+iface pr_myvnet
+ link-type veth
+ veth-peer-name ln_myvnet
+
+auto vmbr0v100
+iface vmbr0v100
+ bridge_ports eth0.100 pr_myvnet
+ bridge_stp off
+ bridge_fd 0
diff --git a/src/test/zones/vlan/bridge/interfaces b/src/test/zones/vlan/bridge/interfaces
new file mode 100644
index 0000000..68b6a88
--- /dev/null
+++ b/src/test/zones/vlan/bridge/interfaces
@@ -0,0 +1,5 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/vlan/bridge/sdn_config b/src/test/zones/vlan/bridge/sdn_config
new file mode 100644
index 0000000..c6cfaaa
--- /dev/null
+++ b/src/test/zones/vlan/bridge/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", ipam => "pve", type => "vlan" } },
+ },
+}
diff --git a/src/test/zones/vlan/bridge_vlanaware/expected_sdn_interfaces b/src/test/zones/vlan/bridge_vlanaware/expected_sdn_interfaces
new file mode 100644
index 0000000..a318c7a
--- /dev/null
+++ b/src/test/zones/vlan/bridge_vlanaware/expected_sdn_interfaces
@@ -0,0 +1,7 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ bridge_ports vmbr0.100
+ bridge_stp off
+ bridge_fd 0
diff --git a/src/test/zones/vlan/bridge_vlanaware/interfaces b/src/test/zones/vlan/bridge_vlanaware/interfaces
new file mode 100644
index 0000000..cfdfafe
--- /dev/null
+++ b/src/test/zones/vlan/bridge_vlanaware/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
+ bridge-vids 2-4094
+ bridge-vlan-aware 1
diff --git a/src/test/zones/vlan/bridge_vlanaware/sdn_config b/src/test/zones/vlan/bridge_vlanaware/sdn_config
new file mode 100644
index 0000000..c6cfaaa
--- /dev/null
+++ b/src/test/zones/vlan/bridge_vlanaware/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", ipam => "pve", type => "vlan" } },
+ },
+}
diff --git a/src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces b/src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces
new file mode 100644
index 0000000..ebf9d2e
--- /dev/null
+++ b/src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces
@@ -0,0 +1,9 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ bridge_ports vmbr0.100
+ bridge_stp off
+ bridge_fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
diff --git a/src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/interfaces b/src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/interfaces
new file mode 100644
index 0000000..64eec9e
--- /dev/null
+++ b/src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet manual
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4096
diff --git a/src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/sdn_config b/src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/sdn_config
new file mode 100644
index 0000000..67068f9
--- /dev/null
+++ b/src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => "100", type => "vnet", vlanaware => 1, zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", ipam => "pve", type => "vlan" } },
+ },
+}
diff --git a/src/test/zones/vlan/ovs/expected_sdn_interfaces b/src/test/zones/vlan/ovs/expected_sdn_interfaces
new file mode 100644
index 0000000..044559e
--- /dev/null
+++ b/src/test/zones/vlan/ovs/expected_sdn_interfaces
@@ -0,0 +1,17 @@
+#version:1
+
+auto ln_myvnet
+iface ln_myvnet
+ ovs_type OVSIntPort
+ ovs_bridge vmbr0
+ ovs_options tag=100
+
+auto myvnet
+iface myvnet
+ bridge_ports ln_myvnet
+ bridge_stp off
+ bridge_fd 0
+
+auto vmbr0
+iface vmbr0
+ ovs_ports ln_myvnet
diff --git a/src/test/zones/vlan/ovs/interfaces b/src/test/zones/vlan/ovs/interfaces
new file mode 100644
index 0000000..14d2f1e
--- /dev/null
+++ b/src/test/zones/vlan/ovs/interfaces
@@ -0,0 +1,9 @@
+auto eth0
+iface eth0 inet manual
+ ovs_type OVSPort
+ ovs_bridge vmbr0
+
+auto vmbr0
+iface vmbr0 inet manual
+ ovs_type OVSBridge
+ ovs_ports eth0
diff --git a/src/test/zones/vlan/ovs/sdn_config b/src/test/zones/vlan/ovs/sdn_config
new file mode 100644
index 0000000..c6cfaaa
--- /dev/null
+++ b/src/test/zones/vlan/ovs/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", ipam => "pve", type => "vlan" } },
+ },
+}
diff --git a/src/test/zones/vlan/ovs_vlanware_vnet/expected_sdn_interfaces b/src/test/zones/vlan/ovs_vlanware_vnet/expected_sdn_interfaces
new file mode 100644
index 0000000..7bb73b6
--- /dev/null
+++ b/src/test/zones/vlan/ovs_vlanware_vnet/expected_sdn_interfaces
@@ -0,0 +1,19 @@
+#version:1
+
+auto ln_myvnet
+iface ln_myvnet
+ ovs_type OVSIntPort
+ ovs_bridge vmbr0
+ ovs_options vlan_mode=dot1q-tunnel other_config:qinq-ethtype=802.1q tag=100
+
+auto myvnet
+iface myvnet
+ bridge_ports ln_myvnet
+ bridge_stp off
+ bridge_fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+
+auto vmbr0
+iface vmbr0
+ ovs_ports ln_myvnet
diff --git a/src/test/zones/vlan/ovs_vlanware_vnet/interfaces b/src/test/zones/vlan/ovs_vlanware_vnet/interfaces
new file mode 100644
index 0000000..14d2f1e
--- /dev/null
+++ b/src/test/zones/vlan/ovs_vlanware_vnet/interfaces
@@ -0,0 +1,9 @@
+auto eth0
+iface eth0 inet manual
+ ovs_type OVSPort
+ ovs_bridge vmbr0
+
+auto vmbr0
+iface vmbr0 inet manual
+ ovs_type OVSBridge
+ ovs_ports eth0
diff --git a/src/test/zones/vlan/ovs_vlanware_vnet/sdn_config b/src/test/zones/vlan/ovs_vlanware_vnet/sdn_config
new file mode 100644
index 0000000..9cfdb52
--- /dev/null
+++ b/src/test/zones/vlan/ovs_vlanware_vnet/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", vlanaware => "1", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { bridge => "vmbr0", ipam => "pve", type => "vlan" } },
+ },
+}
diff --git a/src/test/zones/vxlan/basic/expected_sdn_interfaces b/src/test/zones/vxlan/basic/expected_sdn_interfaces
new file mode 100644
index 0000000..7b73c3e
--- /dev/null
+++ b/src/test/zones/vxlan/basic/expected_sdn_interfaces
@@ -0,0 +1,15 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan_remoteip 192.168.0.2
+ vxlan_remoteip 192.168.0.3
+ mtu 1450
diff --git a/src/test/zones/vxlan/basic/interfaces b/src/test/zones/vxlan/basic/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/vxlan/basic/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/vxlan/basic/sdn_config b/src/test/zones/vxlan/basic/sdn_config
new file mode 100644
index 0000000..f929304
--- /dev/null
+++ b/src/test/zones/vxlan/basic/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "vxlan", peers => "192.168.0.1,192.168.0.2,192.168.0.3" } },
+ },
+}
diff --git a/src/test/zones/vxlan/vlanawarevnet/expected_sdn_interfaces b/src/test/zones/vxlan/vlanawarevnet/expected_sdn_interfaces
new file mode 100644
index 0000000..55cdf9c
--- /dev/null
+++ b/src/test/zones/vxlan/vlanawarevnet/expected_sdn_interfaces
@@ -0,0 +1,17 @@
+#version:1
+
+auto myvnet
+iface myvnet
+ bridge_ports vxlan_myvnet
+ bridge_stp off
+ bridge_fd 0
+ bridge-vlan-aware yes
+ bridge-vids 2-4094
+ mtu 1450
+
+auto vxlan_myvnet
+iface vxlan_myvnet
+ vxlan-id 100
+ vxlan_remoteip 192.168.0.2
+ vxlan_remoteip 192.168.0.3
+ mtu 1450
diff --git a/src/test/zones/vxlan/vlanawarevnet/interfaces b/src/test/zones/vxlan/vlanawarevnet/interfaces
new file mode 100644
index 0000000..66bb826
--- /dev/null
+++ b/src/test/zones/vxlan/vlanawarevnet/interfaces
@@ -0,0 +1,7 @@
+auto vmbr0
+iface vmbr0 inet static
+ address 192.168.0.1/24
+ gateway 192.168.0.254
+ bridge-ports eth0
+ bridge-stp off
+ bridge-fd 0
diff --git a/src/test/zones/vxlan/vlanawarevnet/sdn_config b/src/test/zones/vxlan/vlanawarevnet/sdn_config
new file mode 100644
index 0000000..23fb557
--- /dev/null
+++ b/src/test/zones/vxlan/vlanawarevnet/sdn_config
@@ -0,0 +1,11 @@
+{
+ version => 1,
+ vnets => {
+ ids => {
+ myvnet => { tag => 100, type => "vnet", vlanaware => "1", zone => "myzone" },
+ },
+ },
+ zones => {
+ ids => { myzone => { ipam => "pve", type => "vxlan", peers => "192.168.0.1,192.168.0.2,192.168.0.3" } },
+ },
+}