]> git.proxmox.com Git - pve-network.git/commitdiff
separate packaging and source build system
authorThomas Lamprecht <t.lamprecht@proxmox.com>
Thu, 25 May 2023 16:10:14 +0000 (18:10 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Thu, 25 May 2023 16:18:57 +0000 (18:18 +0200)
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>
476 files changed:
Makefile
PVE/API2/Makefile [deleted file]
PVE/API2/Network/Makefile [deleted file]
PVE/API2/Network/SDN.pm [deleted file]
PVE/API2/Network/SDN/Controllers.pm [deleted file]
PVE/API2/Network/SDN/Dns.pm [deleted file]
PVE/API2/Network/SDN/Ipams.pm [deleted file]
PVE/API2/Network/SDN/Makefile [deleted file]
PVE/API2/Network/SDN/Subnets.pm [deleted file]
PVE/API2/Network/SDN/Vnets.pm [deleted file]
PVE/API2/Network/SDN/Zones.pm [deleted file]
PVE/API2/Network/SDN/Zones/Content.pm [deleted file]
PVE/API2/Network/SDN/Zones/Makefile [deleted file]
PVE/API2/Network/SDN/Zones/Status.pm [deleted file]
PVE/Makefile [deleted file]
PVE/Network/Makefile [deleted file]
PVE/Network/SDN.pm [deleted file]
PVE/Network/SDN/Controllers.pm [deleted file]
PVE/Network/SDN/Controllers/BgpPlugin.pm [deleted file]
PVE/Network/SDN/Controllers/EvpnPlugin.pm [deleted file]
PVE/Network/SDN/Controllers/FaucetPlugin.pm [deleted file]
PVE/Network/SDN/Controllers/Makefile [deleted file]
PVE/Network/SDN/Controllers/Plugin.pm [deleted file]
PVE/Network/SDN/Dns.pm [deleted file]
PVE/Network/SDN/Dns/Makefile [deleted file]
PVE/Network/SDN/Dns/Plugin.pm [deleted file]
PVE/Network/SDN/Dns/PowerdnsPlugin.pm [deleted file]
PVE/Network/SDN/Ipams.pm [deleted file]
PVE/Network/SDN/Ipams/Makefile [deleted file]
PVE/Network/SDN/Ipams/NetboxPlugin.pm [deleted file]
PVE/Network/SDN/Ipams/PVEPlugin.pm [deleted file]
PVE/Network/SDN/Ipams/PhpIpamPlugin.pm [deleted file]
PVE/Network/SDN/Ipams/Plugin.pm [deleted file]
PVE/Network/SDN/Makefile [deleted file]
PVE/Network/SDN/SubnetPlugin.pm [deleted file]
PVE/Network/SDN/Subnets.pm [deleted file]
PVE/Network/SDN/VnetPlugin.pm [deleted file]
PVE/Network/SDN/Vnets.pm [deleted file]
PVE/Network/SDN/Zones.pm [deleted file]
PVE/Network/SDN/Zones/EvpnPlugin.pm [deleted file]
PVE/Network/SDN/Zones/FaucetPlugin.pm [deleted file]
PVE/Network/SDN/Zones/Makefile [deleted file]
PVE/Network/SDN/Zones/Plugin.pm [deleted file]
PVE/Network/SDN/Zones/QinQPlugin.pm [deleted file]
PVE/Network/SDN/Zones/SimplePlugin.pm [deleted file]
PVE/Network/SDN/Zones/VlanPlugin.pm [deleted file]
PVE/Network/SDN/Zones/VxlanPlugin.pm [deleted file]
src/Makefile [new file with mode: 0644]
src/PVE/API2/Makefile [new file with mode: 0644]
src/PVE/API2/Network/Makefile [new file with mode: 0644]
src/PVE/API2/Network/SDN.pm [new file with mode: 0644]
src/PVE/API2/Network/SDN/Controllers.pm [new file with mode: 0644]
src/PVE/API2/Network/SDN/Dns.pm [new file with mode: 0644]
src/PVE/API2/Network/SDN/Ipams.pm [new file with mode: 0644]
src/PVE/API2/Network/SDN/Makefile [new file with mode: 0644]
src/PVE/API2/Network/SDN/Subnets.pm [new file with mode: 0644]
src/PVE/API2/Network/SDN/Vnets.pm [new file with mode: 0644]
src/PVE/API2/Network/SDN/Zones.pm [new file with mode: 0644]
src/PVE/API2/Network/SDN/Zones/Content.pm [new file with mode: 0644]
src/PVE/API2/Network/SDN/Zones/Makefile [new file with mode: 0644]
src/PVE/API2/Network/SDN/Zones/Status.pm [new file with mode: 0644]
src/PVE/Makefile [new file with mode: 0644]
src/PVE/Network/Makefile [new file with mode: 0644]
src/PVE/Network/SDN.pm [new file with mode: 0644]
src/PVE/Network/SDN/Controllers.pm [new file with mode: 0644]
src/PVE/Network/SDN/Controllers/BgpPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Controllers/EvpnPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Controllers/FaucetPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Controllers/Makefile [new file with mode: 0644]
src/PVE/Network/SDN/Controllers/Plugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Dns.pm [new file with mode: 0644]
src/PVE/Network/SDN/Dns/Makefile [new file with mode: 0644]
src/PVE/Network/SDN/Dns/Plugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Dns/PowerdnsPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Ipams.pm [new file with mode: 0644]
src/PVE/Network/SDN/Ipams/Makefile [new file with mode: 0644]
src/PVE/Network/SDN/Ipams/NetboxPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Ipams/PVEPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Ipams/Plugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Makefile [new file with mode: 0644]
src/PVE/Network/SDN/SubnetPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Subnets.pm [new file with mode: 0644]
src/PVE/Network/SDN/VnetPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Vnets.pm [new file with mode: 0644]
src/PVE/Network/SDN/Zones.pm [new file with mode: 0644]
src/PVE/Network/SDN/Zones/EvpnPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Zones/FaucetPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Zones/Makefile [new file with mode: 0644]
src/PVE/Network/SDN/Zones/Plugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Zones/QinQPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Zones/SimplePlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Zones/VlanPlugin.pm [new file with mode: 0644]
src/PVE/Network/SDN/Zones/VxlanPlugin.pm [new file with mode: 0644]
src/test/Makefile [new file with mode: 0644]
src/test/debug/documentation.txt [new file with mode: 0644]
src/test/debug/generateconfig.pl [new file with mode: 0644]
src/test/debug/statuscheck.pl [new file with mode: 0644]
src/test/dns/powerdns/dns_config [new file with mode: 0644]
src/test/dns/powerdns/expected.add_a_multiple_record.ipv4 [new file with mode: 0644]
src/test/dns/powerdns/expected.add_a_multiple_record.ipv6 [new file with mode: 0644]
src/test/dns/powerdns/expected.add_a_record.ipv4 [new file with mode: 0644]
src/test/dns/powerdns/expected.add_a_record.ipv6 [new file with mode: 0644]
src/test/dns/powerdns/expected.add_ptr_record.ipv4 [new file with mode: 0644]
src/test/dns/powerdns/expected.add_ptr_record.ipv6 [new file with mode: 0644]
src/test/dns/powerdns/expected.del_a_multiple_record.ipv4 [new file with mode: 0644]
src/test/dns/powerdns/expected.del_a_multiple_record.ipv6 [new file with mode: 0644]
src/test/dns/powerdns/expected.del_a_record.ipv4 [new file with mode: 0644]
src/test/dns/powerdns/expected.del_a_record.ipv6 [new file with mode: 0644]
src/test/dns/powerdns/expected.del_ptr_record.ipv4 [new file with mode: 0644]
src/test/dns/powerdns/expected.del_ptr_record.ipv6 [new file with mode: 0644]
src/test/dns/powerdns/expected.verify_zone [new file with mode: 0644]
src/test/dns/powerdns/sdn_config [new file with mode: 0644]
src/test/ipams/netbox/expected.add_ip [new file with mode: 0644]
src/test/ipams/netbox/expected.add_ip_notgateway [new file with mode: 0644]
src/test/ipams/netbox/expected.add_next_freeip [new file with mode: 0644]
src/test/ipams/netbox/expected.add_subnet [new file with mode: 0644]
src/test/ipams/netbox/expected.del_ip [new file with mode: 0644]
src/test/ipams/netbox/expected.del_subnet [new file with mode: 0644]
src/test/ipams/netbox/expected.update_ip [new file with mode: 0644]
src/test/ipams/netbox/ipam_config [new file with mode: 0644]
src/test/ipams/netbox/sdn_config [new file with mode: 0644]
src/test/ipams/phpipam/expected.add_ip [new file with mode: 0644]
src/test/ipams/phpipam/expected.add_ip_notgateway [new file with mode: 0644]
src/test/ipams/phpipam/expected.add_next_freeip [new file with mode: 0644]
src/test/ipams/phpipam/expected.add_subnet [new file with mode: 0644]
src/test/ipams/phpipam/expected.del_ip [new file with mode: 0644]
src/test/ipams/phpipam/expected.del_subnet [new file with mode: 0644]
src/test/ipams/phpipam/expected.update_ip [new file with mode: 0644]
src/test/ipams/phpipam/ipam_config [new file with mode: 0644]
src/test/ipams/phpipam/sdn_config [new file with mode: 0644]
src/test/run_test_dns.pl [new file with mode: 0755]
src/test/run_test_ipams.pl [new file with mode: 0755]
src/test/run_test_subnets.pl [new file with mode: 0755]
src/test/run_test_vnets.pl [new file with mode: 0755]
src/test/run_test_zones.pl [new file with mode: 0755]
src/test/subnets/ipv4/ipam_config [new file with mode: 0644]
src/test/subnets/ipv4/sdn_config [new file with mode: 0644]
src/test/subnets/ipv6/ipam_config [new file with mode: 0644]
src/test/subnets/ipv6/sdn_config [new file with mode: 0644]
src/test/subnets/noipam/ipam_config [new file with mode: 0644]
src/test/subnets/noipam/sdn_config [new file with mode: 0644]
src/test/vnets/ipv4/ipam.db [new file with mode: 0644]
src/test/vnets/ipv4/ipam_config [new file with mode: 0644]
src/test/vnets/ipv4/sdn_config [new file with mode: 0644]
src/test/vnets/ipv4noipam/ipam.db [new file with mode: 0644]
src/test/vnets/ipv4noipam/ipam_config [new file with mode: 0644]
src/test/vnets/ipv4noipam/sdn_config [new file with mode: 0644]
src/test/vnets/ipv6/ipam.db [new file with mode: 0644]
src/test/vnets/ipv6/ipam_config [new file with mode: 0644]
src/test/vnets/ipv6/sdn_config [new file with mode: 0644]
src/test/zones/evpn/advertise_subnets/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/advertise_subnets/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/advertise_subnets/interfaces [new file with mode: 0644]
src/test/zones/evpn/advertise_subnets/sdn_config [new file with mode: 0644]
src/test/zones/evpn/disable_arp_nd_suppression/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/disable_arp_nd_suppression/interfaces [new file with mode: 0644]
src/test/zones/evpn/disable_arp_nd_suppression/sdn_config [new file with mode: 0644]
src/test/zones/evpn/ebgp/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/ebgp/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/ebgp/interfaces [new file with mode: 0644]
src/test/zones/evpn/ebgp/sdn_config [new file with mode: 0644]
src/test/zones/evpn/ebgp_loopback/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/ebgp_loopback/interfaces [new file with mode: 0644]
src/test/zones/evpn/ebgp_loopback/sdn_config [new file with mode: 0644]
src/test/zones/evpn/exitnode/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/exitnode/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/exitnode/interfaces [new file with mode: 0644]
src/test/zones/evpn/exitnode/sdn_config [new file with mode: 0644]
src/test/zones/evpn/exitnode_local_routing/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/exitnode_local_routing/interfaces [new file with mode: 0644]
src/test/zones/evpn/exitnode_local_routing/sdn_config [new file with mode: 0644]
src/test/zones/evpn/exitnode_primary/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/exitnode_primary/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/exitnode_primary/interfaces [new file with mode: 0644]
src/test/zones/evpn/exitnode_primary/sdn_config [new file with mode: 0644]
src/test/zones/evpn/exitnode_snat/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/exitnode_snat/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/exitnode_snat/interfaces [new file with mode: 0644]
src/test/zones/evpn/exitnode_snat/sdn_config [new file with mode: 0644]
src/test/zones/evpn/ipv4/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/ipv4/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/ipv4/interfaces [new file with mode: 0644]
src/test/zones/evpn/ipv4/sdn_config [new file with mode: 0644]
src/test/zones/evpn/ipv4ipv6/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/ipv4ipv6/interfaces [new file with mode: 0644]
src/test/zones/evpn/ipv4ipv6/sdn_config [new file with mode: 0644]
src/test/zones/evpn/ipv4ipv6nogateway/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/ipv4ipv6nogateway/interfaces [new file with mode: 0644]
src/test/zones/evpn/ipv4ipv6nogateway/sdn_config [new file with mode: 0644]
src/test/zones/evpn/ipv6/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/ipv6/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/ipv6/interfaces [new file with mode: 0644]
src/test/zones/evpn/ipv6/sdn_config [new file with mode: 0644]
src/test/zones/evpn/multipath_relax/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/multipath_relax/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/multipath_relax/interfaces [new file with mode: 0644]
src/test/zones/evpn/multipath_relax/sdn_config [new file with mode: 0644]
src/test/zones/evpn/rt_import/expected_controller_config [new file with mode: 0644]
src/test/zones/evpn/rt_import/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/evpn/rt_import/interfaces [new file with mode: 0644]
src/test/zones/evpn/rt_import/sdn_config [new file with mode: 0644]
src/test/zones/qinq/bridge/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge/interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge/sdn_config [new file with mode: 0644]
src/test/zones/qinq/bridge_notagvnet/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_notagvnet/interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_notagvnet/sdn_config [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware/interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware/sdn_config [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware_notagvnet/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware_notagvnet/interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware_notagvnet/sdn_config [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware_vlanawarevnet/sdn_config [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware_vlanprotocol/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware_vlanprotocol/interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanaware_vlanprotocol/sdn_config [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanawarevnet/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanawarevnet/interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanawarevnet/sdn_config [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanprotocol/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanprotocol/interfaces [new file with mode: 0644]
src/test/zones/qinq/bridge_vlanprotocol/sdn_config [new file with mode: 0644]
src/test/zones/qinq/ovs/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/ovs/interfaces [new file with mode: 0644]
src/test/zones/qinq/ovs/sdn_config [new file with mode: 0644]
src/test/zones/qinq/ovs_notagvnet/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/ovs_notagvnet/interfaces [new file with mode: 0644]
src/test/zones/qinq/ovs_notagvnet/sdn_config [new file with mode: 0644]
src/test/zones/qinq/ovs_vlanawarevnet/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/ovs_vlanawarevnet/interfaces [new file with mode: 0644]
src/test/zones/qinq/ovs_vlanawarevnet/sdn_config [new file with mode: 0644]
src/test/zones/qinq/ovs_vlanprotocol/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/qinq/ovs_vlanprotocol/interfaces [new file with mode: 0644]
src/test/zones/qinq/ovs_vlanprotocol/sdn_config [new file with mode: 0644]
src/test/zones/simple/basic/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/simple/basic/interfaces [new file with mode: 0644]
src/test/zones/simple/basic/sdn_config [new file with mode: 0644]
src/test/zones/simple/hetzner/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/simple/hetzner/interfaces [new file with mode: 0644]
src/test/zones/simple/hetzner/sdn_config [new file with mode: 0644]
src/test/zones/simple/ipv4/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/simple/ipv4/interfaces [new file with mode: 0644]
src/test/zones/simple/ipv4/sdn_config [new file with mode: 0644]
src/test/zones/simple/ipv4snat/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/simple/ipv4snat/interfaces [new file with mode: 0644]
src/test/zones/simple/ipv4snat/sdn_config [new file with mode: 0644]
src/test/zones/simple/ipv4v6/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/simple/ipv4v6/interfaces [new file with mode: 0644]
src/test/zones/simple/ipv4v6/sdn_config [new file with mode: 0644]
src/test/zones/simple/ipv4v6nogateway/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/simple/ipv4v6nogateway/interfaces [new file with mode: 0644]
src/test/zones/simple/ipv4v6nogateway/sdn_config [new file with mode: 0644]
src/test/zones/simple/ipv6snat/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/simple/ipv6snat/interfaces [new file with mode: 0644]
src/test/zones/simple/ipv6snat/sdn_config [new file with mode: 0644]
src/test/zones/vlan/bridge/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/vlan/bridge/interfaces [new file with mode: 0644]
src/test/zones/vlan/bridge/sdn_config [new file with mode: 0644]
src/test/zones/vlan/bridge_vlanaware/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/vlan/bridge_vlanaware/interfaces [new file with mode: 0644]
src/test/zones/vlan/bridge_vlanaware/sdn_config [new file with mode: 0644]
src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/interfaces [new file with mode: 0644]
src/test/zones/vlan/bridge_vlanaware_vlanawarevnet/sdn_config [new file with mode: 0644]
src/test/zones/vlan/ovs/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/vlan/ovs/interfaces [new file with mode: 0644]
src/test/zones/vlan/ovs/sdn_config [new file with mode: 0644]
src/test/zones/vlan/ovs_vlanware_vnet/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/vlan/ovs_vlanware_vnet/interfaces [new file with mode: 0644]
src/test/zones/vlan/ovs_vlanware_vnet/sdn_config [new file with mode: 0644]
src/test/zones/vxlan/basic/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/vxlan/basic/interfaces [new file with mode: 0644]
src/test/zones/vxlan/basic/sdn_config [new file with mode: 0644]
src/test/zones/vxlan/vlanawarevnet/expected_sdn_interfaces [new file with mode: 0644]
src/test/zones/vxlan/vlanawarevnet/interfaces [new file with mode: 0644]
src/test/zones/vxlan/vlanawarevnet/sdn_config [new file with mode: 0644]
test/Makefile [deleted file]
test/debug/documentation.txt [deleted file]
test/debug/generateconfig.pl [deleted file]
test/debug/statuscheck.pl [deleted file]
test/dns/powerdns/dns_config [deleted file]
test/dns/powerdns/expected.add_a_multiple_record.ipv4 [deleted file]
test/dns/powerdns/expected.add_a_multiple_record.ipv6 [deleted file]
test/dns/powerdns/expected.add_a_record.ipv4 [deleted file]
test/dns/powerdns/expected.add_a_record.ipv6 [deleted file]
test/dns/powerdns/expected.add_ptr_record.ipv4 [deleted file]
test/dns/powerdns/expected.add_ptr_record.ipv6 [deleted file]
test/dns/powerdns/expected.del_a_multiple_record.ipv4 [deleted file]
test/dns/powerdns/expected.del_a_multiple_record.ipv6 [deleted file]
test/dns/powerdns/expected.del_a_record.ipv4 [deleted file]
test/dns/powerdns/expected.del_a_record.ipv6 [deleted file]
test/dns/powerdns/expected.del_ptr_record.ipv4 [deleted file]
test/dns/powerdns/expected.del_ptr_record.ipv6 [deleted file]
test/dns/powerdns/expected.verify_zone [deleted file]
test/dns/powerdns/sdn_config [deleted file]
test/ipams/netbox/expected.add_ip [deleted file]
test/ipams/netbox/expected.add_ip_notgateway [deleted file]
test/ipams/netbox/expected.add_next_freeip [deleted file]
test/ipams/netbox/expected.add_subnet [deleted file]
test/ipams/netbox/expected.del_ip [deleted file]
test/ipams/netbox/expected.del_subnet [deleted file]
test/ipams/netbox/expected.update_ip [deleted file]
test/ipams/netbox/ipam_config [deleted file]
test/ipams/netbox/sdn_config [deleted file]
test/ipams/phpipam/expected.add_ip [deleted file]
test/ipams/phpipam/expected.add_ip_notgateway [deleted file]
test/ipams/phpipam/expected.add_next_freeip [deleted file]
test/ipams/phpipam/expected.add_subnet [deleted file]
test/ipams/phpipam/expected.del_ip [deleted file]
test/ipams/phpipam/expected.del_subnet [deleted file]
test/ipams/phpipam/expected.update_ip [deleted file]
test/ipams/phpipam/ipam_config [deleted file]
test/ipams/phpipam/sdn_config [deleted file]
test/run_test_dns.pl [deleted file]
test/run_test_ipams.pl [deleted file]
test/run_test_subnets.pl [deleted file]
test/run_test_vnets.pl [deleted file]
test/run_test_zones.pl [deleted file]
test/subnets/ipv4/ipam_config [deleted file]
test/subnets/ipv4/sdn_config [deleted file]
test/subnets/ipv6/ipam_config [deleted file]
test/subnets/ipv6/sdn_config [deleted file]
test/subnets/noipam/ipam_config [deleted file]
test/subnets/noipam/sdn_config [deleted file]
test/vnets/ipv4/ipam.db [deleted file]
test/vnets/ipv4/ipam_config [deleted file]
test/vnets/ipv4/sdn_config [deleted file]
test/vnets/ipv4noipam/ipam.db [deleted file]
test/vnets/ipv4noipam/ipam_config [deleted file]
test/vnets/ipv4noipam/sdn_config [deleted file]
test/vnets/ipv6/ipam.db [deleted file]
test/vnets/ipv6/ipam_config [deleted file]
test/vnets/ipv6/sdn_config [deleted file]
test/zones/evpn/advertise_subnets/expected_controller_config [deleted file]
test/zones/evpn/advertise_subnets/expected_sdn_interfaces [deleted file]
test/zones/evpn/advertise_subnets/interfaces [deleted file]
test/zones/evpn/advertise_subnets/sdn_config [deleted file]
test/zones/evpn/disable_arp_nd_suppression/expected_controller_config [deleted file]
test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces [deleted file]
test/zones/evpn/disable_arp_nd_suppression/interfaces [deleted file]
test/zones/evpn/disable_arp_nd_suppression/sdn_config [deleted file]
test/zones/evpn/ebgp/expected_controller_config [deleted file]
test/zones/evpn/ebgp/expected_sdn_interfaces [deleted file]
test/zones/evpn/ebgp/interfaces [deleted file]
test/zones/evpn/ebgp/sdn_config [deleted file]
test/zones/evpn/ebgp_loopback/expected_controller_config [deleted file]
test/zones/evpn/ebgp_loopback/expected_sdn_interfaces [deleted file]
test/zones/evpn/ebgp_loopback/interfaces [deleted file]
test/zones/evpn/ebgp_loopback/sdn_config [deleted file]
test/zones/evpn/exitnode/expected_controller_config [deleted file]
test/zones/evpn/exitnode/expected_sdn_interfaces [deleted file]
test/zones/evpn/exitnode/interfaces [deleted file]
test/zones/evpn/exitnode/sdn_config [deleted file]
test/zones/evpn/exitnode_local_routing/expected_controller_config [deleted file]
test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces [deleted file]
test/zones/evpn/exitnode_local_routing/interfaces [deleted file]
test/zones/evpn/exitnode_local_routing/sdn_config [deleted file]
test/zones/evpn/exitnode_primary/expected_controller_config [deleted file]
test/zones/evpn/exitnode_primary/expected_sdn_interfaces [deleted file]
test/zones/evpn/exitnode_primary/interfaces [deleted file]
test/zones/evpn/exitnode_primary/sdn_config [deleted file]
test/zones/evpn/exitnode_snat/expected_controller_config [deleted file]
test/zones/evpn/exitnode_snat/expected_sdn_interfaces [deleted file]
test/zones/evpn/exitnode_snat/interfaces [deleted file]
test/zones/evpn/exitnode_snat/sdn_config [deleted file]
test/zones/evpn/ipv4/expected_controller_config [deleted file]
test/zones/evpn/ipv4/expected_sdn_interfaces [deleted file]
test/zones/evpn/ipv4/interfaces [deleted file]
test/zones/evpn/ipv4/sdn_config [deleted file]
test/zones/evpn/ipv4ipv6/expected_controller_config [deleted file]
test/zones/evpn/ipv4ipv6/expected_sdn_interfaces [deleted file]
test/zones/evpn/ipv4ipv6/interfaces [deleted file]
test/zones/evpn/ipv4ipv6/sdn_config [deleted file]
test/zones/evpn/ipv4ipv6nogateway/expected_controller_config [deleted file]
test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces [deleted file]
test/zones/evpn/ipv4ipv6nogateway/interfaces [deleted file]
test/zones/evpn/ipv4ipv6nogateway/sdn_config [deleted file]
test/zones/evpn/ipv6/expected_controller_config [deleted file]
test/zones/evpn/ipv6/expected_sdn_interfaces [deleted file]
test/zones/evpn/ipv6/interfaces [deleted file]
test/zones/evpn/ipv6/sdn_config [deleted file]
test/zones/evpn/multipath_relax/expected_controller_config [deleted file]
test/zones/evpn/multipath_relax/expected_sdn_interfaces [deleted file]
test/zones/evpn/multipath_relax/interfaces [deleted file]
test/zones/evpn/multipath_relax/sdn_config [deleted file]
test/zones/evpn/rt_import/expected_controller_config [deleted file]
test/zones/evpn/rt_import/expected_sdn_interfaces [deleted file]
test/zones/evpn/rt_import/interfaces [deleted file]
test/zones/evpn/rt_import/sdn_config [deleted file]
test/zones/qinq/bridge/expected_sdn_interfaces [deleted file]
test/zones/qinq/bridge/interfaces [deleted file]
test/zones/qinq/bridge/sdn_config [deleted file]
test/zones/qinq/bridge_notagvnet/expected_sdn_interfaces [deleted file]
test/zones/qinq/bridge_notagvnet/interfaces [deleted file]
test/zones/qinq/bridge_notagvnet/sdn_config [deleted file]
test/zones/qinq/bridge_vlanaware/expected_sdn_interfaces [deleted file]
test/zones/qinq/bridge_vlanaware/interfaces [deleted file]
test/zones/qinq/bridge_vlanaware/sdn_config [deleted file]
test/zones/qinq/bridge_vlanaware_notagvnet/expected_sdn_interfaces [deleted file]
test/zones/qinq/bridge_vlanaware_notagvnet/interfaces [deleted file]
test/zones/qinq/bridge_vlanaware_notagvnet/sdn_config [deleted file]
test/zones/qinq/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces [deleted file]
test/zones/qinq/bridge_vlanaware_vlanawarevnet/interfaces [deleted file]
test/zones/qinq/bridge_vlanaware_vlanawarevnet/sdn_config [deleted file]
test/zones/qinq/bridge_vlanaware_vlanprotocol/expected_sdn_interfaces [deleted file]
test/zones/qinq/bridge_vlanaware_vlanprotocol/interfaces [deleted file]
test/zones/qinq/bridge_vlanaware_vlanprotocol/sdn_config [deleted file]
test/zones/qinq/bridge_vlanawarevnet/expected_sdn_interfaces [deleted file]
test/zones/qinq/bridge_vlanawarevnet/interfaces [deleted file]
test/zones/qinq/bridge_vlanawarevnet/sdn_config [deleted file]
test/zones/qinq/bridge_vlanprotocol/expected_sdn_interfaces [deleted file]
test/zones/qinq/bridge_vlanprotocol/interfaces [deleted file]
test/zones/qinq/bridge_vlanprotocol/sdn_config [deleted file]
test/zones/qinq/ovs/expected_sdn_interfaces [deleted file]
test/zones/qinq/ovs/interfaces [deleted file]
test/zones/qinq/ovs/sdn_config [deleted file]
test/zones/qinq/ovs_notagvnet/expected_sdn_interfaces [deleted file]
test/zones/qinq/ovs_notagvnet/interfaces [deleted file]
test/zones/qinq/ovs_notagvnet/sdn_config [deleted file]
test/zones/qinq/ovs_vlanawarevnet/expected_sdn_interfaces [deleted file]
test/zones/qinq/ovs_vlanawarevnet/interfaces [deleted file]
test/zones/qinq/ovs_vlanawarevnet/sdn_config [deleted file]
test/zones/qinq/ovs_vlanprotocol/expected_sdn_interfaces [deleted file]
test/zones/qinq/ovs_vlanprotocol/interfaces [deleted file]
test/zones/qinq/ovs_vlanprotocol/sdn_config [deleted file]
test/zones/simple/basic/expected_sdn_interfaces [deleted file]
test/zones/simple/basic/interfaces [deleted file]
test/zones/simple/basic/sdn_config [deleted file]
test/zones/simple/hetzner/expected_sdn_interfaces [deleted file]
test/zones/simple/hetzner/interfaces [deleted file]
test/zones/simple/hetzner/sdn_config [deleted file]
test/zones/simple/ipv4/expected_sdn_interfaces [deleted file]
test/zones/simple/ipv4/interfaces [deleted file]
test/zones/simple/ipv4/sdn_config [deleted file]
test/zones/simple/ipv4snat/expected_sdn_interfaces [deleted file]
test/zones/simple/ipv4snat/interfaces [deleted file]
test/zones/simple/ipv4snat/sdn_config [deleted file]
test/zones/simple/ipv4v6/expected_sdn_interfaces [deleted file]
test/zones/simple/ipv4v6/interfaces [deleted file]
test/zones/simple/ipv4v6/sdn_config [deleted file]
test/zones/simple/ipv4v6nogateway/expected_sdn_interfaces [deleted file]
test/zones/simple/ipv4v6nogateway/interfaces [deleted file]
test/zones/simple/ipv4v6nogateway/sdn_config [deleted file]
test/zones/simple/ipv6snat/expected_sdn_interfaces [deleted file]
test/zones/simple/ipv6snat/interfaces [deleted file]
test/zones/simple/ipv6snat/sdn_config [deleted file]
test/zones/vlan/bridge/expected_sdn_interfaces [deleted file]
test/zones/vlan/bridge/interfaces [deleted file]
test/zones/vlan/bridge/sdn_config [deleted file]
test/zones/vlan/bridge_vlanaware/expected_sdn_interfaces [deleted file]
test/zones/vlan/bridge_vlanaware/interfaces [deleted file]
test/zones/vlan/bridge_vlanaware/sdn_config [deleted file]
test/zones/vlan/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces [deleted file]
test/zones/vlan/bridge_vlanaware_vlanawarevnet/interfaces [deleted file]
test/zones/vlan/bridge_vlanaware_vlanawarevnet/sdn_config [deleted file]
test/zones/vlan/ovs/expected_sdn_interfaces [deleted file]
test/zones/vlan/ovs/interfaces [deleted file]
test/zones/vlan/ovs/sdn_config [deleted file]
test/zones/vlan/ovs_vlanware_vnet/expected_sdn_interfaces [deleted file]
test/zones/vlan/ovs_vlanware_vnet/interfaces [deleted file]
test/zones/vlan/ovs_vlanware_vnet/sdn_config [deleted file]
test/zones/vxlan/basic/expected_sdn_interfaces [deleted file]
test/zones/vxlan/basic/interfaces [deleted file]
test/zones/vxlan/basic/sdn_config [deleted file]
test/zones/vxlan/vlanawarevnet/expected_sdn_interfaces [deleted file]
test/zones/vxlan/vlanawarevnet/interfaces [deleted file]
test/zones/vxlan/vlanawarevnet/sdn_config [deleted file]

index b4980de2d3416ed7e427ca97d60e969199afc556..d381be7eb937f1a370e1c7aef797b75857633ae7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -7,17 +7,16 @@ BUILDDIR ?= $(PACKAGE)-$(DEB_VERSION_UPSTREAM)
 DEB=$(PACKAGE)_$(DEB_VERSION_UPSTREAM_REVISION)_all.deb
 DSC=$(PACKAGE)_$(DEB_VERSION_UPSTREAM_REVISION).dsc
 
-all:
-       $(MAKE) -C PVE
-
 .PHONY: dinstall
 dinstall: deb
        dpkg -i $(DEB)
 
-$(BUILDDIR): PVE debian
-       rm -rf $(BUILDDIR)
-       rsync -a * $(BUILDDIR)
-       echo "git clone git://git.proxmox.com/git/pve-network.git\\ngit checkout $(shell git rev-parse HEAD)" > $(BUILDDIR)/debian/SOURCE
+$(BUILDDIR): src debian
+       rm -rf $@ $@.tmp
+       cp -a src $@.tmp
+       cp -a debian $@.tmp/
+       echo "git clone git://git.proxmox.com/git/pve-network.git\\ngit checkout $(shell git rev-parse HEAD)" > $@.tmp/debian/SOURCE
+       mv $@.tmp $@
 
 .PHONY: deb
 deb: $(DEB)
@@ -41,14 +40,6 @@ distclean: clean
 clean:
        rm -rf *~ *.deb *.changes $(PACKAGE)-[0-9]*/ $(PACKAGE)*.tar* *.build *.buildinfo *.dsc
 
-.PHONY: test
-test:
-       $(MAKE) -C test
-
-.PHONY: install
-install:
-       $(MAKE) -C PVE install
-
 .PHONY: upload
 upload: $(DEB)
        tar cf - $(DEB)|ssh -X repoman@repo.proxmox.com -- upload --product pve --dist bullseye
diff --git a/PVE/API2/Makefile b/PVE/API2/Makefile
deleted file mode 100644 (file)
index 28b2830..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-
-.PHONY: install
-install:
-       make -C Network install
diff --git a/PVE/API2/Network/Makefile b/PVE/API2/Network/Makefile
deleted file mode 100644 (file)
index 396f79d..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-SOURCES=SDN.pm
-
-
-PERL5DIR=${DESTDIR}/usr/share/perl5
-
-.PHONY: install
-install:
-       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/API2/Network/$$i; done
-       make -C SDN install
diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
deleted file mode 100644 (file)
index f129d60..0000000
+++ /dev/null
@@ -1,144 +0,0 @@
-package PVE::API2::Network::SDN;
-
-use strict;
-use warnings;
-
-use PVE::Cluster qw(cfs_lock_file cfs_read_file cfs_write_file);
-use PVE::Exception qw(raise_param_exc);
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::RESTHandler;
-use PVE::RPCEnvironment;
-use PVE::SafeSyslog;
-use PVE::Tools qw(run_command);
-use PVE::Network::SDN;
-
-use PVE::API2::Network::SDN::Controllers;
-use PVE::API2::Network::SDN::Vnets;
-use PVE::API2::Network::SDN::Zones;
-use PVE::API2::Network::SDN::Ipams;
-use PVE::API2::Network::SDN::Dns;
-
-use base qw(PVE::RESTHandler);
-
-__PACKAGE__->register_method ({
-    subclass => "PVE::API2::Network::SDN::Vnets",
-    path => 'vnets',
-});
-
-__PACKAGE__->register_method ({
-    subclass => "PVE::API2::Network::SDN::Zones",
-    path => 'zones',
-});
-
-__PACKAGE__->register_method ({
-    subclass => "PVE::API2::Network::SDN::Controllers",
-    path => 'controllers',
-});
-
-__PACKAGE__->register_method ({
-    subclass => "PVE::API2::Network::SDN::Ipams",
-    path => 'ipams',
-});
-
-__PACKAGE__->register_method ({
-    subclass => "PVE::API2::Network::SDN::Dns",
-    path => 'dns',
-});
-
-__PACKAGE__->register_method({
-    name => 'index',
-    path => '',
-    method => 'GET',
-    description => "Directory index.",
-    permissions => {
-       check => ['perm', '/', [ 'SDN.Audit' ]],
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {},
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => {
-               id => { type => 'string' },
-           },
-       },
-       links => [ { rel => 'child', href => "{id}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-
-       my $res = [
-           { id => 'vnets' },
-           { id => 'zones' },
-           { id => 'controllers' },
-           { id => 'ipams' },
-           { id => 'dns' },
-       ];
-
-       return $res;
-    }});
-
-my $create_reload_network_worker = sub {
-    my ($nodename) = @_;
-
-    # FIXME: how to proxy to final node ?
-    my $upid;
-    run_command(['pvesh', 'set', "/nodes/$nodename/network"], outfunc => sub {
-       my $line = shift;
-       if ($line =~ /^["']?(UPID:[^\s"']+)["']?$/) {
-           $upid = $1;
-       }
-    });
-    #my $upid = PVE::API2::Network->reload_network_config(node => $nodename});
-    my $res = PVE::Tools::upid_decode($upid);
-
-    return $res->{pid};
-};
-
-__PACKAGE__->register_method ({
-    name => 'reload',
-    protected => 1,
-    path => '',
-    method => 'PUT',
-    description => "Apply sdn controller changes && reload.",
-    permissions => {
-       check => ['perm', '/sdn', ['SDN.Allocate']],
-    },
-    parameters => {
-        additionalProperties => 0,
-    },
-    returns => {
-        type => 'string',
-    },
-    code => sub {
-        my ($param) = @_;
-
-        my $rpcenv = PVE::RPCEnvironment::get();
-        my $authuser = $rpcenv->get_user();
-
-       PVE::Network::SDN::commit_config();
-
-        my $code = sub {
-            $rpcenv->{type} = 'priv'; # to start tasks in background
-           PVE::Cluster::check_cfs_quorum();
-           my $nodelist = PVE::Cluster::get_nodelist();
-           for my $node (@$nodelist) {
-               my $pid = eval { $create_reload_network_worker->($node) };
-               warn $@ if $@;
-           }
-
-           # FIXME: use libpve-apiclient (like in cluster join) to create
-           # tasks and moitor the tasks.
-
-           return;
-        };
-
-        return $rpcenv->fork_worker('reloadnetworkall', undef, $authuser, $code);
-
-    }});
-
-
-1;
diff --git a/PVE/API2/Network/SDN/Controllers.pm b/PVE/API2/Network/SDN/Controllers.pm
deleted file mode 100644 (file)
index d8f18ab..0000000
+++ /dev/null
@@ -1,290 +0,0 @@
-package PVE::API2::Network::SDN::Controllers;
-
-use strict;
-use warnings;
-
-use PVE::SafeSyslog;
-use PVE::Tools qw(extract_param);
-use PVE::Cluster qw(cfs_read_file cfs_write_file);
-use PVE::Network::SDN;
-use PVE::Network::SDN::Zones;
-use PVE::Network::SDN::Controllers;
-use PVE::Network::SDN::Controllers::Plugin;
-use PVE::Network::SDN::Controllers::EvpnPlugin;
-use PVE::Network::SDN::Controllers::BgpPlugin;
-use PVE::Network::SDN::Controllers::FaucetPlugin;
-
-use Storable qw(dclone);
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::RPCEnvironment;
-
-use PVE::RESTHandler;
-
-use base qw(PVE::RESTHandler);
-
-my $sdn_controllers_type_enum = PVE::Network::SDN::Controllers::Plugin->lookup_types();
-
-my $api_sdn_controllers_config = sub {
-    my ($cfg, $id) = @_;
-
-    my $scfg = dclone(PVE::Network::SDN::Controllers::sdn_controllers_config($cfg, $id));
-    $scfg->{controller} = $id;
-    $scfg->{digest} = $cfg->{digest};
-
-    return $scfg;
-};
-
-__PACKAGE__->register_method ({
-    name => 'index',
-    path => '',
-    method => 'GET',
-    description => "SDN controllers index.",
-    permissions => {
-       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/controllers/<controller>'",
-       user => 'all',
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           type => {
-               description => "Only list sdn controllers of specific type",
-               type => 'string',
-               enum => $sdn_controllers_type_enum,
-               optional => 1,
-           },
-           running => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display running config.",
-           },
-           pending => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display pending config.",
-           },
-       },
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => { controller => { type => 'string' },
-                           type => { type => 'string' },
-                           state => { type => 'string', optional => 1 },
-                            pending => { optional => 1},
-           },
-       },
-       links => [ { rel => 'child', href => "{controller}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-
-       my $rpcenv = PVE::RPCEnvironment::get();
-       my $authuser = $rpcenv->get_user();
-
-        my $cfg = {};
-        if($param->{pending}) {
-            my $running_cfg = PVE::Network::SDN::running_config();
-            my $config = PVE::Network::SDN::Controllers::config();
-            $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'controllers');
-        } elsif ($param->{running}) {
-            my $running_cfg = PVE::Network::SDN::running_config();
-            $cfg = $running_cfg->{controllers};
-        } else {
-            $cfg = PVE::Network::SDN::Controllers::config();
-        }
-
-       my @sids = PVE::Network::SDN::Controllers::sdn_controllers_ids($cfg);
-       my $res = [];
-       foreach my $id (@sids) {
-           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
-           next if !$rpcenv->check_any($authuser, "/sdn/controllers/$id", $privs, 1);
-
-           my $scfg = &$api_sdn_controllers_config($cfg, $id);
-           next if $param->{type} && $param->{type} ne $scfg->{type};
-
-           my $plugin_config = $cfg->{ids}->{$id};
-           my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
-           push @$res, $scfg;
-       }
-
-       return $res;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'read',
-    path => '{controller}',
-    method => 'GET',
-    description => "Read sdn controller configuration.",
-    permissions => {
-       check => ['perm', '/sdn/controllers/{controller}', ['SDN.Allocate']],
-   },
-
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           controller => get_standard_option('pve-sdn-controller-id'),
-           running => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display running config.",
-           },
-           pending => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display pending config.",
-           },
-       },
-    },
-    returns => { type => 'object' },
-    code => sub {
-       my ($param) = @_;
-
-        my $cfg = {};
-        if($param->{pending}) {
-            my $running_cfg = PVE::Network::SDN::running_config();
-            my $config = PVE::Network::SDN::Controllers::config();
-            $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'controllers');
-        } elsif ($param->{running}) {
-            my $running_cfg = PVE::Network::SDN::running_config();
-            $cfg = $running_cfg->{controllers};
-        } else {
-            $cfg = PVE::Network::SDN::Controllers::config();
-        }
-
-       return &$api_sdn_controllers_config($cfg, $param->{controller});
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'create',
-    protected => 1,
-    path => '',
-    method => 'POST',
-    description => "Create a new sdn controller object.",
-    permissions => {
-       check => ['perm', '/sdn/controllers', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::Controllers::Plugin->createSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $type = extract_param($param, 'type');
-       my $id = extract_param($param, 'controller');
-
-       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($type);
-       my $opts = $plugin->check_config($id, $param, 1, 1);
-
-        # create /etc/pve/sdn directory
-        PVE::Cluster::check_cfs_quorum();
-        mkdir("/etc/pve/sdn");
-
-        PVE::Network::SDN::lock_sdn_config(
-           sub {
-
-               my $controller_cfg = PVE::Network::SDN::Controllers::config();
-
-               my $scfg = undef;
-               if ($scfg = PVE::Network::SDN::Controllers::sdn_controllers_config($controller_cfg, $id, 1)) {
-                   die "sdn controller object ID '$id' already defined\n";
-               }
-
-               $controller_cfg->{ids}->{$id} = $opts;
-               $plugin->on_update_hook($id, $controller_cfg);
-
-               PVE::Network::SDN::Controllers::write_config($controller_cfg);
-
-           }, "create sdn controller object failed");
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'update',
-    protected => 1,
-    path => '{controller}',
-    method => 'PUT',
-    description => "Update sdn controller object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/controllers', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::Controllers::Plugin->updateSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'controller');
-       my $digest = extract_param($param, 'digest');
-
-        PVE::Network::SDN::lock_sdn_config(
-        sub {
-
-           my $controller_cfg = PVE::Network::SDN::Controllers::config();
-
-           PVE::SectionConfig::assert_if_modified($controller_cfg, $digest);
-
-           my $scfg = PVE::Network::SDN::Controllers::sdn_controllers_config($controller_cfg, $id);
-
-           my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($scfg->{type});
-           my $opts = $plugin->check_config($id, $param, 0, 1);
-
-           foreach my $k (%$opts) {
-               $scfg->{$k} = $opts->{$k};
-           }
-
-           $plugin->on_update_hook($id, $controller_cfg);
-
-           PVE::Network::SDN::Controllers::write_config($controller_cfg);
-
-
-           }, "update sdn controller object failed");
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'delete',
-    protected => 1,
-    path => '{controller}',
-    method => 'DELETE',
-    description => "Delete sdn controller object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/controllers', ['SDN.Allocate']],
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           controller => get_standard_option('pve-sdn-controller-id', {
-                completion => \&PVE::Network::SDN::Controllers::complete_sdn_controllers,
-            }),
-       },
-    },
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'controller');
-
-        PVE::Network::SDN::lock_sdn_config(
-           sub {
-
-               my $cfg = PVE::Network::SDN::Controllers::config();
-
-               my $scfg = PVE::Network::SDN::Controllers::sdn_controllers_config($cfg, $id);
-
-               my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($scfg->{type});
-
-               my $zone_cfg = PVE::Network::SDN::Zones::config();
-
-               $plugin->on_delete_hook($id, $zone_cfg);
-
-               delete $cfg->{ids}->{$id};
-               PVE::Network::SDN::Controllers::write_config($cfg);
-
-           }, "delete sdn controller object failed");
-
-
-       return undef;
-    }});
-
-1;
diff --git a/PVE/API2/Network/SDN/Dns.pm b/PVE/API2/Network/SDN/Dns.pm
deleted file mode 100644 (file)
index 3d08552..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-package PVE::API2::Network::SDN::Dns;
-
-use strict;
-use warnings;
-
-use PVE::SafeSyslog;
-use PVE::Tools qw(extract_param);
-use PVE::Cluster qw(cfs_read_file cfs_write_file);
-use PVE::Network::SDN;
-use PVE::Network::SDN::Dns;
-use PVE::Network::SDN::Dns::Plugin;
-use PVE::Network::SDN::Dns::PowerdnsPlugin;
-
-use Storable qw(dclone);
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::RPCEnvironment;
-
-use PVE::RESTHandler;
-
-use base qw(PVE::RESTHandler);
-
-my $sdn_dns_type_enum = PVE::Network::SDN::Dns::Plugin->lookup_types();
-
-my $api_sdn_dns_config = sub {
-    my ($cfg, $id) = @_;
-
-    my $scfg = dclone(PVE::Network::SDN::Dns::sdn_dns_config($cfg, $id));
-    $scfg->{dns} = $id;
-    $scfg->{digest} = $cfg->{digest};
-
-    return $scfg;
-};
-
-__PACKAGE__->register_method ({
-    name => 'index',
-    path => '',
-    method => 'GET',
-    description => "SDN dns index.",
-    permissions => {
-       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/dns/<dns>'",
-       user => 'all',
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           type => {
-               description => "Only list sdn dns of specific type",
-               type => 'string',
-               enum => $sdn_dns_type_enum,
-               optional => 1,
-           },
-       },
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => { dns => { type => 'string'},
-                           type => { type => 'string'},
-                         },
-       },
-       links => [ { rel => 'child', href => "{dns}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-
-       my $rpcenv = PVE::RPCEnvironment::get();
-       my $authuser = $rpcenv->get_user();
-
-
-       my $cfg = PVE::Network::SDN::Dns::config();
-
-       my @sids = PVE::Network::SDN::Dns::sdn_dns_ids($cfg);
-       my $res = [];
-       foreach my $id (@sids) {
-           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
-           next if !$rpcenv->check_any($authuser, "/sdn/dns/$id", $privs, 1);
-
-           my $scfg = &$api_sdn_dns_config($cfg, $id);
-           next if $param->{type} && $param->{type} ne $scfg->{type};
-
-           my $plugin_config = $cfg->{ids}->{$id};
-           my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
-           push @$res, $scfg;
-       }
-
-       return $res;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'read',
-    path => '{dns}',
-    method => 'GET',
-    description => "Read sdn dns configuration.",
-    permissions => {
-       check => ['perm', '/sdn/dns/{dns}', ['SDN.Allocate']],
-   },
-
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           dns => get_standard_option('pve-sdn-dns-id'),
-       },
-    },
-    returns => { type => 'object' },
-    code => sub {
-       my ($param) = @_;
-
-       my $cfg = PVE::Network::SDN::Dns::config();
-
-       return &$api_sdn_dns_config($cfg, $param->{dns});
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'create',
-    protected => 1,
-    path => '',
-    method => 'POST',
-    description => "Create a new sdn dns object.",
-    permissions => {
-       check => ['perm', '/sdn/dns', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::Dns::Plugin->createSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $type = extract_param($param, 'type');
-       my $id = extract_param($param, 'dns');
-
-       my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($type);
-       my $opts = $plugin->check_config($id, $param, 1, 1);
-
-        # create /etc/pve/sdn directory
-        PVE::Cluster::check_cfs_quorum();
-        mkdir("/etc/pve/sdn");
-
-        PVE::Network::SDN::lock_sdn_config(
-           sub {
-
-               my $dns_cfg = PVE::Network::SDN::Dns::config();
-
-               my $scfg = undef;
-               if ($scfg = PVE::Network::SDN::Dns::sdn_dns_config($dns_cfg, $id, 1)) {
-                   die "sdn dns object ID '$id' already defined\n";
-               }
-
-               $dns_cfg->{ids}->{$id} = $opts;
-
-               my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($opts->{type});
-               $plugin->on_update_hook($opts);
-
-               PVE::Network::SDN::Dns::write_config($dns_cfg);
-
-           }, "create sdn dns object failed");
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'update',
-    protected => 1,
-    path => '{dns}',
-    method => 'PUT',
-    description => "Update sdn dns object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/dns', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::Dns::Plugin->updateSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'dns');
-       my $digest = extract_param($param, 'digest');
-
-        PVE::Network::SDN::lock_sdn_config(
-        sub {
-
-           my $dns_cfg = PVE::Network::SDN::Dns::config();
-
-           PVE::SectionConfig::assert_if_modified($dns_cfg, $digest);
-
-           my $scfg = PVE::Network::SDN::Dns::sdn_dns_config($dns_cfg, $id);
-
-           my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
-           my $opts = $plugin->check_config($id, $param, 0, 1);
-
-           foreach my $k (%$opts) {
-               $scfg->{$k} = $opts->{$k};
-           }
-
-           $plugin->on_update_hook($scfg);
-
-           PVE::Network::SDN::Dns::write_config($dns_cfg);
-
-           }, "update sdn dns object failed");
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'delete',
-    protected => 1,
-    path => '{dns}',
-    method => 'DELETE',
-    description => "Delete sdn dns object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/dns', ['SDN.Allocate']],
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           dns => get_standard_option('pve-sdn-dns-id', {
-                completion => \&PVE::Network::SDN::Dns::complete_sdn_dns,
-            }),
-       },
-    },
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'dns');
-
-        PVE::Network::SDN::lock_sdn_config(
-           sub {
-
-               my $cfg = PVE::Network::SDN::Dns::config();
-
-               my $scfg = PVE::Network::SDN::Dns::sdn_dns_config($cfg, $id);
-
-               my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
-
-               delete $cfg->{ids}->{$id};
-               PVE::Network::SDN::Dns::write_config($cfg);
-
-           }, "delete sdn dns object failed");
-
-       return undef;
-    }});
-
-1;
diff --git a/PVE/API2/Network/SDN/Ipams.pm b/PVE/API2/Network/SDN/Ipams.pm
deleted file mode 100644 (file)
index 6410e8e..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-package PVE::API2::Network::SDN::Ipams;
-
-use strict;
-use warnings;
-
-use PVE::SafeSyslog;
-use PVE::Tools qw(extract_param);
-use PVE::Cluster qw(cfs_read_file cfs_write_file);
-use PVE::Network::SDN;
-use PVE::Network::SDN::Ipams;
-use PVE::Network::SDN::Ipams::Plugin;
-use PVE::Network::SDN::Ipams::PVEPlugin;
-use PVE::Network::SDN::Ipams::PhpIpamPlugin;
-use PVE::Network::SDN::Ipams::NetboxPlugin;
-
-use Storable qw(dclone);
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::RPCEnvironment;
-
-use PVE::RESTHandler;
-
-use base qw(PVE::RESTHandler);
-
-my $sdn_ipams_type_enum = PVE::Network::SDN::Ipams::Plugin->lookup_types();
-
-my $api_sdn_ipams_config = sub {
-    my ($cfg, $id) = @_;
-
-    my $scfg = dclone(PVE::Network::SDN::Ipams::sdn_ipams_config($cfg, $id));
-    $scfg->{ipam} = $id;
-    $scfg->{digest} = $cfg->{digest};
-
-    return $scfg;
-};
-
-__PACKAGE__->register_method ({
-    name => 'index',
-    path => '',
-    method => 'GET',
-    description => "SDN ipams index.",
-    permissions => {
-       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/ipams/<ipam>'",
-       user => 'all',
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           type => {
-               description => "Only list sdn ipams of specific type",
-               type => 'string',
-               enum => $sdn_ipams_type_enum,
-               optional => 1,
-           },
-       },
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => { ipam => { type => 'string'},
-                           type => { type => 'string'},
-                         },
-       },
-       links => [ { rel => 'child', href => "{ipam}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-
-       my $rpcenv = PVE::RPCEnvironment::get();
-       my $authuser = $rpcenv->get_user();
-
-
-       my $cfg = PVE::Network::SDN::Ipams::config();
-
-       my @sids = PVE::Network::SDN::Ipams::sdn_ipams_ids($cfg);
-       my $res = [];
-       foreach my $id (@sids) {
-           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
-           next if !$rpcenv->check_any($authuser, "/sdn/ipams/$id", $privs, 1);
-
-           my $scfg = &$api_sdn_ipams_config($cfg, $id);
-           next if $param->{type} && $param->{type} ne $scfg->{type};
-
-           my $plugin_config = $cfg->{ids}->{$id};
-           my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-           push @$res, $scfg;
-       }
-
-       return $res;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'read',
-    path => '{ipam}',
-    method => 'GET',
-    description => "Read sdn ipam configuration.",
-    permissions => {
-       check => ['perm', '/sdn/ipams/{ipam}', ['SDN.Allocate']],
-   },
-
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           ipam => get_standard_option('pve-sdn-ipam-id'),
-       },
-    },
-    returns => { type => 'object' },
-    code => sub {
-       my ($param) = @_;
-
-       my $cfg = PVE::Network::SDN::Ipams::config();
-
-       return &$api_sdn_ipams_config($cfg, $param->{ipam});
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'create',
-    protected => 1,
-    path => '',
-    method => 'POST',
-    description => "Create a new sdn ipam object.",
-    permissions => {
-       check => ['perm', '/sdn/ipams', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::Ipams::Plugin->createSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $type = extract_param($param, 'type');
-       my $id = extract_param($param, 'ipam');
-
-       my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($type);
-       my $opts = $plugin->check_config($id, $param, 1, 1);
-
-        # create /etc/pve/sdn directory
-        PVE::Cluster::check_cfs_quorum();
-        mkdir("/etc/pve/sdn");
-
-        PVE::Network::SDN::lock_sdn_config(
-           sub {
-
-               my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-               my $controller_cfg = PVE::Network::SDN::Controllers::config();
-
-               my $scfg = undef;
-               if ($scfg = PVE::Network::SDN::Ipams::sdn_ipams_config($ipam_cfg, $id, 1)) {
-                   die "sdn ipam object ID '$id' already defined\n";
-               }
-
-               $ipam_cfg->{ids}->{$id} = $opts;
-
-               my $plugin_config = $opts;
-               my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-               $plugin->on_update_hook($plugin_config);
-
-               PVE::Network::SDN::Ipams::write_config($ipam_cfg);
-
-           }, "create sdn ipam object failed");
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'update',
-    protected => 1,
-    path => '{ipam}',
-    method => 'PUT',
-    description => "Update sdn ipam object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/ipams', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::Ipams::Plugin->updateSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'ipam');
-       my $digest = extract_param($param, 'digest');
-
-        PVE::Network::SDN::lock_sdn_config(
-        sub {
-
-           my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-
-           PVE::SectionConfig::assert_if_modified($ipam_cfg, $digest);
-
-           my $scfg = PVE::Network::SDN::Ipams::sdn_ipams_config($ipam_cfg, $id);
-
-           my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($scfg->{type});
-           my $opts = $plugin->check_config($id, $param, 0, 1);
-
-           foreach my $k (%$opts) {
-               $scfg->{$k} = $opts->{$k};
-           }
-
-            $plugin->on_update_hook($scfg);
-
-           PVE::Network::SDN::Ipams::write_config($ipam_cfg);
-
-           }, "update sdn ipam object failed");
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'delete',
-    protected => 1,
-    path => '{ipam}',
-    method => 'DELETE',
-    description => "Delete sdn ipam object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/ipams', ['SDN.Allocate']],
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           ipam => get_standard_option('pve-sdn-ipam-id', {
-                completion => \&PVE::Network::SDN::Ipams::complete_sdn_ipams,
-            }),
-       },
-    },
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'ipam');
-
-        PVE::Network::SDN::lock_sdn_config(
-           sub {
-
-               my $cfg = PVE::Network::SDN::Ipams::config();
-
-               my $scfg = PVE::Network::SDN::Ipams::sdn_ipams_config($cfg, $id);
-
-               my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($scfg->{type});
-
-               my $vnet_cfg = PVE::Network::SDN::Vnets::config();
-
-               delete $cfg->{ids}->{$id};
-               PVE::Network::SDN::Ipams::write_config($cfg);
-
-           }, "delete sdn zone object failed");
-
-       return undef;
-    }});
-
-1;
diff --git a/PVE/API2/Network/SDN/Makefile b/PVE/API2/Network/SDN/Makefile
deleted file mode 100644 (file)
index 3683fa4..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm
-
-
-PERL5DIR=${DESTDIR}/usr/share/perl5
-
-.PHONY: install
-install:
-       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/API2/Network/SDN/$$i; done
-       make -C Zones install
-
diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
deleted file mode 100644 (file)
index 377a568..0000000
+++ /dev/null
@@ -1,303 +0,0 @@
-package PVE::API2::Network::SDN::Subnets;
-
-use strict;
-use warnings;
-
-use PVE::SafeSyslog;
-use PVE::Tools qw(extract_param);
-use PVE::Cluster qw(cfs_read_file cfs_write_file);
-use PVE::Exception qw(raise raise_param_exc);
-use PVE::Network::SDN;
-use PVE::Network::SDN::Subnets;
-use PVE::Network::SDN::SubnetPlugin;
-use PVE::Network::SDN::Vnets;
-use PVE::Network::SDN::Zones;
-use PVE::Network::SDN::Ipams;
-use PVE::Network::SDN::Ipams::Plugin;
-
-use Storable qw(dclone);
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::RPCEnvironment;
-
-use PVE::RESTHandler;
-
-use base qw(PVE::RESTHandler);
-
-my $api_sdn_subnets_config = sub {
-    my ($cfg, $id) = @_;
-
-    my $scfg = dclone(PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id));
-    $scfg->{subnet} = $id;
-    $scfg->{digest} = $cfg->{digest};
-
-    return $scfg;
-};
-
-__PACKAGE__->register_method ({
-    name => 'index',
-    path => '',
-    method => 'GET',
-    description => "SDN subnets index.",
-    permissions => {
-       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/subnets/<subnet>'",
-       user => 'all',
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           vnet => get_standard_option('pve-sdn-vnet-id'),
-           running => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display running config.",
-           },
-           pending => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display pending config.",
-           },
-        },
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => {},
-       },
-       links => [ { rel => 'child', href => "{subnet}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-
-       my $rpcenv = PVE::RPCEnvironment::get();
-       my $authuser = $rpcenv->get_user();
-
-        my $vnetid = $param->{vnet};
-
-        my $cfg = {};
-        if($param->{pending}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           my $config = PVE::Network::SDN::Subnets::config();
-           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'subnets');
-        } elsif ($param->{running}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           $cfg = $running_cfg->{subnets};
-        } else {
-           $cfg = PVE::Network::SDN::Subnets::config();
-        }
-
-       my @sids = PVE::Network::SDN::Subnets::sdn_subnets_ids($cfg);
-       my $res = [];
-       foreach my $id (@sids) {
-           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
-           next if !$rpcenv->check_any($authuser, "/sdn/vnets/$vnetid/subnets/$id", $privs, 1);
-
-           my $scfg = &$api_sdn_subnets_config($cfg, $id);
-           next if !$scfg->{vnet} || $scfg->{vnet} ne $vnetid;
-           push @$res, $scfg;
-       }
-
-       return $res;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'read',
-    path => '{subnet}',
-    method => 'GET',
-    description => "Read sdn subnet configuration.",
-    permissions => {
-       check => ['perm', '/sdn/vnets/{vnet}/subnets/{subnet}', ['SDN.Allocate']],
-   },
-
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           vnet => get_standard_option('pve-sdn-vnet-id'),
-           subnet => get_standard_option('pve-sdn-subnet-id', {
-               completion => \&PVE::Network::SDN::Subnets::complete_sdn_subnets,
-           }),
-           running => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display running config.",
-           },
-           pending => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display pending config.",
-           },
-        },
-    },
-    returns => { type => 'object' },
-    code => sub {
-       my ($param) = @_;
-
-        my $cfg = {};
-        if($param->{pending}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           my $config = PVE::Network::SDN::Subnets::config();
-           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'subnets');
-        } elsif ($param->{running}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           $cfg = $running_cfg->{subnets};
-        } else {
-           $cfg = PVE::Network::SDN::Subnets::config();
-        }
-
-        my $scfg = &$api_sdn_subnets_config($cfg, $param->{subnet});
-
-       raise_param_exc({ vnet => "wrong vnet"}) if $param->{vnet} ne $scfg->{vnet};
-
-       return $scfg;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'create',
-    protected => 1,
-    path => '',
-    method => 'POST',
-    description => "Create a new sdn subnet object.",
-    permissions => {
-       check => ['perm', '/sdn/vnets/{vnet}/subnets', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::SubnetPlugin->createSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $type = extract_param($param, 'type');
-       my $cidr = extract_param($param, 'subnet');
-
-       # create /etc/pve/sdn directory
-       PVE::Cluster::check_cfs_quorum();
-       mkdir("/etc/pve/sdn") if ! -d '/etc/pve/sdn';
-
-        PVE::Network::SDN::lock_sdn_config(
-           sub {
-
-               my $cfg = PVE::Network::SDN::Subnets::config();
-               my $zone_cfg = PVE::Network::SDN::Zones::config();
-               my $vnet_cfg = PVE::Network::SDN::Vnets::config();
-               my $vnet = $param->{vnet};
-               my $zoneid = $vnet_cfg->{ids}->{$vnet}->{zone};
-               my $zone = $zone_cfg->{ids}->{$zoneid};      
-               my $id = $cidr =~ s/\//-/r;
-               $id = "$zoneid-$id";
-               
-               my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 1, 1);
-
-               my $scfg = undef;
-               if ($scfg = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id, 1)) {
-                   die "sdn subnet object ID '$id' already defined\n";
-               }
-
-               $cfg->{ids}->{$id} = $opts;
-
-               my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id);
-               PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $subnet);
-
-               PVE::Network::SDN::Subnets::write_config($cfg);
-
-           }, "create sdn subnet object failed");
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'update',
-    protected => 1,
-    path => '{subnet}',
-    method => 'PUT',
-    description => "Update sdn subnet object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/vnets/{vnet}/subnets', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::SubnetPlugin->updateSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'subnet');
-       my $digest = extract_param($param, 'digest');
-
-        PVE::Network::SDN::lock_sdn_config(
-        sub {
-
-           my $cfg = PVE::Network::SDN::Subnets::config();
-           my $zone_cfg = PVE::Network::SDN::Zones::config();
-           my $vnet_cfg = PVE::Network::SDN::Vnets::config();
-           my $vnet = $param->{vnet};
-           my $zoneid = $vnet_cfg->{ids}->{$vnet}->{zone};
-           my $zone = $zone_cfg->{ids}->{$zoneid};
-
-           my $scfg = &$api_sdn_subnets_config($cfg, $id);
-
-           PVE::SectionConfig::assert_if_modified($cfg, $digest);
-
-           my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 0, 1);
-           $cfg->{ids}->{$id} = $opts;
-
-           raise_param_exc({ ipam => "you can't change ipam"}) if $opts->{ipam} && $scfg->{ipam} && $opts->{ipam} ne $scfg->{ipam};
-
-           my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id);
-           PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $subnet, $scfg);
-
-           PVE::Network::SDN::Subnets::write_config($cfg);
-
-           }, "update sdn subnet object failed");
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'delete',
-    protected => 1,
-    path => '{subnet}',
-    method => 'DELETE',
-    description => "Delete sdn subnet object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/vnets/{vnet}/subnets', ['SDN.Allocate']],
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-            vnet => get_standard_option('pve-sdn-vnet-id'),
-           subnet => get_standard_option('pve-sdn-subnet-id', {
-                completion => \&PVE::Network::SDN::Subnets::complete_sdn_subnets,
-            }),
-       },
-    },
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'subnet');
-
-        PVE::Network::SDN::lock_sdn_config(
-           sub {
-               my $cfg = PVE::Network::SDN::Subnets::config();
-
-               my $scfg = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id, 1);
-
-               my $vnets_cfg = PVE::Network::SDN::Vnets::config();
-
-               PVE::Network::SDN::SubnetPlugin->on_delete_hook($id, $cfg, $vnets_cfg);
-
-               my $zone_cfg = PVE::Network::SDN::Zones::config();
-               my $vnet = $param->{vnet};
-               my $zoneid = $vnets_cfg->{ids}->{$vnet}->{zone};
-               my $zone = $zone_cfg->{ids}->{$zoneid};
-
-               PVE::Network::SDN::Subnets::del_subnet($zone, $id, $scfg);
-
-               delete $cfg->{ids}->{$id};
-
-               PVE::Network::SDN::Subnets::write_config($cfg);
-
-           }, "delete sdn subnet object failed");
-
-
-       return undef;
-    }});
-
-1;
diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
deleted file mode 100644 (file)
index 811a2e8..0000000
+++ /dev/null
@@ -1,292 +0,0 @@
-package PVE::API2::Network::SDN::Vnets;
-
-use strict;
-use warnings;
-
-use PVE::SafeSyslog;
-use PVE::Tools qw(extract_param);
-use PVE::Cluster qw(cfs_read_file cfs_write_file);
-use PVE::Network::SDN;
-use PVE::Network::SDN::Zones;
-use PVE::Network::SDN::Zones::Plugin;
-use PVE::Network::SDN::Vnets;
-use PVE::Network::SDN::VnetPlugin;
-use PVE::Network::SDN::Subnets;
-use PVE::API2::Network::SDN::Subnets;
-
-use Storable qw(dclone);
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::RPCEnvironment;
-use PVE::Exception qw(raise raise_param_exc);
-
-use PVE::RESTHandler;
-
-use base qw(PVE::RESTHandler);
-
-__PACKAGE__->register_method ({
-    subclass => "PVE::API2::Network::SDN::Subnets",
-    path => '{vnet}/subnets',
-});
-
-my $api_sdn_vnets_config = sub {
-    my ($cfg, $id) = @_;
-
-    my $scfg = dclone(PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $id));
-    $scfg->{vnet} = $id;
-    $scfg->{digest} = $cfg->{digest};
-    
-    return $scfg;
-};
-
-my $api_sdn_vnets_deleted_config = sub {
-    my ($cfg, $running_cfg, $id) = @_;
-
-    if (!$cfg->{ids}->{$id}) {
-
-       my $vnet_cfg = dclone(PVE::Network::SDN::Vnets::sdn_vnets_config($running_cfg->{vnets}, $id));
-       $vnet_cfg->{state} = "deleted";
-       $vnet_cfg->{vnet} = $id;
-       return $vnet_cfg;
-    }
-};
-
-__PACKAGE__->register_method ({
-    name => 'index',
-    path => '',
-    method => 'GET',
-    description => "SDN vnets index.",
-    permissions => {
-       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate'"
-           ." permissions on '/sdn/vnets/<vnet>'",
-       user => 'all',
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-            running => {
-                type => 'boolean',
-                optional => 1,
-                description => "Display running config.",
-            },
-           pending => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display pending config.",
-           },
-       },
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => {},
-       },
-       links => [ { rel => 'child', href => "{vnet}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-
-       my $rpcenv = PVE::RPCEnvironment::get();
-       my $authuser = $rpcenv->get_user();
-
-       my $cfg = {};
-       if($param->{pending}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           my $config = PVE::Network::SDN::Vnets::config();
-           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'vnets');
-       } elsif ($param->{running}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           $cfg = $running_cfg->{vnets};
-       } else {
-           $cfg = PVE::Network::SDN::Vnets::config();
-       }
-
-       my @sids = PVE::Network::SDN::Vnets::sdn_vnets_ids($cfg);
-       my $res = [];
-       foreach my $id (@sids) {
-           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
-           next if !$rpcenv->check_any($authuser, "/sdn/vnets/$id", $privs, 1);
-
-           my $scfg = &$api_sdn_vnets_config($cfg, $id);
-           push @$res, $scfg;
-       }
-
-       return $res;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'read',
-    path => '{vnet}',
-    method => 'GET',
-    description => "Read sdn vnet configuration.",
-    permissions => {
-       check => ['perm', '/sdn/vnets/{vnet}', ['SDN.Allocate']],
-   },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           vnet => get_standard_option('pve-sdn-vnet-id', {
-               completion => \&PVE::Network::SDN::Vnets::complete_sdn_vnets,
-           }),
-            running => {
-                type => 'boolean',
-                optional => 1,
-                description => "Display running config.",
-            },
-           pending => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display pending config.",
-           },
-       },
-    },
-    returns => { type => 'object' },
-    code => sub {
-       my ($param) = @_;
-
-       my $cfg = {};
-       if($param->{pending}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           my $config = PVE::Network::SDN::Vnets::config();
-           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'vnets');
-       } elsif ($param->{running}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           $cfg = $running_cfg->{vnets};
-       } else {
-           $cfg = PVE::Network::SDN::Vnets::config();
-       }
-
-       return $api_sdn_vnets_config->($cfg, $param->{vnet});
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'create',
-    protected => 1,
-    path => '',
-    method => 'POST',
-    description => "Create a new sdn vnet object.",
-    permissions => {
-       check => ['perm', '/sdn/vnets', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::VnetPlugin->createSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $type = extract_param($param, 'type');
-       my $id = extract_param($param, 'vnet');
-
-       PVE::Cluster::check_cfs_quorum();
-       mkdir("/etc/pve/sdn");
-
-        PVE::Network::SDN::lock_sdn_config(sub {
-           my $cfg = PVE::Network::SDN::Vnets::config();
-           my $opts = PVE::Network::SDN::VnetPlugin->check_config($id, $param, 1, 1);
-
-           if (PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $id, 1)) {
-               die "sdn vnet object ID '$id' already defined\n";
-           }
-           $cfg->{ids}->{$id} = $opts;
-
-           my $zone_cfg = PVE::Network::SDN::Zones::config();
-           my $zoneid = $cfg->{ids}->{$id}->{zone};
-           my $plugin_config = $zone_cfg->{ids}->{$zoneid};
-           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-            $plugin->vnet_update_hook($cfg, $id, $zone_cfg);
-
-           PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
-
-           PVE::Network::SDN::Vnets::write_config($cfg);
-
-       }, "create sdn vnet object failed");
-
-       return undef;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'update',
-    protected => 1,
-    path => '{vnet}',
-    method => 'PUT',
-    description => "Update sdn vnet object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/vnets', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::VnetPlugin->updateSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'vnet');
-       my $digest = extract_param($param, 'digest');
-
-       PVE::Network::SDN::lock_sdn_config(sub {
-           my $cfg = PVE::Network::SDN::Vnets::config();
-
-           PVE::SectionConfig::assert_if_modified($cfg, $digest);
-
-
-           my $opts = PVE::Network::SDN::VnetPlugin->check_config($id, $param, 0, 1);
-           raise_param_exc({ zone => "missing zone"}) if !$opts->{zone};
-           my $subnets = PVE::Network::SDN::Vnets::get_subnets($id);
-           raise_param_exc({ zone => "can't change zone if subnets exists"}) if($subnets && $opts->{zone} ne $cfg->{ids}->{$id}->{zone});
-
-           $cfg->{ids}->{$id} = $opts;
-
-           my $zone_cfg = PVE::Network::SDN::Zones::config();
-           my $zoneid = $cfg->{ids}->{$id}->{zone};
-           my $plugin_config = $zone_cfg->{ids}->{$zoneid};
-           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-           $plugin->vnet_update_hook($cfg, $id, $zone_cfg);
-
-           PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
-
-           PVE::Network::SDN::Vnets::write_config($cfg);
-
-       }, "update sdn vnet object failed");
-
-       return undef;
-    }
-});
-
-__PACKAGE__->register_method ({
-    name => 'delete',
-    protected => 1,
-    path => '{vnet}',
-    method => 'DELETE',
-    description => "Delete sdn vnet object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/vnets', ['SDN.Allocate']],
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           vnet => get_standard_option('pve-sdn-vnet-id', {
-               completion => \&PVE::Network::SDN::Vnets::complete_sdn_vnets,
-           }),
-       },
-    },
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'vnet');
-
-        PVE::Network::SDN::lock_sdn_config(sub {
-           my $cfg = PVE::Network::SDN::Vnets::config();
-           my $scfg = PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $id); # check if exists
-           my $vnet_cfg = PVE::Network::SDN::Vnets::config();
-
-           PVE::Network::SDN::VnetPlugin->on_delete_hook($id, $vnet_cfg);
-
-           delete $cfg->{ids}->{$id};
-           PVE::Network::SDN::Vnets::write_config($cfg);
-
-       }, "delete sdn vnet object failed");
-
-
-       return undef;
-    }
-});
-
-1;
diff --git a/PVE/API2/Network/SDN/Zones.pm b/PVE/API2/Network/SDN/Zones.pm
deleted file mode 100644 (file)
index 6e53240..0000000
+++ /dev/null
@@ -1,351 +0,0 @@
-package PVE::API2::Network::SDN::Zones;
-
-use strict;
-use warnings;
-
-use Storable qw(dclone);
-
-use PVE::Cluster qw(cfs_read_file cfs_write_file);
-use PVE::Exception qw(raise raise_param_exc);
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::RPCEnvironment;
-use PVE::SafeSyslog;
-use PVE::Tools qw(extract_param);
-
-use PVE::Network::SDN::Dns;
-use PVE::Network::SDN::Subnets;
-use PVE::Network::SDN::Vnets;
-use PVE::Network::SDN;
-
-use PVE::Network::SDN::Zones::EvpnPlugin;
-use PVE::Network::SDN::Zones::FaucetPlugin;
-use PVE::Network::SDN::Zones::Plugin;
-use PVE::Network::SDN::Zones::QinQPlugin;
-use PVE::Network::SDN::Zones::SimplePlugin;
-use PVE::Network::SDN::Zones::VlanPlugin;
-use PVE::Network::SDN::Zones::VxlanPlugin;
-use PVE::Network::SDN::Zones;
-
-use PVE::RESTHandler;
-use base qw(PVE::RESTHandler);
-
-my $sdn_zones_type_enum = PVE::Network::SDN::Zones::Plugin->lookup_types();
-
-my $api_sdn_zones_config = sub {
-    my ($cfg, $id) = @_;
-
-    my $scfg = dclone(PVE::Network::SDN::Zones::sdn_zones_config($cfg, $id));
-    $scfg->{zone} = $id;
-    $scfg->{digest} = $cfg->{digest};
-
-    if ($scfg->{nodes}) {
-        $scfg->{nodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'nodes', $scfg->{nodes});
-    }
-
-    if ($scfg->{exitnodes}) {
-        $scfg->{exitnodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'exitnodes', $scfg->{exitnodes});
-    }
-
-    my $pending = $scfg->{pending};
-    if ($pending->{nodes}) {
-        $pending->{nodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'nodes', $pending->{nodes});
-    }
-
-    if ($pending->{exitnodes}) {
-        $pending->{exitnodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'exitnodes', $pending->{exitnodes});
-    }
-
-    return $scfg;
-};
-
-__PACKAGE__->register_method ({
-    name => 'index',
-    path => '',
-    method => 'GET',
-    description => "SDN zones index.",
-    permissions => {
-       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/zones/<zone>'",
-       user => 'all',
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           type => {
-               description => "Only list SDN zones of specific type",
-               type => 'string',
-               enum => $sdn_zones_type_enum,
-               optional => 1,
-           },
-           running => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display running config.",
-           },
-           pending => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display pending config.",
-           },
-       },
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => { zone => { type => 'string'},
-                           type => { type => 'string'},
-                           mtu => { type => 'integer', optional => 1 },
-                           dns => { type => 'string', optional => 1},
-                           reversedns => { type => 'string', optional => 1},
-                           dnszone => { type => 'string', optional => 1},
-                           ipam => { type => 'string', optional => 1},
-                           pending => { optional => 1},
-                           state => { type => 'string', optional => 1},
-                           nodes => { type => 'string', optional => 1},
-                         },
-       },
-       links => [ { rel => 'child', href => "{zone}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-
-       my $rpcenv = PVE::RPCEnvironment::get();
-       my $authuser = $rpcenv->get_user();
-
-       my $cfg = {};
-       if ($param->{pending}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           my $config = PVE::Network::SDN::Zones::config();
-           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'zones');
-        } elsif ($param->{running}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           $cfg = $running_cfg->{zones};
-        } else {
-           $cfg = PVE::Network::SDN::Zones::config();
-        }
-
-       my @sids = PVE::Network::SDN::Zones::sdn_zones_ids($cfg);
-       my $res = [];
-       for my $id (@sids) {
-           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
-           next if !$rpcenv->check_any($authuser, "/sdn/zones/$id", $privs, 1);
-
-           my $scfg = &$api_sdn_zones_config($cfg, $id);
-           next if $param->{type} && $param->{type} ne $scfg->{type};
-
-           my $plugin_config = $cfg->{ids}->{$id};
-           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-           push @$res, $scfg;
-       }
-
-       return $res;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'read',
-    path => '{zone}',
-    method => 'GET',
-    description => "Read sdn zone configuration.",
-    permissions => {
-       check => ['perm', '/sdn/zones/{zone}', ['SDN.Allocate']],
-   },
-
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           zone => get_standard_option('pve-sdn-zone-id'),
-           running => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display running config.",
-           },
-           pending => {
-               type => 'boolean',
-               optional => 1,
-               description => "Display pending config.",
-           }
-       },
-    },
-    returns => { type => 'object' },
-    code => sub {
-       my ($param) = @_;
-
-       my $cfg = {};
-       if ($param->{pending}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           my $config = PVE::Network::SDN::Zones::config();
-           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'zones');
-        } elsif ($param->{running}) {
-           my $running_cfg = PVE::Network::SDN::running_config();
-           $cfg = $running_cfg->{zones};
-        } else {
-           $cfg = PVE::Network::SDN::Zones::config();
-        }
-
-       return &$api_sdn_zones_config($cfg, $param->{zone});
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'create',
-    protected => 1,
-    path => '',
-    method => 'POST',
-    description => "Create a new sdn zone object.",
-    permissions => {
-       check => ['perm', '/sdn/zones', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::Zones::Plugin->createSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $type = extract_param($param, 'type');
-       my $id = extract_param($param, 'zone');
-
-       my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($type);
-       my $opts = $plugin->check_config($id, $param, 1, 1);
-
-       PVE::Cluster::check_cfs_quorum();
-       mkdir("/etc/pve/sdn");
-
-       PVE::Network::SDN::lock_sdn_config(sub {
-           my $zone_cfg = PVE::Network::SDN::Zones::config();
-           my $controller_cfg = PVE::Network::SDN::Controllers::config();
-           my $dns_cfg = PVE::Network::SDN::Dns::config();
-
-           my $scfg = undef;
-           if ($scfg = PVE::Network::SDN::Zones::sdn_zones_config($zone_cfg, $id, 1)) {
-               die "sdn zone object ID '$id' already defined\n";
-           }
-
-           my $dnsserver = $opts->{dns};
-           raise_param_exc({ dns => "$dnsserver don't exist"})
-               if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
-
-           my $reversednsserver = $opts->{reversedns};
-           raise_param_exc({ reversedns => "$reversednsserver don't exist"})
-               if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
-
-           my $dnszone = $opts->{dnszone};
-           raise_param_exc({ dnszone => "missing dns server"})
-               if $dnszone && !$dnsserver;
-
-           my $ipam = $opts->{ipam};
-           my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-           raise_param_exc({ ipam => "$ipam not existing"}) if $ipam && !$ipam_cfg->{ids}->{$ipam};
-
-           $zone_cfg->{ids}->{$id} = $opts;
-           $plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
-
-           PVE::Network::SDN::Zones::write_config($zone_cfg);
-
-       }, "create sdn zone object failed");
-
-       return;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'update',
-    protected => 1,
-    path => '{zone}',
-    method => 'PUT',
-    description => "Update sdn zone object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/zones', ['SDN.Allocate']],
-    },
-    parameters => PVE::Network::SDN::Zones::Plugin->updateSchema(),
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'zone');
-       my $digest = extract_param($param, 'digest');
-
-       PVE::Network::SDN::lock_sdn_config(sub {
-           my $zone_cfg = PVE::Network::SDN::Zones::config();
-           my $controller_cfg = PVE::Network::SDN::Controllers::config();
-           my $dns_cfg = PVE::Network::SDN::Dns::config();
-
-           PVE::SectionConfig::assert_if_modified($zone_cfg, $digest);
-
-           my $scfg = PVE::Network::SDN::Zones::sdn_zones_config($zone_cfg, $id);
-
-           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($scfg->{type});
-           my $opts = $plugin->check_config($id, $param, 0, 1);
-
-           if ($opts->{ipam} && !$scfg->{ipam} || $opts->{ipam} ne $scfg->{ipam}) {
-
-               # don't allow ipam change if subnet are defined for now, need to implement resync ipam content
-               my $subnets_cfg = PVE::Network::SDN::Subnets::config();
-               for my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
-                   my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
-                   raise_param_exc({ ipam => "can't change ipam if a subnet is already defined in this zone"})
-                       if $subnet->{zone} eq $id;
-               }
-           }
-
-           $zone_cfg->{ids}->{$id} = $opts;
-
-           my $dnsserver = $opts->{dns};
-           raise_param_exc({ dns => "$dnsserver don't exist"}) if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
-
-           my $reversednsserver = $opts->{reversedns};
-           raise_param_exc({ reversedns => "$reversednsserver don't exist"}) if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
-
-           my $dnszone = $opts->{dnszone};
-           raise_param_exc({ dnszone => "missing dns server"}) if $dnszone && !$dnsserver;
-
-           my $ipam = $opts->{ipam};
-           my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-           raise_param_exc({ ipam => "$ipam not existing"}) if $ipam && !$ipam_cfg->{ids}->{$ipam};
-
-           $plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
-
-           PVE::Network::SDN::Zones::write_config($zone_cfg);
-
-       }, "update sdn zone object failed");
-
-       return;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'delete',
-    protected => 1,
-    path => '{zone}',
-    method => 'DELETE',
-    description => "Delete sdn zone object configuration.",
-    permissions => {
-       check => ['perm', '/sdn/zones', ['SDN.Allocate']],
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           zone => get_standard_option('pve-sdn-zone-id', {
-               completion => \&PVE::Network::SDN::Zones::complete_sdn_zones,
-           }),
-       },
-    },
-    returns => { type => 'null' },
-    code => sub {
-       my ($param) = @_;
-
-       my $id = extract_param($param, 'zone');
-
-        PVE::Network::SDN::lock_sdn_config(sub {
-           my $cfg = PVE::Network::SDN::Zones::config();
-           my $scfg = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $id);
-
-           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($scfg->{type});
-           my $vnet_cfg = PVE::Network::SDN::Vnets::config();
-
-           $plugin->on_delete_hook($id, $vnet_cfg);
-
-           delete $cfg->{ids}->{$id};
-
-           PVE::Network::SDN::Zones::write_config($cfg);
-       }, "delete sdn zone object failed");
-
-       return;
-    }});
-
-1;
diff --git a/PVE/API2/Network/SDN/Zones/Content.pm b/PVE/API2/Network/SDN/Zones/Content.pm
deleted file mode 100644 (file)
index 66f49df..0000000
+++ /dev/null
@@ -1,85 +0,0 @@
-package PVE::API2::Network::SDN::Zones::Content;
-
-use strict;
-use warnings;
-use Data::Dumper;
-
-use PVE::SafeSyslog;
-use PVE::Cluster;
-use PVE::INotify;
-use PVE::Exception qw(raise_param_exc);
-use PVE::RPCEnvironment;
-use PVE::RESTHandler;
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::Network::SDN;
-
-use base qw(PVE::RESTHandler);
-
-__PACKAGE__->register_method ({
-    name => 'index',
-    path => '',
-    method => 'GET',
-    description => "List zone content.",
-    permissions => {
-       check => ['perm', '/sdn/zones/{zone}', ['SDN.Audit'], any => 1],
-    },
-    protected => 1,
-    proxyto => 'node',
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           node => get_standard_option('pve-node'),
-           zone => get_standard_option('pve-sdn-zone-id', {
-               completion => \&PVE::Network::SDN::Zones::complete_sdn_zone,
-            }),
-       },
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => {
-               vnet => {
-                   description => "Vnet identifier.",
-                   type => 'string',
-               },
-               status => {
-                   description => "Status.",
-                   type => 'string',
-                   optional => 1,
-               },
-               statusmsg => {
-                   description => "Status details",
-                   type => 'string',
-                   optional => 1,
-               },
-           },
-       },
-       links => [ { rel => 'child', href => "{vnet}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-
-       my $rpcenv = PVE::RPCEnvironment::get();
-
-       my $authuser = $rpcenv->get_user();
-
-       my $zoneid = $param->{zone};
-
-       my $res = [];
-
-        my ($zone_status, $vnet_status) = PVE::Network::SDN::status();
-
-       foreach my $id (keys %{$vnet_status}) {
-           if ($vnet_status->{$id}->{zone} eq $zoneid) {
-               my $item->{vnet} = $id;
-               $item->{status} = $vnet_status->{$id}->{'status'};
-               $item->{statusmsg} = $vnet_status->{$id}->{'statusmsg'};
-               push @$res,$item;
-           }
-        }
-
-       return $res;
-    }});
-
-1;
diff --git a/PVE/API2/Network/SDN/Zones/Makefile b/PVE/API2/Network/SDN/Zones/Makefile
deleted file mode 100644 (file)
index 9b0a42b..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-SOURCES=Status.pm Content.pm
-
-
-PERL5DIR=${DESTDIR}/usr/share/perl5
-
-.PHONY: install
-install:
-       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/API2/Network/SDN/Zones/$$i; done
diff --git a/PVE/API2/Network/SDN/Zones/Status.pm b/PVE/API2/Network/SDN/Zones/Status.pm
deleted file mode 100644 (file)
index 17de68f..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-package PVE::API2::Network::SDN::Zones::Status;
-
-use strict;
-use warnings;
-
-use File::Path;
-use File::Basename;
-use PVE::Tools;
-use PVE::INotify;
-use PVE::Cluster;
-use PVE::API2::Network::SDN::Zones::Content;
-use PVE::RESTHandler;
-use PVE::RPCEnvironment;
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::Exception qw(raise_param_exc);
-
-use base qw(PVE::RESTHandler);
-
-__PACKAGE__->register_method ({
-    subclass => "PVE::API2::Network::SDN::Zones::Content",
-    path => '{zone}/content',
-});
-
-__PACKAGE__->register_method ({
-    name => 'index',
-    path => '',
-    method => 'GET',
-    description => "Get status for all zones.",
-    permissions => {
-       description => "Only list entries where you have 'SDN.Audit'",
-       user => 'all',
-    },
-    protected => 1,
-    proxyto => 'node',
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           node => get_standard_option('pve-node')
-       },
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => {
-               zone => get_standard_option('pve-sdn-zone-id'),
-               status => {
-                   description => "Status of zone",
-                   type => 'string',
-                   enum => ['available', 'pending', 'error'],
-               },
-           },
-       },
-       links => [ { rel => 'child', href => "{zone}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-
-       my $rpcenv = PVE::RPCEnvironment::get();
-       my $authuser = $rpcenv->get_user();
-
-       my $localnode = PVE::INotify::nodename();
-
-       my $res = [];
-
-       my ($zone_status, $vnet_status) = PVE::Network::SDN::status();
-
-       foreach my $id (sort keys %{$zone_status}) {
-           my $item->{zone} = $id;
-           $item->{status} = $zone_status->{$id}->{'status'};
-           push @$res, $item;
-       }
-
-       return $res;
-    }});
-
-__PACKAGE__->register_method ({
-    name => 'diridx',
-    path => '{zone}',
-    method => 'GET',
-    description => "",
-    permissions => {
-       check => ['perm', '/sdn/zones/{zone}', ['SDN.Audit'], any => 1],
-    },
-    parameters => {
-       additionalProperties => 0,
-       properties => {
-           node => get_standard_option('pve-node'),
-           zone => get_standard_option('pve-sdn-zone-id'),
-       },
-    },
-    returns => {
-       type => 'array',
-       items => {
-           type => "object",
-           properties => {
-               subdir => { type => 'string' },
-           },
-       },
-       links => [ { rel => 'child', href => "{subdir}" } ],
-    },
-    code => sub {
-       my ($param) = @_;
-       my $res = [
-           { subdir => 'content' },
-           ];
-
-       return $res;
-    }});
-
-1;
diff --git a/PVE/Makefile b/PVE/Makefile
deleted file mode 100644 (file)
index 26e01a4..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-all:
-
-.PHONY: install
-install:
-       make -C Network install
-       make -C API2 install
diff --git a/PVE/Network/Makefile b/PVE/Network/Makefile
deleted file mode 100644 (file)
index 277e19c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-SOURCES=SDN.pm
-
-
-PERL5DIR=${DESTDIR}/usr/share/perl5
-
-.PHONY: install
-install:
-       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/$$i; done
-       make -C SDN install
diff --git a/PVE/Network/SDN.pm b/PVE/Network/SDN.pm
deleted file mode 100644 (file)
index b95dd5b..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-package PVE::Network::SDN;
-
-use strict;
-use warnings;
-
-use Data::Dumper;
-use JSON;
-
-use PVE::Network::SDN::Vnets;
-use PVE::Network::SDN::Zones;
-use PVE::Network::SDN::Controllers;
-use PVE::Network::SDN::Subnets;
-
-use PVE::Tools qw(extract_param dir_glob_regex run_command);
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-
-
-my $running_cfg = "sdn/.running-config";
-
-my $parse_running_cfg = sub {
-    my ($filename, $raw) = @_;
-
-    my $cfg = {};
-
-    return $cfg if !defined($raw) || $raw eq '';
-
-    eval {
-       $cfg = from_json($raw);
-    };
-    return {} if $@;
-
-    return $cfg;
-};
-
-my $write_running_cfg = sub {
-    my ($filename, $cfg) = @_;
-
-    my $json = to_json($cfg);
-
-    return $json;
-};
-
-PVE::Cluster::cfs_register_file($running_cfg, $parse_running_cfg, $write_running_cfg);
-
-
-# improve me : move status code inside plugins ?
-
-sub ifquery_check {
-
-    my $cmd = ['ifquery', '-a', '-c', '-o','json'];
-
-    my $result = '';
-    my $reader = sub { $result .= shift };
-
-    eval {
-       run_command($cmd, outfunc => $reader);
-    };
-
-    my $resultjson = decode_json($result);
-    my $interfaces = {};
-
-    foreach my $interface (@$resultjson) {
-       my $name = $interface->{name};
-       $interfaces->{$name} = {
-           status => $interface->{status},
-           config => $interface->{config},
-           config_status => $interface->{config_status},
-       };
-    }
-
-    return $interfaces;
-}
-
-sub status {
-
-    my ($zone_status, $vnet_status) = PVE::Network::SDN::Zones::status();
-    return($zone_status, $vnet_status);
-}
-
-sub running_config {
-    return cfs_read_file($running_cfg);
-}
-
-sub pending_config {
-    my ($running_cfg, $cfg, $type) = @_;
-
-    my $pending = {};
-
-    my $running_objects = $running_cfg->{$type}->{ids};
-    my $config_objects = $cfg->{ids};
-
-    foreach my $id (sort keys %{$running_objects}) {
-       my $running_object = $running_objects->{$id};
-       my $config_object = $config_objects->{$id};
-       foreach my $key (sort keys %{$running_object}) {
-           $pending->{$id}->{$key} = $running_object->{$key};
-           if(!keys %{$config_object}) {
-               $pending->{$id}->{state} = "deleted";
-           } elsif (!defined($config_object->{$key})) {
-               $pending->{$id}->{"pending"}->{$key} = 'deleted';
-               $pending->{$id}->{state} = "changed";
-           } elsif (PVE::Network::SDN::encode_value(undef, $key, $running_object->{$key})
-                        ne PVE::Network::SDN::encode_value(undef, $key, $config_object->{$key})) {
-               $pending->{$id}->{state} = "changed";
-           }
-       }
-       $pending->{$id}->{"pending"} = {} if $pending->{$id}->{state} && !defined($pending->{$id}->{"pending"});
-    }
-
-   foreach my $id (sort keys %{$config_objects}) {
-       my $running_object = $running_objects->{$id};
-       my $config_object = $config_objects->{$id};
-
-       foreach my $key (sort keys %{$config_object}) {
-           my $config_value = PVE::Network::SDN::encode_value(undef, $key, $config_object->{$key}) if $config_object->{$key};
-           my $running_value = PVE::Network::SDN::encode_value(undef, $key, $running_object->{$key}) if $running_object->{$key};
-           if($key eq 'type' || $key eq 'vnet') {
-               $pending->{$id}->{$key} = $config_value;
-           } else {
-               $pending->{$id}->{"pending"}->{$key} = $config_value if !defined($running_value) || ($config_value ne $running_value);
-           }
-           if(!keys %{$running_object}) {
-               $pending->{$id}->{state} = "new";
-           } elsif (!defined($running_value) && defined($config_value)) {
-               $pending->{$id}->{state} = "changed";
-           }
-       }
-       $pending->{$id}->{"pending"} = {} if  $pending->{$id}->{state} && !defined($pending->{$id}->{"pending"});
-   }
-
-   return {ids => $pending};
-
-}
-
-sub commit_config {
-
-    my $cfg = cfs_read_file($running_cfg);
-    my $version = $cfg->{version};
-
-    if ($version) {
-       $version++;
-    } else {
-       $version = 1;
-    }
-
-    my $vnets_cfg = PVE::Network::SDN::Vnets::config();
-    my $zones_cfg = PVE::Network::SDN::Zones::config();
-    my $controllers_cfg = PVE::Network::SDN::Controllers::config();
-    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
-
-    my $vnets = { ids => $vnets_cfg->{ids} };
-    my $zones = { ids => $zones_cfg->{ids} };
-    my $controllers = { ids => $controllers_cfg->{ids} };
-    my $subnets = { ids => $subnets_cfg->{ids} };
-
-     $cfg = { version => $version, vnets => $vnets, zones => $zones, controllers => $controllers, subnets => $subnets };
-
-    cfs_write_file($running_cfg, $cfg);
-}
-
-sub lock_sdn_config {
-    my ($code, $errmsg) = @_;
-
-    cfs_lock_file($running_cfg, undef, $code);
-
-    if (my $err = $@) {
-        $errmsg ? die "$errmsg: $err" : die $err;
-    }
-}
-
-sub get_local_vnets {
-
-    my $rpcenv = PVE::RPCEnvironment::get();
-
-    my $authuser = $rpcenv->get_user();
-
-    my $nodename = PVE::INotify::nodename();
-
-    my $cfg = PVE::Network::SDN::running_config();
-    my $vnets_cfg = $cfg->{vnets};
-    my $zones_cfg = $cfg->{zones};
-
-    my @vnetids = PVE::Network::SDN::Vnets::sdn_vnets_ids($vnets_cfg);
-
-    my $vnets = {};
-
-    foreach my $vnetid (@vnetids) {
-
-       my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($vnets_cfg, $vnetid);
-       my $zoneid = $vnet->{zone};
-       my $comments = $vnet->{alias};
-
-       my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
-
-       next if !$zoneid;
-       next if !$rpcenv->check_any($authuser, "/sdn/zones/$zoneid", $privs, 1) && !$rpcenv->check_any($authuser, "/sdn/vnets/$vnetid", $privs, 1);
-
-       my $zone_config = PVE::Network::SDN::Zones::sdn_zones_config($zones_cfg, $zoneid);
-
-       next if defined($zone_config->{nodes}) && !$zone_config->{nodes}->{$nodename};
-       my $ipam = $zone_config->{ipam} ? 1 : 0;
-       my $vlanaware = $vnet->{vlanaware} ? 1 : 0;
-       $vnets->{$vnetid} = { type => 'vnet', active => '1', ipam => $ipam, vlanaware => $vlanaware, comments => $comments };
-    }
-
-    return $vnets;
-}
-
-sub generate_zone_config {
-    my $raw_config = PVE::Network::SDN::Zones::generate_etc_network_config();
-    PVE::Network::SDN::Zones::write_etc_network_config($raw_config);
-}
-
-sub generate_controller_config {
-    my ($reload) = @_;
-
-    my $raw_config = PVE::Network::SDN::Controllers::generate_controller_config();
-    PVE::Network::SDN::Controllers::write_controller_config($raw_config);
-
-    PVE::Network::SDN::Controllers::reload_controller() if $reload;
-}
-
-sub encode_value {
-    my ($type, $key, $value) = @_;
-
-    if ($key eq 'nodes' || $key eq 'exitnodes') {
-        if(ref($value) eq 'HASH') {
-            return join(',', sort keys(%$value));
-        } else {
-            return $value;
-        }
-    }
-
-    return $value;
-}
-
-
-#helpers
-sub api_request {
-    my ($method, $url, $headers, $data) = @_;
-
-    my $encoded_data = to_json($data) if $data;
-
-    my $req = HTTP::Request->new($method,$url, $headers, $encoded_data);
-
-    my $ua = LWP::UserAgent->new(protocols_allowed => ['http', 'https'], timeout => 30);
-    my $proxy = undef;
-
-    if ($proxy) {
-        $ua->proxy(['http', 'https'], $proxy);
-    } else {
-        $ua->env_proxy;
-    }
-
-    $ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00);
-
-    my $response = $ua->request($req);
-    my $code = $response->code;
-
-    if ($code !~ /^2(\d+)$/) {
-        my $msg = $response->message || 'unknown';
-        die "Invalid response from server: $code $msg\n";
-    }
-
-    my $raw = '';
-    if (defined($response->decoded_content)) {
-       $raw = $response->decoded_content;
-    } else {
-       $raw = $response->content;
-    }
-
-    return if $raw eq '';
-
-    my $json = '';
-    eval {
-       $json = from_json($raw);
-    };
-    die "api response is not a json" if $@;
-
-    return $json;
-}
-
-1;
diff --git a/PVE/Network/SDN/Controllers.pm b/PVE/Network/SDN/Controllers.pm
deleted file mode 100644 (file)
index a23048e..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-package PVE::Network::SDN::Controllers;
-
-use strict;
-use warnings;
-
-use Data::Dumper;
-use JSON;
-
-use PVE::Tools qw(extract_param dir_glob_regex run_command);
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-
-use PVE::Network::SDN::Vnets;
-use PVE::Network::SDN::Zones;
-
-use PVE::Network::SDN::Controllers::EvpnPlugin;
-use PVE::Network::SDN::Controllers::BgpPlugin;
-use PVE::Network::SDN::Controllers::FaucetPlugin;
-use PVE::Network::SDN::Controllers::Plugin;
-PVE::Network::SDN::Controllers::EvpnPlugin->register();
-PVE::Network::SDN::Controllers::BgpPlugin->register();
-PVE::Network::SDN::Controllers::FaucetPlugin->register();
-PVE::Network::SDN::Controllers::Plugin->init();
-
-
-sub sdn_controllers_config {
-    my ($cfg, $id, $noerr) = @_;
-
-    die "no sdn controller ID specified\n" if !$id;
-
-    my $scfg = $cfg->{ids}->{$id};
-    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
-
-    return $scfg;
-}
-
-sub config {
-    my $config = cfs_read_file("sdn/controllers.cfg");
-    $config = cfs_read_file("sdn/controllers.cfg") if !keys %{$config->{ids}};
-    return $config;
-}
-
-sub write_config {
-    my ($cfg) = @_;
-
-    cfs_write_file("sdn/controllers.cfg", $cfg);
-}
-
-sub lock_sdn_controllers_config {
-    my ($code, $errmsg) = @_;
-
-    cfs_lock_file("sdn/controllers.cfg", undef, $code);
-    if (my $err = $@) {
-        $errmsg ? die "$errmsg: $err" : die $err;
-    }
-}
-
-sub sdn_controllers_ids {
-    my ($cfg) = @_;
-
-    return sort keys %{$cfg->{ids}};
-}
-
-sub complete_sdn_controller {
-    my ($cmdname, $pname, $cvalue) = @_;
-
-    my $cfg = PVE::Network::SDN::running_config();
-
-    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_controllers_ids($cfg) ];
-}
-
-sub generate_controller_config {
-
-    my $cfg = PVE::Network::SDN::running_config();
-    my $vnet_cfg = $cfg->{vnets};
-    my $zone_cfg = $cfg->{zones};
-    my $controller_cfg = $cfg->{controllers};
-
-    return if !$vnet_cfg && !$zone_cfg && !$controller_cfg;
-
-    #read main config for physical interfaces
-    my $current_config_file = "/etc/network/interfaces";
-    my $fh = IO::File->new($current_config_file);
-    my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
-    $fh->close();
-
-    # check uplinks
-    my $uplinks = {};
-    foreach my $id (keys %{$interfaces_config->{ifaces}}) {
-       my $interface = $interfaces_config->{ifaces}->{$id};
-       if (my $uplink = $interface->{'uplink-id'}) {
-           die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
-           $interface->{name} = $id;
-           $uplinks->{$interface->{'uplink-id'}} = $interface;
-       }
-    }
-
-    # generate configuration
-    my $config = {};
-
-    foreach my $id (sort keys %{$controller_cfg->{ids}}) {
-       my $plugin_config = $controller_cfg->{ids}->{$id};
-       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
-       $plugin->generate_controller_config($plugin_config, $controller_cfg, $id, $uplinks, $config);
-    }
-
-    foreach my $id (sort keys %{$zone_cfg->{ids}}) {
-       my $plugin_config = $zone_cfg->{ids}->{$id};
-       my $controllerid = $plugin_config->{controller};
-       next if !$controllerid;
-       my $controller = $controller_cfg->{ids}->{$controllerid};
-       if ($controller) {
-           my $controller_plugin = PVE::Network::SDN::Controllers::Plugin->lookup($controller->{type});
-           $controller_plugin->generate_controller_zone_config($plugin_config, $controller, $controller_cfg, $id, $uplinks, $config);
-       }
-    }
-
-    foreach my $id (sort keys %{$vnet_cfg->{ids}}) {
-       my $plugin_config = $vnet_cfg->{ids}->{$id};
-       my $zoneid = $plugin_config->{zone};
-       next if !$zoneid;
-       my $zone = $zone_cfg->{ids}->{$zoneid};
-       next if !$zone;
-       my $controllerid = $zone->{controller};
-       next if !$controllerid;
-       my $controller = $controller_cfg->{ids}->{$controllerid};
-       if ($controller) {
-           my $controller_plugin = PVE::Network::SDN::Controllers::Plugin->lookup($controller->{type});
-           $controller_plugin->generate_controller_vnet_config($plugin_config, $controller, $zone, $zoneid, $id, $config);
-       }
-    }
-
-    return $config;
-}
-
-
-sub reload_controller {
-
-    my $cfg = PVE::Network::SDN::running_config();
-    my $controller_cfg = $cfg->{controllers};
-
-    return if !$controller_cfg;
-
-    foreach my $id (keys %{$controller_cfg->{ids}}) {
-       my $plugin_config = $controller_cfg->{ids}->{$id};
-       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
-       $plugin->reload_controller();
-    }
-}
-
-sub generate_controller_rawconfig {
-    my ($config) = @_;
-
-    my $cfg = PVE::Network::SDN::running_config();
-    my $controller_cfg = $cfg->{controllers};
-    return if !$controller_cfg;
-
-    my $rawconfig = "";
-    foreach my $id (keys %{$controller_cfg->{ids}}) {
-       my $plugin_config = $controller_cfg->{ids}->{$id};
-       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
-       $rawconfig .= $plugin->generate_controller_rawconfig($plugin_config, $config);
-    }
-    return $rawconfig;
-}
-
-sub write_controller_config {
-    my ($config) = @_;
-
-    my $cfg = PVE::Network::SDN::running_config();
-    my $controller_cfg = $cfg->{controllers};
-    return if !$controller_cfg;
-
-    foreach my $id (keys %{$controller_cfg->{ids}}) {
-       my $plugin_config = $controller_cfg->{ids}->{$id};
-       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
-       $plugin->write_controller_config($plugin_config, $config);
-    }
-}
-
-1;
-
diff --git a/PVE/Network/SDN/Controllers/BgpPlugin.pm b/PVE/Network/SDN/Controllers/BgpPlugin.pm
deleted file mode 100644 (file)
index 0b8cf1a..0000000
+++ /dev/null
@@ -1,183 +0,0 @@
-package PVE::Network::SDN::Controllers::BgpPlugin;
-
-use strict;
-use warnings;
-
-use PVE::INotify;
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::Tools qw(run_command file_set_contents file_get_contents);
-
-use PVE::Network::SDN::Controllers::Plugin;
-use PVE::Network::SDN::Zones::Plugin;
-use Net::IP;
-
-use base('PVE::Network::SDN::Controllers::Plugin');
-
-sub type {
-    return 'bgp';
-}
-
-sub properties {
-    return {
-       'bgp-multipath-as-path-relax' => {
-           type => 'boolean',
-           optional => 1,
-       },
-       ebgp => {
-           type => 'boolean',
-           optional => 1,
-           description => "Enable ebgp. (remote-as external)",
-       },
-       'ebgp-multihop' => {
-           type => 'integer',
-           optional => 1,
-       },
-       loopback => {
-           description => "source loopback interface.",
-           type => 'string'
-       },
-        node => get_standard_option('pve-node'),
-    };
-}
-
-sub options {
-    return {
-       'node' => { optional => 0 },
-       'asn' => { optional => 0 },
-       'peers' => { optional => 0 },
-       'bgp-multipath-as-path-relax' => { optional => 1 },
-       'ebgp' => { optional => 1 },
-       'ebgp-multihop' => { optional => 1 },
-       'loopback' => { optional => 1 },
-    };
-}
-
-# Plugin implementation
-sub generate_controller_config {
-    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
-
-    my @peers;
-    @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'};
-
-    my $asn = $plugin_config->{asn};
-    my $ebgp = $plugin_config->{ebgp};
-    my $ebgp_multihop = $plugin_config->{'ebgp-multihop'};
-    my $loopback = $plugin_config->{loopback};
-    my $multipath_relax = $plugin_config->{'bgp-multipath-as-path-relax'};
-
-    my $local_node = PVE::INotify::nodename();
-
-
-    return if !$asn;
-    return if $local_node ne $plugin_config->{node};
-
-    my $bgp = $config->{frr}->{router}->{"bgp $asn"} //= {};
-
-    my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
-
-    my $remoteas = $ebgp ? "external" : $asn;
-
-    #global options
-    my @controller_config = (
-        "bgp router-id $ifaceip",
-        "no bgp default ipv4-unicast",
-        "coalesce-time 1000"
-    );
-
-    push(@{$bgp->{""}}, @controller_config) if keys %{$bgp} == 0;
-
-    @controller_config = ();
-    if($ebgp) {
-       push @controller_config, "bgp disable-ebgp-connected-route-check" if $loopback;
-    }
-
-    push @controller_config, "bgp bestpath as-path multipath-relax" if $multipath_relax;
-
-    #BGP neighbors
-    if(@peers) {
-       push @controller_config, "neighbor BGP peer-group";
-       push @controller_config, "neighbor BGP remote-as $remoteas";
-       push @controller_config, "neighbor BGP bfd";
-       push @controller_config, "neighbor BGP ebgp-multihop $ebgp_multihop" if $ebgp && $ebgp_multihop;
-    }
-
-    # BGP peers
-    foreach my $address (@peers) {
-       push @controller_config, "neighbor $address peer-group BGP";
-    }
-    push(@{$bgp->{""}}, @controller_config);
-
-    # address-family unicast
-    if (@peers) {
-       my $ipversion = Net::IP::ip_is_ipv6($ifaceip) ? "ipv6" : "ipv4";
-       my $mask = Net::IP::ip_is_ipv6($ifaceip) ? "/128" : "32";
-
-       push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "network $ifaceip/$mask") if $loopback;
-       push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP activate");
-       push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP soft-reconfiguration inbound");
-    }
-
-    if ($loopback) {
-       $config->{frr_prefix_list}->{loopbacks_ips}->{10} = "permit 0.0.0.0/0 le 32";
-       push(@{$config->{frr}->{''}}, "ip protocol bgp route-map correct_src");
-
-       my $routemap_config = ();
-       push @{$routemap_config}, "match ip address prefix-list loopbacks_ips";
-       push @{$routemap_config}, "set src $ifaceip";
-       my $routemap = { rule => $routemap_config, action => "permit" };
-       push(@{$config->{frr_routemap}->{'correct_src'}}, $routemap);
-    }
-
-    return $config;
-}
-
-sub generate_controller_zone_config {
-    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
-
-}
-
-sub on_delete_hook {
-    my ($class, $controllerid, $zone_cfg) = @_;
-
-    # verify that zone is associated to this controller
-    foreach my $id (keys %{$zone_cfg->{ids}}) {
-       my $zone = $zone_cfg->{ids}->{$id};
-       die "controller $controllerid is used by $id"
-           if (defined($zone->{controller}) && $zone->{controller} eq $controllerid);
-    }
-}
-
-sub on_update_hook {
-    my ($class, $controllerid, $controller_cfg) = @_;
-
-    # we can only have 1 bgp controller by node
-    my $local_node = PVE::INotify::nodename();
-    my $controllernb = 0;
-    foreach my $id (keys %{$controller_cfg->{ids}}) {
-        next if $id eq $controllerid;
-        my $controller = $controller_cfg->{ids}->{$id};
-        next if $controller->{type} ne "bgp";
-        next if $controller->{node} ne $local_node;
-        $controllernb++;
-        die "only 1 bgp controller can be defined" if $controllernb > 1;
-    }
-}
-
-sub generate_controller_rawconfig {
-    my ($class, $plugin_config, $config) = @_;
-    return "";
-}
-
-sub write_controller_config {
-    my ($class, $plugin_config, $config) = @_;
-    return;
-}
-
-sub reload_controller {
-    my ($class) = @_;
-    return;
-}
-
-1;
-
-
diff --git a/PVE/Network/SDN/Controllers/EvpnPlugin.pm b/PVE/Network/SDN/Controllers/EvpnPlugin.pm
deleted file mode 100644 (file)
index 727aeaa..0000000
+++ /dev/null
@@ -1,542 +0,0 @@
-package PVE::Network::SDN::Controllers::EvpnPlugin;
-
-use strict;
-use warnings;
-
-use PVE::INotify;
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::Tools qw(run_command file_set_contents file_get_contents);
-
-use PVE::Network::SDN::Controllers::Plugin;
-use PVE::Network::SDN::Zones::Plugin;
-use Net::IP;
-
-use base('PVE::Network::SDN::Controllers::Plugin');
-
-sub type {
-    return 'evpn';
-}
-
-sub properties {
-    return {
-       asn => {
-           type => 'integer',
-           description => "autonomous system number",
-           minimum => 0,
-           maximum => 4294967296
-       },
-       peers => {
-           description => "peers address list.",
-           type => 'string', format => 'ip-list'
-       },
-    };
-}
-
-sub options {
-    return {
-       'asn' => { optional => 0 },
-       'peers' => { optional => 0 },
-    };
-}
-
-# Plugin implementation
-sub generate_controller_config {
-    my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_;
-
-    my @peers;
-    @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'};
-
-    my $local_node = PVE::INotify::nodename();
-
-    my $asn = $plugin_config->{asn};
-    my $ebgp = undef;
-    my $loopback = undef;
-    my $autortas = undef;
-    my $bgprouter = find_bgp_controller($local_node, $controller_cfg);
-    if ($bgprouter) {
-       $ebgp = 1 if $plugin_config->{'asn'} ne $bgprouter->{asn};
-       $loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
-       $asn = $bgprouter->{asn} if $bgprouter->{asn};
-       $autortas = $plugin_config->{'asn'} if $ebgp;
-    }
-
-    return if !$asn;
-
-    my $bgp = $config->{frr}->{router}->{"bgp $asn"} //= {};
-
-    my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
-
-    my $remoteas = $ebgp ? "external" : $asn;
-
-    #global options
-    my @controller_config = (
-       "bgp router-id $ifaceip",
-       "no bgp default ipv4-unicast",
-       "coalesce-time 1000",
-    );
-
-    push(@{$bgp->{""}}, @controller_config) if keys %{$bgp} == 0;
-
-    @controller_config = ();
-
-    #VTEP neighbors
-    push @controller_config, "neighbor VTEP peer-group";
-    push @controller_config, "neighbor VTEP remote-as $remoteas";
-    push @controller_config, "neighbor VTEP bfd";
-
-    if($ebgp && $loopback) {
-       push @controller_config, "neighbor VTEP ebgp-multihop 10";
-       push @controller_config, "neighbor VTEP update-source $loopback";
-    }
-
-    # VTEP peers
-    foreach my $address (@peers) {
-       next if $address eq $ifaceip;
-       push @controller_config, "neighbor $address peer-group VTEP";
-    }
-
-    push(@{$bgp->{""}}, @controller_config);
-
-    # address-family l2vpn
-    @controller_config = ();
-    push @controller_config, "neighbor VTEP route-map MAP_VTEP_IN in";
-    push @controller_config, "neighbor VTEP route-map MAP_VTEP_OUT out";
-    push @controller_config, "neighbor VTEP activate";
-    push @controller_config, "advertise-all-vni";
-    push @controller_config, "autort as $autortas" if $autortas;
-    push(@{$bgp->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
-
-    my $routemap = { rule => undef, action => "permit" };
-    push(@{$config->{frr_routemap}->{'MAP_VTEP_IN'}}, $routemap );
-    push(@{$config->{frr_routemap}->{'MAP_VTEP_OUT'}}, $routemap );
-
-    return $config;
-}
-
-sub generate_controller_zone_config {
-    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
-
-    my $local_node = PVE::INotify::nodename();
-
-    my $vrf = "vrf_$id";
-    my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
-    my $exitnodes = $plugin_config->{'exitnodes'};
-    my $exitnodes_primary = $plugin_config->{'exitnodes-primary'};
-    my $advertisesubnets = $plugin_config->{'advertise-subnets'};
-    my $exitnodes_local_routing = $plugin_config->{'exitnodes-local-routing'};
-    my $rt_import;
-    $rt_import = [PVE::Tools::split_list($plugin_config->{'rt-import'})] if $plugin_config->{'rt-import'};
-
-    my $asn = $controller->{asn};
-    my @peers;
-    @peers = PVE::Tools::split_list($controller->{'peers'}) if $controller->{'peers'};
-    my $ebgp = undef;
-    my $loopback = undef;
-    my $autortas = undef;
-    my $bgprouter = find_bgp_controller($local_node, $controller_cfg);
-    if($bgprouter) {
-        $ebgp = 1 if $controller->{'asn'} ne $bgprouter->{asn};
-       $loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
-       $asn = $bgprouter->{asn} if $bgprouter->{asn};
-       $autortas = $controller->{'asn'} if $ebgp;
-    }
-
-    return if !$vrf || !$vrfvxlan || !$asn;
-
-    my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
-
-    # vrf
-    my @controller_config = ();
-    push @controller_config, "vni $vrfvxlan";
-    push(@{$config->{frr}->{vrf}->{"$vrf"}}, @controller_config);
-
-    #main vrf router
-    @controller_config = ();
-    push @controller_config, "bgp router-id $ifaceip";
-#    push @controller_config, "!";
-    push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{""}}, @controller_config);
-
-    if ($autortas) {
-       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, "route-target import $autortas:$vrfvxlan");
-       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, "route-target export $autortas:$vrfvxlan");
-    }
-
-    my $is_gateway = $exitnodes->{$local_node};
-
-    if ($is_gateway) {
-
-       if (!$exitnodes_primary || $exitnodes_primary eq $local_node) {
-           #filter default type5 route coming from other exit nodes on primary node or both nodes if no primary is defined.
-           my $routemap_config = ();
-           push @{$routemap_config}, "match evpn route-type prefix";
-           my $routemap = { rule => $routemap_config, action => "deny" };
-           unshift(@{$config->{frr_routemap}->{'MAP_VTEP_IN'}}, $routemap);
-       } elsif ($exitnodes_primary ne $local_node) {
-           my $routemap_config = ();
-           push @{$routemap_config}, "match evpn vni $vrfvxlan";
-           push @{$routemap_config}, "match evpn route-type prefix";
-           push @{$routemap_config}, "set metric 200";
-           my $routemap = { rule => $routemap_config, action => "permit" };
-           unshift(@{$config->{frr_routemap}->{'MAP_VTEP_OUT'}}, $routemap);
-        }
-
-
-       if (!$exitnodes_local_routing) {
-           @controller_config = ();
-           #import /32 routes of evpn network from vrf1 to default vrf (for packet return)
-           push @controller_config, "import vrf $vrf";
-           push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config);
-           push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config);
-
-           @controller_config = ();
-           #redistribute connected to be able to route to local vms on the gateway
-           push @controller_config, "redistribute connected";
-           push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config);
-           push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config);
-       }
-
-       @controller_config = ();
-       #add default originate to announce 0.0.0.0/0 type5 route in evpn
-       push @controller_config, "default-originate ipv4";
-       push @controller_config, "default-originate ipv6";
-       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
-    } elsif ($advertisesubnets) {
-
-       @controller_config = ();
-       #redistribute connected networks
-       push @controller_config, "redistribute connected";
-       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config);
-       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config);
-
-       @controller_config = ();
-       #advertise connected networks type5 route in evpn
-       push @controller_config, "advertise ipv4 unicast";
-       push @controller_config, "advertise ipv6 unicast";
-       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
-    }
-
-    if ($rt_import) {
-       @controller_config = ();
-       foreach my $rt (sort @{$rt_import}) {
-           push @controller_config, "route-target import $rt";
-       }
-       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
-    }
-
-    return $config;
-}
-
-sub generate_controller_vnet_config {
-    my ($class, $plugin_config, $controller, $zone, $zoneid, $vnetid, $config) = @_;
-
-    my $exitnodes = $zone->{'exitnodes'};
-    my $exitnodes_local_routing = $zone->{'exitnodes-local-routing'};
-
-    return if !$exitnodes_local_routing;
-
-    my $local_node = PVE::INotify::nodename();
-    my $is_gateway = $exitnodes->{$local_node};
-
-    return if !$is_gateway;
-
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
-    my @controller_config = ();
-    foreach my $subnetid (sort keys %{$subnets}) {
-        my $subnet = $subnets->{$subnetid};
-       my $cidr = $subnet->{cidr};
-       push @controller_config, "ip route $cidr 10.255.255.2 xvrf_$zoneid";
-    }
-    push(@{$config->{frr}->{''}}, @controller_config);
-}
-
-sub on_delete_hook {
-    my ($class, $controllerid, $zone_cfg) = @_;
-
-    # verify that zone is associated to this controller
-    foreach my $id (keys %{$zone_cfg->{ids}}) {
-       my $zone = $zone_cfg->{ids}->{$id};
-       die "controller $controllerid is used by $id"
-           if (defined($zone->{controller}) && $zone->{controller} eq $controllerid);
-    }
-}
-
-sub on_update_hook {
-    my ($class, $controllerid, $controller_cfg) = @_;
-
-    # we can only have 1 evpn controller / 1 asn by server
-
-    my $controllernb = 0;
-    foreach my $id (keys %{$controller_cfg->{ids}}) {
-       next if $id eq $controllerid;
-       my $controller = $controller_cfg->{ids}->{$id};
-       next if $controller->{type} ne "evpn";
-       $controllernb++;
-       die "only 1 global evpn controller can be defined" if $controllernb >= 1;
-    }
-}
-
-sub find_bgp_controller {
-    my ($nodename, $controller_cfg) = @_;
-
-    my $controller = undef;
-    foreach my $id  (keys %{$controller_cfg->{ids}}) {
-        $controller = $controller_cfg->{ids}->{$id};
-        next if $controller->{type} ne 'bgp';
-        next if $controller->{node} ne $nodename;
-       last;
-    }
-
-    return $controller;
-}
-
-
-sub sort_frr_config {
-    my $order = {};
-    $order->{''} = 0;
-    $order->{'vrf'} = 1;
-    $order->{'ipv4 unicast'} = 1;
-    $order->{'ipv6 unicast'} = 2;
-    $order->{'l2vpn evpn'} = 3;
-
-    my $a_val = 100;
-    my $b_val = 100;
-
-    $a_val = $order->{$a} if defined($order->{$a});
-    $b_val = $order->{$b} if defined($order->{$b});
-
-    if ($a =~ /bgp (\d+)$/) {
-       $a_val = 2;
-    }
-
-    if ($b =~ /bgp (\d+)$/) {
-       $b_val = 2;
-    }
-
-    return $a_val <=> $b_val;
-}
-
-sub generate_frr_recurse{
-   my ($final_config, $content, $parentkey, $level) = @_;
-
-   my $keylist = {};
-   $keylist->{vrf} = 1;
-   $keylist->{'address-family'} = 1;
-   $keylist->{router} = 1;
-
-   my $exitkeylist = {};
-   $exitkeylist->{vrf} = 1;
-   $exitkeylist->{'address-family'} = 1;
-
-   my $simple_exitkeylist = {};
-   $simple_exitkeylist->{router} = 1;
-
-   # FIXME: make this generic
-   my $paddinglevel = undef;
-   if ($level == 1 || $level == 2) {
-       $paddinglevel = $level - 1;
-   } elsif ($level == 3 || $level ==  4) {
-       $paddinglevel = $level - 2;
-   }
-
-   my $padding = "";
-   $padding = ' ' x ($paddinglevel) if $paddinglevel;
-
-   if (ref $content eq  'HASH') {
-       foreach my $key (sort sort_frr_config keys %$content) {
-           if ($parentkey && defined($keylist->{$parentkey})) {
-               push @{$final_config}, $padding."!";
-               push @{$final_config}, $padding."$parentkey $key";
-           } elsif ($key ne '' && !defined($keylist->{$key})) {
-               push @{$final_config}, $padding."$key";
-           }
-
-           my $option = $content->{$key};
-           generate_frr_recurse($final_config, $option, $key, $level+1);
-
-           push @{$final_config}, $padding."exit-$parentkey" if $parentkey && defined($exitkeylist->{$parentkey});
-           push @{$final_config}, $padding."exit" if $parentkey && defined($simple_exitkeylist->{$parentkey});
-       }
-    }
-
-    if (ref $content eq 'ARRAY') {
-       push @{$final_config}, map { $padding . "$_" } @$content;
-    }
-}
-
-sub generate_frr_routemap {
-   my ($final_config, $routemaps) = @_;
-
-   foreach my $id (sort keys %$routemaps) {
-
-       my $routemap = $routemaps->{$id};
-       my $order = 0;
-       foreach my $seq (@$routemap) {
-               $order++;
-               next if !defined($seq->{action});
-               my @config = ();
-               push @config, "!";
-               push @config, "route-map $id $seq->{action} $order";
-               my $rule = $seq->{rule};
-               push @config, map { " $_" } @$rule;
-               push @{$final_config}, @config;
-               push @{$final_config}, "exit";
-       }
-   }
-}
-
-sub generate_frr_list {
-    my ($final_config, $lists, $type) = @_;
-
-    my $config = [];
-
-    for my $id (sort keys %$lists) {
-       my $list = $lists->{$id};
-
-       for my $seq (sort keys %$list) {
-           my $rule = $list->{$seq};
-           push @$config, "$type $id seq $seq $rule";
-       }
-    }
-
-    if (@$config > 0) {
-       push @{$final_config}, "!", @$config;
-    }
-}
-
-sub generate_controller_rawconfig {
-    my ($class, $plugin_config, $config) = @_;
-
-    my $nodename = PVE::INotify::nodename();
-
-    my $final_config = [];
-    push @{$final_config}, "frr version 8.2.2";
-    push @{$final_config}, "frr defaults datacenter";
-    push @{$final_config}, "hostname $nodename";
-    push @{$final_config}, "log syslog informational";
-    push @{$final_config}, "service integrated-vtysh-config";
-    push @{$final_config}, "!";
-
-    if (-e "/etc/frr/frr.conf.local") {
-       my $local_conf = file_get_contents("/etc/frr/frr.conf.local");
-       parse_merge_frr_local_config($config, $local_conf);
-    }
-
-    generate_frr_recurse($final_config, $config->{frr}, undef, 0);
-    generate_frr_list($final_config, $config->{frr_access_list}, "access-list");
-    generate_frr_list($final_config, $config->{frr_prefix_list}, "ip prefix-list");
-    generate_frr_routemap($final_config, $config->{frr_routemap});
-
-    push @{$final_config}, "!";
-    push @{$final_config}, "line vty";
-    push @{$final_config}, "!";
-
-    my $rawconfig = join("\n", @{$final_config});
-
-    return if !$rawconfig;
-    return $rawconfig;
-}
-
-sub parse_merge_frr_local_config {
-    my ($config, $local_conf) = @_;
-
-    my $section = \$config->{""};
-    my $router = undef;
-    my $routemap = undef;
-    my $routemap_config = ();
-    my $routemap_action = undef;
-
-    while ($local_conf =~ /^\s*(.+?)\s*$/gm) {
-        my $line = $1;
-       $line =~ s/^\s+|\s+$//g;
-
-       if ($line =~ m/^router (.+)$/) {
-           $router = $1;
-           $section = \$config->{'frr'}->{'router'}->{$router}->{""};
-           next;
-       } elsif ($line =~ m/^vrf (.+)$/) {
-           $section = \$config->{'frr'}->{'vrf'}->{$1};
-           next;
-       } elsif ($line =~ m/address-family (.+)$/) {
-           $section = \$config->{'frr'}->{'router'}->{$router}->{'address-family'}->{$1};
-           next;
-       } elsif ($line =~ m/^route-map (.+) (permit|deny) (\d+)/) {
-           $routemap = $1;
-           $routemap_config = ();
-           $routemap_action = $2;
-           $section = \$config->{'frr_routemap'}->{$routemap};
-           next;
-       } elsif ($line =~ m/^access-list (.+) seq (\d+) (.+)$/) {
-           $config->{'frr_access_list'}->{$1}->{$2} = $3;
-           next;
-       } elsif ($line =~ m/^ip prefix-list (.+) seq (\d+) (.*)$/) {
-           $config->{'frr_prefix_list'}->{$1}->{$2} = $3;
-           next;
-       } elsif($line =~ m/^exit-address-family$/) {
-           next;
-       } elsif($line =~ m/^exit$/) {
-           if($router) {
-               $section = \$config->{''};
-               $router = undef;
-           } elsif($routemap) {
-               push(@{$$section}, { rule => $routemap_config, action => $routemap_action });
-               $section = \$config->{''};
-               $routemap = undef;
-               $routemap_action = undef;
-               $routemap_config = ();
-           }
-           next;
-       } elsif($line =~ m/!/) {
-           next;
-       }
-
-       next if !$section;
-       if($routemap) {
-           push(@{$routemap_config}, $line);
-       } else {
-           push(@{$$section}, $line);
-       }
-    }
-}
-
-sub write_controller_config {
-    my ($class, $plugin_config, $config) = @_;
-
-    my $rawconfig = $class->generate_controller_rawconfig($plugin_config, $config);
-    return if !$rawconfig;
-    return if !-d "/etc/frr";
-
-    file_set_contents("/etc/frr/frr.conf", $rawconfig);
-}
-
-sub reload_controller {
-    my ($class) = @_;
-
-    my $conf_file = "/etc/frr/frr.conf";
-    my $bin_path = "/usr/lib/frr/frr-reload.py";
-
-    if (!-e $bin_path) {
-       warn "missing $bin_path. Please install frr-pythontools package";
-       return;
-    }
-
-    my $err = sub {
-       my $line = shift;
-       if ($line =~ /ERROR:/) {
-           warn "$line \n";
-       }
-    };
-
-    if (-e $conf_file && -e $bin_path) {
-       eval {
-           run_command([$bin_path, '--stdout', '--reload', $conf_file], outfunc => {}, errfunc => $err);
-       };
-       if ($@) {
-           warn "frr reload command fail. Restarting frr.";
-           eval { run_command(['systemctl', 'restart', 'frr']); };
-       }
-    }
-}
-
-1;
-
-
diff --git a/PVE/Network/SDN/Controllers/FaucetPlugin.pm b/PVE/Network/SDN/Controllers/FaucetPlugin.pm
deleted file mode 100644 (file)
index 4f3bb5c..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-package PVE::Network::SDN::Controllers::FaucetPlugin;
-
-use strict;
-use warnings;
-use PVE::Network::SDN::Controllers::Plugin;
-use PVE::Tools;
-use PVE::INotify;
-use PVE::JSONSchema qw(get_standard_option);
-use CPAN::Meta::YAML;
-use Encode;
-
-use base('PVE::Network::SDN::Controllers::Plugin');
-
-sub type {
-    return 'faucet';
-}
-
-sub properties {
-    return {
-    };
-}
-
-# Plugin implementation
-sub generate_controller_config {
-    my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_;
-
-}
-
-sub generate_controller_zone_config {
-    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
-
-    my $dpid = $plugin_config->{'dp-id'};
-    my $dphex = printf("%x",$dpid);
-
-    my $zone_config = {
-                               dp_id => $dphex,
-                               hardware => "Open vSwitch",
-                          };
-
-    $config->{faucet}->{dps}->{$id} = $zone_config;
-
-}
-
-
-sub generate_controller_vnet_config {
-    my ($class, $plugin_config, $controller, $zone, $zoneid, $vnetid, $config) = @_;
-
-    my $mac = $plugin_config->{mac};
-    my $ipv4 = $plugin_config->{ipv4};
-    my $ipv6 = $plugin_config->{ipv6};
-    my $tag = $plugin_config->{tag};
-    my $alias = $plugin_config->{alias};
-
-    my @ips = ();
-    push @ips, $ipv4 if $ipv4;
-    push @ips, $ipv6 if $ipv6;
-
-    my $vlan_config = { vid => $tag };
-
-    $vlan_config->{description} = $alias if $alias;
-    $vlan_config->{faucet_mac} = $mac if $mac;
-    $vlan_config->{faucet_vips} = \@ips if scalar @ips > 0;
-
-    $config->{faucet}->{vlans}->{$vnetid} = $vlan_config;
-
-    push(@{$config->{faucet}->{routers}->{$zoneid}->{vlans}} , $vnetid);
-
-}
-
-sub write_controller_config {
-    my ($class, $plugin_config, $config) = @_;
-
-    my $rawconfig = encode('UTF-8', CPAN::Meta::YAML::Dump($config->{faucet}));
-
-    return if !$rawconfig;
-    return if !-d "/etc/faucet";
-
-    my $frr_config_file = "/etc/faucet/faucet.yaml";
-
-    my $writefh = IO::File->new($frr_config_file,">");
-    print $writefh $rawconfig;
-    $writefh->close();
-}
-
-sub reload_controller {
-    my ($class) = @_;
-
-    my $conf_file = "/etc/faucet/faucet.yaml";
-    my $bin_path = "/usr/bin/faucet";
-
-    if (-e $conf_file && -e $bin_path) {
-        PVE::Tools::run_command(['systemctl', 'reload', 'faucet']);
-    }
-}
-
-1;
-
diff --git a/PVE/Network/SDN/Controllers/Makefile b/PVE/Network/SDN/Controllers/Makefile
deleted file mode 100644 (file)
index 11686a3..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-SOURCES=Plugin.pm FaucetPlugin.pm EvpnPlugin.pm BgpPlugin.pm
-
-
-PERL5DIR=${DESTDIR}/usr/share/perl5
-
-.PHONY: install
-install:
-       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Controllers/$$i; done
diff --git a/PVE/Network/SDN/Controllers/Plugin.pm b/PVE/Network/SDN/Controllers/Plugin.pm
deleted file mode 100644 (file)
index c1c2cfd..0000000
+++ /dev/null
@@ -1,121 +0,0 @@
-package PVE::Network::SDN::Controllers::Plugin;
-
-use strict;
-use warnings;
-
-use PVE::Tools;
-use PVE::JSONSchema;
-use PVE::Cluster;
-
-use Data::Dumper;
-use PVE::JSONSchema qw(get_standard_option);
-use base qw(PVE::SectionConfig);
-
-PVE::Cluster::cfs_register_file('sdn/controllers.cfg',
-    sub { __PACKAGE__->parse_config(@_); },
-    sub { __PACKAGE__->write_config(@_); }
-);
-
-PVE::JSONSchema::register_standard_option('pve-sdn-controller-id', {
-    description => "The SDN controller object identifier.",
-    type => 'string', format => 'pve-sdn-controller-id',
-});
-
-PVE::JSONSchema::register_format('pve-sdn-controller-id', \&parse_sdn_controller_id);
-sub parse_sdn_controller_id {
-    my ($id, $noerr) = @_;
-
-    if ($id !~ m/^[a-z][a-z0-9_-]*[a-z0-9]$/i) {
-        return undef if $noerr;
-        die "controller ID '$id' contains illegal characters\n";
-    }
-    die "controller ID '$id' can't be more length than 64 characters\n" if length($id) > 64;
-    return $id;
-}
-
-my $defaultData = {
-
-    propertyList => {
-       type => {
-           description => "Plugin type.",
-           type => 'string', format => 'pve-configid',
-           type => 'string',
-       },
-        controller => get_standard_option('pve-sdn-controller-id',
-            { completion => \&PVE::Network::SDN::complete_sdn_controller }),
-    },
-};
-
-sub private {
-    return $defaultData;
-}
-
-sub parse_section_header {
-    my ($class, $line) = @_;
-
-    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
-        my ($type, $id) = (lc($1), $2);
-       my $errmsg = undef; # set if you want to skip whole section
-       eval { PVE::JSONSchema::pve_verify_configid($type); };
-       $errmsg = $@ if $@;
-       my $config = {}; # to return additional attributes
-       return ($type, $id, $errmsg, $config);
-    }
-    return undef;
-}
-
-sub generate_sdn_config {
-    my ($class, $plugin_config, $node, $data, $ctime) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub generate_controller_config {
-    my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_;
-
-    die "please implement inside plugin";
-}
-
-
-sub generate_controller_zone_config {
-    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub generate_controller_vnet_config {
-    my ($class, $plugin_config, $controller, $zoneid, $vnetid, $config) = @_;
-
-}
-
-sub generate_controller_rawconfig {
-    my ($class, $plugin_config, $config) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub write_controller_config {
-    my ($class, $plugin_config, $config) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub controller_reload {
-    my ($class) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub on_delete_hook {
-    my ($class, $controllerid, $zone_cfg) = @_;
-
-    # do nothing by default
-}
-
-sub on_update_hook {
-    my ($class, $controllerid, $controller_cfg) = @_;
-
-    # do nothing by default
-}
-
-1;
diff --git a/PVE/Network/SDN/Dns.pm b/PVE/Network/SDN/Dns.pm
deleted file mode 100644 (file)
index c2e153a..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-package PVE::Network::SDN::Dns;
-
-use strict;
-use warnings;
-
-use Data::Dumper;
-use JSON;
-
-use PVE::Tools qw(extract_param dir_glob_regex run_command);
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-use PVE::Network;
-
-use PVE::Network::SDN::Dns::PowerdnsPlugin;
-use PVE::Network::SDN::Dns::Plugin;
-
-PVE::Network::SDN::Dns::PowerdnsPlugin->register();
-PVE::Network::SDN::Dns::Plugin->init();
-
-
-sub sdn_dns_config {
-    my ($cfg, $id, $noerr) = @_;
-
-    die "no sdn dns ID specified\n" if !$id;
-
-    my $scfg = $cfg->{ids}->{$id};
-    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
-
-    return $scfg;
-}
-
-sub config {
-    my $config = cfs_read_file("sdn/dns.cfg");
-    return $config;
-}
-
-sub write_config {
-    my ($cfg) = @_;
-
-    cfs_write_file("sdn/dns.cfg", $cfg);
-}
-
-sub sdn_dns_ids {
-    my ($cfg) = @_;
-
-    return keys %{$cfg->{ids}};
-}
-
-sub complete_sdn_dns {
-    my ($cmdname, $pname, $cvalue) = @_;
-
-    my $cfg = PVE::Network::SDN::Dns::config();
-
-    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Dns::sdn_dns_ids($cfg) ];
-}
-
-1;
-
diff --git a/PVE/Network/SDN/Dns/Makefile b/PVE/Network/SDN/Dns/Makefile
deleted file mode 100644 (file)
index 81cd2a1..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-SOURCES=Plugin.pm PowerdnsPlugin.pm
-
-
-PERL5DIR=${DESTDIR}/usr/share/perl5
-
-.PHONY: install
-install:
-       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Dns/$$i; done
diff --git a/PVE/Network/SDN/Dns/Plugin.pm b/PVE/Network/SDN/Dns/Plugin.pm
deleted file mode 100644 (file)
index 07d0be1..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-package PVE::Network::SDN::Dns::Plugin;
-
-use strict;
-use warnings;
-
-use PVE::Tools qw(run_command);
-use PVE::JSONSchema;
-use PVE::Cluster;
-use HTTP::Request;
-use LWP::UserAgent;
-
-use Data::Dumper;
-use PVE::JSONSchema qw(get_standard_option);
-use base qw(PVE::SectionConfig);
-
-PVE::Cluster::cfs_register_file('sdn/dns.cfg',
-                                sub { __PACKAGE__->parse_config(@_); },
-                                sub { __PACKAGE__->write_config(@_); });
-
-PVE::JSONSchema::register_standard_option('pve-sdn-dns-id', {
-    description => "The SDN dns object identifier.",
-    type => 'string', format => 'pve-sdn-dns-id',
-});
-
-PVE::JSONSchema::register_format('pve-sdn-dns-id', \&parse_sdn_dns_id);
-sub parse_sdn_dns_id {
-    my ($id, $noerr) = @_;
-
-    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
-       return undef if $noerr;
-       die "dns ID '$id' contains illegal characters\n";
-    }
-    return $id;
-}
-
-my $defaultData = {
-
-    propertyList => {
-       type => {
-           description => "Plugin type.",
-           type => 'string', format => 'pve-configid',
-       },
-        ttl => { type => 'integer', optional => 1 },
-        reversev6mask => { type => 'integer', optional => 1 },
-        dns => get_standard_option('pve-sdn-dns-id',
-            { completion => \&PVE::Network::SDN::Dns::complete_sdn_dns }),
-    },
-};
-
-sub private {
-    return $defaultData;
-}
-
-sub parse_section_header {
-    my ($class, $line) = @_;
-
-    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
-        my ($type, $id) = (lc($1), $2);
-       my $errmsg = undef; # set if you want to skip whole section
-       eval { PVE::JSONSchema::pve_verify_configid($type); };
-       $errmsg = $@ if $@;
-       my $config = {}; # to return additional attributes
-       return ($type, $id, $errmsg, $config);
-    }
-    return undef;
-}
-
-
-sub add_a_record {
-    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub add_ptr_record {
-    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub del_ptr_record {
-    my ($class, $plugin_config, $zone, $ip, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub del_a_record {
-    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub verify_zone {
-    my ($class, $plugin_config, $zone, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub get_reversedns_zone {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub on_update_hook {
-    my ($class, $plugin_config) = @_;
-}
-
-1;
diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
deleted file mode 100644 (file)
index 096d131..0000000
+++ /dev/null
@@ -1,329 +0,0 @@
-package PVE::Network::SDN::Dns::PowerdnsPlugin;
-
-use strict;
-use warnings;
-use PVE::INotify;
-use PVE::Cluster;
-use PVE::Tools;
-use JSON;
-use Net::IP;
-use NetAddr::IP qw(:lower);
-use base('PVE::Network::SDN::Dns::Plugin');
-
-sub type {
-    return 'powerdns';
-}
-
-sub properties {
-    return {
-       url => {
-           type => 'string',
-       },
-       key => {
-           type => 'string',
-       },
-        reversemaskv6 => { 
-           type => 'integer' 
-        },
-    };
-}
-
-sub options {
-
-    return {
-        url => { optional => 0},
-        key => { optional => 0 },
-        ttl => { optional => 1 },
-        reversemaskv6 => { optional => 1, description => "force a different netmask for the ipv6 reverse zone name." },
-
-    };
-}
-
-# Plugin implementation
-
-sub add_a_record {
-    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
-
-    my $url = $plugin_config->{url};
-    my $key = $plugin_config->{key};
-    my $ttl = $plugin_config->{ttl} ? $plugin_config->{ttl} : 14400;
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
-
-    my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
-    my $fqdn = $hostname.".".$zone.".";
-
-    my $zonecontent = get_zone_content($plugin_config, $zone);
-    my $existing_rrset = get_zone_rrset($zonecontent, $fqdn);
-
-    my $final_records = [];
-    my $foundrecord = undef;
-    foreach my $record (@{$existing_rrset->{records}}) {
-       if($record->{content} eq $ip) {
-           $foundrecord = 1;
-           next;
-       }
-       push @$final_records, $record;
-    }
-    return if $foundrecord;
-
-    my $record = { content => $ip, 
-                   disabled => JSON::false, 
-                  name => $fqdn, 
-                   type => $type, 
-                   priority => 0 };
-
-    push @$final_records, $record;
-
-    my $rrset = { name => $fqdn, 
-                 type => $type, 
-                   ttl =>  $ttl, 
-                 changetype => "REPLACE",
-                 records => $final_records  };
-
-
-    my $params = { rrsets => [ $rrset ] };
-
-    eval {
-       PVE::Network::SDN::api_request("PATCH", "$url/zones/$zone", $headers, $params);
-    };
-
-    if ($@) {
-       die "error add $fqdn to zone $zone: $@" if !$noerr;
-    }
-}
-
-sub add_ptr_record {
-    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
-
-    my $url = $plugin_config->{url};
-    my $key = $plugin_config->{key};
-    my $ttl = $plugin_config->{ttl} ? $plugin_config->{ttl} : 14400;
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
-    $hostname .= ".";
-
-    my $reverseip = Net::IP->new($ip)->reverse_ip();
-
-    my $type = "PTR";
-
-    my $record = { content => $hostname, 
-                   disabled => JSON::false, 
-                  name => $reverseip, 
-                   type => $type, 
-                   priority => 0 };
-
-    my $rrset = { name => $reverseip, 
-                 type => $type, 
-                   ttl =>  $ttl, 
-                 changetype => "REPLACE",
-                 records => [ $record ] };
-
-
-    my $params = { rrsets => [ $rrset ] };
-
-    eval {
-       PVE::Network::SDN::api_request("PATCH", "$url/zones/$zone", $headers, $params);
-    };
-
-    if ($@) {
-       die "error add $reverseip to zone $zone: $@" if !$noerr;
-    }
-}
-
-sub del_a_record {
-    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
-
-    my $url = $plugin_config->{url};
-    my $key = $plugin_config->{key};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
-    my $fqdn = $hostname.".".$zone.".";
-    my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
-
-    my $zonecontent = get_zone_content($plugin_config, $zone);
-    my $existing_rrset = get_zone_rrset($zonecontent, $fqdn);
-
-    my $final_records = [];
-    my $foundrecord = undef;
-    foreach my $record (@{$existing_rrset->{records}}) {
-        if ($record->{content} eq $ip) {
-           $foundrecord = 1;
-           next;
-       }
-       push @$final_records, $record;
-    }
-    return if !$foundrecord;
-
-    my $rrset = {};
-   
-    if (scalar (@{$final_records}) > 0) {
-       #if we still have other records, we rewrite them without removed ip
-       $rrset = { name => $fqdn,
-                  type => $type,
-                  ttl =>  $existing_rrset->{ttl},
-                  changetype => "REPLACE",
-                  records => $final_records  };
-
-    } else {
-
-       $rrset = { name => $fqdn, 
-                  type => $type, 
-                  changetype => "DELETE",
-                   records => [] };
-    }
-
-    my $params = { rrsets => [ $rrset ] };
-
-    eval {
-       PVE::Network::SDN::api_request("PATCH", "$url/zones/$zone", $headers, $params);
-    };
-
-    if ($@) {
-       die "error delete $fqdn from zone $zone: $@" if !$noerr;
-    }
-}
-
-sub del_ptr_record {
-    my ($class, $plugin_config, $zone, $ip, $noerr) = @_;
-
-    my $url = $plugin_config->{url};
-    my $key = $plugin_config->{key};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
-
-    my $reverseip = Net::IP->new($ip)->reverse_ip();
-
-    my $type = "PTR";
-
-    my $rrset = { name => $reverseip, 
-                 type => $type, 
-                 changetype => "DELETE",
-                 records => [] };
-
-    my $params = { rrsets => [ $rrset ] };
-
-    eval {
-       PVE::Network::SDN::api_request("PATCH", "$url/zones/$zone", $headers, $params);
-    };
-
-    if ($@) {
-       die "error delete $reverseip from zone $zone: $@" if !$noerr;
-    }
-}
-
-sub verify_zone {
-    my ($class, $plugin_config, $zone, $noerr) = @_;
-
-    #verify that api is working              
-
-    my $url = $plugin_config->{url};
-    my $key = $plugin_config->{key};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
-
-    eval {
-        PVE::Network::SDN::api_request("GET", "$url/zones/$zone?rrsets=false", $headers);
-    };
-
-    if ($@) {
-        die "can't read zone $zone: $@" if !$noerr;
-    }
-}
-
-sub get_reversedns_zone {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $mask = $subnet->{mask};
-
-    my $zone = "";
-
-    if (Net::IP::ip_is_ipv4($ip)) {
-       my ($ipblock1, $ipblock2, $ipblock3, $ipblock4) = split(/\./, $ip);
-
-        my $ipv4 = NetAddr::IP->new($cidr);
-       #private addresse #powerdns built-in private zone : serve-rfc1918
-       if($ipv4->is_rfc1918()) {
-           if ($ipblock1 == 192) {
-               $zone = "168.192.in-addr.arpa.";
-           } elsif ($ipblock1 == 172) {
-               $zone = "16-31.172.in-addr.arpa.";
-           } elsif ($ipblock1 == 10) {
-               $zone = "10.in-addr.arpa.";
-           }
-               
-       } else {
-           #public ipv4 : RIPE,ARIN,AFRNIC
-           #. Delegations can be managed in IPv4 on bit boundaries (/8, /16 or /24s), and IPv6 networks can be managed on nibble boundaries (every 4 bits of the IPv6 address)
-           #One or more /24 type zones need to be created if your address space has a prefix length between /17 and /24. 
-           # If your prefix length is between /16 and /9 you will have to request one or more delegations for /16 type zones.
-
-           if ($mask <= 24) {
-               $zone = "$ipblock3.$ipblock2.$ipblock1.in-addr.arpa.";
-           } elsif ($mask <= 16) {
-               $zone = "$ipblock2.$ipblock1.in-addr.arpa.";
-           } elsif ($mask <= 8) {
-               $zone = "$ipblock1.in-addr.arpa.";
-           }
-       }
-    } else {
-       $mask = $plugin_config->{reversemaskv6} if $plugin_config->{reversemaskv6};
-       die "reverse dns zone mask need to be a multiple of 4" if ($mask % 4);
-       my $networkv6 = NetAddr::IP->new($cidr)->network();
-       $zone = Net::IP->new($networkv6)->reverse_ip();
-    }
-
-    return $zone;
-}
-
-
-sub on_update_hook {
-    my ($class, $plugin_config) = @_;
-
-    #verify that api is working
-
-    my $url = $plugin_config->{url};
-    my $key = $plugin_config->{key};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
-
-    eval {
-       PVE::Network::SDN::api_request("GET", "$url", $headers);
-    };
-
-    if ($@) {
-       die "dns api error: $@";
-    }
-}
-
-
-sub get_zone_content {
-    my ($plugin_config, $zone) = @_;
-
-    #verify that api is working              
-
-    my $url = $plugin_config->{url};
-    my $key = $plugin_config->{key};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
-
-    my $result = undef;
-    eval {
-        $result = PVE::Network::SDN::api_request("GET", "$url/zones/$zone", $headers);
-    };
-
-    if ($@) {
-        die "can't read zone $zone: $@";
-    }
-    return $result;
-}
-
-sub get_zone_rrset {
-    my ($zonecontent, $name) = @_;
-
-    my $rrsetresult = undef;
-    foreach my $rrset (@{$zonecontent->{rrsets}}) {
-       next if $rrset->{name} ne $name;
-        $rrsetresult = $rrset;
-       last; 
-    }
-    return $rrsetresult;
-}
-
-1;
-
-
diff --git a/PVE/Network/SDN/Ipams.pm b/PVE/Network/SDN/Ipams.pm
deleted file mode 100644 (file)
index e8a4b0b..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-package PVE::Network::SDN::Ipams;
-
-use strict;
-use warnings;
-
-use JSON;
-
-use PVE::Tools qw(extract_param dir_glob_regex run_command);
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-use PVE::Network;
-
-use PVE::Network::SDN::Ipams::PVEPlugin;
-use PVE::Network::SDN::Ipams::NetboxPlugin;
-use PVE::Network::SDN::Ipams::PhpIpamPlugin;
-use PVE::Network::SDN::Ipams::Plugin;
-
-PVE::Network::SDN::Ipams::PVEPlugin->register();
-PVE::Network::SDN::Ipams::NetboxPlugin->register();
-PVE::Network::SDN::Ipams::PhpIpamPlugin->register();
-PVE::Network::SDN::Ipams::Plugin->init();
-
-
-sub sdn_ipams_config {
-    my ($cfg, $id, $noerr) = @_;
-
-    die "no sdn ipam ID specified\n" if !$id;
-
-    my $scfg = $cfg->{ids}->{$id};
-    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
-
-    return $scfg;
-}
-
-sub config {
-    my $config = cfs_read_file("sdn/ipams.cfg");
-    #add default internal pve
-    $config->{ids}->{pve}->{type} = 'pve';
-    return $config;
-}
-
-sub get_plugin_config {
-    my ($vnet) = @_;
-    my $ipamid = $vnet->{ipam};
-    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-    return $ipam_cfg->{ids}->{$ipamid};
-}
-
-sub write_config {
-    my ($cfg) = @_;
-
-    cfs_write_file("sdn/ipams.cfg", $cfg);
-}
-
-sub sdn_ipams_ids {
-    my ($cfg) = @_;
-
-    return keys %{$cfg->{ids}};
-}
-
-sub complete_sdn_vnet {
-    my ($cmdname, $pname, $cvalue) = @_;
-
-    my $cfg = PVE::Network::SDN::Ipams::config();
-
-    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Vnets::sdn_ipams_ids($cfg) ];
-}
-
-1;
-
diff --git a/PVE/Network/SDN/Ipams/Makefile b/PVE/Network/SDN/Ipams/Makefile
deleted file mode 100644 (file)
index 4e7d65f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-SOURCES=Plugin.pm PhpIpamPlugin.pm NetboxPlugin.pm PVEPlugin.pm
-
-
-PERL5DIR=${DESTDIR}/usr/share/perl5
-
-.PHONY: install
-install:
-       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Ipams/$$i; done
diff --git a/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
deleted file mode 100644 (file)
index f0e7168..0000000
+++ /dev/null
@@ -1,226 +0,0 @@
-package PVE::Network::SDN::Ipams::NetboxPlugin;
-
-use strict;
-use warnings;
-use PVE::INotify;
-use PVE::Cluster;
-use PVE::Tools;
-
-use base('PVE::Network::SDN::Ipams::Plugin');
-
-sub type {
-    return 'netbox';
-}
-
-sub properties {
-    return {
-    };
-}
-
-sub options {
-
-    return {
-        url => { optional => 0},
-        token => { optional => 0 },
-    };
-}
-
-# Plugin implementation
-
-sub add_subnet {
-    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $gateway = $subnet->{gateway};
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
-
-    my $internalid = get_prefix_id($url, $cidr, $headers);
-
-    #create subnet
-    if (!$internalid) {
-
-       my $params = { prefix => $cidr };
-
-       eval {
-               my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/prefixes/", $headers, $params);
-       };
-       if ($@) {
-           die "error add subnet to ipam: $@" if !$noerr;
-       }
-    }
-   
-}
-
-sub del_subnet {
-    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $gateway = $subnet->{gateway};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
-
-    my $internalid = get_prefix_id($url, $cidr, $headers);
-    return if !$internalid;
-
-    return; #fixme: check that prefix is empty exluding gateway, before delete
-
-    eval {
-       PVE::Network::SDN::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers);
-    };
-    if ($@) {
-       die "error deleting subnet from ipam: $@" if !$noerr;
-    }
-
-}
-
-sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
-
-    my $mask = $subnet->{mask};
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $section = $plugin_config->{section};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
-    $description .= " mac:$mac" if $mac && $description;
-
-    my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
-
-    eval {
-       PVE::Network::SDN::api_request("POST", "$url/ipam/ip-addresses/", $headers, $params);
-    };
-
-    if ($@) {
-        if($is_gateway) {
-           die "error add subnet ip to ipam: ip $ip already exist: $@" if !is_ip_gateway($url, $ip, $headers) && !$noerr;
-        } else {
-           die "error add subnet ip to ipam: ip already exist: $@" if !$noerr;
-       }
-    }
-}
-
-sub update_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
-
-    my $mask = $subnet->{mask};
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $section = $plugin_config->{section};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
-    $description .= " mac:$mac" if $mac && $description;
-
-    my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
-
-    my $ip_id = get_ip_id($url, $ip, $headers);
-    die "can't find ip $ip in ipam" if !$ip_id;
-
-    eval {
-       PVE::Network::SDN::api_request("PATCH", "$url/ipam/ip-addresses/$ip_id/", $headers, $params);
-    };
-    if ($@) {
-       die "error update ip $ip : $@" if !$noerr;
-    }
-}
-
-sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
-
-    my $cidr = $subnet->{cidr};
-
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
-
-    my $internalid = get_prefix_id($url, $cidr, $headers);
-    $description .= " mac:$mac" if $mac && $description;
-
-    my $params = { dns_name => $hostname, description => $description };
-
-    my $ip = undef;
-    eval {
-       my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/prefixes/$internalid/available-ips/", $headers, $params);
-       $ip = $result->{address};
-    };
-
-    if ($@) {
-       die "can't find free ip in subnet $cidr: $@" if !$noerr;
-    }
-
-    return $ip;
-}
-
-sub del_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
-
-    return if !$ip;
-
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
-
-    my $ip_id = get_ip_id($url, $ip, $headers);
-    die "can't find ip $ip in ipam" if !$ip_id;
-
-    eval {
-       PVE::Network::SDN::api_request("DELETE", "$url/ipam/ip-addresses/$ip_id/", $headers);
-    };
-    if ($@) {
-       die "error delete ip $ip : $@" if !$noerr;
-    }
-}
-
-sub verify_api {
-    my ($class, $plugin_config) = @_;
-
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
-
-
-    eval {
-       PVE::Network::SDN::api_request("GET", "$url/ipam/aggregates/", $headers);
-    };
-    if ($@) {
-       die "Can't connect to netbox api: $@";
-    }
-}
-
-sub on_update_hook {
-    my ($class, $plugin_config) = @_;
-
-    PVE::Network::SDN::Ipams::NetboxPlugin::verify_api($class, $plugin_config);
-}
-
-#helpers
-
-sub get_prefix_id {
-    my ($url, $cidr, $headers) = @_;
-
-    my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/prefixes/?q=$cidr", $headers);
-    my $data = @{$result->{results}}[0];
-    my $internalid = $data->{id};
-    return $internalid;
-}
-
-sub get_ip_id {
-    my ($url, $ip, $headers) = @_;
-    my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers);
-    my $data = @{$result->{results}}[0];
-    my $ip_id = $data->{id};
-    return $ip_id;
-}
-
-sub is_ip_gateway {
-    my ($url, $ip, $headers) = @_;
-    my $result = PVE::Network::SDN::api_request("GET", "$url/addresses/search/$ip", $headers);
-    my $data = @{$result->{data}}[0];
-    my $description = $data->{description};
-    my $is_gateway = 1 if $description eq 'gateway';
-    return $is_gateway;
-}
-
-1;
-
-
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
deleted file mode 100644 (file)
index 3e8ffc5..0000000
+++ /dev/null
@@ -1,210 +0,0 @@
-package PVE::Network::SDN::Ipams::PVEPlugin;
-
-use strict;
-use warnings;
-use PVE::INotify;
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file);
-use PVE::Tools;
-use JSON;
-use NetAddr::IP qw(:lower);
-
-use Net::IP;
-use Digest::SHA;
-
-use base('PVE::Network::SDN::Ipams::Plugin');
-
-
-my $ipamdb_file = "priv/ipam.db";
-
-PVE::Cluster::cfs_register_file($ipamdb_file,
-                                 sub { PVE::Network::SDN::Ipams::PVEPlugin->parse_config(@_); },
-                                 sub { PVE::Network::SDN::Ipams::PVEPlugin->write_config(@_); });
-
-sub type {
-    return 'pve';
-}
-
-sub properties {
-}
-
-sub options {
-}
-
-# Plugin implementation
-
-sub add_subnet {
-    my ($class, $plugin_config, $subnetid, $subnet) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $zone = $subnet->{zone};
-    my $gateway = $subnet->{gateway};
-
-
-    cfs_lock_file($ipamdb_file, undef, sub {
-       my $db = {};
-       $db = read_db();
-
-       $db->{zones}->{$zone} = {} if !$db->{zones}->{$zone};
-       my $zonedb = $db->{zones}->{$zone};
-
-       if(!$zonedb->{subnets}->{$cidr}) {
-           #create subnet
-           $zonedb->{subnets}->{$cidr}->{ips} = {};
-           write_db($db);
-       }
-    });
-    die "$@" if $@;
-}
-
-sub del_subnet {
-    my ($class, $plugin_config, $subnetid, $subnet) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $zone = $subnet->{zone};
-
-    cfs_lock_file($ipamdb_file, undef, sub {
-
-       my $db = read_db();
-
-       my $dbzone = $db->{zones}->{$zone};
-       die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
-       my $dbsubnet = $dbzone->{subnets}->{$cidr};
-       die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
-
-       die "cannot delete subnet '$cidr', not empty\n" if keys %{$dbsubnet->{ips}} > 0;
-
-       delete $dbzone->{subnets}->{$cidr};
-
-       write_db($db);
-    });
-    die "$@" if $@;
-
-}
-
-sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $zone = $subnet->{zone};
-
-    cfs_lock_file($ipamdb_file, undef, sub {
-
-       my $db = read_db();
-       my $dbzone = $db->{zones}->{$zone};
-       die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
-       my $dbsubnet = $dbzone->{subnets}->{$cidr};
-       die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
-
-       die "IP '$ip' already exist\n" if (!$is_gateway && defined($dbsubnet->{ips}->{$ip})) || ($is_gateway && defined($dbsubnet->{ips}->{$ip}) && !defined($dbsubnet->{ips}->{$ip}->{gateway}));
-       $dbsubnet->{ips}->{$ip} = {};
-       $dbsubnet->{ips}->{$ip} = {gateway => 1} if $is_gateway;
-
-       write_db($db);
-    });
-    die "$@" if $@;
-}
-
-sub update_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
-    return;
-}
-
-sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $network = $subnet->{network};
-    my $zone = $subnet->{zone};
-    my $mask = $subnet->{mask};
-    my $freeip = undef;
-
-    cfs_lock_file($ipamdb_file, undef, sub {
-
-       my $db = read_db();
-       my $dbzone = $db->{zones}->{$zone};
-       die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
-       my $dbsubnet = $dbzone->{subnets}->{$cidr};
-       die "subnet '$cidr' doesn't exist in IPAM DB" if !$dbsubnet;
-
-       if (Net::IP::ip_is_ipv4($network) && $mask == 32) {
-           die "cannot find free IP in subnet '$cidr'\n" if defined($dbsubnet->{ips}->{$network});
-           $freeip = $network;
-       } else {
-           my $iplist = NetAddr::IP->new($cidr);
-           my $lastip = $iplist->last()->canon();
-           $iplist++ if Net::IP::ip_is_ipv4($network); #skip network address for ipv4
-           while(1) {
-               my $ip = $iplist->canon();
-               if (defined($dbsubnet->{ips}->{$ip})) {
-                   last if $ip eq $lastip;
-                   $iplist++;
-                   next;
-               } 
-               $freeip = $ip;
-               last;
-           }
-       }
-
-       die "can't find free ip in subnet '$cidr'\n" if !$freeip;
-
-       $dbsubnet->{ips}->{$freeip} = {};
-
-       write_db($db);
-    });
-    die "$@" if $@;
-
-    return "$freeip/$mask";
-}
-
-sub del_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $zone = $subnet->{zone};
-
-    cfs_lock_file($ipamdb_file, undef, sub {
-
-       my $db = read_db();
-       die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone};
-       my $dbzone = $db->{zones}->{$zone};
-       die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr};
-       my $dbsubnet = $dbzone->{subnets}->{$cidr};
-
-       die "IP '$ip' does not exist in IPAM DB\n" if !defined($dbsubnet->{ips}->{$ip});
-       delete $dbsubnet->{ips}->{$ip};
-
-       write_db($db);
-    });
-    die "$@" if $@;
-}
-
-#helpers
-
-sub read_db {
-    my $db = cfs_read_file($ipamdb_file);
-    return $db;
-}
-
-sub write_db {
-    my ($cfg) = @_;
-
-    my $json = to_json($cfg);
-    cfs_write_file($ipamdb_file, $json);
-}
-
-sub write_config {
-    my ($class, $filename, $cfg) = @_;
-
-    return $cfg;
-}
-
-sub parse_config {
-    my ($class, $filename, $raw) = @_;
-
-    $raw = '{}' if !defined($raw) ||$raw eq '';
-    my $cfg = from_json($raw);
-
-    return $cfg;
-}
-
-1;
diff --git a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
deleted file mode 100644 (file)
index ad5286b..0000000
+++ /dev/null
@@ -1,259 +0,0 @@
-package PVE::Network::SDN::Ipams::PhpIpamPlugin;
-
-use strict;
-use warnings;
-use PVE::INotify;
-use PVE::Cluster;
-use PVE::Tools;
-
-use base('PVE::Network::SDN::Ipams::Plugin');
-
-sub type {
-    return 'phpipam';
-}
-
-sub properties {
-    return {
-       url => {
-           type => 'string',
-       },
-       token => {
-           type => 'string',
-       },
-       section => {
-           type => 'integer',
-       },
-    };
-}
-
-sub options {
-
-    return {
-        url => { optional => 0},
-        token => { optional => 0 },
-        section => { optional => 0 },
-    };
-}
-
-# Plugin implementation
-
-sub add_subnet {
-    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $network = $subnet->{network};
-    my $mask = $subnet->{mask};
-
-    my $gateway = $subnet->{gateway};
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $section = $plugin_config->{section};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
-
-    #search subnet
-    my $internalid = get_prefix_id($url, $cidr, $headers);
-
-    #create subnet
-    if (!$internalid) {
-       my $params = { subnet => $network,
-                  mask => $mask,
-                  sectionId => $section,
-                 };
-
-       eval {
-               PVE::Network::SDN::api_request("POST", "$url/subnets/", $headers, $params);
-       };
-       if ($@) {
-           die "error add subnet to ipam: $@" if !$noerr;
-       }
-    }
-
-}
-
-sub del_subnet {
-    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $section = $plugin_config->{section};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
-
-    my $internalid = get_prefix_id($url, $cidr, $headers);
-    return if !$internalid;
-
-    return; #fixme: check that prefix is empty exluding gateway, before delete
-
-    eval {
-       PVE::Network::SDN::api_request("DELETE", "$url/subnets/$internalid", $headers);
-    };
-    if ($@) {
-       die "error deleting subnet from ipam: $@" if !$noerr;
-    }
-
-}
-
-sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $section = $plugin_config->{section};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
-
-    my $internalid = get_prefix_id($url, $cidr, $headers);
-
-    my $params = { ip => $ip,
-                  subnetId => $internalid,
-                  hostname => $hostname,
-                  description => $description,
-                 };
-    $params->{is_gateway} = 1 if $is_gateway;
-    $params->{mac} = $mac if $mac;
-
-    eval {
-       PVE::Network::SDN::api_request("POST", "$url/addresses/", $headers, $params);
-    };
-
-    if ($@) {
-       if($is_gateway) {
-           die "error add subnet ip to ipam: ip $ip already exist: $@" if !is_ip_gateway($url, $ip, $headers) && !$noerr;
-       } else {
-           die "error add subnet ip to ipam: ip $ip already exist: $@" if !$noerr;
-       }
-    }
-}
-
-sub update_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $section = $plugin_config->{section};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
-
-    my $ip_id = get_ip_id($url, $ip, $headers);
-    die "can't find ip addresse in ipam" if !$ip_id;
-
-    my $params = { 
-                  hostname => $hostname,
-                  description => $description,
-                 };
-    $params->{is_gateway} = 1 if $is_gateway;
-    $params->{mac} = $mac if $mac;
-
-    eval {
-       PVE::Network::SDN::api_request("PATCH", "$url/addresses/$ip_id", $headers, $params);
-    };
-
-    if ($@) {
-       die "ipam: error update subnet ip $ip: $@" if !$noerr;
-    }
-}
-
-sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
-
-    my $cidr = $subnet->{cidr};  
-    my $mask = $subnet->{mask};  
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $section = $plugin_config->{section};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
-
-    my $internalid = get_prefix_id($url, $cidr, $headers);
-
-    my $params = { hostname => $hostname,
-                  description => $description,
-                 };
-
-    $params->{mac} = $mac if $mac;
-
-    my $ip = undef;
-    eval {
-       my $result = PVE::Network::SDN::api_request("POST", "$url/addresses/first_free/$internalid/", $headers, $params);
-       $ip = $result->{data};
-    };
-
-    if ($@) {
-        die "can't find free ip in subnet $cidr: $@" if !$noerr;
-    }
-
-    return "$ip/$mask" if $ip && $mask;
-}
-
-sub del_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
-
-    return if !$ip;
-
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
-
-    my $ip_id = get_ip_id($url, $ip, $headers);
-    return if !$ip_id;
-
-    eval {
-       PVE::Network::SDN::api_request("DELETE", "$url/addresses/$ip_id", $headers);
-    };
-    if ($@) {
-       die "error delete ip $ip: $@" if !$noerr;
-    }
-}
-
-sub verify_api {
-    my ($class, $plugin_config) = @_;
-
-    my $url = $plugin_config->{url};
-    my $token = $plugin_config->{token};
-    my $sectionid = $plugin_config->{section};
-    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
-
-    eval {
-       PVE::Network::SDN::api_request("GET", "$url/sections/$sectionid", $headers);
-    };
-    if ($@) {
-       die "Can't connect to phpipam api: $@";
-    }
-}
-
-sub on_update_hook {
-    my ($class, $plugin_config) = @_;
-
-    PVE::Network::SDN::Ipams::PhpIpamPlugin::verify_api($class, $plugin_config);
-}
-
-
-#helpers
-
-sub get_prefix_id {
-    my ($url, $cidr, $headers) = @_;
-
-    my $result = PVE::Network::SDN::api_request("GET", "$url/subnets/cidr/$cidr", $headers);
-    my $data = @{$result->{data}}[0];
-    my $internalid = $data->{id};
-    return $internalid;
-}
-
-sub get_ip_id {
-    my ($url, $ip, $headers) = @_;
-    my $result = PVE::Network::SDN::api_request("GET", "$url/addresses/search/$ip", $headers);
-    my $data = @{$result->{data}}[0];
-    my $ip_id = $data->{id};
-    return $ip_id;
-}
-
-sub is_ip_gateway {
-    my ($url, $ip, $headers) = @_;
-    my $result = PVE::Network::SDN::api_request("GET", "$url/addresses/search/$ip", $headers);
-    my $data = @{$result->{data}}[0];
-    my $is_gateway = $data->{is_gateway};
-    return $is_gateway;
-}
-
-1;
-
-
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
deleted file mode 100644 (file)
index c96eeda..0000000
+++ /dev/null
@@ -1,111 +0,0 @@
-package PVE::Network::SDN::Ipams::Plugin;
-
-use strict;
-use warnings;
-
-use PVE::Tools qw(run_command);
-use PVE::JSONSchema;
-use PVE::Cluster;
-use HTTP::Request;
-use LWP::UserAgent;
-use JSON;
-
-use Data::Dumper;
-use PVE::JSONSchema qw(get_standard_option);
-use base qw(PVE::SectionConfig);
-
-PVE::Cluster::cfs_register_file('sdn/ipams.cfg',
-                                sub { __PACKAGE__->parse_config(@_); },
-                                sub { __PACKAGE__->write_config(@_); });
-
-PVE::JSONSchema::register_standard_option('pve-sdn-ipam-id', {
-    description => "The SDN ipam object identifier.",
-    type => 'string', format => 'pve-sdn-ipam-id',
-});
-
-PVE::JSONSchema::register_format('pve-sdn-ipam-id', \&parse_sdn_ipam_id);
-sub parse_sdn_ipam_id {
-    my ($id, $noerr) = @_;
-
-    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
-       return undef if $noerr;
-       die "ipam ID '$id' contains illegal characters\n";
-    }
-    return $id;
-}
-
-my $defaultData = {
-
-    propertyList => {
-       type => {
-           description => "Plugin type.",
-           type => 'string', format => 'pve-configid',
-           type => 'string',
-       },
-        ipam => get_standard_option('pve-sdn-ipam-id',
-            { completion => \&PVE::Network::SDN::Ipams::complete_sdn_ipam }),
-    },
-};
-
-sub private {
-    return $defaultData;
-}
-
-sub parse_section_header {
-    my ($class, $line) = @_;
-
-    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
-        my ($type, $id) = (lc($1), $2);
-       my $errmsg = undef; # set if you want to skip whole section
-       eval { PVE::JSONSchema::pve_verify_configid($type); };
-       $errmsg = $@ if $@;
-       my $config = {}; # to return additional attributes
-       return ($type, $id, $errmsg, $config);
-    }
-    return undef;
-}
-
-
-sub add_subnet {
-    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub del_subnet {
-    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub update_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
-    # only update ip attributes (mac,hostname,..). Don't change the ip addresses itself, as some ipam
-    # don't allow ip address change without del/add
-
-    die "please implement inside plugin";
-}
-
-sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub del_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub on_update_hook {
-    my ($class, $plugin_config)  = @_;
-}
-
-1;
diff --git a/PVE/Network/SDN/Makefile b/PVE/Network/SDN/Makefile
deleted file mode 100644 (file)
index 92cfcd0..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm Dns.pm
-
-
-PERL5DIR=${DESTDIR}/usr/share/perl5
-
-.PHONY: install
-install:
-       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/$$i; done
-       make -C Controllers install
-       make -C Zones install
-       make -C Ipams install
-       make -C Dns install
-
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
deleted file mode 100644 (file)
index 15b370f..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-package PVE::Network::SDN::SubnetPlugin;
-
-use strict;
-use warnings;
-
-use Net::IP;
-use Net::Subnet qw(subnet_matcher);
-
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-use PVE::Exception qw(raise raise_param_exc);
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::Network::SDN::Ipams;
-use PVE::Network::SDN::Vnets;
-
-use base qw(PVE::SectionConfig);
-
-PVE::Cluster::cfs_register_file('sdn/subnets.cfg',
-                                 sub { __PACKAGE__->parse_config(@_); },
-                                 sub { __PACKAGE__->write_config(@_); });
-
-PVE::JSONSchema::register_standard_option('pve-sdn-subnet-id', {
-    description => "The SDN subnet object identifier.",
-    type => 'string', format => 'pve-sdn-subnet-id',
-    type => 'string'
-});
-
-PVE::JSONSchema::register_format('pve-sdn-subnet-id', \&parse_sdn_subnet_id);
-sub parse_sdn_subnet_id {
-    my ($id, $noerr) = @_;
-
-    my $cidr = "";
-    if($id =~ /\//) {
-       $cidr = $id;
-    } else {
-       my ($zone, $ip, $mask) = split(/-/, $id);
-       $cidr = "$ip/$mask";
-    }
-
-    if (!(PVE::JSONSchema::pve_verify_cidrv4($cidr, 1) ||
-          PVE::JSONSchema::pve_verify_cidrv6($cidr, 1)))
-    {
-        return undef if $noerr;
-        die "value does not look like a valid CIDR network\n";
-    }
-    return $id;
-}
-
-my $defaultData = {
-
-    propertyList => {
-        subnet => get_standard_option('pve-sdn-subnet-id',
-            { completion => \&PVE::Network::SDN::Subnets::complete_sdn_subnet }),
-    },
-};
-
-sub type {
-    return 'subnet';
-}
-
-sub private {
-    return $defaultData;
-}
-
-sub properties {
-    return {
-        vnet => {
-            type => 'string',
-            description => "associated vnet",
-        },
-        gateway => {
-            type => 'string', format => 'ip',
-            description => "Subnet Gateway: Will be assign on vnet for layer3 zones",
-        },
-        snat => {
-            type => 'boolean',
-            description => "enable masquerade for this subnet if pve-firewall",
-        },
-#      #cloudinit, dhcp options
-#        routes => {
-#            type => 'string',
-#            description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
-#        },
-        dnszoneprefix => {
-            type => 'string', format => 'dns-name',
-            description => "dns domain zone prefix  ex: 'adm' -> <hostname>.adm.mydomain.com",
-        },
-    };
-}
-
-sub options {
-    return {
-       vnet => { optional => 0 },
-       gateway => { optional => 1 },
-#      routes => { optional => 1 },
-       snat => { optional => 1 },
-       dnszoneprefix => { optional => 1 },
-    };
-}
-
-sub on_update_hook {
-    my ($class, $zone, $subnetid, $subnet, $old_subnet) = @_;
-
-    my $cidr = $subnet->{cidr};
-    my $mask = $subnet->{mask};
-
-    my $subnet_matcher = subnet_matcher($cidr);
-
-    my $vnetid = $subnet->{vnet};
-    my $gateway = $subnet->{gateway};
-    my $ipam = $zone->{ipam};
-    my $dns = $zone->{dns};
-    my $dnszone = $zone->{dnszone};
-    my $reversedns = $zone->{reversedns};
-
-    my $old_gateway = $old_subnet->{gateway} if $old_subnet;
-    my $mac = undef;
-
-    if($vnetid) {
-       my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
-       raise_param_exc({ vnet => "$vnetid don't exist"}) if !$vnet;
-       raise_param_exc({ vnet => "you can't add a subnet on a vlanaware vnet"}) if $vnet->{vlanaware};
-       $mac = $vnet->{mac};
-    }
-
-    my $pointopoint = 1 if Net::IP::ip_is_ipv4($gateway) && $mask == 32;
-
-    #for /32 pointopoint, we allow gateway outside the subnet
-    raise_param_exc({ gateway => "$gateway is not in subnet $cidr"}) if $gateway && !$subnet_matcher->($gateway) && !$pointopoint;
-
-
-    if ($ipam) {
-       PVE::Network::SDN::Subnets::add_subnet($zone, $subnetid, $subnet);
-
-       #don't register gateway for pointopoint
-       return if $pointopoint;
-
-       #delete gateway on removal
-       if (!defined($gateway) && $old_gateway) {
-           eval {
-               PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
-           };
-           warn if $@;
-       }
-        if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
-           my $hostname = "$vnetid-gw";
-           my $description = "gateway";
-           PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway, $hostname, $mac, $description, 1);
-       }
-
-       #delete old gateway after update
-       if($gateway && $old_gateway && $gateway ne $old_gateway) {
-           eval {
-               PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
-           };
-           warn if $@;
-       }
-    }
-}
-
-sub on_delete_hook {
-    my ($class, $subnetid, $subnet_cfg, $vnet_cfg) = @_;
-
-    return;
-}
-
-1;
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
deleted file mode 100644 (file)
index 6bb42e5..0000000
+++ /dev/null
@@ -1,375 +0,0 @@
-package PVE::Network::SDN::Subnets;
-
-use strict;
-use warnings;
-
-use Net::Subnet qw(subnet_matcher);
-use Net::IP;
-use NetAddr::IP qw(:lower);
-
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-use PVE::Network::SDN::Dns;
-use PVE::Network::SDN::Ipams;
-
-use PVE::Network::SDN::SubnetPlugin;
-PVE::Network::SDN::SubnetPlugin->register();
-PVE::Network::SDN::SubnetPlugin->init();
-
-sub sdn_subnets_config {
-    my ($cfg, $id, $noerr) = @_;
-
-    die "no sdn subnet ID specified\n" if !$id;
-
-    my $scfg = $cfg->{ids}->{$id};
-    die "sdn subnet '$id' does not exist\n" if (!$noerr && !$scfg);
-
-    if($scfg) {
-       my ($zone, $network, $mask) = split(/-/, $id);
-       $scfg->{cidr} = "$network/$mask";
-       $scfg->{zone} = $zone;
-       $scfg->{network} = $network;
-       $scfg->{mask} = $mask;
-    }
-
-    return $scfg;
-}
-
-sub config {
-    my $config = cfs_read_file("sdn/subnets.cfg");
-}
-
-sub write_config {
-    my ($cfg) = @_;
-
-    cfs_write_file("sdn/subnets.cfg", $cfg);
-}
-
-sub sdn_subnets_ids {
-    my ($cfg) = @_;
-
-    return sort keys %{$cfg->{ids}};
-}
-
-sub complete_sdn_subnet {
-    my ($cmdname, $pname, $cvalue) = @_;
-
-    my $cfg = PVE::Network::SDN::Subnets::config();
-
-    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Subnets::sdn_subnets_ids($cfg) ];
-}
-
-sub get_subnet {
-    my ($subnetid, $running) = @_;
-
-    my $cfg = {};
-    if($running) {
-       my $cfg = PVE::Network::SDN::running_config();
-       $cfg = $cfg->{subnets};
-    } else {
-       $cfg = PVE::Network::SDN::Subnets::config();
-    }
-
-    my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
-    return $subnet;
-}
-
-sub find_ip_subnet {
-    my ($ip, $mask, $subnets) = @_;
-
-    my $subnet = undef;
-    my $subnetid = undef;
-
-    foreach my $id (sort keys %{$subnets}) {
-
-       next if $mask ne $subnets->{$id}->{mask};
-       my $cidr = $subnets->{$id}->{cidr};
-       my $subnet_matcher = subnet_matcher($cidr);
-       next if !$subnet_matcher->($ip);
-       $subnet = $subnets->{$id};
-       $subnetid = $id;
-       last;
-    }
-    die  "can't find any subnet for ip $ip" if !$subnet;
-
-    return ($subnetid, $subnet);
-}
-
-sub verify_dns_zone {
-    my ($zone, $dns) = @_;
-
-    return if !$zone || !$dns;
-
-    my $dns_cfg = PVE::Network::SDN::Dns::config();
-    my $plugin_config = $dns_cfg->{ids}->{$dns};
-    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
-    $plugin->verify_zone($plugin_config, $zone);
-}
-
-sub get_reversedns_zone {
-    my ($subnetid, $subnet, $dns, $ip) = @_;
-
-    return if !$subnetid || !$dns || !$ip;
-
-    my $dns_cfg = PVE::Network::SDN::Dns::config();
-    my $plugin_config = $dns_cfg->{ids}->{$dns};
-    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
-    $plugin->get_reversedns_zone($plugin_config, $subnetid, $subnet, $ip);
-}
-
-sub add_dns_record {
-    my ($zone, $dns, $hostname, $ip) = @_;
-    return if !$zone || !$dns || !$hostname || !$ip;
-
-    my $dns_cfg = PVE::Network::SDN::Dns::config();
-    my $plugin_config = $dns_cfg->{ids}->{$dns};
-    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
-    $plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
-
-}
-
-sub add_dns_ptr_record {
-    my ($reversezone, $zone, $dns, $hostname, $ip) = @_;
-
-    return if !$zone || !$reversezone || !$dns || !$hostname || !$ip;
-
-    $hostname .= ".$zone";
-    my $dns_cfg = PVE::Network::SDN::Dns::config();
-    my $plugin_config = $dns_cfg->{ids}->{$dns};
-    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
-    $plugin->add_ptr_record($plugin_config, $reversezone, $hostname, $ip);
-}
-
-sub del_dns_record {
-    my ($zone, $dns, $hostname, $ip) = @_;
-
-    return if !$zone || !$dns || !$hostname || !$ip;
-
-    my $dns_cfg = PVE::Network::SDN::Dns::config();
-    my $plugin_config = $dns_cfg->{ids}->{$dns};
-    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
-    $plugin->del_a_record($plugin_config, $zone, $hostname, $ip);
-}
-
-sub del_dns_ptr_record {
-    my ($reversezone, $dns, $ip) = @_;
-
-    return if !$reversezone || !$dns || !$ip;
-
-    my $dns_cfg = PVE::Network::SDN::Dns::config();
-    my $plugin_config = $dns_cfg->{ids}->{$dns};
-    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
-    $plugin->del_ptr_record($plugin_config, $reversezone, $ip);
-}
-
-sub add_subnet {
-    my ($zone, $subnetid, $subnet) = @_;
-
-    my $ipam = $zone->{ipam};
-    return if !$ipam;
-    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-    my $plugin_config = $ipam_cfg->{ids}->{$ipam};
-    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-    $plugin->add_subnet($plugin_config, $subnetid, $subnet);
-}
-
-sub del_subnet {
-    my ($zone, $subnetid, $subnet) = @_;
-
-    my $ipam = $zone->{ipam};
-    return if !$ipam;
-    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-    my $plugin_config = $ipam_cfg->{ids}->{$ipam};
-    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-    $plugin->del_subnet($plugin_config, $subnetid, $subnet);
-}
-
-sub next_free_ip {
-    my ($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns) = @_;
-
-    my $cidr = undef;
-    my $ip = undef;
-    $description = '' if !$description;
-
-    my $ipamid = $zone->{ipam};
-    my $dns = $zone->{dns};
-    my $dnszone = $zone->{dnszone};
-    my $reversedns = $zone->{reversedns};
-    my $dnszoneprefix = $subnet->{dnszoneprefix};
-
-    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
-
-    #verify dns zones before ipam
-    verify_dns_zone($dnszone, $dns) if !$skipdns;
-
-    if($ipamid) {
-       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});
-       eval {
-           $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $description);
-           ($ip, undef) = split(/\//, $cidr);
-       };
-       die $@ if $@;
-    }
-
-    eval {
-       my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
-
-       if(!$skipdns) {
-           #add dns
-           add_dns_record($dnszone, $dns, $hostname, $ip);
-           #add reverse dns
-           add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
-       }
-    };
-    if ($@) {
-       #rollback
-       my $err = $@;
-       eval {
-           PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname)
-       };
-       die $err;
-    }
-    return $cidr;
-}
-
-sub add_ip {
-    my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $skipdns) = @_;
-
-    return if !$subnet || !$ip; 
-
-    my $ipaddr = NetAddr::IP->new($ip);
-    $ip = $ipaddr->canon();
-
-    my $ipamid = $zone->{ipam};
-    my $dns = $zone->{dns};
-    my $dnszone = $zone->{dnszone};
-    my $reversedns = $zone->{reversedns};
-    my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
-    my $dnszoneprefix = $subnet->{dnszoneprefix};
-
-    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
-
-    #verify dns zones before ipam
-    if(!$skipdns) {
-       verify_dns_zone($dnszone, $dns);
-       verify_dns_zone($reversednszone, $reversedns);
-    }
-
-    if ($ipamid) {
-
-       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});
-
-       eval {
-           $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway);
-       };
-       die $@ if $@;
-    }
-
-    eval {
-       if(!$skipdns) {
-           #add dns
-           add_dns_record($dnszone, $dns, $hostname, $ip);
-           #add reverse dns
-           add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
-       }
-    };
-    if ($@) {
-       #rollback
-       my $err = $@;
-       eval {
-           PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname)
-       };
-       die $err;
-    }
-}
-
-sub update_ip {
-    my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns) = @_;
-
-    return if !$subnet || !$ip; 
-
-    my $ipaddr = NetAddr::IP->new($ip);
-    $ip = $ipaddr->canon();
-
-    my $ipamid = $zone->{ipam};
-    my $dns = $zone->{dns};
-    my $dnszone = $zone->{dnszone};
-    my $reversedns = $zone->{reversedns};
-    my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
-    my $dnszoneprefix = $subnet->{dnszoneprefix};
-
-    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
-
-    #verify dns zones before ipam
-    if(!$skipdns) {
-       verify_dns_zone($dnszone, $dns);
-       verify_dns_zone($reversednszone, $reversedns);
-    }
-
-    if ($ipamid) {
-       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});
-       eval {
-           $plugin->update_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description);
-       };
-       die $@ if $@;
-    }
-
-    return if $hostname eq $oldhostname;
-
-    eval {
-       if(!$skipdns) {
-           #add dns
-           del_dns_record($dnszone, $dns, $oldhostname, $ip);
-           add_dns_record($dnszone, $dns, $hostname, $ip);
-           #add reverse dns
-           del_dns_ptr_record($reversednszone, $reversedns, $ip);
-           add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
-       }
-    };
-}
-
-sub del_ip {
-    my ($zone, $subnetid, $subnet, $ip, $hostname, $skipdns) = @_;
-
-    return if !$subnet || !$ip;
-
-    my $ipaddr = NetAddr::IP->new($ip);
-    $ip = $ipaddr->canon();
-
-    my $ipamid = $zone->{ipam};
-    my $dns = $zone->{dns};
-    my $dnszone = $zone->{dnszone};
-    my $reversedns = $zone->{reversedns};
-    my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
-    my $dnszoneprefix = $subnet->{dnszoneprefix};
-    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
-
-    if(!$skipdns) {
-       verify_dns_zone($dnszone, $dns);
-       verify_dns_zone($reversednszone, $reversedns);
-    }
-
-    if ($ipamid) {
-       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});
-       $plugin->del_ip($plugin_config, $subnetid, $subnet, $ip);
-    }
-
-    eval {
-       if(!$skipdns) {
-           del_dns_record($dnszone, $dns, $hostname, $ip);
-           del_dns_ptr_record($reversednszone, $reversedns, $ip);
-       }
-    };
-    if ($@) {
-       warn $@;
-    }
-}
-
-1;
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
deleted file mode 100644 (file)
index 062904c..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-package PVE::Network::SDN::VnetPlugin;
-
-use strict;
-use warnings;
-
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-use PVE::Exception qw(raise raise_param_exc);
-use PVE::JSONSchema qw(get_standard_option);
-
-use PVE::SectionConfig;
-use base qw(PVE::SectionConfig);
-
-PVE::Cluster::cfs_register_file('sdn/vnets.cfg',
-                                 sub { __PACKAGE__->parse_config(@_); },
-                                 sub { __PACKAGE__->write_config(@_); });
-
-PVE::JSONSchema::register_standard_option('pve-sdn-vnet-id', {
-    description => "The SDN vnet object identifier.",
-    type => 'string', format => 'pve-sdn-vnet-id',
-});
-
-PVE::JSONSchema::register_format('pve-sdn-vnet-id', \&parse_sdn_vnet_id);
-sub parse_sdn_vnet_id {
-    my ($id, $noerr) = @_;
-
-    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
-        return undef if $noerr;
-        die "vnet ID '$id' contains illegal characters\n";
-    }
-    die "vnet ID '$id' can't be more length than 8 characters\n" if length($id) > 8;
-    return $id;
-}
-
-my $defaultData = {
-
-    propertyList => {
-        vnet => get_standard_option('pve-sdn-vnet-id',
-            { completion => \&PVE::Network::SDN::Vnets::complete_sdn_vnet }),
-    },
-};
-
-sub type {
-    return 'vnet';
-}
-
-sub private {
-    return $defaultData;
-}
-
-sub properties {
-    return {
-       zone => {
-            type => 'string',
-            description => "zone id",
-       },
-        type => {
-            description => "Type",
-            optional => 1,
-        },
-       tag => {
-            type => 'integer',
-            description => "vlan or vxlan id",
-       },
-       vlanaware => {
-           type => 'boolean',
-           description => 'Allow vm VLANs to pass through this vnet.',
-       },
-        alias => {
-            type => 'string',
-            description => "alias name of the vnet",
-            pattern => qr/[\(\)-_.\w\d\s]{0,256}/i,
-            maxLength => 256,
-           optional => 1,
-        },
-    };
-}
-
-sub options {
-    return {
-        zone => { optional => 0},
-        tag => { optional => 1},
-        alias => { optional => 1 },
-        vlanaware => { optional => 1 },
-    };
-}
-
-sub on_delete_hook {
-    my ($class, $vnetid, $vnet_cfg) = @_;
-
-    #verify if subnets are associated
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
-    raise_param_exc({ vnet => "Can't delete vnet if subnets exists"}) if $subnets;
-}
-
-sub on_update_hook {
-    my ($class, $vnetid, $vnet_cfg) = @_;
-
-    my $vnet = $vnet_cfg->{ids}->{$vnetid};
-    my $tag = $vnet->{tag};
-    my $vlanaware = $vnet->{vlanaware};
-
-    #don't allow vlanaware change if subnets are defined
-    if($vnet->{vlanaware}) {
-       my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
-       raise_param_exc({ vlanaware => "vlanaware vnet is not compatible with subnets"}) if $subnets;
-    }
-}
-
-1;
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
deleted file mode 100644 (file)
index 0b32c58..0000000
+++ /dev/null
@@ -1,163 +0,0 @@
-package PVE::Network::SDN::Vnets;
-
-use strict;
-use warnings;
-
-use Net::IP;
-
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-use PVE::Network::SDN;
-use PVE::Network::SDN::Subnets;
-use PVE::Network::SDN::Zones;
-
-use PVE::Network::SDN::VnetPlugin;
-PVE::Network::SDN::VnetPlugin->register();
-PVE::Network::SDN::VnetPlugin->init();
-
-sub sdn_vnets_config {
-    my ($cfg, $id, $noerr) = @_;
-
-    die "no sdn vnet ID specified\n" if !$id;
-
-    my $scfg = $cfg->{ids}->{$id};
-    die "sdn vnet '$id' does not exist\n" if (!$noerr && !$scfg);
-
-    return $scfg;
-}
-
-sub config {
-    return cfs_read_file("sdn/vnets.cfg");
-}
-
-sub write_config {
-    my ($cfg) = @_;
-
-    cfs_write_file("sdn/vnets.cfg", $cfg);
-}
-
-sub sdn_vnets_ids {
-    my ($cfg) = @_;
-
-    return sort keys %{$cfg->{ids}};
-}
-
-sub complete_sdn_vnet {
-    my ($cmdname, $pname, $cvalue) = @_;
-
-    my $cfg = PVE::Network::SDN::Vnets::config();
-
-    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Vnets::sdn_vnet_ids($cfg) ];
-}
-
-sub get_vnet {
-    my ($vnetid, $running) = @_;
-
-    return if !$vnetid;
-
-    my $scfg = {};
-    if($running) {
-       my $cfg = PVE::Network::SDN::running_config();
-       $scfg = $cfg->{vnets};
-    } else {
-       $scfg = PVE::Network::SDN::Vnets::config();
-    }
-
-    my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($scfg, $vnetid, 1);
-
-    return $vnet;
-}
-
-sub get_subnets {
-    my ($vnetid) = @_;
-
-    return if !$vnetid;
-
-    my $subnets = undef;
-    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
-    foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
-       my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
-       next if !$subnet->{vnet} || $subnet->{vnet} ne $vnetid;
-       $subnets->{$subnetid} = $subnet;
-    }
-    return $subnets;
-
-}
-
-sub get_subnet_from_vnet_cidr {
-    my ($vnetid, $cidr) = @_;
-
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
-    my $zoneid = $vnet->{zone};
-    my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
-
-    my ($ip, $mask) = split(/\//, $cidr);
-    die "ip address is not in cidr format" if !$mask;
-
-    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets);
-
-    return ($zone, $subnetid, $subnet, $ip);
-}
-
-sub get_next_free_cidr {
-    my ($vnetid, $hostname, $mac, $description, $ipversion, $skipdns) = @_;
-
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
-    my $zoneid = $vnet->{zone};
-    my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
-
-    return if !$zone->{ipam};
-
-    $ipversion = 4 if !$ipversion;
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
-    my $ip = undef;
-    my $subnetcount = 0;
-
-    foreach my $subnetid (sort keys %{$subnets}) {
-        my $subnet = $subnets->{$subnetid};
-       my $network = $subnet->{network};
-
-       next if $ipversion != Net::IP::ip_get_version($network);
-       $subnetcount++;
-
-       eval {
-           $ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns);
-       };
-       warn $@ if $@;
-       last if $ip;
-    }
-    die "can't find any free ip" if !$ip && $subnetcount > 0;
-
-    return $ip;
-}
-
-sub add_cidr {
-    my ($vnetid, $cidr, $hostname, $mac, $description, $skipdns) = @_;
-
-    return if !$vnetid;
-    
-    my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr);
-    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, undef, $skipdns);
-}
-
-sub update_cidr {
-    my ($vnetid, $cidr, $hostname, $oldhostname, $mac, $description, $skipdns) = @_;
-
-    return if !$vnetid;
-
-    my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr);
-    PVE::Network::SDN::Subnets::update_ip($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns);
-}
-
-sub del_cidr {
-    my ($vnetid, $cidr, $hostname, $skipdns) = @_;
-
-    return if !$vnetid;
-
-    my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr);
-    PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $skipdns);
-}
-
-
-
-1;
diff --git a/PVE/Network/SDN/Zones.pm b/PVE/Network/SDN/Zones.pm
deleted file mode 100644 (file)
index f8e40b1..0000000
+++ /dev/null
@@ -1,357 +0,0 @@
-package PVE::Network::SDN::Zones;
-
-use strict;
-use warnings;
-
-use JSON;
-
-use PVE::Tools qw(extract_param dir_glob_regex run_command);
-use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-use PVE::Network;
-
-use PVE::Network::SDN::Vnets;
-use PVE::Network::SDN::Zones::VlanPlugin;
-use PVE::Network::SDN::Zones::QinQPlugin;
-use PVE::Network::SDN::Zones::VxlanPlugin;
-use PVE::Network::SDN::Zones::EvpnPlugin;
-use PVE::Network::SDN::Zones::FaucetPlugin;
-use PVE::Network::SDN::Zones::SimplePlugin;
-use PVE::Network::SDN::Zones::Plugin;
-
-PVE::Network::SDN::Zones::VlanPlugin->register();
-PVE::Network::SDN::Zones::QinQPlugin->register();
-PVE::Network::SDN::Zones::VxlanPlugin->register();
-PVE::Network::SDN::Zones::EvpnPlugin->register();
-PVE::Network::SDN::Zones::FaucetPlugin->register();
-PVE::Network::SDN::Zones::SimplePlugin->register();
-PVE::Network::SDN::Zones::Plugin->init();
-
-my $local_network_sdn_file = "/etc/network/interfaces.d/sdn";
-
-sub sdn_zones_config {
-    my ($cfg, $id, $noerr) = @_;
-
-    die "no sdn zone ID specified\n" if !$id;
-
-    my $scfg = $cfg->{ids}->{$id};
-    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
-
-    return $scfg;
-}
-
-sub config {
-    my $config = cfs_read_file("sdn/zones.cfg");
-    return $config;
-}
-
-sub get_plugin_config {
-    my ($vnet) = @_;
-    my $zoneid = $vnet->{zone};
-    my $zone_cfg = PVE::Network::SDN::Zones::config();
-    return $zone_cfg->{ids}->{$zoneid};
-}
-
-sub write_config {
-    my ($cfg) = @_;
-
-    cfs_write_file("sdn/zones.cfg", $cfg);
-}
-
-sub sdn_zones_ids {
-    my ($cfg) = @_;
-
-    return sort keys %{$cfg->{ids}};
-}
-
-sub complete_sdn_zone {
-    my ($cmdname, $pname, $cvalue) = @_;
-
-    my $cfg = PVE::Network::SDN::running_config();
-
-    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_zones_ids($cfg) ];
-}
-
-sub get_zone {
-    my ($zoneid, $running) = @_;
-
-    my $cfg = {};
-    if($running) {
-        my $cfg = PVE::Network::SDN::running_config();
-        $cfg = $cfg->{vnets};
-    } else {
-        $cfg = PVE::Network::SDN::Zones::config();
-    }
-
-    my $zone = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $zoneid, 1);
-
-    return $zone;
-}
-
-
-sub generate_etc_network_config {
-
-    my $cfg = PVE::Network::SDN::running_config();
-
-    my $version = $cfg->{version};
-    my $vnet_cfg = $cfg->{vnets};
-    my $zone_cfg = $cfg->{zones};
-    my $subnet_cfg = $cfg->{subnets};
-    my $controller_cfg = $cfg->{controllers};
-    return if !$vnet_cfg && !$zone_cfg;
-
-    my $interfaces_config = PVE::INotify::read_file('interfaces');
-
-    #generate configuration
-    my $config = {};
-    my $nodename = PVE::INotify::nodename();
-
-    for my $id (sort keys %{$vnet_cfg->{ids}}) {
-       my $vnet = $vnet_cfg->{ids}->{$id};
-       my $zone = $vnet->{zone};
-
-       if (!$zone) {
-           warn "can't generate vnet '$id': no zone assigned!\n";
-           next;
-       }
-
-       my $plugin_config = $zone_cfg->{ids}->{$zone};
-
-       if (!defined($plugin_config)) {
-           warn "can't generate vnet '$id': zone $zone don't exist\n";
-           next;
-       }
-
-       next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
-
-       my $controller;
-       if (my $controllerid = $plugin_config->{controller}) {
-           $controller = $controller_cfg->{ids}->{$controllerid};
-       }
-
-       my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-       eval {
-           $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config);
-       };
-       if (my $err = $@) {
-           warn "zone $zone : vnet $id : $err\n";
-           next;
-       }
-    }
-
-    my $raw_network_config = "\#version:$version\n";
-    foreach my $iface (sort keys %$config) {
-       $raw_network_config .= "\n";
-       $raw_network_config .= "auto $iface\n";
-       $raw_network_config .= "iface $iface\n";
-       foreach my $option (@{$config->{$iface}}) {
-           $raw_network_config .= "\t$option\n";
-       }
-    }
-
-    return $raw_network_config;
-}
-
-sub write_etc_network_config {
-    my ($rawconfig) = @_;
-
-    return if !$rawconfig;
-
-    my $writefh = IO::File->new($local_network_sdn_file,">");
-    print $writefh $rawconfig;
-    $writefh->close();
-}
-
-sub read_etc_network_config_version {
-    my $versionstr = PVE::Tools::file_read_firstline($local_network_sdn_file);
-
-    return if !defined($versionstr);
-
-    if ($versionstr =~ m/^\#version:(\d+)$/) {
-       return $1;
-    }
-}
-
-sub ifquery_check {
-
-    my $cmd = ['ifquery', '-a', '-c', '-o','json'];
-
-    my $result = '';
-    my $reader = sub { $result .= shift };
-
-    eval {
-       run_command($cmd, outfunc => $reader);
-    };
-
-    my $resultjson = decode_json($result);
-    my $interfaces = {};
-
-    foreach my $interface (@$resultjson) {
-       my $name = $interface->{name};
-       $interfaces->{$name} = {
-           status => $interface->{status},
-           config => $interface->{config},
-           config_status => $interface->{config_status},
-       };
-    }
-
-    return $interfaces;
-}
-
-my $warned_about_reload;
-
-sub status {
-
-    my $err_config = undef;
-
-    my $local_version = PVE::Network::SDN::Zones::read_etc_network_config_version();
-    my $cfg = PVE::Network::SDN::running_config();
-    my $sdn_version = $cfg->{version};
-
-    return if !$sdn_version;
-
-    if (!$local_version) {
-       $err_config = "local sdn network configuration is not yet generated, please reload";
-       if (!$warned_about_reload) {
-           $warned_about_reload = 1;
-           warn "$err_config\n";
-       }
-    } elsif ($local_version < $sdn_version) {
-       $err_config = "local sdn network configuration is too old, please reload";
-       if (!$warned_about_reload) {
-           $warned_about_reload = 1;
-           warn "$err_config\n";
-       }
-    } else {
-       $warned_about_reload = 0;
-    }
-
-    my $status = ifquery_check();
-
-    my $vnet_cfg = $cfg->{vnets};
-    my $zone_cfg = $cfg->{zones};
-    my $nodename = PVE::INotify::nodename();
-
-    my $vnet_status = {};
-    my $zone_status = {};
-
-    for my $id (sort keys %{$zone_cfg->{ids}}) {
-       next if defined($zone_cfg->{ids}->{$id}->{nodes}) && !$zone_cfg->{ids}->{$id}->{nodes}->{$nodename};
-       $zone_status->{$id}->{status} = $err_config ? 'pending' : 'available';
-    }
-
-    foreach my $id (sort keys %{$vnet_cfg->{ids}}) {
-       my $vnet = $vnet_cfg->{ids}->{$id};
-       my $zone = $vnet->{zone};
-       next if !defined($zone);
-
-       my $plugin_config = $zone_cfg->{ids}->{$zone};
-
-       if (!defined($plugin_config)) {
-           $vnet_status->{$id}->{status} = 'error';
-           $vnet_status->{$id}->{statusmsg} = "unknown zone '$zone' configured";
-           next;
-       }
-
-       next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
-
-       $vnet_status->{$id}->{zone} = $zone;
-       $vnet_status->{$id}->{status} = 'available';
-
-       if ($err_config) {
-           $vnet_status->{$id}->{status} = 'pending';
-           $vnet_status->{$id}->{statusmsg} = $err_config;
-           next;
-       }
-
-       my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-       my $err_msg = $plugin->status($plugin_config, $zone, $id, $vnet, $status);
-       if (@{$err_msg} > 0) {
-           $vnet_status->{$id}->{status} = 'error';
-           $vnet_status->{$id}->{statusmsg} = join(',', @{$err_msg});
-           $zone_status->{$id}->{status} = 'error';
-       }
-    }
-
-    return ($zone_status, $vnet_status);
-}
-
-sub tap_create {
-    my ($iface, $bridge) = @_;
-
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
-    if (!$vnet) { # fallback for classic bridge
-       PVE::Network::tap_create($iface, $bridge);
-       return;
-    }
-
-    my $plugin_config = get_plugin_config($vnet);
-    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-    $plugin->tap_create($plugin_config, $vnet, $iface, $bridge);
-}
-
-sub veth_create {
-    my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
-
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
-    if (!$vnet) { # fallback for classic bridge
-       PVE::Network::veth_create($veth, $vethpeer, $bridge, $hwaddr);
-       return;
-    }
-
-    my $plugin_config = get_plugin_config($vnet);
-    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-    $plugin->veth_create($plugin_config, $vnet, $veth, $vethpeer, $bridge, $hwaddr);
-}
-
-sub tap_plug {
-    my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
-
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
-    if (!$vnet) { # fallback for classic bridge
-       my $interfaces_config = PVE::INotify::read_file('interfaces');
-       my $opts = {};
-       $opts->{learning} = 0 if $interfaces_config->{ifaces}->{$bridge} && $interfaces_config->{ifaces}->{$bridge}->{'bridge-disable-mac-learning'};
-       PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate, $opts);
-       return;
-    }
-
-    my $plugin_config = get_plugin_config($vnet);
-    my $nodename = PVE::INotify::nodename();
-
-    die "vnet $bridge is not allowed on this node\n"
-       if $plugin_config->{nodes} && !defined($plugin_config->{nodes}->{$nodename});
-
-    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-    $plugin->tap_plug($plugin_config, $vnet, $tag, $iface, $bridge, $firewall, $trunks, $rate);
-}
-
-sub add_bridge_fdb {
-    my ($iface, $macaddr, $bridge, $firewall) = @_;
-
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
-    if (!$vnet) { # fallback for classic bridge
-       PVE::Network::add_bridge_fdb($iface, $macaddr, $firewall);
-       return;
-    }
-
-    my $plugin_config = get_plugin_config($vnet);
-    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-    PVE::Network::add_bridge_fdb($iface, $macaddr, $firewall) if $plugin_config->{'bridge-disable-mac-learning'};
-}
-
-sub del_bridge_fdb {
-    my ($iface, $macaddr, $bridge, $firewall) = @_;
-
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
-    if (!$vnet) { # fallback for classic bridge
-       PVE::Network::del_bridge_fdb($iface, $macaddr, $firewall);
-       return;
-    }
-
-    my $plugin_config = get_plugin_config($vnet);
-    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
-    PVE::Network::del_bridge_fdb($iface, $macaddr, $firewall) if $plugin_config->{'bridge-disable-mac-learning'};
-}
-
-1;
-
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
deleted file mode 100644 (file)
index a5a7539..0000000
+++ /dev/null
@@ -1,315 +0,0 @@
-package PVE::Network::SDN::Zones::EvpnPlugin;
-
-use strict;
-use warnings;
-use PVE::Network::SDN::Zones::VxlanPlugin;
-use PVE::Exception qw(raise raise_param_exc);
-use PVE::JSONSchema qw(get_standard_option);
-use PVE::Tools qw($IPV4RE);
-use PVE::INotify;
-use PVE::Cluster;
-use PVE::Tools;
-use Net::IP;
-
-use PVE::Network::SDN::Controllers::EvpnPlugin;
-
-use base('PVE::Network::SDN::Zones::VxlanPlugin');
-
-sub type {
-    return 'evpn';
-}
-
-PVE::JSONSchema::register_format('pve-sdn-bgp-rt', \&pve_verify_sdn_bgp_rt);
-sub pve_verify_sdn_bgp_rt {
-    my ($rt) = @_;
-
-    if ($rt =~ m/^(\d+):(\d+)$/) {
-       my $asn = $1;
-       my $id = $2;
-
-       if ($asn < 0 || $asn > 4294967295) {
-           die "value does not look like a valid bgp route-target\n";
-       }
-       if ($id < 0 || $id > 4294967295) {
-           die "value does not look like a valid bgp route-target\n";
-       }
-    } else {
-       die "value does not look like a valid bgp route-target\n";
-    }
-    return $rt;
-}
-
-sub properties {
-    return {
-       'vrf-vxlan' => {
-           type => 'integer',
-           description => "l3vni.",
-       },
-       'controller' => {
-           type => 'string',
-           description => "Frr router name",
-       },
-       'mac' => {
-           type => 'string',
-           description => "Anycast logical router mac address",
-           optional => 1, format => 'mac-addr'
-       },
-       'exitnodes' => get_standard_option('pve-node-list'),
-       'exitnodes-local-routing' => {
-           type => 'boolean',
-           description => "Allow exitnodes to connect to evpn guests",
-           optional => 1
-       },
-       'exitnodes-primary' => get_standard_option('pve-node', {
-           description => "Force traffic to this exitnode first."}),
-       'advertise-subnets' => {
-           type => 'boolean',
-           description => "Advertise evpn subnets if you have silent hosts",
-           optional => 1
-       },
-       'disable-arp-nd-suppression' => {
-           type => 'boolean',
-           description => "Disable ipv4 arp && ipv6 neighbour discovery suppression",
-           optional => 1
-       },
-       'rt-import' => {
-           type => 'string',
-           description => "Route-Target import",
-           optional => 1, format => 'pve-sdn-bgp-rt-list'
-        }
-    };
-}
-
-sub options {
-    return {
-       nodes => { optional => 1},
-       'vrf-vxlan' => { optional => 0 },
-       controller => { optional => 0 },
-       exitnodes => { optional => 1 },
-       'exitnodes-local-routing' => { optional => 1 },
-       'exitnodes-primary' => { optional => 1 },
-       'advertise-subnets' => { optional => 1 },
-       'disable-arp-nd-suppression' => { optional => 1 },
-       'rt-import' => { optional => 1 },
-       mtu => { optional => 1 },
-       mac => { optional => 1 },
-       dns => { optional => 1 },
-       reversedns => { optional => 1 },
-       dnszone => { optional => 1 },
-       ipam => { optional => 1 },
-    };
-}
-
-# Plugin implementation
-sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
-
-    my $tag = $vnet->{tag};
-    my $alias = $vnet->{alias};
-    my $mac = $plugin_config->{'mac'};
-
-    my $vrf_iface = "vrf_$zoneid";
-    my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
-    my $local_node = PVE::INotify::nodename();
-
-    die "missing vxlan tag" if !$tag;
-    die "missing controller" if !$controller;
-    warn "vlan-aware vnet can't be enabled with evpn plugin" if $vnet->{vlanaware};
-
-    my @peers = PVE::Tools::split_list($controller->{'peers'});
-    my $bgprouter = PVE::Network::SDN::Controllers::EvpnPlugin::find_bgp_controller($local_node, $controller_cfg);
-    my $loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
-    my ($ifaceip, $iface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
-    my $is_evpn_gateway = $plugin_config->{'exitnodes'}->{$local_node};
-    my $exitnodes_local_routing = $plugin_config->{'exitnodes-local-routing'};
-
-
-    my $mtu = 1450;
-    $mtu = $interfaces_config->{$iface}->{mtu} - 50 if $interfaces_config->{$iface}->{mtu};
-    $mtu = $plugin_config->{mtu} if $plugin_config->{mtu};
-
-    #vxlan interface
-    my $vxlan_iface = "vxlan_$vnetid";
-    my @iface_config = ();
-    push @iface_config, "vxlan-id $tag";
-    push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
-    push @iface_config, "bridge-learning off";
-    push @iface_config, "bridge-arp-nd-suppress on" if !$plugin_config->{'disable-arp-nd-suppression'};
-
-    push @iface_config, "mtu $mtu" if $mtu;
-    push(@{$config->{$vxlan_iface}}, @iface_config) if !$config->{$vxlan_iface};
-
-    #vnet bridge
-    @iface_config = ();
-
-    my $address = {};
-    my $ipv4 = undef;
-    my $ipv6 = undef;
-    my $enable_forward_v4 = undef;
-    my $enable_forward_v6 = undef;
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
-    foreach my $subnetid (sort keys %{$subnets}) {
-       my $subnet = $subnets->{$subnetid};
-       my $cidr = $subnet->{cidr};
-       my $mask = $subnet->{mask};
-
-       my $gateway = $subnet->{gateway};
-       if ($gateway) {
-           push @iface_config, "address $gateway/$mask" if !defined($address->{$gateway});
-           $address->{$gateway} = 1;
-       }
-
-        my $iptables = undef;
-        my $checkrouteip = undef;
-        my $ipversion = Net::IP::ip_is_ipv6($gateway) ? 6 : 4;
-
-       if ($ipversion == 6) {
-           $ipv6 = 1;
-           $iptables = "ip6tables";
-           $checkrouteip = '2001:4860:4860::8888';
-           $enable_forward_v6 = 1 if $gateway;
-       } else {
-           $ipv4 = 1;
-           $iptables = "iptables";
-           $checkrouteip = '8.8.8.8';
-           $enable_forward_v4 = 1 if $gateway;
-       }
-
-       if ($subnet->{snat}) {
-
-            #find outgoing interface
-            my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip);
-            if ($outip && $outiface && $is_evpn_gateway) {
-                #use snat, faster than masquerade
-                push @iface_config, "post-up $iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
-                push @iface_config, "post-down $iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
-                #add conntrack zone once on outgoing interface
-                push @iface_config, "post-up $iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
-                push @iface_config, "post-down $iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
-            }
-        }
-    }
-
-    push @iface_config, "hwaddress $mac" if $mac;
-    push @iface_config, "bridge_ports $vxlan_iface";
-    push @iface_config, "bridge_stp off";
-    push @iface_config, "bridge_fd 0";
-    push @iface_config, "mtu $mtu" if $mtu;
-    push @iface_config, "alias $alias" if $alias;
-    push @iface_config, "ip-forward on" if $enable_forward_v4;
-    push @iface_config, "ip6-forward on" if $enable_forward_v6;
-    push @iface_config, "arp-accept on" if $ipv4||$ipv6;
-    push @iface_config, "vrf $vrf_iface" if $vrf_iface;
-    push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
-
-    if ($vrf_iface) {
-       #vrf interface
-       @iface_config = ();
-       push @iface_config, "vrf-table auto";
-       if(!$is_evpn_gateway) {
-           push @iface_config, "post-up ip route add vrf $vrf_iface unreachable default metric 4278198272";
-       } else {
-           push @iface_config, "post-up ip route del vrf $vrf_iface unreachable default metric 4278198272";
-       }
-
-       push(@{$config->{$vrf_iface}}, @iface_config) if !$config->{$vrf_iface};
-
-       if ($vrfvxlan) {
-           #l3vni vxlan interface
-           my $iface_vrf_vxlan = "vrfvx_$zoneid";
-           @iface_config = ();
-           push @iface_config, "vxlan-id $vrfvxlan";
-           push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
-           push @iface_config, "bridge-learning off";
-           push @iface_config, "bridge-arp-nd-suppress on" if !$plugin_config->{'disable-arp-nd-suppression'};
-           push @iface_config, "mtu $mtu" if $mtu;
-           push(@{$config->{$iface_vrf_vxlan}}, @iface_config) if !$config->{$iface_vrf_vxlan};
-
-           #l3vni bridge
-           my $brvrf = "vrfbr_$zoneid";
-           @iface_config = ();
-           push @iface_config, "bridge-ports $iface_vrf_vxlan";
-           push @iface_config, "bridge_stp off";
-           push @iface_config, "bridge_fd 0";
-           push @iface_config, "mtu $mtu" if $mtu;
-           push @iface_config, "vrf $vrf_iface";
-           push(@{$config->{$brvrf}}, @iface_config) if !$config->{$brvrf};
-       }
-
-       if ( $is_evpn_gateway && $exitnodes_local_routing ) {
-           #add a veth pair for local cross-vrf routing
-           my $iface_xvrf = "xvrf_$zoneid";
-           my $iface_xvrfp = "xvrfp_$zoneid";
-
-           @iface_config = ();
-           push @iface_config, "link-type veth";
-           push @iface_config, "address 10.255.255.1/30";
-           push @iface_config, "veth-peer-name $iface_xvrfp";
-           push @iface_config, "mtu ".($mtu+50) if $mtu;
-           push(@{$config->{$iface_xvrf}}, @iface_config) if !$config->{$iface_xvrf};
-
-           @iface_config = ();
-           push @iface_config, "link-type veth";
-           push @iface_config, "address 10.255.255.2/30";
-           push @iface_config, "veth-peer-name $iface_xvrf";
-           push @iface_config, "vrf $vrf_iface";
-           push @iface_config, "mtu ".($mtu+50) if $mtu;
-           push(@{$config->{$iface_xvrfp}}, @iface_config) if !$config->{$iface_xvrfp};
-       }
-    }
-    return $config;
-}
-
-sub on_update_hook {
-    my ($class, $zoneid, $zone_cfg, $controller_cfg) = @_;
-
-    # verify that controller exist
-    my $controller = $zone_cfg->{ids}->{$zoneid}->{controller};
-    if (!defined($controller_cfg->{ids}->{$controller})) {
-       die "controller $controller don't exist";
-    } else {
-       die "$controller is not a evpn controller type" if $controller_cfg->{ids}->{$controller}->{type} ne 'evpn';
-    }
-
-    #vrf-vxlan need to be defined
-
-    my $vrfvxlan = $zone_cfg->{ids}->{$zoneid}->{'vrf-vxlan'};
-    # verify that vrf-vxlan is not already declared in another zone
-    foreach my $id (keys %{$zone_cfg->{ids}}) {
-       next if $id eq $zoneid;
-       die "vrf-vxlan $vrfvxlan is already declared in $id"
-               if (defined($zone_cfg->{ids}->{$id}->{'vrf-vxlan'}) && $zone_cfg->{ids}->{$id}->{'vrf-vxlan'} eq $vrfvxlan);
-    }
-
-    if (!defined($zone_cfg->{ids}->{$zoneid}->{'mac'})) {
-       my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
-       $zone_cfg->{ids}->{$zoneid}->{'mac'} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
-    }
-}
-
-
-sub vnet_update_hook {
-    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
-
-    my $vnet = $vnet_cfg->{ids}->{$vnetid};
-    my $tag = $vnet->{tag};
-
-    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($tag);
-    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $tag > 16777216;
-
-    # verify that tag is not already defined globally (vxlan-id are unique)
-    foreach my $id (keys %{$vnet_cfg->{ids}}) {
-       next if $id eq $vnetid;
-       my $othervnet = $vnet_cfg->{ids}->{$id};
-       my $other_tag = $othervnet->{tag};
-       my $other_zoneid = $othervnet->{zone};
-       my $other_zone = $zone_cfg->{ids}->{$other_zoneid};
-       next if $other_zone->{type} ne 'vxlan' && $other_zone->{type} ne 'evpn';
-       raise_param_exc({ tag => "vxlan tag $tag already exist in vnet $id in zone $other_zoneid "}) if $other_tag && $tag eq $other_tag;
-    }
-}
-
-
-1;
-
-
diff --git a/PVE/Network/SDN/Zones/FaucetPlugin.pm b/PVE/Network/SDN/Zones/FaucetPlugin.pm
deleted file mode 100644 (file)
index a237d17..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-package PVE::Network::SDN::Zones::FaucetPlugin;
-
-use strict;
-use warnings;
-use PVE::Network::SDN::Zones::VlanPlugin;
-
-use base('PVE::Network::SDN::Zones::VlanPlugin');
-
-sub type {
-    return 'faucet';
-}
-
-sub properties {
-    return {
-        'dp-id' => {
-            type => 'integer',
-            description => 'Faucet dataplane id',
-        },
-    };
-}
-
-sub options {
-
-    return {
-       nodes => { optional => 1},
-       'dp-id' => { optional => 0 },
-#      'uplink-id' => { optional => 0 },
-       'controller' => { optional => 0 },
-       dns => { optional => 1 },
-       reversedns => { optional => 1 },
-       dnszone => { optional => 1 },
-       ipam => { optional => 1 },
-    };
-}
-
-# Plugin implementation
-sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $uplinks, $controller, $config) = @_;
-
-    my $mtu = $vnet->{mtu};
-    my $uplink = $plugin_config->{'uplink-id'};
-    my $dpid = $plugin_config->{'dp-id'};
-    my $dphex = printf("%x",$dpid);  #fixme :should be 16characters hex
-
-    my $iface = $uplinks->{$uplink}->{name};
-    $iface = "uplink${uplink}" if !$iface;
-
-    #tagged interface
-    my @iface_config = ();
-    push @iface_config, "ovs_type OVSPort";
-    push @iface_config, "ovs_bridge $zoneid";
-    push @iface_config, "ovs_mtu $mtu" if $mtu;
-    push(@{$config->{$iface}}, @iface_config) if !$config->{$iface};
-
-    #vnet bridge
-    @iface_config = ();
-    push @iface_config, "ovs_port $iface";
-    push @iface_config, "ovs_type OVSBridge";
-    push @iface_config, "ovs_mtu $mtu" if $mtu;
-
-    push @iface_config, "ovs_extra set bridge $zoneid other-config:datapath-id=$dphex";
-    push @iface_config, "ovs_extra set bridge $zoneid other-config:disable-in-band=true";
-    push @iface_config, "ovs_extra set bridge $zoneid fail_mode=secure";
-    push @iface_config, "ovs_extra set-controller $vnetid tcp:127.0.0.1:6653";
-
-    push(@{$config->{$zoneid}}, @iface_config) if !$config->{$zoneid};
-
-    return $config;
-}
-
-
-1;
-
-
diff --git a/PVE/Network/SDN/Zones/Makefile b/PVE/Network/SDN/Zones/Makefile
deleted file mode 100644 (file)
index 8454388..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-SOURCES=Plugin.pm VlanPlugin.pm VxlanPlugin.pm FaucetPlugin.pm EvpnPlugin.pm QinQPlugin.pm SimplePlugin.pm
-
-
-PERL5DIR=${DESTDIR}/usr/share/perl5
-
-.PHONY: install
-install:
-       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Zones/$$i; done
diff --git a/PVE/Network/SDN/Zones/Plugin.pm b/PVE/Network/SDN/Zones/Plugin.pm
deleted file mode 100644 (file)
index 2c707b3..0000000
+++ /dev/null
@@ -1,340 +0,0 @@
-package PVE::Network::SDN::Zones::Plugin;
-
-use strict;
-use warnings;
-
-use PVE::Tools qw(run_command);
-use PVE::JSONSchema;
-use PVE::Cluster;
-use PVE::Network;
-
-use PVE::JSONSchema qw(get_standard_option);
-use base qw(PVE::SectionConfig);
-
-PVE::Cluster::cfs_register_file(
-    'sdn/zones.cfg',
-    sub { __PACKAGE__->parse_config(@_); },
-    sub { __PACKAGE__->write_config(@_); },
-);
-
-PVE::JSONSchema::register_standard_option('pve-sdn-zone-id', {
-    description => "The SDN zone object identifier.",
-    type => 'string', format => 'pve-sdn-zone-id',
-});
-
-PVE::JSONSchema::register_format('pve-sdn-zone-id', \&parse_sdn_zone_id);
-sub parse_sdn_zone_id {
-    my ($id, $noerr) = @_;
-
-    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
-       return undef if $noerr;
-       die "zone ID '$id' contains illegal characters\n";
-    }
-    die "zone ID '$id' can't be more length than 8 characters\n" if length($id) > 8;
-    return $id;
-}
-
-my $defaultData = {
-
-    propertyList => {
-       type => {
-           description => "Plugin type.",
-           type => 'string', format => 'pve-configid',
-           type => 'string',
-       },
-       nodes => get_standard_option('pve-node-list', { optional => 1 }),
-       zone => get_standard_option('pve-sdn-zone-id', {
-           completion => \&PVE::Network::SDN::Zones::complete_sdn_zone,
-       }),
-       ipam => {
-           type => 'string',
-           description => "use a specific ipam",
-           optional => 1,
-       },
-    },
-};
-
-sub private {
-    return $defaultData;
-}
-
-sub parse_section_header {
-    my ($class, $line) = @_;
-
-    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
-        my ($type, $id) = (lc($1), $2);
-       my $errmsg = undef; # set if you want to skip whole section
-       eval { PVE::JSONSchema::pve_verify_configid($type); };
-       $errmsg = $@ if $@;
-       my $config = {}; # to return additional attributes
-       return ($type, $id, $errmsg, $config);
-    }
-    return undef;
-}
-
-sub decode_value {
-    my ($class, $type, $key, $value) = @_;
-
-    if ($key eq 'nodes' || $key eq 'exitnodes') {
-       my $res = {};
-
-       foreach my $node (PVE::Tools::split_list($value)) {
-           if (PVE::JSONSchema::pve_verify_node_name($node)) {
-               $res->{$node} = 1;
-           }
-       }
-
-       return $res;
-    }
-
-    return $value;
-}
-
-sub encode_value {
-    my ($class, $type, $key, $value) = @_;
-
-    if ($key eq 'nodes' || $key eq 'exitnodes') {
-       return join(',', keys(%$value));
-    }
-
-    return $value;
-}
-
-sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub generate_controller_config {
-    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub generate_controller_vnet_config {
-    my ($class, $plugin_config, $controller, $zoneid, $vnetid, $config) = @_;
-
-}
-
-sub write_controller_config {
-    my ($class, $plugin_config, $config) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub controller_reload {
-    my ($class) = @_;
-
-    die "please implement inside plugin";
-}
-
-sub on_delete_hook {
-    my ($class, $zoneid, $vnet_cfg) = @_;
-
-    # verify that no vnet are associated to this zone
-    foreach my $id (keys %{$vnet_cfg->{ids}}) {
-       my $vnet = $vnet_cfg->{ids}->{$id};
-       die "zone $zoneid is used by vnet $id"
-           if ($vnet->{type} eq 'vnet' && defined($vnet->{zone}) && $vnet->{zone} eq $zoneid);
-    }
-}
-
-sub on_update_hook {
-    my ($class, $zoneid, $zone_cfg, $controller_cfg) = @_;
-
-    # do nothing by default
-}
-
-sub vnet_update_hook {
-    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
-
-    # do nothing by default
-}
-
-#helpers
-sub parse_tag_number_or_range {
-    my ($str, $max, $tag) = @_;
-
-    my @elements = split(/,/, $str);
-    my $count = 0;
-    my $allowed = undef;
-
-    die "extraneous commas in list\n" if $str ne join(',', @elements);
-    foreach my $item (@elements) {
-       if ($item =~ m/^([0-9]+)-([0-9]+)$/) {
-           $count += 2;
-           my ($port1, $port2) = ($1, $2);
-           die "invalid port '$port1'\n" if $port1 > $max;
-           die "invalid port '$port2'\n" if $port2 > $max;
-           die "backwards range '$port1:$port2' not allowed, did you mean '$port2:$port1'?\n" if $port1 > $port2;
-
-           if ($tag && $tag >= $port1 && $tag <= $port2){
-               $allowed = 1;
-               last;
-           }
-
-       } elsif ($item =~ m/^([0-9]+)$/) {
-           $count += 1;
-           my $port = $1;
-           die "invalid port '$port'\n" if $port > $max;
-
-           if ($tag && $tag == $port){
-               $allowed = 1;
-               last;
-           }
-       }
-    }
-    die "tag $tag is not allowed" if $tag && !$allowed;
-
-    return (scalar(@elements) > 1);
-}
-
-sub status {
-    my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_;
-
-    my $err_msg = [];
-
-    # ifaces to check
-    my $ifaces = [ $vnetid ];
-
-    foreach my $iface (@{$ifaces}) {
-        if (!$status->{$iface}->{status}) {
-           push @$err_msg, "missing $iface";
-        } elsif ($status->{$iface}->{status} ne 'pass') {
-           push @$err_msg, "error $iface";
-        }
-    }
-    return $err_msg;
-}
-
-
-sub tap_create {
-    my ($class, $plugin_config, $vnet, $iface, $vnetid) = @_;
-
-    PVE::Network::tap_create($iface, $vnetid);
-}
-
-sub veth_create {
-    my ($class, $plugin_config, $vnet, $veth, $vethpeer, $vnetid, $hwaddr) = @_;
-
-    PVE::Network::veth_create($veth, $vethpeer, $vnetid, $hwaddr);
-}
-
-sub tap_plug {
-    my ($class, $plugin_config, $vnet, $tag, $iface, $vnetid, $firewall, $trunks, $rate) = @_;
-
-    my $vlan_aware = PVE::Tools::file_read_firstline("/sys/class/net/$vnetid/bridge/vlan_filtering");
-    die "vm vlans are not allowed on vnet $vnetid" if !$vlan_aware && ($tag || $trunks);
-
-    my $opts = {};
-    $opts->{learning} = 0 if $plugin_config->{'bridge-disable-mac-learning'};
-    PVE::Network::tap_plug($iface, $vnetid, $tag, $firewall, $trunks, $rate, $opts);
-}
-
-#helper
-
-sub get_uplink_iface {
-    my ($interfaces_config, $uplink) = @_;
-
-    my $iface = undef;
-    foreach my $id (keys %{$interfaces_config->{ifaces}}) {
-        my $interface = $interfaces_config->{ifaces}->{$id};
-        if (my $iface_uplink = $interface->{'uplink-id'}) {
-           next if $iface_uplink ne $uplink;
-            if($interface->{type} ne 'eth' && $interface->{type} ne 'bond') {
-                warn "uplink $uplink is not a physical or bond interface";
-                next;
-            }
-           $iface = $id;
-        }
-    }
-
-    #create a dummy uplink interface if no uplink found
-    if(!$iface) {
-        warn "can't find uplink $uplink in physical interface";
-        $iface = "uplink${uplink}";
-    }
-
-    return $iface;
-}
-
-sub get_local_route_ip {
-    my ($targetip) = @_;
-
-    my $ip = undef;
-    my $interface = undef;
-
-    run_command(['/sbin/ip', 'route', 'get', $targetip], outfunc => sub {
-        if ($_[0] =~ m/src ($PVE::Tools::IPRE)/) {
-            $ip = $1;
-        }
-        if ($_[0] =~ m/dev (\S+)/) {
-            $interface = $1;
-        }
-
-    });
-    return ($ip, $interface);
-}
-
-
-sub find_local_ip_interface_peers {
-    my ($peers, $iface) = @_;
-
-    my $network_config = PVE::INotify::read_file('interfaces');
-    my $ifaces = $network_config->{ifaces};
-    
-    #if iface is defined, return ip if exist (if not,try to find it on other ifaces)
-    if ($iface) {
-       my $ip = $ifaces->{$iface}->{address};
-       return ($ip,$iface) if $ip;
-    }
-
-    #is a local ip member of peers list ?
-    foreach my $address (@{$peers}) {
-       while (my $interface = each %$ifaces) {
-           my $ip = $ifaces->{$interface}->{address};
-           if ($ip && $ip eq $address) {
-               return ($ip, $interface);
-           }
-       }
-    }
-
-    #if peer is remote, find source with ip route
-    foreach my $address (@{$peers}) {
-       my ($ip, $interface) = get_local_route_ip($address);
-       return ($ip, $interface);
-    }
-}
-
-sub find_bridge {
-    my ($bridge) = @_;
-
-    die "can't find bridge $bridge" if !-d "/sys/class/net/$bridge";
-}
-
-sub is_vlanaware {
-    my ($bridge) = @_;
-
-    return PVE::Tools::file_read_firstline("/sys/class/net/$bridge/bridge/vlan_filtering");
-}
-
-sub is_ovs {
-    my ($bridge) = @_;
-
-    my $is_ovs = !-d "/sys/class/net/$bridge/brif";
-    return $is_ovs;    
-}
-
-sub get_bridge_ifaces {
-    my ($bridge) = @_;
-
-    my @bridge_ifaces = ();
-    my $dir = "/sys/class/net/$bridge/brif";
-    PVE::Tools::dir_glob_foreach($dir, '(((eth|bond)\d+|en[^.]+)(\.\d+)?)', sub {
-       push @bridge_ifaces, $_[0];
-    });
-
-    return @bridge_ifaces;
-}
-1;
diff --git a/PVE/Network/SDN/Zones/QinQPlugin.pm b/PVE/Network/SDN/Zones/QinQPlugin.pm
deleted file mode 100644 (file)
index f4d12bc..0000000
+++ /dev/null
@@ -1,233 +0,0 @@
-package PVE::Network::SDN::Zones::QinQPlugin;
-
-use strict;
-use warnings;
-
-use PVE::Exception qw(raise raise_param_exc);
-
-use PVE::Network::SDN::Zones::Plugin;
-
-use base('PVE::Network::SDN::Zones::Plugin');
-
-sub type {
-    return 'qinq';
-}
-
-sub properties {
-    return {
-       tag => {
-           type => 'integer',
-           minimum => 0,
-           description => "Service-VLAN Tag",
-       },
-       mtu => {
-           type => 'integer',
-           description => "MTU",
-           optional => 1,
-       },
-       'vlan-protocol' => {
-           type => 'string',
-           enum => ['802.1q', '802.1ad'],
-           default => '802.1q',
-           optional => 1,
-       }
-    };
-}
-
-sub options {
-    return {
-       nodes => { optional => 1},
-       'tag' => { optional => 0 },
-       'bridge' => { optional => 0 },
-        'bridge-disable-mac-learning' => { optional => 1 },
-       'mtu' => { optional => 1 },
-       'vlan-protocol' => { optional => 1 },
-       dns => { optional => 1 },
-       reversedns => { optional => 1 },
-       dnszone => { optional => 1 },
-       ipam => { optional => 1 },
-    };
-}
-
-# Plugin implementation
-sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
-
-    my ($bridge, $mtu, $stag) = $plugin_config->@{'bridge', 'mtu', 'tag'};
-    my $vlanprotocol = $plugin_config->{'vlan-protocol'};
-
-    PVE::Network::SDN::Zones::Plugin::find_bridge($bridge);
-
-    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
-    my $is_ovs = PVE::Network::SDN::Zones::Plugin::is_ovs($bridge);
-
-    my @iface_config = ();
-    my $zone_notag_uplink = "ln_${zoneid}";
-    my $zone_notag_uplinkpeer = "pr_${zoneid}";
-    my $zone = "z_${zoneid}";
-
-    my $vnet_bridge_ports = "";
-    if (my $ctag = $vnet->{tag}) {
-       $vnet_bridge_ports = "$zone.$ctag";
-    } else {
-       $vnet_bridge_ports = $zone_notag_uplinkpeer;
-    }
-
-    my $zone_bridge_ports = "";
-    if ($is_ovs) {
-        # ovs--->ovsintport(dot1q-tunnel tag)------->vlanawarebrige-----(tag)--->vnet
-
-       $vlanprotocol = "802.1q" if !$vlanprotocol;
-       my $svlan_iface = "sv_".$zoneid;
-
-       # ovs dot1q-tunnel port
-       @iface_config = ();
-       push @iface_config, "ovs_type OVSIntPort";
-       push @iface_config, "ovs_bridge $bridge";
-       push @iface_config, "ovs_mtu $mtu" if $mtu;
-       push @iface_config, "ovs_options vlan_mode=dot1q-tunnel tag=$stag other_config:qinq-ethtype=$vlanprotocol";
-       push(@{$config->{$svlan_iface}}, @iface_config) if !$config->{$svlan_iface};
-
-        # redefine main ovs bridge, ifupdown2 will merge ovs_ports
-       @{$config->{$bridge}}[0] = "ovs_ports" if !@{$config->{$bridge}}[0];
-       my @ovs_ports = split / / , @{$config->{$bridge}}[0];
-       @{$config->{$bridge}}[0] .= " $svlan_iface" if !grep( $_ eq $svlan_iface, @ovs_ports );
-
-       $zone_bridge_ports = $svlan_iface;
-
-    } elsif ($vlan_aware) {
-        # VLAN_aware_brige-(tag)----->vlanwarebridge-(tag)----->vnet
-
-       if ($vlanprotocol) {
-           @iface_config = ();
-           push @iface_config, "bridge-vlan-protocol $vlanprotocol";
-           push(@{$config->{$bridge}}, @iface_config) if !$config->{$bridge};
-       }
-
-       $zone_bridge_ports = "$bridge.$stag";
-
-    } else {
-       # eth--->eth.x(svlan)----->vlanwarebridge-(tag)----->vnet---->vnet
-
-       my @bridge_ifaces = PVE::Network::SDN::Zones::Plugin::get_bridge_ifaces($bridge);
-
-       for my $bridge_iface (@bridge_ifaces) {
-           # use named vlan interface to avoid too long names
-           my $svlan_iface = "sv_$zoneid";
-
-           # svlan
-           @iface_config = ();
-           push @iface_config, "vlan-raw-device $bridge_iface";
-           push @iface_config, "vlan-id $stag";
-           push @iface_config, "vlan-protocol $vlanprotocol" if $vlanprotocol;
-           push(@{$config->{$svlan_iface}}, @iface_config) if !$config->{$svlan_iface};
-
-           $zone_bridge_ports = $svlan_iface;
-           last;
-        }
-   }
-
-    # veth peer for notag vnet
-    @iface_config = ();
-    push @iface_config, "link-type veth";
-    push @iface_config, "veth-peer-name $zone_notag_uplinkpeer";
-    push(@{$config->{$zone_notag_uplink}}, @iface_config) if !$config->{$zone_notag_uplink};
-
-    @iface_config = ();
-    push @iface_config, "link-type veth";
-    push @iface_config, "veth-peer-name $zone_notag_uplink";
-    push(@{$config->{$zone_notag_uplinkpeer}}, @iface_config) if !$config->{$zone_notag_uplinkpeer};
-
-    # zone vlan aware bridge
-    @iface_config = ();
-    push @iface_config, "mtu $mtu" if $mtu;
-    push @iface_config, "bridge-stp off";
-    push @iface_config, "bridge-ports $zone_bridge_ports $zone_notag_uplink";
-    push @iface_config, "bridge-fd 0";
-    push @iface_config, "bridge-vlan-aware yes";
-    push @iface_config, "bridge-vids 2-4094";
-    push(@{$config->{$zone}}, @iface_config) if !$config->{$zone};
-
-    # vnet bridge
-    @iface_config = ();
-    push @iface_config, "bridge_ports $vnet_bridge_ports";
-    push @iface_config, "bridge_stp off";
-    push @iface_config, "bridge_fd 0";
-    if($vnet->{vlanaware}) {
-       push @iface_config, "bridge-vlan-aware yes";
-       push @iface_config, "bridge-vids 2-4094";
-    }
-    push @iface_config, "mtu $mtu" if $mtu;
-    push @iface_config, "alias $vnet->{alias}" if $vnet->{alias};
-    push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
-}
-
-sub status {
-    my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_;
-
-    my $bridge = $plugin_config->{bridge};
-    my $err_msg = [];
-
-    if (!-d "/sys/class/net/$bridge") {
-        push @$err_msg, "missing $bridge";
-        return $err_msg;
-    }
-
-    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
-
-    my $tag = $vnet->{tag};
-    my $vnet_uplink = "ln_".$vnetid;
-    my $vnet_uplinkpeer = "pr_".$vnetid;
-    my $zone_notag_uplink = "ln_".$zone;
-    my $zone_notag_uplinkpeer = "pr_".$zone;
-    my $zonebridge = "z_$zone";
-
-    # ifaces to check
-    my $ifaces = [ $vnetid, $bridge ];
-
-    push @$ifaces, $zonebridge;
-    push @$ifaces, $zone_notag_uplink;
-    push @$ifaces, $zone_notag_uplinkpeer;
-
-    if (!$vlan_aware) {
-       my $svlan_iface = "sv_$zone";
-       push @$ifaces, $svlan_iface;
-    }
-
-    foreach my $iface (@{$ifaces}) {
-       if (!$status->{$iface}->{status}) {
-           push @$err_msg, "missing $iface";
-        } elsif ($status->{$iface}->{status} ne 'pass') {
-           push @$err_msg, "error $iface";
-       }
-    }
-    return $err_msg;
-}
-
-sub vnet_update_hook {
-    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
-
-    my $vnet = $vnet_cfg->{ids}->{$vnetid};
-
-    my $tag = $vnet->{tag};
-    raise_param_exc({ tag => "VLAN tag maximal value is 4096" }) if $tag && $tag > 4096;
-
-    # verify that tag is not already defined in another vnet on same zone
-    for my $id (sort keys %{$vnet_cfg->{ids}}) {
-       next if $id eq $vnetid;
-       my $other_vnet = $vnet_cfg->{ids}->{$id};
-       next if $vnet->{zone} ne $other_vnet->{zone};
-       my $other_tag = $other_vnet->{tag};
-       if ($tag) {
-           raise_param_exc({ tag => "tag $tag already exist in zone $vnet->{zone} vnet $id"})
-               if $other_tag && $tag eq $other_tag;
-       } else {
-           raise_param_exc({ tag => "tag-less vnet already exists in zone $vnet->{zone} vnet $id"})
-               if !$other_tag;
-       }
-    }
-}
-
-1;
-
-
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
deleted file mode 100644 (file)
index 7757747..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-package PVE::Network::SDN::Zones::SimplePlugin;
-
-use strict;
-use warnings;
-use PVE::Network::SDN::Zones::Plugin;
-use PVE::Exception qw(raise raise_param_exc);
-use PVE::Cluster;
-use PVE::Tools;
-
-use base('PVE::Network::SDN::Zones::Plugin');
-
-sub type {
-    return 'simple';
-}
-
-sub properties {
-    return {
-       dns => {
-           type => 'string',
-           description => "dns api server",
-       },
-       reversedns => {
-           type => 'string',
-           description => "reverse dns api server",
-       },
-       dnszone => {
-           type => 'string', format => 'dns-name',
-           description => "dns domain zone  ex: mydomain.com",
-       }
-    };
-}
-
-sub options {
-    return {
-       nodes => { optional => 1},
-       mtu => { optional => 1 },
-       dns => { optional => 1 },
-       reversedns => { optional => 1 },
-       dnszone => { optional => 1 },
-       ipam => { optional => 1 },
-    };
-}
-
-# Plugin implementation
-sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
-
-    return $config if$config->{$vnetid}; # nothing to do
-
-    my $mac = $vnet->{mac};
-    my $alias = $vnet->{alias};
-    my $mtu = $plugin_config->{mtu} if $plugin_config->{mtu};
-
-    # vnet bridge
-    my @iface_config = ();
-
-    my $address = {};
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
-
-    my $ipv4 = undef;
-    my $ipv6 = undef;
-    my $enable_forward_v4 = undef;
-    my $enable_forward_v6 = undef;
-
-    foreach my $subnetid (sort keys %{$subnets}) {
-       my $subnet = $subnets->{$subnetid};
-       my $cidr = $subnet->{cidr};
-       my $mask = $subnet->{mask};
-
-       my $gateway = $subnet->{gateway};
-       if ($gateway) {
-           push @iface_config, "address $gateway/$mask" if !defined($address->{$gateway});
-           $address->{$gateway} = 1;
-       }
-
-       my $iptables = undef;
-       my $checkrouteip = undef;
-       my $ipversion = Net::IP::ip_is_ipv6($gateway) ? 6 : 4;
-
-       if ( $ipversion == 6) {
-           $ipv6 = 1;
-           $iptables = "ip6tables";
-           $checkrouteip = '2001:4860:4860::8888';
-           $enable_forward_v6 = 1 if $gateway;
-       } else {
-           $ipv4 = 1;
-           $iptables = "iptables";
-           $checkrouteip = '8.8.8.8';
-           $enable_forward_v4 = 1 if $gateway;
-       }
-
-       #add route for /32 pointtopoint
-       push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32 && $ipversion == 4;
-       if ($subnet->{snat}) {
-           #find outgoing interface
-           my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip);
-           if ($outip && $outiface) {
-               #use snat, faster than masquerade
-               push @iface_config, "post-up $iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
-               push @iface_config, "post-down $iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
-               #add conntrack zone once on outgoing interface
-               push @iface_config, "post-up $iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
-               push @iface_config, "post-down $iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
-           }
-       }
-    }
-
-    push @iface_config, "hwaddress $mac" if $mac;
-    push @iface_config, "bridge_ports none";
-    push @iface_config, "bridge_stp off";
-    push @iface_config, "bridge_fd 0";
-    if ($vnet->{vlanaware}) {
-        push @iface_config, "bridge-vlan-aware yes";
-        push @iface_config, "bridge-vids 2-4094";
-    }
-    push @iface_config, "mtu $mtu" if $mtu;
-    push @iface_config, "alias $alias" if $alias;
-    push @iface_config, "ip-forward on" if $enable_forward_v4;
-    push @iface_config, "ip6-forward on" if $enable_forward_v6;
-
-    push @{$config->{$vnetid}}, @iface_config;
-
-    return $config;
-}
-
-sub status {
-    my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_;
-
-    # ifaces to check
-    my $ifaces = [ $vnetid ];
-    my $err_msg = [];
-    foreach my $iface (@{$ifaces}) {
-       if (!$status->{$iface}->{status}) {
-           push @$err_msg, "missing $iface";
-       } elsif ($status->{$iface}->{status} ne 'pass') {
-           push @$err_msg, "error iface $iface";
-       }
-    }
-    return $err_msg;
-}
-
-
-sub vnet_update_hook {
-    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
-
-    my $vnet = $vnet_cfg->{ids}->{$vnetid};
-    my $tag = $vnet->{tag};
-
-    raise_param_exc({ tag => "vlan tag is not allowed on simple zone"}) if defined($tag);
-
-    if (!defined($vnet->{mac})) {
-        my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
-        $vnet->{mac} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
-    }
-}
-
-1;
-
-
diff --git a/PVE/Network/SDN/Zones/VlanPlugin.pm b/PVE/Network/SDN/Zones/VlanPlugin.pm
deleted file mode 100644 (file)
index 0bb6b8a..0000000
+++ /dev/null
@@ -1,199 +0,0 @@
-package PVE::Network::SDN::Zones::VlanPlugin;
-
-use strict;
-use warnings;
-use PVE::Network::SDN::Zones::Plugin;
-use PVE::Exception qw(raise raise_param_exc);
-
-use base('PVE::Network::SDN::Zones::Plugin');
-
-sub type {
-    return 'vlan';
-}
-
-PVE::JSONSchema::register_format('pve-sdn-vlanrange', \&pve_verify_sdn_vlanrange);
-sub pve_verify_sdn_vlanrange {
-   my ($vlanstr) = @_;
-
-   PVE::Network::SDN::Zones::Plugin::parse_tag_number_or_range($vlanstr, '4096');
-
-   return $vlanstr;
-}
-
-sub properties {
-    return {
-       'bridge' => {
-           type => 'string',
-       },
-       'bridge-disable-mac-learning' => {
-           type => 'boolean',
-            description => "Disable auto mac learning.",
-       }
-    };
-}
-
-sub options {
-
-    return {
-       nodes => { optional => 1},
-       'bridge' => { optional => 0 },
-       'bridge-disable-mac-learning' => { optional => 1 },
-       mtu => { optional => 1 },
-       dns => { optional => 1 },
-       reversedns => { optional => 1 },
-       dnszone => { optional => 1 },
-       ipam => { optional => 1 },
-    };
-}
-
-# Plugin implementation
-sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
-
-    my $bridge = $plugin_config->{bridge};
-    PVE::Network::SDN::Zones::Plugin::find_bridge($bridge);
-
-    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
-    my $is_ovs = PVE::Network::SDN::Zones::Plugin::is_ovs($bridge);
-
-    my $tag = $vnet->{tag};
-    my $alias = $vnet->{alias};
-    my $mtu = $plugin_config->{mtu};
-
-    my $vnet_uplink = "ln_".$vnetid;
-    my $vnet_uplinkpeer = "pr_".$vnetid;
-
-    my @iface_config = ();
-
-    if($is_ovs) {
-
-        # keep vmbrXvY for compatibility with existing network
-        # eth0----ovs vmbr0--(ovsintport tag)---->vnet---->vm
-
-       @iface_config = ();
-       push @iface_config, "ovs_type OVSIntPort";
-       push @iface_config, "ovs_bridge $bridge";
-       push @iface_config, "ovs_mtu $mtu" if $mtu;
-       if($vnet->{vlanaware}) {
-           push @iface_config, "ovs_options vlan_mode=dot1q-tunnel other_config:qinq-ethtype=802.1q tag=$tag";
-       } else {
-           push @iface_config, "ovs_options tag=$tag";
-       }
-       push(@{$config->{$vnet_uplink}}, @iface_config) if !$config->{$vnet_uplink};
-
-       #redefine main ovs bridge, ifupdown2 will merge ovs_ports
-       @iface_config = ();
-       push @iface_config, "ovs_ports $vnet_uplink";
-       push(@{$config->{$bridge}}, @iface_config);
-
-    } elsif ($vlan_aware) {
-        # eth0----vlanaware bridge vmbr0--(vmbr0.X tag)---->vnet---->vm
-       $vnet_uplink = "$bridge.$tag";
-    } else {
-
-        # keep vmbrXvY for compatibility with existing network
-        # eth0<---->eth0.X----vmbr0v10------vnet---->vm
-
-       my $bridgevlan = $bridge."v".$tag;
-
-       my @bridge_ifaces = PVE::Network::SDN::Zones::Plugin::get_bridge_ifaces($bridge);
-
-       my $bridge_ports = "";
-       foreach my $bridge_iface (@bridge_ifaces) {
-           $bridge_ports .= " $bridge_iface.$tag";
-       }
-
-       @iface_config = ();
-       push @iface_config, "link-type veth";
-       push @iface_config, "veth-peer-name $vnet_uplinkpeer";
-       push(@{$config->{$vnet_uplink}}, @iface_config) if !$config->{$vnet_uplink};
-
-       @iface_config = ();
-       push @iface_config, "link-type veth";
-       push @iface_config, "veth-peer-name $vnet_uplink";
-       push(@{$config->{$vnet_uplinkpeer}}, @iface_config) if !$config->{$vnet_uplinkpeer};
-
-       @iface_config = ();
-       push @iface_config, "bridge_ports $bridge_ports $vnet_uplinkpeer";
-       push @iface_config, "bridge_stp off";
-       push @iface_config, "bridge_fd 0";
-       push(@{$config->{$bridgevlan}}, @iface_config) if !$config->{$bridgevlan};
-    }
-
-    #vnet bridge
-    @iface_config = ();
-    push @iface_config, "bridge_ports $vnet_uplink";
-    push @iface_config, "bridge_stp off";
-    push @iface_config, "bridge_fd 0";
-    if($vnet->{vlanaware}) {
-        push @iface_config, "bridge-vlan-aware yes";
-        push @iface_config, "bridge-vids 2-4094";
-    }
-    push @iface_config, "mtu $mtu" if $mtu;
-    push @iface_config, "alias $alias" if $alias;
-    push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
-
-    return $config;
-}
-
-sub status {
-    my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_;
-
-    my $bridge = $plugin_config->{bridge};
-
-    my $err_msg = [];
-    if (!-d "/sys/class/net/$bridge") {
-        push @$err_msg, "missing $bridge";
-       return $err_msg;
-    }
-
-    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
-    my $is_ovs = PVE::Network::SDN::Zones::Plugin::is_ovs($bridge);
-
-    my $tag = $vnet->{tag};
-    my $vnet_uplink = "ln_".$vnetid;
-    my $vnet_uplinkpeer = "pr_".$vnetid;
-
-    # ifaces to check
-    my $ifaces = [ $vnetid, $bridge ];
-    if($is_ovs) {
-       push @$ifaces, $vnet_uplink;
-    } elsif (!$vlan_aware) {
-       my $bridgevlan = $bridge."v".$tag;
-       push @$ifaces, $bridgevlan;
-       push @$ifaces, $vnet_uplink;
-       push @$ifaces, $vnet_uplinkpeer;
-    }
-
-    foreach my $iface (@{$ifaces}) {
-       if (!$status->{$iface}->{status}) {
-           push @$err_msg, "missing $iface";
-        } elsif ($status->{$iface}->{status} ne 'pass') {
-           push @$err_msg, "error iface $iface";
-       }
-    }
-    return $err_msg;
-}
-
-sub vnet_update_hook {
-    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
-
-    my $vnet = $vnet_cfg->{ids}->{$vnetid};
-    my $tag = $vnet->{tag};
-
-    raise_param_exc({ tag => "missing vlan tag"}) if !defined($vnet->{tag});
-    raise_param_exc({ tag => "vlan tag max value is 4096"}) if $vnet->{tag} > 4096;
-
-    # verify that tag is not already defined in another vnet on same zone
-    foreach my $id (keys %{$vnet_cfg->{ids}}) {
-       next if $id eq $vnetid;
-       my $othervnet = $vnet_cfg->{ids}->{$id};
-       my $other_tag = $othervnet->{tag};
-       next if $vnet->{zone} ne $othervnet->{zone};
-       raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $other_tag && $tag eq $other_tag;
-    }
-}
-
-1;
-
-
diff --git a/PVE/Network/SDN/Zones/VxlanPlugin.pm b/PVE/Network/SDN/Zones/VxlanPlugin.pm
deleted file mode 100644 (file)
index c523cf7..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-package PVE::Network::SDN::Zones::VxlanPlugin;
-
-use strict;
-use warnings;
-use PVE::Network::SDN::Zones::Plugin;
-use PVE::Tools qw($IPV4RE);
-use PVE::INotify;
-use PVE::Network::SDN::Controllers::EvpnPlugin;
-use PVE::Exception qw(raise raise_param_exc);
-
-use base('PVE::Network::SDN::Zones::Plugin');
-
-PVE::JSONSchema::register_format('pve-sdn-vxlanrange', \&pve_verify_sdn_vxlanrange);
-sub pve_verify_sdn_vxlanrange {
-   my ($vxlanstr) = @_;
-
-   PVE::Network::SDN::Zones::Plugin::parse_tag_number_or_range($vxlanstr, '16777216');
-
-   return $vxlanstr;
-}
-
-sub type {
-    return 'vxlan';
-}
-
-sub properties {
-    return {
-        'peers' => {
-            description => "peers address list.",
-            type => 'string', format => 'ip-list'
-        },
-    };
-}
-
-sub options {
-    return {
-       nodes => { optional => 1},
-       peers => { optional => 0 },
-       mtu => { optional => 1 },
-       dns => { optional => 1 },
-       reversedns => { optional => 1 },
-       dnszone => { optional => 1 },
-       ipam => { optional => 1 },
-    };
-}
-
-# Plugin implementation
-sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
-
-    my $tag = $vnet->{tag};
-    my $alias = $vnet->{alias};
-    my $multicastaddress = $plugin_config->{'multicast-address'};
-    my @peers;
-    @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'};
-    my $vxlan_iface = "vxlan_$vnetid";
-
-    die "missing vxlan tag" if !$tag;
-
-    my ($ifaceip, $iface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers);
-
-    my $mtu = 1450;
-    $mtu = $interfaces_config->{$iface}->{mtu} - 50 if $interfaces_config->{$iface}->{mtu};
-    $mtu = $plugin_config->{mtu} if $plugin_config->{mtu};
-
-    #vxlan interface
-    my @iface_config = ();
-    push @iface_config, "vxlan-id $tag";
-
-    for my $address (@peers) {
-       next if $address eq $ifaceip;
-       push @iface_config, "vxlan_remoteip $address";
-    }
-
-
-    push @iface_config, "mtu $mtu" if $mtu;
-    push(@{$config->{$vxlan_iface}}, @iface_config) if !$config->{$vxlan_iface};
-
-    #vnet bridge
-    @iface_config = ();
-    push @iface_config, "bridge_ports $vxlan_iface";
-    push @iface_config, "bridge_stp off";
-    push @iface_config, "bridge_fd 0";
-    if ($vnet->{vlanaware}) {
-       push @iface_config, "bridge-vlan-aware yes";
-       push @iface_config, "bridge-vids 2-4094";
-    }
-    push @iface_config, "mtu $mtu" if $mtu;
-    push @iface_config, "alias $alias" if $alias;
-    push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
-
-    return $config;
-}
-
-sub vnet_update_hook {
-    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
-
-    my $vnet = $vnet_cfg->{ids}->{$vnetid};
-    my $tag = $vnet->{tag};
-
-    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($tag);
-    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $tag > 16777216;
-
-    # verify that tag is not already defined globally (vxlan-id are unique)
-    for my $id (sort keys %{$vnet_cfg->{ids}}) {
-       next if $id eq $vnetid;
-       my $othervnet = $vnet_cfg->{ids}->{$id};
-       my $other_tag = $othervnet->{tag};
-       my $other_zoneid = $othervnet->{zone};
-       my $other_zone = $zone_cfg->{ids}->{$other_zoneid};
-       next if $other_zone->{type} ne 'vxlan' && $other_zone->{type} ne 'evpn';
-       raise_param_exc({ tag => "vxlan tag $tag already exist in vnet $id in zone $other_zoneid "}) if $other_tag && $tag eq $other_tag;
-    }
-}
-
-1;
-
-
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..1529c87
--- /dev/null
@@ -0,0 +1,15 @@
+all:
+       $(MAKE) -C PVE
+
+.PHONY: clean
+clean:
+       $(MAKE) -C test $@
+       $(MAKE) -C PVE $@
+
+.PHONY: test
+test:
+       $(MAKE) -C $@
+
+.PHONY: install
+install:
+       $(MAKE) -C PVE $@
diff --git a/src/PVE/API2/Makefile b/src/PVE/API2/Makefile
new file mode 100644 (file)
index 0000000..28b2830
--- /dev/null
@@ -0,0 +1,4 @@
+
+.PHONY: install
+install:
+       make -C Network install
diff --git a/src/PVE/API2/Network/Makefile b/src/PVE/API2/Network/Makefile
new file mode 100644 (file)
index 0000000..396f79d
--- /dev/null
@@ -0,0 +1,9 @@
+SOURCES=SDN.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/API2/Network/$$i; done
+       make -C SDN install
diff --git a/src/PVE/API2/Network/SDN.pm b/src/PVE/API2/Network/SDN.pm
new file mode 100644 (file)
index 0000000..f129d60
--- /dev/null
@@ -0,0 +1,144 @@
+package PVE::API2::Network::SDN;
+
+use strict;
+use warnings;
+
+use PVE::Cluster qw(cfs_lock_file cfs_read_file cfs_write_file);
+use PVE::Exception qw(raise_param_exc);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RESTHandler;
+use PVE::RPCEnvironment;
+use PVE::SafeSyslog;
+use PVE::Tools qw(run_command);
+use PVE::Network::SDN;
+
+use PVE::API2::Network::SDN::Controllers;
+use PVE::API2::Network::SDN::Vnets;
+use PVE::API2::Network::SDN::Zones;
+use PVE::API2::Network::SDN::Ipams;
+use PVE::API2::Network::SDN::Dns;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Vnets",
+    path => 'vnets',
+});
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Zones",
+    path => 'zones',
+});
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Controllers",
+    path => 'controllers',
+});
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Ipams",
+    path => 'ipams',
+});
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Dns",
+    path => 'dns',
+});
+
+__PACKAGE__->register_method({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "Directory index.",
+    permissions => {
+       check => ['perm', '/', [ 'SDN.Audit' ]],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {},
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => {
+               id => { type => 'string' },
+           },
+       },
+       links => [ { rel => 'child', href => "{id}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $res = [
+           { id => 'vnets' },
+           { id => 'zones' },
+           { id => 'controllers' },
+           { id => 'ipams' },
+           { id => 'dns' },
+       ];
+
+       return $res;
+    }});
+
+my $create_reload_network_worker = sub {
+    my ($nodename) = @_;
+
+    # FIXME: how to proxy to final node ?
+    my $upid;
+    run_command(['pvesh', 'set', "/nodes/$nodename/network"], outfunc => sub {
+       my $line = shift;
+       if ($line =~ /^["']?(UPID:[^\s"']+)["']?$/) {
+           $upid = $1;
+       }
+    });
+    #my $upid = PVE::API2::Network->reload_network_config(node => $nodename});
+    my $res = PVE::Tools::upid_decode($upid);
+
+    return $res->{pid};
+};
+
+__PACKAGE__->register_method ({
+    name => 'reload',
+    protected => 1,
+    path => '',
+    method => 'PUT',
+    description => "Apply sdn controller changes && reload.",
+    permissions => {
+       check => ['perm', '/sdn', ['SDN.Allocate']],
+    },
+    parameters => {
+        additionalProperties => 0,
+    },
+    returns => {
+        type => 'string',
+    },
+    code => sub {
+        my ($param) = @_;
+
+        my $rpcenv = PVE::RPCEnvironment::get();
+        my $authuser = $rpcenv->get_user();
+
+       PVE::Network::SDN::commit_config();
+
+        my $code = sub {
+            $rpcenv->{type} = 'priv'; # to start tasks in background
+           PVE::Cluster::check_cfs_quorum();
+           my $nodelist = PVE::Cluster::get_nodelist();
+           for my $node (@$nodelist) {
+               my $pid = eval { $create_reload_network_worker->($node) };
+               warn $@ if $@;
+           }
+
+           # FIXME: use libpve-apiclient (like in cluster join) to create
+           # tasks and moitor the tasks.
+
+           return;
+        };
+
+        return $rpcenv->fork_worker('reloadnetworkall', undef, $authuser, $code);
+
+    }});
+
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Controllers.pm b/src/PVE/API2/Network/SDN/Controllers.pm
new file mode 100644 (file)
index 0000000..d8f18ab
--- /dev/null
@@ -0,0 +1,290 @@
+package PVE::API2::Network::SDN::Controllers;
+
+use strict;
+use warnings;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Controllers;
+use PVE::Network::SDN::Controllers::Plugin;
+use PVE::Network::SDN::Controllers::EvpnPlugin;
+use PVE::Network::SDN::Controllers::BgpPlugin;
+use PVE::Network::SDN::Controllers::FaucetPlugin;
+
+use Storable qw(dclone);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $sdn_controllers_type_enum = PVE::Network::SDN::Controllers::Plugin->lookup_types();
+
+my $api_sdn_controllers_config = sub {
+    my ($cfg, $id) = @_;
+
+    my $scfg = dclone(PVE::Network::SDN::Controllers::sdn_controllers_config($cfg, $id));
+    $scfg->{controller} = $id;
+    $scfg->{digest} = $cfg->{digest};
+
+    return $scfg;
+};
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "SDN controllers index.",
+    permissions => {
+       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/controllers/<controller>'",
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           type => {
+               description => "Only list sdn controllers of specific type",
+               type => 'string',
+               enum => $sdn_controllers_type_enum,
+               optional => 1,
+           },
+           running => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display running config.",
+           },
+           pending => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display pending config.",
+           },
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => { controller => { type => 'string' },
+                           type => { type => 'string' },
+                           state => { type => 'string', optional => 1 },
+                            pending => { optional => 1},
+           },
+       },
+       links => [ { rel => 'child', href => "{controller}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+
+        my $cfg = {};
+        if($param->{pending}) {
+            my $running_cfg = PVE::Network::SDN::running_config();
+            my $config = PVE::Network::SDN::Controllers::config();
+            $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'controllers');
+        } elsif ($param->{running}) {
+            my $running_cfg = PVE::Network::SDN::running_config();
+            $cfg = $running_cfg->{controllers};
+        } else {
+            $cfg = PVE::Network::SDN::Controllers::config();
+        }
+
+       my @sids = PVE::Network::SDN::Controllers::sdn_controllers_ids($cfg);
+       my $res = [];
+       foreach my $id (@sids) {
+           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+           next if !$rpcenv->check_any($authuser, "/sdn/controllers/$id", $privs, 1);
+
+           my $scfg = &$api_sdn_controllers_config($cfg, $id);
+           next if $param->{type} && $param->{type} ne $scfg->{type};
+
+           my $plugin_config = $cfg->{ids}->{$id};
+           my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
+           push @$res, $scfg;
+       }
+
+       return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'read',
+    path => '{controller}',
+    method => 'GET',
+    description => "Read sdn controller configuration.",
+    permissions => {
+       check => ['perm', '/sdn/controllers/{controller}', ['SDN.Allocate']],
+   },
+
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           controller => get_standard_option('pve-sdn-controller-id'),
+           running => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display running config.",
+           },
+           pending => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display pending config.",
+           },
+       },
+    },
+    returns => { type => 'object' },
+    code => sub {
+       my ($param) = @_;
+
+        my $cfg = {};
+        if($param->{pending}) {
+            my $running_cfg = PVE::Network::SDN::running_config();
+            my $config = PVE::Network::SDN::Controllers::config();
+            $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'controllers');
+        } elsif ($param->{running}) {
+            my $running_cfg = PVE::Network::SDN::running_config();
+            $cfg = $running_cfg->{controllers};
+        } else {
+            $cfg = PVE::Network::SDN::Controllers::config();
+        }
+
+       return &$api_sdn_controllers_config($cfg, $param->{controller});
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    path => '',
+    method => 'POST',
+    description => "Create a new sdn controller object.",
+    permissions => {
+       check => ['perm', '/sdn/controllers', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Controllers::Plugin->createSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $type = extract_param($param, 'type');
+       my $id = extract_param($param, 'controller');
+
+       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($type);
+       my $opts = $plugin->check_config($id, $param, 1, 1);
+
+        # create /etc/pve/sdn directory
+        PVE::Cluster::check_cfs_quorum();
+        mkdir("/etc/pve/sdn");
+
+        PVE::Network::SDN::lock_sdn_config(
+           sub {
+
+               my $controller_cfg = PVE::Network::SDN::Controllers::config();
+
+               my $scfg = undef;
+               if ($scfg = PVE::Network::SDN::Controllers::sdn_controllers_config($controller_cfg, $id, 1)) {
+                   die "sdn controller object ID '$id' already defined\n";
+               }
+
+               $controller_cfg->{ids}->{$id} = $opts;
+               $plugin->on_update_hook($id, $controller_cfg);
+
+               PVE::Network::SDN::Controllers::write_config($controller_cfg);
+
+           }, "create sdn controller object failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    path => '{controller}',
+    method => 'PUT',
+    description => "Update sdn controller object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/controllers', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Controllers::Plugin->updateSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'controller');
+       my $digest = extract_param($param, 'digest');
+
+        PVE::Network::SDN::lock_sdn_config(
+        sub {
+
+           my $controller_cfg = PVE::Network::SDN::Controllers::config();
+
+           PVE::SectionConfig::assert_if_modified($controller_cfg, $digest);
+
+           my $scfg = PVE::Network::SDN::Controllers::sdn_controllers_config($controller_cfg, $id);
+
+           my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($scfg->{type});
+           my $opts = $plugin->check_config($id, $param, 0, 1);
+
+           foreach my $k (%$opts) {
+               $scfg->{$k} = $opts->{$k};
+           }
+
+           $plugin->on_update_hook($id, $controller_cfg);
+
+           PVE::Network::SDN::Controllers::write_config($controller_cfg);
+
+
+           }, "update sdn controller object failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    path => '{controller}',
+    method => 'DELETE',
+    description => "Delete sdn controller object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/controllers', ['SDN.Allocate']],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           controller => get_standard_option('pve-sdn-controller-id', {
+                completion => \&PVE::Network::SDN::Controllers::complete_sdn_controllers,
+            }),
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'controller');
+
+        PVE::Network::SDN::lock_sdn_config(
+           sub {
+
+               my $cfg = PVE::Network::SDN::Controllers::config();
+
+               my $scfg = PVE::Network::SDN::Controllers::sdn_controllers_config($cfg, $id);
+
+               my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($scfg->{type});
+
+               my $zone_cfg = PVE::Network::SDN::Zones::config();
+
+               $plugin->on_delete_hook($id, $zone_cfg);
+
+               delete $cfg->{ids}->{$id};
+               PVE::Network::SDN::Controllers::write_config($cfg);
+
+           }, "delete sdn controller object failed");
+
+
+       return undef;
+    }});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Dns.pm b/src/PVE/API2/Network/SDN/Dns.pm
new file mode 100644 (file)
index 0000000..3d08552
--- /dev/null
@@ -0,0 +1,242 @@
+package PVE::API2::Network::SDN::Dns;
+
+use strict;
+use warnings;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Network::SDN;
+use PVE::Network::SDN::Dns;
+use PVE::Network::SDN::Dns::Plugin;
+use PVE::Network::SDN::Dns::PowerdnsPlugin;
+
+use Storable qw(dclone);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $sdn_dns_type_enum = PVE::Network::SDN::Dns::Plugin->lookup_types();
+
+my $api_sdn_dns_config = sub {
+    my ($cfg, $id) = @_;
+
+    my $scfg = dclone(PVE::Network::SDN::Dns::sdn_dns_config($cfg, $id));
+    $scfg->{dns} = $id;
+    $scfg->{digest} = $cfg->{digest};
+
+    return $scfg;
+};
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "SDN dns index.",
+    permissions => {
+       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/dns/<dns>'",
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           type => {
+               description => "Only list sdn dns of specific type",
+               type => 'string',
+               enum => $sdn_dns_type_enum,
+               optional => 1,
+           },
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => { dns => { type => 'string'},
+                           type => { type => 'string'},
+                         },
+       },
+       links => [ { rel => 'child', href => "{dns}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+
+
+       my $cfg = PVE::Network::SDN::Dns::config();
+
+       my @sids = PVE::Network::SDN::Dns::sdn_dns_ids($cfg);
+       my $res = [];
+       foreach my $id (@sids) {
+           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+           next if !$rpcenv->check_any($authuser, "/sdn/dns/$id", $privs, 1);
+
+           my $scfg = &$api_sdn_dns_config($cfg, $id);
+           next if $param->{type} && $param->{type} ne $scfg->{type};
+
+           my $plugin_config = $cfg->{ids}->{$id};
+           my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+           push @$res, $scfg;
+       }
+
+       return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'read',
+    path => '{dns}',
+    method => 'GET',
+    description => "Read sdn dns configuration.",
+    permissions => {
+       check => ['perm', '/sdn/dns/{dns}', ['SDN.Allocate']],
+   },
+
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           dns => get_standard_option('pve-sdn-dns-id'),
+       },
+    },
+    returns => { type => 'object' },
+    code => sub {
+       my ($param) = @_;
+
+       my $cfg = PVE::Network::SDN::Dns::config();
+
+       return &$api_sdn_dns_config($cfg, $param->{dns});
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    path => '',
+    method => 'POST',
+    description => "Create a new sdn dns object.",
+    permissions => {
+       check => ['perm', '/sdn/dns', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Dns::Plugin->createSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $type = extract_param($param, 'type');
+       my $id = extract_param($param, 'dns');
+
+       my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($type);
+       my $opts = $plugin->check_config($id, $param, 1, 1);
+
+        # create /etc/pve/sdn directory
+        PVE::Cluster::check_cfs_quorum();
+        mkdir("/etc/pve/sdn");
+
+        PVE::Network::SDN::lock_sdn_config(
+           sub {
+
+               my $dns_cfg = PVE::Network::SDN::Dns::config();
+
+               my $scfg = undef;
+               if ($scfg = PVE::Network::SDN::Dns::sdn_dns_config($dns_cfg, $id, 1)) {
+                   die "sdn dns object ID '$id' already defined\n";
+               }
+
+               $dns_cfg->{ids}->{$id} = $opts;
+
+               my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($opts->{type});
+               $plugin->on_update_hook($opts);
+
+               PVE::Network::SDN::Dns::write_config($dns_cfg);
+
+           }, "create sdn dns object failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    path => '{dns}',
+    method => 'PUT',
+    description => "Update sdn dns object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/dns', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Dns::Plugin->updateSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'dns');
+       my $digest = extract_param($param, 'digest');
+
+        PVE::Network::SDN::lock_sdn_config(
+        sub {
+
+           my $dns_cfg = PVE::Network::SDN::Dns::config();
+
+           PVE::SectionConfig::assert_if_modified($dns_cfg, $digest);
+
+           my $scfg = PVE::Network::SDN::Dns::sdn_dns_config($dns_cfg, $id);
+
+           my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
+           my $opts = $plugin->check_config($id, $param, 0, 1);
+
+           foreach my $k (%$opts) {
+               $scfg->{$k} = $opts->{$k};
+           }
+
+           $plugin->on_update_hook($scfg);
+
+           PVE::Network::SDN::Dns::write_config($dns_cfg);
+
+           }, "update sdn dns object failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    path => '{dns}',
+    method => 'DELETE',
+    description => "Delete sdn dns object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/dns', ['SDN.Allocate']],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           dns => get_standard_option('pve-sdn-dns-id', {
+                completion => \&PVE::Network::SDN::Dns::complete_sdn_dns,
+            }),
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'dns');
+
+        PVE::Network::SDN::lock_sdn_config(
+           sub {
+
+               my $cfg = PVE::Network::SDN::Dns::config();
+
+               my $scfg = PVE::Network::SDN::Dns::sdn_dns_config($cfg, $id);
+
+               my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
+
+               delete $cfg->{ids}->{$id};
+               PVE::Network::SDN::Dns::write_config($cfg);
+
+           }, "delete sdn dns object failed");
+
+       return undef;
+    }});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Ipams.pm b/src/PVE/API2/Network/SDN/Ipams.pm
new file mode 100644 (file)
index 0000000..6410e8e
--- /dev/null
@@ -0,0 +1,248 @@
+package PVE::API2::Network::SDN::Ipams;
+
+use strict;
+use warnings;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Network::SDN;
+use PVE::Network::SDN::Ipams;
+use PVE::Network::SDN::Ipams::Plugin;
+use PVE::Network::SDN::Ipams::PVEPlugin;
+use PVE::Network::SDN::Ipams::PhpIpamPlugin;
+use PVE::Network::SDN::Ipams::NetboxPlugin;
+
+use Storable qw(dclone);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $sdn_ipams_type_enum = PVE::Network::SDN::Ipams::Plugin->lookup_types();
+
+my $api_sdn_ipams_config = sub {
+    my ($cfg, $id) = @_;
+
+    my $scfg = dclone(PVE::Network::SDN::Ipams::sdn_ipams_config($cfg, $id));
+    $scfg->{ipam} = $id;
+    $scfg->{digest} = $cfg->{digest};
+
+    return $scfg;
+};
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "SDN ipams index.",
+    permissions => {
+       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/ipams/<ipam>'",
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           type => {
+               description => "Only list sdn ipams of specific type",
+               type => 'string',
+               enum => $sdn_ipams_type_enum,
+               optional => 1,
+           },
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => { ipam => { type => 'string'},
+                           type => { type => 'string'},
+                         },
+       },
+       links => [ { rel => 'child', href => "{ipam}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+
+
+       my $cfg = PVE::Network::SDN::Ipams::config();
+
+       my @sids = PVE::Network::SDN::Ipams::sdn_ipams_ids($cfg);
+       my $res = [];
+       foreach my $id (@sids) {
+           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+           next if !$rpcenv->check_any($authuser, "/sdn/ipams/$id", $privs, 1);
+
+           my $scfg = &$api_sdn_ipams_config($cfg, $id);
+           next if $param->{type} && $param->{type} ne $scfg->{type};
+
+           my $plugin_config = $cfg->{ids}->{$id};
+           my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+           push @$res, $scfg;
+       }
+
+       return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'read',
+    path => '{ipam}',
+    method => 'GET',
+    description => "Read sdn ipam configuration.",
+    permissions => {
+       check => ['perm', '/sdn/ipams/{ipam}', ['SDN.Allocate']],
+   },
+
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           ipam => get_standard_option('pve-sdn-ipam-id'),
+       },
+    },
+    returns => { type => 'object' },
+    code => sub {
+       my ($param) = @_;
+
+       my $cfg = PVE::Network::SDN::Ipams::config();
+
+       return &$api_sdn_ipams_config($cfg, $param->{ipam});
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    path => '',
+    method => 'POST',
+    description => "Create a new sdn ipam object.",
+    permissions => {
+       check => ['perm', '/sdn/ipams', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Ipams::Plugin->createSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $type = extract_param($param, 'type');
+       my $id = extract_param($param, 'ipam');
+
+       my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($type);
+       my $opts = $plugin->check_config($id, $param, 1, 1);
+
+        # create /etc/pve/sdn directory
+        PVE::Cluster::check_cfs_quorum();
+        mkdir("/etc/pve/sdn");
+
+        PVE::Network::SDN::lock_sdn_config(
+           sub {
+
+               my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+               my $controller_cfg = PVE::Network::SDN::Controllers::config();
+
+               my $scfg = undef;
+               if ($scfg = PVE::Network::SDN::Ipams::sdn_ipams_config($ipam_cfg, $id, 1)) {
+                   die "sdn ipam object ID '$id' already defined\n";
+               }
+
+               $ipam_cfg->{ids}->{$id} = $opts;
+
+               my $plugin_config = $opts;
+               my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+               $plugin->on_update_hook($plugin_config);
+
+               PVE::Network::SDN::Ipams::write_config($ipam_cfg);
+
+           }, "create sdn ipam object failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    path => '{ipam}',
+    method => 'PUT',
+    description => "Update sdn ipam object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/ipams', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Ipams::Plugin->updateSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'ipam');
+       my $digest = extract_param($param, 'digest');
+
+        PVE::Network::SDN::lock_sdn_config(
+        sub {
+
+           my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+
+           PVE::SectionConfig::assert_if_modified($ipam_cfg, $digest);
+
+           my $scfg = PVE::Network::SDN::Ipams::sdn_ipams_config($ipam_cfg, $id);
+
+           my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($scfg->{type});
+           my $opts = $plugin->check_config($id, $param, 0, 1);
+
+           foreach my $k (%$opts) {
+               $scfg->{$k} = $opts->{$k};
+           }
+
+            $plugin->on_update_hook($scfg);
+
+           PVE::Network::SDN::Ipams::write_config($ipam_cfg);
+
+           }, "update sdn ipam object failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    path => '{ipam}',
+    method => 'DELETE',
+    description => "Delete sdn ipam object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/ipams', ['SDN.Allocate']],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           ipam => get_standard_option('pve-sdn-ipam-id', {
+                completion => \&PVE::Network::SDN::Ipams::complete_sdn_ipams,
+            }),
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'ipam');
+
+        PVE::Network::SDN::lock_sdn_config(
+           sub {
+
+               my $cfg = PVE::Network::SDN::Ipams::config();
+
+               my $scfg = PVE::Network::SDN::Ipams::sdn_ipams_config($cfg, $id);
+
+               my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($scfg->{type});
+
+               my $vnet_cfg = PVE::Network::SDN::Vnets::config();
+
+               delete $cfg->{ids}->{$id};
+               PVE::Network::SDN::Ipams::write_config($cfg);
+
+           }, "delete sdn zone object failed");
+
+       return undef;
+    }});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Makefile b/src/PVE/API2/Network/SDN/Makefile
new file mode 100644 (file)
index 0000000..3683fa4
--- /dev/null
@@ -0,0 +1,10 @@
+SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/API2/Network/SDN/$$i; done
+       make -C Zones install
+
diff --git a/src/PVE/API2/Network/SDN/Subnets.pm b/src/PVE/API2/Network/SDN/Subnets.pm
new file mode 100644 (file)
index 0000000..377a568
--- /dev/null
@@ -0,0 +1,303 @@
+package PVE::API2::Network::SDN::Subnets;
+
+use strict;
+use warnings;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::Network::SDN;
+use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::SubnetPlugin;
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Ipams;
+use PVE::Network::SDN::Ipams::Plugin;
+
+use Storable qw(dclone);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $api_sdn_subnets_config = sub {
+    my ($cfg, $id) = @_;
+
+    my $scfg = dclone(PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id));
+    $scfg->{subnet} = $id;
+    $scfg->{digest} = $cfg->{digest};
+
+    return $scfg;
+};
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "SDN subnets index.",
+    permissions => {
+       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/subnets/<subnet>'",
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           vnet => get_standard_option('pve-sdn-vnet-id'),
+           running => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display running config.",
+           },
+           pending => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display pending config.",
+           },
+        },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => {},
+       },
+       links => [ { rel => 'child', href => "{subnet}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+
+        my $vnetid = $param->{vnet};
+
+        my $cfg = {};
+        if($param->{pending}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           my $config = PVE::Network::SDN::Subnets::config();
+           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'subnets');
+        } elsif ($param->{running}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           $cfg = $running_cfg->{subnets};
+        } else {
+           $cfg = PVE::Network::SDN::Subnets::config();
+        }
+
+       my @sids = PVE::Network::SDN::Subnets::sdn_subnets_ids($cfg);
+       my $res = [];
+       foreach my $id (@sids) {
+           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+           next if !$rpcenv->check_any($authuser, "/sdn/vnets/$vnetid/subnets/$id", $privs, 1);
+
+           my $scfg = &$api_sdn_subnets_config($cfg, $id);
+           next if !$scfg->{vnet} || $scfg->{vnet} ne $vnetid;
+           push @$res, $scfg;
+       }
+
+       return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'read',
+    path => '{subnet}',
+    method => 'GET',
+    description => "Read sdn subnet configuration.",
+    permissions => {
+       check => ['perm', '/sdn/vnets/{vnet}/subnets/{subnet}', ['SDN.Allocate']],
+   },
+
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           vnet => get_standard_option('pve-sdn-vnet-id'),
+           subnet => get_standard_option('pve-sdn-subnet-id', {
+               completion => \&PVE::Network::SDN::Subnets::complete_sdn_subnets,
+           }),
+           running => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display running config.",
+           },
+           pending => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display pending config.",
+           },
+        },
+    },
+    returns => { type => 'object' },
+    code => sub {
+       my ($param) = @_;
+
+        my $cfg = {};
+        if($param->{pending}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           my $config = PVE::Network::SDN::Subnets::config();
+           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'subnets');
+        } elsif ($param->{running}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           $cfg = $running_cfg->{subnets};
+        } else {
+           $cfg = PVE::Network::SDN::Subnets::config();
+        }
+
+        my $scfg = &$api_sdn_subnets_config($cfg, $param->{subnet});
+
+       raise_param_exc({ vnet => "wrong vnet"}) if $param->{vnet} ne $scfg->{vnet};
+
+       return $scfg;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    path => '',
+    method => 'POST',
+    description => "Create a new sdn subnet object.",
+    permissions => {
+       check => ['perm', '/sdn/vnets/{vnet}/subnets', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::SubnetPlugin->createSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $type = extract_param($param, 'type');
+       my $cidr = extract_param($param, 'subnet');
+
+       # create /etc/pve/sdn directory
+       PVE::Cluster::check_cfs_quorum();
+       mkdir("/etc/pve/sdn") if ! -d '/etc/pve/sdn';
+
+        PVE::Network::SDN::lock_sdn_config(
+           sub {
+
+               my $cfg = PVE::Network::SDN::Subnets::config();
+               my $zone_cfg = PVE::Network::SDN::Zones::config();
+               my $vnet_cfg = PVE::Network::SDN::Vnets::config();
+               my $vnet = $param->{vnet};
+               my $zoneid = $vnet_cfg->{ids}->{$vnet}->{zone};
+               my $zone = $zone_cfg->{ids}->{$zoneid};      
+               my $id = $cidr =~ s/\//-/r;
+               $id = "$zoneid-$id";
+               
+               my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 1, 1);
+
+               my $scfg = undef;
+               if ($scfg = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id, 1)) {
+                   die "sdn subnet object ID '$id' already defined\n";
+               }
+
+               $cfg->{ids}->{$id} = $opts;
+
+               my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id);
+               PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $subnet);
+
+               PVE::Network::SDN::Subnets::write_config($cfg);
+
+           }, "create sdn subnet object failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    path => '{subnet}',
+    method => 'PUT',
+    description => "Update sdn subnet object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/vnets/{vnet}/subnets', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::SubnetPlugin->updateSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'subnet');
+       my $digest = extract_param($param, 'digest');
+
+        PVE::Network::SDN::lock_sdn_config(
+        sub {
+
+           my $cfg = PVE::Network::SDN::Subnets::config();
+           my $zone_cfg = PVE::Network::SDN::Zones::config();
+           my $vnet_cfg = PVE::Network::SDN::Vnets::config();
+           my $vnet = $param->{vnet};
+           my $zoneid = $vnet_cfg->{ids}->{$vnet}->{zone};
+           my $zone = $zone_cfg->{ids}->{$zoneid};
+
+           my $scfg = &$api_sdn_subnets_config($cfg, $id);
+
+           PVE::SectionConfig::assert_if_modified($cfg, $digest);
+
+           my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 0, 1);
+           $cfg->{ids}->{$id} = $opts;
+
+           raise_param_exc({ ipam => "you can't change ipam"}) if $opts->{ipam} && $scfg->{ipam} && $opts->{ipam} ne $scfg->{ipam};
+
+           my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id);
+           PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $subnet, $scfg);
+
+           PVE::Network::SDN::Subnets::write_config($cfg);
+
+           }, "update sdn subnet object failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    path => '{subnet}',
+    method => 'DELETE',
+    description => "Delete sdn subnet object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/vnets/{vnet}/subnets', ['SDN.Allocate']],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+            vnet => get_standard_option('pve-sdn-vnet-id'),
+           subnet => get_standard_option('pve-sdn-subnet-id', {
+                completion => \&PVE::Network::SDN::Subnets::complete_sdn_subnets,
+            }),
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'subnet');
+
+        PVE::Network::SDN::lock_sdn_config(
+           sub {
+               my $cfg = PVE::Network::SDN::Subnets::config();
+
+               my $scfg = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id, 1);
+
+               my $vnets_cfg = PVE::Network::SDN::Vnets::config();
+
+               PVE::Network::SDN::SubnetPlugin->on_delete_hook($id, $cfg, $vnets_cfg);
+
+               my $zone_cfg = PVE::Network::SDN::Zones::config();
+               my $vnet = $param->{vnet};
+               my $zoneid = $vnets_cfg->{ids}->{$vnet}->{zone};
+               my $zone = $zone_cfg->{ids}->{$zoneid};
+
+               PVE::Network::SDN::Subnets::del_subnet($zone, $id, $scfg);
+
+               delete $cfg->{ids}->{$id};
+
+               PVE::Network::SDN::Subnets::write_config($cfg);
+
+           }, "delete sdn subnet object failed");
+
+
+       return undef;
+    }});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Vnets.pm b/src/PVE/API2/Network/SDN/Vnets.pm
new file mode 100644 (file)
index 0000000..811a2e8
--- /dev/null
@@ -0,0 +1,292 @@
+package PVE::API2::Network::SDN::Vnets;
+
+use strict;
+use warnings;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Network::SDN;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Zones::Plugin;
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::VnetPlugin;
+use PVE::Network::SDN::Subnets;
+use PVE::API2::Network::SDN::Subnets;
+
+use Storable qw(dclone);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+use PVE::Exception qw(raise raise_param_exc);
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Subnets",
+    path => '{vnet}/subnets',
+});
+
+my $api_sdn_vnets_config = sub {
+    my ($cfg, $id) = @_;
+
+    my $scfg = dclone(PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $id));
+    $scfg->{vnet} = $id;
+    $scfg->{digest} = $cfg->{digest};
+    
+    return $scfg;
+};
+
+my $api_sdn_vnets_deleted_config = sub {
+    my ($cfg, $running_cfg, $id) = @_;
+
+    if (!$cfg->{ids}->{$id}) {
+
+       my $vnet_cfg = dclone(PVE::Network::SDN::Vnets::sdn_vnets_config($running_cfg->{vnets}, $id));
+       $vnet_cfg->{state} = "deleted";
+       $vnet_cfg->{vnet} = $id;
+       return $vnet_cfg;
+    }
+};
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "SDN vnets index.",
+    permissions => {
+       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate'"
+           ." permissions on '/sdn/vnets/<vnet>'",
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+            running => {
+                type => 'boolean',
+                optional => 1,
+                description => "Display running config.",
+            },
+           pending => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display pending config.",
+           },
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => {},
+       },
+       links => [ { rel => 'child', href => "{vnet}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+
+       my $cfg = {};
+       if($param->{pending}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           my $config = PVE::Network::SDN::Vnets::config();
+           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'vnets');
+       } elsif ($param->{running}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           $cfg = $running_cfg->{vnets};
+       } else {
+           $cfg = PVE::Network::SDN::Vnets::config();
+       }
+
+       my @sids = PVE::Network::SDN::Vnets::sdn_vnets_ids($cfg);
+       my $res = [];
+       foreach my $id (@sids) {
+           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+           next if !$rpcenv->check_any($authuser, "/sdn/vnets/$id", $privs, 1);
+
+           my $scfg = &$api_sdn_vnets_config($cfg, $id);
+           push @$res, $scfg;
+       }
+
+       return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'read',
+    path => '{vnet}',
+    method => 'GET',
+    description => "Read sdn vnet configuration.",
+    permissions => {
+       check => ['perm', '/sdn/vnets/{vnet}', ['SDN.Allocate']],
+   },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           vnet => get_standard_option('pve-sdn-vnet-id', {
+               completion => \&PVE::Network::SDN::Vnets::complete_sdn_vnets,
+           }),
+            running => {
+                type => 'boolean',
+                optional => 1,
+                description => "Display running config.",
+            },
+           pending => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display pending config.",
+           },
+       },
+    },
+    returns => { type => 'object' },
+    code => sub {
+       my ($param) = @_;
+
+       my $cfg = {};
+       if($param->{pending}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           my $config = PVE::Network::SDN::Vnets::config();
+           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'vnets');
+       } elsif ($param->{running}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           $cfg = $running_cfg->{vnets};
+       } else {
+           $cfg = PVE::Network::SDN::Vnets::config();
+       }
+
+       return $api_sdn_vnets_config->($cfg, $param->{vnet});
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    path => '',
+    method => 'POST',
+    description => "Create a new sdn vnet object.",
+    permissions => {
+       check => ['perm', '/sdn/vnets', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::VnetPlugin->createSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $type = extract_param($param, 'type');
+       my $id = extract_param($param, 'vnet');
+
+       PVE::Cluster::check_cfs_quorum();
+       mkdir("/etc/pve/sdn");
+
+        PVE::Network::SDN::lock_sdn_config(sub {
+           my $cfg = PVE::Network::SDN::Vnets::config();
+           my $opts = PVE::Network::SDN::VnetPlugin->check_config($id, $param, 1, 1);
+
+           if (PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $id, 1)) {
+               die "sdn vnet object ID '$id' already defined\n";
+           }
+           $cfg->{ids}->{$id} = $opts;
+
+           my $zone_cfg = PVE::Network::SDN::Zones::config();
+           my $zoneid = $cfg->{ids}->{$id}->{zone};
+           my $plugin_config = $zone_cfg->{ids}->{$zoneid};
+           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+            $plugin->vnet_update_hook($cfg, $id, $zone_cfg);
+
+           PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
+
+           PVE::Network::SDN::Vnets::write_config($cfg);
+
+       }, "create sdn vnet object failed");
+
+       return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    path => '{vnet}',
+    method => 'PUT',
+    description => "Update sdn vnet object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/vnets', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::VnetPlugin->updateSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'vnet');
+       my $digest = extract_param($param, 'digest');
+
+       PVE::Network::SDN::lock_sdn_config(sub {
+           my $cfg = PVE::Network::SDN::Vnets::config();
+
+           PVE::SectionConfig::assert_if_modified($cfg, $digest);
+
+
+           my $opts = PVE::Network::SDN::VnetPlugin->check_config($id, $param, 0, 1);
+           raise_param_exc({ zone => "missing zone"}) if !$opts->{zone};
+           my $subnets = PVE::Network::SDN::Vnets::get_subnets($id);
+           raise_param_exc({ zone => "can't change zone if subnets exists"}) if($subnets && $opts->{zone} ne $cfg->{ids}->{$id}->{zone});
+
+           $cfg->{ids}->{$id} = $opts;
+
+           my $zone_cfg = PVE::Network::SDN::Zones::config();
+           my $zoneid = $cfg->{ids}->{$id}->{zone};
+           my $plugin_config = $zone_cfg->{ids}->{$zoneid};
+           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+           $plugin->vnet_update_hook($cfg, $id, $zone_cfg);
+
+           PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
+
+           PVE::Network::SDN::Vnets::write_config($cfg);
+
+       }, "update sdn vnet object failed");
+
+       return undef;
+    }
+});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    path => '{vnet}',
+    method => 'DELETE',
+    description => "Delete sdn vnet object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/vnets', ['SDN.Allocate']],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           vnet => get_standard_option('pve-sdn-vnet-id', {
+               completion => \&PVE::Network::SDN::Vnets::complete_sdn_vnets,
+           }),
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'vnet');
+
+        PVE::Network::SDN::lock_sdn_config(sub {
+           my $cfg = PVE::Network::SDN::Vnets::config();
+           my $scfg = PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $id); # check if exists
+           my $vnet_cfg = PVE::Network::SDN::Vnets::config();
+
+           PVE::Network::SDN::VnetPlugin->on_delete_hook($id, $vnet_cfg);
+
+           delete $cfg->{ids}->{$id};
+           PVE::Network::SDN::Vnets::write_config($cfg);
+
+       }, "delete sdn vnet object failed");
+
+
+       return undef;
+    }
+});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Zones.pm b/src/PVE/API2/Network/SDN/Zones.pm
new file mode 100644 (file)
index 0000000..6e53240
--- /dev/null
@@ -0,0 +1,351 @@
+package PVE::API2::Network::SDN::Zones;
+
+use strict;
+use warnings;
+
+use Storable qw(dclone);
+
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+
+use PVE::Network::SDN::Dns;
+use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN;
+
+use PVE::Network::SDN::Zones::EvpnPlugin;
+use PVE::Network::SDN::Zones::FaucetPlugin;
+use PVE::Network::SDN::Zones::Plugin;
+use PVE::Network::SDN::Zones::QinQPlugin;
+use PVE::Network::SDN::Zones::SimplePlugin;
+use PVE::Network::SDN::Zones::VlanPlugin;
+use PVE::Network::SDN::Zones::VxlanPlugin;
+use PVE::Network::SDN::Zones;
+
+use PVE::RESTHandler;
+use base qw(PVE::RESTHandler);
+
+my $sdn_zones_type_enum = PVE::Network::SDN::Zones::Plugin->lookup_types();
+
+my $api_sdn_zones_config = sub {
+    my ($cfg, $id) = @_;
+
+    my $scfg = dclone(PVE::Network::SDN::Zones::sdn_zones_config($cfg, $id));
+    $scfg->{zone} = $id;
+    $scfg->{digest} = $cfg->{digest};
+
+    if ($scfg->{nodes}) {
+        $scfg->{nodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'nodes', $scfg->{nodes});
+    }
+
+    if ($scfg->{exitnodes}) {
+        $scfg->{exitnodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'exitnodes', $scfg->{exitnodes});
+    }
+
+    my $pending = $scfg->{pending};
+    if ($pending->{nodes}) {
+        $pending->{nodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'nodes', $pending->{nodes});
+    }
+
+    if ($pending->{exitnodes}) {
+        $pending->{exitnodes} = PVE::Network::SDN::encode_value($scfg->{type}, 'exitnodes', $pending->{exitnodes});
+    }
+
+    return $scfg;
+};
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "SDN zones index.",
+    permissions => {
+       description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/zones/<zone>'",
+       user => 'all',
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           type => {
+               description => "Only list SDN zones of specific type",
+               type => 'string',
+               enum => $sdn_zones_type_enum,
+               optional => 1,
+           },
+           running => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display running config.",
+           },
+           pending => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display pending config.",
+           },
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => { zone => { type => 'string'},
+                           type => { type => 'string'},
+                           mtu => { type => 'integer', optional => 1 },
+                           dns => { type => 'string', optional => 1},
+                           reversedns => { type => 'string', optional => 1},
+                           dnszone => { type => 'string', optional => 1},
+                           ipam => { type => 'string', optional => 1},
+                           pending => { optional => 1},
+                           state => { type => 'string', optional => 1},
+                           nodes => { type => 'string', optional => 1},
+                         },
+       },
+       links => [ { rel => 'child', href => "{zone}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+
+       my $cfg = {};
+       if ($param->{pending}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           my $config = PVE::Network::SDN::Zones::config();
+           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'zones');
+        } elsif ($param->{running}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           $cfg = $running_cfg->{zones};
+        } else {
+           $cfg = PVE::Network::SDN::Zones::config();
+        }
+
+       my @sids = PVE::Network::SDN::Zones::sdn_zones_ids($cfg);
+       my $res = [];
+       for my $id (@sids) {
+           my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+           next if !$rpcenv->check_any($authuser, "/sdn/zones/$id", $privs, 1);
+
+           my $scfg = &$api_sdn_zones_config($cfg, $id);
+           next if $param->{type} && $param->{type} ne $scfg->{type};
+
+           my $plugin_config = $cfg->{ids}->{$id};
+           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+           push @$res, $scfg;
+       }
+
+       return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'read',
+    path => '{zone}',
+    method => 'GET',
+    description => "Read sdn zone configuration.",
+    permissions => {
+       check => ['perm', '/sdn/zones/{zone}', ['SDN.Allocate']],
+   },
+
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           zone => get_standard_option('pve-sdn-zone-id'),
+           running => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display running config.",
+           },
+           pending => {
+               type => 'boolean',
+               optional => 1,
+               description => "Display pending config.",
+           }
+       },
+    },
+    returns => { type => 'object' },
+    code => sub {
+       my ($param) = @_;
+
+       my $cfg = {};
+       if ($param->{pending}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           my $config = PVE::Network::SDN::Zones::config();
+           $cfg = PVE::Network::SDN::pending_config($running_cfg, $config, 'zones');
+        } elsif ($param->{running}) {
+           my $running_cfg = PVE::Network::SDN::running_config();
+           $cfg = $running_cfg->{zones};
+        } else {
+           $cfg = PVE::Network::SDN::Zones::config();
+        }
+
+       return &$api_sdn_zones_config($cfg, $param->{zone});
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    path => '',
+    method => 'POST',
+    description => "Create a new sdn zone object.",
+    permissions => {
+       check => ['perm', '/sdn/zones', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Zones::Plugin->createSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $type = extract_param($param, 'type');
+       my $id = extract_param($param, 'zone');
+
+       my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($type);
+       my $opts = $plugin->check_config($id, $param, 1, 1);
+
+       PVE::Cluster::check_cfs_quorum();
+       mkdir("/etc/pve/sdn");
+
+       PVE::Network::SDN::lock_sdn_config(sub {
+           my $zone_cfg = PVE::Network::SDN::Zones::config();
+           my $controller_cfg = PVE::Network::SDN::Controllers::config();
+           my $dns_cfg = PVE::Network::SDN::Dns::config();
+
+           my $scfg = undef;
+           if ($scfg = PVE::Network::SDN::Zones::sdn_zones_config($zone_cfg, $id, 1)) {
+               die "sdn zone object ID '$id' already defined\n";
+           }
+
+           my $dnsserver = $opts->{dns};
+           raise_param_exc({ dns => "$dnsserver don't exist"})
+               if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
+
+           my $reversednsserver = $opts->{reversedns};
+           raise_param_exc({ reversedns => "$reversednsserver don't exist"})
+               if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
+
+           my $dnszone = $opts->{dnszone};
+           raise_param_exc({ dnszone => "missing dns server"})
+               if $dnszone && !$dnsserver;
+
+           my $ipam = $opts->{ipam};
+           my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+           raise_param_exc({ ipam => "$ipam not existing"}) if $ipam && !$ipam_cfg->{ids}->{$ipam};
+
+           $zone_cfg->{ids}->{$id} = $opts;
+           $plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
+
+           PVE::Network::SDN::Zones::write_config($zone_cfg);
+
+       }, "create sdn zone object failed");
+
+       return;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    path => '{zone}',
+    method => 'PUT',
+    description => "Update sdn zone object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/zones', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Zones::Plugin->updateSchema(),
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'zone');
+       my $digest = extract_param($param, 'digest');
+
+       PVE::Network::SDN::lock_sdn_config(sub {
+           my $zone_cfg = PVE::Network::SDN::Zones::config();
+           my $controller_cfg = PVE::Network::SDN::Controllers::config();
+           my $dns_cfg = PVE::Network::SDN::Dns::config();
+
+           PVE::SectionConfig::assert_if_modified($zone_cfg, $digest);
+
+           my $scfg = PVE::Network::SDN::Zones::sdn_zones_config($zone_cfg, $id);
+
+           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($scfg->{type});
+           my $opts = $plugin->check_config($id, $param, 0, 1);
+
+           if ($opts->{ipam} && !$scfg->{ipam} || $opts->{ipam} ne $scfg->{ipam}) {
+
+               # don't allow ipam change if subnet are defined for now, need to implement resync ipam content
+               my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+               for my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
+                   my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
+                   raise_param_exc({ ipam => "can't change ipam if a subnet is already defined in this zone"})
+                       if $subnet->{zone} eq $id;
+               }
+           }
+
+           $zone_cfg->{ids}->{$id} = $opts;
+
+           my $dnsserver = $opts->{dns};
+           raise_param_exc({ dns => "$dnsserver don't exist"}) if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
+
+           my $reversednsserver = $opts->{reversedns};
+           raise_param_exc({ reversedns => "$reversednsserver don't exist"}) if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
+
+           my $dnszone = $opts->{dnszone};
+           raise_param_exc({ dnszone => "missing dns server"}) if $dnszone && !$dnsserver;
+
+           my $ipam = $opts->{ipam};
+           my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+           raise_param_exc({ ipam => "$ipam not existing"}) if $ipam && !$ipam_cfg->{ids}->{$ipam};
+
+           $plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
+
+           PVE::Network::SDN::Zones::write_config($zone_cfg);
+
+       }, "update sdn zone object failed");
+
+       return;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    path => '{zone}',
+    method => 'DELETE',
+    description => "Delete sdn zone object configuration.",
+    permissions => {
+       check => ['perm', '/sdn/zones', ['SDN.Allocate']],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           zone => get_standard_option('pve-sdn-zone-id', {
+               completion => \&PVE::Network::SDN::Zones::complete_sdn_zones,
+           }),
+       },
+    },
+    returns => { type => 'null' },
+    code => sub {
+       my ($param) = @_;
+
+       my $id = extract_param($param, 'zone');
+
+        PVE::Network::SDN::lock_sdn_config(sub {
+           my $cfg = PVE::Network::SDN::Zones::config();
+           my $scfg = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $id);
+
+           my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($scfg->{type});
+           my $vnet_cfg = PVE::Network::SDN::Vnets::config();
+
+           $plugin->on_delete_hook($id, $vnet_cfg);
+
+           delete $cfg->{ids}->{$id};
+
+           PVE::Network::SDN::Zones::write_config($cfg);
+       }, "delete sdn zone object failed");
+
+       return;
+    }});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Zones/Content.pm b/src/PVE/API2/Network/SDN/Zones/Content.pm
new file mode 100644 (file)
index 0000000..66f49df
--- /dev/null
@@ -0,0 +1,85 @@
+package PVE::API2::Network::SDN::Zones::Content;
+
+use strict;
+use warnings;
+use Data::Dumper;
+
+use PVE::SafeSyslog;
+use PVE::Cluster;
+use PVE::INotify;
+use PVE::Exception qw(raise_param_exc);
+use PVE::RPCEnvironment;
+use PVE::RESTHandler;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Network::SDN;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "List zone content.",
+    permissions => {
+       check => ['perm', '/sdn/zones/{zone}', ['SDN.Audit'], any => 1],
+    },
+    protected => 1,
+    proxyto => 'node',
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           zone => get_standard_option('pve-sdn-zone-id', {
+               completion => \&PVE::Network::SDN::Zones::complete_sdn_zone,
+            }),
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => {
+               vnet => {
+                   description => "Vnet identifier.",
+                   type => 'string',
+               },
+               status => {
+                   description => "Status.",
+                   type => 'string',
+                   optional => 1,
+               },
+               statusmsg => {
+                   description => "Status details",
+                   type => 'string',
+                   optional => 1,
+               },
+           },
+       },
+       links => [ { rel => 'child', href => "{vnet}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+
+       my $authuser = $rpcenv->get_user();
+
+       my $zoneid = $param->{zone};
+
+       my $res = [];
+
+        my ($zone_status, $vnet_status) = PVE::Network::SDN::status();
+
+       foreach my $id (keys %{$vnet_status}) {
+           if ($vnet_status->{$id}->{zone} eq $zoneid) {
+               my $item->{vnet} = $id;
+               $item->{status} = $vnet_status->{$id}->{'status'};
+               $item->{statusmsg} = $vnet_status->{$id}->{'statusmsg'};
+               push @$res,$item;
+           }
+        }
+
+       return $res;
+    }});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Zones/Makefile b/src/PVE/API2/Network/SDN/Zones/Makefile
new file mode 100644 (file)
index 0000000..9b0a42b
--- /dev/null
@@ -0,0 +1,8 @@
+SOURCES=Status.pm Content.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/API2/Network/SDN/Zones/$$i; done
diff --git a/src/PVE/API2/Network/SDN/Zones/Status.pm b/src/PVE/API2/Network/SDN/Zones/Status.pm
new file mode 100644 (file)
index 0000000..17de68f
--- /dev/null
@@ -0,0 +1,111 @@
+package PVE::API2::Network::SDN::Zones::Status;
+
+use strict;
+use warnings;
+
+use File::Path;
+use File::Basename;
+use PVE::Tools;
+use PVE::INotify;
+use PVE::Cluster;
+use PVE::API2::Network::SDN::Zones::Content;
+use PVE::RESTHandler;
+use PVE::RPCEnvironment;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Exception qw(raise_param_exc);
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Zones::Content",
+    path => '{zone}/content',
+});
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "Get status for all zones.",
+    permissions => {
+       description => "Only list entries where you have 'SDN.Audit'",
+       user => 'all',
+    },
+    protected => 1,
+    proxyto => 'node',
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node')
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => {
+               zone => get_standard_option('pve-sdn-zone-id'),
+               status => {
+                   description => "Status of zone",
+                   type => 'string',
+                   enum => ['available', 'pending', 'error'],
+               },
+           },
+       },
+       links => [ { rel => 'child', href => "{zone}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+
+       my $rpcenv = PVE::RPCEnvironment::get();
+       my $authuser = $rpcenv->get_user();
+
+       my $localnode = PVE::INotify::nodename();
+
+       my $res = [];
+
+       my ($zone_status, $vnet_status) = PVE::Network::SDN::status();
+
+       foreach my $id (sort keys %{$zone_status}) {
+           my $item->{zone} = $id;
+           $item->{status} = $zone_status->{$id}->{'status'};
+           push @$res, $item;
+       }
+
+       return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'diridx',
+    path => '{zone}',
+    method => 'GET',
+    description => "",
+    permissions => {
+       check => ['perm', '/sdn/zones/{zone}', ['SDN.Audit'], any => 1],
+    },
+    parameters => {
+       additionalProperties => 0,
+       properties => {
+           node => get_standard_option('pve-node'),
+           zone => get_standard_option('pve-sdn-zone-id'),
+       },
+    },
+    returns => {
+       type => 'array',
+       items => {
+           type => "object",
+           properties => {
+               subdir => { type => 'string' },
+           },
+       },
+       links => [ { rel => 'child', href => "{subdir}" } ],
+    },
+    code => sub {
+       my ($param) = @_;
+       my $res = [
+           { subdir => 'content' },
+           ];
+
+       return $res;
+    }});
+
+1;
diff --git a/src/PVE/Makefile b/src/PVE/Makefile
new file mode 100644 (file)
index 0000000..7f1cf98
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+
+.PHONY: install
+install:
+       make -C Network install
+       make -C API2 install
+
+clean:
diff --git a/src/PVE/Network/Makefile b/src/PVE/Network/Makefile
new file mode 100644 (file)
index 0000000..277e19c
--- /dev/null
@@ -0,0 +1,9 @@
+SOURCES=SDN.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/$$i; done
+       make -C SDN install
diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm
new file mode 100644 (file)
index 0000000..b95dd5b
--- /dev/null
@@ -0,0 +1,283 @@
+package PVE::Network::SDN;
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Controllers;
+use PVE::Network::SDN::Subnets;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+
+
+my $running_cfg = "sdn/.running-config";
+
+my $parse_running_cfg = sub {
+    my ($filename, $raw) = @_;
+
+    my $cfg = {};
+
+    return $cfg if !defined($raw) || $raw eq '';
+
+    eval {
+       $cfg = from_json($raw);
+    };
+    return {} if $@;
+
+    return $cfg;
+};
+
+my $write_running_cfg = sub {
+    my ($filename, $cfg) = @_;
+
+    my $json = to_json($cfg);
+
+    return $json;
+};
+
+PVE::Cluster::cfs_register_file($running_cfg, $parse_running_cfg, $write_running_cfg);
+
+
+# improve me : move status code inside plugins ?
+
+sub ifquery_check {
+
+    my $cmd = ['ifquery', '-a', '-c', '-o','json'];
+
+    my $result = '';
+    my $reader = sub { $result .= shift };
+
+    eval {
+       run_command($cmd, outfunc => $reader);
+    };
+
+    my $resultjson = decode_json($result);
+    my $interfaces = {};
+
+    foreach my $interface (@$resultjson) {
+       my $name = $interface->{name};
+       $interfaces->{$name} = {
+           status => $interface->{status},
+           config => $interface->{config},
+           config_status => $interface->{config_status},
+       };
+    }
+
+    return $interfaces;
+}
+
+sub status {
+
+    my ($zone_status, $vnet_status) = PVE::Network::SDN::Zones::status();
+    return($zone_status, $vnet_status);
+}
+
+sub running_config {
+    return cfs_read_file($running_cfg);
+}
+
+sub pending_config {
+    my ($running_cfg, $cfg, $type) = @_;
+
+    my $pending = {};
+
+    my $running_objects = $running_cfg->{$type}->{ids};
+    my $config_objects = $cfg->{ids};
+
+    foreach my $id (sort keys %{$running_objects}) {
+       my $running_object = $running_objects->{$id};
+       my $config_object = $config_objects->{$id};
+       foreach my $key (sort keys %{$running_object}) {
+           $pending->{$id}->{$key} = $running_object->{$key};
+           if(!keys %{$config_object}) {
+               $pending->{$id}->{state} = "deleted";
+           } elsif (!defined($config_object->{$key})) {
+               $pending->{$id}->{"pending"}->{$key} = 'deleted';
+               $pending->{$id}->{state} = "changed";
+           } elsif (PVE::Network::SDN::encode_value(undef, $key, $running_object->{$key})
+                        ne PVE::Network::SDN::encode_value(undef, $key, $config_object->{$key})) {
+               $pending->{$id}->{state} = "changed";
+           }
+       }
+       $pending->{$id}->{"pending"} = {} if $pending->{$id}->{state} && !defined($pending->{$id}->{"pending"});
+    }
+
+   foreach my $id (sort keys %{$config_objects}) {
+       my $running_object = $running_objects->{$id};
+       my $config_object = $config_objects->{$id};
+
+       foreach my $key (sort keys %{$config_object}) {
+           my $config_value = PVE::Network::SDN::encode_value(undef, $key, $config_object->{$key}) if $config_object->{$key};
+           my $running_value = PVE::Network::SDN::encode_value(undef, $key, $running_object->{$key}) if $running_object->{$key};
+           if($key eq 'type' || $key eq 'vnet') {
+               $pending->{$id}->{$key} = $config_value;
+           } else {
+               $pending->{$id}->{"pending"}->{$key} = $config_value if !defined($running_value) || ($config_value ne $running_value);
+           }
+           if(!keys %{$running_object}) {
+               $pending->{$id}->{state} = "new";
+           } elsif (!defined($running_value) && defined($config_value)) {
+               $pending->{$id}->{state} = "changed";
+           }
+       }
+       $pending->{$id}->{"pending"} = {} if  $pending->{$id}->{state} && !defined($pending->{$id}->{"pending"});
+   }
+
+   return {ids => $pending};
+
+}
+
+sub commit_config {
+
+    my $cfg = cfs_read_file($running_cfg);
+    my $version = $cfg->{version};
+
+    if ($version) {
+       $version++;
+    } else {
+       $version = 1;
+    }
+
+    my $vnets_cfg = PVE::Network::SDN::Vnets::config();
+    my $zones_cfg = PVE::Network::SDN::Zones::config();
+    my $controllers_cfg = PVE::Network::SDN::Controllers::config();
+    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+
+    my $vnets = { ids => $vnets_cfg->{ids} };
+    my $zones = { ids => $zones_cfg->{ids} };
+    my $controllers = { ids => $controllers_cfg->{ids} };
+    my $subnets = { ids => $subnets_cfg->{ids} };
+
+     $cfg = { version => $version, vnets => $vnets, zones => $zones, controllers => $controllers, subnets => $subnets };
+
+    cfs_write_file($running_cfg, $cfg);
+}
+
+sub lock_sdn_config {
+    my ($code, $errmsg) = @_;
+
+    cfs_lock_file($running_cfg, undef, $code);
+
+    if (my $err = $@) {
+        $errmsg ? die "$errmsg: $err" : die $err;
+    }
+}
+
+sub get_local_vnets {
+
+    my $rpcenv = PVE::RPCEnvironment::get();
+
+    my $authuser = $rpcenv->get_user();
+
+    my $nodename = PVE::INotify::nodename();
+
+    my $cfg = PVE::Network::SDN::running_config();
+    my $vnets_cfg = $cfg->{vnets};
+    my $zones_cfg = $cfg->{zones};
+
+    my @vnetids = PVE::Network::SDN::Vnets::sdn_vnets_ids($vnets_cfg);
+
+    my $vnets = {};
+
+    foreach my $vnetid (@vnetids) {
+
+       my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($vnets_cfg, $vnetid);
+       my $zoneid = $vnet->{zone};
+       my $comments = $vnet->{alias};
+
+       my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+
+       next if !$zoneid;
+       next if !$rpcenv->check_any($authuser, "/sdn/zones/$zoneid", $privs, 1) && !$rpcenv->check_any($authuser, "/sdn/vnets/$vnetid", $privs, 1);
+
+       my $zone_config = PVE::Network::SDN::Zones::sdn_zones_config($zones_cfg, $zoneid);
+
+       next if defined($zone_config->{nodes}) && !$zone_config->{nodes}->{$nodename};
+       my $ipam = $zone_config->{ipam} ? 1 : 0;
+       my $vlanaware = $vnet->{vlanaware} ? 1 : 0;
+       $vnets->{$vnetid} = { type => 'vnet', active => '1', ipam => $ipam, vlanaware => $vlanaware, comments => $comments };
+    }
+
+    return $vnets;
+}
+
+sub generate_zone_config {
+    my $raw_config = PVE::Network::SDN::Zones::generate_etc_network_config();
+    PVE::Network::SDN::Zones::write_etc_network_config($raw_config);
+}
+
+sub generate_controller_config {
+    my ($reload) = @_;
+
+    my $raw_config = PVE::Network::SDN::Controllers::generate_controller_config();
+    PVE::Network::SDN::Controllers::write_controller_config($raw_config);
+
+    PVE::Network::SDN::Controllers::reload_controller() if $reload;
+}
+
+sub encode_value {
+    my ($type, $key, $value) = @_;
+
+    if ($key eq 'nodes' || $key eq 'exitnodes') {
+        if(ref($value) eq 'HASH') {
+            return join(',', sort keys(%$value));
+        } else {
+            return $value;
+        }
+    }
+
+    return $value;
+}
+
+
+#helpers
+sub api_request {
+    my ($method, $url, $headers, $data) = @_;
+
+    my $encoded_data = to_json($data) if $data;
+
+    my $req = HTTP::Request->new($method,$url, $headers, $encoded_data);
+
+    my $ua = LWP::UserAgent->new(protocols_allowed => ['http', 'https'], timeout => 30);
+    my $proxy = undef;
+
+    if ($proxy) {
+        $ua->proxy(['http', 'https'], $proxy);
+    } else {
+        $ua->env_proxy;
+    }
+
+    $ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00);
+
+    my $response = $ua->request($req);
+    my $code = $response->code;
+
+    if ($code !~ /^2(\d+)$/) {
+        my $msg = $response->message || 'unknown';
+        die "Invalid response from server: $code $msg\n";
+    }
+
+    my $raw = '';
+    if (defined($response->decoded_content)) {
+       $raw = $response->decoded_content;
+    } else {
+       $raw = $response->content;
+    }
+
+    return if $raw eq '';
+
+    my $json = '';
+    eval {
+       $json = from_json($raw);
+    };
+    die "api response is not a json" if $@;
+
+    return $json;
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Controllers.pm b/src/PVE/Network/SDN/Controllers.pm
new file mode 100644 (file)
index 0000000..a23048e
--- /dev/null
@@ -0,0 +1,181 @@
+package PVE::Network::SDN::Controllers;
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Zones;
+
+use PVE::Network::SDN::Controllers::EvpnPlugin;
+use PVE::Network::SDN::Controllers::BgpPlugin;
+use PVE::Network::SDN::Controllers::FaucetPlugin;
+use PVE::Network::SDN::Controllers::Plugin;
+PVE::Network::SDN::Controllers::EvpnPlugin->register();
+PVE::Network::SDN::Controllers::BgpPlugin->register();
+PVE::Network::SDN::Controllers::FaucetPlugin->register();
+PVE::Network::SDN::Controllers::Plugin->init();
+
+
+sub sdn_controllers_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn controller ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/controllers.cfg");
+    $config = cfs_read_file("sdn/controllers.cfg") if !keys %{$config->{ids}};
+    return $config;
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/controllers.cfg", $cfg);
+}
+
+sub lock_sdn_controllers_config {
+    my ($code, $errmsg) = @_;
+
+    cfs_lock_file("sdn/controllers.cfg", undef, $code);
+    if (my $err = $@) {
+        $errmsg ? die "$errmsg: $err" : die $err;
+    }
+}
+
+sub sdn_controllers_ids {
+    my ($cfg) = @_;
+
+    return sort keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_controller {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::running_config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_controllers_ids($cfg) ];
+}
+
+sub generate_controller_config {
+
+    my $cfg = PVE::Network::SDN::running_config();
+    my $vnet_cfg = $cfg->{vnets};
+    my $zone_cfg = $cfg->{zones};
+    my $controller_cfg = $cfg->{controllers};
+
+    return if !$vnet_cfg && !$zone_cfg && !$controller_cfg;
+
+    #read main config for physical interfaces
+    my $current_config_file = "/etc/network/interfaces";
+    my $fh = IO::File->new($current_config_file);
+    my $interfaces_config = PVE::INotify::read_etc_network_interfaces(1,$fh);
+    $fh->close();
+
+    # check uplinks
+    my $uplinks = {};
+    foreach my $id (keys %{$interfaces_config->{ifaces}}) {
+       my $interface = $interfaces_config->{ifaces}->{$id};
+       if (my $uplink = $interface->{'uplink-id'}) {
+           die "uplink-id $uplink is already defined on $uplinks->{$uplink}" if $uplinks->{$uplink};
+           $interface->{name} = $id;
+           $uplinks->{$interface->{'uplink-id'}} = $interface;
+       }
+    }
+
+    # generate configuration
+    my $config = {};
+
+    foreach my $id (sort keys %{$controller_cfg->{ids}}) {
+       my $plugin_config = $controller_cfg->{ids}->{$id};
+       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
+       $plugin->generate_controller_config($plugin_config, $controller_cfg, $id, $uplinks, $config);
+    }
+
+    foreach my $id (sort keys %{$zone_cfg->{ids}}) {
+       my $plugin_config = $zone_cfg->{ids}->{$id};
+       my $controllerid = $plugin_config->{controller};
+       next if !$controllerid;
+       my $controller = $controller_cfg->{ids}->{$controllerid};
+       if ($controller) {
+           my $controller_plugin = PVE::Network::SDN::Controllers::Plugin->lookup($controller->{type});
+           $controller_plugin->generate_controller_zone_config($plugin_config, $controller, $controller_cfg, $id, $uplinks, $config);
+       }
+    }
+
+    foreach my $id (sort keys %{$vnet_cfg->{ids}}) {
+       my $plugin_config = $vnet_cfg->{ids}->{$id};
+       my $zoneid = $plugin_config->{zone};
+       next if !$zoneid;
+       my $zone = $zone_cfg->{ids}->{$zoneid};
+       next if !$zone;
+       my $controllerid = $zone->{controller};
+       next if !$controllerid;
+       my $controller = $controller_cfg->{ids}->{$controllerid};
+       if ($controller) {
+           my $controller_plugin = PVE::Network::SDN::Controllers::Plugin->lookup($controller->{type});
+           $controller_plugin->generate_controller_vnet_config($plugin_config, $controller, $zone, $zoneid, $id, $config);
+       }
+    }
+
+    return $config;
+}
+
+
+sub reload_controller {
+
+    my $cfg = PVE::Network::SDN::running_config();
+    my $controller_cfg = $cfg->{controllers};
+
+    return if !$controller_cfg;
+
+    foreach my $id (keys %{$controller_cfg->{ids}}) {
+       my $plugin_config = $controller_cfg->{ids}->{$id};
+       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
+       $plugin->reload_controller();
+    }
+}
+
+sub generate_controller_rawconfig {
+    my ($config) = @_;
+
+    my $cfg = PVE::Network::SDN::running_config();
+    my $controller_cfg = $cfg->{controllers};
+    return if !$controller_cfg;
+
+    my $rawconfig = "";
+    foreach my $id (keys %{$controller_cfg->{ids}}) {
+       my $plugin_config = $controller_cfg->{ids}->{$id};
+       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
+       $rawconfig .= $plugin->generate_controller_rawconfig($plugin_config, $config);
+    }
+    return $rawconfig;
+}
+
+sub write_controller_config {
+    my ($config) = @_;
+
+    my $cfg = PVE::Network::SDN::running_config();
+    my $controller_cfg = $cfg->{controllers};
+    return if !$controller_cfg;
+
+    foreach my $id (keys %{$controller_cfg->{ids}}) {
+       my $plugin_config = $controller_cfg->{ids}->{$id};
+       my $plugin = PVE::Network::SDN::Controllers::Plugin->lookup($plugin_config->{type});
+       $plugin->write_controller_config($plugin_config, $config);
+    }
+}
+
+1;
+
diff --git a/src/PVE/Network/SDN/Controllers/BgpPlugin.pm b/src/PVE/Network/SDN/Controllers/BgpPlugin.pm
new file mode 100644 (file)
index 0000000..0b8cf1a
--- /dev/null
@@ -0,0 +1,183 @@
+package PVE::Network::SDN::Controllers::BgpPlugin;
+
+use strict;
+use warnings;
+
+use PVE::INotify;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw(run_command file_set_contents file_get_contents);
+
+use PVE::Network::SDN::Controllers::Plugin;
+use PVE::Network::SDN::Zones::Plugin;
+use Net::IP;
+
+use base('PVE::Network::SDN::Controllers::Plugin');
+
+sub type {
+    return 'bgp';
+}
+
+sub properties {
+    return {
+       'bgp-multipath-as-path-relax' => {
+           type => 'boolean',
+           optional => 1,
+       },
+       ebgp => {
+           type => 'boolean',
+           optional => 1,
+           description => "Enable ebgp. (remote-as external)",
+       },
+       'ebgp-multihop' => {
+           type => 'integer',
+           optional => 1,
+       },
+       loopback => {
+           description => "source loopback interface.",
+           type => 'string'
+       },
+        node => get_standard_option('pve-node'),
+    };
+}
+
+sub options {
+    return {
+       'node' => { optional => 0 },
+       'asn' => { optional => 0 },
+       'peers' => { optional => 0 },
+       'bgp-multipath-as-path-relax' => { optional => 1 },
+       'ebgp' => { optional => 1 },
+       'ebgp-multihop' => { optional => 1 },
+       'loopback' => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+sub generate_controller_config {
+    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
+
+    my @peers;
+    @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'};
+
+    my $asn = $plugin_config->{asn};
+    my $ebgp = $plugin_config->{ebgp};
+    my $ebgp_multihop = $plugin_config->{'ebgp-multihop'};
+    my $loopback = $plugin_config->{loopback};
+    my $multipath_relax = $plugin_config->{'bgp-multipath-as-path-relax'};
+
+    my $local_node = PVE::INotify::nodename();
+
+
+    return if !$asn;
+    return if $local_node ne $plugin_config->{node};
+
+    my $bgp = $config->{frr}->{router}->{"bgp $asn"} //= {};
+
+    my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
+
+    my $remoteas = $ebgp ? "external" : $asn;
+
+    #global options
+    my @controller_config = (
+        "bgp router-id $ifaceip",
+        "no bgp default ipv4-unicast",
+        "coalesce-time 1000"
+    );
+
+    push(@{$bgp->{""}}, @controller_config) if keys %{$bgp} == 0;
+
+    @controller_config = ();
+    if($ebgp) {
+       push @controller_config, "bgp disable-ebgp-connected-route-check" if $loopback;
+    }
+
+    push @controller_config, "bgp bestpath as-path multipath-relax" if $multipath_relax;
+
+    #BGP neighbors
+    if(@peers) {
+       push @controller_config, "neighbor BGP peer-group";
+       push @controller_config, "neighbor BGP remote-as $remoteas";
+       push @controller_config, "neighbor BGP bfd";
+       push @controller_config, "neighbor BGP ebgp-multihop $ebgp_multihop" if $ebgp && $ebgp_multihop;
+    }
+
+    # BGP peers
+    foreach my $address (@peers) {
+       push @controller_config, "neighbor $address peer-group BGP";
+    }
+    push(@{$bgp->{""}}, @controller_config);
+
+    # address-family unicast
+    if (@peers) {
+       my $ipversion = Net::IP::ip_is_ipv6($ifaceip) ? "ipv6" : "ipv4";
+       my $mask = Net::IP::ip_is_ipv6($ifaceip) ? "/128" : "32";
+
+       push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "network $ifaceip/$mask") if $loopback;
+       push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP activate");
+       push(@{$bgp->{"address-family"}->{"$ipversion unicast"}}, "neighbor BGP soft-reconfiguration inbound");
+    }
+
+    if ($loopback) {
+       $config->{frr_prefix_list}->{loopbacks_ips}->{10} = "permit 0.0.0.0/0 le 32";
+       push(@{$config->{frr}->{''}}, "ip protocol bgp route-map correct_src");
+
+       my $routemap_config = ();
+       push @{$routemap_config}, "match ip address prefix-list loopbacks_ips";
+       push @{$routemap_config}, "set src $ifaceip";
+       my $routemap = { rule => $routemap_config, action => "permit" };
+       push(@{$config->{frr_routemap}->{'correct_src'}}, $routemap);
+    }
+
+    return $config;
+}
+
+sub generate_controller_zone_config {
+    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
+
+}
+
+sub on_delete_hook {
+    my ($class, $controllerid, $zone_cfg) = @_;
+
+    # verify that zone is associated to this controller
+    foreach my $id (keys %{$zone_cfg->{ids}}) {
+       my $zone = $zone_cfg->{ids}->{$id};
+       die "controller $controllerid is used by $id"
+           if (defined($zone->{controller}) && $zone->{controller} eq $controllerid);
+    }
+}
+
+sub on_update_hook {
+    my ($class, $controllerid, $controller_cfg) = @_;
+
+    # we can only have 1 bgp controller by node
+    my $local_node = PVE::INotify::nodename();
+    my $controllernb = 0;
+    foreach my $id (keys %{$controller_cfg->{ids}}) {
+        next if $id eq $controllerid;
+        my $controller = $controller_cfg->{ids}->{$id};
+        next if $controller->{type} ne "bgp";
+        next if $controller->{node} ne $local_node;
+        $controllernb++;
+        die "only 1 bgp controller can be defined" if $controllernb > 1;
+    }
+}
+
+sub generate_controller_rawconfig {
+    my ($class, $plugin_config, $config) = @_;
+    return "";
+}
+
+sub write_controller_config {
+    my ($class, $plugin_config, $config) = @_;
+    return;
+}
+
+sub reload_controller {
+    my ($class) = @_;
+    return;
+}
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm b/src/PVE/Network/SDN/Controllers/EvpnPlugin.pm
new file mode 100644 (file)
index 0000000..727aeaa
--- /dev/null
@@ -0,0 +1,542 @@
+package PVE::Network::SDN::Controllers::EvpnPlugin;
+
+use strict;
+use warnings;
+
+use PVE::INotify;
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw(run_command file_set_contents file_get_contents);
+
+use PVE::Network::SDN::Controllers::Plugin;
+use PVE::Network::SDN::Zones::Plugin;
+use Net::IP;
+
+use base('PVE::Network::SDN::Controllers::Plugin');
+
+sub type {
+    return 'evpn';
+}
+
+sub properties {
+    return {
+       asn => {
+           type => 'integer',
+           description => "autonomous system number",
+           minimum => 0,
+           maximum => 4294967296
+       },
+       peers => {
+           description => "peers address list.",
+           type => 'string', format => 'ip-list'
+       },
+    };
+}
+
+sub options {
+    return {
+       'asn' => { optional => 0 },
+       'peers' => { optional => 0 },
+    };
+}
+
+# Plugin implementation
+sub generate_controller_config {
+    my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_;
+
+    my @peers;
+    @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'};
+
+    my $local_node = PVE::INotify::nodename();
+
+    my $asn = $plugin_config->{asn};
+    my $ebgp = undef;
+    my $loopback = undef;
+    my $autortas = undef;
+    my $bgprouter = find_bgp_controller($local_node, $controller_cfg);
+    if ($bgprouter) {
+       $ebgp = 1 if $plugin_config->{'asn'} ne $bgprouter->{asn};
+       $loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
+       $asn = $bgprouter->{asn} if $bgprouter->{asn};
+       $autortas = $plugin_config->{'asn'} if $ebgp;
+    }
+
+    return if !$asn;
+
+    my $bgp = $config->{frr}->{router}->{"bgp $asn"} //= {};
+
+    my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
+
+    my $remoteas = $ebgp ? "external" : $asn;
+
+    #global options
+    my @controller_config = (
+       "bgp router-id $ifaceip",
+       "no bgp default ipv4-unicast",
+       "coalesce-time 1000",
+    );
+
+    push(@{$bgp->{""}}, @controller_config) if keys %{$bgp} == 0;
+
+    @controller_config = ();
+
+    #VTEP neighbors
+    push @controller_config, "neighbor VTEP peer-group";
+    push @controller_config, "neighbor VTEP remote-as $remoteas";
+    push @controller_config, "neighbor VTEP bfd";
+
+    if($ebgp && $loopback) {
+       push @controller_config, "neighbor VTEP ebgp-multihop 10";
+       push @controller_config, "neighbor VTEP update-source $loopback";
+    }
+
+    # VTEP peers
+    foreach my $address (@peers) {
+       next if $address eq $ifaceip;
+       push @controller_config, "neighbor $address peer-group VTEP";
+    }
+
+    push(@{$bgp->{""}}, @controller_config);
+
+    # address-family l2vpn
+    @controller_config = ();
+    push @controller_config, "neighbor VTEP route-map MAP_VTEP_IN in";
+    push @controller_config, "neighbor VTEP route-map MAP_VTEP_OUT out";
+    push @controller_config, "neighbor VTEP activate";
+    push @controller_config, "advertise-all-vni";
+    push @controller_config, "autort as $autortas" if $autortas;
+    push(@{$bgp->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
+
+    my $routemap = { rule => undef, action => "permit" };
+    push(@{$config->{frr_routemap}->{'MAP_VTEP_IN'}}, $routemap );
+    push(@{$config->{frr_routemap}->{'MAP_VTEP_OUT'}}, $routemap );
+
+    return $config;
+}
+
+sub generate_controller_zone_config {
+    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
+
+    my $local_node = PVE::INotify::nodename();
+
+    my $vrf = "vrf_$id";
+    my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
+    my $exitnodes = $plugin_config->{'exitnodes'};
+    my $exitnodes_primary = $plugin_config->{'exitnodes-primary'};
+    my $advertisesubnets = $plugin_config->{'advertise-subnets'};
+    my $exitnodes_local_routing = $plugin_config->{'exitnodes-local-routing'};
+    my $rt_import;
+    $rt_import = [PVE::Tools::split_list($plugin_config->{'rt-import'})] if $plugin_config->{'rt-import'};
+
+    my $asn = $controller->{asn};
+    my @peers;
+    @peers = PVE::Tools::split_list($controller->{'peers'}) if $controller->{'peers'};
+    my $ebgp = undef;
+    my $loopback = undef;
+    my $autortas = undef;
+    my $bgprouter = find_bgp_controller($local_node, $controller_cfg);
+    if($bgprouter) {
+        $ebgp = 1 if $controller->{'asn'} ne $bgprouter->{asn};
+       $loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
+       $asn = $bgprouter->{asn} if $bgprouter->{asn};
+       $autortas = $controller->{'asn'} if $ebgp;
+    }
+
+    return if !$vrf || !$vrfvxlan || !$asn;
+
+    my ($ifaceip, $interface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
+
+    # vrf
+    my @controller_config = ();
+    push @controller_config, "vni $vrfvxlan";
+    push(@{$config->{frr}->{vrf}->{"$vrf"}}, @controller_config);
+
+    #main vrf router
+    @controller_config = ();
+    push @controller_config, "bgp router-id $ifaceip";
+#    push @controller_config, "!";
+    push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{""}}, @controller_config);
+
+    if ($autortas) {
+       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, "route-target import $autortas:$vrfvxlan");
+       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, "route-target export $autortas:$vrfvxlan");
+    }
+
+    my $is_gateway = $exitnodes->{$local_node};
+
+    if ($is_gateway) {
+
+       if (!$exitnodes_primary || $exitnodes_primary eq $local_node) {
+           #filter default type5 route coming from other exit nodes on primary node or both nodes if no primary is defined.
+           my $routemap_config = ();
+           push @{$routemap_config}, "match evpn route-type prefix";
+           my $routemap = { rule => $routemap_config, action => "deny" };
+           unshift(@{$config->{frr_routemap}->{'MAP_VTEP_IN'}}, $routemap);
+       } elsif ($exitnodes_primary ne $local_node) {
+           my $routemap_config = ();
+           push @{$routemap_config}, "match evpn vni $vrfvxlan";
+           push @{$routemap_config}, "match evpn route-type prefix";
+           push @{$routemap_config}, "set metric 200";
+           my $routemap = { rule => $routemap_config, action => "permit" };
+           unshift(@{$config->{frr_routemap}->{'MAP_VTEP_OUT'}}, $routemap);
+        }
+
+
+       if (!$exitnodes_local_routing) {
+           @controller_config = ();
+           #import /32 routes of evpn network from vrf1 to default vrf (for packet return)
+           push @controller_config, "import vrf $vrf";
+           push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config);
+           push(@{$config->{frr}->{router}->{"bgp $asn"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config);
+
+           @controller_config = ();
+           #redistribute connected to be able to route to local vms on the gateway
+           push @controller_config, "redistribute connected";
+           push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config);
+           push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config);
+       }
+
+       @controller_config = ();
+       #add default originate to announce 0.0.0.0/0 type5 route in evpn
+       push @controller_config, "default-originate ipv4";
+       push @controller_config, "default-originate ipv6";
+       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
+    } elsif ($advertisesubnets) {
+
+       @controller_config = ();
+       #redistribute connected networks
+       push @controller_config, "redistribute connected";
+       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv4 unicast"}}, @controller_config);
+       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"ipv6 unicast"}}, @controller_config);
+
+       @controller_config = ();
+       #advertise connected networks type5 route in evpn
+       push @controller_config, "advertise ipv4 unicast";
+       push @controller_config, "advertise ipv6 unicast";
+       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
+    }
+
+    if ($rt_import) {
+       @controller_config = ();
+       foreach my $rt (sort @{$rt_import}) {
+           push @controller_config, "route-target import $rt";
+       }
+       push(@{$config->{frr}->{router}->{"bgp $asn vrf $vrf"}->{"address-family"}->{"l2vpn evpn"}}, @controller_config);
+    }
+
+    return $config;
+}
+
+sub generate_controller_vnet_config {
+    my ($class, $plugin_config, $controller, $zone, $zoneid, $vnetid, $config) = @_;
+
+    my $exitnodes = $zone->{'exitnodes'};
+    my $exitnodes_local_routing = $zone->{'exitnodes-local-routing'};
+
+    return if !$exitnodes_local_routing;
+
+    my $local_node = PVE::INotify::nodename();
+    my $is_gateway = $exitnodes->{$local_node};
+
+    return if !$is_gateway;
+
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
+    my @controller_config = ();
+    foreach my $subnetid (sort keys %{$subnets}) {
+        my $subnet = $subnets->{$subnetid};
+       my $cidr = $subnet->{cidr};
+       push @controller_config, "ip route $cidr 10.255.255.2 xvrf_$zoneid";
+    }
+    push(@{$config->{frr}->{''}}, @controller_config);
+}
+
+sub on_delete_hook {
+    my ($class, $controllerid, $zone_cfg) = @_;
+
+    # verify that zone is associated to this controller
+    foreach my $id (keys %{$zone_cfg->{ids}}) {
+       my $zone = $zone_cfg->{ids}->{$id};
+       die "controller $controllerid is used by $id"
+           if (defined($zone->{controller}) && $zone->{controller} eq $controllerid);
+    }
+}
+
+sub on_update_hook {
+    my ($class, $controllerid, $controller_cfg) = @_;
+
+    # we can only have 1 evpn controller / 1 asn by server
+
+    my $controllernb = 0;
+    foreach my $id (keys %{$controller_cfg->{ids}}) {
+       next if $id eq $controllerid;
+       my $controller = $controller_cfg->{ids}->{$id};
+       next if $controller->{type} ne "evpn";
+       $controllernb++;
+       die "only 1 global evpn controller can be defined" if $controllernb >= 1;
+    }
+}
+
+sub find_bgp_controller {
+    my ($nodename, $controller_cfg) = @_;
+
+    my $controller = undef;
+    foreach my $id  (keys %{$controller_cfg->{ids}}) {
+        $controller = $controller_cfg->{ids}->{$id};
+        next if $controller->{type} ne 'bgp';
+        next if $controller->{node} ne $nodename;
+       last;
+    }
+
+    return $controller;
+}
+
+
+sub sort_frr_config {
+    my $order = {};
+    $order->{''} = 0;
+    $order->{'vrf'} = 1;
+    $order->{'ipv4 unicast'} = 1;
+    $order->{'ipv6 unicast'} = 2;
+    $order->{'l2vpn evpn'} = 3;
+
+    my $a_val = 100;
+    my $b_val = 100;
+
+    $a_val = $order->{$a} if defined($order->{$a});
+    $b_val = $order->{$b} if defined($order->{$b});
+
+    if ($a =~ /bgp (\d+)$/) {
+       $a_val = 2;
+    }
+
+    if ($b =~ /bgp (\d+)$/) {
+       $b_val = 2;
+    }
+
+    return $a_val <=> $b_val;
+}
+
+sub generate_frr_recurse{
+   my ($final_config, $content, $parentkey, $level) = @_;
+
+   my $keylist = {};
+   $keylist->{vrf} = 1;
+   $keylist->{'address-family'} = 1;
+   $keylist->{router} = 1;
+
+   my $exitkeylist = {};
+   $exitkeylist->{vrf} = 1;
+   $exitkeylist->{'address-family'} = 1;
+
+   my $simple_exitkeylist = {};
+   $simple_exitkeylist->{router} = 1;
+
+   # FIXME: make this generic
+   my $paddinglevel = undef;
+   if ($level == 1 || $level == 2) {
+       $paddinglevel = $level - 1;
+   } elsif ($level == 3 || $level ==  4) {
+       $paddinglevel = $level - 2;
+   }
+
+   my $padding = "";
+   $padding = ' ' x ($paddinglevel) if $paddinglevel;
+
+   if (ref $content eq  'HASH') {
+       foreach my $key (sort sort_frr_config keys %$content) {
+           if ($parentkey && defined($keylist->{$parentkey})) {
+               push @{$final_config}, $padding."!";
+               push @{$final_config}, $padding."$parentkey $key";
+           } elsif ($key ne '' && !defined($keylist->{$key})) {
+               push @{$final_config}, $padding."$key";
+           }
+
+           my $option = $content->{$key};
+           generate_frr_recurse($final_config, $option, $key, $level+1);
+
+           push @{$final_config}, $padding."exit-$parentkey" if $parentkey && defined($exitkeylist->{$parentkey});
+           push @{$final_config}, $padding."exit" if $parentkey && defined($simple_exitkeylist->{$parentkey});
+       }
+    }
+
+    if (ref $content eq 'ARRAY') {
+       push @{$final_config}, map { $padding . "$_" } @$content;
+    }
+}
+
+sub generate_frr_routemap {
+   my ($final_config, $routemaps) = @_;
+
+   foreach my $id (sort keys %$routemaps) {
+
+       my $routemap = $routemaps->{$id};
+       my $order = 0;
+       foreach my $seq (@$routemap) {
+               $order++;
+               next if !defined($seq->{action});
+               my @config = ();
+               push @config, "!";
+               push @config, "route-map $id $seq->{action} $order";
+               my $rule = $seq->{rule};
+               push @config, map { " $_" } @$rule;
+               push @{$final_config}, @config;
+               push @{$final_config}, "exit";
+       }
+   }
+}
+
+sub generate_frr_list {
+    my ($final_config, $lists, $type) = @_;
+
+    my $config = [];
+
+    for my $id (sort keys %$lists) {
+       my $list = $lists->{$id};
+
+       for my $seq (sort keys %$list) {
+           my $rule = $list->{$seq};
+           push @$config, "$type $id seq $seq $rule";
+       }
+    }
+
+    if (@$config > 0) {
+       push @{$final_config}, "!", @$config;
+    }
+}
+
+sub generate_controller_rawconfig {
+    my ($class, $plugin_config, $config) = @_;
+
+    my $nodename = PVE::INotify::nodename();
+
+    my $final_config = [];
+    push @{$final_config}, "frr version 8.2.2";
+    push @{$final_config}, "frr defaults datacenter";
+    push @{$final_config}, "hostname $nodename";
+    push @{$final_config}, "log syslog informational";
+    push @{$final_config}, "service integrated-vtysh-config";
+    push @{$final_config}, "!";
+
+    if (-e "/etc/frr/frr.conf.local") {
+       my $local_conf = file_get_contents("/etc/frr/frr.conf.local");
+       parse_merge_frr_local_config($config, $local_conf);
+    }
+
+    generate_frr_recurse($final_config, $config->{frr}, undef, 0);
+    generate_frr_list($final_config, $config->{frr_access_list}, "access-list");
+    generate_frr_list($final_config, $config->{frr_prefix_list}, "ip prefix-list");
+    generate_frr_routemap($final_config, $config->{frr_routemap});
+
+    push @{$final_config}, "!";
+    push @{$final_config}, "line vty";
+    push @{$final_config}, "!";
+
+    my $rawconfig = join("\n", @{$final_config});
+
+    return if !$rawconfig;
+    return $rawconfig;
+}
+
+sub parse_merge_frr_local_config {
+    my ($config, $local_conf) = @_;
+
+    my $section = \$config->{""};
+    my $router = undef;
+    my $routemap = undef;
+    my $routemap_config = ();
+    my $routemap_action = undef;
+
+    while ($local_conf =~ /^\s*(.+?)\s*$/gm) {
+        my $line = $1;
+       $line =~ s/^\s+|\s+$//g;
+
+       if ($line =~ m/^router (.+)$/) {
+           $router = $1;
+           $section = \$config->{'frr'}->{'router'}->{$router}->{""};
+           next;
+       } elsif ($line =~ m/^vrf (.+)$/) {
+           $section = \$config->{'frr'}->{'vrf'}->{$1};
+           next;
+       } elsif ($line =~ m/address-family (.+)$/) {
+           $section = \$config->{'frr'}->{'router'}->{$router}->{'address-family'}->{$1};
+           next;
+       } elsif ($line =~ m/^route-map (.+) (permit|deny) (\d+)/) {
+           $routemap = $1;
+           $routemap_config = ();
+           $routemap_action = $2;
+           $section = \$config->{'frr_routemap'}->{$routemap};
+           next;
+       } elsif ($line =~ m/^access-list (.+) seq (\d+) (.+)$/) {
+           $config->{'frr_access_list'}->{$1}->{$2} = $3;
+           next;
+       } elsif ($line =~ m/^ip prefix-list (.+) seq (\d+) (.*)$/) {
+           $config->{'frr_prefix_list'}->{$1}->{$2} = $3;
+           next;
+       } elsif($line =~ m/^exit-address-family$/) {
+           next;
+       } elsif($line =~ m/^exit$/) {
+           if($router) {
+               $section = \$config->{''};
+               $router = undef;
+           } elsif($routemap) {
+               push(@{$$section}, { rule => $routemap_config, action => $routemap_action });
+               $section = \$config->{''};
+               $routemap = undef;
+               $routemap_action = undef;
+               $routemap_config = ();
+           }
+           next;
+       } elsif($line =~ m/!/) {
+           next;
+       }
+
+       next if !$section;
+       if($routemap) {
+           push(@{$routemap_config}, $line);
+       } else {
+           push(@{$$section}, $line);
+       }
+    }
+}
+
+sub write_controller_config {
+    my ($class, $plugin_config, $config) = @_;
+
+    my $rawconfig = $class->generate_controller_rawconfig($plugin_config, $config);
+    return if !$rawconfig;
+    return if !-d "/etc/frr";
+
+    file_set_contents("/etc/frr/frr.conf", $rawconfig);
+}
+
+sub reload_controller {
+    my ($class) = @_;
+
+    my $conf_file = "/etc/frr/frr.conf";
+    my $bin_path = "/usr/lib/frr/frr-reload.py";
+
+    if (!-e $bin_path) {
+       warn "missing $bin_path. Please install frr-pythontools package";
+       return;
+    }
+
+    my $err = sub {
+       my $line = shift;
+       if ($line =~ /ERROR:/) {
+           warn "$line \n";
+       }
+    };
+
+    if (-e $conf_file && -e $bin_path) {
+       eval {
+           run_command([$bin_path, '--stdout', '--reload', $conf_file], outfunc => {}, errfunc => $err);
+       };
+       if ($@) {
+           warn "frr reload command fail. Restarting frr.";
+           eval { run_command(['systemctl', 'restart', 'frr']); };
+       }
+    }
+}
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Controllers/FaucetPlugin.pm b/src/PVE/Network/SDN/Controllers/FaucetPlugin.pm
new file mode 100644 (file)
index 0000000..4f3bb5c
--- /dev/null
@@ -0,0 +1,97 @@
+package PVE::Network::SDN::Controllers::FaucetPlugin;
+
+use strict;
+use warnings;
+use PVE::Network::SDN::Controllers::Plugin;
+use PVE::Tools;
+use PVE::INotify;
+use PVE::JSONSchema qw(get_standard_option);
+use CPAN::Meta::YAML;
+use Encode;
+
+use base('PVE::Network::SDN::Controllers::Plugin');
+
+sub type {
+    return 'faucet';
+}
+
+sub properties {
+    return {
+    };
+}
+
+# Plugin implementation
+sub generate_controller_config {
+    my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_;
+
+}
+
+sub generate_controller_zone_config {
+    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
+
+    my $dpid = $plugin_config->{'dp-id'};
+    my $dphex = printf("%x",$dpid);
+
+    my $zone_config = {
+                               dp_id => $dphex,
+                               hardware => "Open vSwitch",
+                          };
+
+    $config->{faucet}->{dps}->{$id} = $zone_config;
+
+}
+
+
+sub generate_controller_vnet_config {
+    my ($class, $plugin_config, $controller, $zone, $zoneid, $vnetid, $config) = @_;
+
+    my $mac = $plugin_config->{mac};
+    my $ipv4 = $plugin_config->{ipv4};
+    my $ipv6 = $plugin_config->{ipv6};
+    my $tag = $plugin_config->{tag};
+    my $alias = $plugin_config->{alias};
+
+    my @ips = ();
+    push @ips, $ipv4 if $ipv4;
+    push @ips, $ipv6 if $ipv6;
+
+    my $vlan_config = { vid => $tag };
+
+    $vlan_config->{description} = $alias if $alias;
+    $vlan_config->{faucet_mac} = $mac if $mac;
+    $vlan_config->{faucet_vips} = \@ips if scalar @ips > 0;
+
+    $config->{faucet}->{vlans}->{$vnetid} = $vlan_config;
+
+    push(@{$config->{faucet}->{routers}->{$zoneid}->{vlans}} , $vnetid);
+
+}
+
+sub write_controller_config {
+    my ($class, $plugin_config, $config) = @_;
+
+    my $rawconfig = encode('UTF-8', CPAN::Meta::YAML::Dump($config->{faucet}));
+
+    return if !$rawconfig;
+    return if !-d "/etc/faucet";
+
+    my $frr_config_file = "/etc/faucet/faucet.yaml";
+
+    my $writefh = IO::File->new($frr_config_file,">");
+    print $writefh $rawconfig;
+    $writefh->close();
+}
+
+sub reload_controller {
+    my ($class) = @_;
+
+    my $conf_file = "/etc/faucet/faucet.yaml";
+    my $bin_path = "/usr/bin/faucet";
+
+    if (-e $conf_file && -e $bin_path) {
+        PVE::Tools::run_command(['systemctl', 'reload', 'faucet']);
+    }
+}
+
+1;
+
diff --git a/src/PVE/Network/SDN/Controllers/Makefile b/src/PVE/Network/SDN/Controllers/Makefile
new file mode 100644 (file)
index 0000000..11686a3
--- /dev/null
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm FaucetPlugin.pm EvpnPlugin.pm BgpPlugin.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Controllers/$$i; done
diff --git a/src/PVE/Network/SDN/Controllers/Plugin.pm b/src/PVE/Network/SDN/Controllers/Plugin.pm
new file mode 100644 (file)
index 0000000..c1c2cfd
--- /dev/null
@@ -0,0 +1,121 @@
+package PVE::Network::SDN::Controllers::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Tools;
+use PVE::JSONSchema;
+use PVE::Cluster;
+
+use Data::Dumper;
+use PVE::JSONSchema qw(get_standard_option);
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file('sdn/controllers.cfg',
+    sub { __PACKAGE__->parse_config(@_); },
+    sub { __PACKAGE__->write_config(@_); }
+);
+
+PVE::JSONSchema::register_standard_option('pve-sdn-controller-id', {
+    description => "The SDN controller object identifier.",
+    type => 'string', format => 'pve-sdn-controller-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-controller-id', \&parse_sdn_controller_id);
+sub parse_sdn_controller_id {
+    my ($id, $noerr) = @_;
+
+    if ($id !~ m/^[a-z][a-z0-9_-]*[a-z0-9]$/i) {
+        return undef if $noerr;
+        die "controller ID '$id' contains illegal characters\n";
+    }
+    die "controller ID '$id' can't be more length than 64 characters\n" if length($id) > 64;
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+       type => {
+           description => "Plugin type.",
+           type => 'string', format => 'pve-configid',
+           type => 'string',
+       },
+        controller => get_standard_option('pve-sdn-controller-id',
+            { completion => \&PVE::Network::SDN::complete_sdn_controller }),
+    },
+};
+
+sub private {
+    return $defaultData;
+}
+
+sub parse_section_header {
+    my ($class, $line) = @_;
+
+    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
+        my ($type, $id) = (lc($1), $2);
+       my $errmsg = undef; # set if you want to skip whole section
+       eval { PVE::JSONSchema::pve_verify_configid($type); };
+       $errmsg = $@ if $@;
+       my $config = {}; # to return additional attributes
+       return ($type, $id, $errmsg, $config);
+    }
+    return undef;
+}
+
+sub generate_sdn_config {
+    my ($class, $plugin_config, $node, $data, $ctime) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub generate_controller_config {
+    my ($class, $plugin_config, $controller_cfg, $id, $uplinks, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+
+sub generate_controller_zone_config {
+    my ($class, $plugin_config, $controller, $controller_cfg, $id, $uplinks, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub generate_controller_vnet_config {
+    my ($class, $plugin_config, $controller, $zoneid, $vnetid, $config) = @_;
+
+}
+
+sub generate_controller_rawconfig {
+    my ($class, $plugin_config, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub write_controller_config {
+    my ($class, $plugin_config, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub controller_reload {
+    my ($class) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub on_delete_hook {
+    my ($class, $controllerid, $zone_cfg) = @_;
+
+    # do nothing by default
+}
+
+sub on_update_hook {
+    my ($class, $controllerid, $controller_cfg) = @_;
+
+    # do nothing by default
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Dns.pm b/src/PVE/Network/SDN/Dns.pm
new file mode 100644 (file)
index 0000000..c2e153a
--- /dev/null
@@ -0,0 +1,57 @@
+package PVE::Network::SDN::Dns;
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network;
+
+use PVE::Network::SDN::Dns::PowerdnsPlugin;
+use PVE::Network::SDN::Dns::Plugin;
+
+PVE::Network::SDN::Dns::PowerdnsPlugin->register();
+PVE::Network::SDN::Dns::Plugin->init();
+
+
+sub sdn_dns_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn dns ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/dns.cfg");
+    return $config;
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/dns.cfg", $cfg);
+}
+
+sub sdn_dns_ids {
+    my ($cfg) = @_;
+
+    return keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_dns {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::Dns::config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Dns::sdn_dns_ids($cfg) ];
+}
+
+1;
+
diff --git a/src/PVE/Network/SDN/Dns/Makefile b/src/PVE/Network/SDN/Dns/Makefile
new file mode 100644 (file)
index 0000000..81cd2a1
--- /dev/null
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm PowerdnsPlugin.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Dns/$$i; done
diff --git a/src/PVE/Network/SDN/Dns/Plugin.pm b/src/PVE/Network/SDN/Dns/Plugin.pm
new file mode 100644 (file)
index 0000000..07d0be1
--- /dev/null
@@ -0,0 +1,109 @@
+package PVE::Network::SDN::Dns::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(run_command);
+use PVE::JSONSchema;
+use PVE::Cluster;
+use HTTP::Request;
+use LWP::UserAgent;
+
+use Data::Dumper;
+use PVE::JSONSchema qw(get_standard_option);
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file('sdn/dns.cfg',
+                                sub { __PACKAGE__->parse_config(@_); },
+                                sub { __PACKAGE__->write_config(@_); });
+
+PVE::JSONSchema::register_standard_option('pve-sdn-dns-id', {
+    description => "The SDN dns object identifier.",
+    type => 'string', format => 'pve-sdn-dns-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-dns-id', \&parse_sdn_dns_id);
+sub parse_sdn_dns_id {
+    my ($id, $noerr) = @_;
+
+    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
+       return undef if $noerr;
+       die "dns ID '$id' contains illegal characters\n";
+    }
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+       type => {
+           description => "Plugin type.",
+           type => 'string', format => 'pve-configid',
+       },
+        ttl => { type => 'integer', optional => 1 },
+        reversev6mask => { type => 'integer', optional => 1 },
+        dns => get_standard_option('pve-sdn-dns-id',
+            { completion => \&PVE::Network::SDN::Dns::complete_sdn_dns }),
+    },
+};
+
+sub private {
+    return $defaultData;
+}
+
+sub parse_section_header {
+    my ($class, $line) = @_;
+
+    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
+        my ($type, $id) = (lc($1), $2);
+       my $errmsg = undef; # set if you want to skip whole section
+       eval { PVE::JSONSchema::pve_verify_configid($type); };
+       $errmsg = $@ if $@;
+       my $config = {}; # to return additional attributes
+       return ($type, $id, $errmsg, $config);
+    }
+    return undef;
+}
+
+
+sub add_a_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub add_ptr_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub del_ptr_record {
+    my ($class, $plugin_config, $zone, $ip, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub del_a_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub verify_zone {
+    my ($class, $plugin_config, $zone, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub get_reversedns_zone {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub on_update_hook {
+    my ($class, $plugin_config) = @_;
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/src/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
new file mode 100644 (file)
index 0000000..096d131
--- /dev/null
@@ -0,0 +1,329 @@
+package PVE::Network::SDN::Dns::PowerdnsPlugin;
+
+use strict;
+use warnings;
+use PVE::INotify;
+use PVE::Cluster;
+use PVE::Tools;
+use JSON;
+use Net::IP;
+use NetAddr::IP qw(:lower);
+use base('PVE::Network::SDN::Dns::Plugin');
+
+sub type {
+    return 'powerdns';
+}
+
+sub properties {
+    return {
+       url => {
+           type => 'string',
+       },
+       key => {
+           type => 'string',
+       },
+        reversemaskv6 => { 
+           type => 'integer' 
+        },
+    };
+}
+
+sub options {
+
+    return {
+        url => { optional => 0},
+        key => { optional => 0 },
+        ttl => { optional => 1 },
+        reversemaskv6 => { optional => 1, description => "force a different netmask for the ipv6 reverse zone name." },
+
+    };
+}
+
+# Plugin implementation
+
+sub add_a_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $ttl = $plugin_config->{ttl} ? $plugin_config->{ttl} : 14400;
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
+    my $fqdn = $hostname.".".$zone.".";
+
+    my $zonecontent = get_zone_content($plugin_config, $zone);
+    my $existing_rrset = get_zone_rrset($zonecontent, $fqdn);
+
+    my $final_records = [];
+    my $foundrecord = undef;
+    foreach my $record (@{$existing_rrset->{records}}) {
+       if($record->{content} eq $ip) {
+           $foundrecord = 1;
+           next;
+       }
+       push @$final_records, $record;
+    }
+    return if $foundrecord;
+
+    my $record = { content => $ip, 
+                   disabled => JSON::false, 
+                  name => $fqdn, 
+                   type => $type, 
+                   priority => 0 };
+
+    push @$final_records, $record;
+
+    my $rrset = { name => $fqdn, 
+                 type => $type, 
+                   ttl =>  $ttl, 
+                 changetype => "REPLACE",
+                 records => $final_records  };
+
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+       PVE::Network::SDN::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+       die "error add $fqdn to zone $zone: $@" if !$noerr;
+    }
+}
+
+sub add_ptr_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $ttl = $plugin_config->{ttl} ? $plugin_config->{ttl} : 14400;
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+    $hostname .= ".";
+
+    my $reverseip = Net::IP->new($ip)->reverse_ip();
+
+    my $type = "PTR";
+
+    my $record = { content => $hostname, 
+                   disabled => JSON::false, 
+                  name => $reverseip, 
+                   type => $type, 
+                   priority => 0 };
+
+    my $rrset = { name => $reverseip, 
+                 type => $type, 
+                   ttl =>  $ttl, 
+                 changetype => "REPLACE",
+                 records => [ $record ] };
+
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+       PVE::Network::SDN::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+       die "error add $reverseip to zone $zone: $@" if !$noerr;
+    }
+}
+
+sub del_a_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip, $noerr) = @_;
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+    my $fqdn = $hostname.".".$zone.".";
+    my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
+
+    my $zonecontent = get_zone_content($plugin_config, $zone);
+    my $existing_rrset = get_zone_rrset($zonecontent, $fqdn);
+
+    my $final_records = [];
+    my $foundrecord = undef;
+    foreach my $record (@{$existing_rrset->{records}}) {
+        if ($record->{content} eq $ip) {
+           $foundrecord = 1;
+           next;
+       }
+       push @$final_records, $record;
+    }
+    return if !$foundrecord;
+
+    my $rrset = {};
+   
+    if (scalar (@{$final_records}) > 0) {
+       #if we still have other records, we rewrite them without removed ip
+       $rrset = { name => $fqdn,
+                  type => $type,
+                  ttl =>  $existing_rrset->{ttl},
+                  changetype => "REPLACE",
+                  records => $final_records  };
+
+    } else {
+
+       $rrset = { name => $fqdn, 
+                  type => $type, 
+                  changetype => "DELETE",
+                   records => [] };
+    }
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+       PVE::Network::SDN::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+       die "error delete $fqdn from zone $zone: $@" if !$noerr;
+    }
+}
+
+sub del_ptr_record {
+    my ($class, $plugin_config, $zone, $ip, $noerr) = @_;
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    my $reverseip = Net::IP->new($ip)->reverse_ip();
+
+    my $type = "PTR";
+
+    my $rrset = { name => $reverseip, 
+                 type => $type, 
+                 changetype => "DELETE",
+                 records => [] };
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+       PVE::Network::SDN::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+       die "error delete $reverseip from zone $zone: $@" if !$noerr;
+    }
+}
+
+sub verify_zone {
+    my ($class, $plugin_config, $zone, $noerr) = @_;
+
+    #verify that api is working              
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    eval {
+        PVE::Network::SDN::api_request("GET", "$url/zones/$zone?rrsets=false", $headers);
+    };
+
+    if ($@) {
+        die "can't read zone $zone: $@" if !$noerr;
+    }
+}
+
+sub get_reversedns_zone {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $mask = $subnet->{mask};
+
+    my $zone = "";
+
+    if (Net::IP::ip_is_ipv4($ip)) {
+       my ($ipblock1, $ipblock2, $ipblock3, $ipblock4) = split(/\./, $ip);
+
+        my $ipv4 = NetAddr::IP->new($cidr);
+       #private addresse #powerdns built-in private zone : serve-rfc1918
+       if($ipv4->is_rfc1918()) {
+           if ($ipblock1 == 192) {
+               $zone = "168.192.in-addr.arpa.";
+           } elsif ($ipblock1 == 172) {
+               $zone = "16-31.172.in-addr.arpa.";
+           } elsif ($ipblock1 == 10) {
+               $zone = "10.in-addr.arpa.";
+           }
+               
+       } else {
+           #public ipv4 : RIPE,ARIN,AFRNIC
+           #. Delegations can be managed in IPv4 on bit boundaries (/8, /16 or /24s), and IPv6 networks can be managed on nibble boundaries (every 4 bits of the IPv6 address)
+           #One or more /24 type zones need to be created if your address space has a prefix length between /17 and /24. 
+           # If your prefix length is between /16 and /9 you will have to request one or more delegations for /16 type zones.
+
+           if ($mask <= 24) {
+               $zone = "$ipblock3.$ipblock2.$ipblock1.in-addr.arpa.";
+           } elsif ($mask <= 16) {
+               $zone = "$ipblock2.$ipblock1.in-addr.arpa.";
+           } elsif ($mask <= 8) {
+               $zone = "$ipblock1.in-addr.arpa.";
+           }
+       }
+    } else {
+       $mask = $plugin_config->{reversemaskv6} if $plugin_config->{reversemaskv6};
+       die "reverse dns zone mask need to be a multiple of 4" if ($mask % 4);
+       my $networkv6 = NetAddr::IP->new($cidr)->network();
+       $zone = Net::IP->new($networkv6)->reverse_ip();
+    }
+
+    return $zone;
+}
+
+
+sub on_update_hook {
+    my ($class, $plugin_config) = @_;
+
+    #verify that api is working
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    eval {
+       PVE::Network::SDN::api_request("GET", "$url", $headers);
+    };
+
+    if ($@) {
+       die "dns api error: $@";
+    }
+}
+
+
+sub get_zone_content {
+    my ($plugin_config, $zone) = @_;
+
+    #verify that api is working              
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    my $result = undef;
+    eval {
+        $result = PVE::Network::SDN::api_request("GET", "$url/zones/$zone", $headers);
+    };
+
+    if ($@) {
+        die "can't read zone $zone: $@";
+    }
+    return $result;
+}
+
+sub get_zone_rrset {
+    my ($zonecontent, $name) = @_;
+
+    my $rrsetresult = undef;
+    foreach my $rrset (@{$zonecontent->{rrsets}}) {
+       next if $rrset->{name} ne $name;
+        $rrsetresult = $rrset;
+       last; 
+    }
+    return $rrsetresult;
+}
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Ipams.pm b/src/PVE/Network/SDN/Ipams.pm
new file mode 100644 (file)
index 0000000..e8a4b0b
--- /dev/null
@@ -0,0 +1,69 @@
+package PVE::Network::SDN::Ipams;
+
+use strict;
+use warnings;
+
+use JSON;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network;
+
+use PVE::Network::SDN::Ipams::PVEPlugin;
+use PVE::Network::SDN::Ipams::NetboxPlugin;
+use PVE::Network::SDN::Ipams::PhpIpamPlugin;
+use PVE::Network::SDN::Ipams::Plugin;
+
+PVE::Network::SDN::Ipams::PVEPlugin->register();
+PVE::Network::SDN::Ipams::NetboxPlugin->register();
+PVE::Network::SDN::Ipams::PhpIpamPlugin->register();
+PVE::Network::SDN::Ipams::Plugin->init();
+
+
+sub sdn_ipams_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn ipam ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/ipams.cfg");
+    #add default internal pve
+    $config->{ids}->{pve}->{type} = 'pve';
+    return $config;
+}
+
+sub get_plugin_config {
+    my ($vnet) = @_;
+    my $ipamid = $vnet->{ipam};
+    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+    return $ipam_cfg->{ids}->{$ipamid};
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/ipams.cfg", $cfg);
+}
+
+sub sdn_ipams_ids {
+    my ($cfg) = @_;
+
+    return keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_vnet {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::Ipams::config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Vnets::sdn_ipams_ids($cfg) ];
+}
+
+1;
+
diff --git a/src/PVE/Network/SDN/Ipams/Makefile b/src/PVE/Network/SDN/Ipams/Makefile
new file mode 100644 (file)
index 0000000..4e7d65f
--- /dev/null
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm PhpIpamPlugin.pm NetboxPlugin.pm PVEPlugin.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Ipams/$$i; done
diff --git a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
new file mode 100644 (file)
index 0000000..f0e7168
--- /dev/null
@@ -0,0 +1,226 @@
+package PVE::Network::SDN::Ipams::NetboxPlugin;
+
+use strict;
+use warnings;
+use PVE::INotify;
+use PVE::Cluster;
+use PVE::Tools;
+
+use base('PVE::Network::SDN::Ipams::Plugin');
+
+sub type {
+    return 'netbox';
+}
+
+sub properties {
+    return {
+    };
+}
+
+sub options {
+
+    return {
+        url => { optional => 0},
+        token => { optional => 0 },
+    };
+}
+
+# Plugin implementation
+
+sub add_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $gateway = $subnet->{gateway};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+
+    #create subnet
+    if (!$internalid) {
+
+       my $params = { prefix => $cidr };
+
+       eval {
+               my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/prefixes/", $headers, $params);
+       };
+       if ($@) {
+           die "error add subnet to ipam: $@" if !$noerr;
+       }
+    }
+   
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $gateway = $subnet->{gateway};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+    return if !$internalid;
+
+    return; #fixme: check that prefix is empty exluding gateway, before delete
+
+    eval {
+       PVE::Network::SDN::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers);
+    };
+    if ($@) {
+       die "error deleting subnet from ipam: $@" if !$noerr;
+    }
+
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+
+    my $mask = $subnet->{mask};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+    $description .= " mac:$mac" if $mac && $description;
+
+    my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
+
+    eval {
+       PVE::Network::SDN::api_request("POST", "$url/ipam/ip-addresses/", $headers, $params);
+    };
+
+    if ($@) {
+        if($is_gateway) {
+           die "error add subnet ip to ipam: ip $ip already exist: $@" if !is_ip_gateway($url, $ip, $headers) && !$noerr;
+        } else {
+           die "error add subnet ip to ipam: ip already exist: $@" if !$noerr;
+       }
+    }
+}
+
+sub update_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+
+    my $mask = $subnet->{mask};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+    $description .= " mac:$mac" if $mac && $description;
+
+    my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
+
+    my $ip_id = get_ip_id($url, $ip, $headers);
+    die "can't find ip $ip in ipam" if !$ip_id;
+
+    eval {
+       PVE::Network::SDN::api_request("PATCH", "$url/ipam/ip-addresses/$ip_id/", $headers, $params);
+    };
+    if ($@) {
+       die "error update ip $ip : $@" if !$noerr;
+    }
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
+
+    my $cidr = $subnet->{cidr};
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+    $description .= " mac:$mac" if $mac && $description;
+
+    my $params = { dns_name => $hostname, description => $description };
+
+    my $ip = undef;
+    eval {
+       my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/prefixes/$internalid/available-ips/", $headers, $params);
+       $ip = $result->{address};
+    };
+
+    if ($@) {
+       die "can't find free ip in subnet $cidr: $@" if !$noerr;
+    }
+
+    return $ip;
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
+
+    return if !$ip;
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $ip_id = get_ip_id($url, $ip, $headers);
+    die "can't find ip $ip in ipam" if !$ip_id;
+
+    eval {
+       PVE::Network::SDN::api_request("DELETE", "$url/ipam/ip-addresses/$ip_id/", $headers);
+    };
+    if ($@) {
+       die "error delete ip $ip : $@" if !$noerr;
+    }
+}
+
+sub verify_api {
+    my ($class, $plugin_config) = @_;
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+
+    eval {
+       PVE::Network::SDN::api_request("GET", "$url/ipam/aggregates/", $headers);
+    };
+    if ($@) {
+       die "Can't connect to netbox api: $@";
+    }
+}
+
+sub on_update_hook {
+    my ($class, $plugin_config) = @_;
+
+    PVE::Network::SDN::Ipams::NetboxPlugin::verify_api($class, $plugin_config);
+}
+
+#helpers
+
+sub get_prefix_id {
+    my ($url, $cidr, $headers) = @_;
+
+    my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/prefixes/?q=$cidr", $headers);
+    my $data = @{$result->{results}}[0];
+    my $internalid = $data->{id};
+    return $internalid;
+}
+
+sub get_ip_id {
+    my ($url, $ip, $headers) = @_;
+    my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers);
+    my $data = @{$result->{results}}[0];
+    my $ip_id = $data->{id};
+    return $ip_id;
+}
+
+sub is_ip_gateway {
+    my ($url, $ip, $headers) = @_;
+    my $result = PVE::Network::SDN::api_request("GET", "$url/addresses/search/$ip", $headers);
+    my $data = @{$result->{data}}[0];
+    my $description = $data->{description};
+    my $is_gateway = 1 if $description eq 'gateway';
+    return $is_gateway;
+}
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Ipams/PVEPlugin.pm b/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
new file mode 100644 (file)
index 0000000..3e8ffc5
--- /dev/null
@@ -0,0 +1,210 @@
+package PVE::Network::SDN::Ipams::PVEPlugin;
+
+use strict;
+use warnings;
+use PVE::INotify;
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file);
+use PVE::Tools;
+use JSON;
+use NetAddr::IP qw(:lower);
+
+use Net::IP;
+use Digest::SHA;
+
+use base('PVE::Network::SDN::Ipams::Plugin');
+
+
+my $ipamdb_file = "priv/ipam.db";
+
+PVE::Cluster::cfs_register_file($ipamdb_file,
+                                 sub { PVE::Network::SDN::Ipams::PVEPlugin->parse_config(@_); },
+                                 sub { PVE::Network::SDN::Ipams::PVEPlugin->write_config(@_); });
+
+sub type {
+    return 'pve';
+}
+
+sub properties {
+}
+
+sub options {
+}
+
+# Plugin implementation
+
+sub add_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
+    my $gateway = $subnet->{gateway};
+
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+       my $db = {};
+       $db = read_db();
+
+       $db->{zones}->{$zone} = {} if !$db->{zones}->{$zone};
+       my $zonedb = $db->{zones}->{$zone};
+
+       if(!$zonedb->{subnets}->{$cidr}) {
+           #create subnet
+           $zonedb->{subnets}->{$cidr}->{ips} = {};
+           write_db($db);
+       }
+    });
+    die "$@" if $@;
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+
+       my $db = read_db();
+
+       my $dbzone = $db->{zones}->{$zone};
+       die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
+       my $dbsubnet = $dbzone->{subnets}->{$cidr};
+       die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
+
+       die "cannot delete subnet '$cidr', not empty\n" if keys %{$dbsubnet->{ips}} > 0;
+
+       delete $dbzone->{subnets}->{$cidr};
+
+       write_db($db);
+    });
+    die "$@" if $@;
+
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+
+       my $db = read_db();
+       my $dbzone = $db->{zones}->{$zone};
+       die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
+       my $dbsubnet = $dbzone->{subnets}->{$cidr};
+       die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
+
+       die "IP '$ip' already exist\n" if (!$is_gateway && defined($dbsubnet->{ips}->{$ip})) || ($is_gateway && defined($dbsubnet->{ips}->{$ip}) && !defined($dbsubnet->{ips}->{$ip}->{gateway}));
+       $dbsubnet->{ips}->{$ip} = {};
+       $dbsubnet->{ips}->{$ip} = {gateway => 1} if $is_gateway;
+
+       write_db($db);
+    });
+    die "$@" if $@;
+}
+
+sub update_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
+    return;
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $network = $subnet->{network};
+    my $zone = $subnet->{zone};
+    my $mask = $subnet->{mask};
+    my $freeip = undef;
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+
+       my $db = read_db();
+       my $dbzone = $db->{zones}->{$zone};
+       die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
+       my $dbsubnet = $dbzone->{subnets}->{$cidr};
+       die "subnet '$cidr' doesn't exist in IPAM DB" if !$dbsubnet;
+
+       if (Net::IP::ip_is_ipv4($network) && $mask == 32) {
+           die "cannot find free IP in subnet '$cidr'\n" if defined($dbsubnet->{ips}->{$network});
+           $freeip = $network;
+       } else {
+           my $iplist = NetAddr::IP->new($cidr);
+           my $lastip = $iplist->last()->canon();
+           $iplist++ if Net::IP::ip_is_ipv4($network); #skip network address for ipv4
+           while(1) {
+               my $ip = $iplist->canon();
+               if (defined($dbsubnet->{ips}->{$ip})) {
+                   last if $ip eq $lastip;
+                   $iplist++;
+                   next;
+               } 
+               $freeip = $ip;
+               last;
+           }
+       }
+
+       die "can't find free ip in subnet '$cidr'\n" if !$freeip;
+
+       $dbsubnet->{ips}->{$freeip} = {};
+
+       write_db($db);
+    });
+    die "$@" if $@;
+
+    return "$freeip/$mask";
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+
+       my $db = read_db();
+       die "zone $zone don't exist in ipam db" if !$db->{zones}->{$zone};
+       my $dbzone = $db->{zones}->{$zone};
+       die "subnet $cidr don't exist in ipam db" if !$dbzone->{subnets}->{$cidr};
+       my $dbsubnet = $dbzone->{subnets}->{$cidr};
+
+       die "IP '$ip' does not exist in IPAM DB\n" if !defined($dbsubnet->{ips}->{$ip});
+       delete $dbsubnet->{ips}->{$ip};
+
+       write_db($db);
+    });
+    die "$@" if $@;
+}
+
+#helpers
+
+sub read_db {
+    my $db = cfs_read_file($ipamdb_file);
+    return $db;
+}
+
+sub write_db {
+    my ($cfg) = @_;
+
+    my $json = to_json($cfg);
+    cfs_write_file($ipamdb_file, $json);
+}
+
+sub write_config {
+    my ($class, $filename, $cfg) = @_;
+
+    return $cfg;
+}
+
+sub parse_config {
+    my ($class, $filename, $raw) = @_;
+
+    $raw = '{}' if !defined($raw) ||$raw eq '';
+    my $cfg = from_json($raw);
+
+    return $cfg;
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
new file mode 100644 (file)
index 0000000..ad5286b
--- /dev/null
@@ -0,0 +1,259 @@
+package PVE::Network::SDN::Ipams::PhpIpamPlugin;
+
+use strict;
+use warnings;
+use PVE::INotify;
+use PVE::Cluster;
+use PVE::Tools;
+
+use base('PVE::Network::SDN::Ipams::Plugin');
+
+sub type {
+    return 'phpipam';
+}
+
+sub properties {
+    return {
+       url => {
+           type => 'string',
+       },
+       token => {
+           type => 'string',
+       },
+       section => {
+           type => 'integer',
+       },
+    };
+}
+
+sub options {
+
+    return {
+        url => { optional => 0},
+        token => { optional => 0 },
+        section => { optional => 0 },
+    };
+}
+
+# Plugin implementation
+
+sub add_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $network = $subnet->{network};
+    my $mask = $subnet->{mask};
+
+    my $gateway = $subnet->{gateway};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    #search subnet
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+
+    #create subnet
+    if (!$internalid) {
+       my $params = { subnet => $network,
+                  mask => $mask,
+                  sectionId => $section,
+                 };
+
+       eval {
+               PVE::Network::SDN::api_request("POST", "$url/subnets/", $headers, $params);
+       };
+       if ($@) {
+           die "error add subnet to ipam: $@" if !$noerr;
+       }
+    }
+
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+    return if !$internalid;
+
+    return; #fixme: check that prefix is empty exluding gateway, before delete
+
+    eval {
+       PVE::Network::SDN::api_request("DELETE", "$url/subnets/$internalid", $headers);
+    };
+    if ($@) {
+       die "error deleting subnet from ipam: $@" if !$noerr;
+    }
+
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+
+    my $params = { ip => $ip,
+                  subnetId => $internalid,
+                  hostname => $hostname,
+                  description => $description,
+                 };
+    $params->{is_gateway} = 1 if $is_gateway;
+    $params->{mac} = $mac if $mac;
+
+    eval {
+       PVE::Network::SDN::api_request("POST", "$url/addresses/", $headers, $params);
+    };
+
+    if ($@) {
+       if($is_gateway) {
+           die "error add subnet ip to ipam: ip $ip already exist: $@" if !is_ip_gateway($url, $ip, $headers) && !$noerr;
+       } else {
+           die "error add subnet ip to ipam: ip $ip already exist: $@" if !$noerr;
+       }
+    }
+}
+
+sub update_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    my $ip_id = get_ip_id($url, $ip, $headers);
+    die "can't find ip addresse in ipam" if !$ip_id;
+
+    my $params = { 
+                  hostname => $hostname,
+                  description => $description,
+                 };
+    $params->{is_gateway} = 1 if $is_gateway;
+    $params->{mac} = $mac if $mac;
+
+    eval {
+       PVE::Network::SDN::api_request("PATCH", "$url/addresses/$ip_id", $headers, $params);
+    };
+
+    if ($@) {
+       die "ipam: error update subnet ip $ip: $@" if !$noerr;
+    }
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
+
+    my $cidr = $subnet->{cidr};  
+    my $mask = $subnet->{mask};  
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+
+    my $params = { hostname => $hostname,
+                  description => $description,
+                 };
+
+    $params->{mac} = $mac if $mac;
+
+    my $ip = undef;
+    eval {
+       my $result = PVE::Network::SDN::api_request("POST", "$url/addresses/first_free/$internalid/", $headers, $params);
+       $ip = $result->{data};
+    };
+
+    if ($@) {
+        die "can't find free ip in subnet $cidr: $@" if !$noerr;
+    }
+
+    return "$ip/$mask" if $ip && $mask;
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
+
+    return if !$ip;
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    my $ip_id = get_ip_id($url, $ip, $headers);
+    return if !$ip_id;
+
+    eval {
+       PVE::Network::SDN::api_request("DELETE", "$url/addresses/$ip_id", $headers);
+    };
+    if ($@) {
+       die "error delete ip $ip: $@" if !$noerr;
+    }
+}
+
+sub verify_api {
+    my ($class, $plugin_config) = @_;
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $sectionid = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    eval {
+       PVE::Network::SDN::api_request("GET", "$url/sections/$sectionid", $headers);
+    };
+    if ($@) {
+       die "Can't connect to phpipam api: $@";
+    }
+}
+
+sub on_update_hook {
+    my ($class, $plugin_config) = @_;
+
+    PVE::Network::SDN::Ipams::PhpIpamPlugin::verify_api($class, $plugin_config);
+}
+
+
+#helpers
+
+sub get_prefix_id {
+    my ($url, $cidr, $headers) = @_;
+
+    my $result = PVE::Network::SDN::api_request("GET", "$url/subnets/cidr/$cidr", $headers);
+    my $data = @{$result->{data}}[0];
+    my $internalid = $data->{id};
+    return $internalid;
+}
+
+sub get_ip_id {
+    my ($url, $ip, $headers) = @_;
+    my $result = PVE::Network::SDN::api_request("GET", "$url/addresses/search/$ip", $headers);
+    my $data = @{$result->{data}}[0];
+    my $ip_id = $data->{id};
+    return $ip_id;
+}
+
+sub is_ip_gateway {
+    my ($url, $ip, $headers) = @_;
+    my $result = PVE::Network::SDN::api_request("GET", "$url/addresses/search/$ip", $headers);
+    my $data = @{$result->{data}}[0];
+    my $is_gateway = $data->{is_gateway};
+    return $is_gateway;
+}
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Ipams/Plugin.pm b/src/PVE/Network/SDN/Ipams/Plugin.pm
new file mode 100644 (file)
index 0000000..c96eeda
--- /dev/null
@@ -0,0 +1,111 @@
+package PVE::Network::SDN::Ipams::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(run_command);
+use PVE::JSONSchema;
+use PVE::Cluster;
+use HTTP::Request;
+use LWP::UserAgent;
+use JSON;
+
+use Data::Dumper;
+use PVE::JSONSchema qw(get_standard_option);
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file('sdn/ipams.cfg',
+                                sub { __PACKAGE__->parse_config(@_); },
+                                sub { __PACKAGE__->write_config(@_); });
+
+PVE::JSONSchema::register_standard_option('pve-sdn-ipam-id', {
+    description => "The SDN ipam object identifier.",
+    type => 'string', format => 'pve-sdn-ipam-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-ipam-id', \&parse_sdn_ipam_id);
+sub parse_sdn_ipam_id {
+    my ($id, $noerr) = @_;
+
+    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
+       return undef if $noerr;
+       die "ipam ID '$id' contains illegal characters\n";
+    }
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+       type => {
+           description => "Plugin type.",
+           type => 'string', format => 'pve-configid',
+           type => 'string',
+       },
+        ipam => get_standard_option('pve-sdn-ipam-id',
+            { completion => \&PVE::Network::SDN::Ipams::complete_sdn_ipam }),
+    },
+};
+
+sub private {
+    return $defaultData;
+}
+
+sub parse_section_header {
+    my ($class, $line) = @_;
+
+    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
+        my ($type, $id) = (lc($1), $2);
+       my $errmsg = undef; # set if you want to skip whole section
+       eval { PVE::JSONSchema::pve_verify_configid($type); };
+       $errmsg = $@ if $@;
+       my $config = {}; # to return additional attributes
+       return ($type, $id, $errmsg, $config);
+    }
+    return undef;
+}
+
+
+sub add_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub update_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+    # only update ip attributes (mac,hostname,..). Don't change the ip addresses itself, as some ipam
+    # don't allow ip address change without del/add
+
+    die "please implement inside plugin";
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub on_update_hook {
+    my ($class, $plugin_config)  = @_;
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Makefile b/src/PVE/Network/SDN/Makefile
new file mode 100644 (file)
index 0000000..92cfcd0
--- /dev/null
@@ -0,0 +1,13 @@
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm Dns.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/$$i; done
+       make -C Controllers install
+       make -C Zones install
+       make -C Ipams install
+       make -C Dns install
+
diff --git a/src/PVE/Network/SDN/SubnetPlugin.pm b/src/PVE/Network/SDN/SubnetPlugin.pm
new file mode 100644 (file)
index 0000000..15b370f
--- /dev/null
@@ -0,0 +1,166 @@
+package PVE::Network::SDN::SubnetPlugin;
+
+use strict;
+use warnings;
+
+use Net::IP;
+use Net::Subnet qw(subnet_matcher);
+
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Network::SDN::Ipams;
+use PVE::Network::SDN::Vnets;
+
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file('sdn/subnets.cfg',
+                                 sub { __PACKAGE__->parse_config(@_); },
+                                 sub { __PACKAGE__->write_config(@_); });
+
+PVE::JSONSchema::register_standard_option('pve-sdn-subnet-id', {
+    description => "The SDN subnet object identifier.",
+    type => 'string', format => 'pve-sdn-subnet-id',
+    type => 'string'
+});
+
+PVE::JSONSchema::register_format('pve-sdn-subnet-id', \&parse_sdn_subnet_id);
+sub parse_sdn_subnet_id {
+    my ($id, $noerr) = @_;
+
+    my $cidr = "";
+    if($id =~ /\//) {
+       $cidr = $id;
+    } else {
+       my ($zone, $ip, $mask) = split(/-/, $id);
+       $cidr = "$ip/$mask";
+    }
+
+    if (!(PVE::JSONSchema::pve_verify_cidrv4($cidr, 1) ||
+          PVE::JSONSchema::pve_verify_cidrv6($cidr, 1)))
+    {
+        return undef if $noerr;
+        die "value does not look like a valid CIDR network\n";
+    }
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+        subnet => get_standard_option('pve-sdn-subnet-id',
+            { completion => \&PVE::Network::SDN::Subnets::complete_sdn_subnet }),
+    },
+};
+
+sub type {
+    return 'subnet';
+}
+
+sub private {
+    return $defaultData;
+}
+
+sub properties {
+    return {
+        vnet => {
+            type => 'string',
+            description => "associated vnet",
+        },
+        gateway => {
+            type => 'string', format => 'ip',
+            description => "Subnet Gateway: Will be assign on vnet for layer3 zones",
+        },
+        snat => {
+            type => 'boolean',
+            description => "enable masquerade for this subnet if pve-firewall",
+        },
+#      #cloudinit, dhcp options
+#        routes => {
+#            type => 'string',
+#            description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
+#        },
+        dnszoneprefix => {
+            type => 'string', format => 'dns-name',
+            description => "dns domain zone prefix  ex: 'adm' -> <hostname>.adm.mydomain.com",
+        },
+    };
+}
+
+sub options {
+    return {
+       vnet => { optional => 0 },
+       gateway => { optional => 1 },
+#      routes => { optional => 1 },
+       snat => { optional => 1 },
+       dnszoneprefix => { optional => 1 },
+    };
+}
+
+sub on_update_hook {
+    my ($class, $zone, $subnetid, $subnet, $old_subnet) = @_;
+
+    my $cidr = $subnet->{cidr};
+    my $mask = $subnet->{mask};
+
+    my $subnet_matcher = subnet_matcher($cidr);
+
+    my $vnetid = $subnet->{vnet};
+    my $gateway = $subnet->{gateway};
+    my $ipam = $zone->{ipam};
+    my $dns = $zone->{dns};
+    my $dnszone = $zone->{dnszone};
+    my $reversedns = $zone->{reversedns};
+
+    my $old_gateway = $old_subnet->{gateway} if $old_subnet;
+    my $mac = undef;
+
+    if($vnetid) {
+       my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+       raise_param_exc({ vnet => "$vnetid don't exist"}) if !$vnet;
+       raise_param_exc({ vnet => "you can't add a subnet on a vlanaware vnet"}) if $vnet->{vlanaware};
+       $mac = $vnet->{mac};
+    }
+
+    my $pointopoint = 1 if Net::IP::ip_is_ipv4($gateway) && $mask == 32;
+
+    #for /32 pointopoint, we allow gateway outside the subnet
+    raise_param_exc({ gateway => "$gateway is not in subnet $cidr"}) if $gateway && !$subnet_matcher->($gateway) && !$pointopoint;
+
+
+    if ($ipam) {
+       PVE::Network::SDN::Subnets::add_subnet($zone, $subnetid, $subnet);
+
+       #don't register gateway for pointopoint
+       return if $pointopoint;
+
+       #delete gateway on removal
+       if (!defined($gateway) && $old_gateway) {
+           eval {
+               PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
+           };
+           warn if $@;
+       }
+        if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
+           my $hostname = "$vnetid-gw";
+           my $description = "gateway";
+           PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway, $hostname, $mac, $description, 1);
+       }
+
+       #delete old gateway after update
+       if($gateway && $old_gateway && $gateway ne $old_gateway) {
+           eval {
+               PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
+           };
+           warn if $@;
+       }
+    }
+}
+
+sub on_delete_hook {
+    my ($class, $subnetid, $subnet_cfg, $vnet_cfg) = @_;
+
+    return;
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
new file mode 100644 (file)
index 0000000..6bb42e5
--- /dev/null
@@ -0,0 +1,375 @@
+package PVE::Network::SDN::Subnets;
+
+use strict;
+use warnings;
+
+use Net::Subnet qw(subnet_matcher);
+use Net::IP;
+use NetAddr::IP qw(:lower);
+
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network::SDN::Dns;
+use PVE::Network::SDN::Ipams;
+
+use PVE::Network::SDN::SubnetPlugin;
+PVE::Network::SDN::SubnetPlugin->register();
+PVE::Network::SDN::SubnetPlugin->init();
+
+sub sdn_subnets_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn subnet ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn subnet '$id' does not exist\n" if (!$noerr && !$scfg);
+
+    if($scfg) {
+       my ($zone, $network, $mask) = split(/-/, $id);
+       $scfg->{cidr} = "$network/$mask";
+       $scfg->{zone} = $zone;
+       $scfg->{network} = $network;
+       $scfg->{mask} = $mask;
+    }
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/subnets.cfg");
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/subnets.cfg", $cfg);
+}
+
+sub sdn_subnets_ids {
+    my ($cfg) = @_;
+
+    return sort keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_subnet {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::Subnets::config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Subnets::sdn_subnets_ids($cfg) ];
+}
+
+sub get_subnet {
+    my ($subnetid, $running) = @_;
+
+    my $cfg = {};
+    if($running) {
+       my $cfg = PVE::Network::SDN::running_config();
+       $cfg = $cfg->{subnets};
+    } else {
+       $cfg = PVE::Network::SDN::Subnets::config();
+    }
+
+    my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
+    return $subnet;
+}
+
+sub find_ip_subnet {
+    my ($ip, $mask, $subnets) = @_;
+
+    my $subnet = undef;
+    my $subnetid = undef;
+
+    foreach my $id (sort keys %{$subnets}) {
+
+       next if $mask ne $subnets->{$id}->{mask};
+       my $cidr = $subnets->{$id}->{cidr};
+       my $subnet_matcher = subnet_matcher($cidr);
+       next if !$subnet_matcher->($ip);
+       $subnet = $subnets->{$id};
+       $subnetid = $id;
+       last;
+    }
+    die  "can't find any subnet for ip $ip" if !$subnet;
+
+    return ($subnetid, $subnet);
+}
+
+sub verify_dns_zone {
+    my ($zone, $dns) = @_;
+
+    return if !$zone || !$dns;
+
+    my $dns_cfg = PVE::Network::SDN::Dns::config();
+    my $plugin_config = $dns_cfg->{ids}->{$dns};
+    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+    $plugin->verify_zone($plugin_config, $zone);
+}
+
+sub get_reversedns_zone {
+    my ($subnetid, $subnet, $dns, $ip) = @_;
+
+    return if !$subnetid || !$dns || !$ip;
+
+    my $dns_cfg = PVE::Network::SDN::Dns::config();
+    my $plugin_config = $dns_cfg->{ids}->{$dns};
+    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+    $plugin->get_reversedns_zone($plugin_config, $subnetid, $subnet, $ip);
+}
+
+sub add_dns_record {
+    my ($zone, $dns, $hostname, $ip) = @_;
+    return if !$zone || !$dns || !$hostname || !$ip;
+
+    my $dns_cfg = PVE::Network::SDN::Dns::config();
+    my $plugin_config = $dns_cfg->{ids}->{$dns};
+    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+    $plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
+
+}
+
+sub add_dns_ptr_record {
+    my ($reversezone, $zone, $dns, $hostname, $ip) = @_;
+
+    return if !$zone || !$reversezone || !$dns || !$hostname || !$ip;
+
+    $hostname .= ".$zone";
+    my $dns_cfg = PVE::Network::SDN::Dns::config();
+    my $plugin_config = $dns_cfg->{ids}->{$dns};
+    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+    $plugin->add_ptr_record($plugin_config, $reversezone, $hostname, $ip);
+}
+
+sub del_dns_record {
+    my ($zone, $dns, $hostname, $ip) = @_;
+
+    return if !$zone || !$dns || !$hostname || !$ip;
+
+    my $dns_cfg = PVE::Network::SDN::Dns::config();
+    my $plugin_config = $dns_cfg->{ids}->{$dns};
+    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+    $plugin->del_a_record($plugin_config, $zone, $hostname, $ip);
+}
+
+sub del_dns_ptr_record {
+    my ($reversezone, $dns, $ip) = @_;
+
+    return if !$reversezone || !$dns || !$ip;
+
+    my $dns_cfg = PVE::Network::SDN::Dns::config();
+    my $plugin_config = $dns_cfg->{ids}->{$dns};
+    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+    $plugin->del_ptr_record($plugin_config, $reversezone, $ip);
+}
+
+sub add_subnet {
+    my ($zone, $subnetid, $subnet) = @_;
+
+    my $ipam = $zone->{ipam};
+    return if !$ipam;
+    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+    my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+    $plugin->add_subnet($plugin_config, $subnetid, $subnet);
+}
+
+sub del_subnet {
+    my ($zone, $subnetid, $subnet) = @_;
+
+    my $ipam = $zone->{ipam};
+    return if !$ipam;
+    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+    my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+    $plugin->del_subnet($plugin_config, $subnetid, $subnet);
+}
+
+sub next_free_ip {
+    my ($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns) = @_;
+
+    my $cidr = undef;
+    my $ip = undef;
+    $description = '' if !$description;
+
+    my $ipamid = $zone->{ipam};
+    my $dns = $zone->{dns};
+    my $dnszone = $zone->{dnszone};
+    my $reversedns = $zone->{reversedns};
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+    #verify dns zones before ipam
+    verify_dns_zone($dnszone, $dns) if !$skipdns;
+
+    if($ipamid) {
+       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});
+       eval {
+           $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $description);
+           ($ip, undef) = split(/\//, $cidr);
+       };
+       die $@ if $@;
+    }
+
+    eval {
+       my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
+
+       if(!$skipdns) {
+           #add dns
+           add_dns_record($dnszone, $dns, $hostname, $ip);
+           #add reverse dns
+           add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
+       }
+    };
+    if ($@) {
+       #rollback
+       my $err = $@;
+       eval {
+           PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname)
+       };
+       die $err;
+    }
+    return $cidr;
+}
+
+sub add_ip {
+    my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $skipdns) = @_;
+
+    return if !$subnet || !$ip; 
+
+    my $ipaddr = NetAddr::IP->new($ip);
+    $ip = $ipaddr->canon();
+
+    my $ipamid = $zone->{ipam};
+    my $dns = $zone->{dns};
+    my $dnszone = $zone->{dnszone};
+    my $reversedns = $zone->{reversedns};
+    my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+    #verify dns zones before ipam
+    if(!$skipdns) {
+       verify_dns_zone($dnszone, $dns);
+       verify_dns_zone($reversednszone, $reversedns);
+    }
+
+    if ($ipamid) {
+
+       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});
+
+       eval {
+           $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway);
+       };
+       die $@ if $@;
+    }
+
+    eval {
+       if(!$skipdns) {
+           #add dns
+           add_dns_record($dnszone, $dns, $hostname, $ip);
+           #add reverse dns
+           add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
+       }
+    };
+    if ($@) {
+       #rollback
+       my $err = $@;
+       eval {
+           PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname)
+       };
+       die $err;
+    }
+}
+
+sub update_ip {
+    my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns) = @_;
+
+    return if !$subnet || !$ip; 
+
+    my $ipaddr = NetAddr::IP->new($ip);
+    $ip = $ipaddr->canon();
+
+    my $ipamid = $zone->{ipam};
+    my $dns = $zone->{dns};
+    my $dnszone = $zone->{dnszone};
+    my $reversedns = $zone->{reversedns};
+    my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+    #verify dns zones before ipam
+    if(!$skipdns) {
+       verify_dns_zone($dnszone, $dns);
+       verify_dns_zone($reversednszone, $reversedns);
+    }
+
+    if ($ipamid) {
+       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});
+       eval {
+           $plugin->update_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description);
+       };
+       die $@ if $@;
+    }
+
+    return if $hostname eq $oldhostname;
+
+    eval {
+       if(!$skipdns) {
+           #add dns
+           del_dns_record($dnszone, $dns, $oldhostname, $ip);
+           add_dns_record($dnszone, $dns, $hostname, $ip);
+           #add reverse dns
+           del_dns_ptr_record($reversednszone, $reversedns, $ip);
+           add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
+       }
+    };
+}
+
+sub del_ip {
+    my ($zone, $subnetid, $subnet, $ip, $hostname, $skipdns) = @_;
+
+    return if !$subnet || !$ip;
+
+    my $ipaddr = NetAddr::IP->new($ip);
+    $ip = $ipaddr->canon();
+
+    my $ipamid = $zone->{ipam};
+    my $dns = $zone->{dns};
+    my $dnszone = $zone->{dnszone};
+    my $reversedns = $zone->{reversedns};
+    my $reversednszone = get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+    if(!$skipdns) {
+       verify_dns_zone($dnszone, $dns);
+       verify_dns_zone($reversednszone, $reversedns);
+    }
+
+    if ($ipamid) {
+       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});
+       $plugin->del_ip($plugin_config, $subnetid, $subnet, $ip);
+    }
+
+    eval {
+       if(!$skipdns) {
+           del_dns_record($dnszone, $dns, $hostname, $ip);
+           del_dns_ptr_record($reversednszone, $reversedns, $ip);
+       }
+    };
+    if ($@) {
+       warn $@;
+    }
+}
+
+1;
diff --git a/src/PVE/Network/SDN/VnetPlugin.pm b/src/PVE/Network/SDN/VnetPlugin.pm
new file mode 100644 (file)
index 0000000..062904c
--- /dev/null
@@ -0,0 +1,109 @@
+package PVE::Network::SDN::VnetPlugin;
+
+use strict;
+use warnings;
+
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::JSONSchema qw(get_standard_option);
+
+use PVE::SectionConfig;
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file('sdn/vnets.cfg',
+                                 sub { __PACKAGE__->parse_config(@_); },
+                                 sub { __PACKAGE__->write_config(@_); });
+
+PVE::JSONSchema::register_standard_option('pve-sdn-vnet-id', {
+    description => "The SDN vnet object identifier.",
+    type => 'string', format => 'pve-sdn-vnet-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-vnet-id', \&parse_sdn_vnet_id);
+sub parse_sdn_vnet_id {
+    my ($id, $noerr) = @_;
+
+    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
+        return undef if $noerr;
+        die "vnet ID '$id' contains illegal characters\n";
+    }
+    die "vnet ID '$id' can't be more length than 8 characters\n" if length($id) > 8;
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+        vnet => get_standard_option('pve-sdn-vnet-id',
+            { completion => \&PVE::Network::SDN::Vnets::complete_sdn_vnet }),
+    },
+};
+
+sub type {
+    return 'vnet';
+}
+
+sub private {
+    return $defaultData;
+}
+
+sub properties {
+    return {
+       zone => {
+            type => 'string',
+            description => "zone id",
+       },
+        type => {
+            description => "Type",
+            optional => 1,
+        },
+       tag => {
+            type => 'integer',
+            description => "vlan or vxlan id",
+       },
+       vlanaware => {
+           type => 'boolean',
+           description => 'Allow vm VLANs to pass through this vnet.',
+       },
+        alias => {
+            type => 'string',
+            description => "alias name of the vnet",
+            pattern => qr/[\(\)-_.\w\d\s]{0,256}/i,
+            maxLength => 256,
+           optional => 1,
+        },
+    };
+}
+
+sub options {
+    return {
+        zone => { optional => 0},
+        tag => { optional => 1},
+        alias => { optional => 1 },
+        vlanaware => { optional => 1 },
+    };
+}
+
+sub on_delete_hook {
+    my ($class, $vnetid, $vnet_cfg) = @_;
+
+    #verify if subnets are associated
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+    raise_param_exc({ vnet => "Can't delete vnet if subnets exists"}) if $subnets;
+}
+
+sub on_update_hook {
+    my ($class, $vnetid, $vnet_cfg) = @_;
+
+    my $vnet = $vnet_cfg->{ids}->{$vnetid};
+    my $tag = $vnet->{tag};
+    my $vlanaware = $vnet->{vlanaware};
+
+    #don't allow vlanaware change if subnets are defined
+    if($vnet->{vlanaware}) {
+       my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+       raise_param_exc({ vlanaware => "vlanaware vnet is not compatible with subnets"}) if $subnets;
+    }
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Vnets.pm b/src/PVE/Network/SDN/Vnets.pm
new file mode 100644 (file)
index 0000000..0b32c58
--- /dev/null
@@ -0,0 +1,163 @@
+package PVE::Network::SDN::Vnets;
+
+use strict;
+use warnings;
+
+use Net::IP;
+
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network::SDN;
+use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::Zones;
+
+use PVE::Network::SDN::VnetPlugin;
+PVE::Network::SDN::VnetPlugin->register();
+PVE::Network::SDN::VnetPlugin->init();
+
+sub sdn_vnets_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn vnet ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn vnet '$id' does not exist\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    return cfs_read_file("sdn/vnets.cfg");
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/vnets.cfg", $cfg);
+}
+
+sub sdn_vnets_ids {
+    my ($cfg) = @_;
+
+    return sort keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_vnet {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::Vnets::config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Vnets::sdn_vnet_ids($cfg) ];
+}
+
+sub get_vnet {
+    my ($vnetid, $running) = @_;
+
+    return if !$vnetid;
+
+    my $scfg = {};
+    if($running) {
+       my $cfg = PVE::Network::SDN::running_config();
+       $scfg = $cfg->{vnets};
+    } else {
+       $scfg = PVE::Network::SDN::Vnets::config();
+    }
+
+    my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($scfg, $vnetid, 1);
+
+    return $vnet;
+}
+
+sub get_subnets {
+    my ($vnetid) = @_;
+
+    return if !$vnetid;
+
+    my $subnets = undef;
+    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+    foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
+       my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
+       next if !$subnet->{vnet} || $subnet->{vnet} ne $vnetid;
+       $subnets->{$subnetid} = $subnet;
+    }
+    return $subnets;
+
+}
+
+sub get_subnet_from_vnet_cidr {
+    my ($vnetid, $cidr) = @_;
+
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+    my $zoneid = $vnet->{zone};
+    my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
+
+    my ($ip, $mask) = split(/\//, $cidr);
+    die "ip address is not in cidr format" if !$mask;
+
+    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets);
+
+    return ($zone, $subnetid, $subnet, $ip);
+}
+
+sub get_next_free_cidr {
+    my ($vnetid, $hostname, $mac, $description, $ipversion, $skipdns) = @_;
+
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+    my $zoneid = $vnet->{zone};
+    my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
+
+    return if !$zone->{ipam};
+
+    $ipversion = 4 if !$ipversion;
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
+    my $ip = undef;
+    my $subnetcount = 0;
+
+    foreach my $subnetid (sort keys %{$subnets}) {
+        my $subnet = $subnets->{$subnetid};
+       my $network = $subnet->{network};
+
+       next if $ipversion != Net::IP::ip_get_version($network);
+       $subnetcount++;
+
+       eval {
+           $ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns);
+       };
+       warn $@ if $@;
+       last if $ip;
+    }
+    die "can't find any free ip" if !$ip && $subnetcount > 0;
+
+    return $ip;
+}
+
+sub add_cidr {
+    my ($vnetid, $cidr, $hostname, $mac, $description, $skipdns) = @_;
+
+    return if !$vnetid;
+    
+    my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr);
+    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, undef, $skipdns);
+}
+
+sub update_cidr {
+    my ($vnetid, $cidr, $hostname, $oldhostname, $mac, $description, $skipdns) = @_;
+
+    return if !$vnetid;
+
+    my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr);
+    PVE::Network::SDN::Subnets::update_ip($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns);
+}
+
+sub del_cidr {
+    my ($vnetid, $cidr, $hostname, $skipdns) = @_;
+
+    return if !$vnetid;
+
+    my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr);
+    PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $skipdns);
+}
+
+
+
+1;
diff --git a/src/PVE/Network/SDN/Zones.pm b/src/PVE/Network/SDN/Zones.pm
new file mode 100644 (file)
index 0000000..f8e40b1
--- /dev/null
@@ -0,0 +1,357 @@
+package PVE::Network::SDN::Zones;
+
+use strict;
+use warnings;
+
+use JSON;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network;
+
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Zones::VlanPlugin;
+use PVE::Network::SDN::Zones::QinQPlugin;
+use PVE::Network::SDN::Zones::VxlanPlugin;
+use PVE::Network::SDN::Zones::EvpnPlugin;
+use PVE::Network::SDN::Zones::FaucetPlugin;
+use PVE::Network::SDN::Zones::SimplePlugin;
+use PVE::Network::SDN::Zones::Plugin;
+
+PVE::Network::SDN::Zones::VlanPlugin->register();
+PVE::Network::SDN::Zones::QinQPlugin->register();
+PVE::Network::SDN::Zones::VxlanPlugin->register();
+PVE::Network::SDN::Zones::EvpnPlugin->register();
+PVE::Network::SDN::Zones::FaucetPlugin->register();
+PVE::Network::SDN::Zones::SimplePlugin->register();
+PVE::Network::SDN::Zones::Plugin->init();
+
+my $local_network_sdn_file = "/etc/network/interfaces.d/sdn";
+
+sub sdn_zones_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn zone ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/zones.cfg");
+    return $config;
+}
+
+sub get_plugin_config {
+    my ($vnet) = @_;
+    my $zoneid = $vnet->{zone};
+    my $zone_cfg = PVE::Network::SDN::Zones::config();
+    return $zone_cfg->{ids}->{$zoneid};
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/zones.cfg", $cfg);
+}
+
+sub sdn_zones_ids {
+    my ($cfg) = @_;
+
+    return sort keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_zone {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::running_config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::sdn_zones_ids($cfg) ];
+}
+
+sub get_zone {
+    my ($zoneid, $running) = @_;
+
+    my $cfg = {};
+    if($running) {
+        my $cfg = PVE::Network::SDN::running_config();
+        $cfg = $cfg->{vnets};
+    } else {
+        $cfg = PVE::Network::SDN::Zones::config();
+    }
+
+    my $zone = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $zoneid, 1);
+
+    return $zone;
+}
+
+
+sub generate_etc_network_config {
+
+    my $cfg = PVE::Network::SDN::running_config();
+
+    my $version = $cfg->{version};
+    my $vnet_cfg = $cfg->{vnets};
+    my $zone_cfg = $cfg->{zones};
+    my $subnet_cfg = $cfg->{subnets};
+    my $controller_cfg = $cfg->{controllers};
+    return if !$vnet_cfg && !$zone_cfg;
+
+    my $interfaces_config = PVE::INotify::read_file('interfaces');
+
+    #generate configuration
+    my $config = {};
+    my $nodename = PVE::INotify::nodename();
+
+    for my $id (sort keys %{$vnet_cfg->{ids}}) {
+       my $vnet = $vnet_cfg->{ids}->{$id};
+       my $zone = $vnet->{zone};
+
+       if (!$zone) {
+           warn "can't generate vnet '$id': no zone assigned!\n";
+           next;
+       }
+
+       my $plugin_config = $zone_cfg->{ids}->{$zone};
+
+       if (!defined($plugin_config)) {
+           warn "can't generate vnet '$id': zone $zone don't exist\n";
+           next;
+       }
+
+       next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
+
+       my $controller;
+       if (my $controllerid = $plugin_config->{controller}) {
+           $controller = $controller_cfg->{ids}->{$controllerid};
+       }
+
+       my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+       eval {
+           $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config);
+       };
+       if (my $err = $@) {
+           warn "zone $zone : vnet $id : $err\n";
+           next;
+       }
+    }
+
+    my $raw_network_config = "\#version:$version\n";
+    foreach my $iface (sort keys %$config) {
+       $raw_network_config .= "\n";
+       $raw_network_config .= "auto $iface\n";
+       $raw_network_config .= "iface $iface\n";
+       foreach my $option (@{$config->{$iface}}) {
+           $raw_network_config .= "\t$option\n";
+       }
+    }
+
+    return $raw_network_config;
+}
+
+sub write_etc_network_config {
+    my ($rawconfig) = @_;
+
+    return if !$rawconfig;
+
+    my $writefh = IO::File->new($local_network_sdn_file,">");
+    print $writefh $rawconfig;
+    $writefh->close();
+}
+
+sub read_etc_network_config_version {
+    my $versionstr = PVE::Tools::file_read_firstline($local_network_sdn_file);
+
+    return if !defined($versionstr);
+
+    if ($versionstr =~ m/^\#version:(\d+)$/) {
+       return $1;
+    }
+}
+
+sub ifquery_check {
+
+    my $cmd = ['ifquery', '-a', '-c', '-o','json'];
+
+    my $result = '';
+    my $reader = sub { $result .= shift };
+
+    eval {
+       run_command($cmd, outfunc => $reader);
+    };
+
+    my $resultjson = decode_json($result);
+    my $interfaces = {};
+
+    foreach my $interface (@$resultjson) {
+       my $name = $interface->{name};
+       $interfaces->{$name} = {
+           status => $interface->{status},
+           config => $interface->{config},
+           config_status => $interface->{config_status},
+       };
+    }
+
+    return $interfaces;
+}
+
+my $warned_about_reload;
+
+sub status {
+
+    my $err_config = undef;
+
+    my $local_version = PVE::Network::SDN::Zones::read_etc_network_config_version();
+    my $cfg = PVE::Network::SDN::running_config();
+    my $sdn_version = $cfg->{version};
+
+    return if !$sdn_version;
+
+    if (!$local_version) {
+       $err_config = "local sdn network configuration is not yet generated, please reload";
+       if (!$warned_about_reload) {
+           $warned_about_reload = 1;
+           warn "$err_config\n";
+       }
+    } elsif ($local_version < $sdn_version) {
+       $err_config = "local sdn network configuration is too old, please reload";
+       if (!$warned_about_reload) {
+           $warned_about_reload = 1;
+           warn "$err_config\n";
+       }
+    } else {
+       $warned_about_reload = 0;
+    }
+
+    my $status = ifquery_check();
+
+    my $vnet_cfg = $cfg->{vnets};
+    my $zone_cfg = $cfg->{zones};
+    my $nodename = PVE::INotify::nodename();
+
+    my $vnet_status = {};
+    my $zone_status = {};
+
+    for my $id (sort keys %{$zone_cfg->{ids}}) {
+       next if defined($zone_cfg->{ids}->{$id}->{nodes}) && !$zone_cfg->{ids}->{$id}->{nodes}->{$nodename};
+       $zone_status->{$id}->{status} = $err_config ? 'pending' : 'available';
+    }
+
+    foreach my $id (sort keys %{$vnet_cfg->{ids}}) {
+       my $vnet = $vnet_cfg->{ids}->{$id};
+       my $zone = $vnet->{zone};
+       next if !defined($zone);
+
+       my $plugin_config = $zone_cfg->{ids}->{$zone};
+
+       if (!defined($plugin_config)) {
+           $vnet_status->{$id}->{status} = 'error';
+           $vnet_status->{$id}->{statusmsg} = "unknown zone '$zone' configured";
+           next;
+       }
+
+       next if defined($plugin_config->{nodes}) && !$plugin_config->{nodes}->{$nodename};
+
+       $vnet_status->{$id}->{zone} = $zone;
+       $vnet_status->{$id}->{status} = 'available';
+
+       if ($err_config) {
+           $vnet_status->{$id}->{status} = 'pending';
+           $vnet_status->{$id}->{statusmsg} = $err_config;
+           next;
+       }
+
+       my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+       my $err_msg = $plugin->status($plugin_config, $zone, $id, $vnet, $status);
+       if (@{$err_msg} > 0) {
+           $vnet_status->{$id}->{status} = 'error';
+           $vnet_status->{$id}->{statusmsg} = join(',', @{$err_msg});
+           $zone_status->{$id}->{status} = 'error';
+       }
+    }
+
+    return ($zone_status, $vnet_status);
+}
+
+sub tap_create {
+    my ($iface, $bridge) = @_;
+
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
+    if (!$vnet) { # fallback for classic bridge
+       PVE::Network::tap_create($iface, $bridge);
+       return;
+    }
+
+    my $plugin_config = get_plugin_config($vnet);
+    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+    $plugin->tap_create($plugin_config, $vnet, $iface, $bridge);
+}
+
+sub veth_create {
+    my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
+
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
+    if (!$vnet) { # fallback for classic bridge
+       PVE::Network::veth_create($veth, $vethpeer, $bridge, $hwaddr);
+       return;
+    }
+
+    my $plugin_config = get_plugin_config($vnet);
+    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+    $plugin->veth_create($plugin_config, $vnet, $veth, $vethpeer, $bridge, $hwaddr);
+}
+
+sub tap_plug {
+    my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
+
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
+    if (!$vnet) { # fallback for classic bridge
+       my $interfaces_config = PVE::INotify::read_file('interfaces');
+       my $opts = {};
+       $opts->{learning} = 0 if $interfaces_config->{ifaces}->{$bridge} && $interfaces_config->{ifaces}->{$bridge}->{'bridge-disable-mac-learning'};
+       PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate, $opts);
+       return;
+    }
+
+    my $plugin_config = get_plugin_config($vnet);
+    my $nodename = PVE::INotify::nodename();
+
+    die "vnet $bridge is not allowed on this node\n"
+       if $plugin_config->{nodes} && !defined($plugin_config->{nodes}->{$nodename});
+
+    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+    $plugin->tap_plug($plugin_config, $vnet, $tag, $iface, $bridge, $firewall, $trunks, $rate);
+}
+
+sub add_bridge_fdb {
+    my ($iface, $macaddr, $bridge, $firewall) = @_;
+
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
+    if (!$vnet) { # fallback for classic bridge
+       PVE::Network::add_bridge_fdb($iface, $macaddr, $firewall);
+       return;
+    }
+
+    my $plugin_config = get_plugin_config($vnet);
+    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+    PVE::Network::add_bridge_fdb($iface, $macaddr, $firewall) if $plugin_config->{'bridge-disable-mac-learning'};
+}
+
+sub del_bridge_fdb {
+    my ($iface, $macaddr, $bridge, $firewall) = @_;
+
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
+    if (!$vnet) { # fallback for classic bridge
+       PVE::Network::del_bridge_fdb($iface, $macaddr, $firewall);
+       return;
+    }
+
+    my $plugin_config = get_plugin_config($vnet);
+    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
+    PVE::Network::del_bridge_fdb($iface, $macaddr, $firewall) if $plugin_config->{'bridge-disable-mac-learning'};
+}
+
+1;
+
diff --git a/src/PVE/Network/SDN/Zones/EvpnPlugin.pm b/src/PVE/Network/SDN/Zones/EvpnPlugin.pm
new file mode 100644 (file)
index 0000000..a5a7539
--- /dev/null
@@ -0,0 +1,315 @@
+package PVE::Network::SDN::Zones::EvpnPlugin;
+
+use strict;
+use warnings;
+use PVE::Network::SDN::Zones::VxlanPlugin;
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Tools qw($IPV4RE);
+use PVE::INotify;
+use PVE::Cluster;
+use PVE::Tools;
+use Net::IP;
+
+use PVE::Network::SDN::Controllers::EvpnPlugin;
+
+use base('PVE::Network::SDN::Zones::VxlanPlugin');
+
+sub type {
+    return 'evpn';
+}
+
+PVE::JSONSchema::register_format('pve-sdn-bgp-rt', \&pve_verify_sdn_bgp_rt);
+sub pve_verify_sdn_bgp_rt {
+    my ($rt) = @_;
+
+    if ($rt =~ m/^(\d+):(\d+)$/) {
+       my $asn = $1;
+       my $id = $2;
+
+       if ($asn < 0 || $asn > 4294967295) {
+           die "value does not look like a valid bgp route-target\n";
+       }
+       if ($id < 0 || $id > 4294967295) {
+           die "value does not look like a valid bgp route-target\n";
+       }
+    } else {
+       die "value does not look like a valid bgp route-target\n";
+    }
+    return $rt;
+}
+
+sub properties {
+    return {
+       'vrf-vxlan' => {
+           type => 'integer',
+           description => "l3vni.",
+       },
+       'controller' => {
+           type => 'string',
+           description => "Frr router name",
+       },
+       'mac' => {
+           type => 'string',
+           description => "Anycast logical router mac address",
+           optional => 1, format => 'mac-addr'
+       },
+       'exitnodes' => get_standard_option('pve-node-list'),
+       'exitnodes-local-routing' => {
+           type => 'boolean',
+           description => "Allow exitnodes to connect to evpn guests",
+           optional => 1
+       },
+       'exitnodes-primary' => get_standard_option('pve-node', {
+           description => "Force traffic to this exitnode first."}),
+       'advertise-subnets' => {
+           type => 'boolean',
+           description => "Advertise evpn subnets if you have silent hosts",
+           optional => 1
+       },
+       'disable-arp-nd-suppression' => {
+           type => 'boolean',
+           description => "Disable ipv4 arp && ipv6 neighbour discovery suppression",
+           optional => 1
+       },
+       'rt-import' => {
+           type => 'string',
+           description => "Route-Target import",
+           optional => 1, format => 'pve-sdn-bgp-rt-list'
+        }
+    };
+}
+
+sub options {
+    return {
+       nodes => { optional => 1},
+       'vrf-vxlan' => { optional => 0 },
+       controller => { optional => 0 },
+       exitnodes => { optional => 1 },
+       'exitnodes-local-routing' => { optional => 1 },
+       'exitnodes-primary' => { optional => 1 },
+       'advertise-subnets' => { optional => 1 },
+       'disable-arp-nd-suppression' => { optional => 1 },
+       'rt-import' => { optional => 1 },
+       mtu => { optional => 1 },
+       mac => { optional => 1 },
+       dns => { optional => 1 },
+       reversedns => { optional => 1 },
+       dnszone => { optional => 1 },
+       ipam => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+sub generate_sdn_config {
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
+
+    my $tag = $vnet->{tag};
+    my $alias = $vnet->{alias};
+    my $mac = $plugin_config->{'mac'};
+
+    my $vrf_iface = "vrf_$zoneid";
+    my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
+    my $local_node = PVE::INotify::nodename();
+
+    die "missing vxlan tag" if !$tag;
+    die "missing controller" if !$controller;
+    warn "vlan-aware vnet can't be enabled with evpn plugin" if $vnet->{vlanaware};
+
+    my @peers = PVE::Tools::split_list($controller->{'peers'});
+    my $bgprouter = PVE::Network::SDN::Controllers::EvpnPlugin::find_bgp_controller($local_node, $controller_cfg);
+    my $loopback = $bgprouter->{loopback} if $bgprouter->{loopback};
+    my ($ifaceip, $iface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers, $loopback);
+    my $is_evpn_gateway = $plugin_config->{'exitnodes'}->{$local_node};
+    my $exitnodes_local_routing = $plugin_config->{'exitnodes-local-routing'};
+
+
+    my $mtu = 1450;
+    $mtu = $interfaces_config->{$iface}->{mtu} - 50 if $interfaces_config->{$iface}->{mtu};
+    $mtu = $plugin_config->{mtu} if $plugin_config->{mtu};
+
+    #vxlan interface
+    my $vxlan_iface = "vxlan_$vnetid";
+    my @iface_config = ();
+    push @iface_config, "vxlan-id $tag";
+    push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
+    push @iface_config, "bridge-learning off";
+    push @iface_config, "bridge-arp-nd-suppress on" if !$plugin_config->{'disable-arp-nd-suppression'};
+
+    push @iface_config, "mtu $mtu" if $mtu;
+    push(@{$config->{$vxlan_iface}}, @iface_config) if !$config->{$vxlan_iface};
+
+    #vnet bridge
+    @iface_config = ();
+
+    my $address = {};
+    my $ipv4 = undef;
+    my $ipv6 = undef;
+    my $enable_forward_v4 = undef;
+    my $enable_forward_v6 = undef;
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
+    foreach my $subnetid (sort keys %{$subnets}) {
+       my $subnet = $subnets->{$subnetid};
+       my $cidr = $subnet->{cidr};
+       my $mask = $subnet->{mask};
+
+       my $gateway = $subnet->{gateway};
+       if ($gateway) {
+           push @iface_config, "address $gateway/$mask" if !defined($address->{$gateway});
+           $address->{$gateway} = 1;
+       }
+
+        my $iptables = undef;
+        my $checkrouteip = undef;
+        my $ipversion = Net::IP::ip_is_ipv6($gateway) ? 6 : 4;
+
+       if ($ipversion == 6) {
+           $ipv6 = 1;
+           $iptables = "ip6tables";
+           $checkrouteip = '2001:4860:4860::8888';
+           $enable_forward_v6 = 1 if $gateway;
+       } else {
+           $ipv4 = 1;
+           $iptables = "iptables";
+           $checkrouteip = '8.8.8.8';
+           $enable_forward_v4 = 1 if $gateway;
+       }
+
+       if ($subnet->{snat}) {
+
+            #find outgoing interface
+            my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip);
+            if ($outip && $outiface && $is_evpn_gateway) {
+                #use snat, faster than masquerade
+                push @iface_config, "post-up $iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+                push @iface_config, "post-down $iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+                #add conntrack zone once on outgoing interface
+                push @iface_config, "post-up $iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
+                push @iface_config, "post-down $iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
+            }
+        }
+    }
+
+    push @iface_config, "hwaddress $mac" if $mac;
+    push @iface_config, "bridge_ports $vxlan_iface";
+    push @iface_config, "bridge_stp off";
+    push @iface_config, "bridge_fd 0";
+    push @iface_config, "mtu $mtu" if $mtu;
+    push @iface_config, "alias $alias" if $alias;
+    push @iface_config, "ip-forward on" if $enable_forward_v4;
+    push @iface_config, "ip6-forward on" if $enable_forward_v6;
+    push @iface_config, "arp-accept on" if $ipv4||$ipv6;
+    push @iface_config, "vrf $vrf_iface" if $vrf_iface;
+    push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
+
+    if ($vrf_iface) {
+       #vrf interface
+       @iface_config = ();
+       push @iface_config, "vrf-table auto";
+       if(!$is_evpn_gateway) {
+           push @iface_config, "post-up ip route add vrf $vrf_iface unreachable default metric 4278198272";
+       } else {
+           push @iface_config, "post-up ip route del vrf $vrf_iface unreachable default metric 4278198272";
+       }
+
+       push(@{$config->{$vrf_iface}}, @iface_config) if !$config->{$vrf_iface};
+
+       if ($vrfvxlan) {
+           #l3vni vxlan interface
+           my $iface_vrf_vxlan = "vrfvx_$zoneid";
+           @iface_config = ();
+           push @iface_config, "vxlan-id $vrfvxlan";
+           push @iface_config, "vxlan-local-tunnelip $ifaceip" if $ifaceip;
+           push @iface_config, "bridge-learning off";
+           push @iface_config, "bridge-arp-nd-suppress on" if !$plugin_config->{'disable-arp-nd-suppression'};
+           push @iface_config, "mtu $mtu" if $mtu;
+           push(@{$config->{$iface_vrf_vxlan}}, @iface_config) if !$config->{$iface_vrf_vxlan};
+
+           #l3vni bridge
+           my $brvrf = "vrfbr_$zoneid";
+           @iface_config = ();
+           push @iface_config, "bridge-ports $iface_vrf_vxlan";
+           push @iface_config, "bridge_stp off";
+           push @iface_config, "bridge_fd 0";
+           push @iface_config, "mtu $mtu" if $mtu;
+           push @iface_config, "vrf $vrf_iface";
+           push(@{$config->{$brvrf}}, @iface_config) if !$config->{$brvrf};
+       }
+
+       if ( $is_evpn_gateway && $exitnodes_local_routing ) {
+           #add a veth pair for local cross-vrf routing
+           my $iface_xvrf = "xvrf_$zoneid";
+           my $iface_xvrfp = "xvrfp_$zoneid";
+
+           @iface_config = ();
+           push @iface_config, "link-type veth";
+           push @iface_config, "address 10.255.255.1/30";
+           push @iface_config, "veth-peer-name $iface_xvrfp";
+           push @iface_config, "mtu ".($mtu+50) if $mtu;
+           push(@{$config->{$iface_xvrf}}, @iface_config) if !$config->{$iface_xvrf};
+
+           @iface_config = ();
+           push @iface_config, "link-type veth";
+           push @iface_config, "address 10.255.255.2/30";
+           push @iface_config, "veth-peer-name $iface_xvrf";
+           push @iface_config, "vrf $vrf_iface";
+           push @iface_config, "mtu ".($mtu+50) if $mtu;
+           push(@{$config->{$iface_xvrfp}}, @iface_config) if !$config->{$iface_xvrfp};
+       }
+    }
+    return $config;
+}
+
+sub on_update_hook {
+    my ($class, $zoneid, $zone_cfg, $controller_cfg) = @_;
+
+    # verify that controller exist
+    my $controller = $zone_cfg->{ids}->{$zoneid}->{controller};
+    if (!defined($controller_cfg->{ids}->{$controller})) {
+       die "controller $controller don't exist";
+    } else {
+       die "$controller is not a evpn controller type" if $controller_cfg->{ids}->{$controller}->{type} ne 'evpn';
+    }
+
+    #vrf-vxlan need to be defined
+
+    my $vrfvxlan = $zone_cfg->{ids}->{$zoneid}->{'vrf-vxlan'};
+    # verify that vrf-vxlan is not already declared in another zone
+    foreach my $id (keys %{$zone_cfg->{ids}}) {
+       next if $id eq $zoneid;
+       die "vrf-vxlan $vrfvxlan is already declared in $id"
+               if (defined($zone_cfg->{ids}->{$id}->{'vrf-vxlan'}) && $zone_cfg->{ids}->{$id}->{'vrf-vxlan'} eq $vrfvxlan);
+    }
+
+    if (!defined($zone_cfg->{ids}->{$zoneid}->{'mac'})) {
+       my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
+       $zone_cfg->{ids}->{$zoneid}->{'mac'} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
+    }
+}
+
+
+sub vnet_update_hook {
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
+
+    my $vnet = $vnet_cfg->{ids}->{$vnetid};
+    my $tag = $vnet->{tag};
+
+    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($tag);
+    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $tag > 16777216;
+
+    # verify that tag is not already defined globally (vxlan-id are unique)
+    foreach my $id (keys %{$vnet_cfg->{ids}}) {
+       next if $id eq $vnetid;
+       my $othervnet = $vnet_cfg->{ids}->{$id};
+       my $other_tag = $othervnet->{tag};
+       my $other_zoneid = $othervnet->{zone};
+       my $other_zone = $zone_cfg->{ids}->{$other_zoneid};
+       next if $other_zone->{type} ne 'vxlan' && $other_zone->{type} ne 'evpn';
+       raise_param_exc({ tag => "vxlan tag $tag already exist in vnet $id in zone $other_zoneid "}) if $other_tag && $tag eq $other_tag;
+    }
+}
+
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Zones/FaucetPlugin.pm b/src/PVE/Network/SDN/Zones/FaucetPlugin.pm
new file mode 100644 (file)
index 0000000..a237d17
--- /dev/null
@@ -0,0 +1,74 @@
+package PVE::Network::SDN::Zones::FaucetPlugin;
+
+use strict;
+use warnings;
+use PVE::Network::SDN::Zones::VlanPlugin;
+
+use base('PVE::Network::SDN::Zones::VlanPlugin');
+
+sub type {
+    return 'faucet';
+}
+
+sub properties {
+    return {
+        'dp-id' => {
+            type => 'integer',
+            description => 'Faucet dataplane id',
+        },
+    };
+}
+
+sub options {
+
+    return {
+       nodes => { optional => 1},
+       'dp-id' => { optional => 0 },
+#      'uplink-id' => { optional => 0 },
+       'controller' => { optional => 0 },
+       dns => { optional => 1 },
+       reversedns => { optional => 1 },
+       dnszone => { optional => 1 },
+       ipam => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+sub generate_sdn_config {
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $uplinks, $controller, $config) = @_;
+
+    my $mtu = $vnet->{mtu};
+    my $uplink = $plugin_config->{'uplink-id'};
+    my $dpid = $plugin_config->{'dp-id'};
+    my $dphex = printf("%x",$dpid);  #fixme :should be 16characters hex
+
+    my $iface = $uplinks->{$uplink}->{name};
+    $iface = "uplink${uplink}" if !$iface;
+
+    #tagged interface
+    my @iface_config = ();
+    push @iface_config, "ovs_type OVSPort";
+    push @iface_config, "ovs_bridge $zoneid";
+    push @iface_config, "ovs_mtu $mtu" if $mtu;
+    push(@{$config->{$iface}}, @iface_config) if !$config->{$iface};
+
+    #vnet bridge
+    @iface_config = ();
+    push @iface_config, "ovs_port $iface";
+    push @iface_config, "ovs_type OVSBridge";
+    push @iface_config, "ovs_mtu $mtu" if $mtu;
+
+    push @iface_config, "ovs_extra set bridge $zoneid other-config:datapath-id=$dphex";
+    push @iface_config, "ovs_extra set bridge $zoneid other-config:disable-in-band=true";
+    push @iface_config, "ovs_extra set bridge $zoneid fail_mode=secure";
+    push @iface_config, "ovs_extra set-controller $vnetid tcp:127.0.0.1:6653";
+
+    push(@{$config->{$zoneid}}, @iface_config) if !$config->{$zoneid};
+
+    return $config;
+}
+
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Zones/Makefile b/src/PVE/Network/SDN/Zones/Makefile
new file mode 100644 (file)
index 0000000..8454388
--- /dev/null
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm VlanPlugin.pm VxlanPlugin.pm FaucetPlugin.pm EvpnPlugin.pm QinQPlugin.pm SimplePlugin.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+       for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Zones/$$i; done
diff --git a/src/PVE/Network/SDN/Zones/Plugin.pm b/src/PVE/Network/SDN/Zones/Plugin.pm
new file mode 100644 (file)
index 0000000..2c707b3
--- /dev/null
@@ -0,0 +1,340 @@
+package PVE::Network::SDN::Zones::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(run_command);
+use PVE::JSONSchema;
+use PVE::Cluster;
+use PVE::Network;
+
+use PVE::JSONSchema qw(get_standard_option);
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file(
+    'sdn/zones.cfg',
+    sub { __PACKAGE__->parse_config(@_); },
+    sub { __PACKAGE__->write_config(@_); },
+);
+
+PVE::JSONSchema::register_standard_option('pve-sdn-zone-id', {
+    description => "The SDN zone object identifier.",
+    type => 'string', format => 'pve-sdn-zone-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-zone-id', \&parse_sdn_zone_id);
+sub parse_sdn_zone_id {
+    my ($id, $noerr) = @_;
+
+    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
+       return undef if $noerr;
+       die "zone ID '$id' contains illegal characters\n";
+    }
+    die "zone ID '$id' can't be more length than 8 characters\n" if length($id) > 8;
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+       type => {
+           description => "Plugin type.",
+           type => 'string', format => 'pve-configid',
+           type => 'string',
+       },
+       nodes => get_standard_option('pve-node-list', { optional => 1 }),
+       zone => get_standard_option('pve-sdn-zone-id', {
+           completion => \&PVE::Network::SDN::Zones::complete_sdn_zone,
+       }),
+       ipam => {
+           type => 'string',
+           description => "use a specific ipam",
+           optional => 1,
+       },
+    },
+};
+
+sub private {
+    return $defaultData;
+}
+
+sub parse_section_header {
+    my ($class, $line) = @_;
+
+    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
+        my ($type, $id) = (lc($1), $2);
+       my $errmsg = undef; # set if you want to skip whole section
+       eval { PVE::JSONSchema::pve_verify_configid($type); };
+       $errmsg = $@ if $@;
+       my $config = {}; # to return additional attributes
+       return ($type, $id, $errmsg, $config);
+    }
+    return undef;
+}
+
+sub decode_value {
+    my ($class, $type, $key, $value) = @_;
+
+    if ($key eq 'nodes' || $key eq 'exitnodes') {
+       my $res = {};
+
+       foreach my $node (PVE::Tools::split_list($value)) {
+           if (PVE::JSONSchema::pve_verify_node_name($node)) {
+               $res->{$node} = 1;
+           }
+       }
+
+       return $res;
+    }
+
+    return $value;
+}
+
+sub encode_value {
+    my ($class, $type, $key, $value) = @_;
+
+    if ($key eq 'nodes' || $key eq 'exitnodes') {
+       return join(',', keys(%$value));
+    }
+
+    return $value;
+}
+
+sub generate_sdn_config {
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub generate_controller_config {
+    my ($class, $plugin_config, $controller, $id, $uplinks, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub generate_controller_vnet_config {
+    my ($class, $plugin_config, $controller, $zoneid, $vnetid, $config) = @_;
+
+}
+
+sub write_controller_config {
+    my ($class, $plugin_config, $config) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub controller_reload {
+    my ($class) = @_;
+
+    die "please implement inside plugin";
+}
+
+sub on_delete_hook {
+    my ($class, $zoneid, $vnet_cfg) = @_;
+
+    # verify that no vnet are associated to this zone
+    foreach my $id (keys %{$vnet_cfg->{ids}}) {
+       my $vnet = $vnet_cfg->{ids}->{$id};
+       die "zone $zoneid is used by vnet $id"
+           if ($vnet->{type} eq 'vnet' && defined($vnet->{zone}) && $vnet->{zone} eq $zoneid);
+    }
+}
+
+sub on_update_hook {
+    my ($class, $zoneid, $zone_cfg, $controller_cfg) = @_;
+
+    # do nothing by default
+}
+
+sub vnet_update_hook {
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
+
+    # do nothing by default
+}
+
+#helpers
+sub parse_tag_number_or_range {
+    my ($str, $max, $tag) = @_;
+
+    my @elements = split(/,/, $str);
+    my $count = 0;
+    my $allowed = undef;
+
+    die "extraneous commas in list\n" if $str ne join(',', @elements);
+    foreach my $item (@elements) {
+       if ($item =~ m/^([0-9]+)-([0-9]+)$/) {
+           $count += 2;
+           my ($port1, $port2) = ($1, $2);
+           die "invalid port '$port1'\n" if $port1 > $max;
+           die "invalid port '$port2'\n" if $port2 > $max;
+           die "backwards range '$port1:$port2' not allowed, did you mean '$port2:$port1'?\n" if $port1 > $port2;
+
+           if ($tag && $tag >= $port1 && $tag <= $port2){
+               $allowed = 1;
+               last;
+           }
+
+       } elsif ($item =~ m/^([0-9]+)$/) {
+           $count += 1;
+           my $port = $1;
+           die "invalid port '$port'\n" if $port > $max;
+
+           if ($tag && $tag == $port){
+               $allowed = 1;
+               last;
+           }
+       }
+    }
+    die "tag $tag is not allowed" if $tag && !$allowed;
+
+    return (scalar(@elements) > 1);
+}
+
+sub status {
+    my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_;
+
+    my $err_msg = [];
+
+    # ifaces to check
+    my $ifaces = [ $vnetid ];
+
+    foreach my $iface (@{$ifaces}) {
+        if (!$status->{$iface}->{status}) {
+           push @$err_msg, "missing $iface";
+        } elsif ($status->{$iface}->{status} ne 'pass') {
+           push @$err_msg, "error $iface";
+        }
+    }
+    return $err_msg;
+}
+
+
+sub tap_create {
+    my ($class, $plugin_config, $vnet, $iface, $vnetid) = @_;
+
+    PVE::Network::tap_create($iface, $vnetid);
+}
+
+sub veth_create {
+    my ($class, $plugin_config, $vnet, $veth, $vethpeer, $vnetid, $hwaddr) = @_;
+
+    PVE::Network::veth_create($veth, $vethpeer, $vnetid, $hwaddr);
+}
+
+sub tap_plug {
+    my ($class, $plugin_config, $vnet, $tag, $iface, $vnetid, $firewall, $trunks, $rate) = @_;
+
+    my $vlan_aware = PVE::Tools::file_read_firstline("/sys/class/net/$vnetid/bridge/vlan_filtering");
+    die "vm vlans are not allowed on vnet $vnetid" if !$vlan_aware && ($tag || $trunks);
+
+    my $opts = {};
+    $opts->{learning} = 0 if $plugin_config->{'bridge-disable-mac-learning'};
+    PVE::Network::tap_plug($iface, $vnetid, $tag, $firewall, $trunks, $rate, $opts);
+}
+
+#helper
+
+sub get_uplink_iface {
+    my ($interfaces_config, $uplink) = @_;
+
+    my $iface = undef;
+    foreach my $id (keys %{$interfaces_config->{ifaces}}) {
+        my $interface = $interfaces_config->{ifaces}->{$id};
+        if (my $iface_uplink = $interface->{'uplink-id'}) {
+           next if $iface_uplink ne $uplink;
+            if($interface->{type} ne 'eth' && $interface->{type} ne 'bond') {
+                warn "uplink $uplink is not a physical or bond interface";
+                next;
+            }
+           $iface = $id;
+        }
+    }
+
+    #create a dummy uplink interface if no uplink found
+    if(!$iface) {
+        warn "can't find uplink $uplink in physical interface";
+        $iface = "uplink${uplink}";
+    }
+
+    return $iface;
+}
+
+sub get_local_route_ip {
+    my ($targetip) = @_;
+
+    my $ip = undef;
+    my $interface = undef;
+
+    run_command(['/sbin/ip', 'route', 'get', $targetip], outfunc => sub {
+        if ($_[0] =~ m/src ($PVE::Tools::IPRE)/) {
+            $ip = $1;
+        }
+        if ($_[0] =~ m/dev (\S+)/) {
+            $interface = $1;
+        }
+
+    });
+    return ($ip, $interface);
+}
+
+
+sub find_local_ip_interface_peers {
+    my ($peers, $iface) = @_;
+
+    my $network_config = PVE::INotify::read_file('interfaces');
+    my $ifaces = $network_config->{ifaces};
+    
+    #if iface is defined, return ip if exist (if not,try to find it on other ifaces)
+    if ($iface) {
+       my $ip = $ifaces->{$iface}->{address};
+       return ($ip,$iface) if $ip;
+    }
+
+    #is a local ip member of peers list ?
+    foreach my $address (@{$peers}) {
+       while (my $interface = each %$ifaces) {
+           my $ip = $ifaces->{$interface}->{address};
+           if ($ip && $ip eq $address) {
+               return ($ip, $interface);
+           }
+       }
+    }
+
+    #if peer is remote, find source with ip route
+    foreach my $address (@{$peers}) {
+       my ($ip, $interface) = get_local_route_ip($address);
+       return ($ip, $interface);
+    }
+}
+
+sub find_bridge {
+    my ($bridge) = @_;
+
+    die "can't find bridge $bridge" if !-d "/sys/class/net/$bridge";
+}
+
+sub is_vlanaware {
+    my ($bridge) = @_;
+
+    return PVE::Tools::file_read_firstline("/sys/class/net/$bridge/bridge/vlan_filtering");
+}
+
+sub is_ovs {
+    my ($bridge) = @_;
+
+    my $is_ovs = !-d "/sys/class/net/$bridge/brif";
+    return $is_ovs;    
+}
+
+sub get_bridge_ifaces {
+    my ($bridge) = @_;
+
+    my @bridge_ifaces = ();
+    my $dir = "/sys/class/net/$bridge/brif";
+    PVE::Tools::dir_glob_foreach($dir, '(((eth|bond)\d+|en[^.]+)(\.\d+)?)', sub {
+       push @bridge_ifaces, $_[0];
+    });
+
+    return @bridge_ifaces;
+}
+1;
diff --git a/src/PVE/Network/SDN/Zones/QinQPlugin.pm b/src/PVE/Network/SDN/Zones/QinQPlugin.pm
new file mode 100644 (file)
index 0000000..f4d12bc
--- /dev/null
@@ -0,0 +1,233 @@
+package PVE::Network::SDN::Zones::QinQPlugin;
+
+use strict;
+use warnings;
+
+use PVE::Exception qw(raise raise_param_exc);
+
+use PVE::Network::SDN::Zones::Plugin;
+
+use base('PVE::Network::SDN::Zones::Plugin');
+
+sub type {
+    return 'qinq';
+}
+
+sub properties {
+    return {
+       tag => {
+           type => 'integer',
+           minimum => 0,
+           description => "Service-VLAN Tag",
+       },
+       mtu => {
+           type => 'integer',
+           description => "MTU",
+           optional => 1,
+       },
+       'vlan-protocol' => {
+           type => 'string',
+           enum => ['802.1q', '802.1ad'],
+           default => '802.1q',
+           optional => 1,
+       }
+    };
+}
+
+sub options {
+    return {
+       nodes => { optional => 1},
+       'tag' => { optional => 0 },
+       'bridge' => { optional => 0 },
+        'bridge-disable-mac-learning' => { optional => 1 },
+       'mtu' => { optional => 1 },
+       'vlan-protocol' => { optional => 1 },
+       dns => { optional => 1 },
+       reversedns => { optional => 1 },
+       dnszone => { optional => 1 },
+       ipam => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+sub generate_sdn_config {
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
+
+    my ($bridge, $mtu, $stag) = $plugin_config->@{'bridge', 'mtu', 'tag'};
+    my $vlanprotocol = $plugin_config->{'vlan-protocol'};
+
+    PVE::Network::SDN::Zones::Plugin::find_bridge($bridge);
+
+    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
+    my $is_ovs = PVE::Network::SDN::Zones::Plugin::is_ovs($bridge);
+
+    my @iface_config = ();
+    my $zone_notag_uplink = "ln_${zoneid}";
+    my $zone_notag_uplinkpeer = "pr_${zoneid}";
+    my $zone = "z_${zoneid}";
+
+    my $vnet_bridge_ports = "";
+    if (my $ctag = $vnet->{tag}) {
+       $vnet_bridge_ports = "$zone.$ctag";
+    } else {
+       $vnet_bridge_ports = $zone_notag_uplinkpeer;
+    }
+
+    my $zone_bridge_ports = "";
+    if ($is_ovs) {
+        # ovs--->ovsintport(dot1q-tunnel tag)------->vlanawarebrige-----(tag)--->vnet
+
+       $vlanprotocol = "802.1q" if !$vlanprotocol;
+       my $svlan_iface = "sv_".$zoneid;
+
+       # ovs dot1q-tunnel port
+       @iface_config = ();
+       push @iface_config, "ovs_type OVSIntPort";
+       push @iface_config, "ovs_bridge $bridge";
+       push @iface_config, "ovs_mtu $mtu" if $mtu;
+       push @iface_config, "ovs_options vlan_mode=dot1q-tunnel tag=$stag other_config:qinq-ethtype=$vlanprotocol";
+       push(@{$config->{$svlan_iface}}, @iface_config) if !$config->{$svlan_iface};
+
+        # redefine main ovs bridge, ifupdown2 will merge ovs_ports
+       @{$config->{$bridge}}[0] = "ovs_ports" if !@{$config->{$bridge}}[0];
+       my @ovs_ports = split / / , @{$config->{$bridge}}[0];
+       @{$config->{$bridge}}[0] .= " $svlan_iface" if !grep( $_ eq $svlan_iface, @ovs_ports );
+
+       $zone_bridge_ports = $svlan_iface;
+
+    } elsif ($vlan_aware) {
+        # VLAN_aware_brige-(tag)----->vlanwarebridge-(tag)----->vnet
+
+       if ($vlanprotocol) {
+           @iface_config = ();
+           push @iface_config, "bridge-vlan-protocol $vlanprotocol";
+           push(@{$config->{$bridge}}, @iface_config) if !$config->{$bridge};
+       }
+
+       $zone_bridge_ports = "$bridge.$stag";
+
+    } else {
+       # eth--->eth.x(svlan)----->vlanwarebridge-(tag)----->vnet---->vnet
+
+       my @bridge_ifaces = PVE::Network::SDN::Zones::Plugin::get_bridge_ifaces($bridge);
+
+       for my $bridge_iface (@bridge_ifaces) {
+           # use named vlan interface to avoid too long names
+           my $svlan_iface = "sv_$zoneid";
+
+           # svlan
+           @iface_config = ();
+           push @iface_config, "vlan-raw-device $bridge_iface";
+           push @iface_config, "vlan-id $stag";
+           push @iface_config, "vlan-protocol $vlanprotocol" if $vlanprotocol;
+           push(@{$config->{$svlan_iface}}, @iface_config) if !$config->{$svlan_iface};
+
+           $zone_bridge_ports = $svlan_iface;
+           last;
+        }
+   }
+
+    # veth peer for notag vnet
+    @iface_config = ();
+    push @iface_config, "link-type veth";
+    push @iface_config, "veth-peer-name $zone_notag_uplinkpeer";
+    push(@{$config->{$zone_notag_uplink}}, @iface_config) if !$config->{$zone_notag_uplink};
+
+    @iface_config = ();
+    push @iface_config, "link-type veth";
+    push @iface_config, "veth-peer-name $zone_notag_uplink";
+    push(@{$config->{$zone_notag_uplinkpeer}}, @iface_config) if !$config->{$zone_notag_uplinkpeer};
+
+    # zone vlan aware bridge
+    @iface_config = ();
+    push @iface_config, "mtu $mtu" if $mtu;
+    push @iface_config, "bridge-stp off";
+    push @iface_config, "bridge-ports $zone_bridge_ports $zone_notag_uplink";
+    push @iface_config, "bridge-fd 0";
+    push @iface_config, "bridge-vlan-aware yes";
+    push @iface_config, "bridge-vids 2-4094";
+    push(@{$config->{$zone}}, @iface_config) if !$config->{$zone};
+
+    # vnet bridge
+    @iface_config = ();
+    push @iface_config, "bridge_ports $vnet_bridge_ports";
+    push @iface_config, "bridge_stp off";
+    push @iface_config, "bridge_fd 0";
+    if($vnet->{vlanaware}) {
+       push @iface_config, "bridge-vlan-aware yes";
+       push @iface_config, "bridge-vids 2-4094";
+    }
+    push @iface_config, "mtu $mtu" if $mtu;
+    push @iface_config, "alias $vnet->{alias}" if $vnet->{alias};
+    push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
+}
+
+sub status {
+    my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_;
+
+    my $bridge = $plugin_config->{bridge};
+    my $err_msg = [];
+
+    if (!-d "/sys/class/net/$bridge") {
+        push @$err_msg, "missing $bridge";
+        return $err_msg;
+    }
+
+    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
+
+    my $tag = $vnet->{tag};
+    my $vnet_uplink = "ln_".$vnetid;
+    my $vnet_uplinkpeer = "pr_".$vnetid;
+    my $zone_notag_uplink = "ln_".$zone;
+    my $zone_notag_uplinkpeer = "pr_".$zone;
+    my $zonebridge = "z_$zone";
+
+    # ifaces to check
+    my $ifaces = [ $vnetid, $bridge ];
+
+    push @$ifaces, $zonebridge;
+    push @$ifaces, $zone_notag_uplink;
+    push @$ifaces, $zone_notag_uplinkpeer;
+
+    if (!$vlan_aware) {
+       my $svlan_iface = "sv_$zone";
+       push @$ifaces, $svlan_iface;
+    }
+
+    foreach my $iface (@{$ifaces}) {
+       if (!$status->{$iface}->{status}) {
+           push @$err_msg, "missing $iface";
+        } elsif ($status->{$iface}->{status} ne 'pass') {
+           push @$err_msg, "error $iface";
+       }
+    }
+    return $err_msg;
+}
+
+sub vnet_update_hook {
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
+
+    my $vnet = $vnet_cfg->{ids}->{$vnetid};
+
+    my $tag = $vnet->{tag};
+    raise_param_exc({ tag => "VLAN tag maximal value is 4096" }) if $tag && $tag > 4096;
+
+    # verify that tag is not already defined in another vnet on same zone
+    for my $id (sort keys %{$vnet_cfg->{ids}}) {
+       next if $id eq $vnetid;
+       my $other_vnet = $vnet_cfg->{ids}->{$id};
+       next if $vnet->{zone} ne $other_vnet->{zone};
+       my $other_tag = $other_vnet->{tag};
+       if ($tag) {
+           raise_param_exc({ tag => "tag $tag already exist in zone $vnet->{zone} vnet $id"})
+               if $other_tag && $tag eq $other_tag;
+       } else {
+           raise_param_exc({ tag => "tag-less vnet already exists in zone $vnet->{zone} vnet $id"})
+               if !$other_tag;
+       }
+    }
+}
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Zones/SimplePlugin.pm b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
new file mode 100644 (file)
index 0000000..7757747
--- /dev/null
@@ -0,0 +1,159 @@
+package PVE::Network::SDN::Zones::SimplePlugin;
+
+use strict;
+use warnings;
+use PVE::Network::SDN::Zones::Plugin;
+use PVE::Exception qw(raise raise_param_exc);
+use PVE::Cluster;
+use PVE::Tools;
+
+use base('PVE::Network::SDN::Zones::Plugin');
+
+sub type {
+    return 'simple';
+}
+
+sub properties {
+    return {
+       dns => {
+           type => 'string',
+           description => "dns api server",
+       },
+       reversedns => {
+           type => 'string',
+           description => "reverse dns api server",
+       },
+       dnszone => {
+           type => 'string', format => 'dns-name',
+           description => "dns domain zone  ex: mydomain.com",
+       }
+    };
+}
+
+sub options {
+    return {
+       nodes => { optional => 1},
+       mtu => { optional => 1 },
+       dns => { optional => 1 },
+       reversedns => { optional => 1 },
+       dnszone => { optional => 1 },
+       ipam => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+sub generate_sdn_config {
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
+
+    return $config if$config->{$vnetid}; # nothing to do
+
+    my $mac = $vnet->{mac};
+    my $alias = $vnet->{alias};
+    my $mtu = $plugin_config->{mtu} if $plugin_config->{mtu};
+
+    # vnet bridge
+    my @iface_config = ();
+
+    my $address = {};
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
+
+    my $ipv4 = undef;
+    my $ipv6 = undef;
+    my $enable_forward_v4 = undef;
+    my $enable_forward_v6 = undef;
+
+    foreach my $subnetid (sort keys %{$subnets}) {
+       my $subnet = $subnets->{$subnetid};
+       my $cidr = $subnet->{cidr};
+       my $mask = $subnet->{mask};
+
+       my $gateway = $subnet->{gateway};
+       if ($gateway) {
+           push @iface_config, "address $gateway/$mask" if !defined($address->{$gateway});
+           $address->{$gateway} = 1;
+       }
+
+       my $iptables = undef;
+       my $checkrouteip = undef;
+       my $ipversion = Net::IP::ip_is_ipv6($gateway) ? 6 : 4;
+
+       if ( $ipversion == 6) {
+           $ipv6 = 1;
+           $iptables = "ip6tables";
+           $checkrouteip = '2001:4860:4860::8888';
+           $enable_forward_v6 = 1 if $gateway;
+       } else {
+           $ipv4 = 1;
+           $iptables = "iptables";
+           $checkrouteip = '8.8.8.8';
+           $enable_forward_v4 = 1 if $gateway;
+       }
+
+       #add route for /32 pointtopoint
+       push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32 && $ipversion == 4;
+       if ($subnet->{snat}) {
+           #find outgoing interface
+           my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip($checkrouteip);
+           if ($outip && $outiface) {
+               #use snat, faster than masquerade
+               push @iface_config, "post-up $iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+               push @iface_config, "post-down $iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+               #add conntrack zone once on outgoing interface
+               push @iface_config, "post-up $iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
+               push @iface_config, "post-down $iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
+           }
+       }
+    }
+
+    push @iface_config, "hwaddress $mac" if $mac;
+    push @iface_config, "bridge_ports none";
+    push @iface_config, "bridge_stp off";
+    push @iface_config, "bridge_fd 0";
+    if ($vnet->{vlanaware}) {
+        push @iface_config, "bridge-vlan-aware yes";
+        push @iface_config, "bridge-vids 2-4094";
+    }
+    push @iface_config, "mtu $mtu" if $mtu;
+    push @iface_config, "alias $alias" if $alias;
+    push @iface_config, "ip-forward on" if $enable_forward_v4;
+    push @iface_config, "ip6-forward on" if $enable_forward_v6;
+
+    push @{$config->{$vnetid}}, @iface_config;
+
+    return $config;
+}
+
+sub status {
+    my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_;
+
+    # ifaces to check
+    my $ifaces = [ $vnetid ];
+    my $err_msg = [];
+    foreach my $iface (@{$ifaces}) {
+       if (!$status->{$iface}->{status}) {
+           push @$err_msg, "missing $iface";
+       } elsif ($status->{$iface}->{status} ne 'pass') {
+           push @$err_msg, "error iface $iface";
+       }
+    }
+    return $err_msg;
+}
+
+
+sub vnet_update_hook {
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
+
+    my $vnet = $vnet_cfg->{ids}->{$vnetid};
+    my $tag = $vnet->{tag};
+
+    raise_param_exc({ tag => "vlan tag is not allowed on simple zone"}) if defined($tag);
+
+    if (!defined($vnet->{mac})) {
+        my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
+        $vnet->{mac} = PVE::Tools::random_ether_addr($dc->{mac_prefix});
+    }
+}
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Zones/VlanPlugin.pm b/src/PVE/Network/SDN/Zones/VlanPlugin.pm
new file mode 100644 (file)
index 0000000..0bb6b8a
--- /dev/null
@@ -0,0 +1,199 @@
+package PVE::Network::SDN::Zones::VlanPlugin;
+
+use strict;
+use warnings;
+use PVE::Network::SDN::Zones::Plugin;
+use PVE::Exception qw(raise raise_param_exc);
+
+use base('PVE::Network::SDN::Zones::Plugin');
+
+sub type {
+    return 'vlan';
+}
+
+PVE::JSONSchema::register_format('pve-sdn-vlanrange', \&pve_verify_sdn_vlanrange);
+sub pve_verify_sdn_vlanrange {
+   my ($vlanstr) = @_;
+
+   PVE::Network::SDN::Zones::Plugin::parse_tag_number_or_range($vlanstr, '4096');
+
+   return $vlanstr;
+}
+
+sub properties {
+    return {
+       'bridge' => {
+           type => 'string',
+       },
+       'bridge-disable-mac-learning' => {
+           type => 'boolean',
+            description => "Disable auto mac learning.",
+       }
+    };
+}
+
+sub options {
+
+    return {
+       nodes => { optional => 1},
+       'bridge' => { optional => 0 },
+       'bridge-disable-mac-learning' => { optional => 1 },
+       mtu => { optional => 1 },
+       dns => { optional => 1 },
+       reversedns => { optional => 1 },
+       dnszone => { optional => 1 },
+       ipam => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+sub generate_sdn_config {
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
+
+    my $bridge = $plugin_config->{bridge};
+    PVE::Network::SDN::Zones::Plugin::find_bridge($bridge);
+
+    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
+    my $is_ovs = PVE::Network::SDN::Zones::Plugin::is_ovs($bridge);
+
+    my $tag = $vnet->{tag};
+    my $alias = $vnet->{alias};
+    my $mtu = $plugin_config->{mtu};
+
+    my $vnet_uplink = "ln_".$vnetid;
+    my $vnet_uplinkpeer = "pr_".$vnetid;
+
+    my @iface_config = ();
+
+    if($is_ovs) {
+
+        # keep vmbrXvY for compatibility with existing network
+        # eth0----ovs vmbr0--(ovsintport tag)---->vnet---->vm
+
+       @iface_config = ();
+       push @iface_config, "ovs_type OVSIntPort";
+       push @iface_config, "ovs_bridge $bridge";
+       push @iface_config, "ovs_mtu $mtu" if $mtu;
+       if($vnet->{vlanaware}) {
+           push @iface_config, "ovs_options vlan_mode=dot1q-tunnel other_config:qinq-ethtype=802.1q tag=$tag";
+       } else {
+           push @iface_config, "ovs_options tag=$tag";
+       }
+       push(@{$config->{$vnet_uplink}}, @iface_config) if !$config->{$vnet_uplink};
+
+       #redefine main ovs bridge, ifupdown2 will merge ovs_ports
+       @iface_config = ();
+       push @iface_config, "ovs_ports $vnet_uplink";
+       push(@{$config->{$bridge}}, @iface_config);
+
+    } elsif ($vlan_aware) {
+        # eth0----vlanaware bridge vmbr0--(vmbr0.X tag)---->vnet---->vm
+       $vnet_uplink = "$bridge.$tag";
+    } else {
+
+        # keep vmbrXvY for compatibility with existing network
+        # eth0<---->eth0.X----vmbr0v10------vnet---->vm
+
+       my $bridgevlan = $bridge."v".$tag;
+
+       my @bridge_ifaces = PVE::Network::SDN::Zones::Plugin::get_bridge_ifaces($bridge);
+
+       my $bridge_ports = "";
+       foreach my $bridge_iface (@bridge_ifaces) {
+           $bridge_ports .= " $bridge_iface.$tag";
+       }
+
+       @iface_config = ();
+       push @iface_config, "link-type veth";
+       push @iface_config, "veth-peer-name $vnet_uplinkpeer";
+       push(@{$config->{$vnet_uplink}}, @iface_config) if !$config->{$vnet_uplink};
+
+       @iface_config = ();
+       push @iface_config, "link-type veth";
+       push @iface_config, "veth-peer-name $vnet_uplink";
+       push(@{$config->{$vnet_uplinkpeer}}, @iface_config) if !$config->{$vnet_uplinkpeer};
+
+       @iface_config = ();
+       push @iface_config, "bridge_ports $bridge_ports $vnet_uplinkpeer";
+       push @iface_config, "bridge_stp off";
+       push @iface_config, "bridge_fd 0";
+       push(@{$config->{$bridgevlan}}, @iface_config) if !$config->{$bridgevlan};
+    }
+
+    #vnet bridge
+    @iface_config = ();
+    push @iface_config, "bridge_ports $vnet_uplink";
+    push @iface_config, "bridge_stp off";
+    push @iface_config, "bridge_fd 0";
+    if($vnet->{vlanaware}) {
+        push @iface_config, "bridge-vlan-aware yes";
+        push @iface_config, "bridge-vids 2-4094";
+    }
+    push @iface_config, "mtu $mtu" if $mtu;
+    push @iface_config, "alias $alias" if $alias;
+    push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
+
+    return $config;
+}
+
+sub status {
+    my ($class, $plugin_config, $zone, $vnetid, $vnet, $status) = @_;
+
+    my $bridge = $plugin_config->{bridge};
+
+    my $err_msg = [];
+    if (!-d "/sys/class/net/$bridge") {
+        push @$err_msg, "missing $bridge";
+       return $err_msg;
+    }
+
+    my $vlan_aware = PVE::Network::SDN::Zones::Plugin::is_vlanaware($bridge);
+    my $is_ovs = PVE::Network::SDN::Zones::Plugin::is_ovs($bridge);
+
+    my $tag = $vnet->{tag};
+    my $vnet_uplink = "ln_".$vnetid;
+    my $vnet_uplinkpeer = "pr_".$vnetid;
+
+    # ifaces to check
+    my $ifaces = [ $vnetid, $bridge ];
+    if($is_ovs) {
+       push @$ifaces, $vnet_uplink;
+    } elsif (!$vlan_aware) {
+       my $bridgevlan = $bridge."v".$tag;
+       push @$ifaces, $bridgevlan;
+       push @$ifaces, $vnet_uplink;
+       push @$ifaces, $vnet_uplinkpeer;
+    }
+
+    foreach my $iface (@{$ifaces}) {
+       if (!$status->{$iface}->{status}) {
+           push @$err_msg, "missing $iface";
+        } elsif ($status->{$iface}->{status} ne 'pass') {
+           push @$err_msg, "error iface $iface";
+       }
+    }
+    return $err_msg;
+}
+
+sub vnet_update_hook {
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
+
+    my $vnet = $vnet_cfg->{ids}->{$vnetid};
+    my $tag = $vnet->{tag};
+
+    raise_param_exc({ tag => "missing vlan tag"}) if !defined($vnet->{tag});
+    raise_param_exc({ tag => "vlan tag max value is 4096"}) if $vnet->{tag} > 4096;
+
+    # verify that tag is not already defined in another vnet on same zone
+    foreach my $id (keys %{$vnet_cfg->{ids}}) {
+       next if $id eq $vnetid;
+       my $othervnet = $vnet_cfg->{ids}->{$id};
+       my $other_tag = $othervnet->{tag};
+       next if $vnet->{zone} ne $othervnet->{zone};
+       raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $other_tag && $tag eq $other_tag;
+    }
+}
+
+1;
+
+
diff --git a/src/PVE/Network/SDN/Zones/VxlanPlugin.pm b/src/PVE/Network/SDN/Zones/VxlanPlugin.pm
new file mode 100644 (file)
index 0000000..c523cf7
--- /dev/null
@@ -0,0 +1,118 @@
+package PVE::Network::SDN::Zones::VxlanPlugin;
+
+use strict;
+use warnings;
+use PVE::Network::SDN::Zones::Plugin;
+use PVE::Tools qw($IPV4RE);
+use PVE::INotify;
+use PVE::Network::SDN::Controllers::EvpnPlugin;
+use PVE::Exception qw(raise raise_param_exc);
+
+use base('PVE::Network::SDN::Zones::Plugin');
+
+PVE::JSONSchema::register_format('pve-sdn-vxlanrange', \&pve_verify_sdn_vxlanrange);
+sub pve_verify_sdn_vxlanrange {
+   my ($vxlanstr) = @_;
+
+   PVE::Network::SDN::Zones::Plugin::parse_tag_number_or_range($vxlanstr, '16777216');
+
+   return $vxlanstr;
+}
+
+sub type {
+    return 'vxlan';
+}
+
+sub properties {
+    return {
+        'peers' => {
+            description => "peers address list.",
+            type => 'string', format => 'ip-list'
+        },
+    };
+}
+
+sub options {
+    return {
+       nodes => { optional => 1},
+       peers => { optional => 0 },
+       mtu => { optional => 1 },
+       dns => { optional => 1 },
+       reversedns => { optional => 1 },
+       dnszone => { optional => 1 },
+       ipam => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+sub generate_sdn_config {
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $controller_cfg, $subnet_cfg, $interfaces_config, $config) = @_;
+
+    my $tag = $vnet->{tag};
+    my $alias = $vnet->{alias};
+    my $multicastaddress = $plugin_config->{'multicast-address'};
+    my @peers;
+    @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'};
+    my $vxlan_iface = "vxlan_$vnetid";
+
+    die "missing vxlan tag" if !$tag;
+
+    my ($ifaceip, $iface) = PVE::Network::SDN::Zones::Plugin::find_local_ip_interface_peers(\@peers);
+
+    my $mtu = 1450;
+    $mtu = $interfaces_config->{$iface}->{mtu} - 50 if $interfaces_config->{$iface}->{mtu};
+    $mtu = $plugin_config->{mtu} if $plugin_config->{mtu};
+
+    #vxlan interface
+    my @iface_config = ();
+    push @iface_config, "vxlan-id $tag";
+
+    for my $address (@peers) {
+       next if $address eq $ifaceip;
+       push @iface_config, "vxlan_remoteip $address";
+    }
+
+
+    push @iface_config, "mtu $mtu" if $mtu;
+    push(@{$config->{$vxlan_iface}}, @iface_config) if !$config->{$vxlan_iface};
+
+    #vnet bridge
+    @iface_config = ();
+    push @iface_config, "bridge_ports $vxlan_iface";
+    push @iface_config, "bridge_stp off";
+    push @iface_config, "bridge_fd 0";
+    if ($vnet->{vlanaware}) {
+       push @iface_config, "bridge-vlan-aware yes";
+       push @iface_config, "bridge-vids 2-4094";
+    }
+    push @iface_config, "mtu $mtu" if $mtu;
+    push @iface_config, "alias $alias" if $alias;
+    push(@{$config->{$vnetid}}, @iface_config) if !$config->{$vnetid};
+
+    return $config;
+}
+
+sub vnet_update_hook {
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
+
+    my $vnet = $vnet_cfg->{ids}->{$vnetid};
+    my $tag = $vnet->{tag};
+
+    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($tag);
+    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $tag > 16777216;
+
+    # verify that tag is not already defined globally (vxlan-id are unique)
+    for my $id (sort keys %{$vnet_cfg->{ids}}) {
+       next if $id eq $vnetid;
+       my $othervnet = $vnet_cfg->{ids}->{$id};
+       my $other_tag = $othervnet->{tag};
+       my $other_zoneid = $othervnet->{zone};
+       my $other_zone = $zone_cfg->{ids}->{$other_zoneid};
+       next if $other_zone->{type} ne 'vxlan' && $other_zone->{type} ne 'evpn';
+       raise_param_exc({ tag => "vxlan tag $tag already exist in vnet $id in zone $other_zoneid "}) if $other_tag && $tag eq $other_tag;
+    }
+}
+
+1;
+
+
diff --git a/src/test/Makefile b/src/test/Makefile
new file mode 100644 (file)
index 0000000..eb59d5f
--- /dev/null
@@ -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 (file)
index 0000000..6ee8ee6
--- /dev/null
@@ -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 (file)
index 0000000..250db43
--- /dev/null
@@ -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 (file)
index 0000000..e43003b
--- /dev/null
@@ -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 (file)
index 0000000..6052366
--- /dev/null
@@ -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 (file)
index 0000000..0e5539f
--- /dev/null
@@ -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 (file)
index 0000000..e432e7b
--- /dev/null
@@ -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 (file)
index 0000000..888d67f
--- /dev/null
@@ -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 (file)
index 0000000..bfeeab7
--- /dev/null
@@ -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 (file)
index 0000000..6923971
--- /dev/null
@@ -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 (file)
index 0000000..1d8049f
--- /dev/null
@@ -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 (file)
index 0000000..45d76c6
--- /dev/null
@@ -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 (file)
index 0000000..9b56abd
--- /dev/null
@@ -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 (file)
index 0000000..7c0cf45
--- /dev/null
@@ -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 (file)
index 0000000..9494c83
--- /dev/null
@@ -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 (file)
index 0000000..120485b
--- /dev/null
@@ -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 (file)
index 0000000..7948e78
--- /dev/null
@@ -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 (file)
index 0000000..b476875
--- /dev/null
@@ -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 (file)
index 0000000..2087729
--- /dev/null
@@ -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 (file)
index 0000000..ae876f2
--- /dev/null
@@ -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 (file)
index 0000000..ae876f2
--- /dev/null
@@ -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 (file)
index 0000000..7f80f4c
--- /dev/null
@@ -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 (file)
index 0000000..62ca823
--- /dev/null
@@ -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 (file)
index 0000000..3c41de4
--- /dev/null
@@ -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 (file)
index 0000000..bdadb71
--- /dev/null
@@ -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 (file)
index 0000000..a1202ad
--- /dev/null
@@ -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 (file)
index 0000000..a33be30
--- /dev/null
@@ -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 (file)
index 0000000..c31847b
--- /dev/null
@@ -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 (file)
index 0000000..50af460
--- /dev/null
@@ -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 (file)
index 0000000..7a91359
--- /dev/null
@@ -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 (file)
index 0000000..d72f94f
--- /dev/null
@@ -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 (file)
index 0000000..b10cc5a
--- /dev/null
@@ -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 (file)
index 0000000..72e83cb
--- /dev/null
@@ -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 (file)
index 0000000..349a34f
--- /dev/null
@@ -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 (file)
index 0000000..96c219b
--- /dev/null
@@ -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 (file)
index 0000000..a33be30
--- /dev/null
@@ -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 (file)
index 0000000..c774807
--- /dev/null
@@ -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 (executable)
index 0000000..87e011e
--- /dev/null
@@ -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 (executable)
index 0000000..27bd441
--- /dev/null
@@ -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 (executable)
index 0000000..f6564e1
--- /dev/null
@@ -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 (executable)
index 0000000..5aeb676
--- /dev/null
@@ -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 (executable)
index 0000000..12e017a
--- /dev/null
@@ -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 (file)
index 0000000..a33be30
--- /dev/null
@@ -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 (file)
index 0000000..72697d4
--- /dev/null
@@ -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 (file)
index 0000000..a33be30
--- /dev/null
@@ -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 (file)
index 0000000..618f234
--- /dev/null
@@ -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 (file)
index 0000000..a33be30
--- /dev/null
@@ -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 (file)
index 0000000..55107d6
--- /dev/null
@@ -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 (file)
index 0000000..ef3fa93
--- /dev/null
@@ -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 (file)
index 0000000..f5f36ad
--- /dev/null
@@ -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 (file)
index 0000000..ee11fd1
--- /dev/null
@@ -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 (file)
index 0000000..ef3fa93
--- /dev/null
@@ -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 (file)
index 0000000..f5f36ad
--- /dev/null
@@ -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 (file)
index 0000000..470c1ae
--- /dev/null
@@ -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 (file)
index 0000000..d3f2ce9
--- /dev/null
@@ -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 (file)
index 0000000..f5f36ad
--- /dev/null
@@ -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 (file)
index 0000000..231ca8a
--- /dev/null
@@ -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 (file)
index 0000000..82b06b4
--- /dev/null
@@ -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 (file)
index 0000000..9d1c64c
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..76f16a1
--- /dev/null
@@ -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 (file)
index 0000000..bd7830a
--- /dev/null
@@ -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 (file)
index 0000000..bbde906
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..199596b
--- /dev/null
@@ -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 (file)
index 0000000..ccc0b28
--- /dev/null
@@ -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 (file)
index 0000000..4cf13e0
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..6e9d116
--- /dev/null
@@ -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 (file)
index 0000000..548d532
--- /dev/null
@@ -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 (file)
index 0000000..4cf13e0
--- /dev/null
@@ -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 (file)
index 0000000..f6bc352
--- /dev/null
@@ -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 (file)
index 0000000..c8bc2e0
--- /dev/null
@@ -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 (file)
index 0000000..48830a3
--- /dev/null
@@ -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 (file)
index 0000000..5ab3084
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..fd81817
--- /dev/null
@@ -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 (file)
index 0000000..f671b63
--- /dev/null
@@ -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 (file)
index 0000000..301f5b3
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..f5f7ca1
--- /dev/null
@@ -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 (file)
index 0000000..e45b22c
--- /dev/null
@@ -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 (file)
index 0000000..5ab3084
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..bfeafc5
--- /dev/null
@@ -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 (file)
index 0000000..48830a3
--- /dev/null
@@ -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 (file)
index 0000000..47df77a
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..35cdf5d
--- /dev/null
@@ -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 (file)
index 0000000..bd7830a
--- /dev/null
@@ -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 (file)
index 0000000..9d1c64c
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..dd73b5c
--- /dev/null
@@ -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 (file)
index 0000000..bd7830a
--- /dev/null
@@ -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 (file)
index 0000000..7a5d741
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..4583818
--- /dev/null
@@ -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 (file)
index 0000000..bd7830a
--- /dev/null
@@ -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 (file)
index 0000000..378fa77
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..ab2273f
--- /dev/null
@@ -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 (file)
index 0000000..bd7830a
--- /dev/null
@@ -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 (file)
index 0000000..b2bdbfe
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..949e886
--- /dev/null
@@ -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 (file)
index 0000000..2d1ad44
--- /dev/null
@@ -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 (file)
index 0000000..4cf13e0
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..5a1d8a7
--- /dev/null
@@ -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 (file)
index 0000000..f4f28dd
--- /dev/null
@@ -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 (file)
index 0000000..9d1c64c
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..b62bb2e
--- /dev/null
@@ -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 (file)
index 0000000..58a0e23
--- /dev/null
@@ -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 (file)
index 0000000..68b6a88
--- /dev/null
@@ -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 (file)
index 0000000..6321603
--- /dev/null
@@ -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 (file)
index 0000000..cfa43a2
--- /dev/null
@@ -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 (file)
index 0000000..68b6a88
--- /dev/null
@@ -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 (file)
index 0000000..1f40369
--- /dev/null
@@ -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 (file)
index 0000000..c325dec
--- /dev/null
@@ -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 (file)
index 0000000..cfdfafe
--- /dev/null
@@ -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 (file)
index 0000000..6321603
--- /dev/null
@@ -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 (file)
index 0000000..cd87a3a
--- /dev/null
@@ -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 (file)
index 0000000..cfdfafe
--- /dev/null
@@ -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 (file)
index 0000000..2382f4d
--- /dev/null
@@ -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 (file)
index 0000000..28d215b
--- /dev/null
@@ -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 (file)
index 0000000..cfdfafe
--- /dev/null
@@ -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 (file)
index 0000000..c013176
--- /dev/null
@@ -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 (file)
index 0000000..0bc301b
--- /dev/null
@@ -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 (file)
index 0000000..cfdfafe
--- /dev/null
@@ -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 (file)
index 0000000..20a8a51
--- /dev/null
@@ -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 (file)
index 0000000..bde23d9
--- /dev/null
@@ -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 (file)
index 0000000..68b6a88
--- /dev/null
@@ -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 (file)
index 0000000..c013176
--- /dev/null
@@ -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 (file)
index 0000000..6b59164
--- /dev/null
@@ -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 (file)
index 0000000..68b6a88
--- /dev/null
@@ -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 (file)
index 0000000..20a8a51
--- /dev/null
@@ -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 (file)
index 0000000..d25b2a8
--- /dev/null
@@ -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 (file)
index 0000000..14d2f1e
--- /dev/null
@@ -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 (file)
index 0000000..6321603
--- /dev/null
@@ -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 (file)
index 0000000..5f47b28
--- /dev/null
@@ -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 (file)
index 0000000..14d2f1e
--- /dev/null
@@ -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 (file)
index 0000000..2382f4d
--- /dev/null
@@ -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 (file)
index 0000000..d69d38c
--- /dev/null
@@ -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 (file)
index 0000000..14d2f1e
--- /dev/null
@@ -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 (file)
index 0000000..c013176
--- /dev/null
@@ -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 (file)
index 0000000..aeefec9
--- /dev/null
@@ -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 (file)
index 0000000..14d2f1e
--- /dev/null
@@ -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 (file)
index 0000000..20a8a51
--- /dev/null
@@ -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 (file)
index 0000000..1e0c2c7
--- /dev/null
@@ -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 (file)
index 0000000..68b6a88
--- /dev/null
@@ -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 (file)
index 0000000..527dcba
--- /dev/null
@@ -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 (file)
index 0000000..f47ac53
--- /dev/null
@@ -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 (file)
index 0000000..5ab9635
--- /dev/null
@@ -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 (file)
index 0000000..30773ca
--- /dev/null
@@ -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 (file)
index 0000000..06e43ad
--- /dev/null
@@ -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 (file)
index 0000000..68b6a88
--- /dev/null
@@ -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 (file)
index 0000000..dd77b75
--- /dev/null
@@ -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 (file)
index 0000000..69d7986
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..5936d7d
--- /dev/null
@@ -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 (file)
index 0000000..34ed5db
--- /dev/null
@@ -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 (file)
index 0000000..68b6a88
--- /dev/null
@@ -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 (file)
index 0000000..b8ed848
--- /dev/null
@@ -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 (file)
index 0000000..1e0c2c7
--- /dev/null
@@ -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 (file)
index 0000000..68b6a88
--- /dev/null
@@ -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 (file)
index 0000000..dbd75c9
--- /dev/null
@@ -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 (file)
index 0000000..d3adc24
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..bc38527
--- /dev/null
@@ -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 (file)
index 0000000..f9e96d1
--- /dev/null
@@ -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 (file)
index 0000000..68b6a88
--- /dev/null
@@ -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 (file)
index 0000000..c6cfaaa
--- /dev/null
@@ -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 (file)
index 0000000..a318c7a
--- /dev/null
@@ -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 (file)
index 0000000..cfdfafe
--- /dev/null
@@ -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 (file)
index 0000000..c6cfaaa
--- /dev/null
@@ -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 (file)
index 0000000..ebf9d2e
--- /dev/null
@@ -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 (file)
index 0000000..64eec9e
--- /dev/null
@@ -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 (file)
index 0000000..67068f9
--- /dev/null
@@ -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 (file)
index 0000000..044559e
--- /dev/null
@@ -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 (file)
index 0000000..14d2f1e
--- /dev/null
@@ -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 (file)
index 0000000..c6cfaaa
--- /dev/null
@@ -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 (file)
index 0000000..7bb73b6
--- /dev/null
@@ -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 (file)
index 0000000..14d2f1e
--- /dev/null
@@ -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 (file)
index 0000000..9cfdb52
--- /dev/null
@@ -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 (file)
index 0000000..7b73c3e
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..f929304
--- /dev/null
@@ -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 (file)
index 0000000..55cdf9c
--- /dev/null
@@ -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 (file)
index 0000000..66bb826
--- /dev/null
@@ -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 (file)
index 0000000..23fb557
--- /dev/null
@@ -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" } },
+             },
+}
diff --git a/test/Makefile b/test/Makefile
deleted file mode 100644 (file)
index eedc4e0..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-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
diff --git a/test/debug/documentation.txt b/test/debug/documentation.txt
deleted file mode 100644 (file)
index 6ee8ee6..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-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/test/debug/generateconfig.pl b/test/debug/generateconfig.pl
deleted file mode 100644 (file)
index 250db43..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-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/test/debug/statuscheck.pl b/test/debug/statuscheck.pl
deleted file mode 100644 (file)
index e43003b..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/dns/powerdns/dns_config b/test/dns/powerdns/dns_config
deleted file mode 100644 (file)
index 6052366..0000000
+++ /dev/null
@@ -1,10 +0,0 @@
-{
-          'ids' => {
-                     'powerdns' => {
-                                    'url' => 'http://localhost:8881/api/v1/servers/localhost',
-                                    'type' => 'powerdns',
-                                    'key' => '1234',
-                                    'ttl' => '3600'
-                                  },
-                   },
-}
diff --git a/test/dns/powerdns/expected.add_a_multiple_record.ipv4 b/test/dns/powerdns/expected.add_a_multiple_record.ipv4
deleted file mode 100644 (file)
index 0e5539f..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-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/test/dns/powerdns/expected.add_a_multiple_record.ipv6 b/test/dns/powerdns/expected.add_a_multiple_record.ipv6
deleted file mode 100644 (file)
index e432e7b..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-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/test/dns/powerdns/expected.add_a_record.ipv4 b/test/dns/powerdns/expected.add_a_record.ipv4
deleted file mode 100644 (file)
index 888d67f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/dns/powerdns/expected.add_a_record.ipv6 b/test/dns/powerdns/expected.add_a_record.ipv6
deleted file mode 100644 (file)
index bfeeab7..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-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/test/dns/powerdns/expected.add_ptr_record.ipv4 b/test/dns/powerdns/expected.add_ptr_record.ipv4
deleted file mode 100644 (file)
index 6923971..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-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/test/dns/powerdns/expected.add_ptr_record.ipv6 b/test/dns/powerdns/expected.add_ptr_record.ipv6
deleted file mode 100644 (file)
index 1d8049f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/dns/powerdns/expected.del_a_multiple_record.ipv4 b/test/dns/powerdns/expected.del_a_multiple_record.ipv4
deleted file mode 100644 (file)
index 45d76c6..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-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/test/dns/powerdns/expected.del_a_multiple_record.ipv6 b/test/dns/powerdns/expected.del_a_multiple_record.ipv6
deleted file mode 100644 (file)
index 9b56abd..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/dns/powerdns/expected.del_a_record.ipv4 b/test/dns/powerdns/expected.del_a_record.ipv4
deleted file mode 100644 (file)
index 7c0cf45..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-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/test/dns/powerdns/expected.del_a_record.ipv6 b/test/dns/powerdns/expected.del_a_record.ipv6
deleted file mode 100644 (file)
index 9494c83..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/dns/powerdns/expected.del_ptr_record.ipv4 b/test/dns/powerdns/expected.del_ptr_record.ipv4
deleted file mode 100644 (file)
index 120485b..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/dns/powerdns/expected.del_ptr_record.ipv6 b/test/dns/powerdns/expected.del_ptr_record.ipv6
deleted file mode 100644 (file)
index 7948e78..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-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/test/dns/powerdns/expected.verify_zone b/test/dns/powerdns/expected.verify_zone
deleted file mode 100644 (file)
index b476875..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/dns/powerdns/sdn_config b/test/dns/powerdns/sdn_config
deleted file mode 100644 (file)
index 2087729..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  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/test/ipams/netbox/expected.add_ip b/test/ipams/netbox/expected.add_ip
deleted file mode 100644 (file)
index ae876f2..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/ipams/netbox/expected.add_ip_notgateway b/test/ipams/netbox/expected.add_ip_notgateway
deleted file mode 100644 (file)
index ae876f2..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/ipams/netbox/expected.add_next_freeip b/test/ipams/netbox/expected.add_next_freeip
deleted file mode 100644 (file)
index 7f80f4c..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/ipams/netbox/expected.add_subnet b/test/ipams/netbox/expected.add_subnet
deleted file mode 100644 (file)
index 62ca823..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/ipams/netbox/expected.del_ip b/test/ipams/netbox/expected.del_ip
deleted file mode 100644 (file)
index 3c41de4..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/ipams/netbox/expected.del_subnet b/test/ipams/netbox/expected.del_subnet
deleted file mode 100644 (file)
index bdadb71..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/ipams/netbox/expected.update_ip b/test/ipams/netbox/expected.update_ip
deleted file mode 100644 (file)
index a1202ad..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/ipams/netbox/ipam_config b/test/ipams/netbox/ipam_config
deleted file mode 100644 (file)
index a33be30..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-          '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/test/ipams/netbox/sdn_config b/test/ipams/netbox/sdn_config
deleted file mode 100644 (file)
index c31847b..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  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/test/ipams/phpipam/expected.add_ip b/test/ipams/phpipam/expected.add_ip
deleted file mode 100644 (file)
index 50af460..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/ipams/phpipam/expected.add_ip_notgateway b/test/ipams/phpipam/expected.add_ip_notgateway
deleted file mode 100644 (file)
index 7a91359..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/ipams/phpipam/expected.add_next_freeip b/test/ipams/phpipam/expected.add_next_freeip
deleted file mode 100644 (file)
index d72f94f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/ipams/phpipam/expected.add_subnet b/test/ipams/phpipam/expected.add_subnet
deleted file mode 100644 (file)
index b10cc5a..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/ipams/phpipam/expected.del_ip b/test/ipams/phpipam/expected.del_ip
deleted file mode 100644 (file)
index 72e83cb..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/ipams/phpipam/expected.del_subnet b/test/ipams/phpipam/expected.del_subnet
deleted file mode 100644 (file)
index 349a34f..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/ipams/phpipam/expected.update_ip b/test/ipams/phpipam/expected.update_ip
deleted file mode 100644 (file)
index 96c219b..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-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/test/ipams/phpipam/ipam_config b/test/ipams/phpipam/ipam_config
deleted file mode 100644 (file)
index a33be30..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-          '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/test/ipams/phpipam/sdn_config b/test/ipams/phpipam/sdn_config
deleted file mode 100644 (file)
index c774807..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  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/test/run_test_dns.pl b/test/run_test_dns.pl
deleted file mode 100755 (executable)
index 87e011e..0000000
+++ /dev/null
@@ -1,271 +0,0 @@
-#!/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/test/run_test_ipams.pl b/test/run_test_ipams.pl
deleted file mode 100755 (executable)
index 27bd441..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-#!/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/test/run_test_subnets.pl b/test/run_test_subnets.pl
deleted file mode 100755 (executable)
index f6564e1..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-#!/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/test/run_test_vnets.pl b/test/run_test_vnets.pl
deleted file mode 100755 (executable)
index 5aeb676..0000000
+++ /dev/null
@@ -1,355 +0,0 @@
-#!/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/test/run_test_zones.pl b/test/run_test_zones.pl
deleted file mode 100755 (executable)
index 12e017a..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-#!/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/test/subnets/ipv4/ipam_config b/test/subnets/ipv4/ipam_config
deleted file mode 100644 (file)
index a33be30..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-          '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/test/subnets/ipv4/sdn_config b/test/subnets/ipv4/sdn_config
deleted file mode 100644 (file)
index 72697d4..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  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/test/subnets/ipv6/ipam_config b/test/subnets/ipv6/ipam_config
deleted file mode 100644 (file)
index a33be30..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-          '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/test/subnets/ipv6/sdn_config b/test/subnets/ipv6/sdn_config
deleted file mode 100644 (file)
index 618f234..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  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/test/subnets/noipam/ipam_config b/test/subnets/noipam/ipam_config
deleted file mode 100644 (file)
index a33be30..0000000
+++ /dev/null
@@ -1,18 +0,0 @@
-{
-          '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/test/subnets/noipam/sdn_config b/test/subnets/noipam/sdn_config
deleted file mode 100644 (file)
index 55107d6..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-{
-  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/test/vnets/ipv4/ipam.db b/test/vnets/ipv4/ipam.db
deleted file mode 100644 (file)
index ef3fa93..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-   "zones" => {
-       "myzone" => {
-           "subnets" => {
-               "192.168.0.0/30" => {
-                   "ips" =>{
-                   }
-               },
-               "192.168.1.0/30" => {
-                   "ips" =>{
-                   }
-               },
-           }
-       }
-    }
-}
-
diff --git a/test/vnets/ipv4/ipam_config b/test/vnets/ipv4/ipam_config
deleted file mode 100644 (file)
index f5f36ad..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-          'ids' => {
-                     'pve' => {
-                                'type' => 'pve'
-                              },
-                   },
-}
diff --git a/test/vnets/ipv4/sdn_config b/test/vnets/ipv4/sdn_config
deleted file mode 100644 (file)
index ee11fd1..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  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/test/vnets/ipv4noipam/ipam.db b/test/vnets/ipv4noipam/ipam.db
deleted file mode 100644 (file)
index ef3fa93..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-{
-   "zones" => {
-       "myzone" => {
-           "subnets" => {
-               "192.168.0.0/30" => {
-                   "ips" =>{
-                   }
-               },
-               "192.168.1.0/30" => {
-                   "ips" =>{
-                   }
-               },
-           }
-       }
-    }
-}
-
diff --git a/test/vnets/ipv4noipam/ipam_config b/test/vnets/ipv4noipam/ipam_config
deleted file mode 100644 (file)
index f5f36ad..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-          'ids' => {
-                     'pve' => {
-                                'type' => 'pve'
-                              },
-                   },
-}
diff --git a/test/vnets/ipv4noipam/sdn_config b/test/vnets/ipv4noipam/sdn_config
deleted file mode 100644 (file)
index 470c1ae..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  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/test/vnets/ipv6/ipam.db b/test/vnets/ipv6/ipam.db
deleted file mode 100644 (file)
index d3f2ce9..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-   "zones" => {
-       "myzone" => {
-           "subnets" => {
-               "2001:db8:85a3::8a2e:370:7334/127" => {
-                   "ips" =>{
-                   }
-               },
-               "2001:db8:85a3::8a2e:371:7334/127" => {
-                   "ips" =>{
-                   }
-               },
-           }
-       }
-    }
-}
diff --git a/test/vnets/ipv6/ipam_config b/test/vnets/ipv6/ipam_config
deleted file mode 100644 (file)
index f5f36ad..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-{
-          'ids' => {
-                     'pve' => {
-                                'type' => 'pve'
-                              },
-                   },
-}
diff --git a/test/vnets/ipv6/sdn_config b/test/vnets/ipv6/sdn_config
deleted file mode 100644 (file)
index 231ca8a..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  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/test/zones/evpn/advertise_subnets/expected_controller_config b/test/zones/evpn/advertise_subnets/expected_controller_config
deleted file mode 100644 (file)
index 82b06b4..0000000
+++ /dev/null
@@ -1,54 +0,0 @@
-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/test/zones/evpn/advertise_subnets/expected_sdn_interfaces b/test/zones/evpn/advertise_subnets/expected_sdn_interfaces
deleted file mode 100644 (file)
index 9d1c64c..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#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/test/zones/evpn/advertise_subnets/interfaces b/test/zones/evpn/advertise_subnets/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/advertise_subnets/sdn_config b/test/zones/evpn/advertise_subnets/sdn_config
deleted file mode 100644 (file)
index 76f16a1..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  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/test/zones/evpn/disable_arp_nd_suppression/expected_controller_config b/test/zones/evpn/disable_arp_nd_suppression/expected_controller_config
deleted file mode 100644 (file)
index bd7830a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-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/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces b/test/zones/evpn/disable_arp_nd_suppression/expected_sdn_interfaces
deleted file mode 100644 (file)
index bbde906..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#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/test/zones/evpn/disable_arp_nd_suppression/interfaces b/test/zones/evpn/disable_arp_nd_suppression/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/disable_arp_nd_suppression/sdn_config b/test/zones/evpn/disable_arp_nd_suppression/sdn_config
deleted file mode 100644 (file)
index 199596b..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  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/test/zones/evpn/ebgp/expected_controller_config b/test/zones/evpn/ebgp/expected_controller_config
deleted file mode 100644 (file)
index ccc0b28..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-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/test/zones/evpn/ebgp/expected_sdn_interfaces b/test/zones/evpn/ebgp/expected_sdn_interfaces
deleted file mode 100644 (file)
index 4cf13e0..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#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/test/zones/evpn/ebgp/interfaces b/test/zones/evpn/ebgp/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/ebgp/sdn_config b/test/zones/evpn/ebgp/sdn_config
deleted file mode 100644 (file)
index 6e9d116..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-{
-    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/test/zones/evpn/ebgp_loopback/expected_controller_config b/test/zones/evpn/ebgp_loopback/expected_controller_config
deleted file mode 100644 (file)
index 548d532..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-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/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces b/test/zones/evpn/ebgp_loopback/expected_sdn_interfaces
deleted file mode 100644 (file)
index 4cf13e0..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#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/test/zones/evpn/ebgp_loopback/interfaces b/test/zones/evpn/ebgp_loopback/interfaces
deleted file mode 100644 (file)
index f6bc352..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-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/test/zones/evpn/ebgp_loopback/sdn_config b/test/zones/evpn/ebgp_loopback/sdn_config
deleted file mode 100644 (file)
index c8bc2e0..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-{
-  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/test/zones/evpn/exitnode/expected_controller_config b/test/zones/evpn/exitnode/expected_controller_config
deleted file mode 100644 (file)
index 48830a3..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-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/test/zones/evpn/exitnode/expected_sdn_interfaces b/test/zones/evpn/exitnode/expected_sdn_interfaces
deleted file mode 100644 (file)
index 5ab3084..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#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/test/zones/evpn/exitnode/interfaces b/test/zones/evpn/exitnode/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/exitnode/sdn_config b/test/zones/evpn/exitnode/sdn_config
deleted file mode 100644 (file)
index fd81817..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  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/test/zones/evpn/exitnode_local_routing/expected_controller_config b/test/zones/evpn/exitnode_local_routing/expected_controller_config
deleted file mode 100644 (file)
index f671b63..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-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/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces b/test/zones/evpn/exitnode_local_routing/expected_sdn_interfaces
deleted file mode 100644 (file)
index 301f5b3..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-#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/test/zones/evpn/exitnode_local_routing/interfaces b/test/zones/evpn/exitnode_local_routing/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/exitnode_local_routing/sdn_config b/test/zones/evpn/exitnode_local_routing/sdn_config
deleted file mode 100644 (file)
index f5f7ca1..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  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/test/zones/evpn/exitnode_primary/expected_controller_config b/test/zones/evpn/exitnode_primary/expected_controller_config
deleted file mode 100644 (file)
index e45b22c..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-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/test/zones/evpn/exitnode_primary/expected_sdn_interfaces b/test/zones/evpn/exitnode_primary/expected_sdn_interfaces
deleted file mode 100644 (file)
index 5ab3084..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#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/test/zones/evpn/exitnode_primary/interfaces b/test/zones/evpn/exitnode_primary/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/exitnode_primary/sdn_config b/test/zones/evpn/exitnode_primary/sdn_config
deleted file mode 100644 (file)
index bfeafc5..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  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/test/zones/evpn/exitnode_snat/expected_controller_config b/test/zones/evpn/exitnode_snat/expected_controller_config
deleted file mode 100644 (file)
index 48830a3..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-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/test/zones/evpn/exitnode_snat/expected_sdn_interfaces b/test/zones/evpn/exitnode_snat/expected_sdn_interfaces
deleted file mode 100644 (file)
index 47df77a..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-#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/test/zones/evpn/exitnode_snat/interfaces b/test/zones/evpn/exitnode_snat/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/exitnode_snat/sdn_config b/test/zones/evpn/exitnode_snat/sdn_config
deleted file mode 100644 (file)
index 35cdf5d..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-{
-  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/test/zones/evpn/ipv4/expected_controller_config b/test/zones/evpn/ipv4/expected_controller_config
deleted file mode 100644 (file)
index bd7830a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-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/test/zones/evpn/ipv4/expected_sdn_interfaces b/test/zones/evpn/ipv4/expected_sdn_interfaces
deleted file mode 100644 (file)
index 9d1c64c..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#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/test/zones/evpn/ipv4/interfaces b/test/zones/evpn/ipv4/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/ipv4/sdn_config b/test/zones/evpn/ipv4/sdn_config
deleted file mode 100644 (file)
index dd73b5c..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  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/test/zones/evpn/ipv4ipv6/expected_controller_config b/test/zones/evpn/ipv4ipv6/expected_controller_config
deleted file mode 100644 (file)
index bd7830a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-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/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces b/test/zones/evpn/ipv4ipv6/expected_sdn_interfaces
deleted file mode 100644 (file)
index 7a5d741..0000000
+++ /dev/null
@@ -1,44 +0,0 @@
-#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/test/zones/evpn/ipv4ipv6/interfaces b/test/zones/evpn/ipv4ipv6/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/ipv4ipv6/sdn_config b/test/zones/evpn/ipv4ipv6/sdn_config
deleted file mode 100644 (file)
index 4583818..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-{
-  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/test/zones/evpn/ipv4ipv6nogateway/expected_controller_config b/test/zones/evpn/ipv4ipv6nogateway/expected_controller_config
deleted file mode 100644 (file)
index bd7830a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-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/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces b/test/zones/evpn/ipv4ipv6nogateway/expected_sdn_interfaces
deleted file mode 100644 (file)
index 378fa77..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#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/test/zones/evpn/ipv4ipv6nogateway/interfaces b/test/zones/evpn/ipv4ipv6nogateway/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/ipv4ipv6nogateway/sdn_config b/test/zones/evpn/ipv4ipv6nogateway/sdn_config
deleted file mode 100644 (file)
index ab2273f..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-{
-  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/test/zones/evpn/ipv6/expected_controller_config b/test/zones/evpn/ipv6/expected_controller_config
deleted file mode 100644 (file)
index bd7830a..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-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/test/zones/evpn/ipv6/expected_sdn_interfaces b/test/zones/evpn/ipv6/expected_sdn_interfaces
deleted file mode 100644 (file)
index b2bdbfe..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#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/test/zones/evpn/ipv6/interfaces b/test/zones/evpn/ipv6/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/ipv6/sdn_config b/test/zones/evpn/ipv6/sdn_config
deleted file mode 100644 (file)
index 949e886..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  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/test/zones/evpn/multipath_relax/expected_controller_config b/test/zones/evpn/multipath_relax/expected_controller_config
deleted file mode 100644 (file)
index 2d1ad44..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-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/test/zones/evpn/multipath_relax/expected_sdn_interfaces b/test/zones/evpn/multipath_relax/expected_sdn_interfaces
deleted file mode 100644 (file)
index 4cf13e0..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-#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/test/zones/evpn/multipath_relax/interfaces b/test/zones/evpn/multipath_relax/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/multipath_relax/sdn_config b/test/zones/evpn/multipath_relax/sdn_config
deleted file mode 100644 (file)
index 5a1d8a7..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-{
-    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/test/zones/evpn/rt_import/expected_controller_config b/test/zones/evpn/rt_import/expected_controller_config
deleted file mode 100644 (file)
index f4f28dd..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-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/test/zones/evpn/rt_import/expected_sdn_interfaces b/test/zones/evpn/rt_import/expected_sdn_interfaces
deleted file mode 100644 (file)
index 9d1c64c..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-#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/test/zones/evpn/rt_import/interfaces b/test/zones/evpn/rt_import/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/evpn/rt_import/sdn_config b/test/zones/evpn/rt_import/sdn_config
deleted file mode 100644 (file)
index b62bb2e..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-  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/test/zones/qinq/bridge/expected_sdn_interfaces b/test/zones/qinq/bridge/expected_sdn_interfaces
deleted file mode 100644 (file)
index 58a0e23..0000000
+++ /dev/null
@@ -1,65 +0,0 @@
-#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/test/zones/qinq/bridge/interfaces b/test/zones/qinq/bridge/interfaces
deleted file mode 100644 (file)
index 68b6a88..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-auto vmbr0
-iface vmbr0 inet manual
-        bridge-ports eth0
-        bridge-stp off
-        bridge-fd 0
diff --git a/test/zones/qinq/bridge/sdn_config b/test/zones/qinq/bridge/sdn_config
deleted file mode 100644 (file)
index 6321603..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  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/test/zones/qinq/bridge_notagvnet/expected_sdn_interfaces b/test/zones/qinq/bridge_notagvnet/expected_sdn_interfaces
deleted file mode 100644 (file)
index cfa43a2..0000000
+++ /dev/null
@@ -1,36 +0,0 @@
-#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/test/zones/qinq/bridge_notagvnet/interfaces b/test/zones/qinq/bridge_notagvnet/interfaces
deleted file mode 100644 (file)
index 68b6a88..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-auto vmbr0
-iface vmbr0 inet manual
-        bridge-ports eth0
-        bridge-stp off
-        bridge-fd 0
diff --git a/test/zones/qinq/bridge_notagvnet/sdn_config b/test/zones/qinq/bridge_notagvnet/sdn_config
deleted file mode 100644 (file)
index 1f40369..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-{
-    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/test/zones/qinq/bridge_vlanaware/expected_sdn_interfaces b/test/zones/qinq/bridge_vlanaware/expected_sdn_interfaces
deleted file mode 100644 (file)
index c325dec..0000000
+++ /dev/null
@@ -1,55 +0,0 @@
-#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/test/zones/qinq/bridge_vlanaware/interfaces b/test/zones/qinq/bridge_vlanaware/interfaces
deleted file mode 100644 (file)
index cfdfafe..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/qinq/bridge_vlanaware/sdn_config b/test/zones/qinq/bridge_vlanaware/sdn_config
deleted file mode 100644 (file)
index 6321603..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  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/test/zones/qinq/bridge_vlanaware_notagvnet/expected_sdn_interfaces b/test/zones/qinq/bridge_vlanaware_notagvnet/expected_sdn_interfaces
deleted file mode 100644 (file)
index cd87a3a..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#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/test/zones/qinq/bridge_vlanaware_notagvnet/interfaces b/test/zones/qinq/bridge_vlanaware_notagvnet/interfaces
deleted file mode 100644 (file)
index cfdfafe..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/qinq/bridge_vlanaware_notagvnet/sdn_config b/test/zones/qinq/bridge_vlanaware_notagvnet/sdn_config
deleted file mode 100644 (file)
index 2382f4d..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/qinq/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces b/test/zones/qinq/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces
deleted file mode 100644 (file)
index 28d215b..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-#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/test/zones/qinq/bridge_vlanaware_vlanawarevnet/interfaces b/test/zones/qinq/bridge_vlanaware_vlanawarevnet/interfaces
deleted file mode 100644 (file)
index cfdfafe..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/qinq/bridge_vlanaware_vlanawarevnet/sdn_config b/test/zones/qinq/bridge_vlanaware_vlanawarevnet/sdn_config
deleted file mode 100644 (file)
index c013176..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/qinq/bridge_vlanaware_vlanprotocol/expected_sdn_interfaces b/test/zones/qinq/bridge_vlanaware_vlanprotocol/expected_sdn_interfaces
deleted file mode 100644 (file)
index 0bc301b..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#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/test/zones/qinq/bridge_vlanaware_vlanprotocol/interfaces b/test/zones/qinq/bridge_vlanaware_vlanprotocol/interfaces
deleted file mode 100644 (file)
index cfdfafe..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/qinq/bridge_vlanaware_vlanprotocol/sdn_config b/test/zones/qinq/bridge_vlanaware_vlanprotocol/sdn_config
deleted file mode 100644 (file)
index 20a8a51..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/qinq/bridge_vlanawarevnet/expected_sdn_interfaces b/test/zones/qinq/bridge_vlanawarevnet/expected_sdn_interfaces
deleted file mode 100644 (file)
index bde23d9..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-#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/test/zones/qinq/bridge_vlanawarevnet/interfaces b/test/zones/qinq/bridge_vlanawarevnet/interfaces
deleted file mode 100644 (file)
index 68b6a88..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-auto vmbr0
-iface vmbr0 inet manual
-        bridge-ports eth0
-        bridge-stp off
-        bridge-fd 0
diff --git a/test/zones/qinq/bridge_vlanawarevnet/sdn_config b/test/zones/qinq/bridge_vlanawarevnet/sdn_config
deleted file mode 100644 (file)
index c013176..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/qinq/bridge_vlanprotocol/expected_sdn_interfaces b/test/zones/qinq/bridge_vlanprotocol/expected_sdn_interfaces
deleted file mode 100644 (file)
index 6b59164..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-#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/test/zones/qinq/bridge_vlanprotocol/interfaces b/test/zones/qinq/bridge_vlanprotocol/interfaces
deleted file mode 100644 (file)
index 68b6a88..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-auto vmbr0
-iface vmbr0 inet manual
-        bridge-ports eth0
-        bridge-stp off
-        bridge-fd 0
diff --git a/test/zones/qinq/bridge_vlanprotocol/sdn_config b/test/zones/qinq/bridge_vlanprotocol/sdn_config
deleted file mode 100644 (file)
index 20a8a51..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/qinq/ovs/expected_sdn_interfaces b/test/zones/qinq/ovs/expected_sdn_interfaces
deleted file mode 100644 (file)
index d25b2a8..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-#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/test/zones/qinq/ovs/interfaces b/test/zones/qinq/ovs/interfaces
deleted file mode 100644 (file)
index 14d2f1e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/zones/qinq/ovs/sdn_config b/test/zones/qinq/ovs/sdn_config
deleted file mode 100644 (file)
index 6321603..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-{
-  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/test/zones/qinq/ovs_notagvnet/expected_sdn_interfaces b/test/zones/qinq/ovs_notagvnet/expected_sdn_interfaces
deleted file mode 100644 (file)
index 5f47b28..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#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/test/zones/qinq/ovs_notagvnet/interfaces b/test/zones/qinq/ovs_notagvnet/interfaces
deleted file mode 100644 (file)
index 14d2f1e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/zones/qinq/ovs_notagvnet/sdn_config b/test/zones/qinq/ovs_notagvnet/sdn_config
deleted file mode 100644 (file)
index 2382f4d..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/qinq/ovs_vlanawarevnet/expected_sdn_interfaces b/test/zones/qinq/ovs_vlanawarevnet/expected_sdn_interfaces
deleted file mode 100644 (file)
index d69d38c..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-#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/test/zones/qinq/ovs_vlanawarevnet/interfaces b/test/zones/qinq/ovs_vlanawarevnet/interfaces
deleted file mode 100644 (file)
index 14d2f1e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/zones/qinq/ovs_vlanawarevnet/sdn_config b/test/zones/qinq/ovs_vlanawarevnet/sdn_config
deleted file mode 100644 (file)
index c013176..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/qinq/ovs_vlanprotocol/expected_sdn_interfaces b/test/zones/qinq/ovs_vlanprotocol/expected_sdn_interfaces
deleted file mode 100644 (file)
index aeefec9..0000000
+++ /dev/null
@@ -1,35 +0,0 @@
-#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/test/zones/qinq/ovs_vlanprotocol/interfaces b/test/zones/qinq/ovs_vlanprotocol/interfaces
deleted file mode 100644 (file)
index 14d2f1e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/zones/qinq/ovs_vlanprotocol/sdn_config b/test/zones/qinq/ovs_vlanprotocol/sdn_config
deleted file mode 100644 (file)
index 20a8a51..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/simple/basic/expected_sdn_interfaces b/test/zones/simple/basic/expected_sdn_interfaces
deleted file mode 100644 (file)
index 1e0c2c7..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#version:1
-
-auto myvnet
-iface myvnet
-       bridge_ports none
-       bridge_stp off
-       bridge_fd 0
diff --git a/test/zones/simple/basic/interfaces b/test/zones/simple/basic/interfaces
deleted file mode 100644 (file)
index 68b6a88..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-auto vmbr0
-iface vmbr0 inet manual
-        bridge-ports eth0
-        bridge-stp off
-        bridge-fd 0
diff --git a/test/zones/simple/basic/sdn_config b/test/zones/simple/basic/sdn_config
deleted file mode 100644 (file)
index 527dcba..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  version => 1,
-  vnets   => {
-               ids => {
-                        myvnet => { type => "vnet", zone => "myzone" },
-                      },
-             },
-  zones   => {
-               ids => { myzone => { ipam => "pve", type => "simple" } },
-             },
-}
diff --git a/test/zones/simple/hetzner/expected_sdn_interfaces b/test/zones/simple/hetzner/expected_sdn_interfaces
deleted file mode 100644 (file)
index f47ac53..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#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/test/zones/simple/hetzner/interfaces b/test/zones/simple/hetzner/interfaces
deleted file mode 100644 (file)
index 5ab9635..0000000
+++ /dev/null
@@ -1,6 +0,0 @@
-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/test/zones/simple/hetzner/sdn_config b/test/zones/simple/hetzner/sdn_config
deleted file mode 100644 (file)
index 30773ca..0000000
+++ /dev/null
@@ -1,34 +0,0 @@
-{
-  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/test/zones/simple/ipv4/expected_sdn_interfaces b/test/zones/simple/ipv4/expected_sdn_interfaces
deleted file mode 100644 (file)
index 06e43ad..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#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/test/zones/simple/ipv4/interfaces b/test/zones/simple/ipv4/interfaces
deleted file mode 100644 (file)
index 68b6a88..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-auto vmbr0
-iface vmbr0 inet manual
-        bridge-ports eth0
-        bridge-stp off
-        bridge-fd 0
diff --git a/test/zones/simple/ipv4/sdn_config b/test/zones/simple/ipv4/sdn_config
deleted file mode 100644 (file)
index dd77b75..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-{
-  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/test/zones/simple/ipv4snat/expected_sdn_interfaces b/test/zones/simple/ipv4snat/expected_sdn_interfaces
deleted file mode 100644 (file)
index 69d7986..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#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/test/zones/simple/ipv4snat/interfaces b/test/zones/simple/ipv4snat/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/simple/ipv4snat/sdn_config b/test/zones/simple/ipv4snat/sdn_config
deleted file mode 100644 (file)
index 5936d7d..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-{
-  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/test/zones/simple/ipv4v6/expected_sdn_interfaces b/test/zones/simple/ipv4v6/expected_sdn_interfaces
deleted file mode 100644 (file)
index 34ed5db..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-#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/test/zones/simple/ipv4v6/interfaces b/test/zones/simple/ipv4v6/interfaces
deleted file mode 100644 (file)
index 68b6a88..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-auto vmbr0
-iface vmbr0 inet manual
-        bridge-ports eth0
-        bridge-stp off
-        bridge-fd 0
diff --git a/test/zones/simple/ipv4v6/sdn_config b/test/zones/simple/ipv4v6/sdn_config
deleted file mode 100644 (file)
index b8ed848..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-{
-  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/test/zones/simple/ipv4v6nogateway/expected_sdn_interfaces b/test/zones/simple/ipv4v6nogateway/expected_sdn_interfaces
deleted file mode 100644 (file)
index 1e0c2c7..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#version:1
-
-auto myvnet
-iface myvnet
-       bridge_ports none
-       bridge_stp off
-       bridge_fd 0
diff --git a/test/zones/simple/ipv4v6nogateway/interfaces b/test/zones/simple/ipv4v6nogateway/interfaces
deleted file mode 100644 (file)
index 68b6a88..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-auto vmbr0
-iface vmbr0 inet manual
-        bridge-ports eth0
-        bridge-stp off
-        bridge-fd 0
diff --git a/test/zones/simple/ipv4v6nogateway/sdn_config b/test/zones/simple/ipv4v6nogateway/sdn_config
deleted file mode 100644 (file)
index dbd75c9..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-{
-  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/test/zones/simple/ipv6snat/expected_sdn_interfaces b/test/zones/simple/ipv6snat/expected_sdn_interfaces
deleted file mode 100644 (file)
index d3adc24..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-#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/test/zones/simple/ipv6snat/interfaces b/test/zones/simple/ipv6snat/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/simple/ipv6snat/sdn_config b/test/zones/simple/ipv6snat/sdn_config
deleted file mode 100644 (file)
index bc38527..0000000
+++ /dev/null
@@ -1,24 +0,0 @@
-{
-  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/test/zones/vlan/bridge/expected_sdn_interfaces b/test/zones/vlan/bridge/expected_sdn_interfaces
deleted file mode 100644 (file)
index f9e96d1..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-#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/test/zones/vlan/bridge/interfaces b/test/zones/vlan/bridge/interfaces
deleted file mode 100644 (file)
index 68b6a88..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-auto vmbr0
-iface vmbr0 inet manual
-        bridge-ports eth0
-        bridge-stp off
-        bridge-fd 0
diff --git a/test/zones/vlan/bridge/sdn_config b/test/zones/vlan/bridge/sdn_config
deleted file mode 100644 (file)
index c6cfaaa..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  version => 1,
-  vnets   => {
-               ids => {
-                        myvnet => { tag => 100, type => "vnet", zone => "myzone" },
-                      },
-             },
-  zones   => {
-               ids => { myzone => { bridge => "vmbr0", ipam => "pve", type => "vlan" } },
-             },
-}
diff --git a/test/zones/vlan/bridge_vlanaware/expected_sdn_interfaces b/test/zones/vlan/bridge_vlanaware/expected_sdn_interfaces
deleted file mode 100644 (file)
index a318c7a..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-#version:1
-
-auto myvnet
-iface myvnet
-       bridge_ports vmbr0.100
-       bridge_stp off
-       bridge_fd 0
diff --git a/test/zones/vlan/bridge_vlanaware/interfaces b/test/zones/vlan/bridge_vlanaware/interfaces
deleted file mode 100644 (file)
index cfdfafe..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/vlan/bridge_vlanaware/sdn_config b/test/zones/vlan/bridge_vlanaware/sdn_config
deleted file mode 100644 (file)
index c6cfaaa..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  version => 1,
-  vnets   => {
-               ids => {
-                        myvnet => { tag => 100, type => "vnet", zone => "myzone" },
-                      },
-             },
-  zones   => {
-               ids => { myzone => { bridge => "vmbr0", ipam => "pve", type => "vlan" } },
-             },
-}
diff --git a/test/zones/vlan/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces b/test/zones/vlan/bridge_vlanaware_vlanawarevnet/expected_sdn_interfaces
deleted file mode 100644 (file)
index ebf9d2e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#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/test/zones/vlan/bridge_vlanaware_vlanawarevnet/interfaces b/test/zones/vlan/bridge_vlanaware_vlanawarevnet/interfaces
deleted file mode 100644 (file)
index 64eec9e..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/vlan/bridge_vlanaware_vlanawarevnet/sdn_config b/test/zones/vlan/bridge_vlanaware_vlanawarevnet/sdn_config
deleted file mode 100644 (file)
index 67068f9..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/vlan/ovs/expected_sdn_interfaces b/test/zones/vlan/ovs/expected_sdn_interfaces
deleted file mode 100644 (file)
index 044559e..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#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/test/zones/vlan/ovs/interfaces b/test/zones/vlan/ovs/interfaces
deleted file mode 100644 (file)
index 14d2f1e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/zones/vlan/ovs/sdn_config b/test/zones/vlan/ovs/sdn_config
deleted file mode 100644 (file)
index c6cfaaa..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  version => 1,
-  vnets   => {
-               ids => {
-                        myvnet => { tag => 100, type => "vnet", zone => "myzone" },
-                      },
-             },
-  zones   => {
-               ids => { myzone => { bridge => "vmbr0", ipam => "pve", type => "vlan" } },
-             },
-}
diff --git a/test/zones/vlan/ovs_vlanware_vnet/expected_sdn_interfaces b/test/zones/vlan/ovs_vlanware_vnet/expected_sdn_interfaces
deleted file mode 100644 (file)
index 7bb73b6..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-#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/test/zones/vlan/ovs_vlanware_vnet/interfaces b/test/zones/vlan/ovs_vlanware_vnet/interfaces
deleted file mode 100644 (file)
index 14d2f1e..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-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/test/zones/vlan/ovs_vlanware_vnet/sdn_config b/test/zones/vlan/ovs_vlanware_vnet/sdn_config
deleted file mode 100644 (file)
index 9cfdb52..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/vxlan/basic/expected_sdn_interfaces b/test/zones/vxlan/basic/expected_sdn_interfaces
deleted file mode 100644 (file)
index 7b73c3e..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-#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/test/zones/vxlan/basic/interfaces b/test/zones/vxlan/basic/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/vxlan/basic/sdn_config b/test/zones/vxlan/basic/sdn_config
deleted file mode 100644 (file)
index f929304..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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/test/zones/vxlan/vlanawarevnet/expected_sdn_interfaces b/test/zones/vxlan/vlanawarevnet/expected_sdn_interfaces
deleted file mode 100644 (file)
index 55cdf9c..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-#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/test/zones/vxlan/vlanawarevnet/interfaces b/test/zones/vxlan/vlanawarevnet/interfaces
deleted file mode 100644 (file)
index 66bb826..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-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/test/zones/vxlan/vlanawarevnet/sdn_config b/test/zones/vxlan/vlanawarevnet/sdn_config
deleted file mode 100644 (file)
index 23fb557..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  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" } },
-             },
-}