all lists on lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH v10 pve-network 00/35]  add subnet plugin
@ 2020-10-05 15:08 Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 01/35] " Alexandre Derumier
                   ` (34 more replies)
  0 siblings, 35 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

This patch series add basic subnets managements.
(need pve-cluster V5 patch series)

Subnets will be use for multiple things:
- defined gateway ip on vnets
- enable snat on a subnet
- add cloudnit|dhcp default network configs (gateway, static routes, nameservers,searchdomain,....)
- add ipam management. (ip registrations to external ipam, for vm|ct ip management)
- add dns registration. (reverse dns for subnet, and searchdomain regisration in external dns like powerdns )
- ...


Already implemented:

- gateway option is implemented:

It's currently replace ip management on vnets for layer3 plugins,
through the option "gateway".

If the option gateway is defined, for layer3 plugins (evpn && simple),
the ip will be used for the vnet.

A vnet can have multiple subnets, with multiples ipv4/ipv6.

- ipam
- dns registration


Changelog v2:

- add ipams plugins. Currently netbox && phpipam.
- the subnet && the subnet gateway are registered to ipam
- add/del/find_next_free ip are implemented, so it should be easy to use them in qemu && lxc config.

Changelog v3:

- add an internal ipam plugin

Changelog v4:

- fix pveipam plugin find_free_ip
- detect ipv4/ipv6 in find_free_ip

Changelog v5:

- add vnets add_ip,del_ip,... should be ok for use in lxc/qemuserver

Changelog v6:

- add dns plugins
- internal ipam fixes
- rework vnet-subnet association
- fixes && cleanup

Changelog v7:

- add snat to simple|evpn plugin
- cleanup subnet options
- fix reversedns

Changelog v8:

- move subnet api to /sdn/vnet/<vnet>/subnet
- make ipam non optionnal && use pve ipam as default
- don't allow subnets on vlanaware vnet
- fixes && cleanup

Changelog v9:

- write running config on commit in /etc/pve/sdn/.running-config
  and use it as source for config generation,status

Changelog v10:

- move ipams/dns options from subnets to zone
- add hostname/description to ipam db
- cleanup && bugfix

Alexandre Derumier (35):
  add subnet plugin
  vnets: add subnets
  add subnets verifications hooks
  zones: simple|evpn: add gateway ip from subnets to vnet
  zone: add vnet_update_hook
  vnets: subnets: use cidr
  subnet: fix on_delete_hook
  api2: subnet create: convert cidr to subnetid
  api2: increase version on apply/reload only
  add ipams plugins
  add pve internal ipam plugin
  vnets: find_free_ip : add ipversion detection
  vnets: add add_ip
  vnets: add del_ip + rework add_ip/find_free_ip
  add dns plugin
  Fix vnet gateway for routed setup + /32 pointopoint subnet
  ipam : pveplugin : fix find_next_free_ip
  add vnet to subnets && remove subnetlist from vnet
  zones: evpn|simple: add snat iptables rules
  subnet: disable route option for now and add dns domain format
  dns: fix reverse dns
  subnets: move api to /sdn/vnet/<vnet>/subnets && make vnet option not
    optionnal
  zones: evpn : fix raise exception
  subnet: make ipam not optionnal and use pve ipam as default
  don't allow subnets on vlanware vnet
  generate sdn/.running-config on apply
  api: add running/pending zones/vnets/subnets/controllers
  small bugfixes
  move dns options from subnets to zone
  move ipam option from subnet to zone
  subnets/ipam: allow same subnet on different zones
  vnets: allow duplicate tags in differents zones
  ipam: verify api access  on create/update
  ipam: add hostname/description to ipam db
  update documentation

 PVE/API2/Network/SDN.pm                |  17 ++
 PVE/API2/Network/SDN/Controllers.pm    |  59 ++++-
 PVE/API2/Network/SDN/Dns.pm            | 242 +++++++++++++++++++
 PVE/API2/Network/SDN/Ipams.pm          | 248 ++++++++++++++++++++
 PVE/API2/Network/SDN/Makefile          |   2 +-
 PVE/API2/Network/SDN/Subnets.pm        | 307 +++++++++++++++++++++++++
 PVE/API2/Network/SDN/Vnets.pm          |  82 ++++++-
 PVE/API2/Network/SDN/Zones.pm          | 101 +++++++-
 PVE/Network/SDN.pm                     | 102 ++++++--
 PVE/Network/SDN/Controllers.pm         |  14 +-
 PVE/Network/SDN/Dns.pm                 |  57 +++++
 PVE/Network/SDN/Dns/Makefile           |   8 +
 PVE/Network/SDN/Dns/Plugin.pm          | 118 ++++++++++
 PVE/Network/SDN/Dns/PowerdnsPlugin.pm  | 255 ++++++++++++++++++++
 PVE/Network/SDN/Ipams.pm               |  70 ++++++
 PVE/Network/SDN/Ipams/Makefile         |   8 +
 PVE/Network/SDN/Ipams/NetboxPlugin.pm  | 189 +++++++++++++++
 PVE/Network/SDN/Ipams/PVEPlugin.pm     | 204 ++++++++++++++++
 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 217 +++++++++++++++++
 PVE/Network/SDN/Ipams/Plugin.pm        | 131 +++++++++++
 PVE/Network/SDN/Makefile               |   4 +-
 PVE/Network/SDN/SubnetPlugin.pm        | 165 +++++++++++++
 PVE/Network/SDN/Subnets.pm             | 287 +++++++++++++++++++++++
 PVE/Network/SDN/VnetPlugin.pm          |  37 ++-
 PVE/Network/SDN/Vnets.pm               |  92 +++++++-
 PVE/Network/SDN/Zones.pm               |  45 +++-
 PVE/Network/SDN/Zones/EvpnPlugin.pm    |  70 +++++-
 PVE/Network/SDN/Zones/FaucetPlugin.pm  |   4 +
 PVE/Network/SDN/Zones/Plugin.pm        |  11 +-
 PVE/Network/SDN/Zones/QinQPlugin.pm    |  26 ++-
 PVE/Network/SDN/Zones/SimplePlugin.pm  |  73 +++++-
 PVE/Network/SDN/Zones/VlanPlugin.pm    |  28 ++-
 PVE/Network/SDN/Zones/VxlanPlugin.pm   |  30 ++-
 debian/control                         |   3 +
 test/documentation.txt                 |  33 ++-
 test/generateconfig.pl                 |   5 +-
 36 files changed, 3210 insertions(+), 134 deletions(-)
 create mode 100644 PVE/API2/Network/SDN/Dns.pm
 create mode 100644 PVE/API2/Network/SDN/Ipams.pm
 create mode 100644 PVE/API2/Network/SDN/Subnets.pm
 create mode 100644 PVE/Network/SDN/Dns.pm
 create mode 100644 PVE/Network/SDN/Dns/Makefile
 create mode 100644 PVE/Network/SDN/Dns/Plugin.pm
 create mode 100644 PVE/Network/SDN/Dns/PowerdnsPlugin.pm
 create mode 100644 PVE/Network/SDN/Ipams.pm
 create mode 100644 PVE/Network/SDN/Ipams/Makefile
 create mode 100644 PVE/Network/SDN/Ipams/NetboxPlugin.pm
 create mode 100644 PVE/Network/SDN/Ipams/PVEPlugin.pm
 create mode 100644 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
 create mode 100644 PVE/Network/SDN/Ipams/Plugin.pm
 create mode 100644 PVE/Network/SDN/SubnetPlugin.pm
 create mode 100644 PVE/Network/SDN/Subnets.pm

-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 01/35] add subnet plugin
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 02/35] vnets: add subnets Alexandre Derumier
                   ` (33 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN.pm         |   7 +
 PVE/API2/Network/SDN/Makefile   |   2 +-
 PVE/API2/Network/SDN/Subnets.pm | 219 ++++++++++++++++++++++++++++++++
 PVE/Network/SDN/Makefile        |   2 +-
 PVE/Network/SDN/SubnetPlugin.pm | 115 +++++++++++++++++
 PVE/Network/SDN/Subnets.pm      |  55 ++++++++
 debian/control                  |   1 +
 7 files changed, 399 insertions(+), 2 deletions(-)
 create mode 100644 PVE/API2/Network/SDN/Subnets.pm
 create mode 100644 PVE/Network/SDN/SubnetPlugin.pm
 create mode 100644 PVE/Network/SDN/Subnets.pm

diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
index 3f497fc..38af746 100644
--- a/PVE/API2/Network/SDN.pm
+++ b/PVE/API2/Network/SDN.pm
@@ -14,6 +14,7 @@ use PVE::Tools qw(run_command);
 use PVE::API2::Network::SDN::Controllers;
 use PVE::API2::Network::SDN::Vnets;
 use PVE::API2::Network::SDN::Zones;
+use PVE::API2::Network::SDN::Subnets;
 
 use base qw(PVE::RESTHandler);
 
@@ -32,6 +33,11 @@ __PACKAGE__->register_method ({
     path => 'controllers',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Subnets",
+    path => 'subnets',
+});
+
 __PACKAGE__->register_method({
     name => 'index',
     path => '',
@@ -61,6 +67,7 @@ __PACKAGE__->register_method({
 	    { id => 'vnets' },
 	    { id => 'zones' },
 	    { id => 'controllers' },
+	    { id => 'subnets' },
 	];
 
 	return $res;
diff --git a/PVE/API2/Network/SDN/Makefile b/PVE/API2/Network/SDN/Makefile
index 6f20d4a..59626fa 100644
--- a/PVE/API2/Network/SDN/Makefile
+++ b/PVE/API2/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm Zones.pm Controllers.pm
+SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
new file mode 100644
index 0000000..26b2aa5
--- /dev/null
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -0,0 +1,219 @@
+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::Network::SDN;
+use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::SubnetPlugin;
+
+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,
+    },
+    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 $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/subnets/$id", $privs, 1);
+
+	    my $scfg = &$api_sdn_subnets_config($cfg, $id);
+	    push @$res, $scfg;
+	}
+
+	return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'read',
+    path => '{subnet}',
+    method => 'GET',
+    description => "Read sdn subnet configuration.",
+    permissions => {
+	check => ['perm', '/sdn/subnets/{subnet}', ['SDN.Allocate']],
+   },
+
+    parameters => {
+        additionalProperties => 0,
+        properties => {
+            subnet => get_standard_option('pve-sdn-subnet-id', {
+                completion => \&PVE::Network::SDN::Subnets::complete_sdn_subnets,
+            }),
+        },
+    },
+    returns => { type => 'object' },
+    code => sub {
+	my ($param) = @_;
+
+	my $cfg = PVE::Network::SDN::Subnets::config();
+
+	return &$api_sdn_subnets_config($cfg, $param->{subnet});
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    path => '',
+    method => 'POST',
+    description => "Create a new sdn subnet object.",
+    permissions => {
+	check => ['perm', '/sdn/subnets', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::SubnetPlugin->createSchema(),
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $type = extract_param($param, 'type');
+	my $id = extract_param($param, 'subnet');
+
+        # create /etc/pve/sdn directory
+        PVE::Cluster::check_cfs_quorum();
+        mkdir("/etc/pve/sdn");
+
+        PVE::Network::SDN::lock_sdn_config(
+	    sub {
+
+		my $cfg = PVE::Network::SDN::Subnets::config();
+		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;
+		PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
+		PVE::Network::SDN::Subnets::write_config($cfg);
+		PVE::Network::SDN::increase_version();
+
+	    }, "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/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();
+
+	    PVE::SectionConfig::assert_if_modified($cfg, $digest);
+
+	    my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 0, 1);
+	    $cfg->{ids}->{$id} = $opts;
+
+	    PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
+	    PVE::Network::SDN::Subnets::write_config($cfg);
+	    PVE::Network::SDN::increase_version();
+
+	    }, "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/subnets', ['SDN.Allocate']],
+    },
+    parameters => {
+    	additionalProperties => 0,
+	properties => {
+	    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);
+
+		my $subnet_cfg = PVE::Network::SDN::Subnets::config();
+
+		delete $cfg->{ids}->{$id};
+		PVE::Network::SDN::Subnets::write_config($cfg);
+		PVE::Network::SDN::increase_version();
+
+	    }, "delete sdn subnet object failed");
+
+
+	return undef;
+    }});
+
+1;
diff --git a/PVE/Network/SDN/Makefile b/PVE/Network/SDN/Makefile
index 7622255..59f8c34 100644
--- a/PVE/Network/SDN/Makefile
+++ b/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
new file mode 100644
index 0000000..8900681
--- /dev/null
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -0,0 +1,115 @@
+package PVE::Network::SDN::SubnetPlugin;
+
+use strict;
+use warnings;
+
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use base qw(PVE::SectionConfig);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::Exception qw(raise raise_param_exc);
+use Net::Subnet qw(subnet_matcher);
+
+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 = $id =~ s/-/\//r;
+
+    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 {
+        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>,... ]",
+        },
+	#cloudinit, dhcp options
+        nameservers => {
+            type => 'string', format => 'address-list',
+            description => " dns nameserver",
+        },
+	#cloudinit, dhcp options
+        searchdomain => {
+            type => 'string',
+        },
+        dhcp => {
+            type => 'boolean',
+            description => "enable dhcp for this subnet",
+        },
+        dns_driver => {
+            type => 'string',
+            description => "Develop some dns registrations plugins (powerdns,...)",
+        },
+        ipam_driver => {
+            type => 'string',
+            description => "use a specific ipam",
+        },
+    };
+}
+
+sub options {
+    return {
+	gateway => { optional => 1 },
+	routes => { optional => 1 },
+	nameservers => { optional => 1 },
+	searchdomain => { optional => 1 },
+	snat => { optional => 1 },
+	dhcp => { optional => 1 },
+	dns_driver => { optional => 1 },
+	ipam_driver => { optional => 1 },
+    };
+}
+
+sub on_update_hook {
+    my ($class, $subnetid, $subnet_cfg) = @_;
+
+    my $subnet = $subnetid =~ s/-/\//r;
+    my $subnet_matcher = subnet_matcher($subnet);
+
+    my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+    raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway);
+}
+
+1;
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
new file mode 100644
index 0000000..454a9cf
--- /dev/null
+++ b/PVE/Network/SDN/Subnets.pm
@@ -0,0 +1,55 @@
+package PVE::Network::SDN::Subnets;
+
+use strict;
+use warnings;
+
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+
+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);
+
+    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 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) = @_;
+
+    my $cfg = PVE::Network::SDN::Subnets::config();
+    my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
+    return $subnet;
+}
+
+1;
diff --git a/debian/control b/debian/control
index afdf573..8b67d74 100644
--- a/debian/control
+++ b/debian/control
@@ -16,6 +16,7 @@ Breaks: pve-manager (<< 5.2-12)
 Depends: libpve-common-perl (>= 5.0-45),
          perl (>= 5.6.0-16),
          pve-cluster (>= 5.0-32),
+         libnet-subnet-perl,
          ${misc:Depends},
          ${perl:Depends},
 Recommends: frr-pythontools, ifupdown2
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 02/35] vnets: add subnets
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 01/35] " Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 03/35] add subnets verifications hooks Alexandre Derumier
                   ` (32 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/VnetPlugin.pm | 22 ++++++++++------------
 1 file changed, 10 insertions(+), 12 deletions(-)

diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index 384358c..47ca50b 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -68,16 +68,11 @@ sub properties {
             description => "alias name of the vnet",
 	    optional => 1,
         },
-        ipv4 => {
-            description => "Anycast router ipv4 address.",
-            type => 'string', format => 'CIDRv4',
-            optional => 1,
-        },
-	ipv6 => {
-	    description => "Anycast router ipv6 address.",
-	    type => 'string', format => 'CIDRv6',
+        subnets => {
+            type => 'string',
+            description => "Subnets list",
 	    optional => 1,
-	},
+        },
         mac => {
             type => 'string',
             description => "Anycast router mac address",
@@ -91,8 +86,7 @@ sub options {
         zone => { optional => 0},
         tag => { optional => 1},
         alias => { optional => 1 },
-        ipv4 => { optional => 1 },
-        ipv6 => { optional => 1 },
+        subnets => { optional => 1 },
         mac => { optional => 1 },
         vlanaware => { optional => 1 },
     };
@@ -105,7 +99,7 @@ sub on_delete_hook {
 }
 
 sub on_update_hook {
-    my ($class, $vnetid, $vnet_cfg) = @_;
+    my ($class, $vnetid, $vnet_cfg, $subnet_cfg) = @_;
     # verify that tag is not already defined in another vnet
     if (defined($vnet_cfg->{ids}->{$vnetid}->{tag})) {
 	my $tag = $vnet_cfg->{ids}->{$vnetid}->{tag};
@@ -117,6 +111,10 @@ sub on_update_hook {
 	    }
 	}
     }
+    #verify subnet
+    my $subnets = $vnet_cfg->{ids}->{$vnetid}->{subnets};
+    my @subnets = PVE::Tools::split_list($vnet_cfg->{ids}->{$vnetid}->{subnets}) if $plugin_config->{'peers'};
+
 }
 
 1;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 03/35] add subnets verifications hooks
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 01/35] " Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 02/35] vnets: add subnets Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 04/35] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
                   ` (31 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Subnets.pm |  5 ++++-
 PVE/API2/Network/SDN/Vnets.pm   |  9 +++++++--
 PVE/Network/SDN/SubnetPlugin.pm | 15 +++++++++++++++
 PVE/Network/SDN/VnetPlugin.pm   |  8 +++++---
 4 files changed, 31 insertions(+), 6 deletions(-)

diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index 26b2aa5..3ef1d11 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -9,6 +9,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file);
 use PVE::Network::SDN;
 use PVE::Network::SDN::Subnets;
 use PVE::Network::SDN::SubnetPlugin;
+use PVE::Network::SDN::Vnets;
 
 use Storable qw(dclone);
 use PVE::JSONSchema qw(get_standard_option);
@@ -204,9 +205,11 @@ __PACKAGE__->register_method ({
 
 		my $scfg = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id);
 
-		my $subnet_cfg = PVE::Network::SDN::Subnets::config();
+		my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+		my $vnets_cfg = PVE::Network::SDN::Vnets::config();
 
 		delete $cfg->{ids}->{$id};
+		PVE::Network::SDN::SubnetPlugin->on_delete_hook($id, $subnets_cfg, $vnets_cfg);
 		PVE::Network::SDN::Subnets::write_config($cfg);
 		PVE::Network::SDN::increase_version();
 
diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
index c5860c8..23bc8bb 100644
--- a/PVE/API2/Network/SDN/Vnets.pm
+++ b/PVE/API2/Network/SDN/Vnets.pm
@@ -11,6 +11,7 @@ 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 Storable qw(dclone);
 use PVE::JSONSchema qw(get_standard_option);
@@ -132,7 +133,9 @@ __PACKAGE__->register_method ({
 	    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
 	    $plugin->verify_tag($opts->{tag});
 
-	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
+	    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
+
+	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg);
 
 	    PVE::Network::SDN::Vnets::write_config($cfg);
 	    PVE::Network::SDN::increase_version();
@@ -173,7 +176,9 @@ __PACKAGE__->register_method ({
 	    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
 	    $plugin->verify_tag($opts->{tag});
 
-	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
+	    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
+
+	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg);
 
 	    PVE::Network::SDN::Vnets::write_config($cfg);
 	    PVE::Network::SDN::increase_version();
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 8900681..1b790a6 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -112,4 +112,19 @@ sub on_update_hook {
     raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway);
 }
 
+sub on_delete_hook {
+    my ($class, $subnetid, $subnet_cfg, $vnet_cfg) = @_;
+
+    #verify if vnets have subnet
+    foreach my $id (keys %{$vnet_cfg->{ids}}) {
+	my $vnet = $vnet_cfg->{ids}->{$id};
+	my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+	foreach my $subnet (@subnets) {
+	    raise_param_exc({ subnet => "$subnet is attached to vnet $id"}) if $subnet eq $subnetid;
+	}
+    }
+
+    return;
+}
+
 1;
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index 47ca50b..430b3bf 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -111,10 +111,12 @@ sub on_update_hook {
 	    }
 	}
     }
-    #verify subnet
-    my $subnets = $vnet_cfg->{ids}->{$vnetid}->{subnets};
-    my @subnets = PVE::Tools::split_list($vnet_cfg->{ids}->{$vnetid}->{subnets}) if $plugin_config->{'peers'};
 
