* [pve-devel] [PATCH v7 pve-network 01/21] add subnet plugin
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 02/21] vnets: add subnets Alexandre Derumier
` (19 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 02/21] vnets: add subnets
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 01/21] add subnet plugin Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 03/21] add subnets verifications hooks Alexandre Derumier
` (18 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 03/21] add subnets verifications hooks
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 01/21] add subnet plugin Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 02/21] vnets: add subnets Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 04/21] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
` (17 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 04/21] zones: simple|evpn: add gateway ip from subnets to vnet
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (2 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 03/21] add subnets verifications hooks Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 05/21] zone: add vnet_update_hook Alexandre Derumier
` (16 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 05/21] zone: add vnet_update_hook
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (3 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 04/21] zones: simple|evpn: add gateway ip from subnets to vnet Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 06/21] vnets: subnets: use cidr Alexandre Derumier
` (15 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
move verify_tag code in this hook
add mac address generation for simple && evpn plugin
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 06/21] vnets: subnets: use cidr
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (4 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 05/21] zone: add vnet_update_hook Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 07/21] subnet: fix on_delete_hook Alexandre Derumier
` (14 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 07/21] subnet: fix on_delete_hook
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (5 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 06/21] vnets: subnets: use cidr Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 08/21] api2: subnet create: convert cidr to subnetid Alexandre Derumier
` (13 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 08/21] api2: subnet create: convert cidr to subnetid
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (6 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 07/21] subnet: fix on_delete_hook Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 09/21] api2: increase version on apply/reload only Alexandre Derumier
` (12 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 09/21] api2: increase version on apply/reload only
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (7 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 08/21] api2: subnet create: convert cidr to subnetid Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 10/21] add ipams plugins Alexandre Derumier
` (11 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 10/21] add ipams plugins
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (8 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 09/21] api2: increase version on apply/reload only Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 11/21] add pve internal ipam plugin Alexandre Derumier
` (10 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 11/21] add pve internal ipam plugin
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (9 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 10/21] add ipams plugins Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 12/21] vnets: find_free_ip : add ipversion detection Alexandre Derumier
` (9 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 12/21] vnets: find_free_ip : add ipversion detection
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (10 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 11/21] add pve internal ipam plugin Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 13/21] vnets: add add_ip Alexandre Derumier
` (8 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 13/21] vnets: add add_ip
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (11 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 12/21] vnets: find_free_ip : add ipversion detection Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 14/21] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
` (7 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 14/21] vnets: add del_ip + rework add_ip/find_free_ip
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (12 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 13/21] vnets: add add_ip Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 15/21] add dns plugin Alexandre Derumier
` (6 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
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] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 15/21] add dns plugin
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (13 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 14/21] vnets: add del_ip + rework add_ip/find_free_ip Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 16/21] Fix vnet gateway for routed setup + /32 pointopoint subnet Alexandre Derumier
` (5 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
PVE/API2/Network/SDN.pm | 7 +
PVE/API2/Network/SDN/Dns.pm | 242 ++++++++++++++++++++++++++
PVE/API2/Network/SDN/Makefile | 2 +-
PVE/Network/SDN/Dns.pm | 57 ++++++
PVE/Network/SDN/Dns/Makefile | 8 +
PVE/Network/SDN/Dns/Plugin.pm | 117 +++++++++++++
PVE/Network/SDN/Dns/PowerdnsPlugin.pm | 201 +++++++++++++++++++++
PVE/Network/SDN/Ipams/PVEPlugin.pm | 1 -
PVE/Network/SDN/Ipams/Plugin.pm | 2 +-
PVE/Network/SDN/Makefile | 3 +-
PVE/Network/SDN/SubnetPlugin.pm | 53 ++++--
PVE/Network/SDN/Subnets.pm | 156 +++++++++++++++--
PVE/Network/SDN/Vnets.pm | 12 +-
13 files changed, 814 insertions(+), 47 deletions(-)
create mode 100644 PVE/API2/Network/SDN/Dns.pm
create mode 100644 PVE/Network/SDN/Dns.pm
create mode 100644 PVE/Network/SDN/Dns/Makefile
create mode 100644 PVE/Network/SDN/Dns/Plugin.pm
create mode 100644 PVE/Network/SDN/Dns/PowerdnsPlugin.pm
diff --git a/PVE/API2/Network/SDN.pm b/PVE/API2/Network/SDN.pm
index 6055fe5..0a5fa33 100644
--- a/PVE/API2/Network/SDN.pm
+++ b/PVE/API2/Network/SDN.pm
@@ -17,6 +17,7 @@ use PVE::API2::Network::SDN::Vnets;
use PVE::API2::Network::SDN::Zones;
use PVE::API2::Network::SDN::Subnets;
use PVE::API2::Network::SDN::Ipams;
+use PVE::API2::Network::SDN::Dns;
use base qw(PVE::RESTHandler);
@@ -45,6 +46,11 @@ __PACKAGE__->register_method ({
path => 'ipams',
});
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Network::SDN::Dns",
+ path => 'dns',
+});
+
__PACKAGE__->register_method({
name => 'index',
path => '',
@@ -76,6 +82,7 @@ __PACKAGE__->register_method({
{ id => 'controllers' },
{ id => 'subnets' },
{ id => 'ipams' },
+ { id => 'dns' },
];
return $res;
diff --git a/PVE/API2/Network/SDN/Dns.pm b/PVE/API2/Network/SDN/Dns.pm
new file mode 100644
index 0000000..ea26af3
--- /dev/null
+++ b/PVE/API2/Network/SDN/Dns.pm
@@ -0,0 +1,242 @@
+package PVE::API2::Network::SDN::Dns;
+
+use strict;
+use warnings;
+
+use PVE::SafeSyslog;
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+use PVE::Network::SDN;
+use PVE::Network::SDN::Dns;
+use PVE::Network::SDN::Dns::Plugin;
+use PVE::Network::SDN::Dns::PowerdnsPlugin;
+
+use Storable qw(dclone);
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+my $sdn_dns_type_enum = PVE::Network::SDN::Dns::Plugin->lookup_types();
+
+my $api_sdn_dns_config = sub {
+ my ($cfg, $id) = @_;
+
+ my $scfg = dclone(PVE::Network::SDN::Dns::sdn_dns_config($cfg, $id));
+ $scfg->{dns} = $id;
+ $scfg->{digest} = $cfg->{digest};
+
+ return $scfg;
+};
+
+__PACKAGE__->register_method ({
+ name => 'index',
+ path => '',
+ method => 'GET',
+ description => "SDN dns index.",
+ permissions => {
+ description => "Only list entries where you have 'SDN.Audit' or 'SDN.Allocate' permissions on '/sdn/dns/<dns>'",
+ user => 'all',
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ type => {
+ description => "Only list sdn dns of specific type",
+ type => 'string',
+ enum => $sdn_dns_type_enum,
+ optional => 1,
+ },
+ },
+ },
+ returns => {
+ type => 'array',
+ items => {
+ type => "object",
+ properties => { dns => { type => 'string'},
+ type => { type => 'string'},
+ },
+ },
+ links => [ { rel => 'child', href => "{dns}" } ],
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $rpcenv = PVE::RPCEnvironment::get();
+ my $authuser = $rpcenv->get_user();
+
+
+ my $cfg = PVE::Network::SDN::Dns::config();
+
+ my @sids = PVE::Network::SDN::Dns::sdn_dns_ids($cfg);
+ my $res = [];
+ foreach my $id (@sids) {
+ my $privs = [ 'SDN.Audit', 'SDN.Allocate' ];
+ next if !$rpcenv->check_any($authuser, "/sdn/dns/$id", $privs, 1);
+
+ my $scfg = &$api_sdn_dns_config($cfg, $id);
+ next if $param->{type} && $param->{type} ne $scfg->{type};
+
+ my $plugin_config = $cfg->{ids}->{$id};
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+ push @$res, $scfg;
+ }
+
+ return $res;
+ }});
+
+__PACKAGE__->register_method ({
+ name => 'read',
+ path => '{dns}',
+ method => 'GET',
+ description => "Read sdn dns configuration.",
+ permissions => {
+ check => ['perm', '/sdn/dns/{dns}', ['SDN.Allocate']],
+ },
+
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ dns => get_standard_option('pve-sdn-dns-id'),
+ },
+ },
+ returns => { type => 'object' },
+ code => sub {
+ my ($param) = @_;
+
+ my $cfg = PVE::Network::SDN::Dns::config();
+
+ return &$api_sdn_dns_config($cfg, $param->{dns});
+ }});
+
+__PACKAGE__->register_method ({
+ name => 'create',
+ protected => 1,
+ path => '',
+ method => 'POST',
+ description => "Create a new sdn dns object.",
+ permissions => {
+ check => ['perm', '/sdn/dns', ['SDN.Allocate']],
+ },
+ parameters => PVE::Network::SDN::Dns::Plugin->createSchema(),
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $type = extract_param($param, 'type');
+ my $id = extract_param($param, 'dns');
+
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($type);
+ my $opts = $plugin->check_config($id, $param, 1, 1);
+
+ # create /etc/pve/sdn directory
+ PVE::Cluster::check_cfs_quorum();
+ mkdir("/etc/pve/sdn");
+
+ PVE::Network::SDN::lock_sdn_config(
+ sub {
+
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
+
+ my $scfg = undef;
+ if ($scfg = PVE::Network::SDN::Dns::sdn_dns_config($dns_cfg, $id, 1)) {
+ die "sdn dns object ID '$id' already defined\n";
+ }
+
+ $dns_cfg->{ids}->{$id} = $opts;
+
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
+ $plugin->on_update_hook($opts);
+
+ PVE::Network::SDN::Dns::write_config($dns_cfg);
+
+ }, "create sdn dns object failed");
+
+ return undef;
+ }});
+
+__PACKAGE__->register_method ({
+ name => 'update',
+ protected => 1,
+ path => '{dns}',
+ method => 'PUT',
+ description => "Update sdn dns object configuration.",
+ permissions => {
+ check => ['perm', '/sdn/dns', ['SDN.Allocate']],
+ },
+ parameters => PVE::Network::SDN::Dns::Plugin->updateSchema(),
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $id = extract_param($param, 'dns');
+ my $digest = extract_param($param, 'digest');
+
+ PVE::Network::SDN::lock_sdn_config(
+ sub {
+
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
+
+ PVE::SectionConfig::assert_if_modified($dns_cfg, $digest);
+
+ my $scfg = PVE::Network::SDN::Dns::sdn_dns_config($dns_cfg, $id);
+
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
+ my $opts = $plugin->check_config($id, $param, 0, 1);
+
+ foreach my $k (%$opts) {
+ $scfg->{$k} = $opts->{$k};
+ }
+
+ $plugin->on_update_hook($scfg);
+
+ PVE::Network::SDN::Dns::write_config($dns_cfg);
+
+ }, "update sdn dns object failed");
+
+ return undef;
+ }});
+
+__PACKAGE__->register_method ({
+ name => 'delete',
+ protected => 1,
+ path => '{dns}',
+ method => 'DELETE',
+ description => "Delete sdn dns object configuration.",
+ permissions => {
+ check => ['perm', '/sdn/dns', ['SDN.Allocate']],
+ },
+ parameters => {
+ additionalProperties => 0,
+ properties => {
+ dns => get_standard_option('pve-sdn-dns-id', {
+ completion => \&PVE::Network::SDN::Dns::complete_sdn_dns,
+ }),
+ },
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $id = extract_param($param, 'dns');
+
+ PVE::Network::SDN::lock_sdn_config(
+ sub {
+
+ my $cfg = PVE::Network::SDN::Dns::config();
+
+ my $scfg = PVE::Network::SDN::Dns::sdn_dns_config($cfg, $id);
+
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($scfg->{type});
+
+ delete $cfg->{ids}->{$id};
+ PVE::Network::SDN::Dns::write_config($cfg);
+
+ }, "delete sdn dns object failed");
+
+ return undef;
+ }});
+
+1;
diff --git a/PVE/API2/Network/SDN/Makefile b/PVE/API2/Network/SDN/Makefile
index 1117dfa..3683fa4 100644
--- a/PVE/API2/Network/SDN/Makefile
+++ b/PVE/API2/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm
+SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm
PERL5DIR=${DESTDIR}/usr/share/perl5
diff --git a/PVE/Network/SDN/Dns.pm b/PVE/Network/SDN/Dns.pm
new file mode 100644
index 0000000..c2e153a
--- /dev/null
+++ b/PVE/Network/SDN/Dns.pm
@@ -0,0 +1,57 @@
+package PVE::Network::SDN::Dns;
+
+use strict;
+use warnings;
+
+use Data::Dumper;
+use JSON;
+
+use PVE::Tools qw(extract_param dir_glob_regex run_command);
+use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::Network;
+
+use PVE::Network::SDN::Dns::PowerdnsPlugin;
+use PVE::Network::SDN::Dns::Plugin;
+
+PVE::Network::SDN::Dns::PowerdnsPlugin->register();
+PVE::Network::SDN::Dns::Plugin->init();
+
+
+sub sdn_dns_config {
+ my ($cfg, $id, $noerr) = @_;
+
+ die "no sdn dns ID specified\n" if !$id;
+
+ my $scfg = $cfg->{ids}->{$id};
+ die "sdn '$id' does not exist\n" if (!$noerr && !$scfg);
+
+ return $scfg;
+}
+
+sub config {
+ my $config = cfs_read_file("sdn/dns.cfg");
+ return $config;
+}
+
+sub write_config {
+ my ($cfg) = @_;
+
+ cfs_write_file("sdn/dns.cfg", $cfg);
+}
+
+sub sdn_dns_ids {
+ my ($cfg) = @_;
+
+ return keys %{$cfg->{ids}};
+}
+
+sub complete_sdn_dns {
+ my ($cmdname, $pname, $cvalue) = @_;
+
+ my $cfg = PVE::Network::SDN::Dns::config();
+
+ return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Dns::sdn_dns_ids($cfg) ];
+}
+
+1;
+
diff --git a/PVE/Network/SDN/Dns/Makefile b/PVE/Network/SDN/Dns/Makefile
new file mode 100644
index 0000000..81cd2a1
--- /dev/null
+++ b/PVE/Network/SDN/Dns/Makefile
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm PowerdnsPlugin.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+ for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Dns/$$i; done
diff --git a/PVE/Network/SDN/Dns/Plugin.pm b/PVE/Network/SDN/Dns/Plugin.pm
new file mode 100644
index 0000000..baa9316
--- /dev/null
+++ b/PVE/Network/SDN/Dns/Plugin.pm
@@ -0,0 +1,117 @@
+package PVE::Network::SDN::Dns::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(run_command);
+use PVE::JSONSchema;
+use PVE::Cluster;
+use HTTP::Request;
+use LWP::UserAgent;
+use JSON;
+
+use Data::Dumper;
+use PVE::JSONSchema qw(get_standard_option);
+use base qw(PVE::SectionConfig);
+
+PVE::Cluster::cfs_register_file('sdn/dns.cfg',
+ sub { __PACKAGE__->parse_config(@_); },
+ sub { __PACKAGE__->write_config(@_); });
+
+PVE::JSONSchema::register_standard_option('pve-sdn-dns-id', {
+ description => "The SDN dns object identifier.",
+ type => 'string', format => 'pve-sdn-dns-id',
+});
+
+PVE::JSONSchema::register_format('pve-sdn-dns-id', \&parse_sdn_dns_id);
+sub parse_sdn_dns_id {
+ my ($id, $noerr) = @_;
+
+ if ($id !~ m/^[a-z][a-z0-9]*[a-z0-9]$/i) {
+ return undef if $noerr;
+ die "dns ID '$id' contains illegal characters\n";
+ }
+ return $id;
+}
+
+my $defaultData = {
+
+ propertyList => {
+ type => {
+ description => "Plugin type.",
+ type => 'string', format => 'pve-configid',
+ },
+ ttl => { type => 'integer', optional => 1 },
+ dns => get_standard_option('pve-sdn-dns-id',
+ { completion => \&PVE::Network::SDN::Dns::complete_sdn_dns }),
+ },
+};
+
+sub private {
+ return $defaultData;
+}
+
+sub parse_section_header {
+ my ($class, $line) = @_;
+
+ if ($line =~ m/^(\S+):\s*(\S+)\s*$/) {
+ my ($type, $id) = (lc($1), $2);
+ my $errmsg = undef; # set if you want to skip whole section
+ eval { PVE::JSONSchema::pve_verify_configid($type); };
+ $errmsg = $@ if $@;
+ my $config = {}; # to return additional attributes
+ return ($type, $id, $errmsg, $config);
+ }
+ return undef;
+}
+
+
+sub add_a_record {
+ my ($class, $plugin_config, $type, $zone, $reversezone, $hostname, $ip) = @_;
+}
+
+sub del_a_record {
+ my ($class, $plugin_config, $hostname, $ip) = @_;
+}
+
+sub on_update_hook {
+ my ($class, $plugin_config) = @_;
+}
+
+#helpers
+sub api_request {
+ my ($method, $url, $headers, $data) = @_;
+
+ my $encoded_data = to_json($data) if $data;
+
+ my $req = HTTP::Request->new($method,$url, $headers, $encoded_data);
+
+ my $ua = LWP::UserAgent->new(protocols_allowed => ['http', 'https'], timeout => 30);
+ my $proxy = undef;
+
+ if ($proxy) {
+ $ua->proxy(['http', 'https'], $proxy);
+ } else {
+ $ua->env_proxy;
+ }
+
+ $ua->ssl_opts(verify_hostname => 0, SSL_verify_mode => 0x00);
+
+ my $response = $ua->request($req);
+ my $code = $response->code;
+
+ if ($code !~ /^2(\d+)$/) {
+ my $msg = $response->message || 'unknown';
+ die "Invalid response from server: $code $msg\n";
+ }
+
+ my $raw = '';
+ if (defined($response->decoded_content)) {
+ $raw = $response->decoded_content;
+ } else {
+ $raw = $response->content;
+ }
+ return from_json($raw) if $raw ne '';
+}
+
+1;
diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
new file mode 100644
index 0000000..8c5dd90
--- /dev/null
+++ b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
@@ -0,0 +1,201 @@
+package PVE::Network::SDN::Dns::PowerdnsPlugin;
+
+use strict;
+use warnings;
+use PVE::INotify;
+use PVE::Cluster;
+use PVE::Tools;
+use JSON;
+use Net::IP;
+
+use base('PVE::Network::SDN::Dns::Plugin');
+
+sub type {
+ return 'powerdns';
+}
+
+sub properties {
+ return {
+ url => {
+ type => 'string',
+ },
+ key => {
+ type => 'string',
+ },
+ };
+}
+
+sub options {
+
+ return {
+ url => { optional => 0},
+ key => { optional => 0 },
+ ttl => { optional => 1 },
+ };
+}
+
+# Plugin implementation
+
+sub add_a_record {
+ my ($class, $plugin_config, $zone, $hostname, $ip) = @_;
+
+ my $url = $plugin_config->{url};
+ my $key = $plugin_config->{key};
+ my $ttl = $plugin_config->{ttl} ? $plugin_config->{ttl} : 14400;
+ my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+ my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
+ my $fqdn = $hostname.".".$zone.".";
+
+
+ my $record = { content => $ip,
+ disabled => JSON::false,
+ name => $fqdn,
+ type => $type,
+ priority => 0 };
+
+ my $rrset = { name => $fqdn,
+ type => $type,
+ ttl => $ttl,
+ changetype => "REPLACE",
+ records => [ $record ] };
+
+
+ my $params = { rrsets => [ $rrset ] };
+
+ eval {
+ PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+ };
+
+ if ($@) {
+ die "error add $fqdn to zone $zone: $@";
+ }
+}
+
+sub add_ptr_record {
+ my ($class, $plugin_config, $zone, $hostname, $ip) = @_;
+
+ my $url = $plugin_config->{url};
+ my $key = $plugin_config->{key};
+ my $ttl = $plugin_config->{ttl} ? $plugin_config->{ttl} : 14400;
+ my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+ my $reverseip = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
+ my $fqdn = $hostname.".".$zone.".";
+ my $type = "PTR";
+
+ my $record = { content => $fqdn,
+ disabled => JSON::false,
+ name => $reverseip,
+ type => $type,
+ priority => 0 };
+
+ my $rrset = { name => $reverseip,
+ type => $type,
+ ttl => $ttl,
+ changetype => "REPLACE",
+ records => [ $record ] };
+
+
+ my $params = { rrsets => [ $rrset ] };
+
+ eval {
+ PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+ };
+
+ if ($@) {
+ die "error add $reverseip to zone $zone: $@";
+ }
+}
+
+sub del_a_record {
+ my ($class, $plugin_config, $zone, $hostname) = @_;
+
+ my $url = $plugin_config->{url};
+ my $key = $plugin_config->{key};
+ my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+ my $fqdn = $hostname.".".$zone.".";
+ my $type = "PTR";
+
+ my $rrset = { name => $fqdn,
+ type => $type,
+ changetype => "DELETE",
+ records => [] };
+
+ my $params = { rrsets => [ $rrset ] };
+
+ eval {
+ PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+ };
+
+ if ($@) {
+ die "error delete $fqdn from zone $zone: $@";
+ }
+}
+
+sub del_ptr_record {
+ my ($class, $plugin_config, $zone, $ip) = @_;
+
+ my $url = $plugin_config->{url};
+ my $key = $plugin_config->{key};
+ my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+ my $reverseip = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
+ my $type = "PTR";
+
+ my $rrset = { name => $reverseip,
+ type => $type,
+ changetype => "DELETE",
+ records => [] };
+
+ my $params = { rrsets => [ $rrset ] };
+
+ eval {
+ PVE::Network::SDN::Dns::Plugin::api_request("PATCH", "$url/zones/$zone", $headers, $params);
+ };
+
+ if ($@) {
+ die "error delete $reverseip from zone $zone: $@";
+ }
+}
+
+sub verify_zone {
+ my ($class, $plugin_config, $zone) = @_;
+
+ #verify that api is working
+
+ my $url = $plugin_config->{url};
+ my $key = $plugin_config->{key};
+ my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+ eval {
+ PVE::Network::SDN::Dns::Plugin::api_request("GET", "$url/zones/$zone", $headers);
+ };
+
+ if ($@) {
+ die "can't read zone $zone: $@";
+ }
+}
+
+
+sub on_update_hook {
+ my ($class, $plugin_config) = @_;
+
+ #verify that api is working
+
+ my $url = $plugin_config->{url};
+ my $key = $plugin_config->{key};
+ my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+
+ eval {
+ PVE::Network::SDN::Dns::Plugin::api_request("GET", "$url", $headers);
+ };
+
+ if ($@) {
+ die "dns api error: $@";
+ }
+}
+
+1;
+
+
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 0dfc8a4..99af0ed 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -99,7 +99,6 @@ sub add_next_freeip {
while(1) {
my $ip = $iplist->ip();
++$iplist;
- print "nextip: $ip\n";
next if defined($s->{ips}->{$ip});
$freeip = $ip;
last;
diff --git a/PVE/Network/SDN/Ipams/Plugin.pm b/PVE/Network/SDN/Ipams/Plugin.pm
index fc736b8..683346c 100644
--- a/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/PVE/Network/SDN/Ipams/Plugin.pm
@@ -110,7 +110,7 @@ sub api_request {
my $response = $ua->request($req);
my $code = $response->code;
- if ($code !~ /2(\d+)$/) {
+ if ($code !~ /^2(\d+)$/) {
my $msg = $response->message || 'unknown';
die "Invalid response from server: $code $msg\n";
}
diff --git a/PVE/Network/SDN/Makefile b/PVE/Network/SDN/Makefile
index fb68856..92cfcd0 100644
--- a/PVE/Network/SDN/Makefile
+++ b/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm Dns.pm
PERL5DIR=${DESTDIR}/usr/share/perl5
@@ -9,4 +9,5 @@ install:
make -C Controllers install
make -C Zones install
make -C Ipams install
+ make -C Dns install
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 6224065..3769e04 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -65,22 +65,25 @@ sub properties {
type => 'string',
description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
},
- #cloudinit, dhcp options
- nameservers => {
- type => 'string', format => 'address-list',
- description => " dns nameserver",
+ dns => {
+ type => 'string',
+ description => "dns api server",
},
- #cloudinit, dhcp options
- searchdomain => {
+ reversedns => {
type => 'string',
+ description => "reverse dns api server",
},
- dhcp => {
- type => 'boolean',
- description => "enable dhcp for this subnet",
+ dnszone => {
+ type => 'string',
+ description => "dns domain zone ex: mydomain.com",
},
- dns_driver => {
+ reversednszone => {
type => 'string',
- description => "Develop some dns registrations plugins (powerdns,...)",
+ description => "reverse dns zone ex: 0.168.192.in-addr.arpa",
+ },
+ dnszoneprefix => {
+ type => 'string',
+ description => "dns domain zone prefix ex: 'adm' -> <hostname>.adm.mydomain.com",
},
ipam => {
type => 'string',
@@ -93,11 +96,12 @@ sub options {
return {
gateway => { optional => 1 },
routes => { optional => 1 },
- nameservers => { optional => 1 },
- searchdomain => { optional => 1 },
snat => { optional => 1 },
- dhcp => { optional => 1 },
- dns_driver => { optional => 1 },
+ dns => { optional => 1 },
+ reversedns => { optional => 1 },
+ dnszone => { optional => 1 },
+ reversednszone => { optional => 1 },
+ dnszoneprefix => { optional => 1 },
ipam => { optional => 1 },
};
}
@@ -105,12 +109,25 @@ sub options {
sub on_update_hook {
my ($class, $subnetid, $subnet_cfg) = @_;
- my $subnet = $subnetid =~ s/-/\//r;
- my $subnet_matcher = subnet_matcher($subnet);
+ my $cidr = $subnetid =~ s/-/\//r;
+ my $subnet_matcher = subnet_matcher($cidr);
+
+ my $subnet = $subnet_cfg->{ids}->{$subnetid};
- my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+ my $gateway = $subnet->{gateway};
+ my $dns = $subnet->{dns};
+ my $dnszone = $subnet->{dnszone};
+ my $reversedns = $subnet->{reversedns};
+ my $reversednszone = $subnet->{reversednszone};
+
+ #to: for /32 pointotoping, allow gateway outside the subnet
raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway);
+ raise_param_exc({ dns => "missing dns provider"}) if $dnszone && !$dns;
+ raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
+ raise_param_exc({ reversedns => "missing dns provider"}) if $reversednszone && !$reversedns;
+ raise_param_exc({ reversednszone => "missing dns zone"}) if $reversedns && !$reversednszone;
+
}
sub on_delete_hook {
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 3ce2d44..4e8353e 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -5,8 +5,10 @@ use warnings;
use Net::Subnet qw(subnet_matcher);
use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use Net::IP;
use PVE::Network::SDN::Ipams;
+use PVE::Network::SDN::Dns;
use PVE::Network::SDN::SubnetPlugin;
PVE::Network::SDN::SubnetPlugin->register();
PVE::Network::SDN::SubnetPlugin->init();
@@ -75,41 +77,157 @@ sub find_ip_subnet {
return ($subnetid, $subnet);
}
+my $verify_dns_zone = sub {
+ my ($zone, $dns) = @_;
+
+ return if !$zone || !$dns;
+
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
+ my $plugin_config = $dns_cfg->{ids}->{$dns};
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+ $plugin->verify_zone($plugin_config, $zone);
+};
+
+my $add_dns_record = sub {
+ my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
+
+ return if !$zone || !$dns || !$hostname || !$ip;
+
+ $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
+ my $plugin_config = $dns_cfg->{ids}->{$dns};
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+ if($reverse) {
+ $plugin->add_ptr_record($plugin_config, $zone, $hostname, $ip);
+ } else {
+ $plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
+ }
+};
+
+my $del_dns_record = sub {
+ my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
+
+ return if !$zone || !$dns || !$hostname || !$ip;
+
+ $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
+ my $plugin_config = $dns_cfg->{ids}->{$dns};
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+ if($reverse) {
+ $plugin->del_ptr_record($plugin_config, $zone, $ip);
+ } else {
+ $plugin->del_a_record($plugin_config, $zone, $hostname);
+ }
+};
+
sub next_free_ip {
- my ($subnetid, $subnet) = @_;
+ my ($subnetid, $subnet, $hostname) = @_;
+
+ my $cidr = undef;
+ my $ip = undef;
my $ipamid = $subnet->{ipam};
- return if !$ipamid;
+ my $dns = $subnet->{dns};
+ my $dnszone = $subnet->{dnszone};
+ my $reversedns = $subnet->{reversedns};
+ my $reversednszone = $subnet->{reversednszone};
+ my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+ #verify dns zones before ipam
+ &$verify_dns_zone($dnszone, $dns);
+ &$verify_dns_zone($reversednszone, $reversedns);
+
+ if($ipamid) {
+ my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+ my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
+ my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+ $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
+ ($ip, undef) = split(/\//, $cidr);
+ }
- my $ipam_cfg = PVE::Network::SDN::Ipams::config();
- my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
- my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
- my $ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
- return $ip;
+ eval {
+ #add dns
+ &$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+ #add reverse dns
+ &$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+ };
+ if ($@) {
+ #rollback
+ my $err = $@;
+ eval {
+ PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname)
+ };
+ die $err;
+ }
+ return $cidr;
}
sub add_ip {
- my ($subnetid, $subnet, $ip) = @_;
+ my ($subnetid, $subnet, $ip, $hostname) = @_;
my $ipamid = $subnet->{ipam};
- return if !$ipamid;
+ my $dns = $subnet->{dns};
+ my $dnszone = $subnet->{dnszone};
+ my $reversedns = $subnet->{reversedns};
+ my $reversednszone = $subnet->{reversednszone};
+ my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+ #verify dns zones before ipam
+ &$verify_dns_zone($dnszone, $dns);
+ &$verify_dns_zone($reversednszone, $reversedns);
+
+ if ($ipamid) {
+ my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+ my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
+ my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+ $plugin->add_ip($plugin_config, $subnetid, $ip);
+ }
- my $ipam_cfg = PVE::Network::SDN::Ipams::config();
- my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
- my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
- $plugin->add_ip($plugin_config, $subnetid, $ip);
+ eval {
+ #add dns
+ &$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+ #add reverse dns
+ &$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+ };
+ if ($@) {
+ #rollback
+ my $err = $@;
+ eval {
+ PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname)
+ };
+ die $err;
+ }
}
sub del_ip {
- my ($subnetid, $subnet, $ip) = @_;
+ my ($subnetid, $subnet, $ip, $hostname) = @_;
my $ipamid = $subnet->{ipam};
- return if !$ipamid;
+ my $dns = $subnet->{dns};
+ my $dnszone = $subnet->{dnszone};
+ my $reversedns = $subnet->{reversedns};
+ my $reversednszone = $subnet->{reversednszone};
+ my $dnszoneprefix = $subnet->{dnszoneprefix};
+
+ &$verify_dns_zone($dnszone, $dns);
+ &$verify_dns_zone($reversednszone, $reversedns);
+
+ if ($ipamid) {
+ my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+ my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
+ my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+ $plugin->del_ip($plugin_config, $subnetid, $ip);
+ }
- my $ipam_cfg = PVE::Network::SDN::Ipams::config();
- my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
- my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
- $plugin->del_ip($plugin_config, $subnetid, $ip);
+ eval {
+ &$del_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
+ &$del_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+ };
+ if ($@) {
+ warn $@;
+ }
}
1;
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index 6ea3a9a..c9916b1 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -55,7 +55,7 @@ sub get_vnet {
}
sub get_next_free_ip {
- my ($vnet, $ipversion) = @_;
+ my ($vnet, $hostname, $ipversion) = @_;
$ipversion = 4 if !$ipversion;
my $subnets_cfg = PVE::Network::SDN::Subnets::config();
@@ -71,7 +71,7 @@ sub get_next_free_ip {
$subnet = $subnets_cfg->{ids}->{$subnetid};
if ($subnet && $subnet->{ipam}) {
eval {
- $ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet);
+ $ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet, $hostname);
};
warn $@ if $@;
}
@@ -83,23 +83,23 @@ sub get_next_free_ip {
}
sub add_ip {
- my ($vnet, $cidr, $name) = @_;
+ my ($vnet, $cidr, $hostname) = @_;
my ($ip, $mask) = split(/\//, $cidr);
my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
return if !$subnet->{ipam};
- PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip);
+ PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip, $hostname);
}
sub del_ip {
- my ($vnet, $cidr) = @_;
+ my ($vnet, $cidr, $hostname) = @_;
my ($ip, $mask) = split(/\//, $cidr);
my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
return if !$subnet->{ipam};
- PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip);
+ PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname);
}
1;
--
2.20.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 16/21] Fix vnet gateway for routed setup + /32 pointopoint subnet
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (14 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 15/21] add dns plugin Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 17/21] ipam : pveplugin : fix find_next_free_ip Alexandre Derumier
` (4 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
PVE/Network/SDN/SubnetPlugin.pm | 6 ++++--
PVE/Network/SDN/Zones/EvpnPlugin.pm | 10 ++++++++--
PVE/Network/SDN/Zones/SimplePlugin.pm | 13 +++++++++++--
test/generateconfig.pl | 3 ++-
4 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 3769e04..bc66b82 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -120,8 +120,10 @@ sub on_update_hook {
my $reversedns = $subnet->{reversedns};
my $reversednszone = $subnet->{reversednszone};
- #to: for /32 pointotoping, allow gateway outside the subnet
- raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway);
+ my ($ip, $mask) = split(/\//, $cidr);
+
+ #for /32 pointopoint, we allow gateway outside the subnet
+ raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
raise_param_exc({ dns => "missing dns provider"}) if $dnszone && !$dns;
raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 0ebe13e..17c9262 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -77,9 +77,15 @@ sub generate_sdn_config {
@iface_config = ();
my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+ my $address = {};
foreach my $subnet (@subnets) {
- next if !defined($subnet_cfg->{ids}->{$subnet});
- push @iface_config, "address $subnet_cfg->{ids}->{$subnet}->{gateway}" if $subnet_cfg->{ids}->{$subnet}->{gateway};
+ my $subnetid = $subnet =~ s/\//-/r;
+ next if !defined($subnet_cfg->{ids}->{$subnetid});
+ my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+ if ($gateway) {
+ push @iface_config, "address $gateway" if !defined($address->{$gateway});
+ $address->{$gateway} = 1;
+ }
}
push @iface_config, "hwaddress $mac" if $mac;
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index 7006b13..a1733d5 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -36,9 +36,18 @@ sub generate_sdn_config {
my @iface_config = ();
my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+ my $address = {};
foreach my $subnet (@subnets) {
- next if !defined($subnet_cfg->{ids}->{$subnet});
- push @iface_config, "address $subnet_cfg->{ids}->{$subnet}->{gateway}" if $subnet_cfg->{ids}->{$subnet}->{gateway};
+ my $subnetid = $subnet =~ s/\//-/r;
+ next if !defined($subnet_cfg->{ids}->{$subnetid});
+ my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+ if ($gateway) {
+ push @iface_config, "address $gateway" if !defined($address->{$gateway});
+ $address->{$gateway} = 1;
+ }
+ #add route for /32 pointtopoint
+ my ($ip, $mask) = split(/\//, $subnet);
+ push @iface_config, "up ip route add $subnet dev $vnetid" if $mask == 32;
}
push @iface_config, "hwaddress $mac" if $mac;
diff --git a/test/generateconfig.pl b/test/generateconfig.pl
index 36880ba..92108ec 100644
--- a/test/generateconfig.pl
+++ b/test/generateconfig.pl
@@ -3,17 +3,18 @@ use warnings;
use File::Copy;
use PVE::Cluster qw(cfs_read_file);
+use PVE::Network::SDN;
use PVE::Network::SDN::Zones;
use PVE::Network::SDN::Controllers;
use Data::Dumper;
my $network_config = PVE::Network::SDN::Zones::generate_etc_network_config();
+
PVE::Network::SDN::Zones::write_etc_network_config($network_config);
print "/etc/network/interfaces.d/sdn\n";
print $network_config;
print "\n";
-
my $controller_config = PVE::Network::SDN::Controllers::generate_controller_config();
if ($controller_config) {
print Dumper($controller_config);
--
2.20.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 17/21] ipam : pveplugin : fix find_next_free_ip
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (15 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 16/21] Fix vnet gateway for routed setup + /32 pointopoint subnet Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 18/21] add vnet to subnets && remove subnetlist from vnet Alexandre Derumier
` (3 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
skip network && broadcast address
---
PVE/Network/SDN/Ipams/PVEPlugin.pm | 11 ++++++-----
debian/control | 1 +
2 files changed, 7 insertions(+), 5 deletions(-)
diff --git a/PVE/Network/SDN/Ipams/PVEPlugin.pm b/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 99af0ed..741a680 100644
--- a/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -6,7 +6,7 @@ use PVE::INotify;
use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_register_file cfs_lock_file);
use PVE::Tools;
use JSON;
-use Net::IP;
+use NetAddr::IP;
use Digest::SHA;
use base('PVE::Network::SDN::Ipams::Plugin');
@@ -93,12 +93,13 @@ sub add_next_freeip {
my $db = read_db();
my $s = $db->{subnets}->{$cidr};
-
- my $iplist = new Net::IP($cidr);
+ my $iplist = new NetAddr::IP($cidr);
+ my $broadcast = $iplist->broadcast();
while(1) {
- my $ip = $iplist->ip();
- ++$iplist;
+ $iplist++;
+ last if $iplist eq $broadcast;
+ my $ip = $iplist->addr();
next if defined($s->{ips}->{$ip});
$freeip = $ip;
last;
diff --git a/debian/control b/debian/control
index c54f8bc..b2e3614 100644
--- a/debian/control
+++ b/debian/control
@@ -18,6 +18,7 @@ Depends: libpve-common-perl (>= 5.0-45),
pve-cluster (>= 5.0-32),
libnet-subnet-perl,
libnet-ip-perl,
+ libnetaddr-ip-perl,
${misc:Depends},
${perl:Depends},
Recommends: frr-pythontools, ifupdown2
--
2.20.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 18/21] add vnet to subnets && remove subnetlist from vnet
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (16 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 17/21] ipam : pveplugin : fix find_next_free_ip Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 19/21] zones: evpn|simple: add snat iptables rules Alexandre Derumier
` (2 subsequent siblings)
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
PVE/API2/Network/SDN/Subnets.pm | 31 +-------------
PVE/Network/SDN/SubnetPlugin.pm | 59 ++++++++++++++++++++-------
PVE/Network/SDN/Subnets.pm | 34 +++++++++------
PVE/Network/SDN/VnetPlugin.pm | 23 ++++-------
PVE/Network/SDN/Vnets.pm | 43 ++++++++++++-------
PVE/Network/SDN/Zones/EvpnPlugin.pm | 10 ++---
PVE/Network/SDN/Zones/SimplePlugin.pm | 16 ++++----
7 files changed, 117 insertions(+), 99 deletions(-)
diff --git a/PVE/API2/Network/SDN/Subnets.pm b/PVE/API2/Network/SDN/Subnets.pm
index 094401c..728b939 100644
--- a/PVE/API2/Network/SDN/Subnets.pm
+++ b/PVE/API2/Network/SDN/Subnets.pm
@@ -135,17 +135,7 @@ __PACKAGE__->register_method ({
}
$cfg->{ids}->{$id} = $opts;
- PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
-
- my $ipam_cfg = PVE::Network::SDN::Ipams::config();
- my $ipam = $cfg->{ids}->{$id}->{ipam};
- if ($ipam) {
- raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
- my $plugin_config = $ipam_cfg->{ids}->{$ipam};
- my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
- $plugin->add_subnet($plugin_config, $id, $cfg->{ids}->{$id});
- $plugin->add_ip($plugin_config, $id, $opts->{gateway}, 1) if $opts->{gateway};
- }
+ PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $opts);
PVE::Network::SDN::Subnets::write_config($cfg);
@@ -182,24 +172,7 @@ __PACKAGE__->register_method ({
my $opts = PVE::Network::SDN::SubnetPlugin->check_config($id, $param, 0, 1);
$cfg->{ids}->{$id} = $opts;
- PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $cfg);
-
- my $ipam_cfg = PVE::Network::SDN::Ipams::config();
- my $ipam = $cfg->{ids}->{$id}->{ipam};
- if ($ipam) {
- raise_param_exc({ ipam => "$ipam not existing"}) if !$ipam_cfg->{ids}->{$ipam};
- my $plugin_config = $ipam_cfg->{ids}->{$ipam};
- my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
- $plugin->add_subnet($plugin_config, $id, $cfg->{ids}->{$id});
-
- if($opts->{gateway} && $scfg->{gateway} && $opts->{gateway} ne $scfg->{gateway}) {
- $plugin->del_ip($plugin_config, $id, $scfg->{gateway});
- }
- if (!defined($opts->{gateway}) && $scfg->{gateway}) {
- $plugin->del_ip($plugin_config, $id, $scfg->{gateway});
- }
- $plugin->add_ip($plugin_config, $id, $opts->{gateway}, 1) if $opts->{gateway};
- }
+ PVE::Network::SDN::SubnetPlugin->on_update_hook($id, $opts, $scfg);
PVE::Network::SDN::Subnets::write_config($cfg);
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index bc66b82..84303d1 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -8,6 +8,8 @@ use base qw(PVE::SectionConfig);
use PVE::JSONSchema qw(get_standard_option);
use PVE::Exception qw(raise raise_param_exc);
use Net::Subnet qw(subnet_matcher);
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Ipams;
PVE::Cluster::cfs_register_file('sdn/subnets.cfg',
sub { __PACKAGE__->parse_config(@_); },
@@ -52,6 +54,10 @@ sub private {
sub properties {
return {
+ vnet => {
+ type => 'string',
+ description => "associated vnet",
+ },
gateway => {
type => 'string', format => 'ip',
description => "Subnet Gateway: Will be assign on vnet for layer3 zones",
@@ -94,6 +100,7 @@ sub properties {
sub options {
return {
+ vnet => { optional => 1 },
gateway => { optional => 1 },
routes => { optional => 1 },
snat => { optional => 1 },
@@ -107,44 +114,66 @@ sub options {
}
sub on_update_hook {
- my ($class, $subnetid, $subnet_cfg) = @_;
+ my ($class, $subnetid, $subnet, $old_subnet) = @_;
my $cidr = $subnetid =~ s/-/\//r;
my $subnet_matcher = subnet_matcher($cidr);
- my $subnet = $subnet_cfg->{ids}->{$subnetid};
-
+ my $vnetid = $subnet->{vnet};
my $gateway = $subnet->{gateway};
+ my $ipam = $subnet->{ipam};
my $dns = $subnet->{dns};
my $dnszone = $subnet->{dnszone};
my $reversedns = $subnet->{reversedns};
my $reversednszone = $subnet->{reversednszone};
- my ($ip, $mask) = split(/\//, $cidr);
+ my $old_gateway = $old_subnet->{gateway} if $old_subnet;
+ if($vnetid) {
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+ raise_param_exc({ vnet => "$vnetid don't exist"}) if !$vnet;
+ }
+
+ my ($ip, $mask) = split(/\//, $cidr);
#for /32 pointopoint, we allow gateway outside the subnet
- raise_param_exc({ gateway => "$gateway is not in subnet $subnet"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
+ raise_param_exc({ gateway => "$gateway is not in subnet $subnetid"}) if $gateway && !$subnet_matcher->($gateway) && $mask != 32;
raise_param_exc({ dns => "missing dns provider"}) if $dnszone && !$dns;
raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
raise_param_exc({ reversedns => "missing dns provider"}) if $reversednszone && !$reversedns;
raise_param_exc({ reversednszone => "missing dns zone"}) if $reversedns && !$reversednszone;
+ if ($ipam) {
+ my $ipam_cfg = PVE::Network::SDN::Ipams::config();
+ my $plugin_config = $ipam_cfg->{ids}->{$ipam};
+ raise_param_exc({ ipam => "$ipam not existing"}) if !$plugin_config;
+ my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+ $plugin->add_subnet($plugin_config, $subnetid, $subnet);
+
+ #delete on removal
+ if (!defined($gateway) && $old_gateway) {
+ eval {
+ PVE::Network::SDN::Subnets::del_ip($subnetid, $old_subnet, $old_gateway);
+ };
+ warn if $@;
+ }
+ if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
+ PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $gateway);
+ }
+
+ #delete old ip after update
+ if($gateway && $old_gateway && $gateway ne $old_gateway) {
+ eval {
+ PVE::Network::SDN::Subnets::del_ip($subnetid, $old_subnet, $old_gateway);
+ };
+ warn if $@;
+ }
+ }
}
sub on_delete_hook {
my ($class, $subnetid, $subnet_cfg, $vnet_cfg) = @_;
- #verify if vnets have subnet
- foreach my $vnetid (keys %{$vnet_cfg->{ids}}) {
- my $vnet = $vnet_cfg->{ids}->{$vnetid};
- my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
- foreach my $subnet (@subnets) {
- my $id = $subnet =~ s/\//-/r;
- raise_param_exc({ subnet => "$subnet is attached to vnet $vnetid"}) if $id eq $subnetid;
- }
- }
-
return;
}
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index 4e8353e..d20af9e 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -57,20 +57,18 @@ sub get_subnet {
}
sub find_ip_subnet {
- my ($ip, $subnetslist) = @_;
-
- my $subnets_cfg = PVE::Network::SDN::Subnets::config();
- my @subnets = PVE::Tools::split_list($subnetslist) if $subnetslist;
+ my ($ip, $subnets) = @_;
my $subnet = undef;
my $subnetid = undef;
- foreach my $s (@subnets) {
- my $subnet_matcher = subnet_matcher($s);
- next if !$subnet_matcher->($ip);
- $subnetid = $s =~ s/\//-/r;
- $subnet = $subnets_cfg->{ids}->{$subnetid};
- last;
+ foreach my $id (sort keys %{$subnets}) {
+ my $cidr = $id =~ s/-/\//r;
+ my $subnet_matcher = subnet_matcher($cidr);
+ next if !$subnet_matcher->($ip);
+ $subnet = $subnets->{$id};
+ $subnetid = $id;
+ last;
}
die "can't find any subnet for ip $ip" if !$subnet;
@@ -143,8 +141,11 @@ sub next_free_ip {
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
- $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
- ($ip, undef) = split(/\//, $cidr);
+ eval {
+ $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet);
+ ($ip, undef) = split(/\//, $cidr);
+ };
+ die $@ if $@;
}
eval {
@@ -167,6 +168,8 @@ sub next_free_ip {
sub add_ip {
my ($subnetid, $subnet, $ip, $hostname) = @_;
+ return if !$subnet;
+
my $ipamid = $subnet->{ipam};
my $dns = $subnet->{dns};
my $dnszone = $subnet->{dnszone};
@@ -182,7 +185,10 @@ sub add_ip {
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
- $plugin->add_ip($plugin_config, $subnetid, $ip);
+ eval {
+ $plugin->add_ip($plugin_config, $subnetid, $ip);
+ };
+ die $@ if $@;
}
eval {
@@ -204,6 +210,8 @@ sub add_ip {
sub del_ip {
my ($subnetid, $subnet, $ip, $hostname) = @_;
+ return if !$subnet;
+
my $ipamid = $subnet->{ipam};
my $dns = $subnet->{dns};
my $dnszone = $subnet->{dnszone};
diff --git a/PVE/Network/SDN/VnetPlugin.pm b/PVE/Network/SDN/VnetPlugin.pm
index 6b2bcc8..47fd4d4 100644
--- a/PVE/Network/SDN/VnetPlugin.pm
+++ b/PVE/Network/SDN/VnetPlugin.pm
@@ -68,11 +68,6 @@ sub properties {
description => "alias name of the vnet",
optional => 1,
},
- subnets => {
- type => 'string',
- description => "Subnets list",
- optional => 1,
- },
mac => {
type => 'string',
description => "Anycast router mac address",
@@ -86,16 +81,21 @@ sub options {
zone => { optional => 0},
tag => { optional => 1},
alias => { optional => 1 },
- subnets => { optional => 1 },
mac => { optional => 1 },
vlanaware => { optional => 1 },
};
}
sub on_delete_hook {
- my ($class, $sdnid, $vnet_cfg) = @_;
+ my ($class, $vnetid, $vnet_cfg) = @_;
- return;
+ #verify if subnets are associated
+ my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+ my @subnetlist = ();
+ foreach my $subnetid (sort keys %{$subnets}) {
+ push @subnetlist, $subnetid;
+ }
+ raise_param_exc({ vnet => "Vnet is attached to following subnets:". join(',', @subnetlist)}) if @subnetlist > 0;
}
sub on_update_hook {
@@ -111,13 +111,6 @@ sub on_update_hook {
}
}
}
-
- #verify subnet
- my @subnets = PVE::Tools::split_list($vnet_cfg->{ids}->{$vnetid}->{subnets}) if $vnet_cfg->{ids}->{$vnetid}->{subnets};
- foreach my $subnet (@subnets) {
- my $id = $subnet =~ s/\//-/r;
- raise_param_exc({ subnet => "$subnet not existing"}) if !$subnet_cfg->{ids}->{$id};
- }
}
1;
diff --git a/PVE/Network/SDN/Vnets.pm b/PVE/Network/SDN/Vnets.pm
index c9916b1..7cec418 100644
--- a/PVE/Network/SDN/Vnets.pm
+++ b/PVE/Network/SDN/Vnets.pm
@@ -54,22 +54,35 @@ sub get_vnet {
return $vnet;
}
+sub get_subnets {
+ my ($vnetid) = @_;
+
+ my $subnets = {};
+ my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+ foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
+ my $subnet = $subnets_cfg->{ids}->{$subnetid};
+ next if !$subnet->{vnet} || $subnet->{vnet} ne $vnetid;
+ $subnets->{$subnetid} = $subnet;
+ }
+ return $subnets;
+
+}
+
sub get_next_free_ip {
- my ($vnet, $hostname, $ipversion) = @_;
+ my ($vnetid, $hostname, $ipversion) = @_;
$ipversion = 4 if !$ipversion;
- my $subnets_cfg = PVE::Network::SDN::Subnets::config();
- my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
+ my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
my $ip = undef;
- my $subnet = undef;
my $subnetcount = 0;
- foreach my $s (@subnets) {
- my $subnetid = $s =~ s/\//-/r;
+
+ foreach my $subnetid (sort keys %{$subnets}) {
+ my $subnet = $subnets->{$subnetid};
my ($network, $mask) = split(/-/, $subnetid);
+
next if $ipversion != Net::IP::ip_get_version($network);
$subnetcount++;
- $subnet = $subnets_cfg->{ids}->{$subnetid};
- if ($subnet && $subnet->{ipam}) {
+ if ($subnet->{ipam}) {
eval {
$ip = PVE::Network::SDN::Subnets::next_free_ip($subnetid, $subnet, $hostname);
};
@@ -83,21 +96,23 @@ sub get_next_free_ip {
}
sub add_ip {
- my ($vnet, $cidr, $hostname) = @_;
+ my ($vnetid, $cidr, $hostname) = @_;
+
+ my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
my ($ip, $mask) = split(/\//, $cidr);
- my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
- return if !$subnet->{ipam};
+ my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
PVE::Network::SDN::Subnets::add_ip($subnetid, $subnet, $ip, $hostname);
}
sub del_ip {
- my ($vnet, $cidr, $hostname) = @_;
+ my ($vnetid, $cidr, $hostname) = @_;
+
+ my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
my ($ip, $mask) = split(/\//, $cidr);
- my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $vnet->{subnets});
- return if !$subnet->{ipam};
+ my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
PVE::Network::SDN::Subnets::del_ip($subnetid, $subnet, $ip, $hostname);
}
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index 17c9262..ff25f12 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -76,12 +76,12 @@ sub generate_sdn_config {
#vnet bridge
@iface_config = ();
- my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
my $address = {};
- foreach my $subnet (@subnets) {
- my $subnetid = $subnet =~ s/\//-/r;
- next if !defined($subnet_cfg->{ids}->{$subnetid});
- my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
+ my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+ foreach my $subnetid (sort keys %{$subnets}) {
+ my $subnet = $subnets->{$subnetid};
+ my $cidr = $subnetid =~ s/-/\//r;
+ my $gateway = $subnet->{gateway};
if ($gateway) {
push @iface_config, "address $gateway" if !defined($address->{$gateway});
$address->{$gateway} = 1;
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index a1733d5..a4299dd 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -35,19 +35,19 @@ sub generate_sdn_config {
# vnet bridge
my @iface_config = ();
- my @subnets = PVE::Tools::split_list($vnet->{subnets}) if $vnet->{subnets};
my $address = {};
- foreach my $subnet (@subnets) {
- my $subnetid = $subnet =~ s/\//-/r;
- next if !defined($subnet_cfg->{ids}->{$subnetid});
- my $gateway = $subnet_cfg->{ids}->{$subnetid}->{gateway};
- if ($gateway) {
+ my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid);
+ foreach my $subnetid (sort keys %{$subnets}) {
+ my $subnet = $subnets->{$subnetid};
+ my $cidr = $subnetid =~ s/-/\//r;
+ my $gateway = $subnet->{gateway};
+ if ($gateway) {
push @iface_config, "address $gateway" if !defined($address->{$gateway});
$address->{$gateway} = 1;
}
#add route for /32 pointtopoint
- my ($ip, $mask) = split(/\//, $subnet);
- push @iface_config, "up ip route add $subnet dev $vnetid" if $mask == 32;
+ my ($ip, $mask) = split(/\//, $cidr);
+ push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32;
}
push @iface_config, "hwaddress $mac" if $mac;
--
2.20.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 19/21] zones: evpn|simple: add snat iptables rules
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (17 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 18/21] add vnet to subnets && remove subnetlist from vnet Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 20/21] subnet: disable route option for now and add dns domain format Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 21/21] dns: fix reverse dns Alexandre Derumier
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
(use snat instead masquerade for performance)
---
PVE/Network/SDN/Zones/EvpnPlugin.pm | 18 ++++++++++++++++++
PVE/Network/SDN/Zones/SimplePlugin.pm | 12 ++++++++++++
2 files changed, 30 insertions(+)
diff --git a/PVE/Network/SDN/Zones/EvpnPlugin.pm b/PVE/Network/SDN/Zones/EvpnPlugin.pm
index ff25f12..b89f4b1 100644
--- a/PVE/Network/SDN/Zones/EvpnPlugin.pm
+++ b/PVE/Network/SDN/Zones/EvpnPlugin.pm
@@ -51,6 +51,7 @@ sub generate_sdn_config {
my $vrf_iface = "vrf_$zoneid";
my $vrfvxlan = $plugin_config->{'vrf-vxlan'};
+ my $local_node = PVE::INotify::nodename();
die "missing vxlan tag" if !$tag;
warn "vlan-aware vnet can't be enabled with evpn plugin" if $vnet->{vlanaware};
@@ -86,6 +87,23 @@ sub generate_sdn_config {
push @iface_config, "address $gateway" if !defined($address->{$gateway});
$address->{$gateway} = 1;
}
+ if ($subnet->{snat}) {
+ my $gatewaynodes = $controller->{'gateway-nodes'};
+ my $is_evpn_gateway = "";
+ foreach my $evpn_gatewaynode (PVE::Tools::split_list($gatewaynodes)) {
+ $is_evpn_gateway = 1 if $evpn_gatewaynode eq $local_node;
+ }
+ #find outgoing interface
+ my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip('8.8.8.8');
+ if ($outip && $outiface && $is_evpn_gateway) {
+ #use snat, faster than masquerade
+ push @iface_config, "post-up iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+ push @iface_config, "post-down iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+ #add conntrack zone once on outgoing interface
+ push @iface_config, "post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
+ push @iface_config, "post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
+ }
+ }
}
push @iface_config, "hwaddress $mac" if $mac;
diff --git a/PVE/Network/SDN/Zones/SimplePlugin.pm b/PVE/Network/SDN/Zones/SimplePlugin.pm
index a4299dd..c58ae87 100644
--- a/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -48,6 +48,18 @@ sub generate_sdn_config {
#add route for /32 pointtopoint
my ($ip, $mask) = split(/\//, $cidr);
push @iface_config, "up ip route add $cidr dev $vnetid" if $mask == 32;
+ if ($subnet->{snat}) {
+ #find outgoing interface
+ my ($outip, $outiface) = PVE::Network::SDN::Zones::Plugin::get_local_route_ip('8.8.8.8');
+ if ($outip && $outiface) {
+ #use snat, faster than masquerade
+ push @iface_config, "post-up iptables -t nat -A POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+ push @iface_config, "post-down iptables -t nat -D POSTROUTING -s '$cidr' -o $outiface -j SNAT --to-source $outip";
+ #add conntrack zone once on outgoing interface
+ push @iface_config, "post-up iptables -t raw -I PREROUTING -i fwbr+ -j CT --zone 1";
+ push @iface_config, "post-down iptables -t raw -D PREROUTING -i fwbr+ -j CT --zone 1";
+ }
+ }
}
push @iface_config, "hwaddress $mac" if $mac;
--
2.20.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 20/21] subnet: disable route option for now and add dns domain format
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (18 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 19/21] zones: evpn|simple: add snat iptables rules Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 21/21] dns: fix reverse dns Alexandre Derumier
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
PVE/Network/SDN/SubnetPlugin.pm | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 84303d1..6237867 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -66,11 +66,11 @@ sub properties {
type => 'boolean',
description => "enable masquerade for this subnet if pve-firewall",
},
- #cloudinit, dhcp options
- routes => {
- type => 'string',
- description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
- },
+# #cloudinit, dhcp options
+# routes => {
+# type => 'string',
+# description => "static routes [network=<network>:gateway=<ip>,network=<network>:gateway=<ip>,... ]",
+# },
dns => {
type => 'string',
description => "dns api server",
@@ -80,15 +80,15 @@ sub properties {
description => "reverse dns api server",
},
dnszone => {
- type => 'string',
+ type => 'string', format => 'dns-name',
description => "dns domain zone ex: mydomain.com",
},
reversednszone => {
- type => 'string',
+ type => 'string', format => 'dns-name',
description => "reverse dns zone ex: 0.168.192.in-addr.arpa",
},
dnszoneprefix => {
- type => 'string',
+ type => 'string', format => 'dns-name',
description => "dns domain zone prefix ex: 'adm' -> <hostname>.adm.mydomain.com",
},
ipam => {
@@ -102,7 +102,7 @@ sub options {
return {
vnet => { optional => 1 },
gateway => { optional => 1 },
- routes => { optional => 1 },
+# routes => { optional => 1 },
snat => { optional => 1 },
dns => { optional => 1 },
reversedns => { optional => 1 },
--
2.20.1
^ permalink raw reply [flat|nested] 22+ messages in thread
* [pve-devel] [PATCH v7 pve-network 21/21] dns: fix reverse dns
2020-08-28 12:36 [pve-devel] [PATCH v7 pve-network 00/21] sdn : add subnets management Alexandre Derumier
` (19 preceding siblings ...)
2020-08-28 12:36 ` [pve-devel] [PATCH v7 pve-network 20/21] subnet: disable route option for now and add dns domain format Alexandre Derumier
@ 2020-08-28 12:36 ` Alexandre Derumier
20 siblings, 0 replies; 22+ messages in thread
From: Alexandre Derumier @ 2020-08-28 12:36 UTC (permalink / raw)
To: pve-devel
---
PVE/Network/SDN/Dns/PowerdnsPlugin.pm | 12 +++----
PVE/Network/SDN/SubnetPlugin.pm | 1 +
PVE/Network/SDN/Subnets.pm | 50 ++++++++++++++++++---------
3 files changed, 40 insertions(+), 23 deletions(-)
diff --git a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
index 8c5dd90..f02c2f1 100644
--- a/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
+++ b/PVE/Network/SDN/Dns/PowerdnsPlugin.pm
@@ -79,12 +79,12 @@ sub add_ptr_record {
my $key = $plugin_config->{key};
my $ttl = $plugin_config->{ttl} ? $plugin_config->{ttl} : 14400;
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
+ $hostname .= ".";
- my $reverseip = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
- my $fqdn = $hostname.".".$zone.".";
+ my $reverseip = join(".", reverse(split(/\./, $ip))).".in-addr.arpa.";
my $type = "PTR";
- my $record = { content => $fqdn,
+ my $record = { content => $hostname,
disabled => JSON::false,
name => $reverseip,
type => $type,
@@ -109,13 +109,13 @@ sub add_ptr_record {
}
sub del_a_record {
- my ($class, $plugin_config, $zone, $hostname) = @_;
+ my ($class, $plugin_config, $zone, $hostname, $ip) = @_;
my $url = $plugin_config->{url};
my $key = $plugin_config->{key};
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
my $fqdn = $hostname.".".$zone.".";
- my $type = "PTR";
+ my $type = Net::IP::ip_is_ipv6($ip) ? "AAAA" : "A";
my $rrset = { name => $fqdn,
type => $type,
@@ -140,7 +140,7 @@ sub del_ptr_record {
my $key = $plugin_config->{key};
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'X-API-Key' => $key];
- my $reverseip = join(".", reverse(split(/\./, $ip)))."in-addr.arpa.";
+ my $reverseip = join(".", reverse(split(/\./, $ip))).".in-addr.arpa.";
my $type = "PTR";
my $rrset = { name => $reverseip,
diff --git a/PVE/Network/SDN/SubnetPlugin.pm b/PVE/Network/SDN/SubnetPlugin.pm
index 6237867..b236c3f 100644
--- a/PVE/Network/SDN/SubnetPlugin.pm
+++ b/PVE/Network/SDN/SubnetPlugin.pm
@@ -142,6 +142,7 @@ sub on_update_hook {
raise_param_exc({ dnszone => "missing dns zone"}) if $dns && !$dnszone;
raise_param_exc({ reversedns => "missing dns provider"}) if $reversednszone && !$reversedns;
raise_param_exc({ reversednszone => "missing dns zone"}) if $reversedns && !$reversednszone;
+ raise_param_exc({ reversedns => "missing forward dns zone"}) if $reversednszone && !$dnszone;
if ($ipam) {
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
diff --git a/PVE/Network/SDN/Subnets.pm b/PVE/Network/SDN/Subnets.pm
index d20af9e..626b71d 100644
--- a/PVE/Network/SDN/Subnets.pm
+++ b/PVE/Network/SDN/Subnets.pm
@@ -87,24 +87,33 @@ my $verify_dns_zone = sub {
};
my $add_dns_record = sub {
- my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
-
- return if !$zone || !$dns || !$hostname || !$ip;
+ my ($zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
+ return if !$zone || !$dns || !$hostname || !$ip;
$hostname .= ".$dnszoneprefix" if $dnszoneprefix;
my $dns_cfg = PVE::Network::SDN::Dns::config();
my $plugin_config = $dns_cfg->{ids}->{$dns};
my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
- if($reverse) {
- $plugin->add_ptr_record($plugin_config, $zone, $hostname, $ip);
- } else {
- $plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
- }
+ $plugin->add_a_record($plugin_config, $zone, $hostname, $ip);
+
+};
+
+my $add_dns_ptr_record = sub {
+ my ($reversezone, $zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
+
+ return if !$zone || !$reversezone || !$dns || !$hostname || !$ip;
+
+ $hostname .= ".$dnszoneprefix" if $dnszoneprefix;
+ $hostname .= ".$zone";
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
+ my $plugin_config = $dns_cfg->{ids}->{$dns};
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+ $plugin->add_ptr_record($plugin_config, $reversezone, $hostname, $ip);
};
my $del_dns_record = sub {
- my ($zone, $dns, $hostname, $dnszoneprefix, $ip, $reverse) = @_;
+ my ($zone, $dns, $hostname, $dnszoneprefix, $ip) = @_;
return if !$zone || !$dns || !$hostname || !$ip;
@@ -113,11 +122,18 @@ my $del_dns_record = sub {
my $dns_cfg = PVE::Network::SDN::Dns::config();
my $plugin_config = $dns_cfg->{ids}->{$dns};
my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
- if($reverse) {
- $plugin->del_ptr_record($plugin_config, $zone, $ip);
- } else {
- $plugin->del_a_record($plugin_config, $zone, $hostname);
- }
+ $plugin->del_a_record($plugin_config, $zone, $hostname, $ip);
+};
+
+my $del_dns_ptr_record = sub {
+ my ($reversezone, $dns, $ip) = @_;
+
+ return if !$reversezone || !$dns || !$ip;
+
+ my $dns_cfg = PVE::Network::SDN::Dns::config();
+ my $plugin_config = $dns_cfg->{ids}->{$dns};
+ my $plugin = PVE::Network::SDN::Dns::Plugin->lookup($plugin_config->{type});
+ $plugin->del_ptr_record($plugin_config, $reversezone, $ip);
};
sub next_free_ip {
@@ -152,7 +168,7 @@ sub next_free_ip {
#add dns
&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
#add reverse dns
- &$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+ &$add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $dnszoneprefix, $ip);
};
if ($@) {
#rollback
@@ -195,7 +211,7 @@ sub add_ip {
#add dns
&$add_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
#add reverse dns
- &$add_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+ &$add_dns_ptr_record($reversednszone, $dnszone, $reversedns, $hostname, $dnszoneprefix, $ip);
};
if ($@) {
#rollback
@@ -231,7 +247,7 @@ sub del_ip {
eval {
&$del_dns_record($dnszone, $dns, $hostname, $dnszoneprefix, $ip);
- &$del_dns_record($reversednszone, $reversedns, $hostname, $dnszoneprefix, $ip, 1);
+ &$del_dns_ptr_record($reversednszone, $reversedns, $ip);
};
if ($@) {
warn $@;
--
2.20.1
^ permalink raw reply [flat|nested] 22+ messages in thread