public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management
@ 2020-07-31 17:16 Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 01/14] add subnet plugin Alexandre Derumier
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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 )
- ...


Currently, only 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.


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

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

 PVE/API2/Network/SDN.pm                |  17 ++
 PVE/API2/Network/SDN/Controllers.pm    |   6 -
 PVE/API2/Network/SDN/Ipams.pm          | 242 +++++++++++++++++++++++
 PVE/API2/Network/SDN/Makefile          |   2 +-
 PVE/API2/Network/SDN/Subnets.pm        | 264 +++++++++++++++++++++++++
 PVE/API2/Network/SDN/Vnets.pm          |  16 +-
 PVE/API2/Network/SDN/Zones.pm          |   6 -
 PVE/Network/SDN/Ipams.pm               |  68 +++++++
 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               |   3 +-
 PVE/Network/SDN/SubnetPlugin.pm        | 132 +++++++++++++
 PVE/Network/SDN/Subnets.pm             | 115 +++++++++++
 PVE/Network/SDN/VnetPlugin.pm          |  25 +--
 PVE/Network/SDN/Vnets.pm               |  50 +++++
 PVE/Network/SDN/Zones.pm               |   4 +-
 PVE/Network/SDN/Zones/EvpnPlugin.pm    |  30 ++-
 PVE/Network/SDN/Zones/Plugin.pm        |   7 +-
 PVE/Network/SDN/Zones/QinQPlugin.pm    |  10 +-
 PVE/Network/SDN/Zones/SimplePlugin.pm  |  25 ++-
 PVE/Network/SDN/Zones/VlanPlugin.pm    |  10 +-
 PVE/Network/SDN/Zones/VxlanPlugin.pm   |  16 +-
 debian/control                         |   2 +
 26 files changed, 1638 insertions(+), 71 deletions(-)
 create mode 100644 PVE/API2/Network/SDN/Ipams.pm
 create mode 100644 PVE/API2/Network/SDN/Subnets.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] 15+ messages in thread

* [pve-devel] [PATCH v5 pve-network 01/14] add subnet plugin
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 02/14] vnets: add subnets Alexandre Derumier
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

* [pve-devel] [PATCH v5 pve-network 02/14] vnets: add subnets
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 01/14] add subnet plugin Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 03/14] add subnets verifications hooks Alexandre Derumier
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

* [pve-devel] [PATCH v5 pve-network 03/14] add subnets verifications hooks
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 01/14] add subnet plugin Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 02/14] vnets: add subnets Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 04/14] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

* [pve-devel] [PATCH v5 pve-network 04/14] zones: simple|evpn: add gateway ip from subnets to vnet
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
                   ` (2 preceding siblings ...)
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 03/14] add subnets verifications hooks Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 05/14] zone: add vnet_update_hook Alexandre Derumier
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

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

* [pve-devel] [PATCH v5 pve-network 06/14] vnets: subnets: use cidr
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
                   ` (4 preceding siblings ...)
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 05/14] zone: add vnet_update_hook Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 07/14] subnet: fix on_delete_hook Alexandre Derumier
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

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

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

* [pve-devel] [PATCH v5 pve-network 09/14] api2: increase version on apply/reload only
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
                   ` (7 preceding siblings ...)
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 08/14] api2: subnet create: convert cidr to subnetid Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 10/14] add ipams plugins Alexandre Derumier
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

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

* [pve-devel] [PATCH v5 pve-network 11/14] add pve internal ipam plugin
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
                   ` (9 preceding siblings ...)
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 10/14] add ipams plugins Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 12/14] vnets: find_free_ip : add ipversion detection Alexandre Derumier
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

* [pve-devel] [PATCH v5 pve-network 12/14] vnets: find_free_ip : add ipversion detection
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
                   ` (10 preceding siblings ...)
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 11/14] add pve internal ipam plugin Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 13/14] vnets: add add_ip Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 14/14] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

* [pve-devel] [PATCH v5 pve-network 13/14] vnets: add add_ip
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
                   ` (11 preceding siblings ...)
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 12/14] vnets: find_free_ip : add ipversion detection Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 14/14] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

* [pve-devel] [PATCH v5 pve-network 14/14] vnets: add del_ip + rework add_ip/find_free_ip
  2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
                   ` (12 preceding siblings ...)
  2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 13/14] vnets: add add_ip Alexandre Derumier
@ 2020-07-31 17:16 ` Alexandre Derumier
  13 siblings, 0 replies; 15+ messages in thread
From: Alexandre Derumier @ 2020-07-31 17:16 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] 15+ messages in thread

end of thread, other threads:[~2020-07-31 17:17 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-31 17:16 [pve-devel] [PATCH v5 pve-network 00/14] sdn : add subnets management Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 01/14] add subnet plugin Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 02/14] vnets: add subnets Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 03/14] add subnets verifications hooks Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 04/14] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 05/14] zone: add vnet_update_hook Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 06/14] vnets: subnets: use cidr Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 07/14] subnet: fix on_delete_hook Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 08/14] api2: subnet create: convert cidr to subnetid Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 09/14] api2: increase version on apply/reload only Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 10/14] add ipams plugins Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 11/14] add pve internal ipam plugin Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 12/14] vnets: find_free_ip : add ipversion detection Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 13/14] vnets: add add_ip Alexandre Derumier
2020-07-31 17:16 ` [pve-devel] [PATCH v5 pve-network 14/14] vnets: add del_ip + rework add_ip/find_free_ip 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