public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management
@ 2020-09-24  8:40 Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 01/25] add subnet plugin Alexandre Derumier
                   ` (24 more replies)
  0 siblings, 25 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 UTC (permalink / raw)
  To: pve-devel

This patch series add basic subnets managements.

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

Alexandre Derumier (25):
  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

 PVE/API2/Network/SDN.pm                |  17 ++
 PVE/API2/Network/SDN/Controllers.pm    |   6 -
 PVE/API2/Network/SDN/Dns.pm            | 242 +++++++++++++++++++++++
 PVE/API2/Network/SDN/Ipams.pm          | 242 +++++++++++++++++++++++
 PVE/API2/Network/SDN/Makefile          |   2 +-
 PVE/API2/Network/SDN/Subnets.pm        | 250 ++++++++++++++++++++++++
 PVE/API2/Network/SDN/Vnets.pm          |  22 ++-
 PVE/API2/Network/SDN/Zones.pm          |   6 -
 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.pm               |  70 +++++++
 PVE/Network/SDN/Ipams/Makefile         |   8 +
 PVE/Network/SDN/Ipams/NetboxPlugin.pm  | 169 ++++++++++++++++
 PVE/Network/SDN/Ipams/PVEPlugin.pm     | 166 ++++++++++++++++
 PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 189 ++++++++++++++++++
 PVE/Network/SDN/Ipams/Plugin.pm        | 127 ++++++++++++
 PVE/Network/SDN/Makefile               |   4 +-
 PVE/Network/SDN/SubnetPlugin.pm        | 182 +++++++++++++++++
 PVE/Network/SDN/Subnets.pm             | 257 +++++++++++++++++++++++++
 PVE/Network/SDN/VnetPlugin.pm          |  24 +--
 PVE/Network/SDN/Vnets.pm               |  65 +++++++
 PVE/Network/SDN/Zones.pm               |   4 +-
 PVE/Network/SDN/Zones/EvpnPlugin.pm    |  55 +++++-
 PVE/Network/SDN/Zones/Plugin.pm        |   7 +-
 PVE/Network/SDN/Zones/QinQPlugin.pm    |  10 +-
 PVE/Network/SDN/Zones/SimplePlugin.pm  |  46 ++++-
 PVE/Network/SDN/Zones/VlanPlugin.pm    |  10 +-
 PVE/Network/SDN/Zones/VxlanPlugin.pm   |  16 +-
 debian/control                         |   3 +
 test/generateconfig.pl                 |   3 +-
 32 files changed, 2510 insertions(+), 75 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 01/25] add subnet plugin
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 02/25] vnets: add subnets Alexandre Derumier
                   ` (23 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 02/25] vnets: add subnets
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 01/25] add subnet plugin Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 03/25] add subnets verifications hooks Alexandre Derumier
                   ` (22 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 03/25] add subnets verifications hooks
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 01/25] add subnet plugin Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 02/25] vnets: add subnets Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 04/25] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
                   ` (21 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 04/25] zones: simple|evpn: add gateway ip from subnets to vnet
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (2 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 03/25] add subnets verifications hooks Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 05/25] zone: add vnet_update_hook Alexandre Derumier
                   ` (20 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 05/25] zone: add vnet_update_hook
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (3 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 04/25] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 06/25] vnets: subnets: use cidr Alexandre Derumier
                   ` (19 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 06/25] vnets: subnets: use cidr
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (4 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 05/25] zone: add vnet_update_hook Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 07/25] subnet: fix on_delete_hook Alexandre Derumier
                   ` (18 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 07/25] subnet: fix on_delete_hook
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (5 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 06/25] vnets: subnets: use cidr Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 08/25] api2: subnet create: convert cidr to subnetid Alexandre Derumier
                   ` (17 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 08/25] api2: subnet create: convert cidr to subnetid
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (6 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 07/25] subnet: fix on_delete_hook Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only Alexandre Derumier
                   ` (16 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (7 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 08/25] api2: subnet create: convert cidr to subnetid Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-25  7:15   ` Thomas Lamprecht
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 10/25] add ipams plugins Alexandre Derumier
                   ` (15 subsequent siblings)
  24 siblings, 1 reply; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 10/25] add ipams plugins
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (8 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 11/25] add pve internal ipam plugin Alexandre Derumier
                   ` (14 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 11/25] add pve internal ipam plugin
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (9 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 10/25] add ipams plugins Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 12/25] vnets: find_free_ip : add ipversion detection Alexandre Derumier
                   ` (13 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 12/25] vnets: find_free_ip : add ipversion detection
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (10 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 11/25] add pve internal ipam plugin Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 13/25] vnets: add add_ip Alexandre Derumier
                   ` (12 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 13/25] vnets: add add_ip
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (11 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 12/25] vnets: find_free_ip : add ipversion detection Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 14/25] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
                   ` (11 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 14/25] vnets: add del_ip + rework add_ip/find_free_ip
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (12 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 13/25] vnets: add add_ip Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 15/25] add dns plugin Alexandre Derumier
                   ` (10 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 15/25] add dns plugin
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (13 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 14/25] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 16/25] Fix vnet gateway for routed setup + /32 pointopoint subnet Alexandre Derumier
                   ` (9 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 16/25] Fix vnet gateway for routed setup + /32 pointopoint subnet
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (14 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 15/25] add dns plugin Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 17/25] ipam : pveplugin : fix find_next_free_ip Alexandre Derumier
                   ` (8 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 17/25] ipam : pveplugin : fix find_next_free_ip
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (15 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 16/25] Fix vnet gateway for routed setup + /32 pointopoint subnet Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 18/25] add vnet to subnets && remove subnetlist from vnet Alexandre Derumier
                   ` (7 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 18/25] add vnet to subnets && remove subnetlist from vnet
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (16 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 17/25] ipam : pveplugin : fix find_next_free_ip Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 19/25] zones: evpn|simple: add snat iptables rules Alexandre Derumier
                   ` (6 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 19/25] zones: evpn|simple: add snat iptables rules
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (17 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 18/25] add vnet to subnets && remove subnetlist from vnet Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 20/25] subnet: disable route option for now and add dns domain format Alexandre Derumier
                   ` (5 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 20/25] subnet: disable route option for now and add dns domain format
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (18 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 19/25] zones: evpn|simple: add snat iptables rules Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 21/25] dns: fix reverse dns Alexandre Derumier
                   ` (4 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 21/25] dns: fix reverse dns
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (19 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 20/25] subnet: disable route option for now and add dns domain format Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 22/25] subnets: move api to /sdn/vnet/<vnet>/subnets && make vnet option not optionnal Alexandre Derumier
                   ` (3 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 22/25] subnets: move api to /sdn/vnet/<vnet>/subnets && make vnet option not optionnal
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (20 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 21/25] dns: fix reverse dns Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 23/25] zones: evpn : fix raise exception Alexandre Derumier
                   ` (2 subsequent siblings)
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 23/25] zones: evpn : fix raise exception
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (21 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 22/25] subnets: move api to /sdn/vnet/<vnet>/subnets && make vnet option not optionnal Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 24/25] subnet: make ipam not optionnal and use pve ipam as default Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 25/25] don't allow subnets on vlanware vnet Alexandre Derumier
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 24/25] subnet: make ipam not optionnal and use pve ipam as default
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (22 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 23/25] zones: evpn : fix raise exception Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 25/25] don't allow subnets on vlanware vnet Alexandre Derumier
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* [pve-devel] [PATCH v8 pve-network 25/25] don't allow subnets on vlanware vnet
  2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
                   ` (23 preceding siblings ...)
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 24/25] subnet: make ipam not optionnal and use pve ipam as default Alexandre Derumier
@ 2020-09-24  8:40 ` Alexandre Derumier
  24 siblings, 0 replies; 36+ messages in thread