+    #verify subnet
+    my @subnets = PVE::Tools::split_list($vnet_cfg->{ids}->{$vnetid}->{subnets}) if $vnet_cfg->{ids}->{$vnetid}->{subnets};
+    foreach my $subnet (@subnets) {
+	raise_param_exc({ subnet => "$subnet not existing"}) if !$subnet_cfg->{ids}->{$subnet};
+    }
 }
 
 1;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 04/35] zones: simple|evpn: add gateway ip from subnets to vnet
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (2 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 03/35] add subnets verifications hooks Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 05/35] zone: add vnet_update_hook Alexandre Derumier
                   ` (30 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Zones.pm              |  4 +++-
 PVE/Network/SDN/Zones/EvpnPlugin.pm   | 11 ++++++++---
 PVE/Network/SDN/Zones/Plugin.pm       |  2 +-
 PVE/Network/SDN/Zones/QinQPlugin.pm   |  2 +-
 PVE/Network/SDN/Zones/SimplePlugin.pm | 11 ++++++++---
 PVE/Network/SDN/Zones/VlanPlugin.pm   |  2 +-
 PVE/Network/SDN/Zones/VxlanPlugin.pm  |  8 +-------
 7 files changed, 23 insertions(+), 17 deletions(-)

diff --git a/PVE/Network/SDN/Zones.pm b/PVE/Network/SDN/Zones.pm
index 143d6e5..25af088 100644
--- a/PVE/Network/SDN/Zones.pm
+++ b/PVE/Network/SDN/Zones.pm
@@ -11,6 +11,7 @@ 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::Subnets;
 use PVE::Network::SDN::Zones::VlanPlugin;
 use PVE::Network::SDN::Zones::QinQPlugin;
 use PVE::Network::SDN::Zones::VxlanPlugin;
@@ -78,6 +79,7 @@ sub generate_etc_network_config {
     my $version = PVE::Cluster::cfs_read_file('sdn/.version');
     my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
     my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
+    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
     my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
     return if !$vnet_cfg && !$zone_cfg;
 
@@ -112,7 +114,7 @@ sub generate_etc_network_config {
 
 	my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
 	eval {
-	    $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $interfaces_config, $config);
+	    $plugin->generate_sdn_config($plugin_config, $zone, $id, $vnet, $controller, $subnet_cfg, $interfaces_config, $config);
 	};
 	if (my $err = $@) {
 	    warn "zone $zone : vnet $id : $err\n";
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index a916579..83ceb3a 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -38,7 +38,7 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
 
     my $tag = $vnet->{tag};
     my $alias = $vnet->{alias};
@@ -72,8 +72,13 @@ sub generate_sdn_config {
 
     #vnet bridge
     @iface_config = ();
-    push @iface_config, "address $ipv4" if $ipv4;
-    push @iface_config, "address $ipv6" if $ipv6;
+
+    my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+    foreach my $subnet (@subnets) {
+        next if !defined($subnet_cfg->{ids}->{$subnet});
+        push @iface_config, "address $subnet_cfg->{ids}->{$subnet}->{gateway}" if $subnet_cfg->{ids}->{$subnet}->{gateway};
+    }
+
     push @iface_config, "hwaddress $mac" if $mac;
     push @iface_config, "bridge_ports $vxlan_iface";
     push @iface_config, "bridge_stp off";
diff --git a/PVE/Network/SDN/Zones/Plugin.pm b/PVE/Network/SDN/Zones/Plugin.pm
index d96e069..451699f 100644
--- a/PVE/Network/SDN/Zones/Plugin.pm
+++ b/PVE/Network/SDN/Zones/Plugin.pm
@@ -94,7 +94,7 @@ sub parse_section_header {
 }
 
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
 
     die "please implement inside plugin";
 }
diff --git a/PVE/Network/SDN/Zones/QinQPlugin.pm b/PVE/Network/SDN/Zones/QinQPlugin.pm
index b39732a..5fffd15 100644
--- a/PVE/Network/SDN/Zones/QinQPlugin.pm
+++ b/PVE/Network/SDN/Zones/QinQPlugin.pm
@@ -45,7 +45,7 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
 
     my $stag = $plugin_config->{tag};
     my $mtu = $plugin_config->{mtu};
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index 6137062..312dcbf 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -20,7 +20,7 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
 
     return $config if$config->{$vnetid}; # nothing to do
 
@@ -32,8 +32,13 @@ sub generate_sdn_config {
 
     # vnet bridge
     my @iface_config = ();
-    push @iface_config, "address $ipv4" if $ipv4;
-    push @iface_config, "address $ipv6" if $ipv6;
+
+    my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+    foreach my $subnet (@subnets) {
+	next if !defined($subnet_cfg->{ids}->{$subnet});
+	push @iface_config, "address $subnet_cfg->{ids}->{$subnet}->{gateway}" if $subnet_cfg->{ids}->{$subnet}->{gateway};
+    }
+
     push @iface_config, "hwaddress $mac" if $mac;
     push @iface_config, "bridge_ports none";
     push @iface_config, "bridge_stp off";
diff --git a/PVE/Network/SDN/Zones/VlanPlugin.pm b/PVE/Network/SDN/Zones/VlanPlugin.pm
index db719a0..8485ae1 100644
--- a/PVE/Network/SDN/Zones/VlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VlanPlugin.pm
@@ -39,7 +39,7 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
 
     my $bridge = $plugin_config->{bridge};
     die "can't find bridge $bridge" if !-d "/sys/class/net/$bridge";
diff --git a/PVE/Network/SDN/Zones/VxlanPlugin.pm b/PVE/Network/SDN/Zones/VxlanPlugin.pm
index a256268..8386c43 100644
--- a/PVE/Network/SDN/Zones/VxlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VxlanPlugin.pm
@@ -43,13 +43,10 @@ sub options {
 
 # Plugin implementation
 sub generate_sdn_config {
-    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $interfaces_config, $config) = @_;
+    my ($class, $plugin_config, $zoneid, $vnetid, $vnet, $controller, $subnet_cfg, $interfaces_config, $config) = @_;
 
     my $tag = $vnet->{tag};
     my $alias = $vnet->{alias};
-    my $ipv4 = $vnet->{ipv4};
-    my $ipv6 = $vnet->{ipv6};
-    my $mac = $vnet->{mac};
     my $multicastaddress = $plugin_config->{'multicast-address'};
     my @peers;
     @peers = PVE::Tools::split_list($plugin_config->{'peers'}) if $plugin_config->{'peers'};
@@ -78,9 +75,6 @@ sub generate_sdn_config {
 
     #vnet bridge
     @iface_config = ();
-    push @iface_config, "address $ipv4" if $ipv4;
-    push @iface_config, "address $ipv6" if $ipv6;
-    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";
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 05/35] zone: add vnet_update_hook
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (3 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 04/35] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 06/35] vnets: subnets: use cidr Alexandre Derumier
                   ` (29 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

move verify_tag code in this hook
add mac address generation for simple && evpn plugin

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Vnets.pm         |  4 ++--
 PVE/Network/SDN/Zones/EvpnPlugin.pm   | 19 +++++++++++++++----
 PVE/Network/SDN/Zones/Plugin.pm       |  5 +++--
 PVE/Network/SDN/Zones/QinQPlugin.pm   |  8 ++++----
 PVE/Network/SDN/Zones/SimplePlugin.pm | 14 +++++++++++---
 PVE/Network/SDN/Zones/VlanPlugin.pm   |  8 ++++----
 PVE/Network/SDN/Zones/VxlanPlugin.pm  |  8 ++++----
 7 files changed, 43 insertions(+), 23 deletions(-)

diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
index 23bc8bb..58ec21f 100644
--- a/PVE/API2/Network/SDN/Vnets.pm
+++ b/PVE/API2/Network/SDN/Vnets.pm
@@ -131,7 +131,7 @@ __PACKAGE__->register_method ({
 	    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->verify_tag($opts->{tag});
+            $plugin->vnet_update_hook($cfg->{ids}->{$id});
 
 	    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
 
@@ -174,7 +174,7 @@ __PACKAGE__->register_method ({
 	    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->verify_tag($opts->{tag});
+	    $plugin->vnet_update_hook($cfg->{ids}->{$id});
 
 	    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
 
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 83ceb3a..0ebe13e 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -5,6 +5,9 @@ use warnings;
 use PVE::Network::SDN::Zones::VxlanPlugin;
 use PVE::Tools qw($IPV4RE);
 use PVE::INotify;
+use PVE::Cluster;
+use PVE::Tools;
+
 use PVE::Network::SDN::Controllers::EvpnPlugin;
 
 use base('PVE::Network::SDN::Zones::VxlanPlugin');
@@ -143,15 +146,23 @@ sub on_update_hook {
 	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);
     }
+
 }
 
-sub verify_tag {
-    my ($class, $tag) = @_;
 
-    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($tag);
-    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $tag > 16777216;
+sub vnet_update_hook {
+    my ($class, $vnet) = @_;
+
+    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($vnet->{tag});
+    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $vnet->{tag} > 16777216;
+
+    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/Plugin.pm b/PVE/Network/SDN/Zones/Plugin.pm
index 451699f..7f6db0e 100644
--- a/PVE/Network/SDN/Zones/Plugin.pm
+++ b/PVE/Network/SDN/Zones/Plugin.pm
@@ -139,8 +139,9 @@ sub on_update_hook {
     # do nothing by default
 }
 
-sub verify_tag {
-    my ($class, $tag) = @_;
+sub vnet_update_hook {
+    my ($class, $vnet) = @_;
+
     # do nothing by default
 }
 
diff --git a/PVE/Network/SDN/Zones/QinQPlugin.pm b/PVE/Network/SDN/Zones/QinQPlugin.pm
index 5fffd15..c828af4 100644
--- a/PVE/Network/SDN/Zones/QinQPlugin.pm
+++ b/PVE/Network/SDN/Zones/QinQPlugin.pm
@@ -211,11 +211,11 @@ sub status {
     return $err_msg;
 }
 
-sub verify_tag {
-    my ($class, $tag) = @_;
+sub vnet_update_hook {
+    my ($class, $vnet) = @_;
 
-    raise_param_exc({ tag => "missing vlan tag"}) if !defined($tag);
-    raise_param_exc({ tag => "vlan tag max value is 4096"}) if $tag > 4096;
+    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;
 }
 
 1;
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index 312dcbf..7006b13 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -4,6 +4,8 @@ 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');
 
@@ -71,10 +73,16 @@ sub status {
     return $err_msg;
 }
 
-sub verify_tag {
-    my ($class, $tag) = @_;
 
-    raise_param_exc({ tag => "vlan tag is not allowed on simple bridge"}) if defined($tag);
+sub vnet_update_hook {
+    my ($class, $vnet) = @_;
+
+    raise_param_exc({ tag => "vlan tag is not allowed on simple bridge"}) if defined($vnet->{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
index 8485ae1..7f90d31 100644
--- a/PVE/Network/SDN/Zones/VlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VlanPlugin.pm
@@ -170,11 +170,11 @@ sub status {
     return $err_msg;
 }
 
-sub verify_tag {
-    my ($class, $tag) = @_;
+sub vnet_update_hook {
+    my ($class, $vnet) = @_;
 
-    raise_param_exc({ tag => "missing vlan tag"}) if !defined($tag);
-    raise_param_exc({ tag => "vlan tag max value is 4096"}) if $tag > 4096;
+    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;
 }
 
 1;
diff --git a/PVE/Network/SDN/Zones/VxlanPlugin.pm b/PVE/Network/SDN/Zones/VxlanPlugin.pm
index 8386c43..79af054 100644
--- a/PVE/Network/SDN/Zones/VxlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VxlanPlugin.pm
@@ -89,11 +89,11 @@ sub generate_sdn_config {
     return $config;
 }
 
-sub verify_tag {
-    my ($class, $tag) = @_;
+sub vnet_update_hook {
+    my ($class, $vnet) = @_;
 
-    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($tag);
-    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $tag > 16777216;
+    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($vnet->{tag});
+    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $vnet->{tag} > 16777216;
 }
 
 1;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 06/35] vnets: subnets: use cidr
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (4 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 05/35] zone: add vnet_update_hook Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 07/35] subnet: fix on_delete_hook Alexandre Derumier
                   ` (28 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/SubnetPlugin.pm | 3 ++-
 PVE/Network/SDN/VnetPlugin.pm   | 3 ++-
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 1b790a6..c555314 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -120,7 +120,8 @@ sub on_delete_hook {
 	my $vnet = $vnet_cfg->{ids}->{$id};
 	my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
 	foreach my $subnet (@subnets) {
-	    raise_param_exc({ subnet => "$subnet is attached to vnet $id"}) if $subnet eq $subnetid;
+	    my $id = $subnet =~ s/\//-/r;
+	    raise_param_exc({ subnet => "$subnet is attached to vnet $id"}) if $id eq $subnetid;
 	}
     }
 
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index 430b3bf..6b2bcc8 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -115,7 +115,8 @@ sub on_update_hook {
     #verify subnet
     my @subnets = PVE::Tools::split_list($vnet_cfg->{ids}->{$vnetid}->{subnets}) if $vnet_cfg->{ids}->{$vnetid}->{subnets};
     foreach my $subnet (@subnets) {
-	raise_param_exc({ subnet => "$subnet not existing"}) if !$subnet_cfg->{ids}->{$subnet};
+	my $id = $subnet =~ s/\//-/r;
+	raise_param_exc({ subnet => "$subnet not existing"}) if !$subnet_cfg->{ids}->{$id};
     }
 }
 
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 07/35] subnet: fix on_delete_hook
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (5 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 06/35] vnets: subnets: use cidr Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 08/35] api2: subnet create: convert cidr to subnetid Alexandre Derumier
                   ` (27 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/SubnetPlugin.pm | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index c555314..ea47684 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -116,12 +116,12 @@ sub on_delete_hook {
     my ($class, $subnetid, $subnet_cfg, $vnet_cfg) = @_;
 
     #verify if vnets have subnet
-    foreach my $id (keys %{$vnet_cfg->{ids}}) {
-	my $vnet = $vnet_cfg->{ids}->{$id};
+    foreach my $vnetid (keys %{$vnet_cfg->{ids}}) {
+	my $vnet = $vnet_cfg->{ids}->{$vnetid};
 	my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
 	foreach my $subnet (@subnets) {
 	    my $id = $subnet =~ s/\//-/r;
-	    raise_param_exc({ subnet => "$subnet is attached to vnet $id"}) if $id eq $subnetid;
+	    raise_param_exc({ subnet => "$subnet is attached to vnet $vnetid"}) if $id eq $subnetid;
 	}
     }
 
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 08/35] api2: subnet create: convert cidr to subnetid
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (6 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 07/35] subnet: fix on_delete_hook Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 09/35] api2: increase version on apply/reload only Alexandre Derumier
                   ` (26 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Subnets.pm | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index 3ef1d11..d18cf90 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -24,6 +24,7 @@ my $api_sdn_subnets_config = sub {
 
     my $scfg = dclone(PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id));
     $scfg->{subnet} = $id;
+    $scfg->{cidr} = $id =~ s/-/\//r;
     $scfg->{digest} = $cfg->{digest};
 
     return $scfg;
@@ -112,7 +113,8 @@ __PACKAGE__->register_method ({
 	my ($param) = @_;
 
 	my $type = extract_param($param, 'type');
-	my $id = extract_param($param, 'subnet');
+	my $cidr = extract_param($param, 'subnet');
+	my $id = $cidr =~ s/\//-/r;
 
         # create /etc/pve/sdn directory
         PVE::Cluster::check_cfs_quorum();
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 09/35] api2: increase version on apply/reload only
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (7 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 08/35] api2: subnet create: convert cidr to subnetid Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 10/35] add ipams plugins Alexandre Derumier
                   ` (25 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN.pm             | 3 +++
 PVE/API2/Network/SDN/Controllers.pm | 6 ------
 PVE/API2/Network/SDN/Subnets.pm     | 3 ---
 PVE/API2/Network/SDN/Vnets.pm       | 3 ---
 PVE/API2/Network/SDN/Zones.pm       | 6 ------
 5 files changed, 3 insertions(+), 18 deletions(-)

diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
index 38af746..175f76f 100644
--- a/PVE/API2/Network/SDN.pm
+++ b/PVE/API2/Network/SDN.pm
@@ -10,6 +10,7 @@ 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;
@@ -111,6 +112,8 @@ __PACKAGE__->register_method ({
         my $rpcenv = PVE::RPCEnvironment::get();
         my $authuser = $rpcenv->get_user();
 
+	PVE::Network::SDN::increase_version();
+
         my $code = sub {
             $rpcenv->{type} = 'priv'; # to start tasks in background
 	    PVE::Cluster::check_cfs_quorum();
diff --git a/PVE/API2/Network/SDN/Controllers.pm b/PVE/API2/Network/SDN/Controllers.pm
index 9bc3075..919d343 100644
--- a/PVE/API2/Network/SDN/Controllers.pm
+++ b/PVE/API2/Network/SDN/Controllers.pm
@@ -152,8 +152,6 @@ __PACKAGE__->register_method ({
 
 		PVE::Network::SDN::Controllers::write_config($controller_cfg);
 
-		PVE::Network::SDN::increase_version();
-
 	    }, "create sdn controller object failed");
 
 	return undef;
@@ -196,8 +194,6 @@ __PACKAGE__->register_method ({
 
 	    PVE::Network::SDN::Controllers::write_config($controller_cfg);
 
-	    PVE::Network::SDN::increase_version();
-
 
 	    }, "update sdn controller object failed");
 
@@ -243,8 +239,6 @@ __PACKAGE__->register_method ({
 		delete $cfg->{ids}->{$id};
 		PVE::Network::SDN::Controllers::write_config($cfg);
 
-		PVE::Network::SDN::increase_version();
-
 	    }, "delete sdn controller object failed");
 
 
diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index d18cf90..d9cb9e9 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -134,7 +134,6 @@ __PACKAGE__->register_method ({
 		$cfg->{ids}->{$id} = $opts;
 		PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
 		PVE::Network::SDN::Subnets::write_config($cfg);
-		PVE::Network::SDN::increase_version();
 
 	    }, "create sdn subnet object failed");
 
@@ -170,7 +169,6 @@ __PACKAGE__->register_method ({
 
 	    PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
 	    PVE::Network::SDN::Subnets::write_config($cfg);
-	    PVE::Network::SDN::increase_version();
 
 	    }, "update sdn subnet object failed");
 
@@ -213,7 +211,6 @@ __PACKAGE__->register_method ({
 		delete $cfg->{ids}->{$id};
 		PVE::Network::SDN::SubnetPlugin->on_delete_hook($id, $subnets_cfg, $vnets_cfg);
 		PVE::Network::SDN::Subnets::write_config($cfg);
-		PVE::Network::SDN::increase_version();
 
 	    }, "delete sdn subnet object failed");
 
diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
index 58ec21f..b585c9c 100644
--- a/PVE/API2/Network/SDN/Vnets.pm
+++ b/PVE/API2/Network/SDN/Vnets.pm
@@ -138,7 +138,6 @@ __PACKAGE__->register_method ({
 	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg);
 
 	    PVE::Network::SDN::Vnets::write_config($cfg);
-	    PVE::Network::SDN::increase_version();
 
 	}, "create sdn vnet object failed");
 
@@ -181,7 +180,6 @@ __PACKAGE__->register_method ({
 	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg);
 
 	    PVE::Network::SDN::Vnets::write_config($cfg);
-	    PVE::Network::SDN::increase_version();
 
 	}, "update sdn vnet object failed");
 
@@ -221,7 +219,6 @@ __PACKAGE__->register_method ({
 
 	    delete $cfg->{ids}->{$id};
 	    PVE::Network::SDN::Vnets::write_config($cfg);
-	    PVE::Network::SDN::increase_version();
 
 	}, "delete sdn vnet object failed");
 
diff --git a/PVE/API2/Network/SDN/Zones.pm b/PVE/API2/Network/SDN/Zones.pm
index f629f43..a37df3d 100644
--- a/PVE/API2/Network/SDN/Zones.pm
+++ b/PVE/API2/Network/SDN/Zones.pm
@@ -161,8 +161,6 @@ __PACKAGE__->register_method ({
 
 		PVE::Network::SDN::Zones::write_config($zone_cfg);
 
-		PVE::Network::SDN::increase_version();
-
 	    }, "create sdn zone object failed");
 
 	return undef;
@@ -206,8 +204,6 @@ __PACKAGE__->register_method ({
 
 	    PVE::Network::SDN::Zones::write_config($zone_cfg);
 
-	    PVE::Network::SDN::increase_version();
-
 	    }, "update sdn zone object failed");
 
 	return undef;
@@ -252,8 +248,6 @@ __PACKAGE__->register_method ({
 		delete $cfg->{ids}->{$id};
 		PVE::Network::SDN::Zones::write_config($cfg);
 
-		PVE::Network::SDN::increase_version();
-
 	    }, "delete sdn zone object failed");
 
 
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 10/35] add ipams plugins
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (8 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 09/35] api2: increase version on apply/reload only Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 11/35] add pve internal ipam plugin Alexandre Derumier
                   ` (24 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN.pm                |   7 +
 PVE/API2/Network/SDN/Ipams.pm          | 241 +++++++++++++++++++++++++
 PVE/API2/Network/SDN/Makefile          |   2 +-
 PVE/API2/Network/SDN/Subnets.pm        |  47 ++++-
 PVE/Network/SDN/Ipams.pm               |  78 ++++++++
 PVE/Network/SDN/Ipams/Makefile         |   8 +
 PVE/Network/SDN/Ipams/NetboxPlugin.pm  | 169 +++++++++++++++++
 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 189 +++++++++++++++++++
 PVE/Network/SDN/Ipams/Plugin.pm        | 127 +++++++++++++
 PVE/Network/SDN/Makefile               |   3 +-
 PVE/Network/SDN/SubnetPlugin.pm        |   5 +-
 PVE/Network/SDN/Vnets.pm               |  25 +++
 12 files changed, 895 insertions(+), 6 deletions(-)
 create mode 100644 PVE/API2/Network/SDN/Ipams.pm
 create mode 100644 PVE/Network/SDN/Ipams.pm
 create mode 100644 PVE/Network/SDN/Ipams/Makefile
 create mode 100644 PVE/Network/SDN/Ipams/NetboxPlugin.pm
 create mode 100644 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
 create mode 100644 PVE/Network/SDN/Ipams/Plugin.pm

diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
index 175f76f..6055fe5 100644
--- a/PVE/API2/Network/SDN.pm
+++ b/PVE/API2/Network/SDN.pm
@@ -16,6 +16,7 @@ use PVE::API2::Network::SDN::Controllers;
 use PVE::API2::Network::SDN::Vnets;
 use PVE::API2::Network::SDN::Zones;
 use PVE::API2::Network::SDN::Subnets;
+use PVE::API2::Network::SDN::Ipams;
 
 use base qw(PVE::RESTHandler);
 
@@ -39,6 +40,11 @@ __PACKAGE__->register_method ({
     path => 'subnets',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Ipams",
+    path => 'ipams',
+});
+
 __PACKAGE__->register_method({
     name => 'index',
     path => '',
@@ -69,6 +75,7 @@ __PACKAGE__->register_method({
 	    { id => 'zones' },
 	    { id => 'controllers' },
 	    { id => 'subnets' },
+	    { id => 'ipams' },
 	];
 
 	return $res;
diff --git a/PVE/API2/Network/SDN/Ipams.pm b/PVE/API2/Network/SDN/Ipams.pm
new file mode 100644
index 0000000..f8665a1
--- /dev/null
+++ b/PVE/API2/Network/SDN/Ipams.pm
@@ -0,0 +1,241 @@
+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::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;
+
+		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};
+	    }
+
+	    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
index 59626fa..1117dfa 100644
--- a/PVE/API2/Network/SDN/Makefile
+++ b/PVE/API2/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm
+SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index d9cb9e9..b60db3d 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -6,10 +6,13 @@ 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::Ipams;
+use PVE::Network::SDN::Ipams::Plugin;
 
 use Storable qw(dclone);
 use PVE::JSONSchema qw(get_standard_option);
@@ -133,6 +136,17 @@ __PACKAGE__->register_method ({
 
 		$cfg->{ids}->{$id} = $opts;
 		PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
+
+		my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+		my $ipam = $cfg->{ids}->{$id}->{ipam};
+		if ($ipam) {
+		    raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
+		    my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+		    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+		    $plugin->add_subnet($plugin_config, $id, $cfg->{ids}->{$id});
+		    $plugin->add_ip($plugin_config, $id, $opts->{gateway}, 1) if $opts->{gateway};
+		}
+
 		PVE::Network::SDN::Subnets::write_config($cfg);
 
 	    }, "create sdn subnet object failed");
@@ -161,6 +175,7 @@ __PACKAGE__->register_method ({
 	 sub {
 
 	    my $cfg = PVE::Network::SDN::Subnets::config();
+	    my $scfg = &$api_sdn_subnets_config($cfg, $id);
 
 	    PVE::SectionConfig::assert_if_modified($cfg, $digest);
 
@@ -168,6 +183,24 @@ __PACKAGE__->register_method ({
 	    $cfg->{ids}->{$id} = $opts;
 
 	    PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
+
+            my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+            my $ipam = $cfg->{ids}->{$id}->{ipam};
+	    if ($ipam) {
+		raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
+		my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+		my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+		$plugin->add_subnet($plugin_config, $id, $cfg->{ids}->{$id});
+
+		if($opts->{gateway} && $scfg->{gateway} && $opts->{gateway} ne $scfg->{gateway}) {
+		    $plugin->del_ip($plugin_config, $scfg->{gateway});
+		}
+		if (!defined($opts->{gateway}) && $scfg->{gateway}) {
+		    $plugin->del_ip($plugin_config, $scfg->{gateway});
+		} 
+		$plugin->add_ip($plugin_config, $id, $opts->{gateway}, 1) if $opts->{gateway};
+	    }
+
 	    PVE::Network::SDN::Subnets::write_config($cfg);
 
 	    }, "update sdn subnet object failed");
@@ -200,7 +233,6 @@ __PACKAGE__->register_method ({
 
         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);
@@ -208,8 +240,19 @@ __PACKAGE__->register_method ({
 		my $subnets_cfg = PVE::Network::SDN::Subnets::config();
 		my $vnets_cfg = PVE::Network::SDN::Vnets::config();
 
-		delete $cfg->{ids}->{$id};
 		PVE::Network::SDN::SubnetPlugin->on_delete_hook($id, $subnets_cfg, $vnets_cfg);
+
+		my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+		my $ipam = $cfg->{ids}->{$id}->{ipam};
+		if ($ipam) {
+		    raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
+		    my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+		    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+		    $plugin->del_subnet($plugin_config, $id, $scfg);
+		}
+
+		delete $cfg->{ids}->{$id};
+
 		PVE::Network::SDN::Subnets::write_config($cfg);
 
 	    }, "delete sdn subnet object failed");
diff --git a/PVE/Network/SDN/Ipams.pm b/PVE/Network/SDN/Ipams.pm
new file mode 100644
index 0000000..3d33632
--- /dev/null
+++ b/PVE/Network/SDN/Ipams.pm
@@ -0,0 +1,78 @@
+package PVE::Network::SDN::Ipams;
+
+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::Ipams::NetboxPlugin;
+use PVE::Network::SDN::Ipams::PhpIpamPlugin;
+use PVE::Network::SDN::Ipams::Plugin;
+
+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");
+    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) ];
+}
+
+sub next_free_ip {
+    my ($subnetid, $subnet) = @_;
+
+    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+    my $ipamid = $subnet->{ipam};
+    return if !$ipamid;
+
+    my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
+    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+    my $ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
+    return $ip;
+}
+1;
+
diff --git a/PVE/Network/SDN/Ipams/Makefile b/PVE/Network/SDN/Ipams/Makefile
new file mode 100644
index 0000000..884c47a
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/Makefile
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm PhpIpamPlugin.pm NetboxPlugin.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
new file mode 100644
index 0000000..ccc1184
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -0,0 +1,169 @@
+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) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    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 ($network, $mask) = split(/-/, $subnetid);
+
+	my $params = { prefix => $cidr };
+
+	eval {
+		my $result = PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/prefixes/", $headers, $params);
+		$subnet->{ipamid} = $result->{id} if defined($result->{id});
+	};
+	if ($@) {
+	    die "error add subnet to ipam: $@";
+	}
+    }
+   
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    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;
+    #fixme: check that prefix is empty exluding gateway, before delete
+
+    PVE::Network::SDN::Ipams::NetboxPlugin::del_ip($class, $plugin_config, $gateway) if $gateway;
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers);
+    };
+    if ($@) {
+	die "error deleting subnet from ipam: $@";
+    }
+
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+
+    my ($network, $mask) = split(/-/, $subnetid);
+    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"];
+
+    my $params = { address => "$ip/$mask" };
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/ip-addresses/", $headers, $params);
+    };
+
+    if ($@) {
+	die "error add subnet ip to ipam: ip already exist: $@";
+    }
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    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);
+
+    my $params = {};
+
+    my $ip = undef;
+    eval {
+	my $result = PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/prefixes/$internalid/available-ips/", $headers, $params);
+	$ip = $result->{address};
+    };
+
+    if ($@) {
+	die "can't find free ip in subnet $cidr: $@";
+    }
+
+    return $ip;
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $ip) = @_;
+
+    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::Ipams::Plugin::api_request("DELETE", "$url/ipam/ip-addresses/$ip_id/", $headers);
+    };
+    if ($@) {
+	die "error delete ip $ip";
+    }
+}
+
+#helpers
+
+sub get_prefix_id {
+    my ($url, $cidr, $headers) = @_;
+
+    my $result = PVE::Network::SDN::Ipams::Plugin::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::Ipams::Plugin::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers);
+    my $data = @{$result->{results}}[0];
+    my $ip_id = $data->{id};
+    return $ip_id;
+}
+
+
+1;
+
+
diff --git a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
new file mode 100644
index 0000000..7380bf3
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
@@ -0,0 +1,189 @@
+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) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    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_internalid($url, $cidr, $headers);
+
+    #create subnet
+    if (!$internalid) {
+	my ($network, $mask) = split(/-/, $subnetid);
+
+	my $params = { subnet => $network,
+		   mask => $mask,
+		   sectionId => $section,
+		  };
+
+	eval {
+		PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/subnets/", $headers, $params);
+	};
+	if ($@) {
+	    die "error add subnet to ipam: $@";
+	}
+    }
+
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    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_internalid($url, $cidr, $headers);
+    return if !$internalid;
+
+    #fixme: check that prefix is empty exluding gateway, before delete
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/subnets/$internalid", $headers);
+    };
+    if ($@) {
+	die "error deleting subnet from ipam: $@";
+    }
+
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    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_internalid($url, $cidr, $headers);
+
+    my $params = { ip => $ip,
+		   subnetId => $internalid,
+		   is_gateway => $is_gateway,
+		  };
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/addresses/", $headers, $params);
+    };
+
+    if ($@) {
+	die "error add subnet ip to ipam: ip $ip already exist: $@";
+    }
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config, $subnetid, $subnet, $internalid, $hostname) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    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];
+
+    $internalid = get_internalid($url, $cidr, $headers) if !$internalid;
+
+    my $params = {};
+
+    my $ip = undef;
+    eval {
+	my $result = PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/addresses/first_free/$internalid/", $headers, $params);
+	$ip = $result->{data};
+    };
+
+    if ($@) {
+        die "can't find free ip in subnet $cidr: $@";
+    }
+
+    my ($network, $mask) = split(/-/, $subnetid);
+    return "$ip/$mask";
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $ip) = @_;
+
+    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::Ipams::Plugin::api_request("DELETE", "$url/addresses/$ip_id", $headers);
+    };
+    if ($@) {
+	die "error delete ip $ip";
+    }
+}
+
+
+#helpers
+
+sub get_internalid {
+    my ($url, $cidr, $headers) = @_;
+
+    my $result = PVE::Network::SDN::Ipams::Plugin::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::Ipams::Plugin::api_request("GET", "$url/addresses/search/$ip", $headers);
+    my $data = @{$result->{data}}[0];
+    my $ip_id = $data->{id};
+    return $ip_id;
+}
+
+1;
+
+
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
new file mode 100644
index 0000000..8a44090
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -0,0 +1,127 @@
+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) = @_;
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $internalid, $ip, $hostname, $is_gateway) = @_;
+
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config) = @_;
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $ip) = @_;
+}
+
+
+#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 from_json($raw) if $raw ne '';
+}
+
+1;
diff --git a/PVE/Network/SDN/Makefile b/PVE/Network/SDN/Makefile
index 59f8c34..fb68856 100644
--- a/PVE/Network/SDN/Makefile
+++ b/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
@@ -8,4 +8,5 @@ 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
 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index ea47684..6224065 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -82,7 +82,7 @@ sub properties {
             type => 'string',
             description => "Develop some dns registrations plugins (powerdns,...)",
         },
-        ipam_driver => {
+        ipam => {
             type => 'string',
             description => "use a specific ipam",
         },
@@ -98,7 +98,7 @@ sub options {
 	snat => { optional => 1 },
 	dhcp => { optional => 1 },
 	dns_driver => { optional => 1 },
-	ipam_driver => { optional => 1 },
+	ipam => { optional => 1 },
     };
 }
 
@@ -110,6 +110,7 @@ sub on_update_hook {
 
     my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
     raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway);
+
 }
 
 sub on_delete_hook {
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 073ab80..d474037 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -4,6 +4,8 @@ use strict;
 use warnings;
 
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::Ipams;
 
 use PVE::Network::SDN::VnetPlugin;
 PVE::Network::SDN::VnetPlugin->register();
@@ -52,4 +54,27 @@ sub get_vnet {
     return $vnet;
 }
 
+sub get_next_free_ip {
+    my ($vnetid) = @_;
+
+    my $vnets_cfg = PVE::Network::SDN::Vnets::config();
+    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+    my $vnet = $vnets_cfg->{ids}->{$vnetid};
+    my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+    my $ip = undef;
+    foreach my $s (@subnets) {
+        my $subnetid = $s =~ s/\//-/r;
+        my $subnet = $subnets_cfg->{ids}->{$subnetid};
+        if ($subnet && $subnet->{ipam}) {
+            eval {
+                $ip = PVE::Network::SDN::Ipams::next_free_ip($subnetid, $subnet);
+            };
+            warn $@ if $@;
+        }
+        last if $ip;
+    }
+    die "can't find any ip" if !$ip;
+    return $ip;
+}
+
 1;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 11/35] add pve internal ipam plugin
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (9 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 10/35] add ipams plugins Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 12/35] vnets: find_free_ip : add ipversion detection Alexandre Derumier
                   ` (23 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Ipams.pm          |   1 +
 PVE/API2/Network/SDN/Subnets.pm        |   4 +-
 PVE/Network/SDN/Ipams.pm               |   2 +
 PVE/Network/SDN/Ipams/Makefile         |   2 +-
 PVE/Network/SDN/Ipams/NetboxPlugin.pm  |   4 +-
 PVE/Network/SDN/Ipams/PVEPlugin.pm     | 166 +++++++++++++++++++++++++
 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm |   2 +-
 PVE/Network/SDN/Ipams/Plugin.pm        |   2 +-
 debian/control                         |   1 +
 9 files changed, 177 insertions(+), 7 deletions(-)
 create mode 100644 PVE/Network/SDN/Ipams/PVEPlugin.pm

diff --git a/PVE/API2/Network/SDN/Ipams.pm b/PVE/API2/Network/SDN/Ipams.pm
index f8665a1..0d567c8 100644
--- a/PVE/API2/Network/SDN/Ipams.pm
+++ b/PVE/API2/Network/SDN/Ipams.pm
@@ -9,6 +9,7 @@ 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;
 
diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index b60db3d..094401c 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -193,10 +193,10 @@ __PACKAGE__->register_method ({
 		$plugin->add_subnet($plugin_config, $id, $cfg->{ids}->{$id});
 
 		if($opts->{gateway} && $scfg->{gateway} && $opts->{gateway} ne $scfg->{gateway}) {
-		    $plugin->del_ip($plugin_config, $scfg->{gateway});
+		    $plugin->del_ip($plugin_config, $id, $scfg->{gateway});
 		}
 		if (!defined($opts->{gateway}) && $scfg->{gateway}) {
-		    $plugin->del_ip($plugin_config, $scfg->{gateway});
+		    $plugin->del_ip($plugin_config, $id, $scfg->{gateway});
 		} 
 		$plugin->add_ip($plugin_config, $id, $opts->{gateway}, 1) if $opts->{gateway};
 	    }
diff --git a/PVE/Network/SDN/Ipams.pm b/PVE/Network/SDN/Ipams.pm
index 3d33632..b634020 100644
--- a/PVE/Network/SDN/Ipams.pm
+++ b/PVE/Network/SDN/Ipams.pm
@@ -10,10 +10,12 @@ 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();
diff --git a/PVE/Network/SDN/Ipams/Makefile b/PVE/Network/SDN/Ipams/Makefile
index 884c47a..4e7d65f 100644
--- a/PVE/Network/SDN/Ipams/Makefile
+++ b/PVE/Network/SDN/Ipams/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Plugin.pm PhpIpamPlugin.pm NetboxPlugin.pm
+SOURCES=Plugin.pm PhpIpamPlugin.pm NetboxPlugin.pm PVEPlugin.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
index ccc1184..c25f451 100644
--- a/PVE/Network/SDN/Ipams/NetboxPlugin.pm
+++ b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -68,7 +68,7 @@ sub del_subnet {
     return if !$internalid;
     #fixme: check that prefix is empty exluding gateway, before delete
 
-    PVE::Network::SDN::Ipams::NetboxPlugin::del_ip($class, $plugin_config, $gateway) if $gateway;
+    PVE::Network::SDN::Ipams::NetboxPlugin::del_ip($class, $plugin_config, $subnetid, $gateway) if $gateway;
 
     eval {
 	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers);
@@ -125,7 +125,7 @@ sub add_next_freeip {
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $ip) = @_;
 
     return if !$ip;
 
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
new file mode 100644
index 0000000..0dfc8a4
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -0,0 +1,166 @@
+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 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 = $subnetid =~ s/-/\//r;
+    my $gateway = $subnet->{gateway};
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+	my $config = read_db();
+	#create subnet
+	if (!defined($config->{subnets}->{$cidr})) {
+	    $config->{subnets}->{$cidr}->{ips} = {};
+	    write_db($config);
+	}
+    });
+    die "$@" if $@;
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+
+	my $db = read_db();
+	my $ips = $db->{subnets}->{$cidr}->{ips};
+	die "can't delete subnet, not empty" if keys %{$ips} > 0;
+	delete $db->{subnets}->{$cidr};
+	write_db($db);
+    });
+    die "$@" if $@;
+
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+
+	my $db = read_db();
+	my $s = $db->{subnets}->{$cidr};
+
+	die "ip already exist" if defined($s->{ips}->{$ip});
+
+	#verify that ip is valid for this subnet
+	$s->{ips}->{$ip} = 1;
+	write_db($db);
+    });
+    die "$@" if $@;
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $freeip = undef;
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+
+	my $db = read_db();
+	my $s = $db->{subnets}->{$cidr};
+
+	my $iplist = new Net::IP($cidr);
+
+	while(1) {
+	    my $ip = $iplist->ip();
+	    ++$iplist;
+	    print "nextip: $ip\n";
+	    next if defined($s->{ips}->{$ip});
+	    $freeip = $ip;
+	    last;
+	}
+
+	die "can't find free ip in subnet $cidr" if !$freeip;
+  
+	$s->{ips}->{$freeip} = 1;
+	write_db($db);
+    });
+    die "$@" if $@;
+
+    my ($network, $mask) = split(/-/, $subnetid);
+    return "$freeip/$mask";
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $subnetid, $ip) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+
+    cfs_lock_file($ipamdb_file, undef, sub {
+
+	my $db = read_db();
+	my $s = $db->{subnets}->{$cidr};
+	return if !$ip;
+
+	die "ip does not exist in pam" if !defined($s->{ips}->{$ip});
+	delete $s->{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
index 7380bf3..d7ba3ed 100644
--- a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
@@ -145,7 +145,7 @@ sub add_next_freeip {
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $ip) = @_;
 
     return if !$ip;
 
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
index 8a44090..fc736b8 100644
--- a/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -84,7 +84,7 @@ sub add_next_freeip {
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $ip) = @_;
 }
 
 
diff --git a/debian/control b/debian/control
index 8b67d74..c54f8bc 100644
--- a/debian/control
+++ b/debian/control
@@ -17,6 +17,7 @@ Depends: libpve-common-perl (>= 5.0-45),
          perl (>= 5.6.0-16),
          pve-cluster (>= 5.0-32),
          libnet-subnet-perl,
+         libnet-ip-perl,
          ${misc:Depends},
          ${perl:Depends},
 Recommends: frr-pythontools, ifupdown2
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 12/35] vnets: find_free_ip : add ipversion detection
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (10 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 11/35] add pve internal ipam plugin Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 13/35] vnets: add add_ip Alexandre Derumier
                   ` (22 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Vnets.pm | 33 +++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)

diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index d474037..0de3fd5 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -4,6 +4,7 @@ use strict;
 use warnings;
 
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use Net::IP;
 use PVE::Network::SDN::Subnets;
 use PVE::Network::SDN::Ipams;
 
@@ -55,26 +56,34 @@ sub get_vnet {
 }
 
 sub get_next_free_ip {
-    my ($vnetid) = @_;
+    my ($vnetid, $ipversion) = @_;
 
+    $ipversion = 4 if !$ipversion;
     my $vnets_cfg = PVE::Network::SDN::Vnets::config();
     my $subnets_cfg = PVE::Network::SDN::Subnets::config();
     my $vnet = $vnets_cfg->{ids}->{$vnetid};
     my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
     my $ip = undef;
+    my $subnet = undef;
+    my $subnetcount = 0;
     foreach my $s (@subnets) {
-        my $subnetid = $s =~ s/\//-/r;
-        my $subnet = $subnets_cfg->{ids}->{$subnetid};
-        if ($subnet && $subnet->{ipam}) {
-            eval {
-                $ip = PVE::Network::SDN::Ipams::next_free_ip($subnetid, $subnet);
-            };
-            warn $@ if $@;
-        }
-        last if $ip;
+	my $subnetid = $s =~ s/\//-/r;
+	my ($network, $mask) = split(/-/, $subnetid);
+	next if $ipversion != Net::IP::ip_get_version($network);
+	$subnetcount++;
+	$subnet = $subnets_cfg->{ids}->{$subnetid};
+	if ($subnet && $subnet->{ipam}) {
+	    eval {
+		$ip = PVE::Network::SDN::Ipams::next_free_ip($subnetid, $subnet);
+	    };
+	    warn $@ if $@;
+	}
+	last if $ip;
     }
-    die "can't find any ip" if !$ip;
-    return $ip;
+    die "can't find any free ip" if !$ip && $subnetcount > 0;
+
+    $subnet->{freeip} = $ip;
+    return $subnet;
 }
 
 1;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 13/35] vnets: add add_ip
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (11 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 12/35] vnets: find_free_ip : add ipversion detection Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 14/35] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
                   ` (21 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Vnets.pm | 33 +++++++++++++++++++++++++++++++++
 1 file changed, 33 insertions(+)

diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 0de3fd5..07bc9ff 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -4,7 +4,9 @@ use strict;
 use warnings;
 
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Exception qw(raise_param_exc raise_perm_exc raise);
 use Net::IP;
+use Net::Subnet qw(subnet_matcher);
 use PVE::Network::SDN::Subnets;
 use PVE::Network::SDN::Ipams;
 
@@ -86,4 +88,35 @@ sub get_next_free_ip {
     return $subnet;
 }
 
+sub add_ip {
+    my ($vnetid, $cidr, $name) = @_;
+
+    my $vnets_cfg = PVE::Network::SDN::Vnets::config();
+    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+    my $vnet = $vnets_cfg->{ids}->{$vnetid};
+    my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+    my $subnet = undef;
+    my $subnetid = undef;
+    my ($ip, $mask) = split(/\//, $cidr);
+
+    foreach my $s (@subnets) {
+	my $subnet_matcher = subnet_matcher($s);
+	next if !$subnet_matcher->($ip);
+	$subnetid = $s =~ s/\//-/r;
+	$subnet = $subnets_cfg->{ids}->{$subnetid};
+	last;
+    }
+    raise_param_exc({'ip' =>  "can't find any subnet attached to vnet $vnetid for ip $ip"}) if !$subnet;
+    return if !$subnet->{ipam};
+
+    eval {
+	my $ipamid = $subnet->{ipam};
+	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->add_ip($plugin_config, $subnetid, $ip);
+    };
+    raise_param_exc({'ip' =>  $@}) if $@;
+}
+
 1;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 14/35] vnets: add del_ip + rework add_ip/find_free_ip
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (12 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 13/35] vnets: add add_ip Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 15/35] add dns plugin Alexandre Derumier
                   ` (20 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Ipams.pm   | 12 --------
 PVE/Network/SDN/Subnets.pm | 60 ++++++++++++++++++++++++++++++++++++++
 PVE/Network/SDN/Vnets.pm   | 47 ++++++++++-------------------
 3 files changed, 75 insertions(+), 44 deletions(-)

diff --git a/PVE/Network/SDN/Ipams.pm b/PVE/Network/SDN/Ipams.pm
index b634020..a979d46 100644
--- a/PVE/Network/SDN/Ipams.pm
+++ b/PVE/Network/SDN/Ipams.pm
@@ -64,17 +64,5 @@ sub complete_sdn_vnet {
     return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Vnets::sdn_ipams_ids($cfg) ];
 }
 
-sub next_free_ip {
-    my ($subnetid, $subnet) = @_;
-
-    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-    my $ipamid = $subnet->{ipam};
-    return if !$ipamid;
-
-    my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
-    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-    my $ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
-    return $ip;
-}
 1;
 
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 454a9cf..3ce2d44 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -3,8 +3,10 @@ package PVE::Network::SDN::Subnets;
 use strict;
 use warnings;
 
+use Net::Subnet qw(subnet_matcher);
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
 
+use PVE::Network::SDN::Ipams;
 use PVE::Network::SDN::SubnetPlugin;
 PVE::Network::SDN::SubnetPlugin->register();
 PVE::Network::SDN::SubnetPlugin->init();
@@ -52,4 +54,62 @@ sub get_subnet {
     return $subnet;
 }
 
+sub find_ip_subnet {
+    my ($ip, $subnetslist) = @_;
+
+    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+    my @subnets = PVE::Tools::split_list($subnetslist) if $subnetslist;
+
+    my $subnet = undef;
+    my $subnetid = undef;
+
+    foreach my $s (@subnets) {
+        my $subnet_matcher = subnet_matcher($s);
+        next if !$subnet_matcher->($ip);
+        $subnetid = $s =~ s/\//-/r;
+        $subnet = $subnets_cfg->{ids}->{$subnetid};
+        last;
+    }
+    die  "can't find any subnet for ip $ip" if !$subnet;
+
+    return ($subnetid, $subnet);
+}
+
+sub next_free_ip {
+    my ($subnetid, $subnet) = @_;
+
+    my $ipamid = $subnet->{ipam};
+    return 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});
+    my $ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
+    return $ip;
+}
+
+sub add_ip {
+    my ($subnetid, $subnet, $ip) = @_;
+
+    my $ipamid = $subnet->{ipam};
+    return 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->add_ip($plugin_config, $subnetid, $ip);
+}
+
+sub del_ip {
+    my ($subnetid, $subnet, $ip) = @_;
+
+    my $ipamid = $subnet->{ipam};
+    return 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, $ip);
+}
+
 1;
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 07bc9ff..6ea3a9a 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -4,11 +4,8 @@ use strict;
 use warnings;
 
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
-use PVE::Exception qw(raise_param_exc raise_perm_exc raise);
 use Net::IP;
-use Net::Subnet qw(subnet_matcher);
 use PVE::Network::SDN::Subnets;
-use PVE::Network::SDN::Ipams;
 
 use PVE::Network::SDN::VnetPlugin;
 PVE::Network::SDN::VnetPlugin->register();
@@ -58,12 +55,10 @@ sub get_vnet {
 }
 
 sub get_next_free_ip {
-    my ($vnetid, $ipversion) = @_;
+    my ($vnet, $ipversion) = @_;
 
     $ipversion = 4 if !$ipversion;
-    my $vnets_cfg = PVE::Network::SDN::Vnets::config();
     my $subnets_cfg = PVE::Network::SDN::Subnets::config();
-    my $vnet = $vnets_cfg->{ids}->{$vnetid};
     my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
     my $ip = undef;
     my $subnet = undef;
@@ -76,7 +71,7 @@ sub get_next_free_ip {
 	$subnet = $subnets_cfg->{ids}->{$subnetid};
 	if ($subnet && $subnet->{ipam}) {
 	    eval {
-		$ip = PVE::Network::SDN::Ipams::next_free_ip($subnetid, $subnet);
+		$ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet);
 	    };
 	    warn $@ if $@;
 	}
@@ -84,39 +79,27 @@ sub get_next_free_ip {
     }
     die "can't find any free ip" if !$ip && $subnetcount > 0;
 
-    $subnet->{freeip} = $ip;
-    return $subnet;
+    return $ip;
 }
 
 sub add_ip {
-    my ($vnetid, $cidr, $name) = @_;
+    my ($vnet, $cidr, $name) = @_;
 
-    my $vnets_cfg = PVE::Network::SDN::Vnets::config();
-    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
-    my $vnet = $vnets_cfg->{ids}->{$vnetid};
-    my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
-    my $subnet = undef;
-    my $subnetid = undef;
     my ($ip, $mask) = split(/\//, $cidr);
+    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
+    return if !$subnet->{ipam};
 
-    foreach my $s (@subnets) {
-	my $subnet_matcher = subnet_matcher($s);
-	next if !$subnet_matcher->($ip);
-	$subnetid = $s =~ s/\//-/r;
-	$subnet = $subnets_cfg->{ids}->{$subnetid};
-	last;
-    }
-    raise_param_exc({'ip' =>  "can't find any subnet attached to vnet $vnetid for ip $ip"}) if !$subnet;
+    PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip);
+}
+
+sub del_ip {
+    my ($vnet, $cidr) = @_;
+
+    my ($ip, $mask) = split(/\//, $cidr);
+    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
     return if !$subnet->{ipam};
 
-    eval {
-	my $ipamid = $subnet->{ipam};
-	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->add_ip($plugin_config, $subnetid, $ip);
-    };
-    raise_param_exc({'ip' =>  $@}) if $@;
+    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip);
 }
 
 1;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 15/35] add dns plugin
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (13 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 14/35] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 16/35] Fix vnet gateway for routed setup + /32 pointopoint subnet Alexandre Derumier
                   ` (19 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN.pm               |   7 +
 PVE/API2/Network/SDN/Dns.pm           | 242 ++++++++++++++++++++++++++
 PVE/API2/Network/SDN/Makefile         |   2 +-
 PVE/Network/SDN/Dns.pm                |  57 ++++++
 PVE/Network/SDN/Dns/Makefile          |   8 +
 PVE/Network/SDN/Dns/Plugin.pm         | 117 +++++++++++++
 PVE/Network/SDN/Dns/PowerdnsPlugin.pm | 201 +++++++++++++++++++++
 PVE/Network/SDN/Ipams/PVEPlugin.pm    |   1 -
 PVE/Network/SDN/Ipams/Plugin.pm       |   2 +-
 PVE/Network/SDN/Makefile              |   3 +-
 PVE/Network/SDN/SubnetPlugin.pm       |  53 ++++--
 PVE/Network/SDN/Subnets.pm            | 156 +++++++++++++++--
 PVE/Network/SDN/Vnets.pm              |  12 +-
 13 files changed, 814 insertions(+), 47 deletions(-)
 create mode 100644 PVE/API2/Network/SDN/Dns.pm
 create mode 100644 PVE/Network/SDN/Dns.pm
 create mode 100644 PVE/Network/SDN/Dns/Makefile
 create mode 100644 PVE/Network/SDN/Dns/Plugin.pm
 create mode 100644 PVE/Network/SDN/Dns/PowerdnsPlugin.pm

diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
index 6055fe5..0a5fa33 100644
--- a/PVE/API2/Network/SDN.pm
+++ b/PVE/API2/Network/SDN.pm
@@ -17,6 +17,7 @@ use PVE::API2::Network::SDN::Vnets;
 use PVE::API2::Network::SDN::Zones;
 use PVE::API2::Network::SDN::Subnets;
 use PVE::API2::Network::SDN::Ipams;
+use PVE::API2::Network::SDN::Dns;
 
 use base qw(PVE::RESTHandler);
 
@@ -45,6 +46,11 @@ __PACKAGE__->register_method ({
     path => 'ipams',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Dns",
+    path => 'dns',
+});
+
 __PACKAGE__->register_method({
     name => 'index',
     path => '',
@@ -76,6 +82,7 @@ __PACKAGE__->register_method({
 	    { id => 'controllers' },
 	    { id => 'subnets' },
 	    { id => 'ipams' },
+	    { id => 'dns' },
 	];
 
 	return $res;
diff --git a/PVE/API2/Network/SDN/Dns.pm b/PVE/API2/Network/SDN/Dns.pm
new file mode 100644
index 0000000..ea26af3
--- /dev/null
+++ b/PVE/API2/Network/SDN/Dns.pm
@@ -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($scfg->{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/Makefile b/PVE/API2/Network/SDN/Makefile
index 1117dfa..3683fa4 100644
--- a/PVE/API2/Network/SDN/Makefile
+++ b/PVE/API2/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm
+SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/PVE/Network/SDN/Dns.pm b/PVE/Network/SDN/Dns.pm
new file mode 100644
index 0000000..c2e153a
--- /dev/null
+++ b/PVE/Network/SDN/Dns.pm
@@ -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/PVE/Network/SDN/Dns/Makefile b/PVE/Network/SDN/Dns/Makefile
new file mode 100644
index 0000000..81cd2a1
--- /dev/null
+++ b/PVE/Network/SDN/Dns/Makefile
@@ -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/PVE/Network/SDN/Dns/Plugin.pm b/PVE/Network/SDN/Dns/Plugin.pm
new file mode 100644
index 0000000..baa9316
--- /dev/null
+++ b/PVE/Network/SDN/Dns/Plugin.pm
@@ -0,0 +1,117 @@
+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 JSON;
+
+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 },
+        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, $type, $zone, $reversezone, $hostname, $ip) = @_;
+}
+
+sub del_a_record {
+    my ($class, $plugin_config, $hostname, $ip) = @_;
+}
+
+sub on_update_hook {
+    my ($class, $plugin_config) = @_;
+}
+
+#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 from_json($raw) if $raw ne '';
+}
+
+1;
diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
new file mode 100644
index 0000000..8c5dd90
--- /dev/null
+++ b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
@@ -0,0 +1,201 @@
+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 base('PVE::Network::SDN::Dns::Plugin');
+
+sub type {
+    return 'powerdns';
+}
+
+sub properties {
+    return {
+	url => {
+	    type => 'string',
+	},
+	key => {
+	    type => 'string',
+	},
+    };
+}
+
+sub options {
+
+    return {
+        url => { optional => 0},
+        key => { optional => 0 },
+        ttl => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+
+sub add_a_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip) = @_;
+
+    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 $record = { content => $ip, 
+                   disabled => JSON::false, 
+		   name => $fqdn, 
+                   type => $type, 
+                   priority => 0 };
+
+    my $rrset = { name => $fqdn, 
+		  type => $type, 
+                   ttl =>  $ttl, 
+		  changetype => "REPLACE",
+		  records => [ $record ] };
+
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+	PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+	die "error add $fqdn to zone $zone: $@";
+    }
+}
+
+sub add_ptr_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip) = @_;
+
+    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 $reverseip = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
+    my $fqdn = $hostname.".".$zone.".";
+    my $type = "PTR";
+
+    my $record = { content => $fqdn, 
+                   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::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+	die "error add $reverseip to zone $zone: $@";
+    }
+}
+
+sub del_a_record {
+    my ($class, $plugin_config, $zone, $hostname) = @_;
+
+    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 = "PTR";
+
+    my $rrset = { name => $fqdn, 
+		  type => $type, 
+		  changetype => "DELETE",
+		  records => [] };
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+	PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+	die "error delete $fqdn from zone $zone: $@";
+    }
+}
+
+sub del_ptr_record {
+    my ($class, $plugin_config, $zone, $ip) = @_;
+
+    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 = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
+    my $type = "PTR";
+
+    my $rrset = { name => $reverseip, 
+		  type => $type, 
+		  changetype => "DELETE",
+		  records => [] };
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+	PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+	die "error delete $reverseip from zone $zone: $@";
+    }
+}
+
+sub verify_zone {
+    my ($class, $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];
+
+    eval {
+        PVE::Network::SDN::Dns::Plugin::api_request("GET", "$url/zones/$zone", $headers);
+    };
+
+    if ($@) {
+        die "can't read zone $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::Dns::Plugin::api_request("GET", "$url", $headers);
+    };
+
+    if ($@) {
+	die "dns api error: $@";
+    }
+}
+
+1;
+
+
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 0dfc8a4..99af0ed 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -99,7 +99,6 @@ sub add_next_freeip {
 	while(1) {
 	    my $ip = $iplist->ip();
 	    ++$iplist;
-	    print "nextip: $ip\n";
 	    next if defined($s->{ips}->{$ip});
 	    $freeip = $ip;
 	    last;
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
index fc736b8..683346c 100644
--- a/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -110,7 +110,7 @@ sub api_request {
     my $response = $ua->request($req);
     my $code = $response->code;
 
-    if ($code !~ /2(\d+)$/) {
+    if ($code !~ /^2(\d+)$/) {
         my $msg = $response->message || 'unknown';
         die "Invalid response from server: $code $msg\n";
     }
diff --git a/PVE/Network/SDN/Makefile b/PVE/Network/SDN/Makefile
index fb68856..92cfcd0 100644
--- a/PVE/Network/SDN/Makefile
+++ b/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm Dns.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
@@ -9,4 +9,5 @@ install:
 	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
index 6224065..3769e04 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -65,22 +65,25 @@ sub properties {
             type => 'string',
             description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
         },
-	#cloudinit, dhcp options
-        nameservers => {
-            type => 'string', format => 'address-list',
-            description => " dns nameserver",
+        dns => {
+            type => 'string',
+            description => "dns api server",
         },
-	#cloudinit, dhcp options
-        searchdomain => {
+        reversedns => {
             type => 'string',
+            description => "reverse dns api server",
         },
-        dhcp => {
-            type => 'boolean',
-            description => "enable dhcp for this subnet",
+        dnszone => {
+            type => 'string',
+            description => "dns domain zone  ex: mydomain.com",
         },
-        dns_driver => {
+        reversednszone => {
             type => 'string',
-            description => "Develop some dns registrations plugins (powerdns,...)",
+            description => "reverse dns zone ex: 0.168.192.in-addr.arpa",
+        },
+        dnszoneprefix => {
+            type => 'string',
+            description => "dns domain zone prefix  ex: 'adm' -> <hostname>.adm.mydomain.com",
         },
         ipam => {
             type => 'string',
@@ -93,11 +96,12 @@ sub options {
     return {
 	gateway => { optional => 1 },
 	routes => { optional => 1 },
-	nameservers => { optional => 1 },
-	searchdomain => { optional => 1 },
 	snat => { optional => 1 },
-	dhcp => { optional => 1 },
-	dns_driver => { optional => 1 },
+	dns => { optional => 1 },
+	reversedns => { optional => 1 },
+	dnszone => { optional => 1 },
+	reversednszone => { optional => 1 },
+	dnszoneprefix => { optional => 1 },
 	ipam => { optional => 1 },
     };
 }
@@ -105,12 +109,25 @@ sub options {
 sub on_update_hook {
     my ($class, $subnetid, $subnet_cfg) = @_;
 
-    my $subnet = $subnetid =~ s/-/\//r;
-    my $subnet_matcher = subnet_matcher($subnet);
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $subnet_matcher = subnet_matcher($cidr);
+
+    my $subnet = $subnet_cfg->{ids}->{$subnetid};
 
-    my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+    my $gateway = $subnet->{gateway};
+    my $dns = $subnet->{dns};
+    my $dnszone = $subnet->{dnszone};
+    my $reversedns = $subnet->{reversedns};
+    my $reversednszone = $subnet->{reversednszone};
+
+    #to: for /32 pointotoping, allow gateway outside the subnet
     raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway);
 
+    raise_param_exc({ dns => "missing dns provider"}) if $dnszone && !$dns;
+    raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
+    raise_param_exc({ reversedns => "missing dns provider"}) if $reversednszone && !$reversedns;
+    raise_param_exc({ reversednszone => "missing dns zone"}) if $reversedns && !$reversednszone;
+
 }
 
 sub on_delete_hook {
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 3ce2d44..4e8353e 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -5,8 +5,10 @@ use warnings;
 
 use Net::Subnet qw(subnet_matcher);
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use Net::IP;
 
 use PVE::Network::SDN::Ipams;
+use PVE::Network::SDN::Dns;
 use PVE::Network::SDN::SubnetPlugin;
 PVE::Network::SDN::SubnetPlugin->register();
 PVE::Network::SDN::SubnetPlugin->init();
@@ -75,41 +77,157 @@ sub find_ip_subnet {
     return ($subnetid, $subnet);
 }
 
+my $verify_dns_zone = sub {
+    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);
+};
+
+my $add_dns_record = sub {
+    my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
+
+   return if !$zone || !$dns || !$hostname || !$ip;
+
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+    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});
+    if($reverse) {
+	$plugin->add_ptr_record($plugin_config, $zone, $hostname, $ip);
+    } else {
+	$plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
+    }
+};
+
+my $del_dns_record = sub {
+    my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
+
+    return if !$zone || !$dns || !$hostname || !$ip;
+
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+    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});
+    if($reverse) {
+	$plugin->del_ptr_record($plugin_config, $zone, $ip);
+    } else {
+	$plugin->del_a_record($plugin_config, $zone, $hostname);
+    }
+};
+
 sub next_free_ip {
-    my ($subnetid, $subnet) = @_;
+    my ($subnetid, $subnet, $hostname) = @_;
+
+    my $cidr = undef;
+    my $ip = undef;
 
     my $ipamid = $subnet->{ipam};
-    return if !$ipamid;
+    my $dns = $subnet->{dns};
+    my $dnszone = $subnet->{dnszone};
+    my $reversedns = $subnet->{reversedns};
+    my $reversednszone = $subnet->{reversednszone};
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+    #verify dns zones before ipam
+    &$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});
+	$cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
+	($ip, undef) = split(/\//, $cidr);
+    }
 
-    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 $ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
-    return $ip;
+    eval {
+	#add dns
+	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+	#add reverse dns
+	&$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+    };
+    if ($@) {
+	#rollback
+	my $err = $@;
+	eval {
+	    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname)
+	};
+	die $err;
+    }
+    return $cidr;
 }
 
 sub add_ip {
-    my ($subnetid, $subnet, $ip) = @_;
+    my ($subnetid, $subnet, $ip, $hostname) = @_;
 
     my $ipamid = $subnet->{ipam};
-    return if !$ipamid;
+    my $dns = $subnet->{dns};
+    my $dnszone = $subnet->{dnszone};
+    my $reversedns = $subnet->{reversedns};
+    my $reversednszone = $subnet->{reversednszone};
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+    #verify dns zones before ipam
+    &$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->add_ip($plugin_config, $subnetid, $ip);
+    }
 
-    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->add_ip($plugin_config, $subnetid, $ip);
+    eval {
+	#add dns
+	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+	#add reverse dns
+	&$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+    };
+    if ($@) {
+	#rollback
+	my $err = $@;
+	eval {
+	    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname)
+	};
+	die $err;
+    }
 }
 
 sub del_ip {
-    my ($subnetid, $subnet, $ip) = @_;
+    my ($subnetid, $subnet, $ip, $hostname) = @_;
 
     my $ipamid = $subnet->{ipam};
-    return if !$ipamid;
+    my $dns = $subnet->{dns};
+    my $dnszone = $subnet->{dnszone};
+    my $reversedns = $subnet->{reversedns};
+    my $reversednszone = $subnet->{reversednszone};
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+    &$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, $ip);
+    }
 
-    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, $ip);
+    eval {
+	&$del_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+	&$del_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+    };
+    if ($@) {
+	warn $@;
+    }
 }
 
 1;
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 6ea3a9a..c9916b1 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -55,7 +55,7 @@ sub get_vnet {
 }
 
 sub get_next_free_ip {
-    my ($vnet, $ipversion) = @_;
+    my ($vnet, $hostname, $ipversion) = @_;
 
     $ipversion = 4 if !$ipversion;
     my $subnets_cfg = PVE::Network::SDN::Subnets::config();
@@ -71,7 +71,7 @@ sub get_next_free_ip {
 	$subnet = $subnets_cfg->{ids}->{$subnetid};
 	if ($subnet && $subnet->{ipam}) {
 	    eval {
-		$ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet);
+		$ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet, $hostname);
 	    };
 	    warn $@ if $@;
 	}
