* [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management
@ 2020-07-20 15:44 Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 01/11] add subnet plugin Alexandre Derumier
` (10 more replies)
0 siblings, 11 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:44 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
Alexandre Derumier (11):
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
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 | 80 ++++++++
PVE/Network/SDN/Ipams/Makefile | 8 +
PVE/Network/SDN/Ipams/NetboxPlugin.pm | 169 ++++++++++++++++
PVE/Network/SDN/Ipams/PVEPlugin.pm | 165 ++++++++++++++++
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 | 55 ++++++
PVE/Network/SDN/VnetPlugin.pm | 25 +--
PVE/Network/SDN/Vnets.pm | 25 +++
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, 1564 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 01/11] add subnet plugin
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
@ 2020-07-20 15:44 ` Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 02/11] vnets: add subnets Alexandre Derumier
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:44 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 02/11] vnets: add subnets
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 01/11] add subnet plugin Alexandre Derumier
@ 2020-07-20 15:44 ` Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 03/11] add subnets verifications hooks Alexandre Derumier
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:44 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 03/11] add subnets verifications hooks
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 01/11] add subnet plugin Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 02/11] vnets: add subnets Alexandre Derumier
@ 2020-07-20 15:44 ` Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 04/11] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:44 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 04/11] zones: simple|evpn: add gateway ip from subnets to vnet
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
` (2 preceding siblings ...)
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 03/11] add subnets verifications hooks Alexandre Derumier
@ 2020-07-20 15:44 ` Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 05/11] zone: add vnet_update_hook Alexandre Derumier
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:44 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 05/11] zone: add vnet_update_hook
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
` (3 preceding siblings ...)
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 04/11] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
@ 2020-07-20 15:44 ` Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 06/11] vnets: subnets: use cidr Alexandre Derumier
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:44 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 06/11] vnets: subnets: use cidr
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
` (4 preceding siblings ...)
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 05/11] zone: add vnet_update_hook Alexandre Derumier
@ 2020-07-20 15:44 ` Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 07/11] subnet: fix on_delete_hook Alexandre Derumier
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:44 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 07/11] subnet: fix on_delete_hook
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
` (5 preceding siblings ...)
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 06/11] vnets: subnets: use cidr Alexandre Derumier
@ 2020-07-20 15:44 ` Alexandre Derumier
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 08/11] api2: subnet create: convert cidr to subnetid Alexandre Derumier
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:44 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 08/11] api2: subnet create: convert cidr to subnetid
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
` (6 preceding siblings ...)
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 07/11] subnet: fix on_delete_hook Alexandre Derumier
@ 2020-07-20 15:45 ` Alexandre Derumier
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 09/11] api2: increase version on apply/reload only Alexandre Derumier
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:45 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 09/11] api2: increase version on apply/reload only
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
` (7 preceding siblings ...)
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 08/11] api2: subnet create: convert cidr to subnetid Alexandre Derumier
@ 2020-07-20 15:45 ` Alexandre Derumier
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 10/11] add ipams plugins Alexandre Derumier
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 11/11] add pve internal ipam plugin Alexandre Derumier
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:45 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 10/11] add ipams plugins
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
` (8 preceding siblings ...)
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 09/11] api2: increase version on apply/reload only Alexandre Derumier
@ 2020-07-20 15:45 ` Alexandre Derumier
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 11/11] add pve internal ipam plugin Alexandre Derumier
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:45 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] 12+ messages in thread
* [pve-devel] [PATCH v3 pve-network 11/11] add pve internal ipam plugin
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
` (9 preceding siblings ...)
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 10/11] add ipams plugins Alexandre Derumier
@ 2020-07-20 15:45 ` Alexandre Derumier
10 siblings, 0 replies; 12+ messages in thread
From: Alexandre Derumier @ 2020-07-20 15:45 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 | 165 +++++++++++++++++++++++++
PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 2 +-
PVE/Network/SDN/Ipams/Plugin.pm | 2 +-
debian/control | 1 +
9 files changed, 176 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..f152a55
--- /dev/null
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -0,0 +1,165 @@
+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 $@;
+
+ return $freeip;
+}
+
+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] 12+ messages in thread
end of thread, other threads:[~2020-07-20 15:46 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-07-20 15:44 [pve-devel] [PATCH v3 pve-network 00/11] sdn : add subnets management Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 01/11] add subnet plugin Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 02/11] vnets: add subnets Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 03/11] add subnets verifications hooks Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 04/11] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 05/11] zone: add vnet_update_hook Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 06/11] vnets: subnets: use cidr Alexandre Derumier
2020-07-20 15:44 ` [pve-devel] [PATCH v3 pve-network 07/11] subnet: fix on_delete_hook Alexandre Derumier
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 08/11] api2: subnet create: convert cidr to subnetid Alexandre Derumier
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 09/11] api2: increase version on apply/reload only Alexandre Derumier
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 10/11] add ipams plugins Alexandre Derumier
2020-07-20 15:45 ` [pve-devel] [PATCH v3 pve-network 11/11] add pve internal ipam plugin Alexandre Derumier
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.