From: Alexandre Derumier @ 2020-09-24  8:40 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] 36+ messages in thread

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only Alexandre Derumier
@ 2020-09-25  7:15   ` Thomas Lamprecht
  2020-09-25  8:35     ` Alexandre DERUMIER
  0 siblings, 1 reply; 36+ messages in thread
From: Thomas Lamprecht @ 2020-09-25  7:15 UTC (permalink / raw)
  To: Proxmox VE development discussion, Alexandre Derumier

On 24.09.20 10:40, Alexandre Derumier wrote:
> 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(-)
> 


but how do you detect pending changes now?




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

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-25  7:15   ` Thomas Lamprecht
@ 2020-09-25  8:35     ` Alexandre DERUMIER
  2020-09-25  8:39       ` Alexandre DERUMIER
  0 siblings, 1 reply; 36+ messages in thread
From: Alexandre DERUMIER @ 2020-09-25  8:35 UTC (permalink / raw)
  To: Thomas Lamprecht; +Cc: Proxmox VE development discussion

>>but how do you detect pending changes now?

Well, the feature was mainly to detect pending change after reload.
if a reload don't have applied correctly on a node, or if a node was down.

I don't known if we want to display to user "pending config" changes, not yet applied ?

Befor this commit, It's displaying warning after any config change,
and it's difficult to known if a problem occur after the reload.




----- Mail original -----
De: "Thomas Lamprecht" <t.lamprecht@proxmox.com>
À: "Proxmox VE development discussion" <pve-devel@lists.proxmox.com>, "aderumier" <aderumier@odiso.com>
Envoyé: Vendredi 25 Septembre 2020 09:15:44
Objet: Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only

On 24.09.20 10:40, Alexandre Derumier wrote: 
> 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(-) 
> 


but how do you detect pending changes now? 




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

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-25  8:35     ` Alexandre DERUMIER
@ 2020-09-25  8:39       ` Alexandre DERUMIER
  2020-09-25  9:06         ` Thomas Lamprecht
  0 siblings, 1 reply; 36+ messages in thread
From: Alexandre DERUMIER @ 2020-09-25  8:39 UTC (permalink / raw)
  To: Thomas Lamprecht; +Cc: Proxmox VE development discussion

also,

for example, when you add a new vnet in a zone,

it was displaying a warning all vnets/zones for pending changes.

as I don't have enough granularity currently (a global version info in /etc/network/interfaces.d/sdn, or we should have some kind of versioning info by vnet in /etc/network/interfaces.d/sdn)





----- Mail original -----
De: "aderumier" <aderumier@odiso.com>
À: "Thomas Lamprecht" <t.lamprecht@proxmox.com>
Cc: "Proxmox VE development discussion" <pve-devel@lists.proxmox.com>
Envoyé: Vendredi 25 Septembre 2020 10:35:02
Objet: Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only

>>but how do you detect pending changes now? 

Well, the feature was mainly to detect pending change after reload. 
if a reload don't have applied correctly on a node, or if a node was down. 

I don't known if we want to display to user "pending config" changes, not yet applied ? 

Befor this commit, It's displaying warning after any config change, 
and it's difficult to known if a problem occur after the reload. 




----- Mail original ----- 
De: "Thomas Lamprecht" <t.lamprecht@proxmox.com> 
À: "Proxmox VE development discussion" <pve-devel@lists.proxmox.com>, "aderumier" <aderumier@odiso.com> 
Envoyé: Vendredi 25 Septembre 2020 09:15:44 
Objet: Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only 

On 24.09.20 10:40, Alexandre Derumier wrote: 
> 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(-) 
> 


but how do you detect pending changes now? 




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

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-25  8:39       ` Alexandre DERUMIER
@ 2020-09-25  9:06         ` Thomas Lamprecht
  2020-09-26  5:29           ` Alexandre DERUMIER
  0 siblings, 1 reply; 36+ messages in thread
From: Thomas Lamprecht @ 2020-09-25  9:06 UTC (permalink / raw)
  To: Alexandre DERUMIER; +Cc: Proxmox VE development discussion

On 25.09.20 10:35, Alexandre DERUMIER wrote:
>>> but how do you detect pending changes now?
> 
> Well, the feature was mainly to detect pending change after reload. 
> if a reload don't have applied correctly on a node, or if a node was down. 
> 
> I don't known if we want to display to user "pending config" changes, not yet applied ?

I'd like to have that.

> 
> Befor this commit, It's displaying warning after any config change, 
> and it's difficult to known if a problem occur after the reload. 

On 25.09.20 10:39, Alexandre DERUMIER wrote:
> also,
> 
> for example, when you add a new vnet in a zone,
> 
> it was displaying a warning all vnets/zones for pending changes.
> 
> as I don't have enough granularity currently (a global version info in /etc/network/interfaces.d/sdn, or we should have some kind of versioning info by vnet in /etc/network/interfaces.d/sdn)
> 
> 

Having two versions, the enacted and a pending, could be enough

* if both are the same all is applied
* if pending is newer we can show it, but new changes should not further
  increase the version, they are seen as part of the current pending stuff.
* if pending is older, bug but don't care?

So on each change we bump $pending to $enacted + 1 (*not* $pending++) after we
wrote the changes out. We could make /etc/pve/sdn/.version more structured, either
json map or something like:
enacted=3
pending=4

(json could be more flexible)

An apply sets $enacted to $pending once finished (without errors).

This would be simple, not much to track but still give the admin info if anything
is pending. What do you think?





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

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-25  9:06         ` Thomas Lamprecht
@ 2020-09-26  5:29           ` Alexandre DERUMIER
  2020-09-26  6:51             ` Thomas Lamprecht
  0 siblings, 1 reply; 36+ messages in thread
From: Alexandre DERUMIER @ 2020-09-26  5:29 UTC (permalink / raw)
  To: Thomas Lamprecht; +Cc: Proxmox VE development discussion

>>
>>Having two versions, the enacted and a pending, could be enough
>>
>>* if both are the same all is applied
>>* if pending is newer we can show it, but new changes should not further
>>  increase the version, they are seen as part of the current pending stuff.
>>* if pending is older, bug but don't care?

>>So on each change we bump $pending to $enacted + 1 (*not* $pending++) after we
>>wrote the changes out. We could make /etc/pve/sdn/.version more structured, either
>>json map or something like:
>>enacted=3
>>pending=4
>>
>>(json could be more flexible)
>>
>>An apply sets $enacted to $pending once finished (without errors).
>>
>>This would be simple, not much to track but still give the admin info if anything
>>is pending. What do you think?