@@ -83,23 +83,23 @@ sub get_next_free_ip {
 }
 
 sub add_ip {
-    my ($vnet, $cidr, $name) = @_;
+    my ($vnet, $cidr, $hostname) = @_;
 
     my ($ip, $mask) = split(/\//, $cidr);
     my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
     return if !$subnet->{ipam};
 
-    PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip);
+    PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip, $hostname);
 }
 
 sub del_ip {
-    my ($vnet, $cidr) = @_;
+    my ($vnet, $cidr, $hostname) = @_;
 
     my ($ip, $mask) = split(/\//, $cidr);
     my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
     return if !$subnet->{ipam};
 
-    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip);
+    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname);
 }
 
 1;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 16/35] Fix vnet gateway for routed setup + /32 pointopoint subnet
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (14 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 15/35] add dns plugin Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 17/35] ipam : pveplugin : fix find_next_free_ip Alexandre Derumier
                   ` (18 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/SubnetPlugin.pm       |  6 ++++--
 PVE/Network/SDN/Zones/EvpnPlugin.pm   | 10 ++++++++--
 PVE/Network/SDN/Zones/SimplePlugin.pm | 13 +++++++++++--
 test/generateconfig.pl                |  3 ++-
 4 files changed, 25 insertions(+), 7 deletions(-)

diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 3769e04..bc66b82 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -120,8 +120,10 @@ sub on_update_hook {
     my $reversedns = $subnet->{reversedns};
     my $reversednszone = $subnet->{reversednszone};
 
-    #to: for /32 pointotoping, allow gateway outside the subnet
-    raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway);
+    my ($ip, $mask) = split(/\//, $cidr);
+
+    #for /32 pointopoint, we allow gateway outside the subnet
+    raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
 
     raise_param_exc({ dns => "missing dns provider"}) if $dnszone && !$dns;
     raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 0ebe13e..17c9262 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -77,9 +77,15 @@ sub generate_sdn_config {
     @iface_config = ();
 
     my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+    my $address = {};
     foreach my $subnet (@subnets) {
-        next if !defined($subnet_cfg->{ids}->{$subnet});
-        push @iface_config, "address $subnet_cfg->{ids}->{$subnet}->{gateway}" if $subnet_cfg->{ids}->{$subnet}->{gateway};
+	my $subnetid = $subnet =~ s/\//-/r;
+	next if !defined($subnet_cfg->{ids}->{$subnetid});
+	my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+	if ($gateway) {
+	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
+	    $address->{$gateway} = 1;
+	}
     }
 
     push @iface_config, "hwaddress $mac" if $mac;
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index 7006b13..a1733d5 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -36,9 +36,18 @@ sub generate_sdn_config {
     my @iface_config = ();
 
     my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+    my $address = {};
     foreach my $subnet (@subnets) {
-	next if !defined($subnet_cfg->{ids}->{$subnet});
-	push @iface_config, "address $subnet_cfg->{ids}->{$subnet}->{gateway}" if $subnet_cfg->{ids}->{$subnet}->{gateway};
+	my $subnetid = $subnet =~ s/\//-/r;
+	next if !defined($subnet_cfg->{ids}->{$subnetid});
+        my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+        if ($gateway) {
+	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
+	    $address->{$gateway} = 1;
+	}
+	#add route for /32 pointtopoint
+	my ($ip, $mask) = split(/\//, $subnet);
+	push @iface_config, "up ip route add $subnet dev $vnetid" if $mask == 32;
     }
 
     push @iface_config, "hwaddress $mac" if $mac;
diff --git a/test/generateconfig.pl b/test/generateconfig.pl
index 36880ba..92108ec 100644
--- a/test/generateconfig.pl
+++ b/test/generateconfig.pl
@@ -3,17 +3,18 @@ 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;
 
 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);
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 17/35] ipam : pveplugin : fix find_next_free_ip
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (15 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 16/35] Fix vnet gateway for routed setup + /32 pointopoint subnet Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 18/35] add vnet to subnets && remove subnetlist from vnet Alexandre Derumier
                   ` (17 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

skip network && broadcast address

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Ipams/PVEPlugin.pm | 11 ++++++-----
 debian/control                     |  1 +
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 99af0ed..741a680 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -6,7 +6,7 @@ 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 Net::IP;
+use NetAddr::IP;
 use Digest::SHA;
 
 use base('PVE::Network::SDN::Ipams::Plugin');
@@ -93,12 +93,13 @@ sub add_next_freeip {
 
 	my $db = read_db();
 	my $s = $db->{subnets}->{$cidr};
-
-	my $iplist = new Net::IP($cidr);
+	my $iplist = new NetAddr::IP($cidr);
+	my $broadcast = $iplist->broadcast();
 
 	while(1) {
-	    my $ip = $iplist->ip();
-	    ++$iplist;
+	    $iplist++;
+	    last if $iplist eq $broadcast;
+	    my $ip = $iplist->addr();
 	    next if defined($s->{ips}->{$ip});
 	    $freeip = $ip;
 	    last;
diff --git a/debian/control b/debian/control
index c54f8bc..b2e3614 100644
--- a/debian/control
+++ b/debian/control
@@ -18,6 +18,7 @@ Depends: libpve-common-perl (>= 5.0-45),
          pve-cluster (>= 5.0-32),
          libnet-subnet-perl,
          libnet-ip-perl,
+         libnetaddr-ip-perl,
          ${misc:Depends},
          ${perl:Depends},
 Recommends: frr-pythontools, ifupdown2
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 18/35] add vnet to subnets && remove subnetlist from vnet
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (16 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 17/35] ipam : pveplugin : fix find_next_free_ip Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 19/35] zones: evpn|simple: add snat iptables rules Alexandre Derumier
                   ` (16 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Subnets.pm       | 31 +-------------
 PVE/Network/SDN/SubnetPlugin.pm       | 59 ++++++++++++++++++++-------
 PVE/Network/SDN/Subnets.pm            | 34 +++++++++------
 PVE/Network/SDN/VnetPlugin.pm         | 23 ++++-------
 PVE/Network/SDN/Vnets.pm              | 43 ++++++++++++-------
 PVE/Network/SDN/Zones/EvpnPlugin.pm   | 10 ++---
 PVE/Network/SDN/Zones/SimplePlugin.pm | 16 ++++----
 7 files changed, 117 insertions(+), 99 deletions(-)

diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index 094401c..728b939 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -135,17 +135,7 @@ __PACKAGE__->register_method ({
 		}
 
 		$cfg->{ids}->{$id} = $opts;
-		PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
-
-		my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-		my $ipam = $cfg->{ids}->{$id}->{ipam};
-		if ($ipam) {
-		    raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
-		    my $plugin_config = $ipam_cfg->{ids}->{$ipam};
-		    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-		    $plugin->add_subnet($plugin_config, $id, $cfg->{ids}->{$id});
-		    $plugin->add_ip($plugin_config, $id, $opts->{gateway}, 1) if $opts->{gateway};
-		}
+		PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $opts);
 
 		PVE::Network::SDN::Subnets::write_config($cfg);
 
@@ -182,24 +172,7 @@ __PACKAGE__->register_method ({
 	    my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 0, 1);
 	    $cfg->{ids}->{$id} = $opts;
 
-	    PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
-
-            my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-            my $ipam = $cfg->{ids}->{$id}->{ipam};
-	    if ($ipam) {
-		raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
-		my $plugin_config = $ipam_cfg->{ids}->{$ipam};
-		my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-		$plugin->add_subnet($plugin_config, $id, $cfg->{ids}->{$id});
-
-		if($opts->{gateway} && $scfg->{gateway} && $opts->{gateway} ne $scfg->{gateway}) {
-		    $plugin->del_ip($plugin_config, $id, $scfg->{gateway});
-		}
-		if (!defined($opts->{gateway}) && $scfg->{gateway}) {
-		    $plugin->del_ip($plugin_config, $id, $scfg->{gateway});
-		} 
-		$plugin->add_ip($plugin_config, $id, $opts->{gateway}, 1) if $opts->{gateway};
-	    }
+	    PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $opts, $scfg);
 
 	    PVE::Network::SDN::Subnets::write_config($cfg);
 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index bc66b82..84303d1 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -8,6 +8,8 @@ use base qw(PVE::SectionConfig);
 use PVE::JSONSchema qw(get_standard_option);
 use PVE::Exception qw(raise raise_param_exc);
 use Net::Subnet qw(subnet_matcher);
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Ipams;
 
 PVE::Cluster::cfs_register_file('sdn/subnets.cfg',
                                  sub { __PACKAGE__->parse_config(@_); },
@@ -52,6 +54,10 @@ sub private {
 
 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",
@@ -94,6 +100,7 @@ sub properties {
 
 sub options {
     return {
+	vnet => { optional => 1 },
 	gateway => { optional => 1 },
 	routes => { optional => 1 },
 	snat => { optional => 1 },
@@ -107,44 +114,66 @@ sub options {
 }
 
 sub on_update_hook {
-    my ($class, $subnetid, $subnet_cfg) = @_;
+    my ($class, $subnetid, $subnet, $old_subnet) = @_;
 
     my $cidr = $subnetid =~ s/-/\//r;
     my $subnet_matcher = subnet_matcher($cidr);
 
-    my $subnet = $subnet_cfg->{ids}->{$subnetid};
-
+    my $vnetid = $subnet->{vnet};
     my $gateway = $subnet->{gateway};
+    my $ipam = $subnet->{ipam};
     my $dns = $subnet->{dns};
     my $dnszone = $subnet->{dnszone};
     my $reversedns = $subnet->{reversedns};
     my $reversednszone = $subnet->{reversednszone};
 
-    my ($ip, $mask) = split(/\//, $cidr);
+    my $old_gateway = $old_subnet->{gateway} if $old_subnet;
 
+    if($vnetid) {
+	my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+	raise_param_exc({ vnet => "$vnetid don't exist"}) if !$vnet;
+    }
+
+    my ($ip, $mask) = split(/\//, $cidr);
     #for /32 pointopoint, we allow gateway outside the subnet
-    raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
+    raise_param_exc({ gateway => "$gateway is not in subnet $subnetid"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
 
     raise_param_exc({ dns => "missing dns provider"}) if $dnszone && !$dns;
     raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
     raise_param_exc({ reversedns => "missing dns provider"}) if $reversednszone && !$reversedns;
     raise_param_exc({ reversednszone => "missing dns zone"}) if $reversedns && !$reversednszone;
 
+    if ($ipam) {
+	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+	my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+	raise_param_exc({ ipam => "$ipam not existing"}) if !$plugin_config;
+	my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+	$plugin->add_subnet($plugin_config, $subnetid, $subnet);
+
+	#delete on removal
+	if (!defined($gateway) && $old_gateway) {
+	    eval {
+		PVE::Network::SDN::Subnets::del_ip($subnetid, $old_subnet, $old_gateway);
+	    };
+	    warn if $@;
+	}
+        if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
+	    PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $gateway);
+	}
+
+	#delete old ip after update
+	if($gateway && $old_gateway && $gateway ne $old_gateway) {
+	    eval {
+		PVE::Network::SDN::Subnets::del_ip($subnetid, $old_subnet, $old_gateway);
+	    };
+	    warn if $@;
+	}
+    }
 }
 
 sub on_delete_hook {
     my ($class, $subnetid, $subnet_cfg, $vnet_cfg) = @_;
 
-    #verify if vnets have subnet
-    foreach my $vnetid (keys %{$vnet_cfg->{ids}}) {
-	my $vnet = $vnet_cfg->{ids}->{$vnetid};
-	my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
-	foreach my $subnet (@subnets) {
-	    my $id = $subnet =~ s/\//-/r;
-	    raise_param_exc({ subnet => "$subnet is attached to vnet $vnetid"}) if $id eq $subnetid;
-	}
-    }
-
     return;
 }
 
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 4e8353e..d20af9e 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -57,20 +57,18 @@ sub get_subnet {
 }
 
 sub find_ip_subnet {
-    my ($ip, $subnetslist) = @_;
-
-    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
-    my @subnets = PVE::Tools::split_list($subnetslist) if $subnetslist;
+    my ($ip, $subnets) = @_;
 
     my $subnet = undef;
     my $subnetid = undef;
 
-    foreach my $s (@subnets) {
-        my $subnet_matcher = subnet_matcher($s);
-        next if !$subnet_matcher->($ip);
-        $subnetid = $s =~ s/\//-/r;
-        $subnet = $subnets_cfg->{ids}->{$subnetid};
-        last;
+    foreach my $id (sort keys %{$subnets}) {
+	my $cidr = $id =~ s/-/\//r;
+	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;
 
@@ -143,8 +141,11 @@ sub next_free_ip {
 	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});
-	$cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
-	($ip, undef) = split(/\//, $cidr);
+	eval {
+	    $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
+	    ($ip, undef) = split(/\//, $cidr);
+	};
+	die $@ if $@;
     }
 
     eval {
@@ -167,6 +168,8 @@ sub next_free_ip {
 sub add_ip {
     my ($subnetid, $subnet, $ip, $hostname) = @_;
 
+    return if !$subnet;
+
     my $ipamid = $subnet->{ipam};
     my $dns = $subnet->{dns};
     my $dnszone = $subnet->{dnszone};
@@ -182,7 +185,10 @@ sub add_ip {
 	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->add_ip($plugin_config, $subnetid, $ip);
+	eval {
+	    $plugin->add_ip($plugin_config, $subnetid, $ip);
+	};
+	die $@ if $@;
     }
 
     eval {
@@ -204,6 +210,8 @@ sub add_ip {
 sub del_ip {
     my ($subnetid, $subnet, $ip, $hostname) = @_;
 
+    return if !$subnet;
+
     my $ipamid = $subnet->{ipam};
     my $dns = $subnet->{dns};
     my $dnszone = $subnet->{dnszone};
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index 6b2bcc8..47fd4d4 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -68,11 +68,6 @@ sub properties {
             description => "alias name of the vnet",
 	    optional => 1,
         },
-        subnets => {
-            type => 'string',
-            description => "Subnets list",
-	    optional => 1,
-        },
         mac => {
             type => 'string',
             description => "Anycast router mac address",
@@ -86,16 +81,21 @@ sub options {
         zone => { optional => 0},
         tag => { optional => 1},
         alias => { optional => 1 },
-        subnets => { optional => 1 },
         mac => { optional => 1 },
         vlanaware => { optional => 1 },
     };
 }
 
 sub on_delete_hook {
-    my ($class, $sdnid, $vnet_cfg) = @_;
+    my ($class, $vnetid, $vnet_cfg) = @_;
 
-    return;
+    #verify if subnets are associated
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+    my @subnetlist = ();
+    foreach my $subnetid (sort keys %{$subnets}) {
+	push @subnetlist, $subnetid;
+    }
+    raise_param_exc({ vnet => "Vnet is attached to following subnets:". join(',', @subnetlist)}) if @subnetlist > 0;
 }
 
 sub on_update_hook {
@@ -111,13 +111,6 @@ sub on_update_hook {
 	    }
 	}
     }
-
-    #verify subnet
-    my @subnets = PVE::Tools::split_list($vnet_cfg->{ids}->{$vnetid}->{subnets}) if $vnet_cfg->{ids}->{$vnetid}->{subnets};
-    foreach my $subnet (@subnets) {
-	my $id = $subnet =~ s/\//-/r;
-	raise_param_exc({ subnet => "$subnet not existing"}) if !$subnet_cfg->{ids}->{$id};
-    }
 }
 
 1;
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index c9916b1..7cec418 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -54,22 +54,35 @@ sub get_vnet {
     return $vnet;
 }
 
+sub get_subnets {
+    my ($vnetid) = @_;
+
+    my $subnets = {};
+    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+    foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
+	my $subnet = $subnets_cfg->{ids}->{$subnetid};
+	next if !$subnet->{vnet} || $subnet->{vnet} ne $vnetid;
+	$subnets->{$subnetid} = $subnet;
+    }
+    return $subnets;
+
+}
+
 sub get_next_free_ip {
-    my ($vnet, $hostname, $ipversion) = @_;
+    my ($vnetid, $hostname, $ipversion) = @_;
 
     $ipversion = 4 if !$ipversion;
-    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
-    my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
     my $ip = undef;
-    my $subnet = undef;
     my $subnetcount = 0;
-    foreach my $s (@subnets) {
-	my $subnetid = $s =~ s/\//-/r;
+
+    foreach my $subnetid (sort keys %{$subnets}) {
+        my $subnet = $subnets->{$subnetid};
 	my ($network, $mask) = split(/-/, $subnetid);
+
 	next if $ipversion != Net::IP::ip_get_version($network);
 	$subnetcount++;
-	$subnet = $subnets_cfg->{ids}->{$subnetid};
-	if ($subnet && $subnet->{ipam}) {
+	if ($subnet->{ipam}) {
 	    eval {
 		$ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet, $hostname);
 	    };
@@ -83,21 +96,23 @@ sub get_next_free_ip {
 }
 
 sub add_ip {
-    my ($vnet, $cidr, $hostname) = @_;
+    my ($vnetid, $cidr, $hostname) = @_;
+
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
 
     my ($ip, $mask) = split(/\//, $cidr);
-    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
-    return if !$subnet->{ipam};
+    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
 
     PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip, $hostname);
 }
 
 sub del_ip {
-    my ($vnet, $cidr, $hostname) = @_;
+    my ($vnetid, $cidr, $hostname) = @_;
+
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
 
     my ($ip, $mask) = split(/\//, $cidr);
-    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
-    return if !$subnet->{ipam};
+    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
 
     PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname);
 }
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 17c9262..ff25f12 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -76,12 +76,12 @@ sub generate_sdn_config {
     #vnet bridge
     @iface_config = ();
 
-    my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
     my $address = {};
-    foreach my $subnet (@subnets) {
-	my $subnetid = $subnet =~ s/\//-/r;
-	next if !defined($subnet_cfg->{ids}->{$subnetid});
-	my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+    foreach my $subnetid (sort keys %{$subnets}) {
+	my $subnet = $subnets->{$subnetid};
+	my $cidr = $subnetid =~ s/-/\//r;
+	my $gateway = $subnet->{gateway};
 	if ($gateway) {
 	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
 	    $address->{$gateway} = 1;
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index a1733d5..a4299dd 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -35,19 +35,19 @@ sub generate_sdn_config {
     # vnet bridge
     my @iface_config = ();
 
-    my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
     my $address = {};
-    foreach my $subnet (@subnets) {
-	my $subnetid = $subnet =~ s/\//-/r;
-	next if !defined($subnet_cfg->{ids}->{$subnetid});
-        my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
-        if ($gateway) {
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+    foreach my $subnetid (sort keys %{$subnets}) {
+	my $subnet = $subnets->{$subnetid};
+	my $cidr = $subnetid =~ s/-/\//r; 
+	my $gateway = $subnet->{gateway};
+	if ($gateway) {
 	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
 	    $address->{$gateway} = 1;
 	}
 	#add route for /32 pointtopoint
-	my ($ip, $mask) = split(/\//, $subnet);
-	push @iface_config, "up ip route add $subnet dev $vnetid" if $mask == 32;
+	my ($ip, $mask) = split(/\//, $cidr);
+	push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32;
     }
 
     push @iface_config, "hwaddress $mac" if $mac;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 19/35] zones: evpn|simple: add snat iptables rules
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (17 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 18/35] add vnet to subnets && remove subnetlist from vnet Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 20/35] subnet: disable route option for now and add dns domain format Alexandre Derumier
                   ` (15 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

(use snat instead masquerade for performance)

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Zones/EvpnPlugin.pm   | 18 ++++++++++++++++++
 PVE/Network/SDN/Zones/SimplePlugin.pm | 12 ++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index ff25f12..b89f4b1 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -51,6 +51,7 @@ sub generate_sdn_config {
 
     my $vrf_iface = "vrf_$zoneid";
     my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
+    my $local_node = PVE::INotify::nodename();
 
     die "missing vxlan tag" if !$tag;
     warn "vlan-aware vnet can't be enabled with evpn plugin" if $vnet->{vlanaware};
@@ -86,6 +87,23 @@ sub generate_sdn_config {
 	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
 	    $address->{$gateway} = 1;
 	}
+	if ($subnet->{snat}) {
+	    my $gatewaynodes = $controller->{'gateway-nodes'};
+	    my $is_evpn_gateway = "";
+	    foreach my $evpn_gatewaynode (PVE::Tools::split_list($gatewaynodes)) {
+		$is_evpn_gateway = 1 if $evpn_gatewaynode eq $local_node;
+	    }
+            #find outgoing interface
+            my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip('8.8.8.8');
+            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;
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index a4299dd..c58ae87 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -48,6 +48,18 @@ sub generate_sdn_config {
 	#add route for /32 pointtopoint
 	my ($ip, $mask) = split(/\//, $cidr);
 	push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32;
+	if ($subnet->{snat}) {
+	    #find outgoing interface
+	    my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip('8.8.8.8');
+	    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;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 20/35] subnet: disable route option for now and add dns domain format
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (18 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 19/35] zones: evpn|simple: add snat iptables rules Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 21/35] dns: fix reverse dns Alexandre Derumier
                   ` (14 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/SubnetPlugin.pm | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 84303d1..6237867 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -66,11 +66,11 @@ sub properties {
             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>,... ]",
-        },
+#	#cloudinit, dhcp options
+#        routes => {
+#            type => 'string',
+#            description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
+#        },
         dns => {
             type => 'string',
             description => "dns api server",
@@ -80,15 +80,15 @@ sub properties {
             description => "reverse dns api server",
         },
         dnszone => {
-            type => 'string',
+            type => 'string', format => 'dns-name',
             description => "dns domain zone  ex: mydomain.com",
         },
         reversednszone => {
-            type => 'string',
+            type => 'string', format => 'dns-name',
             description => "reverse dns zone ex: 0.168.192.in-addr.arpa",
         },
         dnszoneprefix => {
-            type => 'string',
+            type => 'string', format => 'dns-name',
             description => "dns domain zone prefix  ex: 'adm' -> <hostname>.adm.mydomain.com",
         },
         ipam => {
@@ -102,7 +102,7 @@ sub options {
     return {
 	vnet => { optional => 1 },
 	gateway => { optional => 1 },
-	routes => { optional => 1 },
+#	routes => { optional => 1 },
 	snat => { optional => 1 },
 	dns => { optional => 1 },
 	reversedns => { optional => 1 },
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 21/35] dns: fix reverse dns
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (19 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 20/35] subnet: disable route option for now and add dns domain format Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 22/35] subnets: move api to /sdn/vnet/<vnet>/subnets && make vnet option not optionnal Alexandre Derumier
                   ` (13 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Dns/PowerdnsPlugin.pm | 12 +++----
 PVE/Network/SDN/SubnetPlugin.pm       |  1 +
 PVE/Network/SDN/Subnets.pm            | 50 ++++++++++++++++++---------
 3 files changed, 40 insertions(+), 23 deletions(-)

diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
index 8c5dd90..f02c2f1 100644
--- a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
+++ b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
@@ -79,12 +79,12 @@ sub add_ptr_record {
     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 = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
-    my $fqdn = $hostname.".".$zone.".";
+    my $reverseip = join(".", reverse(split(/\./, $ip))).".in-addr.arpa.";
     my $type = "PTR";
 
-    my $record = { content => $fqdn, 
+    my $record = { content => $hostname, 
                    disabled => JSON::false, 
 		   name => $reverseip, 
                    type => $type, 
@@ -109,13 +109,13 @@ sub add_ptr_record {
 }
 
 sub del_a_record {
-    my ($class, $plugin_config, $zone, $hostname) = @_;
+    my ($class, $plugin_config, $zone, $hostname, $ip) = @_;
 
     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 = "PTR";
+    my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
 
     my $rrset = { name => $fqdn, 
 		  type => $type, 
@@ -140,7 +140,7 @@ sub del_ptr_record {
     my $key = $plugin_config->{key};
     my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
 
-    my $reverseip = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
+    my $reverseip = join(".", reverse(split(/\./, $ip))).".in-addr.arpa.";
     my $type = "PTR";
 
     my $rrset = { name => $reverseip, 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 6237867..b236c3f 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -142,6 +142,7 @@ sub on_update_hook {
     raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
     raise_param_exc({ reversedns => "missing dns provider"}) if $reversednszone && !$reversedns;
     raise_param_exc({ reversednszone => "missing dns zone"}) if $reversedns && !$reversednszone;
+    raise_param_exc({ reversedns => "missing forward dns zone"}) if $reversednszone && !$dnszone;
 
     if ($ipam) {
 	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index d20af9e..626b71d 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -87,24 +87,33 @@ my $verify_dns_zone = sub {
 };
 
 my $add_dns_record = sub {
-    my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
-
-   return if !$zone || !$dns || !$hostname || !$ip;
+    my ($zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
+    return if !$zone || !$dns || !$hostname || !$ip;
 
     $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
 
     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});
-    if($reverse) {
-	$plugin->add_ptr_record($plugin_config, $zone, $hostname, $ip);
-    } else {
-	$plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
-    }
+    $plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
+
+};
+
+my $add_dns_ptr_record = sub {
+    my ($reversezone, $zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
+
+    return if !$zone || !$reversezone || !$dns || !$hostname || !$ip;
+
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+    $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);
 };
 
 my $del_dns_record = sub {
-    my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
+    my ($zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
 
     return if !$zone || !$dns || !$hostname || !$ip;
 
@@ -113,11 +122,18 @@ my $del_dns_record = sub {
     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});
-    if($reverse) {
-	$plugin->del_ptr_record($plugin_config, $zone, $ip);
-    } else {
-	$plugin->del_a_record($plugin_config, $zone, $hostname);
-    }
+    $plugin->del_a_record($plugin_config, $zone, $hostname, $ip);
+};
+
+my $del_dns_ptr_record = sub {
+    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 next_free_ip {
@@ -152,7 +168,7 @@ sub next_free_ip {
 	#add dns
 	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
 	#add reverse dns
-	&$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+	&$add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $dnszoneprefix, $ip);
     };
     if ($@) {
 	#rollback
@@ -195,7 +211,7 @@ sub add_ip {
 	#add dns
 	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
 	#add reverse dns
-	&$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+	&$add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $dnszoneprefix, $ip);
     };
     if ($@) {
 	#rollback
@@ -231,7 +247,7 @@ sub del_ip {
 
     eval {
 	&$del_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
-	&$del_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+	&$del_dns_ptr_record($reversednszone, $reversedns, $ip);
     };
     if ($@) {
 	warn $@;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 22/35] subnets: move api to /sdn/vnet/<vnet>/subnets && make vnet option not optionnal
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (20 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 21/35] dns: fix reverse dns Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 23/35] zones: evpn : fix raise exception Alexandre Derumier
                   ` (12 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN.pm         |  7 -------
 PVE/API2/Network/SDN/Subnets.pm | 23 +++++++++++++++++------
 PVE/API2/Network/SDN/Vnets.pm   |  6 ++++++
 PVE/Network/SDN/SubnetPlugin.pm |  2 +-
 4 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
index 0a5fa33..fcda11f 100644
--- a/PVE/API2/Network/SDN.pm
+++ b/PVE/API2/Network/SDN.pm
@@ -15,7 +15,6 @@ 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::Subnets;
 use PVE::API2::Network::SDN::Ipams;
 use PVE::API2::Network::SDN::Dns;
 
@@ -36,11 +35,6 @@ __PACKAGE__->register_method ({
     path => 'controllers',
 });
 
-__PACKAGE__->register_method ({
-    subclass => "PVE::API2::Network::SDN::Subnets",
-    path => 'subnets',
-});
-
 __PACKAGE__->register_method ({
     subclass => "PVE::API2::Network::SDN::Ipams",
     path => 'ipams',
@@ -80,7 +74,6 @@ __PACKAGE__->register_method({
 	    { id => 'vnets' },
 	    { id => 'zones' },
 	    { id => 'controllers' },
-	    { id => 'subnets' },
 	    { id => 'ipams' },
 	    { id => 'dns' },
 	];
diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index 728b939..ab4117c 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -44,6 +44,10 @@ __PACKAGE__->register_method ({
     },
     parameters => {
     	additionalProperties => 0,
+        properties => {
+            vnet => get_standard_option('pve-sdn-vnet-id'),
+        },
+
     },
     returns => {
 	type => 'array',
@@ -59,6 +63,7 @@ __PACKAGE__->register_method ({
 	my $rpcenv = PVE::RPCEnvironment::get();
 	my $authuser = $rpcenv->get_user();
 
+        my $vnetid = $param->{vnet};
 
 	my $cfg = PVE::Network::SDN::Subnets::config();
 
@@ -66,9 +71,10 @@ __PACKAGE__->register_method ({
 	my $res = [];
 	foreach my $id (@sids) {
 	    my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
-	    next if !$rpcenv->check_any($authuser, "/sdn/subnets/$id", $privs, 1);
+	    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;
 	}
 
@@ -81,12 +87,13 @@ __PACKAGE__->register_method ({
     method => 'GET',
     description => "Read sdn subnet configuration.",
     permissions => {
-	check => ['perm', '/sdn/subnets/{subnet}', ['SDN.Allocate']],
+	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,
             }),
@@ -97,8 +104,11 @@ __PACKAGE__->register_method ({
 	my ($param) = @_;
 
 	my $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 &$api_sdn_subnets_config($cfg, $param->{subnet});
+	return $scfg;
     }});
 
 __PACKAGE__->register_method ({
@@ -108,7 +118,7 @@ __PACKAGE__->register_method ({
     method => 'POST',
     description => "Create a new sdn subnet object.",
     permissions => {
-	check => ['perm', '/sdn/subnets', ['SDN.Allocate']],
+	check => ['perm', '/sdn/vnets/{vnet}/subnets', ['SDN.Allocate']],
     },
     parameters => PVE::Network::SDN::SubnetPlugin->createSchema(),
     returns => { type => 'null' },
@@ -151,7 +161,7 @@ __PACKAGE__->register_method ({
     method => 'PUT',
     description => "Update sdn subnet object configuration.",
     permissions => {
-	check => ['perm', '/sdn/subnets', ['SDN.Allocate']],
+	check => ['perm', '/sdn/vnets/{vnet}/subnets', ['SDN.Allocate']],
     },
     parameters => PVE::Network::SDN::SubnetPlugin->updateSchema(),
     returns => { type => 'null' },
@@ -188,11 +198,12 @@ __PACKAGE__->register_method ({
     method => 'DELETE',
     description => "Delete sdn subnet object configuration.",
     permissions => {
-	check => ['perm', '/sdn/subnets', ['SDN.Allocate']],
+	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,
             }),
diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
index b585c9c..0fbb747 100644
--- a/PVE/API2/Network/SDN/Vnets.pm
+++ b/PVE/API2/Network/SDN/Vnets.pm
@@ -12,6 +12,7 @@ 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);
@@ -21,6 +22,11 @@ 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) = @_;
 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index b236c3f..97d8cb8 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -100,7 +100,7 @@ sub properties {
 
 sub options {
     return {
-	vnet => { optional => 1 },
+	vnet => { optional => 0 },
 	gateway => { optional => 1 },
 #	routes => { optional => 1 },
 	snat => { optional => 1 },
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 23/35] zones: evpn : fix raise exception
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (21 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 22/35] subnets: move api to /sdn/vnet/<vnet>/subnets && make vnet option not optionnal Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 24/35] subnet: make ipam not optionnal and use pve ipam as default Alexandre Derumier
                   ` (11 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Zones/EvpnPlugin.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index b89f4b1..d5ee56b 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -3,6 +3,7 @@ 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::Tools qw($IPV4RE);
 use PVE::INotify;
 use PVE::Cluster;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 24/35] subnet: make ipam not optionnal and use pve ipam as default
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (22 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 23/35] zones: evpn : fix raise exception Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 25/35] don't allow subnets on vlanware vnet Alexandre Derumier
                   ` (10 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Subnets.pm | 2 ++
 PVE/Network/SDN/Ipams.pm        | 2 ++
 PVE/Network/SDN/SubnetPlugin.pm | 2 +-
 3 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index ab4117c..2dd80a3 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -182,6 +182,8 @@ __PACKAGE__->register_method ({
 	    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};
+
 	    PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $opts, $scfg);
 
 	    PVE::Network::SDN::Subnets::write_config($cfg);
diff --git a/PVE/Network/SDN/Ipams.pm b/PVE/Network/SDN/Ipams.pm
index a979d46..302c4d2 100644
--- a/PVE/Network/SDN/Ipams.pm
+++ b/PVE/Network/SDN/Ipams.pm
@@ -34,6 +34,8 @@ sub sdn_ipams_config {
 
 sub config {
     my $config = cfs_read_file("sdn/ipams.cfg");
+    #add default internal pve
+    $config->{ids}->{pve}->{type} = 'pve';
     return $config;
 }
 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 97d8cb8..341e9e0 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -109,7 +109,7 @@ sub options {
 	dnszone => { optional => 1 },
 	reversednszone => { optional => 1 },
 	dnszoneprefix => { optional => 1 },
-	ipam => { optional => 1 },
+	ipam => { optional => 0 },
     };
 }
 
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 25/35] don't allow subnets on vlanware vnet
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (23 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 24/35] subnet: make ipam not optionnal and use pve ipam as default Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 26/35] generate sdn/.running-config on apply Alexandre Derumier
                   ` (9 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/SubnetPlugin.pm | 1 +
 1 file changed, 1 insertion(+)

diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 341e9e0..8a216b6 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -132,6 +132,7 @@ sub on_update_hook {
     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};
     }
 
     my ($ip, $mask) = split(/\//, $cidr);
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 26/35] generate sdn/.running-config on apply
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (24 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 25/35] don't allow subnets on vlanware vnet Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 27/35] api: add running/pending zones/vnets/subnets/controllers Alexandre Derumier
                   ` (8 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

This is the source configuration for generate local configuration

/sdn/*.cfg are pending configs

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN.pm               |  2 +-
 PVE/Network/SDN.pm                    | 57 +++++++++++++++++++--------
 PVE/Network/SDN/Controllers.pm        | 12 ++++--
 PVE/Network/SDN/Subnets.pm            | 11 +++++-
 PVE/Network/SDN/Vnets.pm              | 18 ++++++---
 PVE/Network/SDN/Zones.pm              | 27 +++++++------
 PVE/Network/SDN/Zones/EvpnPlugin.pm   |  2 +-
 PVE/Network/SDN/Zones/SimplePlugin.pm |  2 +-
 test/generateconfig.pl                |  2 +
 9 files changed, 91 insertions(+), 42 deletions(-)

diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
index fcda11f..f129d60 100644
--- a/PVE/API2/Network/SDN.pm
+++ b/PVE/API2/Network/SDN.pm
@@ -119,7 +119,7 @@ __PACKAGE__->register_method ({
         my $rpcenv = PVE::RPCEnvironment::get();
         my $authuser = $rpcenv->get_user();
 
-	PVE::Network::SDN::increase_version();
+	PVE::Network::SDN::commit_config();
 
         my $code = sub {
             $rpcenv->{type} = 'priv'; # to start tasks in background
diff --git a/PVE/Network/SDN.pm b/PVE/Network/SDN.pm
index 85faca0..f21de15 100644
--- a/PVE/Network/SDN.pm
+++ b/PVE/Network/SDN.pm
@@ -8,32 +8,39 @@ 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 $version_cfg = "sdn/.version";
+my $running_cfg = "sdn/.running-config";
 
-my $parse_version_cfg = sub {
+my $parse_running_cfg = sub {
     my ($filename, $raw) = @_;
 
-    return 0 if !defined($raw) || $raw eq '';
+    my $cfg = {};
 
-    warn "invalid sdn version '$raw'" if $raw !~ m/\d+$/;
+    return $cfg if !defined($raw) || $raw eq '';
 
-    return $raw,
+    eval {
+	$cfg = from_json($raw);
+    };
+    return {} if $@;
+
+    return $cfg;
 };
 
-my $write_version_cfg = sub {
-    my ($filename, $version) = @_;
+my $write_running_cfg = sub {
+    my ($filename, $cfg) = @_;
 
-    warn "invalid sdn version" if $version !~ m/\d+$/;
+    my $json = to_json($cfg);
 
-    return $version;
+    return $json;
 };
 
-PVE::Cluster::cfs_register_file($version_cfg, $parse_version_cfg, $write_version_cfg);
+PVE::Cluster::cfs_register_file($running_cfg, $parse_running_cfg, $write_running_cfg);
 
 
 # improve me : move status code inside plugins ?
@@ -70,23 +77,40 @@ sub status {
     return($zone_status, $vnet_status);
 }
 
+sub config {
+    return cfs_read_file($running_cfg);
+}
+
+sub commit_config {
 
-sub increase_version {
+    my $cfg = cfs_read_file($running_cfg);
+    my $version = $cfg->{version};
 
-    my $version = cfs_read_file($version_cfg);
     if ($version) {
 	$version++;
     } else {
 	$version = 1;
     }
 
-    cfs_write_file($version_cfg, $version);
+    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($version_cfg, undef, $code);
+    cfs_lock_file($running_cfg, undef, $code);
 
     if (my $err = $@) {
         $errmsg ? die "$errmsg: $err" : die $err;
@@ -101,8 +125,9 @@ sub get_local_vnets {
 
     my $nodename = PVE::INotify::nodename();
 
-    my $vnets_cfg = PVE::Network::SDN::Vnets::config();
-    my $zones_cfg = PVE::Network::SDN::Zones::config();
+    my $cfg = PVE::Network::SDN::config();
+    my $vnets_cfg = $cfg->{vnets};
+    my $zones_cfg = $cfg->{zones};
 
     my @vnetids = PVE::Network::SDN::Vnets::sdn_vnets_ids($vnets_cfg);
 
diff --git a/PVE/Network/SDN/Controllers.pm b/PVE/Network/SDN/Controllers.pm
index 91a74d8..c210516 100644
--- a/PVE/Network/SDN/Controllers.pm
+++ b/PVE/Network/SDN/Controllers.pm
@@ -68,9 +68,11 @@ sub complete_sdn_controller {
 
 sub generate_controller_config {
 
-    my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
-    my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
-    my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
+    my $cfg = PVE::Network::SDN::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
@@ -131,7 +133,9 @@ sub generate_controller_config {
 
 sub reload_controller {
 
-    my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
+    my $cfg = PVE::Network::SDN::config();
+    my $controller_cfg = $cfg->{controllers};
+
     return if !$controller_cfg;
 
     foreach my $id (keys %{$controller_cfg->{ids}}) {
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 626b71d..5b99c91 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -49,9 +49,16 @@ sub complete_sdn_subnet {
 }
 
 sub get_subnet {
-    my ($subnetid) = @_;
+    my ($subnetid, $running) = @_;
+
+    my $cfg = {};
+    if($running) {
+	my $cfg = PVE::Network::SDN::config();
+	$cfg = $cfg->{subnets};
+    } else {
+	$cfg = PVE::Network::SDN::Subnets::config();
+    }
 
-    my $cfg = PVE::Network::SDN::Subnets::config();
     my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
     return $subnet;
 }
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 7cec418..d45ef2a 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -47,10 +47,18 @@ sub complete_sdn_vnet {
 }
 
 sub get_vnet {
-    my ($vnetid) = @_;
+    my ($vnetid, $running) = @_;
+
+    my $cfg = {};
+    if($running) {
+	my $cfg = PVE::Network::SDN::config();
+	$cfg = $cfg->{vnets};
+    } else {
+	$cfg = PVE::Network::SDN::Vnets::config();
+    }
 
-    my $cfg = PVE::Network::SDN::Vnets::config();
     my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $vnetid, 1);
+
     return $vnet;
 }
 
@@ -72,7 +80,7 @@ sub get_next_free_ip {
     my ($vnetid, $hostname, $ipversion) = @_;
 
     $ipversion = 4 if !$ipversion;
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     my $ip = undef;
     my $subnetcount = 0;
 
@@ -98,7 +106,7 @@ sub get_next_free_ip {
 sub add_ip {
     my ($vnetid, $cidr, $hostname) = @_;
 
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
 
     my ($ip, $mask) = split(/\//, $cidr);
     my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
@@ -109,7 +117,7 @@ sub add_ip {
 sub del_ip {
     my ($vnetid, $cidr, $hostname) = @_;
 
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
 
     my ($ip, $mask) = split(/\//, $cidr);
     my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
diff --git a/PVE/Network/SDN/Zones.pm b/PVE/Network/SDN/Zones.pm
index 25af088..75f3233 100644
--- a/PVE/Network/SDN/Zones.pm
+++ b/PVE/Network/SDN/Zones.pm
@@ -11,7 +11,6 @@ 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::Subnets;
 use PVE::Network::SDN::Zones::VlanPlugin;
 use PVE::Network::SDN::Zones::QinQPlugin;
 use PVE::Network::SDN::Zones::VxlanPlugin;
@@ -76,11 +75,13 @@ sub complete_sdn_zone {
 
 sub generate_etc_network_config {
 
-    my $version = PVE::Cluster::cfs_read_file('sdn/.version');
-    my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
-    my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
-    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
-    my $controller_cfg = PVE::Cluster::cfs_read_file('sdn/controllers.cfg');
+    my $cfg = PVE::Network::SDN::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');
@@ -188,7 +189,8 @@ sub status {
     my $err_config = undef;
 
     my $local_version = PVE::Network::SDN::Zones::read_etc_network_config_version();
-    my $sdn_version = PVE::Cluster::cfs_read_file('sdn/.version');
+    my $cfg = PVE::Network::SDN::config();
+    my $sdn_version = $cfg->{version};
 
     return if !$sdn_version;
 
@@ -210,8 +212,9 @@ sub status {
 
     my $status = ifquery_check();
 
-    my $vnet_cfg = PVE::Cluster::cfs_read_file('sdn/vnets.cfg');
-    my $zone_cfg = PVE::Cluster::cfs_read_file('sdn/zones.cfg');
+    
+    my $vnet_cfg = $cfg->{vnets};
+    my $zone_cfg = $cfg->{zones};
     my $nodename = PVE::INotify::nodename();
 
     my $vnet_status = {};
@@ -253,7 +256,7 @@ sub status {
 sub tap_create {
     my ($iface, $bridge) = @_;
 
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
     if (!$vnet) { # fallback for classic bridge
 	PVE::Network::tap_create($iface, $bridge);
 	return;
@@ -267,7 +270,7 @@ sub tap_create {
 sub veth_create {
     my ($veth, $vethpeer, $bridge, $hwaddr) = @_;
 
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
+    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;
@@ -281,7 +284,7 @@ sub veth_create {
 sub tap_plug {
     my ($iface, $bridge, $tag, $firewall, $trunks, $rate) = @_;
 
-    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge);
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($bridge, 1);
     if (!$vnet) { # fallback for classic bridge
 	PVE::Network::tap_plug($iface, $bridge, $tag, $firewall, $trunks, $rate);
 	return;
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index d5ee56b..2191008 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -79,7 +79,7 @@ sub generate_sdn_config {
     @iface_config = ();
 
     my $address = {};
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     foreach my $subnetid (sort keys %{$subnets}) {
 	my $subnet = $subnets->{$subnetid};
 	my $cidr = $subnetid =~ s/-/\//r;
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index c58ae87..c0ab1fe 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -36,7 +36,7 @@ sub generate_sdn_config {
     my @iface_config = ();
 
     my $address = {};
-    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     foreach my $subnetid (sort keys %{$subnets}) {
 	my $subnet = $subnets->{$subnetid};
 	my $cidr = $subnetid =~ s/-/\//r; 
diff --git a/test/generateconfig.pl b/test/generateconfig.pl
index 92108ec..250db43 100644
--- a/test/generateconfig.pl
+++ b/test/generateconfig.pl
@@ -8,6 +8,7 @@ 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);
@@ -16,6 +17,7 @@ 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);
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 27/35] api: add running/pending zones/vnets/subnets/controllers
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (25 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 26/35] generate sdn/.running-config on apply Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-08  9:04   ` Thomas Lamprecht
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 28/35] small bugfixes Alexandre Derumier
                   ` (7 subsequent siblings)
  34 siblings, 1 reply; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Controllers.pm | 53 +++++++++++++++++++++---
 PVE/API2/Network/SDN/Subnets.pm     | 45 ++++++++++++++++++++-
 PVE/API2/Network/SDN/Vnets.pm       | 62 +++++++++++++++++++++++++++--
 PVE/API2/Network/SDN/Zones.pm       | 54 +++++++++++++++++++++++--
 PVE/Network/SDN.pm                  | 45 +++++++++++++++++++++
 5 files changed, 245 insertions(+), 14 deletions(-)

diff --git a/PVE/API2/Network/SDN/Controllers.pm b/PVE/API2/Network/SDN/Controllers.pm
index 919d343..75beb6b 100644
--- a/PVE/API2/Network/SDN/Controllers.pm
+++ b/PVE/API2/Network/SDN/Controllers.pm
@@ -51,15 +51,27 @@ __PACKAGE__->register_method ({
 		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'},
-			  },
+	    properties => { controller => { type => 'string' },
+			    type => { type => 'string' },
+			    state => { type => 'string', optional => 1 },
+                            pending => { optional => 1},
+	    },
 	},
 	links => [ { rel => 'child', href => "{controller}" } ],
     },
@@ -69,8 +81,17 @@ __PACKAGE__->register_method ({
 	my $rpcenv = PVE::RPCEnvironment::get();
 	my $authuser = $rpcenv->get_user();
 
-
-	my $cfg = PVE::Network::SDN::Controllers::config();
+        my $cfg = {};
+        if($param->{pending}) {
+            my $running_cfg = PVE::Network::SDN::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::config();
+            $cfg = $running_cfg->{controllers};
+        } else {
+            $cfg = PVE::Network::SDN::Controllers::config();
+        }
 
 	my @sids = PVE::Network::SDN::Controllers::sdn_controllers_ids($cfg);
 	my $res = [];
@@ -102,13 +123,33 @@ __PACKAGE__->register_method ({
     	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 = PVE::Network::SDN::Controllers::config();
+        my $cfg = {};
+        if($param->{pending}) {
+            my $running_cfg = PVE::Network::SDN::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::config();
+            $cfg = $running_cfg->{controllers};
+        } else {
+            $cfg = PVE::Network::SDN::Controllers::config();
+        }
 
 	return &$api_sdn_controllers_config($cfg, $param->{controller});
     }});
diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index 2dd80a3..34fb714 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -46,6 +46,16 @@ __PACKAGE__->register_method ({
     	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.",
+	    },
         },
 
     },
@@ -65,7 +75,17 @@ __PACKAGE__->register_method ({
 
         my $vnetid = $param->{vnet};
 
-	my $cfg = PVE::Network::SDN::Subnets::config();
+        my $cfg = {};
+        if($param->{pending}) {
+	    my $running_cfg = PVE::Network::SDN::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::config();
+	    $cfg = $running_cfg->{subnets};
+        } else {
+	    $cfg = PVE::Network::SDN::Subnets::config();
+        }
 
 	my @sids = PVE::Network::SDN::Subnets::sdn_subnets_ids($cfg);
 	my $res = [];
@@ -97,13 +117,34 @@ __PACKAGE__->register_method ({
             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 = PVE::Network::SDN::Subnets::config();
+        my $cfg = {};
+        if($param->{pending}) {
+	    my $running_cfg = PVE::Network::SDN::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::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};
diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
index 0fbb747..6ff61c5 100644
--- a/PVE/API2/Network/SDN/Vnets.pm
+++ b/PVE/API2/Network/SDN/Vnets.pm
@@ -3,6 +3,8 @@ package PVE::API2::Network::SDN::Vnets;
 use strict;
 use warnings;
 
+use Hash::Diff qw( diff );
+
 use PVE::SafeSyslog;
 use PVE::Tools qw(extract_param);
 use PVE::Cluster qw(cfs_read_file cfs_write_file);
@@ -33,10 +35,22 @@ my $api_sdn_vnets_config = sub {
     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 => '',
@@ -49,6 +63,18 @@ __PACKAGE__->register_method ({
     },
     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',
@@ -64,7 +90,17 @@ __PACKAGE__->register_method ({
 	my $rpcenv = PVE::RPCEnvironment::get();
 	my $authuser = $rpcenv->get_user();
 
-	my $cfg = PVE::Network::SDN::Vnets::config();
+	my $cfg = {};
+	if($param->{pending}) {
+	    my $running_cfg = PVE::Network::SDN::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::config();
+	    $cfg = $running_cfg->{vnets};
+	} else {
+	    $cfg = PVE::Network::SDN::Vnets::config();
+	}
 
 	my @sids = PVE::Network::SDN::Vnets::sdn_vnets_ids($cfg);
 	my $res = [];
@@ -93,13 +129,33 @@ __PACKAGE__->register_method ({
 	    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 = PVE::Network::SDN::Vnets::config();
+	my $cfg = {};
+	if($param->{pending}) {
+	    my $running_cfg = PVE::Network::SDN::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::config();
+	    $cfg = $running_cfg->{vnets};
+	} else {
+	    $cfg = PVE::Network::SDN::Vnets::config();
+	}
 
 	return $api_sdn_vnets_config->($cfg, $param->{vnet});
     }});
diff --git a/PVE/API2/Network/SDN/Zones.pm b/PVE/API2/Network/SDN/Zones.pm
index a37df3d..512945c 100644
--- a/PVE/API2/Network/SDN/Zones.pm
+++ b/PVE/API2/Network/SDN/Zones.pm
@@ -38,6 +38,11 @@ my $api_sdn_zones_config = sub {
         $scfg->{nodes} = PVE::Network::SDN::Zones::Plugin->encode_value($scfg->{type}, 'nodes', $scfg->{nodes});
     }
 
+    my $pending = $scfg->{pending};
+    if ($pending->{nodes}) {
+        $pending->{nodes} = PVE::Network::SDN::Zones::Plugin->encode_value($scfg->{type}, 'nodes', $pending->{nodes});
+    }
+
     return $scfg;
 };
 
@@ -59,6 +64,16 @@ __PACKAGE__->register_method ({
 		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 => {
@@ -67,6 +82,10 @@ __PACKAGE__->register_method ({
 	    type => "object",
 	    properties => { zone => { type => 'string'},
 			    type => { type => 'string'},
+			    mtu => { type => 'integer', optional => 1 },
+			    pending => { optional => 1},
+			    state => { type => 'string', optional => 1},
+			    nodes => { type => 'string', optional => 1},
 			  },
 	},
 	links => [ { rel => 'child', href => "{zone}" } ],
@@ -77,8 +96,17 @@ __PACKAGE__->register_method ({
 	my $rpcenv = PVE::RPCEnvironment::get();
 	my $authuser = $rpcenv->get_user();
 
-
-	my $cfg = PVE::Network::SDN::Zones::config();
+	my $cfg = {};
+	if($param->{pending}) {
+	    my $running_cfg = PVE::Network::SDN::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::config();
+	    $cfg = $running_cfg->{zones};
+        } else {
+	    $cfg = PVE::Network::SDN::Zones::config();
+        }
 
 	my @sids = PVE::Network::SDN::Zones::sdn_zones_ids($cfg);
 	my $res = [];
@@ -110,13 +138,33 @@ __PACKAGE__->register_method ({
     	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 = PVE::Network::SDN::Zones::config();
+	my $cfg = {};
+	if($param->{pending}) {
+	    my $running_cfg = PVE::Network::SDN::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::config();
+	    $cfg = $running_cfg->{zones};
+        } else {
+	    $cfg = PVE::Network::SDN::Zones::config();
+        }
 
 	return &$api_sdn_zones_config($cfg, $param->{zone});
     }});
diff --git a/PVE/Network/SDN.pm b/PVE/Network/SDN.pm
index f21de15..3cd73ff 100644
--- a/PVE/Network/SDN.pm
+++ b/PVE/Network/SDN.pm
@@ -81,6 +81,51 @@ sub 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 ($running_object->{$key} ne $config_object->{$key}) {
+		$pending->{$id}->{state} = "changed";
+	    }
+	}
+    }
+
+   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 = $config_object->{$key} if $config_object->{$key};
+	    my $running_value = $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";
+	    }
+	}
+   }
+
+   return {ids => $pending};
+
+}
+
 sub commit_config {
 
     my $cfg = cfs_read_file($running_cfg);
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 28/35] small bugfixes
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (26 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 27/35] api: add running/pending zones/vnets/subnets/controllers Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 29/35] move dns options from subnets to zone Alexandre Derumier
                   ` (6 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Dns.pm        | 2 +-
 PVE/Network/SDN/Controllers.pm     | 2 +-
 PVE/Network/SDN/Ipams/PVEPlugin.pm | 6 +++---
 PVE/Network/SDN/Subnets.pm         | 4 ++--
 PVE/Network/SDN/Vnets.pm           | 2 +-
 PVE/Network/SDN/Zones.pm           | 2 +-
 6 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/PVE/API2/Network/SDN/Dns.pm b/PVE/API2/Network/SDN/Dns.pm
index ea26af3..3d08552 100644
--- a/PVE/API2/Network/SDN/Dns.pm
+++ b/PVE/API2/Network/SDN/Dns.pm
@@ -147,7 +147,7 @@ __PACKAGE__->register_method ({
 
 		$dns_cfg->{ids}->{$id} = $opts;
 
-		my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
+		my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($opts->{type});
 		$plugin->on_update_hook($opts);
 
 		PVE::Network::SDN::Dns::write_config($dns_cfg);
diff --git a/PVE/Network/SDN/Controllers.pm b/PVE/Network/SDN/Controllers.pm
index c210516..f652d7f 100644
--- a/PVE/Network/SDN/Controllers.pm
+++ b/PVE/Network/SDN/Controllers.pm
@@ -55,7 +55,7 @@ sub lock_sdn_controllers_config {
 sub sdn_controllers_ids {
     my ($cfg) = @_;
 
-    return keys %{$cfg->{ids}};
+    return sort keys %{$cfg->{ids}};
 }
 
 sub complete_sdn_controller {
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 741a680..a3ad3d6 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -56,7 +56,7 @@ sub del_subnet {
 
 	my $db = read_db();
 	my $ips = $db->{subnets}->{$cidr}->{ips};
-	die "can't delete subnet, not empty" if keys %{$ips} > 0;
+	die "can't delete subnet $cidr , not empty" if keys %{$ips} > 0;
 	delete $db->{subnets}->{$cidr};
 	write_db($db);
     });
@@ -74,7 +74,7 @@ sub add_ip {
 	my $db = read_db();
 	my $s = $db->{subnets}->{$cidr};
 
-	die "ip already exist" if defined($s->{ips}->{$ip});
+	die "ip $ip already exist" if defined($s->{ips}->{$ip});
 
 	#verify that ip is valid for this subnet
 	$s->{ips}->{$ip} = 1;
@@ -127,7 +127,7 @@ sub del_ip {
 	my $s = $db->{subnets}->{$cidr};
 	return if !$ip;
 
-	die "ip does not exist in pam" if !defined($s->{ips}->{$ip});
+	die "ip $ip does not exist in pam" if !defined($s->{ips}->{$ip});
 	delete $s->{ips}->{$ip};
 	write_db($db);
     });
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 5b99c91..0f14f6f 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -37,7 +37,7 @@ sub write_config {
 sub sdn_subnets_ids {
     my ($cfg) = @_;
 
-    return keys %{$cfg->{ids}};
+    return sort keys %{$cfg->{ids}};
 }
 
 sub complete_sdn_subnet {
@@ -191,7 +191,7 @@ sub next_free_ip {
 sub add_ip {
     my ($subnetid, $subnet, $ip, $hostname) = @_;
 
-    return if !$subnet;
+    return if !$subnet || !$ip; 
 
     my $ipamid = $subnet->{ipam};
     my $dns = $subnet->{dns};
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index d45ef2a..96aa422 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -35,7 +35,7 @@ sub write_config {
 sub sdn_vnets_ids {
     my ($cfg) = @_;
 
-    return keys %{$cfg->{ids}};
+    return sort keys %{$cfg->{ids}};
 }
 
 sub complete_sdn_vnet {
diff --git a/PVE/Network/SDN/Zones.pm b/PVE/Network/SDN/Zones.pm
index 75f3233..cef4dd2 100644
--- a/PVE/Network/SDN/Zones.pm
+++ b/PVE/Network/SDN/Zones.pm
@@ -61,7 +61,7 @@ sub write_config {
 sub sdn_zones_ids {
     my ($cfg) = @_;
 
-    return keys %{$cfg->{ids}};
+    return sort keys %{$cfg->{ids}};
 }
 
 sub complete_sdn_zone {
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 29/35] move dns options from subnets to zone
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (27 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 28/35] small bugfixes Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 30/35] move ipam option from subnet " Alexandre Derumier
                   ` (5 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Subnets.pm       | 17 +++++++-
 PVE/API2/Network/SDN/Zones.pm         | 21 ++++++++++
 PVE/Network/SDN/Dns/Plugin.pm         |  1 +
 PVE/Network/SDN/Dns/PowerdnsPlugin.pm | 58 ++++++++++++++++++++++++++-
 PVE/Network/SDN/SubnetPlugin.pm       | 41 ++++---------------
 PVE/Network/SDN/Subnets.pm            | 43 ++++++++++++--------
 PVE/Network/SDN/VnetPlugin.pm         |  5 +++
 PVE/Network/SDN/Vnets.pm              | 17 ++++++--
 PVE/Network/SDN/Zones.pm              | 16 ++++++++
 PVE/Network/SDN/Zones/EvpnPlugin.pm   |  3 ++
 PVE/Network/SDN/Zones/FaucetPlugin.pm |  3 ++
 PVE/Network/SDN/Zones/QinQPlugin.pm   |  3 ++
 PVE/Network/SDN/Zones/SimplePlugin.pm | 22 +++++++++-
 PVE/Network/SDN/Zones/VlanPlugin.pm   |  5 ++-
 PVE/Network/SDN/Zones/VxlanPlugin.pm  |  3 ++
 15 files changed, 199 insertions(+), 59 deletions(-)

diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index 34fb714..5ea4fc4 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -11,6 +11,7 @@ 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;
 
@@ -178,6 +179,12 @@ __PACKAGE__->register_method ({
 	    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 $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 1, 1);
 
 		my $scfg = undef;
@@ -186,7 +193,7 @@ __PACKAGE__->register_method ({
 		}
 
 		$cfg->{ids}->{$id} = $opts;
-		PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $opts);
+		PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $opts);
 
 		PVE::Network::SDN::Subnets::write_config($cfg);
 
@@ -216,6 +223,12 @@ __PACKAGE__->register_method ({
 	 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);
@@ -225,7 +238,7 @@ __PACKAGE__->register_method ({
 
 	    raise_param_exc({ ipam => "you can't change ipam"}) if $opts->{ipam} && $scfg->{ipam} && $opts->{ipam} ne $scfg->{ipam};
 
-	    PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $opts, $scfg);
+	    PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $opts, $scfg);
 
 	    PVE::Network::SDN::Subnets::write_config($cfg);
 
diff --git a/PVE/API2/Network/SDN/Zones.pm b/PVE/API2/Network/SDN/Zones.pm
index 512945c..cbfa9fe 100644
--- a/PVE/API2/Network/SDN/Zones.pm
+++ b/PVE/API2/Network/SDN/Zones.pm
@@ -9,6 +9,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file);
 use PVE::Network::SDN;
 use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Dns;
 use PVE::Network::SDN::Zones::Plugin;
 use PVE::Network::SDN::Zones::VlanPlugin;
 use PVE::Network::SDN::Zones::QinQPlugin;
@@ -20,6 +21,7 @@ use PVE::Network::SDN::Zones::SimplePlugin;
 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;
 
@@ -83,6 +85,9 @@ __PACKAGE__->register_method ({
 	    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},
 			    pending => { optional => 1},
 			    state => { type => 'string', optional => 1},
 			    nodes => { type => 'string', optional => 1},
@@ -198,11 +203,19 @@ __PACKAGE__->register_method ({
 
 		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};
+		my $reversednsserver = $opts->{reversedns};
+		my $dnszone = $opts->{dnszone};
+		raise_param_exc({ dns => "$dnsserver don't exist"}) if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
+		raise_param_exc({ reversedns => "$reversednsserver don't exist"}) if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
+		raise_param_exc({ dnszone => "missing dns server"}) if $dnszone && !$dnsserver;
 
 		$zone_cfg->{ids}->{$id} = $opts;
 		$plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
@@ -236,6 +249,7 @@ __PACKAGE__->register_method ({
 
 	    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);
 
@@ -248,6 +262,13 @@ __PACKAGE__->register_method ({
 		$scfg->{$k} = $opts->{$k};
 	    }
 
+	    my $dnsserver = $opts->{dns};
+	    my $reversednsserver = $opts->{reversedns};
+	    my $dnszone = $opts->{dnszone};
+	    raise_param_exc({ dns => "$dnsserver don't exist"}) if $dnsserver && !$dns_cfg->{ids}->{$dnsserver};
+	    raise_param_exc({ reversedns => "$reversednsserver don't exist"}) if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
+	    raise_param_exc({ dnszone => "missing dns server"}) if $dnszone && !$dnsserver;
+
 	    $plugin->on_update_hook($id, $zone_cfg, $controller_cfg);
 
 	    PVE::Network::SDN::Zones::write_config($zone_cfg);
diff --git a/PVE/Network/SDN/Dns/Plugin.pm b/PVE/Network/SDN/Dns/Plugin.pm
index baa9316..be399b0 100644
--- a/PVE/Network/SDN/Dns/Plugin.pm
+++ b/PVE/Network/SDN/Dns/Plugin.pm
@@ -42,6 +42,7 @@ my $defaultData = {
 	    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 }),
     },
diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
index f02c2f1..5b98e87 100644
--- a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
+++ b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
@@ -7,6 +7,7 @@ use PVE::Cluster;
 use PVE::Tools;
 use JSON;
 use Net::IP;
+use NetAddr::IP;
 
 use base('PVE::Network::SDN::Dns::Plugin');
 
@@ -22,6 +23,9 @@ sub properties {
 	key => {
 	    type => 'string',
 	},
+        reversemaskv6 => { 
+	    type => 'integer' 
+        },
     };
 }
 
@@ -31,6 +35,8 @@ sub options {
         url => { optional => 0},
         key => { optional => 0 },
         ttl => { optional => 1 },
+        reversemaskv6 => { optional => 1, description => "force a different netmask for the ipv6 reverse zone name." },
+
     };
 }
 
@@ -81,7 +87,8 @@ sub add_ptr_record {
     my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
     $hostname .= ".";
 
-    my $reverseip = join(".", reverse(split(/\./, $ip))).".in-addr.arpa.";
+    my $reverseip = Net::IP->new($ip)->reverse_ip();
+
     my $type = "PTR";
 
     my $record = { content => $hostname, 
@@ -140,7 +147,8 @@ sub del_ptr_record {
     my $key = $plugin_config->{key};
     my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
 
-    my $reverseip = join(".", reverse(split(/\./, $ip))).".in-addr.arpa.";
+    my $reverseip = Net::IP->new($ip)->reverse_ip();
+
     my $type = "PTR";
 
     my $rrset = { name => $reverseip, 
@@ -177,6 +185,52 @@ sub verify_zone {
     }
 }
 
+sub get_reversedns_zone {
+    my ($class, $plugin_config, $subnetid, $ip) = @_;
+
+    my ($network, $mask) = split(/-/, $subnetid);
+
+    my $cidr = "$ip/$mask";
+    my $zone = "";
+
+    if (Net::IP::ip_is_ipv4($ip)) {
+	my ($ipblock1, $ipblock2, $ipblock3, $ipblock4) = split(/\./, $ip);
+
+        my $ipv4 = new NetAddr::IP($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) = @_;
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 8a216b6..f57a5e9 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -71,22 +71,6 @@ sub properties {
 #            type => 'string',
 #            description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
 #        },
-        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",
-        },
-        reversednszone => {
-            type => 'string', format => 'dns-name',
-            description => "reverse dns zone ex: 0.168.192.in-addr.arpa",
-        },
         dnszoneprefix => {
             type => 'string', format => 'dns-name',
             description => "dns domain zone prefix  ex: 'adm' -> <hostname>.adm.mydomain.com",
@@ -104,17 +88,13 @@ sub options {
 	gateway => { optional => 1 },
 #	routes => { optional => 1 },
 	snat => { optional => 1 },
-	dns => { optional => 1 },
-	reversedns => { optional => 1 },
-	dnszone => { optional => 1 },
-	reversednszone => { optional => 1 },
 	dnszoneprefix => { optional => 1 },
 	ipam => { optional => 0 },
     };
 }
 
 sub on_update_hook {
-    my ($class, $subnetid, $subnet, $old_subnet) = @_;
+    my ($class, $zone, $subnetid, $subnet, $old_subnet) = @_;
 
     my $cidr = $subnetid =~ s/-/\//r;
     my $subnet_matcher = subnet_matcher($cidr);
@@ -122,10 +102,9 @@ sub on_update_hook {
     my $vnetid = $subnet->{vnet};
     my $gateway = $subnet->{gateway};
     my $ipam = $subnet->{ipam};
-    my $dns = $subnet->{dns};
-    my $dnszone = $subnet->{dnszone};
-    my $reversedns = $subnet->{reversedns};
-    my $reversednszone = $subnet->{reversednszone};
+    my $dns = $zone->{dns};
+    my $dnszone = $zone->{dnszone};
+    my $reversedns = $zone->{reversedns};
 
     my $old_gateway = $old_subnet->{gateway} if $old_subnet;
 
@@ -139,12 +118,6 @@ sub on_update_hook {
     #for /32 pointopoint, we allow gateway outside the subnet
     raise_param_exc({ gateway => "$gateway is not in subnet $subnetid"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
 
-    raise_param_exc({ dns => "missing dns provider"}) if $dnszone && !$dns;
-    raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
-    raise_param_exc({ reversedns => "missing dns provider"}) if $reversednszone && !$reversedns;
-    raise_param_exc({ reversednszone => "missing dns zone"}) if $reversedns && !$reversednszone;
-    raise_param_exc({ reversedns => "missing forward dns zone"}) if $reversednszone && !$dnszone;
-
     if ($ipam) {
 	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
 	my $plugin_config = $ipam_cfg->{ids}->{$ipam};
@@ -155,18 +128,18 @@ sub on_update_hook {
 	#delete on removal
 	if (!defined($gateway) && $old_gateway) {
 	    eval {
-		PVE::Network::SDN::Subnets::del_ip($subnetid, $old_subnet, $old_gateway);
+		PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
 	    };
 	    warn if $@;
 	}
         if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
-	    PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $gateway);
+	    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway);
 	}
 
 	#delete old ip after update
 	if($gateway && $old_gateway && $gateway ne $old_gateway) {
 	    eval {
-		PVE::Network::SDN::Subnets::del_ip($subnetid, $old_subnet, $old_gateway);
+		PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $old_subnet, $old_gateway);
 	    };
 	    warn if $@;
 	}
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 0f14f6f..aa7c6c1 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -93,6 +93,17 @@ my $verify_dns_zone = sub {
     $plugin->verify_zone($plugin_config, $zone);
 };
 
+my $get_reversedns_zone = sub {
+    my ($subnetid, $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, $ip);
+};
+
 my $add_dns_record = sub {
     my ($zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
     return if !$zone || !$dns || !$hostname || !$ip;
@@ -144,21 +155,19 @@ my $del_dns_ptr_record = sub {
 };
 
 sub next_free_ip {
-    my ($subnetid, $subnet, $hostname) = @_;
+    my ($zone, $subnetid, $subnet, $hostname) = @_;
 
     my $cidr = undef;
     my $ip = undef;
 
     my $ipamid = $subnet->{ipam};
-    my $dns = $subnet->{dns};
-    my $dnszone = $subnet->{dnszone};
-    my $reversedns = $subnet->{reversedns};
-    my $reversednszone = $subnet->{reversednszone};
+    my $dns = $zone->{dns};
+    my $dnszone = $zone->{dnszone};
+    my $reversedns = $zone->{reversedns};
     my $dnszoneprefix = $subnet->{dnszoneprefix};
 
     #verify dns zones before ipam
     &$verify_dns_zone($dnszone, $dns);
-    &$verify_dns_zone($reversednszone, $reversedns);
 
     if($ipamid) {
 	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
@@ -172,6 +181,8 @@ sub next_free_ip {
     }
 
     eval {
+	my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
+
 	#add dns
 	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
 	#add reverse dns
@@ -189,15 +200,15 @@ sub next_free_ip {
 }
 
 sub add_ip {
-    my ($subnetid, $subnet, $ip, $hostname) = @_;
+    my ($zone, $subnetid, $subnet, $ip, $hostname) = @_;
 
     return if !$subnet || !$ip; 
 
     my $ipamid = $subnet->{ipam};
-    my $dns = $subnet->{dns};
-    my $dnszone = $subnet->{dnszone};
-    my $reversedns = $subnet->{reversedns};
-    my $reversednszone = $subnet->{reversednszone};
+    my $dns = $zone->{dns};
+    my $dnszone = $zone->{dnszone};
+    my $reversedns = $zone->{reversedns};
+    my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
     my $dnszoneprefix = $subnet->{dnszoneprefix};
 
     #verify dns zones before ipam
@@ -231,15 +242,15 @@ sub add_ip {
 }
 
 sub del_ip {
-    my ($subnetid, $subnet, $ip, $hostname) = @_;
+    my ($zone, $subnetid, $subnet, $ip, $hostname) = @_;
 
     return if !$subnet;
 
     my $ipamid = $subnet->{ipam};
-    my $dns = $subnet->{dns};
-    my $dnszone = $subnet->{dnszone};
-    my $reversedns = $subnet->{reversedns};
-    my $reversednszone = $subnet->{reversednszone};
+    my $dns = $zone->{dns};
+    my $dnszone = $zone->{dnszone};
+    my $reversedns = $zone->{reversedns};
+    my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
     my $dnszoneprefix = $subnet->{dnszoneprefix};
 
     &$verify_dns_zone($dnszone, $dns);
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index 47fd4d4..8481f0d 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -100,6 +100,11 @@ sub on_delete_hook {
 
 sub on_update_hook {
     my ($class, $vnetid, $vnet_cfg, $subnet_cfg) = @_;
+
+    #fixme : don't allow change zone if subnets are defined
+    #fixme : don't vlanaware change if subnets are defined
+#    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+   
     # verify that tag is not already defined in another vnet
     if (defined($vnet_cfg->{ids}->{$vnetid}->{tag})) {
 	my $tag = $vnet_cfg->{ids}->{$vnetid}->{tag};
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 96aa422..3b49ada 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -6,6 +6,7 @@ use warnings;
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
 use Net::IP;
 use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::Zones;
 
 use PVE::Network::SDN::VnetPlugin;
 PVE::Network::SDN::VnetPlugin->register();
@@ -79,6 +80,10 @@ sub get_subnets {
 sub get_next_free_ip {
     my ($vnetid, $hostname, $ipversion) = @_;
 
+    my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+    my $zoneid = $vnet->{zone};
+    my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
+
     $ipversion = 4 if !$ipversion;
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     my $ip = undef;
@@ -92,7 +97,7 @@ sub get_next_free_ip {
 	$subnetcount++;
 	if ($subnet->{ipam}) {
 	    eval {
-		$ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet, $hostname);
+		$ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname);
 	    };
 	    warn $@ if $@;
 	}
@@ -107,22 +112,28 @@ sub add_ip {
     my ($vnetid, $cidr, $hostname) = @_;
 
     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);
     my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
 
-    PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip, $hostname);
+    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname);
 }
 
 sub del_ip {
     my ($vnetid, $cidr, $hostname) = @_;
 
     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);
     my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
 
-    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname);
+    PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname);
 }
 
 1;
diff --git a/PVE/Network/SDN/Zones.pm b/PVE/Network/SDN/Zones.pm
index cef4dd2..1f225dc 100644
--- a/PVE/Network/SDN/Zones.pm
+++ b/PVE/Network/SDN/Zones.pm
@@ -72,6 +72,22 @@ sub complete_sdn_zone {
     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::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 {
 
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 2191008..3cb083b 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -37,6 +37,9 @@ sub options {
         'vrf-vxlan' => { optional => 0 },
         'controller' => { optional => 0 },
 	mtu => { optional => 1 },
+	dns => { optional => 1 },
+	reversedns => { optional => 1 },
+	dnszone => { optional => 1 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/FaucetPlugin.pm b/PVE/Network/SDN/Zones/FaucetPlugin.pm
index ef422ca..1b70a57 100644
--- a/PVE/Network/SDN/Zones/FaucetPlugin.pm
+++ b/PVE/Network/SDN/Zones/FaucetPlugin.pm
@@ -26,6 +26,9 @@ sub options {
 	'dp-id' => { optional => 0 },
 #	'uplink-id' => { optional => 0 },
         'controller' => { optional => 0 },
+        dns => { optional => 1 },
+        reversedns => { optional => 1 },
+	dnszone => { optional => 1 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/QinQPlugin.pm b/PVE/Network/SDN/Zones/QinQPlugin.pm
index c828af4..8507ae6 100644
--- a/PVE/Network/SDN/Zones/QinQPlugin.pm
+++ b/PVE/Network/SDN/Zones/QinQPlugin.pm
@@ -40,6 +40,9 @@ sub options {
 	'bridge' => { optional => 0 },
 	'mtu' => { optional => 1 },
 	'vlan-protocol' => { optional => 1 },
+	dns => { optional => 1 },
+	reversedns => { optional => 1 },
+	dnszone => { optional => 1 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index c0ab1fe..58cc1af 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -13,10 +13,30 @@ 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 }
+	mtu => { optional => 1 },
+	dns => { optional => 1 },
+	reversedns => { optional => 1 },
+	dnszone => { optional => 1 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/VlanPlugin.pm b/PVE/Network/SDN/Zones/VlanPlugin.pm
index 7f90d31..fd750c9 100644
--- a/PVE/Network/SDN/Zones/VlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VlanPlugin.pm
@@ -33,7 +33,10 @@ sub options {
     return {
         nodes => { optional => 1},
 	'bridge' => { optional => 0 },
-	mtu => { optional => 1 }
+	mtu => { optional => 1 },
+	dns => { optional => 1 },
+	reversedns => { optional => 1 },
+	dnszone => { optional => 1 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/VxlanPlugin.pm b/PVE/Network/SDN/Zones/VxlanPlugin.pm
index 79af054..7a6687a 100644
--- a/PVE/Network/SDN/Zones/VxlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VxlanPlugin.pm
@@ -38,6 +38,9 @@ sub options {
         nodes => { optional => 1},
         peers => { optional => 0 },
 	mtu => { optional => 1 },
+	dns => { optional => 1 },
+	reversedns => { optional => 1 },
+	dnszone => { optional => 1 },
     };
 }
 
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 30/35] move ipam option from subnet to zone
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (28 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 29/35] move dns options from subnets to zone Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 31/35] subnets/ipam: allow same subnet on different zones Alexandre Derumier
                   ` (4 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Zones.pm         | 9 +++++++++
 PVE/Network/SDN/SubnetPlugin.pm       | 8 +-------
 PVE/Network/SDN/Subnets.pm            | 6 +++---
 PVE/Network/SDN/Vnets.pm              | 2 +-
 PVE/Network/SDN/Zones/EvpnPlugin.pm   | 1 +
 PVE/Network/SDN/Zones/FaucetPlugin.pm | 1 +
 PVE/Network/SDN/Zones/Plugin.pm       | 4 ++++
 PVE/Network/SDN/Zones/QinQPlugin.pm   | 1 +
 PVE/Network/SDN/Zones/SimplePlugin.pm | 3 ++-
 PVE/Network/SDN/Zones/VlanPlugin.pm   | 1 +
 PVE/Network/SDN/Zones/VxlanPlugin.pm  | 1 +
 11 files changed, 25 insertions(+), 12 deletions(-)

diff --git a/PVE/API2/Network/SDN/Zones.pm b/PVE/API2/Network/SDN/Zones.pm
index cbfa9fe..54f087d 100644
--- a/PVE/API2/Network/SDN/Zones.pm
+++ b/PVE/API2/Network/SDN/Zones.pm
@@ -88,6 +88,7 @@ __PACKAGE__->register_method ({
 			    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},
@@ -217,6 +218,10 @@ __PACKAGE__->register_method ({
 		raise_param_exc({ reversedns => "$reversednsserver don't exist"}) if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
 		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);
 
@@ -269,6 +274,10 @@ __PACKAGE__->register_method ({
 	    raise_param_exc({ reversedns => "$reversednsserver don't exist"}) if $reversednsserver && !$dns_cfg->{ids}->{$reversednsserver};
 	    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);
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index f57a5e9..a5d03f6 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -75,10 +75,6 @@ sub properties {
             type => 'string', format => 'dns-name',
             description => "dns domain zone prefix  ex: 'adm' -> <hostname>.adm.mydomain.com",
         },
-        ipam => {
-            type => 'string',
-            description => "use a specific ipam",
-        },
     };
 }
 
@@ -89,7 +85,6 @@ sub options {
 #	routes => { optional => 1 },
 	snat => { optional => 1 },
 	dnszoneprefix => { optional => 1 },
-	ipam => { optional => 0 },
     };
 }
 
@@ -101,7 +96,7 @@ sub on_update_hook {
 
     my $vnetid = $subnet->{vnet};
     my $gateway = $subnet->{gateway};
-    my $ipam = $subnet->{ipam};
+    my $ipam = $zone->{ipam};
     my $dns = $zone->{dns};
     my $dnszone = $zone->{dnszone};
     my $reversedns = $zone->{reversedns};
@@ -121,7 +116,6 @@ sub on_update_hook {
     if ($ipam) {
 	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
 	my $plugin_config = $ipam_cfg->{ids}->{$ipam};
-	raise_param_exc({ ipam => "$ipam not existing"}) if !$plugin_config;
 	my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
 	$plugin->add_subnet($plugin_config, $subnetid, $subnet);
 
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index aa7c6c1..50130d5 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -160,7 +160,7 @@ sub next_free_ip {
     my $cidr = undef;
     my $ip = undef;
 
-    my $ipamid = $subnet->{ipam};
+    my $ipamid = $zone->{ipam};
     my $dns = $zone->{dns};
     my $dnszone = $zone->{dnszone};
     my $reversedns = $zone->{reversedns};
@@ -204,7 +204,7 @@ sub add_ip {
 
     return if !$subnet || !$ip; 
 
-    my $ipamid = $subnet->{ipam};
+    my $ipamid = $zone->{ipam};
     my $dns = $zone->{dns};
     my $dnszone = $zone->{dnszone};
     my $reversedns = $zone->{reversedns};
@@ -246,7 +246,7 @@ sub del_ip {
 
     return if !$subnet;
 
-    my $ipamid = $subnet->{ipam};
+    my $ipamid = $zone->{ipam};
     my $dns = $zone->{dns};
     my $dnszone = $zone->{dnszone};
     my $reversedns = $zone->{reversedns};
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 3b49ada..d08db51 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -95,7 +95,7 @@ sub get_next_free_ip {
 
 	next if $ipversion != Net::IP::ip_get_version($network);
 	$subnetcount++;
-	if ($subnet->{ipam}) {
+	if ($zone->{ipam}) {
 	    eval {
 		$ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname);
 	    };
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 3cb083b..723b3cc 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -40,6 +40,7 @@ sub options {
 	dns => { optional => 1 },
 	reversedns => { optional => 1 },
 	dnszone => { optional => 1 },
+	ipam => { optional => 0 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/FaucetPlugin.pm b/PVE/Network/SDN/Zones/FaucetPlugin.pm
index 1b70a57..a9f119c 100644
--- a/PVE/Network/SDN/Zones/FaucetPlugin.pm
+++ b/PVE/Network/SDN/Zones/FaucetPlugin.pm
@@ -29,6 +29,7 @@ sub options {
         dns => { optional => 1 },
         reversedns => { optional => 1 },
 	dnszone => { optional => 1 },
+	ipam => { optional => 0 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/Plugin.pm b/PVE/Network/SDN/Zones/Plugin.pm
index 7f6db0e..1f24269 100644
--- a/PVE/Network/SDN/Zones/Plugin.pm
+++ b/PVE/Network/SDN/Zones/Plugin.pm
@@ -44,6 +44,10 @@ my $defaultData = {
         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",
+        },
     },
 };
 
diff --git a/PVE/Network/SDN/Zones/QinQPlugin.pm b/PVE/Network/SDN/Zones/QinQPlugin.pm
index 8507ae6..aadfd27 100644
--- a/PVE/Network/SDN/Zones/QinQPlugin.pm
+++ b/PVE/Network/SDN/Zones/QinQPlugin.pm
@@ -43,6 +43,7 @@ sub options {
 	dns => { optional => 1 },
 	reversedns => { optional => 1 },
 	dnszone => { optional => 1 },
+	ipam => { optional => 0 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index 58cc1af..fe0f20f 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -26,7 +26,7 @@ sub properties {
 	dnszone => {
 	    type => 'string', format => 'dns-name',
 	    description => "dns domain zone  ex: mydomain.com",
-	},
+	}
     };
 }
 
@@ -37,6 +37,7 @@ sub options {
 	dns => { optional => 1 },
 	reversedns => { optional => 1 },
 	dnszone => { optional => 1 },
+	ipam => { optional => 0 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/VlanPlugin.pm b/PVE/Network/SDN/Zones/VlanPlugin.pm
index fd750c9..e1ae75b 100644
--- a/PVE/Network/SDN/Zones/VlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VlanPlugin.pm
@@ -37,6 +37,7 @@ sub options {
 	dns => { optional => 1 },
 	reversedns => { optional => 1 },
 	dnszone => { optional => 1 },
+        ipam => { optional => 0 },
     };
 }
 
diff --git a/PVE/Network/SDN/Zones/VxlanPlugin.pm b/PVE/Network/SDN/Zones/VxlanPlugin.pm
index 7a6687a..e8870a0 100644
--- a/PVE/Network/SDN/Zones/VxlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VxlanPlugin.pm
@@ -41,6 +41,7 @@ sub options {
 	dns => { optional => 1 },
 	reversedns => { optional => 1 },
 	dnszone => { optional => 1 },
+	ipam => { optional => 0 },
     };
 }
 
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 31/35] subnets/ipam: allow same subnet on different zones
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (29 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 30/35] move ipam option from subnet " Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 32/35] vnets: allow duplicate tags in differents zones Alexandre Derumier
                   ` (3 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Subnets.pm        |  13 ++--
 PVE/API2/Network/SDN/Vnets.pm          |  14 ++--
 PVE/API2/Network/SDN/Zones.pm          |  11 +++
 PVE/Network/SDN/Dns/PowerdnsPlugin.pm  |   6 +-
 PVE/Network/SDN/Ipams/NetboxPlugin.pm  |  18 ++---
 PVE/Network/SDN/Ipams/PVEPlugin.pm     | 102 ++++++++++++++++---------
 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm |  20 ++---
 PVE/Network/SDN/Ipams/Plugin.pm        |   6 +-
 PVE/Network/SDN/SubnetPlugin.pm        |  26 +++++--
 PVE/Network/SDN/Subnets.pm             |  28 ++++---
 PVE/Network/SDN/VnetPlugin.pm          |  31 ++++----
 PVE/Network/SDN/Vnets.pm               |  18 +++--
 PVE/Network/SDN/Zones/EvpnPlugin.pm    |   3 +-
 PVE/Network/SDN/Zones/SimplePlugin.pm  |   5 +-
 14 files changed, 188 insertions(+), 113 deletions(-)

diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index 5ea4fc4..0fb9420 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -28,7 +28,6 @@ my $api_sdn_subnets_config = sub {
 
     my $scfg = dclone(PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id));
     $scfg->{subnet} = $id;
-    $scfg->{cidr} = $id =~ s/-/\//r;
     $scfg->{digest} = $cfg->{digest};
 
     return $scfg;
@@ -169,7 +168,6 @@ __PACKAGE__->register_method ({
 
 	my $type = extract_param($param, 'type');
 	my $cidr = extract_param($param, 'subnet');
-	my $id = $cidr =~ s/\//-/r;
 
         # create /etc/pve/sdn directory
         PVE::Cluster::check_cfs_quorum();
@@ -184,7 +182,9 @@ __PACKAGE__->register_method ({
 		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;
@@ -193,7 +193,9 @@ __PACKAGE__->register_method ({
 		}
 
 		$cfg->{ids}->{$id} = $opts;
-		PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $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);
 
@@ -238,7 +240,8 @@ __PACKAGE__->register_method ({
 
 	    raise_param_exc({ ipam => "you can't change ipam"}) if $opts->{ipam} && $scfg->{ipam} && $opts->{ipam} ne $scfg->{ipam};
 
-	    PVE::Network::SDN::SubnetPlugin->on_update_hook($zone, $id, $opts, $scfg);
+	    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);
 
diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
index 6ff61c5..3f99f58 100644
--- a/PVE/API2/Network/SDN/Vnets.pm
+++ b/PVE/API2/Network/SDN/Vnets.pm
@@ -19,6 +19,7 @@ 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;
 
@@ -195,9 +196,7 @@ __PACKAGE__->register_method ({
 	    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
             $plugin->vnet_update_hook($cfg->{ids}->{$id});
 
-	    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
-
-	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg);
+	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
 
 	    PVE::Network::SDN::Vnets::write_config($cfg);
 
@@ -228,7 +227,12 @@ __PACKAGE__->register_method ({
 
 	    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();
@@ -237,9 +241,7 @@ __PACKAGE__->register_method ({
 	    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($plugin_config->{type});
 	    $plugin->vnet_update_hook($cfg->{ids}->{$id});
 
-	    my $subnet_cfg = PVE::Network::SDN::Subnets::config();
-
-	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg, $subnet_cfg);
+	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
 
 	    PVE::Network::SDN::Vnets::write_config($cfg);
 
diff --git a/PVE/API2/Network/SDN/Zones.pm b/PVE/API2/Network/SDN/Zones.pm
index 54f087d..5ae577b 100644
--- a/PVE/API2/Network/SDN/Zones.pm
+++ b/PVE/API2/Network/SDN/Zones.pm
@@ -9,6 +9,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file);
 use PVE::Network::SDN;
 use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Zones;
+use PVE::Network::SDN::Subnets;
 use PVE::Network::SDN::Dns;
 use PVE::Network::SDN::Zones::Plugin;
 use PVE::Network::SDN::Zones::VlanPlugin;
@@ -263,6 +264,16 @@ __PACKAGE__->register_method ({
 	    my $plugin = PVE::Network::SDN::Zones::Plugin->lookup($scfg->{type});
 	    my $opts = $plugin->check_config($id, $param, 0, 1);
 
+	    if($opts->{ipam} ne $scfg->{ipam}) {
+
+		#don't allow ipam change if subnet are defined
+		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);
+		    raise_param_exc({ ipam => "can't change ipam if subnet if already defined for this zone"}) if $subnet->{zone} eq $id;
+		}
+	    }
+
 	    foreach my $k (%$opts) {
 		$scfg->{$k} = $opts->{$k};
 	    }
diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
index 5b98e87..b00432e 100644
--- a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
+++ b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
@@ -186,11 +186,11 @@ sub verify_zone {
 }
 
 sub get_reversedns_zone {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 
-    my ($network, $mask) = split(/-/, $subnetid);
+    my $cidr = $subnet->{cidr};
+    my $mask = $subnet->{mask};
 
-    my $cidr = "$ip/$mask";
     my $zone = "";
 
     if (Net::IP::ip_is_ipv4($ip)) {
diff --git a/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
index c25f451..8695b7d 100644
--- a/PVE/Network/SDN/Ipams/NetboxPlugin.pm
+++ b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -30,7 +30,7 @@ sub options {
 sub add_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
     my $gateway = $subnet->{gateway};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
@@ -40,13 +40,11 @@ sub add_subnet {
 
     #create subnet
     if (!$internalid) {
-	my ($network, $mask) = split(/-/, $subnetid);
 
 	my $params = { prefix => $cidr };
 
 	eval {
 		my $result = PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/prefixes/", $headers, $params);
-		$subnet->{ipamid} = $result->{id} if defined($result->{id});
 	};
 	if ($@) {
 	    die "error add subnet to ipam: $@";
@@ -58,7 +56,7 @@ sub add_subnet {
 sub del_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $gateway = $subnet->{gateway};
@@ -66,9 +64,8 @@ sub del_subnet {
 
     my $internalid = get_prefix_id($url, $cidr, $headers);
     return if !$internalid;
-    #fixme: check that prefix is empty exluding gateway, before delete
 
-    PVE::Network::SDN::Ipams::NetboxPlugin::del_ip($class, $plugin_config, $subnetid, $gateway) if $gateway;
+    return; #fixme: check that prefix is empty exluding gateway, before delete
 
     eval {
 	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers);
@@ -80,9 +77,9 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
 
-    my ($network, $mask) = split(/-/, $subnetid);
+    my $mask = $subnet->{mask};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
@@ -102,7 +99,8 @@ sub add_ip {
 sub add_next_freeip {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    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"];
@@ -125,7 +123,7 @@ sub add_next_freeip {
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 
     return if !$ip;
 
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index a3ad3d6..601ad26 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -7,6 +7,7 @@ use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file
 use PVE::Tools;
 use JSON;
 use NetAddr::IP;
+use Net::IP;
 use Digest::SHA;
 
 use base('PVE::Network::SDN::Ipams::Plugin');
@@ -33,15 +34,22 @@ sub options {
 sub add_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
     my $gateway = $subnet->{gateway};
 
+
     cfs_lock_file($ipamdb_file, undef, sub {
-	my $config = read_db();
-	#create subnet
-	if (!defined($config->{subnets}->{$cidr})) {
-	    $config->{subnets}->{$cidr}->{ips} = {};
-	    write_db($config);
+	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 $@;
@@ -50,14 +58,19 @@ sub add_subnet {
 sub del_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
 
     cfs_lock_file($ipamdb_file, undef, sub {
 
 	my $db = read_db();
-	my $ips = $db->{subnets}->{$cidr}->{ips};
+	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};
+	my $ips = $dbsubnet->{ips};
 	die "can't delete subnet $cidr , not empty" if keys %{$ips} > 0;
-	delete $db->{subnets}->{$cidr};
+	delete $dbzone->{subnets}->{$cidr};
 	write_db($db);
     });
     die "$@" if $@;
@@ -65,19 +78,22 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
 
     cfs_lock_file($ipamdb_file, undef, sub {
 
 	my $db = read_db();
-	my $s = $db->{subnets}->{$cidr};
 
-	die "ip $ip already exist" if defined($s->{ips}->{$ip});
+	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};
 
-	#verify that ip is valid for this subnet
-	$s->{ips}->{$ip} = 1;
+	die "ip $ip already exist" if defined($dbsubnet->{ips}->{$ip});
+	$dbsubnet->{ips}->{$ip} = 1;
 	write_db($db);
     });
     die "$@" if $@;
@@ -86,49 +102,65 @@ sub add_ip {
 sub add_next_freeip {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    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 $s = $db->{subnets}->{$cidr};
-	my $iplist = new NetAddr::IP($cidr);
-	my $broadcast = $iplist->broadcast();
-
-	while(1) {
-	    $iplist++;
-	    last if $iplist eq $broadcast;
-	    my $ip = $iplist->addr();
-	    next if defined($s->{ips}->{$ip});
-	    $freeip = $ip;
-	    last;
+	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};
+
+	if (Net::IP::ip_is_ipv4($network) && $mask == 32) {
+	    die "can't find free ip in subnet $cidr" if defined($dbsubnet->{ips}->{$network});
+	    $freeip = $network;
+	} else {
+
+	    my $iplist = new NetAddr::IP($cidr);
+	    my $broadcast = $iplist->broadcast();
+
+	    while(1) {
+		$iplist++;
+		last if $iplist eq $broadcast;
+		my $ip = $iplist->addr();
+		next if defined($dbsubnet->{ips}->{$ip});
+		$freeip = $ip;
+		last;
+	    }
 	}
 
 	die "can't find free ip in subnet $cidr" if !$freeip;
   
-	$s->{ips}->{$freeip} = 1;
+	$dbsubnet->{ips}->{$freeip} = 1;
 	write_db($db);
     });
     die "$@" if $@;
 
-    my ($network, $mask) = split(/-/, $subnetid);
     return "$freeip/$mask";
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $zone = $subnet->{zone};
 
     cfs_lock_file($ipamdb_file, undef, sub {
 
 	my $db = read_db();
-	my $s = $db->{subnets}->{$cidr};
-	return if !$ip;
+	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 pam" if !defined($dbsubnet->{ips}->{$ip});
+	delete $dbsubnet->{ips}->{$ip};
 
-	die "ip $ip does not exist in pam" if !defined($s->{ips}->{$ip});
-	delete $s->{ips}->{$ip};
 	write_db($db);
     });
     die "$@" if $@;
diff --git a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
index d7ba3ed..324f1b2 100644
--- a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
@@ -40,7 +40,10 @@ sub options {
 sub add_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    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};
@@ -52,7 +55,6 @@ sub add_subnet {
 
     #create subnet
     if (!$internalid) {
-	my ($network, $mask) = split(/-/, $subnetid);
 
 	my $params = { subnet => $network,
 		   mask => $mask,
@@ -72,7 +74,7 @@ sub add_subnet {
 sub del_subnet {
     my ($class, $plugin_config, $subnetid, $subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
@@ -81,7 +83,7 @@ sub del_subnet {
     my $internalid = get_internalid($url, $cidr, $headers);
     return if !$internalid;
 
-    #fixme: check that prefix is empty exluding gateway, before delete
+    return; #fixme: check that prefix is empty exluding gateway, before delete
 
     eval {
 	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/subnets/$internalid", $headers);
@@ -93,9 +95,9 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
@@ -120,7 +122,8 @@ sub add_ip {
 sub add_next_freeip {
     my ($class, $plugin_config, $subnetid, $subnet, $internalid, $hostname) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};  
+    my $mask = $subnet->{mask};  
     my $url = $plugin_config->{url};
     my $token = $plugin_config->{token};
     my $section = $plugin_config->{section};
@@ -140,12 +143,11 @@ sub add_next_freeip {
         die "can't find free ip in subnet $cidr: $@";
     }
 
-    my ($network, $mask) = split(/-/, $subnetid);
     return "$ip/$mask";
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 
     return if !$ip;
 
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
index 683346c..a2ade3b 100644
--- a/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -75,16 +75,16 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $internalid, $ip, $hostname, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
 
 }
 
 sub add_next_freeip {
-    my ($class, $plugin_config) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
 }
 
 sub del_ip {
-    my ($class, $plugin_config, $subnetid, $ip) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 }
 
 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index a5d03f6..1444262 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -10,6 +10,7 @@ use PVE::Exception qw(raise raise_param_exc);
 use Net::Subnet qw(subnet_matcher);
 use PVE::Network::SDN::Vnets;
 use PVE::Network::SDN::Ipams;
+use Net::IP;
 
 PVE::Cluster::cfs_register_file('sdn/subnets.cfg',
                                  sub { __PACKAGE__->parse_config(@_); },
@@ -25,7 +26,13 @@ PVE::JSONSchema::register_format('pve-sdn-subnet-id', \&parse_sdn_subnet_id);
 sub parse_sdn_subnet_id {
     my ($id, $noerr) = @_;
 
-    my $cidr = $id =~ s/-/\//r;
+    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)))
@@ -91,7 +98,9 @@ sub options {
 sub on_update_hook {
     my ($class, $zone, $subnetid, $subnet, $old_subnet) = @_;
 
-    my $cidr = $subnetid =~ s/-/\//r;
+    my $cidr = $subnet->{cidr};
+    my $mask = $subnet->{mask};
+
     my $subnet_matcher = subnet_matcher($cidr);
 
     my $vnetid = $subnet->{vnet};
@@ -109,9 +118,11 @@ sub on_update_hook {
 	raise_param_exc({ vnet => "you can't add a subnet on a vlanaware vnet"}) if $vnet->{vlanaware};
     }
 
-    my ($ip, $mask) = split(/\//, $cidr);
+    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 $subnetid"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
+    raise_param_exc({ gateway => "$gateway is not in subnet $cidr"}) if $gateway && !$subnet_matcher->($gateway) && !$pointopoint;
+
 
     if ($ipam) {
 	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
@@ -119,7 +130,10 @@ sub on_update_hook {
 	my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
 	$plugin->add_subnet($plugin_config, $subnetid, $subnet);
 
-	#delete on removal
+	#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);
@@ -130,7 +144,7 @@ sub on_update_hook {
 	    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway);
 	}
 
-	#delete old ip after update
+	#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);
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 50130d5..bd1eb36 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -21,6 +21,14 @@ sub sdn_subnets_config {
     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;
 }
 
@@ -64,13 +72,15 @@ sub get_subnet {
 }
 
 sub find_ip_subnet {
-    my ($ip, $subnets) = @_;
+    my ($ip, $mask, $subnets) = @_;
 
     my $subnet = undef;
     my $subnetid = undef;
 
     foreach my $id (sort keys %{$subnets}) {
-	my $cidr = $id =~ s/-/\//r;
+
+	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};
@@ -94,14 +104,14 @@ my $verify_dns_zone = sub {
 };
 
 my $get_reversedns_zone = sub {
-    my ($subnetid, $dns, $ip) = @_;
+    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, $ip);
+    $plugin->get_reversedns_zone($plugin_config, $subnetid, $subnet, $ip);
 };
 
 my $add_dns_record = sub {
@@ -181,7 +191,7 @@ sub next_free_ip {
     }
 
     eval {
-	my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
+	my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
 
 	#add dns
 	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
@@ -208,7 +218,7 @@ sub add_ip {
     my $dns = $zone->{dns};
     my $dnszone = $zone->{dnszone};
     my $reversedns = $zone->{reversedns};
-    my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
+    my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
     my $dnszoneprefix = $subnet->{dnszoneprefix};
 
     #verify dns zones before ipam
@@ -220,7 +230,7 @@ sub add_ip {
 	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, $ip);
+	    $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip);
 	};
 	die $@ if $@;
     }
@@ -250,7 +260,7 @@ sub del_ip {
     my $dns = $zone->{dns};
     my $dnszone = $zone->{dnszone};
     my $reversedns = $zone->{reversedns};
-    my $reversednszone = &$get_reversedns_zone($subnetid, $reversedns, $ip);
+    my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
     my $dnszoneprefix = $subnet->{dnszoneprefix};
 
     &$verify_dns_zone($dnszone, $dns);
@@ -260,7 +270,7 @@ sub del_ip {
 	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, $ip);
+	$plugin->del_ip($plugin_config, $subnetid, $subnet, $ip);
     }
 
     eval {
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index 8481f0d..518d2dd 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -91,29 +91,28 @@ sub on_delete_hook {
 
     #verify if subnets are associated
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
-    my @subnetlist = ();
-    foreach my $subnetid (sort keys %{$subnets}) {
-	push @subnetlist, $subnetid;
-    }
-    raise_param_exc({ vnet => "Vnet is attached to following subnets:". join(',', @subnetlist)}) if @subnetlist > 0;
+    raise_param_exc({ vnet => "Can't delete vnet if subnets exists"}) if $subnets;
 }
 
 sub on_update_hook {
-    my ($class, $vnetid, $vnet_cfg, $subnet_cfg) = @_;
+    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;
+    }
 
-    #fixme : don't allow change zone if subnets are defined
-    #fixme : don't vlanaware change if subnets are defined
-#    my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
-   
     # verify that tag is not already defined in another vnet
-    if (defined($vnet_cfg->{ids}->{$vnetid}->{tag})) {
-	my $tag = $vnet_cfg->{ids}->{$vnetid}->{tag};
+    if (defined($tag)) {
 	foreach my $id (keys %{$vnet_cfg->{ids}}) {
 	    next if $id eq $vnetid;
-	    my $vnet = $vnet_cfg->{ids}->{$id};
-	    if ($vnet->{type} eq 'vnet' && defined($vnet->{tag})) {
-		raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $tag eq $vnet->{tag};
-	    }
+	    my $othervnettag = $vnet_cfg->{ids}->{$id}->{tag};
+	    raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $othervnettag && $tag eq $othervnettag;
 	}
     }
 }
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index d08db51..6d11003 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -66,10 +66,10 @@ sub get_vnet {
 sub get_subnets {
     my ($vnetid) = @_;
 
-    my $subnets = {};
+    my $subnets = undef;
     my $subnets_cfg = PVE::Network::SDN::Subnets::config();
     foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
-	my $subnet = $subnets_cfg->{ids}->{$subnetid};
+	my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
 	next if !$subnet->{vnet} || $subnet->{vnet} ne $vnetid;
 	$subnets->{$subnetid} = $subnet;
     }
@@ -77,7 +77,7 @@ sub get_subnets {
 
 }
 
-sub get_next_free_ip {
+sub get_next_free_cidr {
     my ($vnetid, $hostname, $ipversion) = @_;
 
     my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
@@ -91,7 +91,7 @@ sub get_next_free_ip {
 
     foreach my $subnetid (sort keys %{$subnets}) {
         my $subnet = $subnets->{$subnetid};
-	my ($network, $mask) = split(/-/, $subnetid);
+	my $network = $subnet->{network};
 
 	next if $ipversion != Net::IP::ip_get_version($network);
 	$subnetcount++;
@@ -108,7 +108,7 @@ sub get_next_free_ip {
     return $ip;
 }
 
-sub add_ip {
+sub add_cidr {
     my ($vnetid, $cidr, $hostname) = @_;
 
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
@@ -117,12 +117,13 @@ sub add_ip {
     my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
 
     my ($ip, $mask) = split(/\//, $cidr);
-    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
+    die "ip address is not in cidr format" if !$mask;
+    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets);
 
     PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname);
 }
 
-sub del_ip {
+sub del_cidr {
     my ($vnetid, $cidr, $hostname) = @_;
 
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
@@ -131,7 +132,8 @@ sub del_ip {
     my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
 
     my ($ip, $mask) = split(/\//, $cidr);
-    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
+    die "ip address is not in cidr format" if !$mask;
+    my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets);
 
     PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname);
 }
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 723b3cc..62ab817 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -86,7 +86,8 @@ sub generate_sdn_config {
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     foreach my $subnetid (sort keys %{$subnets}) {
 	my $subnet = $subnets->{$subnetid};
-	my $cidr = $subnetid =~ s/-/\//r;
+	my $cidr = $subnet->{cidr};
+
 	my $gateway = $subnet->{gateway};
 	if ($gateway) {
 	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index fe0f20f..5294485 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -60,14 +60,15 @@ sub generate_sdn_config {
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     foreach my $subnetid (sort keys %{$subnets}) {
 	my $subnet = $subnets->{$subnetid};
-	my $cidr = $subnetid =~ s/-/\//r; 
+	my $cidr = $subnet->{cidr};
+	my $mask = $subnet->{mask};
+
 	my $gateway = $subnet->{gateway};
 	if ($gateway) {
 	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
 	    $address->{$gateway} = 1;
 	}
 	#add route for /32 pointtopoint
-	my ($ip, $mask) = split(/\//, $cidr);
 	push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32;
 	if ($subnet->{snat}) {
 	    #find outgoing interface
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 32/35] vnets: allow duplicate tags in differents zones
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (30 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 31/35] subnets/ipam: allow same subnet on different zones Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 33/35] ipam: verify api access on create/update Alexandre Derumier
                   ` (2 subsequent siblings)
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

only vxlan need to be unique globally.

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Vnets.pm         |  4 ++--
 PVE/Network/SDN/VnetPlugin.pm         |  9 ---------
 PVE/Network/SDN/Zones/EvpnPlugin.pm   | 20 +++++++++++++++++---
 PVE/Network/SDN/Zones/Plugin.pm       |  2 +-
 PVE/Network/SDN/Zones/QinQPlugin.pm   | 14 +++++++++++++-
 PVE/Network/SDN/Zones/SimplePlugin.pm |  7 +++++--
 PVE/Network/SDN/Zones/VlanPlugin.pm   | 14 +++++++++++++-
 PVE/Network/SDN/Zones/VxlanPlugin.pm  | 20 +++++++++++++++++---
 8 files changed, 68 insertions(+), 22 deletions(-)

diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
index 3f99f58..84cf433 100644
--- a/PVE/API2/Network/SDN/Vnets.pm
+++ b/PVE/API2/Network/SDN/Vnets.pm
@@ -194,7 +194,7 @@ __PACKAGE__->register_method ({
 	    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->{ids}->{$id});
+            $plugin->vnet_update_hook($cfg, $id, $zone_cfg);
 
 	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
 
@@ -239,7 +239,7 @@ __PACKAGE__->register_method ({
 	    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->{ids}->{$id});
+	    $plugin->vnet_update_hook($cfg, $id, $zone_cfg);
 
 	    PVE::Network::SDN::VnetPlugin->on_update_hook($id, $cfg);
 
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index 518d2dd..cac578a 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -106,15 +106,6 @@ sub on_update_hook {
 	my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
 	raise_param_exc({ vlanaware => "vlanaware vnet is not compatible with subnets"}) if $subnets;
     }
-
-    # verify that tag is not already defined in another vnet
-    if (defined($tag)) {
-	foreach my $id (keys %{$vnet_cfg->{ids}}) {
-	    next if $id eq $vnetid;
-	    my $othervnettag = $vnet_cfg->{ids}->{$id}->{tag};
-	    raise_param_exc({ tag => "tag $tag already exist in vnet $id"}) if $othervnettag && $tag eq $othervnettag;
-	}
-    }
 }
 
 1;
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 62ab817..5338a1b 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -181,10 +181,24 @@ sub on_update_hook {
 
 
 sub vnet_update_hook {
-    my ($class, $vnet) = @_;
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
 
-    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($vnet->{tag});
-    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $vnet->{tag} > 16777216;
+    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;
+    }
 
     if (!defined($vnet->{mac})) {
 	my $dc = PVE::Cluster::cfs_read_file('datacenter.cfg');
diff --git a/PVE/Network/SDN/Zones/Plugin.pm b/PVE/Network/SDN/Zones/Plugin.pm
index 1f24269..6fc13eb 100644
--- a/PVE/Network/SDN/Zones/Plugin.pm
+++ b/PVE/Network/SDN/Zones/Plugin.pm
@@ -144,7 +144,7 @@ sub on_update_hook {
 }
 
 sub vnet_update_hook {
-    my ($class, $vnet) = @_;
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
 
     # do nothing by default
 }
diff --git a/PVE/Network/SDN/Zones/QinQPlugin.pm b/PVE/Network/SDN/Zones/QinQPlugin.pm
index aadfd27..5d40db8 100644
--- a/PVE/Network/SDN/Zones/QinQPlugin.pm
+++ b/PVE/Network/SDN/Zones/QinQPlugin.pm
@@ -216,10 +216,22 @@ sub status {
 }
 
 sub vnet_update_hook {
-    my ($class, $vnet) = @_;
+    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/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index 5294485..c4f4475 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -118,9 +118,12 @@ sub status {
 
 
 sub vnet_update_hook {
-    my ($class, $vnet) = @_;
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
 
-    raise_param_exc({ tag => "vlan tag is not allowed on simple bridge"}) if defined($vnet->{tag});
+    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');
diff --git a/PVE/Network/SDN/Zones/VlanPlugin.pm b/PVE/Network/SDN/Zones/VlanPlugin.pm
index e1ae75b..7af9b2c 100644
--- a/PVE/Network/SDN/Zones/VlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VlanPlugin.pm
@@ -175,10 +175,22 @@ sub status {
 }
 
 sub vnet_update_hook {
-    my ($class, $vnet) = @_;
+    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
index e8870a0..1fe16b8 100644
--- a/PVE/Network/SDN/Zones/VxlanPlugin.pm
+++ b/PVE/Network/SDN/Zones/VxlanPlugin.pm
@@ -94,10 +94,24 @@ sub generate_sdn_config {
 }
 
 sub vnet_update_hook {
-    my ($class, $vnet) = @_;
+    my ($class, $vnet_cfg, $vnetid, $zone_cfg) = @_;
 
-    raise_param_exc({ tag => "missing vxlan tag"}) if !defined($vnet->{tag});
-    raise_param_exc({ tag => "vxlan tag max value is 16777216"}) if $vnet->{tag} > 16777216;
+    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;
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 33/35] ipam: verify api access on create/update
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (31 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 32/35] vnets: allow duplicate tags in differents zones Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 34/35] ipam: add hostname/description to ipam db Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 35/35] update documentation Alexandre Derumier
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Ipams.pm          |  6 ++++++
 PVE/Network/SDN/Ipams/NetboxPlugin.pm  | 24 +++++++++++++++++++++++-
 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 24 +++++++++++++++++++++++-
 PVE/Network/SDN/Ipams/Plugin.pm        |  4 ++++
 4 files changed, 56 insertions(+), 2 deletions(-)

diff --git a/PVE/API2/Network/SDN/Ipams.pm b/PVE/API2/Network/SDN/Ipams.pm
index 0d567c8..6410e8e 100644
--- a/PVE/API2/Network/SDN/Ipams.pm
+++ b/PVE/API2/Network/SDN/Ipams.pm
@@ -150,6 +150,10 @@ __PACKAGE__->register_method ({
 
 		$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");
@@ -190,6 +194,8 @@ __PACKAGE__->register_method ({
 		$scfg->{$k} = $opts->{$k};
 	    }
 
+            $plugin->on_update_hook($scfg);
+
 	    PVE::Network::SDN::Ipams::write_config($ipam_cfg);
 
 	    }, "update sdn ipam object failed");
diff --git a/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
index 8695b7d..d696b08 100644
--- a/PVE/Network/SDN/Ipams/NetboxPlugin.pm
+++ b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -138,10 +138,32 @@ sub del_ip {
 	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/ipam/ip-addresses/$ip_id/", $headers);
     };
     if ($@) {
-	die "error delete ip $ip";
+	die "error delete ip $ip : $@";
     }
 }
 
+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::Ipams::Plugin::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 {
diff --git a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
index 324f1b2..f89ef29 100644
--- a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
@@ -162,10 +162,32 @@ sub del_ip {
 	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/addresses/$ip_id", $headers);
     };
     if ($@) {
-	die "error delete ip $ip";
+	die "error delete ip $ip: $@";
     }
 }
 
+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::Ipams::Plugin::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
 
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
index a2ade3b..4c68287 100644
--- a/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -87,6 +87,10 @@ sub del_ip {
     my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
 }
 
+sub on_update_hook {
+    my ($class, $plugin_config)  = @_;
+}
+
 
 #helpers
 sub api_request {
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 34/35] ipam: add hostname/description to ipam db
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (32 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 33/35] ipam: verify api access on create/update Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 35/35] update documentation Alexandre Derumier
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Ipams/NetboxPlugin.pm  |  8 +++---
 PVE/Network/SDN/Ipams/PVEPlugin.pm     | 16 ++++++++----
 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 12 ++++++---
 PVE/Network/SDN/Ipams/Plugin.pm        |  2 +-
 PVE/Network/SDN/SubnetPlugin.pm        |  4 ++-
 PVE/Network/SDN/Subnets.pm             | 36 ++++++++++++++------------
 PVE/Network/SDN/Vnets.pm               |  8 +++---
 7 files changed, 50 insertions(+), 36 deletions(-)

diff --git a/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
index d696b08..298634d 100644
--- a/PVE/Network/SDN/Ipams/NetboxPlugin.pm
+++ b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -77,7 +77,7 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $description, $is_gateway) = @_;
 
     my $mask = $subnet->{mask};
     my $url = $plugin_config->{url};
@@ -85,7 +85,7 @@ sub add_ip {
     my $section = $plugin_config->{section};
     my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
 
-    my $params = { address => "$ip/$mask" };
+    my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
 
     eval {
 	PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/ip-addresses/", $headers, $params);
@@ -97,7 +97,7 @@ sub add_ip {
 }
 
 sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $description) = @_;
 
     my $cidr = $subnet->{cidr};
 
@@ -107,7 +107,7 @@ sub add_next_freeip {
 
     my $internalid = get_prefix_id($url, $cidr, $headers);
 
-    my $params = {};
+    my $params = { dns_name => $hostname, description => $description };
 
     my $ip = undef;
     eval {
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 601ad26..b0fd72f 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -78,7 +78,7 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $description, $is_gateway) = @_;
 
     my $cidr = $subnet->{cidr};
     my $zone = $subnet->{zone};
@@ -93,14 +93,17 @@ sub add_ip {
 	my $dbsubnet = $dbzone->{subnets}->{$cidr};
 
 	die "ip $ip already exist" if defined($dbsubnet->{ips}->{$ip});
-	$dbsubnet->{ips}->{$ip} = 1;
+	my $dbip = {};
+	$dbip->{hostname} = $hostname;
+	$dbip->{description} = $description;
+	$dbsubnet->{ips}->{$ip} = $dbip;
 	write_db($db);
     });
     die "$@" if $@;
 }
 
 sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $description) = @_;
 
     my $cidr = $subnet->{cidr};
     my $network = $subnet->{network};
@@ -135,8 +138,11 @@ sub add_next_freeip {
 	}
 
 	die "can't find free ip in subnet $cidr" if !$freeip;
-  
-	$dbsubnet->{ips}->{$freeip} = 1;
+
+	my $dbip = {};
+	$dbip->{hostname} = $hostname;
+	$dbip->{description} = $description;
+	$dbsubnet->{ips}->{$freeip} = $dbip;
 	write_db($db);
     });
     die "$@" if $@;
diff --git a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
index f89ef29..6261764 100644
--- a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
@@ -95,7 +95,7 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $description, $is_gateway) = @_;
 
     my $cidr = $subnet->{cidr};
     my $url = $plugin_config->{url};
@@ -108,6 +108,8 @@ sub add_ip {
     my $params = { ip => $ip,
 		   subnetId => $internalid,
 		   is_gateway => $is_gateway,
+		   hostname => $hostname,
+		   description => $description,
 		  };
 
     eval {
@@ -120,7 +122,7 @@ sub add_ip {
 }
 
 sub add_next_freeip {
-    my ($class, $plugin_config, $subnetid, $subnet, $internalid, $hostname) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $hostname, $description) = @_;
 
     my $cidr = $subnet->{cidr};  
     my $mask = $subnet->{mask};  
@@ -129,9 +131,11 @@ sub add_next_freeip {
     my $section = $plugin_config->{section};
     my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
 
-    $internalid = get_internalid($url, $cidr, $headers) if !$internalid;
+    my $internalid = get_internalid($url, $cidr, $headers);
 
-    my $params = {};
+    my $params = { hostname => $hostname,
+		   description => $description,
+		  };
 
     my $ip = undef;
     eval {
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
index 4c68287..065225c 100644
--- a/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -75,7 +75,7 @@ sub del_subnet {
 }
 
 sub add_ip {
-    my ($class, $plugin_config, $subnetid, $subnet, $ip, $is_gateway) = @_;
+    my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $description, $is_gateway) = @_;
 
 }
 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 1444262..cb0f4ef 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -141,7 +141,9 @@ sub on_update_hook {
 	    warn if $@;
 	}
         if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
-	    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway);
+	    my $hostname = "$vnetid-gw";
+	    my $description = "$vnetid gw";
+	    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway, $hostname, $description, 1);
 	}
 
 	#delete old gateway after update
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index bd1eb36..09aa942 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -115,11 +115,9 @@ my $get_reversedns_zone = sub {
 };
 
 my $add_dns_record = sub {
-    my ($zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
+    my ($zone, $dns, $hostname, $ip) = @_;
     return if !$zone || !$dns || !$hostname || !$ip;
 
-    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
-
     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});
@@ -128,11 +126,10 @@ my $add_dns_record = sub {
 };
 
 my $add_dns_ptr_record = sub {
-    my ($reversezone, $zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
+    my ($reversezone, $zone, $dns, $hostname, $ip) = @_;
 
     return if !$zone || !$reversezone || !$dns || !$hostname || !$ip;
 
-    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
     $hostname .= ".$zone";
     my $dns_cfg = PVE::Network::SDN::Dns::config();
     my $plugin_config = $dns_cfg->{ids}->{$dns};
@@ -141,12 +138,10 @@ my $add_dns_ptr_record = sub {
 };
 
 my $del_dns_record = sub {
-    my ($zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
+    my ($zone, $dns, $hostname, $ip) = @_;
 
     return if !$zone || !$dns || !$hostname || !$ip;
 
-    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
-
     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});
@@ -165,10 +160,11 @@ my $del_dns_ptr_record = sub {
 };
 
 sub next_free_ip {
-    my ($zone, $subnetid, $subnet, $hostname) = @_;
+    my ($zone, $subnetid, $subnet, $hostname, $description) = @_;
 
     my $cidr = undef;
     my $ip = undef;
+    $description = '' if !$description;
 
     my $ipamid = $zone->{ipam};
     my $dns = $zone->{dns};
@@ -176,6 +172,8 @@ sub next_free_ip {
     my $reversedns = $zone->{reversedns};
     my $dnszoneprefix = $subnet->{dnszoneprefix};
 
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
     #verify dns zones before ipam
     &$verify_dns_zone($dnszone, $dns);
 
@@ -184,7 +182,7 @@ sub next_free_ip {
 	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);
+	    $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $description);
 	    ($ip, undef) = split(/\//, $cidr);
 	};
 	die $@ if $@;
@@ -194,9 +192,9 @@ sub next_free_ip {
 	my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
 
 	#add dns
-	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+	&$add_dns_record($dnszone, $dns, $hostname, $ip);
 	#add reverse dns
-	&$add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $dnszoneprefix, $ip);
+	&$add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
     };
     if ($@) {
 	#rollback
@@ -210,7 +208,7 @@ sub next_free_ip {
 }
 
 sub add_ip {
-    my ($zone, $subnetid, $subnet, $ip, $hostname) = @_;
+    my ($zone, $subnetid, $subnet, $ip, $hostname, $description) = @_;
 
     return if !$subnet || !$ip; 
 
@@ -221,6 +219,8 @@ sub add_ip {
     my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
     my $dnszoneprefix = $subnet->{dnszoneprefix};
 
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
     #verify dns zones before ipam
     &$verify_dns_zone($dnszone, $dns);
     &$verify_dns_zone($reversednszone, $reversedns);
@@ -230,16 +230,16 @@ sub add_ip {
 	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);
+	    $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $description);
 	};
 	die $@ if $@;
     }
 
     eval {
 	#add dns
-	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+	&$add_dns_record($dnszone, $dns, $hostname, $ip);
 	#add reverse dns
-	&$add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $dnszoneprefix, $ip);
+	&$add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $ip);
     };
     if ($@) {
 	#rollback
@@ -262,6 +262,8 @@ sub del_ip {
     my $reversedns = $zone->{reversedns};
     my $reversednszone = &$get_reversedns_zone($subnetid, $subnet, $reversedns, $ip);
     my $dnszoneprefix = $subnet->{dnszoneprefix};
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
 
     &$verify_dns_zone($dnszone, $dns);
     &$verify_dns_zone($reversednszone, $reversedns);
@@ -274,7 +276,7 @@ sub del_ip {
     }
 
     eval {
-	&$del_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+	&$del_dns_record($dnszone, $dns, $hostname, $ip);
 	&$del_dns_ptr_record($reversednszone, $reversedns, $ip);
     };
     if ($@) {
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 6d11003..5616419 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -78,7 +78,7 @@ sub get_subnets {
 }
 
 sub get_next_free_cidr {
-    my ($vnetid, $hostname, $ipversion) = @_;
+    my ($vnetid, $hostname, $description, $ipversion) = @_;
 
     my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
     my $zoneid = $vnet->{zone};
@@ -97,7 +97,7 @@ sub get_next_free_cidr {
 	$subnetcount++;
 	if ($zone->{ipam}) {
 	    eval {
-		$ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname);
+		$ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $description);
 	    };
 	    warn $@ if $@;
 	}
@@ -109,7 +109,7 @@ sub get_next_free_cidr {
 }
 
 sub add_cidr {
-    my ($vnetid, $cidr, $hostname) = @_;
+    my ($vnetid, $cidr, $hostname, $description) = @_;
 
     my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
     my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
@@ -120,7 +120,7 @@ sub add_cidr {
     die "ip address is not in cidr format" if !$mask;
     my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets);
 
-    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname);
+    PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $description);
 }
 
 sub del_cidr {
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* [pve-devel] [PATCH v10 pve-network 35/35] update documentation
  2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
                   ` (33 preceding siblings ...)
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 34/35] ipam: add hostname/description to ipam db Alexandre Derumier
@ 2020-10-05 15:09 ` Alexandre Derumier
  34 siblings, 0 replies; 37+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:09 UTC (permalink / raw)
  To: pve-devel

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 test/documentation.txt | 33 ++++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/test/documentation.txt b/test/documentation.txt
index 316e5af..7886966 100644
--- a/test/documentation.txt
+++ b/test/documentation.txt
@@ -2,28 +2,47 @@ Here a sample of command with pvesh to manage the sdn.
 
 
 #create a vlan transportzone
-pvesh create /cluster/sdn/zones/ --zone vlanzone --type vlan --bridge vmbr0
+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 --peers 192.168.0.1,192.168.0.2,192.168.0.3
+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 controller
 pvesh create /cluster/sdn/controllers/ --controller frrrouter1 --type evpn --peers 192.168.0.1,192.168.0.2,192.168.0.3 --asn 1234 --gateway-nodes pxnode1,pxnode2 --gateway-external-peers 192.168.0.253,192.168.0.254
 
 #create a layer2 vxlan bgpevpn transportzone
-pvesh create /cluster/sdn/zones/ --zone layer2evpnzone --type evpn --controller frrrouter1
+pvesh create /cluster/sdn/zones/ --zone layer2evpnzone --type evpn --ipam pve --controller frrrouter1
 
 #create a layer3 routable vxlan bgpevpn transportzone
-pvesh create /cluster/sdn/zones/ --zone layer3evpnzone --type evpn --controller frrrouter1 --vrf-vxlan 4000
+pvesh create /cluster/sdn/zones/ --zone layer3evpnzone --type evpn --ipam pve --controller frrrouter1 --vrf-vxlan 4000
 
 
 #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 ip for evpn routing
-pvesh create /cluster/sdn/vnets/ --vnet vnet11 --type vnet --zone layer3evpnzone --tag 11 --ipv4 10.0.0.1/24 --mac c8:1f:66:f8:62:8d
-pvesh create /cluster/sdn/vnets/ --vnet vnet12 --type vnet --zone layer3evpnzone --tag 12 --ipv4 10.0.1.1/24 --mac c8:1f:66:f8:62:8e
+#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
-- 
2.20.1




^ permalink raw reply	[flat|nested] 37+ messages in thread

* Re: [pve-devel] [PATCH v10 pve-network 27/35] api: add running/pending zones/vnets/subnets/controllers
  2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 27/35] api: add running/pending zones/vnets/subnets/controllers Alexandre Derumier
@ 2020-10-08  9:04   ` Thomas Lamprecht
  0 siblings, 0 replies; 37+ messages in thread
From: Thomas Lamprecht @ 2020-10-08  9:04 UTC (permalink / raw)
  To: Proxmox VE development discussion, Alexandre Derumier

On 05.10.20 17:09, Alexandre Derumier wrote:
> diff --git a/PVE/API2/Network/SDN/Vnets.pm b/PVE/API2/Network/SDN/Vnets.pm
> index 0fbb747..6ff61c5 100644
> --- a/PVE/API2/Network/SDN/Vnets.pm
> +++ b/PVE/API2/Network/SDN/Vnets.pm
> @@ -3,6 +3,8 @@ package PVE::API2::Network::SDN::Vnets;
>  use strict;
>  use warnings;
>  
> +use Hash::Diff qw( diff );

You add this use statement here, but never use it nor add the corresponding
dependency to debian/control (would probably be libhash-diff-perl?)

Can I just drop this line or do I overlook something?

> +
>  use PVE::SafeSyslog;
>  use PVE::Tools qw(extract_param);
>  use PVE::Cluster qw(cfs_read_file cfs_write_file);
> @@ -33,10 +35,22 @@ my $api_sdn_vnets_config = sub {
>      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 => '',
> @@ -49,6 +63,18 @@ __PACKAGE__->register_method ({
>      },
>      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',
> @@ -64,7 +90,17 @@ __PACKAGE__->register_method ({
>  	my $rpcenv = PVE::RPCEnvironment::get();
>  	my $authuser = $rpcenv->get_user();
>  
> -	my $cfg = PVE::Network::SDN::Vnets::config();
> +	my $cfg = {};
> +	if($param->{pending}) {
> +	    my $running_cfg = PVE::Network::SDN::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::config();
> +	    $cfg = $running_cfg->{vnets};
> +	} else {
> +	    $cfg = PVE::Network::SDN::Vnets::config();
> +	}
>  
>  	my @sids = PVE::Network::SDN::Vnets::sdn_vnets_ids($cfg);
>  	my $res = [];
> @@ -93,13 +129,33 @@ __PACKAGE__->register_method ({
>  	    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 = PVE::Network::SDN::Vnets::config();
> +	my $cfg = {};
> +	if($param->{pending}) {
> +	    my $running_cfg = PVE::Network::SDN::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::config();
> +	    $cfg = $running_cfg->{vnets};
> +	} else {
> +	    $cfg = PVE::Network::SDN::Vnets::config();
> +	}
>  
>  	return $api_sdn_vnets_config->($cfg, $param->{vnet});
>      }});




^ permalink raw reply	[flat|nested] 37+ messages in thread

end of thread, other threads:[~2020-10-08  9:04 UTC | newest]

Thread overview: 37+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-10-05 15:08 [pve-devel] [PATCH v10 pve-network 00/35] add subnet plugin Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 01/35] " Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 02/35] vnets: add subnets Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 03/35] add subnets verifications hooks Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 04/35] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 05/35] zone: add vnet_update_hook Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 06/35] vnets: subnets: use cidr Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 07/35] subnet: fix on_delete_hook Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 08/35] api2: subnet create: convert cidr to subnetid Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 09/35] api2: increase version on apply/reload only Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 10/35] add ipams plugins Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 11/35] add pve internal ipam plugin Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 12/35] vnets: find_free_ip : add ipversion detection Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 13/35] vnets: add add_ip Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 14/35] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 15/35] add dns plugin Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 16/35] Fix vnet gateway for routed setup + /32 pointopoint subnet Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 17/35] ipam : pveplugin : fix find_next_free_ip Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 18/35] add vnet to subnets && remove subnetlist from vnet Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 19/35] zones: evpn|simple: add snat iptables rules Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 20/35] subnet: disable route option for now and add dns domain format Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 21/35] dns: fix reverse dns Alexandre Derumier
2020-10-05 15:08 ` [pve-devel] [PATCH v10 pve-network 22/35] subnets: move api to /sdn/vnet/<vnet>/subnets && make vnet option not optionnal Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 23/35] zones: evpn : fix raise exception Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 24/35] subnet: make ipam not optionnal and use pve ipam as default Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 25/35] don't allow subnets on vlanware vnet Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 26/35] generate sdn/.running-config on apply Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 27/35] api: add running/pending zones/vnets/subnets/controllers Alexandre Derumier
2020-10-08  9:04   ` Thomas Lamprecht
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 28/35] small bugfixes Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 29/35] move dns options from subnets to zone Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 30/35] move ipam option from subnet " Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 31/35] subnets/ipam: allow same subnet on different zones Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 32/35] vnets: allow duplicate tags in differents zones Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 33/35] ipam: verify api access on create/update Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 34/35] ipam: add hostname/description to ipam db Alexandre Derumier
2020-10-05 15:09 ` [pve-devel] [PATCH v10 pve-network 35/35] update documentation Alexandre Derumier

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal