public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH v9 pve-network 00/26] add subnet plugin
@ 2020-10-05 15:07 Alexandre Derumier
  2020-10-05 15:07 ` [pve-devel] [PATCH v9 pve-network 01/26] " Alexandre Derumier
                   ` (25 more replies)
  0 siblings, 26 replies; 28+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:07 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

Changelog v9:

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


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

 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.pm                     |  57 ++++--
 PVE/Network/SDN/Controllers.pm         |  12 +-
 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             | 264 +++++++++++++++++++++++++
 PVE/Network/SDN/VnetPlugin.pm          |  24 +--
 PVE/Network/SDN/Vnets.pm               |  77 +++++++-
 PVE/Network/SDN/Zones.pm               |  27 +--
 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                 |   5 +-
 34 files changed, 2591 insertions(+), 107 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] 28+ messages in thread

* [pve-devel] [PATCH v9 pve-network 01/26] add subnet plugin
  2020-10-05 15:07 [pve-devel] [PATCH v9 pve-network 00/26] add subnet plugin Alexandre Derumier
@ 2020-10-05 15:07 ` Alexandre Derumier
  2020-10-05 15:07 ` [pve-devel] [PATCH v9 pve-network 02/26] vnets: add subnets Alexandre Derumier
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:07 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] 28+ messages in thread

* [pve-devel] [PATCH v9 pve-network 02/26] vnets: add subnets
  2020-10-05 15:07 [pve-devel] [PATCH v9 pve-network 00/26] add subnet plugin Alexandre Derumier
  2020-10-05 15:07 ` [pve-devel] [PATCH v9 pve-network 01/26] " Alexandre Derumier
@ 2020-10-05 15:07 ` Alexandre Derumier
  2020-10-05 15:08 ` [pve-devel] [PATCH v9 pve-network 03/26] add subnets verifications hooks Alexandre Derumier
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 28+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:07 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] 28+ messages in thread

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

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

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




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

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

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

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




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

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

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

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

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




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

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

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

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




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

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

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

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




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

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

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

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




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

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

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

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




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

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

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

diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
index 175f76f..6055fe5 100644
--- a/PVE/API2/Network/SDN.pm
+++ b/PVE/API2/Network/SDN.pm
@@ -16,6 +16,7 @@ use PVE::API2::Network::SDN::Controllers;
 use PVE::API2::Network::SDN::Vnets;
 use PVE::API2::Network::SDN::Zones;
 use PVE::API2::Network::SDN::Subnets;
+use PVE::API2::Network::SDN::Ipams;
 
 use base qw(PVE::RESTHandler);
 
@@ -39,6 +40,11 @@ __PACKAGE__->register_method ({
     path => 'subnets',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Ipams",
+    path => 'ipams',
+});
+
 __PACKAGE__->register_method({
     name => 'index',
     path => '',
@@ -69,6 +75,7 @@ __PACKAGE__->register_method({
 	    { id => 'zones' },
 	    { id => 'controllers' },
 	    { id => 'subnets' },
+	    { id => 'ipams' },
 	];
 
 	return $res;
diff --git a/PVE/API2/Network/SDN/Ipams.pm b/PVE/API2/Network/SDN/Ipams.pm
new file mode 100644
index 0000000..f8665a1
--- /dev/null
+++ b/PVE/API2/Network/SDN/Ipams.pm
@@ -0,0 +1,241 @@
+package PVE::API2::Network::SDN::Ipams;
+
+use strict;
+use warnings;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Network::SDN;
+use PVE::Network::SDN::Ipams;
+use PVE::Network::SDN::Ipams::Plugin;
+use PVE::Network::SDN::Ipams::PhpIpamPlugin;
+use PVE::Network::SDN::Ipams::NetboxPlugin;
+
+use Storable qw(dclone);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $sdn_ipams_type_enum = PVE::Network::SDN::Ipams::Plugin->lookup_types();
+
+my $api_sdn_ipams_config = sub {
+    my ($cfg, $id) = @_;
+
+    my $scfg = dclone(PVE::Network::SDN::Ipams::sdn_ipams_config($cfg, $id));
+    $scfg->{ipam} = $id;
+    $scfg->{digest} = $cfg->{digest};
+
+    return $scfg;
+};
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "SDN ipams index.",
+    permissions => {
+	description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/ipams/<ipam>'",
+	user => 'all',
+    },
+    parameters => {
+    	additionalProperties => 0,
+	properties => {
+	    type => {
+		description => "Only list sdn ipams of specific type",
+		type => 'string',
+		enum => $sdn_ipams_type_enum,
+		optional => 1,
+	    },
+	},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => "object",
+	    properties => { ipam => { type => 'string'},
+			    type => { type => 'string'},
+			  },
+	},
+	links => [ { rel => 'child', href => "{ipam}" } ],
+    },
+    code => sub {
+	my ($param) = @_;
+
+	my $rpcenv = PVE::RPCEnvironment::get();
+	my $authuser = $rpcenv->get_user();
+
+
+	my $cfg = PVE::Network::SDN::Ipams::config();
+
+	my @sids = PVE::Network::SDN::Ipams::sdn_ipams_ids($cfg);
+	my $res = [];
+	foreach my $id (@sids) {
+	    my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+	    next if !$rpcenv->check_any($authuser, "/sdn/ipams/$id", $privs, 1);
+
+	    my $scfg = &$api_sdn_ipams_config($cfg, $id);
+	    next if $param->{type} && $param->{type} ne $scfg->{type};
+
+	    my $plugin_config = $cfg->{ids}->{$id};
+	    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+	    push @$res, $scfg;
+	}
+
+	return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'read',
+    path => '{ipam}',
+    method => 'GET',
+    description => "Read sdn ipam configuration.",
+    permissions => {
+	check => ['perm', '/sdn/ipams/{ipam}', ['SDN.Allocate']],
+   },
+
+    parameters => {
+    	additionalProperties => 0,
+	properties => {
+	    ipam => get_standard_option('pve-sdn-ipam-id'),
+	},
+    },
+    returns => { type => 'object' },
+    code => sub {
+	my ($param) = @_;
+
+	my $cfg = PVE::Network::SDN::Ipams::config();
+
+	return &$api_sdn_ipams_config($cfg, $param->{ipam});
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    path => '',
+    method => 'POST',
+    description => "Create a new sdn ipam object.",
+    permissions => {
+	check => ['perm', '/sdn/ipams', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Ipams::Plugin->createSchema(),
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $type = extract_param($param, 'type');
+	my $id = extract_param($param, 'ipam');
+
+	my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($type);
+	my $opts = $plugin->check_config($id, $param, 1, 1);
+
+        # create /etc/pve/sdn directory
+        PVE::Cluster::check_cfs_quorum();
+        mkdir("/etc/pve/sdn");
+
+        PVE::Network::SDN::lock_sdn_config(
+	    sub {
+
+		my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+		my $controller_cfg = PVE::Network::SDN::Controllers::config();
+
+		my $scfg = undef;
+		if ($scfg = PVE::Network::SDN::Ipams::sdn_ipams_config($ipam_cfg, $id, 1)) {
+		    die "sdn ipam object ID '$id' already defined\n";
+		}
+
+		$ipam_cfg->{ids}->{$id} = $opts;
+
+		PVE::Network::SDN::Ipams::write_config($ipam_cfg);
+
+	    }, "create sdn ipam object failed");
+
+	return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    path => '{ipam}',
+    method => 'PUT',
+    description => "Update sdn ipam object configuration.",
+    permissions => {
+	check => ['perm', '/sdn/ipams', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Ipams::Plugin->updateSchema(),
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $id = extract_param($param, 'ipam');
+	my $digest = extract_param($param, 'digest');
+
+        PVE::Network::SDN::lock_sdn_config(
+	 sub {
+
+	    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+
+	    PVE::SectionConfig::assert_if_modified($ipam_cfg, $digest);
+
+	    my $scfg = PVE::Network::SDN::Ipams::sdn_ipams_config($ipam_cfg, $id);
+
+	    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($scfg->{type});
+	    my $opts = $plugin->check_config($id, $param, 0, 1);
+
+	    foreach my $k (%$opts) {
+		$scfg->{$k} = $opts->{$k};
+	    }
+
+	    PVE::Network::SDN::Ipams::write_config($ipam_cfg);
+
+	    }, "update sdn ipam object failed");
+
+	return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    path => '{ipam}',
+    method => 'DELETE',
+    description => "Delete sdn ipam object configuration.",
+    permissions => {
+	check => ['perm', '/sdn/ipams', ['SDN.Allocate']],
+    },
+    parameters => {
+    	additionalProperties => 0,
+	properties => {
+	    ipam => get_standard_option('pve-sdn-ipam-id', {
+                completion => \&PVE::Network::SDN::Ipams::complete_sdn_ipams,
+            }),
+	},
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $id = extract_param($param, 'ipam');
+
+        PVE::Network::SDN::lock_sdn_config(
+	    sub {
+
+		my $cfg = PVE::Network::SDN::Ipams::config();
+
+		my $scfg = PVE::Network::SDN::Ipams::sdn_ipams_config($cfg, $id);
+
+		my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($scfg->{type});
+
+		my $vnet_cfg = PVE::Network::SDN::Vnets::config();
+
+		delete $cfg->{ids}->{$id};
+		PVE::Network::SDN::Ipams::write_config($cfg);
+
+	    }, "delete sdn zone object failed");
+
+	return undef;
+    }});
+
+1;
diff --git a/PVE/API2/Network/SDN/Makefile b/PVE/API2/Network/SDN/Makefile
index 59626fa..1117dfa 100644
--- a/PVE/API2/Network/SDN/Makefile
+++ b/PVE/API2/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm
+SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index d9cb9e9..b60db3d 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -6,10 +6,13 @@ use warnings;
 use PVE::SafeSyslog;
 use PVE::Tools qw(extract_param);
 use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Exception qw(raise raise_param_exc);
 use PVE::Network::SDN;
 use PVE::Network::SDN::Subnets;
 use PVE::Network::SDN::SubnetPlugin;
 use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Ipams;
+use PVE::Network::SDN::Ipams::Plugin;
 
 use Storable qw(dclone);
 use PVE::JSONSchema qw(get_standard_option);
@@ -133,6 +136,17 @@ __PACKAGE__->register_method ({
 
 		$cfg->{ids}->{$id} = $opts;
 		PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
+
+		my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+		my $ipam = $cfg->{ids}->{$id}->{ipam};
+		if ($ipam) {
+		    raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
+		    my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+		    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+		    $plugin->add_subnet($plugin_config, $id, $cfg->{ids}->{$id});
+		    $plugin->add_ip($plugin_config, $id, $opts->{gateway}, 1) if $opts->{gateway};
+		}
+
 		PVE::Network::SDN::Subnets::write_config($cfg);
 
 	    }, "create sdn subnet object failed");
@@ -161,6 +175,7 @@ __PACKAGE__->register_method ({
 	 sub {
 
 	    my $cfg = PVE::Network::SDN::Subnets::config();
+	    my $scfg = &$api_sdn_subnets_config($cfg, $id);
 
 	    PVE::SectionConfig::assert_if_modified($cfg, $digest);
 
@@ -168,6 +183,24 @@ __PACKAGE__->register_method ({
 	    $cfg->{ids}->{$id} = $opts;
 
 	    PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
+
+            my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+            my $ipam = $cfg->{ids}->{$id}->{ipam};
+	    if ($ipam) {
+		raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
+		my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+		my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+		$plugin->add_subnet($plugin_config, $id, $cfg->{ids}->{$id});
+
+		if($opts->{gateway} && $scfg->{gateway} && $opts->{gateway} ne $scfg->{gateway}) {
+		    $plugin->del_ip($plugin_config, $scfg->{gateway});
+		}
+		if (!defined($opts->{gateway}) && $scfg->{gateway}) {
+		    $plugin->del_ip($plugin_config, $scfg->{gateway});
+		} 
+		$plugin->add_ip($plugin_config, $id, $opts->{gateway}, 1) if $opts->{gateway};
+	    }
+
 	    PVE::Network::SDN::Subnets::write_config($cfg);
 
 	    }, "update sdn subnet object failed");
@@ -200,7 +233,6 @@ __PACKAGE__->register_method ({
 
         PVE::Network::SDN::lock_sdn_config(
 	    sub {
-
 		my $cfg = PVE::Network::SDN::Subnets::config();
 
 		my $scfg = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id);
@@ -208,8 +240,19 @@ __PACKAGE__->register_method ({
 		my $subnets_cfg = PVE::Network::SDN::Subnets::config();
 		my $vnets_cfg = PVE::Network::SDN::Vnets::config();
 
-		delete $cfg->{ids}->{$id};
 		PVE::Network::SDN::SubnetPlugin->on_delete_hook($id, $subnets_cfg, $vnets_cfg);
+
+		my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+		my $ipam = $cfg->{ids}->{$id}->{ipam};
+		if ($ipam) {
+		    raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
+		    my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+		    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+		    $plugin->del_subnet($plugin_config, $id, $scfg);
+		}
+
+		delete $cfg->{ids}->{$id};
+
 		PVE::Network::SDN::Subnets::write_config($cfg);
 
 	    }, "delete sdn subnet object failed");
diff --git a/PVE/Network/SDN/Ipams.pm b/PVE/Network/SDN/Ipams.pm
new file mode 100644
index 0000000..3d33632
--- /dev/null
+++ b/PVE/Network/SDN/Ipams.pm
@@ -0,0 +1,78 @@
+package PVE::Network::SDN::Ipams;
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network;
+
+use PVE::Network::SDN::Ipams::NetboxPlugin;
+use PVE::Network::SDN::Ipams::PhpIpamPlugin;
+use PVE::Network::SDN::Ipams::Plugin;
+
+PVE::Network::SDN::Ipams::NetboxPlugin->register();
+PVE::Network::SDN::Ipams::PhpIpamPlugin->register();
+PVE::Network::SDN::Ipams::Plugin->init();
+
+
+sub sdn_ipams_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn ipam ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/ipams.cfg");
+    return $config;
+}
+
+sub get_plugin_config {
+    my ($vnet) = @_;
+    my $ipamid = $vnet->{ipam};
+    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+    return $ipam_cfg->{ids}->{$ipamid};
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/ipams.cfg", $cfg);
+}
+
+sub sdn_ipams_ids {
+    my ($cfg) = @_;
+
+    return keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_vnet {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::Ipams::config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Vnets::sdn_ipams_ids($cfg) ];
+}
+
+sub next_free_ip {
+    my ($subnetid, $subnet) = @_;
+
+    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+    my $ipamid = $subnet->{ipam};
+    return if !$ipamid;
+
+    my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
+    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+    my $ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
+    return $ip;
+}
+1;
+
diff --git a/PVE/Network/SDN/Ipams/Makefile b/PVE/Network/SDN/Ipams/Makefile
new file mode 100644
index 0000000..884c47a
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/Makefile
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm PhpIpamPlugin.pm NetboxPlugin.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+	for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Ipams/$$i; done
diff --git a/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
new file mode 100644
index 0000000..ccc1184
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -0,0 +1,169 @@
+package PVE::Network::SDN::Ipams::NetboxPlugin;
+
+use strict;
+use warnings;
+use PVE::INotify;
+use PVE::Cluster;
+use PVE::Tools;
+
+use base('PVE::Network::SDN::Ipams::Plugin');
+
+sub type {
+    return 'netbox';
+}
+
+sub properties {
+    return {
+    };
+}
+
+sub options {
+
+    return {
+        url => { optional => 0},
+        token => { optional => 0 },
+    };
+}
+
+# Plugin implementation
+
+sub add_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $gateway = $subnet->{gateway};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+
+    #create subnet
+    if (!$internalid) {
+	my ($network, $mask) = split(/-/, $subnetid);
+
+	my $params = { prefix => $cidr };
+
+	eval {
+		my $result = PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/prefixes/", $headers, $params);
+		$subnet->{ipamid} = $result->{id} if defined($result->{id});
+	};
+	if ($@) {
+	    die "error add subnet to ipam: $@";
+	}
+    }
+   
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $gateway = $subnet->{gateway};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+    return if !$internalid;
+    #fixme: check that prefix is empty exluding gateway, before delete
+
+    PVE::Network::SDN::Ipams::NetboxPlugin::del_ip($class, $plugin_config, $gateway) if $gateway;
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/ipam/prefixes/$internalid/", $headers);
+    };
+    if ($@) {
+	die "error deleting subnet from ipam: $@";
+    }
+
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+
+    my ($network, $mask) = split(/-/, $subnetid);
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $params = { address => "$ip/$mask" };
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/ip-addresses/", $headers, $params);
+    };
+
+    if ($@) {
+	die "error add subnet ip to ipam: ip already exist: $@";
+    }
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $internalid = get_prefix_id($url, $cidr, $headers);
+
+    my $params = {};
+
+    my $ip = undef;
+    eval {
+	my $result = PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/ipam/prefixes/$internalid/available-ips/", $headers, $params);
+	$ip = $result->{address};
+    };
+
+    if ($@) {
+	die "can't find free ip in subnet $cidr: $@";
+    }
+
+    return $ip;
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $ip) = @_;
+
+    return if !$ip;
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+    my $ip_id = get_ip_id($url, $ip, $headers);
+    die "can't find ip $ip in ipam" if !$ip_id;
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/ipam/ip-addresses/$ip_id/", $headers);
+    };
+    if ($@) {
+	die "error delete ip $ip";
+    }
+}
+
+#helpers
+
+sub get_prefix_id {
+    my ($url, $cidr, $headers) = @_;
+
+    my $result = PVE::Network::SDN::Ipams::Plugin::api_request("GET", "$url/ipam/prefixes/?q=$cidr", $headers);
+    my $data = @{$result->{results}}[0];
+    my $internalid = $data->{id};
+    return $internalid;
+}
+
+sub get_ip_id {
+    my ($url, $ip, $headers) = @_;
+    my $result = PVE::Network::SDN::Ipams::Plugin::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers);
+    my $data = @{$result->{results}}[0];
+    my $ip_id = $data->{id};
+    return $ip_id;
+}
+
+
+1;
+
+
diff --git a/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
new file mode 100644
index 0000000..7380bf3
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
@@ -0,0 +1,189 @@
+package PVE::Network::SDN::Ipams::PhpIpamPlugin;
+
+use strict;
+use warnings;
+use PVE::INotify;
+use PVE::Cluster;
+use PVE::Tools;
+
+use base('PVE::Network::SDN::Ipams::Plugin');
+
+sub type {
+    return 'phpipam';
+}
+
+sub properties {
+    return {
+	url => {
+	    type => 'string',
+	},
+	token => {
+	    type => 'string',
+	},
+	section => {
+	    type => 'integer',
+	},
+    };
+}
+
+sub options {
+
+    return {
+        url => { optional => 0},
+        token => { optional => 0 },
+        section => { optional => 0 },
+    };
+}
+
+# Plugin implementation
+
+sub add_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $gateway = $subnet->{gateway};
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    #search subnet
+    my $internalid = get_internalid($url, $cidr, $headers);
+
+    #create subnet
+    if (!$internalid) {
+	my ($network, $mask) = split(/-/, $subnetid);
+
+	my $params = { subnet => $network,
+		   mask => $mask,
+		   sectionId => $section,
+		  };
+
+	eval {
+		PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/subnets/", $headers, $params);
+	};
+	if ($@) {
+	    die "error add subnet to ipam: $@";
+	}
+    }
+
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    my $internalid = get_internalid($url, $cidr, $headers);
+    return if !$internalid;
+
+    #fixme: check that prefix is empty exluding gateway, before delete
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/subnets/$internalid", $headers);
+    };
+    if ($@) {
+	die "error deleting subnet from ipam: $@";
+    }
+
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $ip, $is_gateway) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    my $internalid = get_internalid($url, $cidr, $headers);
+
+    my $params = { ip => $ip,
+		   subnetId => $internalid,
+		   is_gateway => $is_gateway,
+		  };
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/addresses/", $headers, $params);
+    };
+
+    if ($@) {
+	die "error add subnet ip to ipam: ip $ip already exist: $@";
+    }
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config, $subnetid, $subnet, $internalid, $hostname) = @_;
+
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $section = $plugin_config->{section};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    $internalid = get_internalid($url, $cidr, $headers) if !$internalid;
+
+    my $params = {};
+
+    my $ip = undef;
+    eval {
+	my $result = PVE::Network::SDN::Ipams::Plugin::api_request("POST", "$url/addresses/first_free/$internalid/", $headers, $params);
+	$ip = $result->{data};
+    };
+
+    if ($@) {
+        die "can't find free ip in subnet $cidr: $@";
+    }
+
+    my ($network, $mask) = split(/-/, $subnetid);
+    return "$ip/$mask";
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $ip) = @_;
+
+    return if !$ip;
+
+    my $url = $plugin_config->{url};
+    my $token = $plugin_config->{token};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+    my $ip_id = get_ip_id($url, $ip, $headers);
+    return if !$ip_id;
+
+    eval {
+	PVE::Network::SDN::Ipams::Plugin::api_request("DELETE", "$url/addresses/$ip_id", $headers);
+    };
+    if ($@) {
+	die "error delete ip $ip";
+    }
+}
+
+
+#helpers
+
+sub get_internalid {
+    my ($url, $cidr, $headers) = @_;
+
+    my $result = PVE::Network::SDN::Ipams::Plugin::api_request("GET", "$url/subnets/cidr/$cidr", $headers);
+    my $data = @{$result->{data}}[0];
+    my $internalid = $data->{id};
+    return $internalid;
+}
+
+sub get_ip_id {
+    my ($url, $ip, $headers) = @_;
+    my $result = PVE::Network::SDN::Ipams::Plugin::api_request("GET", "$url/addresses/search/$ip", $headers);
+    my $data = @{$result->{data}}[0];
+    my $ip_id = $data->{id};
+    return $ip_id;
+}
+
+1;
+
+
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
new file mode 100644
index 0000000..8a44090
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -0,0 +1,127 @@
+package PVE::Network::SDN::Ipams::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(run_command);
+use PVE::JSONSchema;
+use PVE::Cluster;
+use HTTP::Request;
+use LWP::UserAgent;
+use JSON;
+
+use Data::Dumper;
+use PVE::JSONSchema qw(get_standard_option);
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file('sdn/ipams.cfg',
+				 sub { __PACKAGE__->parse_config(@_); },
+				 sub { __PACKAGE__->write_config(@_); });
+
+PVE::JSONSchema::register_standard_option('pve-sdn-ipam-id', {
+    description => "The SDN ipam object identifier.",
+    type => 'string', format => 'pve-sdn-ipam-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-ipam-id', \&parse_sdn_ipam_id);
+sub parse_sdn_ipam_id {
+    my ($id, $noerr) = @_;
+
+    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
+	return undef if $noerr;
+	die "ipam ID '$id' contains illegal characters\n";
+    }
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+	type => {
+	    description => "Plugin type.",
+	    type => 'string', format => 'pve-configid',
+	    type => 'string',
+	},
+        ipam => get_standard_option('pve-sdn-ipam-id',
+            { completion => \&PVE::Network::SDN::Ipams::complete_sdn_ipam }),
+    },
+};
+
+sub private {
+    return $defaultData;
+}
+
+sub parse_section_header {
+    my ($class, $line) = @_;
+
+    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
+        my ($type, $id) = (lc($1), $2);
+	my $errmsg = undef; # set if you want to skip whole section
+	eval { PVE::JSONSchema::pve_verify_configid($type); };
+	$errmsg = $@ if $@;
+	my $config = {}; # to return additional attributes
+	return ($type, $id, $errmsg, $config);
+    }
+    return undef;
+}
+
+
+sub add_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+}
+
+sub del_subnet {
+    my ($class, $plugin_config, $subnetid, $subnet) = @_;
+}
+
+sub add_ip {
+    my ($class, $plugin_config, $subnetid, $subnet, $internalid, $ip, $hostname, $is_gateway) = @_;
+
+}
+
+sub add_next_freeip {
+    my ($class, $plugin_config) = @_;
+}
+
+sub del_ip {
+    my ($class, $plugin_config, $ip) = @_;
+}
+
+
+#helpers
+sub api_request {
+    my ($method, $url, $headers, $data) = @_;
+
+    my $encoded_data = to_json($data) if $data;
+
+    my $req = HTTP::Request->new($method,$url, $headers, $encoded_data);
+
+    my $ua = LWP::UserAgent->new(protocols_allowed => ['http', 'https'], timeout => 30);
+    my $proxy = undef;
+
+    if ($proxy) {
+        $ua->proxy(['http', 'https'], $proxy);
+    } else {
+        $ua->env_proxy;
+    }
+
+    $ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00);
+
+    my $response = $ua->request($req);
+    my $code = $response->code;
+
+    if ($code !~ /2(\d+)$/) {
+        my $msg = $response->message || 'unknown';
+        die "Invalid response from server: $code $msg\n";
+    }
+
+    my $raw = '';
+    if (defined($response->decoded_content)) {
+	$raw = $response->decoded_content;
+    } else {
+	$raw = $response->content;
+    }
+    return from_json($raw) if $raw ne '';
+}
+
+1;
diff --git a/PVE/Network/SDN/Makefile b/PVE/Network/SDN/Makefile
index 59f8c34..fb68856 100644
--- a/PVE/Network/SDN/Makefile
+++ b/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
@@ -8,4 +8,5 @@ install:
 	for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/$$i; done
 	make -C Controllers install
 	make -C Zones install
+	make -C Ipams install
 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index ea47684..6224065 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -82,7 +82,7 @@ sub properties {
             type => 'string',
             description => "Develop some dns registrations plugins (powerdns,...)",
         },
-        ipam_driver => {
+        ipam => {
             type => 'string',
             description => "use a specific ipam",
         },
@@ -98,7 +98,7 @@ sub options {
 	snat => { optional => 1 },
 	dhcp => { optional => 1 },
 	dns_driver => { optional => 1 },
-	ipam_driver => { optional => 1 },
+	ipam => { optional => 1 },
     };
 }
 
@@ -110,6 +110,7 @@ sub on_update_hook {
 
     my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
     raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway);
+
 }
 
 sub on_delete_hook {
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 073ab80..d474037 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -4,6 +4,8 @@ use strict;
 use warnings;
 
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::Ipams;
 
 use PVE::Network::SDN::VnetPlugin;
 PVE::Network::SDN::VnetPlugin->register();
@@ -52,4 +54,27 @@ sub get_vnet {
     return $vnet;
 }
 
+sub get_next_free_ip {
+    my ($vnetid) = @_;
+
+    my $vnets_cfg = PVE::Network::SDN::Vnets::config();
+    my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+    my $vnet = $vnets_cfg->{ids}->{$vnetid};
+    my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+    my $ip = undef;
+    foreach my $s (@subnets) {
+        my $subnetid = $s =~ s/\//-/r;
+        my $subnet = $subnets_cfg->{ids}->{$subnetid};
+        if ($subnet && $subnet->{ipam}) {
+            eval {
+                $ip = PVE::Network::SDN::Ipams::next_free_ip($subnetid, $subnet);
+            };
+            warn $@ if $@;
+        }
+        last if $ip;
+    }
+    die "can't find any ip" if !$ip;
+    return $ip;
+}
+
 1;
-- 
2.20.1




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

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

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

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




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

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

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

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




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

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

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

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




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

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

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

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




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

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

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

diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
index 6055fe5..0a5fa33 100644
--- a/PVE/API2/Network/SDN.pm
+++ b/PVE/API2/Network/SDN.pm
@@ -17,6 +17,7 @@ use PVE::API2::Network::SDN::Vnets;
 use PVE::API2::Network::SDN::Zones;
 use PVE::API2::Network::SDN::Subnets;
 use PVE::API2::Network::SDN::Ipams;
+use PVE::API2::Network::SDN::Dns;
 
 use base qw(PVE::RESTHandler);
 
@@ -45,6 +46,11 @@ __PACKAGE__->register_method ({
     path => 'ipams',
 });
 
+__PACKAGE__->register_method ({
+    subclass => "PVE::API2::Network::SDN::Dns",
+    path => 'dns',
+});
+
 __PACKAGE__->register_method({
     name => 'index',
     path => '',
@@ -76,6 +82,7 @@ __PACKAGE__->register_method({
 	    { id => 'controllers' },
 	    { id => 'subnets' },
 	    { id => 'ipams' },
+	    { id => 'dns' },
 	];
 
 	return $res;
diff --git a/PVE/API2/Network/SDN/Dns.pm b/PVE/API2/Network/SDN/Dns.pm
new file mode 100644
index 0000000..ea26af3
--- /dev/null
+++ b/PVE/API2/Network/SDN/Dns.pm
@@ -0,0 +1,242 @@
+package PVE::API2::Network::SDN::Dns;
+
+use strict;
+use warnings;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Network::SDN;
+use PVE::Network::SDN::Dns;
+use PVE::Network::SDN::Dns::Plugin;
+use PVE::Network::SDN::Dns::PowerdnsPlugin;
+
+use Storable qw(dclone);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $sdn_dns_type_enum = PVE::Network::SDN::Dns::Plugin->lookup_types();
+
+my $api_sdn_dns_config = sub {
+    my ($cfg, $id) = @_;
+
+    my $scfg = dclone(PVE::Network::SDN::Dns::sdn_dns_config($cfg, $id));
+    $scfg->{dns} = $id;
+    $scfg->{digest} = $cfg->{digest};
+
+    return $scfg;
+};
+
+__PACKAGE__->register_method ({
+    name => 'index',
+    path => '',
+    method => 'GET',
+    description => "SDN dns index.",
+    permissions => {
+	description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/dns/<dns>'",
+	user => 'all',
+    },
+    parameters => {
+    	additionalProperties => 0,
+	properties => {
+	    type => {
+		description => "Only list sdn dns of specific type",
+		type => 'string',
+		enum => $sdn_dns_type_enum,
+		optional => 1,
+	    },
+	},
+    },
+    returns => {
+	type => 'array',
+	items => {
+	    type => "object",
+	    properties => { dns => { type => 'string'},
+			    type => { type => 'string'},
+			  },
+	},
+	links => [ { rel => 'child', href => "{dns}" } ],
+    },
+    code => sub {
+	my ($param) = @_;
+
+	my $rpcenv = PVE::RPCEnvironment::get();
+	my $authuser = $rpcenv->get_user();
+
+
+	my $cfg = PVE::Network::SDN::Dns::config();
+
+	my @sids = PVE::Network::SDN::Dns::sdn_dns_ids($cfg);
+	my $res = [];
+	foreach my $id (@sids) {
+	    my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+	    next if !$rpcenv->check_any($authuser, "/sdn/dns/$id", $privs, 1);
+
+	    my $scfg = &$api_sdn_dns_config($cfg, $id);
+	    next if $param->{type} && $param->{type} ne $scfg->{type};
+
+	    my $plugin_config = $cfg->{ids}->{$id};
+	    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+	    push @$res, $scfg;
+	}
+
+	return $res;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'read',
+    path => '{dns}',
+    method => 'GET',
+    description => "Read sdn dns configuration.",
+    permissions => {
+	check => ['perm', '/sdn/dns/{dns}', ['SDN.Allocate']],
+   },
+
+    parameters => {
+    	additionalProperties => 0,
+	properties => {
+	    dns => get_standard_option('pve-sdn-dns-id'),
+	},
+    },
+    returns => { type => 'object' },
+    code => sub {
+	my ($param) = @_;
+
+	my $cfg = PVE::Network::SDN::Dns::config();
+
+	return &$api_sdn_dns_config($cfg, $param->{dns});
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'create',
+    protected => 1,
+    path => '',
+    method => 'POST',
+    description => "Create a new sdn dns object.",
+    permissions => {
+	check => ['perm', '/sdn/dns', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Dns::Plugin->createSchema(),
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $type = extract_param($param, 'type');
+	my $id = extract_param($param, 'dns');
+
+	my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($type);
+	my $opts = $plugin->check_config($id, $param, 1, 1);
+
+        # create /etc/pve/sdn directory
+        PVE::Cluster::check_cfs_quorum();
+        mkdir("/etc/pve/sdn");
+
+        PVE::Network::SDN::lock_sdn_config(
+	    sub {
+
+		my $dns_cfg = PVE::Network::SDN::Dns::config();
+
+		my $scfg = undef;
+		if ($scfg = PVE::Network::SDN::Dns::sdn_dns_config($dns_cfg, $id, 1)) {
+		    die "sdn dns object ID '$id' already defined\n";
+		}
+
+		$dns_cfg->{ids}->{$id} = $opts;
+
+		my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
+		$plugin->on_update_hook($opts);
+
+		PVE::Network::SDN::Dns::write_config($dns_cfg);
+
+	    }, "create sdn dns object failed");
+
+	return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'update',
+    protected => 1,
+    path => '{dns}',
+    method => 'PUT',
+    description => "Update sdn dns object configuration.",
+    permissions => {
+	check => ['perm', '/sdn/dns', ['SDN.Allocate']],
+    },
+    parameters => PVE::Network::SDN::Dns::Plugin->updateSchema(),
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $id = extract_param($param, 'dns');
+	my $digest = extract_param($param, 'digest');
+
+        PVE::Network::SDN::lock_sdn_config(
+	 sub {
+
+	    my $dns_cfg = PVE::Network::SDN::Dns::config();
+
+	    PVE::SectionConfig::assert_if_modified($dns_cfg, $digest);
+
+	    my $scfg = PVE::Network::SDN::Dns::sdn_dns_config($dns_cfg, $id);
+
+	    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
+	    my $opts = $plugin->check_config($id, $param, 0, 1);
+
+	    foreach my $k (%$opts) {
+		$scfg->{$k} = $opts->{$k};
+	    }
+
+	    $plugin->on_update_hook($scfg);
+
+	    PVE::Network::SDN::Dns::write_config($dns_cfg);
+
+	    }, "update sdn dns object failed");
+
+	return undef;
+    }});
+
+__PACKAGE__->register_method ({
+    name => 'delete',
+    protected => 1,
+    path => '{dns}',
+    method => 'DELETE',
+    description => "Delete sdn dns object configuration.",
+    permissions => {
+	check => ['perm', '/sdn/dns', ['SDN.Allocate']],
+    },
+    parameters => {
+    	additionalProperties => 0,
+	properties => {
+	    dns => get_standard_option('pve-sdn-dns-id', {
+                completion => \&PVE::Network::SDN::Dns::complete_sdn_dns,
+            }),
+	},
+    },
+    returns => { type => 'null' },
+    code => sub {
+	my ($param) = @_;
+
+	my $id = extract_param($param, 'dns');
+
+        PVE::Network::SDN::lock_sdn_config(
+	    sub {
+
+		my $cfg = PVE::Network::SDN::Dns::config();
+
+		my $scfg = PVE::Network::SDN::Dns::sdn_dns_config($cfg, $id);
+
+		my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
+
+		delete $cfg->{ids}->{$id};
+		PVE::Network::SDN::Dns::write_config($cfg);
+
+	    }, "delete sdn dns object failed");
+
+	return undef;
+    }});
+
+1;
diff --git a/PVE/API2/Network/SDN/Makefile b/PVE/API2/Network/SDN/Makefile
index 1117dfa..3683fa4 100644
--- a/PVE/API2/Network/SDN/Makefile
+++ b/PVE/API2/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm
+SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/PVE/Network/SDN/Dns.pm b/PVE/Network/SDN/Dns.pm
new file mode 100644
index 0000000..c2e153a
--- /dev/null
+++ b/PVE/Network/SDN/Dns.pm
@@ -0,0 +1,57 @@
+package PVE::Network::SDN::Dns;
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network;
+
+use PVE::Network::SDN::Dns::PowerdnsPlugin;
+use PVE::Network::SDN::Dns::Plugin;
+
+PVE::Network::SDN::Dns::PowerdnsPlugin->register();
+PVE::Network::SDN::Dns::Plugin->init();
+
+
+sub sdn_dns_config {
+    my ($cfg, $id, $noerr) = @_;
+
+    die "no sdn dns ID specified\n" if !$id;
+
+    my $scfg = $cfg->{ids}->{$id};
+    die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
+
+    return $scfg;
+}
+
+sub config {
+    my $config = cfs_read_file("sdn/dns.cfg");
+    return $config;
+}
+
+sub write_config {
+    my ($cfg) = @_;
+
+    cfs_write_file("sdn/dns.cfg", $cfg);
+}
+
+sub sdn_dns_ids {
+    my ($cfg) = @_;
+
+    return keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_dns {
+    my ($cmdname, $pname, $cvalue) = @_;
+
+    my $cfg = PVE::Network::SDN::Dns::config();
+
+    return  $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Dns::sdn_dns_ids($cfg) ];
+}
+
+1;
+
diff --git a/PVE/Network/SDN/Dns/Makefile b/PVE/Network/SDN/Dns/Makefile
new file mode 100644
index 0000000..81cd2a1
--- /dev/null
+++ b/PVE/Network/SDN/Dns/Makefile
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm PowerdnsPlugin.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+	for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Dns/$$i; done
diff --git a/PVE/Network/SDN/Dns/Plugin.pm b/PVE/Network/SDN/Dns/Plugin.pm
new file mode 100644
index 0000000..baa9316
--- /dev/null
+++ b/PVE/Network/SDN/Dns/Plugin.pm
@@ -0,0 +1,117 @@
+package PVE::Network::SDN::Dns::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(run_command);
+use PVE::JSONSchema;
+use PVE::Cluster;
+use HTTP::Request;
+use LWP::UserAgent;
+use JSON;
+
+use Data::Dumper;
+use PVE::JSONSchema qw(get_standard_option);
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file('sdn/dns.cfg',
+				 sub { __PACKAGE__->parse_config(@_); },
+				 sub { __PACKAGE__->write_config(@_); });
+
+PVE::JSONSchema::register_standard_option('pve-sdn-dns-id', {
+    description => "The SDN dns object identifier.",
+    type => 'string', format => 'pve-sdn-dns-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-dns-id', \&parse_sdn_dns_id);
+sub parse_sdn_dns_id {
+    my ($id, $noerr) = @_;
+
+    if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
+	return undef if $noerr;
+	die "dns ID '$id' contains illegal characters\n";
+    }
+    return $id;
+}
+
+my $defaultData = {
+
+    propertyList => {
+	type => {
+	    description => "Plugin type.",
+	    type => 'string', format => 'pve-configid',
+	},
+        ttl => { type => 'integer', optional => 1 },
+        dns => get_standard_option('pve-sdn-dns-id',
+            { completion => \&PVE::Network::SDN::Dns::complete_sdn_dns }),
+    },
+};
+
+sub private {
+    return $defaultData;
+}
+
+sub parse_section_header {
+    my ($class, $line) = @_;
+
+    if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
+        my ($type, $id) = (lc($1), $2);
+	my $errmsg = undef; # set if you want to skip whole section
+	eval { PVE::JSONSchema::pve_verify_configid($type); };
+	$errmsg = $@ if $@;
+	my $config = {}; # to return additional attributes
+	return ($type, $id, $errmsg, $config);
+    }
+    return undef;
+}
+
+
+sub add_a_record {
+    my ($class, $plugin_config, $type, $zone, $reversezone, $hostname, $ip) = @_;
+}
+
+sub del_a_record {
+    my ($class, $plugin_config, $hostname, $ip) = @_;
+}
+
+sub on_update_hook {
+    my ($class, $plugin_config) = @_;
+}
+
+#helpers
+sub api_request {
+    my ($method, $url, $headers, $data) = @_;
+
+    my $encoded_data = to_json($data) if $data;
+
+    my $req = HTTP::Request->new($method,$url, $headers, $encoded_data);
+
+    my $ua = LWP::UserAgent->new(protocols_allowed => ['http', 'https'], timeout => 30);
+    my $proxy = undef;
+
+    if ($proxy) {
+        $ua->proxy(['http', 'https'], $proxy);
+    } else {
+        $ua->env_proxy;
+    }
+
+    $ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00);
+
+    my $response = $ua->request($req);
+    my $code = $response->code;
+
+    if ($code !~ /^2(\d+)$/) {
+        my $msg = $response->message || 'unknown';
+        die "Invalid response from server: $code $msg\n";
+    }
+
+    my $raw = '';
+    if (defined($response->decoded_content)) {
+	$raw = $response->decoded_content;
+    } else {
+	$raw = $response->content;
+    }
+    return from_json($raw) if $raw ne '';
+}
+
+1;
diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
new file mode 100644
index 0000000..8c5dd90
--- /dev/null
+++ b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
@@ -0,0 +1,201 @@
+package PVE::Network::SDN::Dns::PowerdnsPlugin;
+
+use strict;
+use warnings;
+use PVE::INotify;
+use PVE::Cluster;
+use PVE::Tools;
+use JSON;
+use Net::IP;
+
+use base('PVE::Network::SDN::Dns::Plugin');
+
+sub type {
+    return 'powerdns';
+}
+
+sub properties {
+    return {
+	url => {
+	    type => 'string',
+	},
+	key => {
+	    type => 'string',
+	},
+    };
+}
+
+sub options {
+
+    return {
+        url => { optional => 0},
+        key => { optional => 0 },
+        ttl => { optional => 1 },
+    };
+}
+
+# Plugin implementation
+
+sub add_a_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip) = @_;
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $ttl = $plugin_config->{ttl} ? $plugin_config->{ttl} : 14400;
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
+    my $fqdn = $hostname.".".$zone.".";
+
+
+    my $record = { content => $ip, 
+                   disabled => JSON::false, 
+		   name => $fqdn, 
+                   type => $type, 
+                   priority => 0 };
+
+    my $rrset = { name => $fqdn, 
+		  type => $type, 
+                   ttl =>  $ttl, 
+		  changetype => "REPLACE",
+		  records => [ $record ] };
+
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+	PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+	die "error add $fqdn to zone $zone: $@";
+    }
+}
+
+sub add_ptr_record {
+    my ($class, $plugin_config, $zone, $hostname, $ip) = @_;
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $ttl = $plugin_config->{ttl} ? $plugin_config->{ttl} : 14400;
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    my $reverseip = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
+    my $fqdn = $hostname.".".$zone.".";
+    my $type = "PTR";
+
+    my $record = { content => $fqdn, 
+                   disabled => JSON::false, 
+		   name => $reverseip, 
+                   type => $type, 
+                   priority => 0 };
+
+    my $rrset = { name => $reverseip, 
+		  type => $type, 
+                   ttl =>  $ttl, 
+		  changetype => "REPLACE",
+		  records => [ $record ] };
+
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+	PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+	die "error add $reverseip to zone $zone: $@";
+    }
+}
+
+sub del_a_record {
+    my ($class, $plugin_config, $zone, $hostname) = @_;
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+    my $fqdn = $hostname.".".$zone.".";
+    my $type = "PTR";
+
+    my $rrset = { name => $fqdn, 
+		  type => $type, 
+		  changetype => "DELETE",
+		  records => [] };
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+	PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+	die "error delete $fqdn from zone $zone: $@";
+    }
+}
+
+sub del_ptr_record {
+    my ($class, $plugin_config, $zone, $ip) = @_;
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    my $reverseip = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
+    my $type = "PTR";
+
+    my $rrset = { name => $reverseip, 
+		  type => $type, 
+		  changetype => "DELETE",
+		  records => [] };
+
+    my $params = { rrsets => [ $rrset ] };
+
+    eval {
+	PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+    };
+
+    if ($@) {
+	die "error delete $reverseip from zone $zone: $@";
+    }
+}
+
+sub verify_zone {
+    my ($class, $plugin_config, $zone) = @_;
+
+    #verify that api is working              
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    eval {
+        PVE::Network::SDN::Dns::Plugin::api_request("GET", "$url/zones/$zone", $headers);
+    };
+
+    if ($@) {
+        die "can't read zone $zone: $@";
+    }
+}
+
+
+sub on_update_hook {
+    my ($class, $plugin_config) = @_;
+
+    #verify that api is working
+
+    my $url = $plugin_config->{url};
+    my $key = $plugin_config->{key};
+    my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+    eval {
+	PVE::Network::SDN::Dns::Plugin::api_request("GET", "$url", $headers);
+    };
+
+    if ($@) {
+	die "dns api error: $@";
+    }
+}
+
+1;
+
+
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 0dfc8a4..99af0ed 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -99,7 +99,6 @@ sub add_next_freeip {
 	while(1) {
 	    my $ip = $iplist->ip();
 	    ++$iplist;
-	    print "nextip: $ip\n";
 	    next if defined($s->{ips}->{$ip});
 	    $freeip = $ip;
 	    last;
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
index fc736b8..683346c 100644
--- a/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -110,7 +110,7 @@ sub api_request {
     my $response = $ua->request($req);
     my $code = $response->code;
 
-    if ($code !~ /2(\d+)$/) {
+    if ($code !~ /^2(\d+)$/) {
         my $msg = $response->message || 'unknown';
         die "Invalid response from server: $code $msg\n";
     }
diff --git a/PVE/Network/SDN/Makefile b/PVE/Network/SDN/Makefile
index fb68856..92cfcd0 100644
--- a/PVE/Network/SDN/Makefile
+++ b/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm Dns.pm
 
 
 PERL5DIR=${DESTDIR}/usr/share/perl5
@@ -9,4 +9,5 @@ install:
 	make -C Controllers install
 	make -C Zones install
 	make -C Ipams install
+	make -C Dns install
 
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 6224065..3769e04 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -65,22 +65,25 @@ sub properties {
             type => 'string',
             description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
         },
-	#cloudinit, dhcp options
-        nameservers => {
-            type => 'string', format => 'address-list',
-            description => " dns nameserver",
+        dns => {
+            type => 'string',
+            description => "dns api server",
         },
-	#cloudinit, dhcp options
-        searchdomain => {
+        reversedns => {
             type => 'string',
+            description => "reverse dns api server",
         },
-        dhcp => {
-            type => 'boolean',
-            description => "enable dhcp for this subnet",
+        dnszone => {
+            type => 'string',
+            description => "dns domain zone  ex: mydomain.com",
         },
-        dns_driver => {
+        reversednszone => {
             type => 'string',
-            description => "Develop some dns registrations plugins (powerdns,...)",
+            description => "reverse dns zone ex: 0.168.192.in-addr.arpa",
+        },
+        dnszoneprefix => {
+            type => 'string',
+            description => "dns domain zone prefix  ex: 'adm' -> <hostname>.adm.mydomain.com",
         },
         ipam => {
             type => 'string',
@@ -93,11 +96,12 @@ sub options {
     return {
 	gateway => { optional => 1 },
 	routes => { optional => 1 },
-	nameservers => { optional => 1 },
-	searchdomain => { optional => 1 },
 	snat => { optional => 1 },
-	dhcp => { optional => 1 },
-	dns_driver => { optional => 1 },
+	dns => { optional => 1 },
+	reversedns => { optional => 1 },
+	dnszone => { optional => 1 },
+	reversednszone => { optional => 1 },
+	dnszoneprefix => { optional => 1 },
 	ipam => { optional => 1 },
     };
 }
@@ -105,12 +109,25 @@ sub options {
 sub on_update_hook {
     my ($class, $subnetid, $subnet_cfg) = @_;
 
-    my $subnet = $subnetid =~ s/-/\//r;
-    my $subnet_matcher = subnet_matcher($subnet);
+    my $cidr = $subnetid =~ s/-/\//r;
+    my $subnet_matcher = subnet_matcher($cidr);
+
+    my $subnet = $subnet_cfg->{ids}->{$subnetid};
 
-    my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+    my $gateway = $subnet->{gateway};
+    my $dns = $subnet->{dns};
+    my $dnszone = $subnet->{dnszone};
+    my $reversedns = $subnet->{reversedns};
+    my $reversednszone = $subnet->{reversednszone};
+
+    #to: for /32 pointotoping, allow gateway outside the subnet
     raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway);
 
+    raise_param_exc({ dns => "missing dns provider"}) if $dnszone && !$dns;
+    raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
+    raise_param_exc({ reversedns => "missing dns provider"}) if $reversednszone && !$reversedns;
+    raise_param_exc({ reversednszone => "missing dns zone"}) if $reversedns && !$reversednszone;
+
 }
 
 sub on_delete_hook {
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 3ce2d44..4e8353e 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -5,8 +5,10 @@ use warnings;
 
 use Net::Subnet qw(subnet_matcher);
 use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use Net::IP;
 
 use PVE::Network::SDN::Ipams;
+use PVE::Network::SDN::Dns;
 use PVE::Network::SDN::SubnetPlugin;
 PVE::Network::SDN::SubnetPlugin->register();
 PVE::Network::SDN::SubnetPlugin->init();
@@ -75,41 +77,157 @@ sub find_ip_subnet {
     return ($subnetid, $subnet);
 }
 
+my $verify_dns_zone = sub {
+    my ($zone, $dns) = @_;
+
+    return if !$zone || !$dns;
+
+    my $dns_cfg = PVE::Network::SDN::Dns::config();
+    my $plugin_config = $dns_cfg->{ids}->{$dns};
+    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+    $plugin->verify_zone($plugin_config, $zone);
+};
+
+my $add_dns_record = sub {
+    my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
+
+   return if !$zone || !$dns || !$hostname || !$ip;
+
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+    my $dns_cfg = PVE::Network::SDN::Dns::config();
+    my $plugin_config = $dns_cfg->{ids}->{$dns};
+    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+    if($reverse) {
+	$plugin->add_ptr_record($plugin_config, $zone, $hostname, $ip);
+    } else {
+	$plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
+    }
+};
+
+my $del_dns_record = sub {
+    my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
+
+    return if !$zone || !$dns || !$hostname || !$ip;
+
+    $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+    my $dns_cfg = PVE::Network::SDN::Dns::config();
+    my $plugin_config = $dns_cfg->{ids}->{$dns};
+    my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+    if($reverse) {
+	$plugin->del_ptr_record($plugin_config, $zone, $ip);
+    } else {
+	$plugin->del_a_record($plugin_config, $zone, $hostname);
+    }
+};
+
 sub next_free_ip {
-    my ($subnetid, $subnet) = @_;
+    my ($subnetid, $subnet, $hostname) = @_;
+
+    my $cidr = undef;
+    my $ip = undef;
 
     my $ipamid = $subnet->{ipam};
-    return if !$ipamid;
+    my $dns = $subnet->{dns};
+    my $dnszone = $subnet->{dnszone};
+    my $reversedns = $subnet->{reversedns};
+    my $reversednszone = $subnet->{reversednszone};
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+    #verify dns zones before ipam
+    &$verify_dns_zone($dnszone, $dns);
+    &$verify_dns_zone($reversednszone, $reversedns);
+
+    if($ipamid) {
+	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+	my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
+	my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+	$cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
+	($ip, undef) = split(/\//, $cidr);
+    }
 
-    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-    my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
-    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-    my $ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
-    return $ip;
+    eval {
+	#add dns
+	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+	#add reverse dns
+	&$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+    };
+    if ($@) {
+	#rollback
+	my $err = $@;
+	eval {
+	    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname)
+	};
+	die $err;
+    }
+    return $cidr;
 }
 
 sub add_ip {
-    my ($subnetid, $subnet, $ip) = @_;
+    my ($subnetid, $subnet, $ip, $hostname) = @_;
 
     my $ipamid = $subnet->{ipam};
-    return if !$ipamid;
+    my $dns = $subnet->{dns};
+    my $dnszone = $subnet->{dnszone};
+    my $reversedns = $subnet->{reversedns};
+    my $reversednszone = $subnet->{reversednszone};
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+    #verify dns zones before ipam
+    &$verify_dns_zone($dnszone, $dns);
+    &$verify_dns_zone($reversednszone, $reversedns);
+
+    if ($ipamid) {
+	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+	my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
+	my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+	$plugin->add_ip($plugin_config, $subnetid, $ip);
+    }
 
-    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-    my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
-    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-    $plugin->add_ip($plugin_config, $subnetid, $ip);
+    eval {
+	#add dns
+	&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+	#add reverse dns
+	&$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+    };
+    if ($@) {
+	#rollback
+	my $err = $@;
+	eval {
+	    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname)
+	};
+	die $err;
+    }
 }
 
 sub del_ip {
-    my ($subnetid, $subnet, $ip) = @_;
+    my ($subnetid, $subnet, $ip, $hostname) = @_;
 
     my $ipamid = $subnet->{ipam};
-    return if !$ipamid;
+    my $dns = $subnet->{dns};
+    my $dnszone = $subnet->{dnszone};
+    my $reversedns = $subnet->{reversedns};
+    my $reversednszone = $subnet->{reversednszone};
+    my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+    &$verify_dns_zone($dnszone, $dns);
+    &$verify_dns_zone($reversednszone, $reversedns);
+
+    if ($ipamid) {
+	my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+	my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
+	my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+	$plugin->del_ip($plugin_config, $subnetid, $ip);
+    }
 
-    my $ipam_cfg = PVE::Network::SDN::Ipams::config();
-    my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
-    my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
-    $plugin->del_ip($plugin_config, $subnetid, $ip);
+    eval {
+	&$del_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+	&$del_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+    };
+    if ($@) {
+	warn $@;
+    }
 }
 
 1;
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 6ea3a9a..c9916b1 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -55,7 +55,7 @@ sub get_vnet {
 }
 
 sub get_next_free_ip {
-    my ($vnet, $ipversion) = @_;
+    my ($vnet, $hostname, $ipversion) = @_;
 
     $ipversion = 4 if !$ipversion;
     my $subnets_cfg = PVE::Network::SDN::Subnets::config();
@@ -71,7 +71,7 @@ sub get_next_free_ip {
 	$subnet = $subnets_cfg->{ids}->{$subnetid};
 	if ($subnet && $subnet->{ipam}) {
 	    eval {
-		$ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet);
+		$ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet, $hostname);
 	    };
 	    warn $@ if $@;
 	}
@@ -83,23 +83,23 @@ sub get_next_free_ip {
 }
 
 sub add_ip {
-    my ($vnet, $cidr, $name) = @_;
+    my ($vnet, $cidr, $hostname) = @_;
 
     my ($ip, $mask) = split(/\//, $cidr);
     my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
     return if !$subnet->{ipam};
 
-    PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip);
+    PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip, $hostname);
 }
 
 sub del_ip {
-    my ($vnet, $cidr) = @_;
+    my ($vnet, $cidr, $hostname) = @_;
 
     my ($ip, $mask) = split(/\//, $cidr);
     my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
     return if !$subnet->{ipam};
 
-    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip);
+    PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname);
 }
 
 1;
-- 
2.20.1




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

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

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

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




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

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

skip network && broadcast address

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

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




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

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

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

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




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

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

(use snat instead masquerade for performance)

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

diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index ff25f12..b89f4b1 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -51,6 +51,7 @@ sub generate_sdn_config {
 
     my $vrf_iface = "vrf_$zoneid";
     my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
+    my $local_node = PVE::INotify::nodename();
 
     die "missing vxlan tag" if !$tag;
     warn "vlan-aware vnet can't be enabled with evpn plugin" if $vnet->{vlanaware};
@@ -86,6 +87,23 @@ sub generate_sdn_config {
 	    push @iface_config, "address $gateway" if !defined($address->{$gateway});
 	    $address->{$gateway} = 1;
 	}
+	if ($subnet->{snat}) {
+	    my $gatewaynodes = $controller->{'gateway-nodes'};
+	    my $is_evpn_gateway = "";
+	    foreach my $evpn_gatewaynode (PVE::Tools::split_list($gatewaynodes)) {
+		$is_evpn_gateway = 1 if $evpn_gatewaynode eq $local_node;
+	    }
+            #find outgoing interface
+            my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip('8.8.8.8');
+            if ($outip && $outiface && $is_evpn_gateway) {
+                #use snat, faster than masquerade
+                push @iface_config, "post-up iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+                push @iface_config, "post-down iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+                #add conntrack zone once on outgoing interface
+                push @iface_config, "post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
+                push @iface_config, "post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
+            }
+        }
     }
 
     push @iface_config, "hwaddress $mac" if $mac;
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index a4299dd..c58ae87 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -48,6 +48,18 @@ sub generate_sdn_config {
 	#add route for /32 pointtopoint
 	my ($ip, $mask) = split(/\//, $cidr);
 	push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32;
+	if ($subnet->{snat}) {
+	    #find outgoing interface
+	    my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip('8.8.8.8');
+	    if ($outip && $outiface) {
+		#use snat, faster than masquerade
+		push @iface_config, "post-up iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+		push @iface_config, "post-down iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+		#add conntrack zone once on outgoing interface
+		push @iface_config, "post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
+		push @iface_config, "post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
+	    }
+	}
     }
 
     push @iface_config, "hwaddress $mac" if $mac;
-- 
2.20.1




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

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

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

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




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

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

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

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




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

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

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

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




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

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

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/Zones/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] 28+ messages in thread

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

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/API2/Network/SDN/Subnets.pm | 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] 28+ messages in thread

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

Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
 PVE/Network/SDN/SubnetPlugin.pm | 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] 28+ messages in thread

* [pve-devel] [PATCH v9 pve-network 26/26] generate sdn/.running-config on apply
  2020-10-05 15:07 [pve-devel] [PATCH v9 pve-network 00/26] add subnet plugin Alexandre Derumier
                   ` (24 preceding siblings ...)
  2020-10-05 15:08 ` [pve-devel] [PATCH v9 pve-network 25/26] don't allow subnets on vlanware vnet Alexandre Derumier
@ 2020-10-05 15:08 ` Alexandre Derumier
  25 siblings, 0 replies; 28+ messages in thread
From: Alexandre Derumier @ 2020-10-05 15:08 UTC (permalink / raw)
  To: pve-devel

This is the source configuration for generate local configuration

/sdn/*.cfg are pending configs

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

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




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

* [pve-devel] [PATCH v9 pve-network 20/26] subnet: disable route option for now and add dns domain format
  2020-09-28  8:43 [pve-devel] [PATCH v9 pve-network 00/26] add subnet plugin Alexandre Derumier
@ 2020-09-28  8:43 ` Alexandre Derumier
  0 siblings, 0 replies; 28+ messages in thread
From: Alexandre Derumier @ 2020-09-28  8:43 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] 28+ messages in thread

end of thread, other threads:[~2020-10-05 15:09 UTC | newest]

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