I was thinking about another way, where user could also manualing edit /etc/pve/sdn/*.cfg files
(or with some automations tools like puppet,ansible,... to manage their network).

I was think about this:

sdn/*.cfg  are the pending config,  we don't increase any version counter here

when when apply config, we increase version but also we generate a json dump of configurations (vnets,zones,controllers,subnets,...).
(instead .version file, maybe create a .running-config file, with the json + version in the json)


This json dump of configuration with be the source to generate the local configuration of each node.


Like this, we could also display pending change for each vnets,zones,...(or a simple display a "status:pending" in a new column in the config grid for a specific element)
and user is still able to modify *.cfg manually.

what do you think about this ?


----- Mail original -----
De: "Thomas Lamprecht" <t.lamprecht@proxmox.com>
À: "aderumier" <aderumier@odiso.com>
Cc: "Proxmox VE development discussion" <pve-devel@lists.proxmox.com>
Envoyé: Vendredi 25 Septembre 2020 11:06:10
Objet: Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only

On 25.09.20 10:35, Alexandre DERUMIER wrote: 
>>> but how do you detect pending changes now? 
> 
> Well, the feature was mainly to detect pending change after reload. 
> if a reload don't have applied correctly on a node, or if a node was down. 
> 
> I don't known if we want to display to user "pending config" changes, not yet applied ? 

I'd like to have that. 

> 
> Befor this commit, It's displaying warning after any config change, 
> and it's difficult to known if a problem occur after the reload. 

On 25.09.20 10:39, Alexandre DERUMIER wrote: 
> also, 
> 
> for example, when you add a new vnet in a zone, 
> 
> it was displaying a warning all vnets/zones for pending changes. 
> 
> as I don't have enough granularity currently (a global version info in /etc/network/interfaces.d/sdn, or we should have some kind of versioning info by vnet in /etc/network/interfaces.d/sdn) 
> 
> 

Having two versions, the enacted and a pending, could be enough 

* if both are the same all is applied 
* if pending is newer we can show it, but new changes should not further 
increase the version, they are seen as part of the current pending stuff. 
* if pending is older, bug but don't care? 

So on each change we bump $pending to $enacted + 1 (*not* $pending++) after we 
wrote the changes out. We could make /etc/pve/sdn/.version more structured, either 
json map or something like: 
enacted=3 
pending=4 

(json could be more flexible) 

An apply sets $enacted to $pending once finished (without errors). 

This would be simple, not much to track but still give the admin info if anything 
is pending. What do you think? 




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

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-26  5:29           ` Alexandre DERUMIER
@ 2020-09-26  6:51             ` Thomas Lamprecht
  2020-09-27  6:27               ` Alexandre DERUMIER
  0 siblings, 1 reply; 36+ messages in thread
From: Thomas Lamprecht @ 2020-09-26  6:51 UTC (permalink / raw)
  To: Alexandre DERUMIER; +Cc: Proxmox VE development discussion

On 26.09.20 07:29, Alexandre DERUMIER wrote:
> I was thinking about another way, where user could also manualing edit /etc/pve/sdn/*.cfg files
> (or with some automations tools like puppet,ansible,... to manage their network).
> 
> I was think about this:
> 
> sdn/*.cfg  are the pending config,  we don't increase any version counter here
> 
> when when apply config, we increase version but also we generate a json dump of configurations (vnets,zones,controllers,subnets,...).
> (instead .version file, maybe create a .running-config file, with the json + version in the json)
> 
> 
> This json dump of configuration with be the source to generate the local configuration of each node.
> 
> 
> Like this, we could also display pending change for each vnets,zones,...(or a simple display a "status:pending" in a new column in the config grid for a specific element)
> and user is still able to modify *.cfg manually.
> 
> what do you think about this ?


sounds good to me.

But, do you think complex setups could outgrow the 512k pmxcfs file limit for
big setups?





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

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-26  6:51             ` Thomas Lamprecht
@ 2020-09-27  6:27               ` Alexandre DERUMIER
  2020-09-28  5:13                 ` Alexandre DERUMIER
  0 siblings, 1 reply; 36+ messages in thread
From: Alexandre DERUMIER @ 2020-09-27  6:27 UTC (permalink / raw)
  To: Thomas Lamprecht; +Cc: Proxmox VE development discussion

>>But, do you think complex setups could outgrow the 512k pmxcfs file limit for
>>big setups

I think it should be enough,

zones/controllers/ipam/dns should be small, vnets + subnets should be the biggest part.

around 50bytes by vnet  and around 80bytes by subnet with all options.

so around 150bytes for 1vnet+1subnet.,  this should give use around 3000vnets/subnets.


I think it's enough the current cluster size (20-40nodes max), so maybe 5000vms max
by cluster,  3000vnets should be enough.







----- Mail original -----
De: "Thomas Lamprecht" <t.lamprecht@proxmox.com>
À: "Alexandre Derumier" <aderumier@odiso.com>
Cc: "Proxmox VE development discussion" <pve-devel@lists.proxmox.com>
Envoyé: Samedi 26 Septembre 2020 08:51:42
Objet: Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only

On 26.09.20 07:29, Alexandre DERUMIER wrote: 
> I was thinking about another way, where user could also manualing edit /etc/pve/sdn/*.cfg files 
> (or with some automations tools like puppet,ansible,... to manage their network). 
> 
> I was think about this: 
> 
> sdn/*.cfg are the pending config, we don't increase any version counter here 
> 
> when when apply config, we increase version but also we generate a json dump of configurations (vnets,zones,controllers,subnets,...). 
> (instead .version file, maybe create a .running-config file, with the json + version in the json) 
> 
> 
> This json dump of configuration with be the source to generate the local configuration of each node. 
> 
> 
> Like this, we could also display pending change for each vnets,zones,...(or a simple display a "status:pending" in a new column in the config grid for a specific element) 
> and user is still able to modify *.cfg manually. 
> 
> what do you think about this ? 


sounds good to me. 

But, do you think complex setups could outgrow the 512k pmxcfs file limit for 
big setups?



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

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-27  6:27               ` Alexandre DERUMIER
@ 2020-09-28  5:13                 ` Alexandre DERUMIER
  2020-09-28  7:28                   ` Thomas Lamprecht
  0 siblings, 1 reply; 36+ messages in thread
From: Alexandre DERUMIER @ 2020-09-28  5:13 UTC (permalink / raw)
  To: Proxmox VE development discussion; +Cc: Thomas Lamprecht

I'll send a patch today or tomorrow (works fine with the ./running-config generated file)

I'm currently polishing the gui to diplay changes in grids.


----- Mail original -----
De: "aderumier" <aderumier@odiso.com>
À: "Thomas Lamprecht" <t.lamprecht@proxmox.com>
Cc: "Proxmox VE development discussion" <pve-devel@lists.proxmox.com>
Envoyé: Dimanche 27 Septembre 2020 08:27:57
Objet: Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only

>>But, do you think complex setups could outgrow the 512k pmxcfs file limit for 
>>big setups 

I think it should be enough, 

zones/controllers/ipam/dns should be small, vnets + subnets should be the biggest part. 

around 50bytes by vnet and around 80bytes by subnet with all options. 

so around 150bytes for 1vnet+1subnet., this should give use around 3000vnets/subnets. 


I think it's enough the current cluster size (20-40nodes max), so maybe 5000vms max 
by cluster, 3000vnets should be enough. 







----- Mail original ----- 
De: "Thomas Lamprecht" <t.lamprecht@proxmox.com> 
À: "Alexandre Derumier" <aderumier@odiso.com> 
Cc: "Proxmox VE development discussion" <pve-devel@lists.proxmox.com> 
Envoyé: Samedi 26 Septembre 2020 08:51:42 
Objet: Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only 

On 26.09.20 07:29, Alexandre DERUMIER wrote: 
> I was thinking about another way, where user could also manualing edit /etc/pve/sdn/*.cfg files 
> (or with some automations tools like puppet,ansible,... to manage their network). 
> 
> I was think about this: 
> 
> sdn/*.cfg are the pending config, we don't increase any version counter here 
> 
> when when apply config, we increase version but also we generate a json dump of configurations (vnets,zones,controllers,subnets,...). 
> (instead .version file, maybe create a .running-config file, with the json + version in the json) 
> 
> 
> This json dump of configuration with be the source to generate the local configuration of each node. 
> 
> 
> Like this, we could also display pending change for each vnets,zones,...(or a simple display a "status:pending" in a new column in the config grid for a specific element) 
> and user is still able to modify *.cfg manually. 
> 
> what do you think about this ? 


sounds good to me. 

But, do you think complex setups could outgrow the 512k pmxcfs file limit for 
big setups? 

_______________________________________________ 
pve-devel mailing list 
pve-devel@lists.proxmox.com 
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel 




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

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-28  5:13                 ` Alexandre DERUMIER
@ 2020-09-28  7:28                   ` Thomas Lamprecht
  2020-09-28  8:20                     ` Alexandre DERUMIER
  0 siblings, 1 reply; 36+ messages in thread
From: Thomas Lamprecht @ 2020-09-28  7:28 UTC (permalink / raw)
  To: Alexandre DERUMIER, Proxmox VE development discussion

On 28.09.20 07:13, Alexandre DERUMIER wrote:
> I'll send a patch today or tomorrow (works fine with the ./running-config generated file)

Would it help if I apply the v8 stuff until the patch which drops the
version increase?

> 
> I'm currently polishing the gui to diplay changes in grids.

cool.




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

* Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only
  2020-09-28  7:28                   ` Thomas Lamprecht
@ 2020-09-28  8:20                     ` Alexandre DERUMIER
  0 siblings, 0 replies; 36+ messages in thread
From: Alexandre DERUMIER @ 2020-09-28  8:20 UTC (permalink / raw)
  To: Thomas Lamprecht; +Cc: Proxmox VE development discussion

>>Would it help if I apply the v8 stuff until the patch which drops the 
>>version increase? 

I can already send a first patch (pve-cluster && pve-network)  for handling /sdn/.running-version.



Then I have another patch for pve-network api && pve-manager which need to be polished, mainly for display changes,....
I think I need for time for this.


----- Mail original -----
De: "Thomas Lamprecht" <t.lamprecht@proxmox.com>
À: "aderumier" <aderumier@odiso.com>, "Proxmox VE development discussion" <pve-devel@lists.proxmox.com>
Envoyé: Lundi 28 Septembre 2020 09:28:30
Objet: Re: [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only

On 28.09.20 07:13, Alexandre DERUMIER wrote: 
> I'll send a patch today or tomorrow (works fine with the ./running-config generated file) 

Would it help if I apply the v8 stuff until the patch which drops the 
version increase? 

> 
> I'm currently polishing the gui to diplay changes in grids. 

cool. 




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

end of thread, other threads:[~2020-09-28  8:20 UTC | newest]

Thread overview: 36+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-09-24  8:40 [pve-devel] [PATCH v8 pve-network 00/25] sdn: add subnets management Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 01/25] add subnet plugin Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 02/25] vnets: add subnets Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 03/25] add subnets verifications hooks Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 04/25] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 05/25] zone: add vnet_update_hook Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 06/25] vnets: subnets: use cidr Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 07/25] subnet: fix on_delete_hook Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 08/25] api2: subnet create: convert cidr to subnetid Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 09/25] api2: increase version on apply/reload only Alexandre Derumier
2020-09-25  7:15   ` Thomas Lamprecht
2020-09-25  8:35     ` Alexandre DERUMIER
2020-09-25  8:39       ` Alexandre DERUMIER
2020-09-25  9:06         ` Thomas Lamprecht
2020-09-26  5:29           ` Alexandre DERUMIER
2020-09-26  6:51             ` Thomas Lamprecht
2020-09-27  6:27               ` Alexandre DERUMIER
2020-09-28  5:13                 ` Alexandre DERUMIER
2020-09-28  7:28                   ` Thomas Lamprecht
2020-09-28  8:20                     ` Alexandre DERUMIER
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 10/25] add ipams plugins Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 11/25] add pve internal ipam plugin Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 12/25] vnets: find_free_ip : add ipversion detection Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 13/25] vnets: add add_ip Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 14/25] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 15/25] add dns plugin Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 16/25] Fix vnet gateway for routed setup + /32 pointopoint subnet Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 17/25] ipam : pveplugin : fix find_next_free_ip Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 18/25] add vnet to subnets && remove subnetlist from vnet Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 19/25] zones: evpn|simple: add snat iptables rules Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 20/25] subnet: disable route option for now and add dns domain format Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 21/25] dns: fix reverse dns Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 22/25] subnets: move api to /sdn/vnet/<vnet>/subnets && make vnet option not optionnal Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 23/25] zones: evpn : fix raise exception Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 24/25] subnet: make ipam not optionnal and use pve ipam as default Alexandre Derumier
2020-09-24  8:40 ` [pve-devel] [PATCH v8 pve-network 25/25] don't allow subnets on vlanware vnet Alexandre Derumier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal