* [pve-devel] [WIP v3 pve-cluster 01/22] add priv/macs.db
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
@ 2023-11-14 18:05 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 02/22] sdn: preparations for DHCP plugin Stefan Hanreich
` (20 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:05 UTC (permalink / raw)
To: pve-devel
From: Alexandre Derumier <aderumier@odiso.com>
use to cache mac-ip list association.
can be use by external ipam, firewall,etc for fast lookup
Signed-off-by: Alexandre Derumier <aderumier@odiso.com>
---
src/PVE/Cluster.pm | 1 +
src/pmxcfs/status.c | 1 +
2 files changed, 2 insertions(+)
diff --git a/src/PVE/Cluster.pm b/src/PVE/Cluster.pm
index cfa2583..80c4bc0 100644
--- a/src/PVE/Cluster.pm
+++ b/src/PVE/Cluster.pm
@@ -62,6 +62,7 @@ my $observed = {
'priv/token.cfg' => 1,
'priv/acme/plugins.cfg' => 1,
'priv/ipam.db' => 1,
+ 'priv/macs.db' => 1,
'/qemu-server/' => 1,
'/openvz/' => 1,
'/lxc/' => 1,
diff --git a/src/pmxcfs/status.c b/src/pmxcfs/status.c
index c8094ac..078602e 100644
--- a/src/pmxcfs/status.c
+++ b/src/pmxcfs/status.c
@@ -89,6 +89,7 @@ static memdb_change_t memdb_change_array[] = {
{ .path = "priv/tfa.cfg" },
{ .path = "priv/token.cfg" },
{ .path = "priv/ipam.db" },
+ { .path = "priv/macs.db" },
{ .path = "datacenter.cfg" },
{ .path = "vzdump.cron" },
{ .path = "vzdump.conf" },
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 02/22] sdn: preparations for DHCP plugin
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
2023-11-14 18:05 ` [pve-devel] [WIP v3 pve-cluster 01/22] add priv/macs.db Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 03/22] subnet: add dhcp options Stefan Hanreich
` (19 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/Network/SDN/Subnets.pm | 25 +++++++++++++------------
src/PVE/Network/SDN/Vnets.pm | 27 +++++++++++++--------------
src/PVE/Network/SDN/Zones.pm | 34 +++++++++++++++++++++++++---------
3 files changed, 51 insertions(+), 35 deletions(-)
diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
index 6bb42e5..f654d3a 100644
--- a/src/PVE/Network/SDN/Subnets.pm
+++ b/src/PVE/Network/SDN/Subnets.pm
@@ -23,7 +23,9 @@ sub sdn_subnets_config {
my $scfg = $cfg->{ids}->{$id};
die "sdn subnet '$id' does not exist\n" if (!$noerr && !$scfg);
- if($scfg) {
+ if ($scfg) {
+ $scfg->{id} = $id;
+
my ($zone, $network, $mask) = split(/-/, $id);
$scfg->{cidr} = "$network/$mask";
$scfg->{zone} = $zone;
@@ -35,7 +37,14 @@ sub sdn_subnets_config {
}
sub config {
- my $config = cfs_read_file("sdn/subnets.cfg");
+ my ($running) = @_;
+
+ if ($running) {
+ my $cfg = PVE::Network::SDN::running_config();
+ return $cfg->{subnets};
+ }
+
+ return cfs_read_file("sdn/subnets.cfg");
}
sub write_config {
@@ -61,16 +70,8 @@ sub complete_sdn_subnet {
sub get_subnet {
my ($subnetid, $running) = @_;
- my $cfg = {};
- if($running) {
- my $cfg = PVE::Network::SDN::running_config();
- $cfg = $cfg->{subnets};
- } else {
- $cfg = PVE::Network::SDN::Subnets::config();
- }
-
- my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
- return $subnet;
+ my $cfg = PVE::Network::SDN::Subnets::config($running);
+ return PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $subnetid, 1);
}
sub find_ip_subnet {
diff --git a/src/PVE/Network/SDN/Vnets.pm b/src/PVE/Network/SDN/Vnets.pm
index 1106c9f..39bdda0 100644
--- a/src/PVE/Network/SDN/Vnets.pm
+++ b/src/PVE/Network/SDN/Vnets.pm
@@ -26,6 +26,13 @@ sub sdn_vnets_config {
}
sub config {
+ my ($running) = @_;
+
+ if ($running) {
+ my $cfg = PVE::Network::SDN::running_config();
+ return $cfg->{vnets};
+ }
+
return cfs_read_file("sdn/vnets.cfg");
}
@@ -54,31 +61,23 @@ sub get_vnet {
return if !$vnetid;
- my $scfg = {};
- if($running) {
- my $cfg = PVE::Network::SDN::running_config();
- $scfg = $cfg->{vnets};
- } else {
- $scfg = PVE::Network::SDN::Vnets::config();
- }
-
- my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($scfg, $vnetid, 1);
-
- return $vnet;
+ my $cfg = PVE::Network::SDN::Vnets::config($running);
+ return PVE::Network::SDN::Vnets::sdn_vnets_config($cfg, $vnetid, 1);
}
sub get_subnets {
- my ($vnetid) = @_;
+ my ($vnetid, $running) = @_;
my $subnets = undef;
- my $subnets_cfg = PVE::Network::SDN::Subnets::config();
+ my $subnets_cfg = PVE::Network::SDN::Subnets::config($running);
+
foreach my $subnetid (sort keys %{$subnets_cfg->{ids}}) {
my $subnet = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets_cfg, $subnetid);
next if !$subnet->{vnet} || ($vnetid && $subnet->{vnet} ne $vnetid);
$subnets->{$subnetid} = $subnet;
}
- return $subnets;
+ return $subnets;
}
sub get_subnet_from_vnet_cidr {
diff --git a/src/PVE/Network/SDN/Zones.pm b/src/PVE/Network/SDN/Zones.pm
index 4ad4e4d..5bd3536 100644
--- a/src/PVE/Network/SDN/Zones.pm
+++ b/src/PVE/Network/SDN/Zones.pm
@@ -40,8 +40,14 @@ sub sdn_zones_config {
}
sub config {
- my $config = cfs_read_file("sdn/zones.cfg");
- return $config;
+ my ($running) = @_;
+
+ if ($running) {
+ my $cfg = PVE::Network::SDN::running_config();
+ return $cfg->{zones};
+ }
+
+ return cfs_read_file("sdn/zones.cfg");
}
sub get_plugin_config {
@@ -74,19 +80,29 @@ sub complete_sdn_zone {
sub get_zone {
my ($zoneid, $running) = @_;
- my $cfg = {};
- if($running) {
- my $cfg = PVE::Network::SDN::running_config();
- $cfg = $cfg->{vnets};
- } else {
- $cfg = PVE::Network::SDN::Zones::config();
- }
+ my $cfg = PVE::Network::SDN::Zones::config($running);
my $zone = PVE::Network::SDN::Zones::sdn_zones_config($cfg, $zoneid, 1);
return $zone;
}
+sub get_vnets {
+ my ($zoneid, $running) = @_;
+
+ return if !$zoneid;
+
+ my $vnets_config = PVE::Network::SDN::Vnets::config($running);
+ my $vnets = undef;
+
+ for my $vnetid (keys %{$vnets_config->{ids}}) {
+ my $vnet = PVE::Network::SDN::Vnets::sdn_vnets_config($vnets_config, $vnetid);
+ next if !$vnet->{zone} || $vnet->{zone} ne $zoneid;
+ $vnets->{$vnetid} = $vnet;
+ }
+
+ return $vnets;
+}
sub generate_etc_network_config {
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 03/22] subnet: add dhcp options
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
2023-11-14 18:05 ` [pve-devel] [WIP v3 pve-cluster 01/22] add priv/macs.db Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 02/22] sdn: preparations for DHCP plugin Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 04/22] sdn: zone: " Stefan Hanreich
` (18 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/Network/SDN/SubnetPlugin.pm | 29 +++++++++++++++++++++++++++++
src/PVE/Network/SDN/Subnets.pm | 23 +++++++++++++++++++++++
2 files changed, 52 insertions(+)
diff --git a/src/PVE/Network/SDN/SubnetPlugin.pm b/src/PVE/Network/SDN/SubnetPlugin.pm
index 15b370f..a4adae8 100644
--- a/src/PVE/Network/SDN/SubnetPlugin.pm
+++ b/src/PVE/Network/SDN/SubnetPlugin.pm
@@ -61,6 +61,19 @@ sub private {
return $defaultData;
}
+my $dhcp_range_fmt = {
+ 'start-address' => {
+ type => 'ip',
+ description => 'Start address for the DHCP IP range',
+ },
+ 'end-address' => {
+ type => 'ip',
+ description => 'End address for the DHCP IP range',
+ },
+};
+
+PVE::JSONSchema::register_format('pve-sdn-dhcp-range', $dhcp_range_fmt);
+
sub properties {
return {
vnet => {
@@ -84,6 +97,20 @@ sub properties {
type => 'string', format => 'dns-name',
description => "dns domain zone prefix ex: 'adm' -> <hostname>.adm.mydomain.com",
},
+ 'dhcp-range' => {
+ type => 'array',
+ description => 'A list of DHCP ranges for this subnet',
+ optional => 1,
+ items => {
+ type => 'string',
+ format => 'pve-sdn-dhcp-range',
+ }
+ },
+ 'dhcp-dns-server' => {
+ type => 'ip',
+ description => 'IP address for the DNS server',
+ optional => 1,
+ },
};
}
@@ -94,6 +121,8 @@ sub options {
# routes => { optional => 1 },
snat => { optional => 1 },
dnszoneprefix => { optional => 1 },
+ 'dhcp-range' => { optional => 1 },
+ 'dhcp-dns-server' => { optional => 1 },
};
}
diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
index f654d3a..6e74de1 100644
--- a/src/PVE/Network/SDN/Subnets.pm
+++ b/src/PVE/Network/SDN/Subnets.pm
@@ -8,6 +8,7 @@ use Net::IP;
use NetAddr::IP qw(:lower);
use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
+use PVE::JSONSchema qw(parse_property_string);
use PVE::Network::SDN::Dns;
use PVE::Network::SDN::Ipams;
@@ -36,6 +37,28 @@ sub sdn_subnets_config {
return $scfg;
}
+sub get_dhcp_ranges {
+ my ($subnet_config) = @_;
+
+ my @dhcp_ranges = ();
+
+ if ($subnet_config->{'dhcp-range'}) {
+ foreach my $element (@{$subnet_config->{'dhcp-range'}}) {
+ my $dhcp_range = eval { parse_property_string('pve-sdn-dhcp-range', $element) };
+
+ if ($@ || !$dhcp_range) {
+ warn "Unable to parse dhcp-range string: $element\n";
+ warn "$@\n" if $@;
+ next;
+ }
+
+ push @dhcp_ranges, $dhcp_range;
+ }
+ }
+
+ return \@dhcp_ranges;
+}
+
sub config {
my ($running) = @_;
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 04/22] sdn: zone: add dhcp options
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (2 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 03/22] subnet: add dhcp options Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 05/22] sdn: subnet: vnet: refactor IPAM related methods Stefan Hanreich
` (17 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/Network/SDN/Zones/SimplePlugin.pm | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/PVE/Network/SDN/Zones/SimplePlugin.pm b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
index 4922903..f30278c 100644
--- a/src/PVE/Network/SDN/Zones/SimplePlugin.pm
+++ b/src/PVE/Network/SDN/Zones/SimplePlugin.pm
@@ -26,7 +26,11 @@ sub properties {
dnszone => {
type => 'string', format => 'dns-name',
description => "dns domain zone ex: mydomain.com",
- }
+ },
+ dhcp => {
+ type => 'pve-configid',
+ description => 'ID of the DHCP server responsible for managing this range',
+ },
};
}
@@ -38,6 +42,7 @@ sub options {
reversedns => { optional => 1 },
dnszone => { optional => 1 },
ipam => { optional => 1 },
+ dhcp => { optional => 1 },
};
}
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 05/22] sdn: subnet: vnet: refactor IPAM related methods
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (3 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 04/22] sdn: zone: " Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 06/22] ipam: plugins: preparations for DHCP Stefan Hanreich
` (16 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/Network/SDN/SubnetPlugin.pm | 3 +-
src/PVE/Network/SDN/Subnets.pm | 50 ++++++++++-----
src/PVE/Network/SDN/Vnets.pm | 95 +++++++++++++++++------------
3 files changed, 92 insertions(+), 56 deletions(-)
diff --git a/src/PVE/Network/SDN/SubnetPlugin.pm b/src/PVE/Network/SDN/SubnetPlugin.pm
index a4adae8..88933f5 100644
--- a/src/PVE/Network/SDN/SubnetPlugin.pm
+++ b/src/PVE/Network/SDN/SubnetPlugin.pm
@@ -172,8 +172,7 @@ sub on_update_hook {
}
if(!$old_gateway || $gateway && $gateway ne $old_gateway) {
my $hostname = "$vnetid-gw";
- my $description = "gateway";
- PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway, $hostname, $mac, $description, 1);
+ PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $gateway, $hostname, $mac, undef, 1);
}
#delete old gateway after update
diff --git a/src/PVE/Network/SDN/Subnets.pm b/src/PVE/Network/SDN/Subnets.pm
index 6e74de1..b05b3d9 100644
--- a/src/PVE/Network/SDN/Subnets.pm
+++ b/src/PVE/Network/SDN/Subnets.pm
@@ -98,14 +98,12 @@ sub get_subnet {
}
sub find_ip_subnet {
- my ($ip, $mask, $subnets) = @_;
+ my ($ip, $subnets) = @_;
my $subnet = undef;
my $subnetid = undef;
foreach my $id (sort keys %{$subnets}) {
-
- next if $mask ne $subnets->{$id}->{mask};
my $cidr = $subnets->{$id}->{cidr};
my $subnet_matcher = subnet_matcher($cidr);
next if !$subnet_matcher->($ip);
@@ -207,12 +205,11 @@ sub del_subnet {
$plugin->del_subnet($plugin_config, $subnetid, $subnet);
}
-sub next_free_ip {
- my ($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns) = @_;
+sub add_next_free_ip {
+ my ($zone, $subnetid, $subnet, $hostname, $mac, $vmid, $skipdns, $dhcprange) = @_;
my $cidr = undef;
my $ip = undef;
- $description = '' if !$description;
my $ipamid = $zone->{ipam};
my $dns = $zone->{dns};
@@ -230,10 +227,28 @@ sub next_free_ip {
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
eval {
- $cidr = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $description);
- ($ip, undef) = split(/\//, $cidr);
+ if ($dhcprange) {
+ my $data = {
+ mac => $mac,
+ hostname => $hostname,
+ vmid => $vmid,
+ };
+
+ my $dhcp_ranges = PVE::Network::SDN::Subnets::get_dhcp_ranges($subnet);
+
+ foreach my $range (@$dhcp_ranges) {
+ $ip = $plugin->add_range_next_freeip($plugin_config, $subnet, $range, $data);
+ next if !$ip;
+ }
+ } else {
+ $ip = $plugin->add_next_freeip($plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid);
+ }
};
+
die $@ if $@;
+
+ eval { PVE::Network::SDN::Ipams::add_cache_mac_ip($mac, $ip); };
+ warn $@ if $@;
}
eval {
@@ -250,15 +265,15 @@ sub next_free_ip {
#rollback
my $err = $@;
eval {
- PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname)
+ PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac)
};
die $err;
}
- return $cidr;
+ return $ip;
}
sub add_ip {
- my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $skipdns) = @_;
+ my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $skipdns) = @_;
return if !$subnet || !$ip;
@@ -287,7 +302,7 @@ sub add_ip {
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
eval {
- $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway);
+ $plugin->add_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway);
};
die $@ if $@;
}
@@ -304,14 +319,14 @@ sub add_ip {
#rollback
my $err = $@;
eval {
- PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname)
+ PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac)
};
die $err;
}
}
sub update_ip {
- my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns) = @_;
+ my ($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $vmid, $skipdns) = @_;
return if !$subnet || !$ip;
@@ -338,7 +353,7 @@ sub update_ip {
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
eval {
- $plugin->update_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description);
+ $plugin->update_ip($plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid);
};
die $@ if $@;
}
@@ -358,7 +373,7 @@ sub update_ip {
}
sub del_ip {
- my ($zone, $subnetid, $subnet, $ip, $hostname, $skipdns) = @_;
+ my ($zone, $subnetid, $subnet, $ip, $hostname, $mac, $skipdns) = @_;
return if !$subnet || !$ip;
@@ -383,6 +398,9 @@ sub del_ip {
my $plugin_config = $ipam_cfg->{ids}->{$ipamid};
my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
$plugin->del_ip($plugin_config, $subnetid, $subnet, $ip);
+
+ eval { PVE::Network::SDN::Ipams::del_cache_mac_ip($mac, $ip); };
+ warn $@ if $@;
}
eval {
diff --git a/src/PVE/Network/SDN/Vnets.pm b/src/PVE/Network/SDN/Vnets.pm
index 39bdda0..9ec16eb 100644
--- a/src/PVE/Network/SDN/Vnets.pm
+++ b/src/PVE/Network/SDN/Vnets.pm
@@ -80,81 +80,100 @@ sub get_subnets {
return $subnets;
}
-sub get_subnet_from_vnet_cidr {
- my ($vnetid, $cidr) = @_;
+sub get_subnet_from_vnet_ip {
+ my ($vnetid, $ip) = @_;
my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
my $zoneid = $vnet->{zone};
my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
- my ($ip, $mask) = split(/\//, $cidr);
- die "ip address is not in cidr format" if !$mask;
-
- my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $mask, $subnets);
+ my ($subnetid, $subnet) = PVE::Network::SDN::Subnets::find_ip_subnet($ip, $subnets);
return ($zone, $subnetid, $subnet, $ip);
}
-sub get_next_free_cidr {
- my ($vnetid, $hostname, $mac, $description, $ipversion, $skipdns) = @_;
+sub add_next_free_cidr {
+ my ($vnetid, $hostname, $mac, $vmid, $skipdns, $dhcprange) = @_;
my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+ return if !$vnet;
+
my $zoneid = $vnet->{zone};
my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
return if !$zone->{ipam};
- $ipversion = 4 if !$ipversion;
my $subnets = PVE::Network::SDN::Vnets::get_subnets($vnetid, 1);
- my $ip = undef;
- my $subnetcount = 0;
-
- foreach my $subnetid (sort keys %{$subnets}) {
- my $subnet = $subnets->{$subnetid};
- my $network = $subnet->{network};
-
- next if $ipversion != Net::IP::ip_get_version($network);
- $subnetcount++;
- eval {
- $ip = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description, $skipdns);
- };
- warn $@ if $@;
- last if $ip;
+ my @ipversions = qw/ 4 6 /;
+ for my $ipversion (@ipversions) {
+ my $ip = undef;
+ my $subnetcount = 0;
+ foreach my $subnetid (sort keys %{$subnets}) {
+ my $subnet = $subnets->{$subnetid};
+ my $network = $subnet->{network};
+
+ next if Net::IP::ip_get_version($network) != $ipversion;
+ $subnetcount++;
+
+ eval {
+ $ip = PVE::Network::SDN::Subnets::add_next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $vmid, $skipdns, $dhcprange);
+ };
+ die $@ if $@;
+ last if $ip;
+ }
+ die "can't find any free ip" if !$ip && $subnetcount > 0;
}
- die "can't find any free ip" if !$ip && $subnetcount > 0;
-
- return $ip;
}
-sub add_cidr {
- my ($vnetid, $cidr, $hostname, $mac, $description, $skipdns) = @_;
+sub add_ip {
+ my ($vnetid, $ip, $hostname, $mac, $vmid, $skipdns) = @_;
return if !$vnetid;
- my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr);
- PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $description, undef, $skipdns);
+ my ($zone, $subnetid, $subnet) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip($vnetid, $ip);
+ PVE::Network::SDN::Subnets::add_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, undef, $skipdns);
}
-sub update_cidr {
- my ($vnetid, $cidr, $hostname, $oldhostname, $mac, $description, $skipdns) = @_;
+sub update_ip {
+ my ($vnetid, $ip, $hostname, $oldhostname, $mac, $vmid, $skipdns) = @_;
return if !$vnetid;
- my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr);
- PVE::Network::SDN::Subnets::update_ip($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $description, $skipdns);
+ my ($zone, $subnetid, $subnet) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip($vnetid, $ip);
+ PVE::Network::SDN::Subnets::update_ip($zone, $subnetid, $subnet, $ip, $hostname, $oldhostname, $mac, $vmid, $skipdns);
}
-sub del_cidr {
- my ($vnetid, $cidr, $hostname, $skipdns) = @_;
+sub del_ip {
+ my ($vnetid, $ip, $hostname, $mac, $skipdns) = @_;
return if !$vnetid;
- my ($zone, $subnetid, $subnet, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_cidr($vnetid, $cidr);
- PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $skipdns);
+ my ($zone, $subnetid, $subnet) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip($vnetid, $ip);
+ PVE::Network::SDN::Subnets::del_ip($zone, $subnetid, $subnet, $ip, $hostname, $mac, $skipdns);
}
+sub get_ips_from_mac {
+ my ($vnetid, $mac) = @_;
+
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+ return if !$vnet;
+ my $zoneid = $vnet->{zone};
+ my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
+
+ return if !$zone->{ipam} || !$zone->{dhcp};
+
+ return PVE::Network::SDN::Ipams::get_ips_from_mac($mac, $zoneid, $zone);
+}
+
+sub del_ips_from_mac {
+ my ($vnetid, $mac, $hostname) = @_;
+
+ my ($ip4, $ip6) = PVE::Network::SDN::Vnets::get_ips_from_mac($vnetid, $mac);
+ PVE::Network::SDN::Vnets::del_ip($vnetid, $ip4, $hostname, $mac) if $ip4;
+ PVE::Network::SDN::Vnets::del_ip($vnetid, $ip6, $hostname, $mac) if $ip6;
+}
1;
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 06/22] ipam: plugins: preparations for DHCP
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (4 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 05/22] sdn: subnet: vnet: refactor IPAM related methods Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 07/22] dhcp: add abstract class for DHCP plugins Stefan Hanreich
` (15 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/Network/SDN/Ipams.pm | 80 +++++++++++++++++++-
src/PVE/Network/SDN/Ipams/NetboxPlugin.pm | 86 ++++++++++++++++++++--
src/PVE/Network/SDN/Ipams/PVEPlugin.pm | 85 +++++++++++++++++++--
src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm | 29 ++++++++
src/PVE/Network/SDN/Ipams/Plugin.pm | 19 ++++-
5 files changed, 281 insertions(+), 18 deletions(-)
diff --git a/src/PVE/Network/SDN/Ipams.pm b/src/PVE/Network/SDN/Ipams.pm
index e8a4b0b..926df90 100644
--- a/src/PVE/Network/SDN/Ipams.pm
+++ b/src/PVE/Network/SDN/Ipams.pm
@@ -4,9 +4,10 @@ use strict;
use warnings;
use JSON;
+use Net::IP;
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::Cluster qw(cfs_register_file cfs_read_file cfs_write_file cfs_lock_file);
use PVE::Network;
use PVE::Network::SDN::Ipams::PVEPlugin;
@@ -19,6 +20,64 @@ PVE::Network::SDN::Ipams::NetboxPlugin->register();
PVE::Network::SDN::Ipams::PhpIpamPlugin->register();
PVE::Network::SDN::Ipams::Plugin->init();
+my $macdb_filename = 'priv/macs.db';
+
+cfs_register_file($macdb_filename, \&json_reader, \&json_writer);
+
+sub json_reader {
+ my ($filename, $data) = @_;
+
+ return defined($data) && length($data) > 0 ? decode_json($data) : {};
+}
+
+sub json_writer {
+ my ($filename, $data) = @_;
+
+ return encode_json($data);
+}
+
+sub read_macdb {
+ my () = @_;
+
+ return cfs_read_file($macdb_filename);
+}
+
+sub write_macdb {
+ my ($data) = @_;
+
+ cfs_write_file($macdb_filename, $data);
+}
+
+sub add_cache_mac_ip {
+ my ($mac, $ip) = @_;
+
+ cfs_lock_file($macdb_filename, undef, sub {
+ my $db = read_macdb();
+ if (Net::IP::ip_is_ipv4($ip)) {
+ $db->{macs}->{$mac}->{ip4} = $ip;
+ } else {
+ $db->{macs}->{$mac}->{ip6} = $ip;
+ }
+ write_macdb($db);
+ });
+ warn "$@" if $@;
+}
+
+sub del_cache_mac_ip {
+ my ($mac, $ip) = @_;
+
+ cfs_lock_file($macdb_filename, undef, sub {
+ my $db = read_macdb();
+ if (Net::IP::ip_is_ipv4($ip)) {
+ delete $db->{macs}->{$mac}->{ip4};
+ } else {
+ delete $db->{macs}->{$mac}->{ip6};
+ }
+ delete $db->{macs}->{$mac} if !defined($db->{macs}->{$mac}->{ip4}) && !defined($db->{macs}->{$mac}->{ip6});
+ write_macdb($db);
+ });
+ warn "$@" if $@;
+}
sub sdn_ipams_config {
my ($cfg, $id, $noerr) = @_;
@@ -39,8 +98,8 @@ sub config {
}
sub get_plugin_config {
- my ($vnet) = @_;
- my $ipamid = $vnet->{ipam};
+ my ($zone) = @_;
+ my $ipamid = $zone->{ipam};
my $ipam_cfg = PVE::Network::SDN::Ipams::config();
return $ipam_cfg->{ids}->{$ipamid};
}
@@ -65,5 +124,20 @@ sub complete_sdn_vnet {
return $cmdname eq 'add' ? [] : [ PVE::Network::SDN::Vnets::sdn_ipams_ids($cfg) ];
}
+sub get_ips_from_mac {
+ my ($mac, $zoneid, $zone) = @_;
+
+ my $macdb = read_macdb();
+ return ($macdb->{macs}->{$mac}->{ip4}, $macdb->{macs}->{$mac}->{ip6}) if $macdb->{macs}->{$mac};
+
+ my $plugin_config = get_plugin_config($zone);
+ my $plugin = PVE::Network::SDN::Ipams::Plugin->lookup($plugin_config->{type});
+ ($macdb->{macs}->{$mac}->{ip4}, $macdb->{macs}->{$mac}->{ip6}) = $plugin->get_ips_from_mac($plugin_config, $mac, $zoneid);
+
+ write_macdb($macdb);
+
+ return ($macdb->{macs}->{$mac}->{ip4}, $macdb->{macs}->{$mac}->{ip6});
+}
+
1;
diff --git a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
index f0e7168..91010bb 100644
--- a/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
+++ b/src/PVE/Network/SDN/Ipams/NetboxPlugin.pm
@@ -77,14 +77,20 @@ sub del_subnet {
}
sub add_ip {
- my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+ my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
my $mask = $subnet->{mask};
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"];
- $description .= " mac:$mac" if $mac && $description;
+
+ my $description = undef;
+ if ($is_gateway) {
+ $description = 'gateway'
+ } elsif ($mac) {
+ $description = "mac:$mac";
+ }
my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
@@ -102,14 +108,20 @@ sub add_ip {
}
sub update_ip {
- my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+ my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
my $mask = $subnet->{mask};
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"];
- $description .= " mac:$mac" if $mac && $description;
+
+ my $description = undef;
+ if ($is_gateway) {
+ $description = 'gateway'
+ } elsif ($mac) {
+ $description = "mac:$mac";
+ }
my $params = { address => "$ip/$mask", dns_name => $hostname, description => $description };
@@ -125,7 +137,7 @@ sub update_ip {
}
sub add_next_freeip {
- my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
+ my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid, $noerr) = @_;
my $cidr = $subnet->{cidr};
@@ -134,7 +146,8 @@ sub add_next_freeip {
my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
my $internalid = get_prefix_id($url, $cidr, $headers);
- $description .= " mac:$mac" if $mac && $description;
+
+ my $description = "mac:$mac" if $mac;
my $params = { dns_name => $hostname, description => $description };
@@ -151,6 +164,33 @@ sub add_next_freeip {
return $ip;
}
+sub add_range_next_freeip {
+ my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
+
+ 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_iprange_id($url, $range, $headers);
+ my $description = "mac:$data->{mac}" if $data->{mac};
+
+ my $params = { dns_name => $data->{hostname}, description => $description };
+
+ my $ip = undef;
+ eval {
+ my $result = PVE::Network::SDN::api_request("POST", "$url/ipam/ip-ranges/$internalid/available-ips/", $headers, $params);
+ $ip = $result->{address};
+ print "found ip free $ip in range $range->{'start-address'}-$range->{'end-address'}\n" if $ip;
+ };
+
+ if ($@) {
+ die "can't find free ip in range $range->{'start-address'}-$range->{'end-address'}: $@" if !$noerr;
+ }
+
+ return $ip;
+
+}
+
sub del_ip {
my ($class, $plugin_config, $subnetid, $subnet, $ip, $noerr) = @_;
@@ -171,6 +211,31 @@ sub del_ip {
}
}
+sub get_ips_from_mac {
+ my ($class, $plugin_config, $mac, $zoneid) = @_;
+
+ my $url = $plugin_config->{url};
+ my $token = $plugin_config->{token};
+ my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Authorization' => "token $token"];
+
+ my $ip4 = undef;
+ my $ip6 = undef;
+
+ my $data = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-addresses/?description__ic=$mac", $headers);
+ for my $ip (@{$data->{results}}) {
+ if ($ip->{family}->{value} == 4 && !$ip4) {
+ ($ip4, undef) = split(/\//, $ip->{address});
+ }
+
+ if ($ip->{family}->{value} == 6 && !$ip6) {
+ ($ip6, undef) = split(/\//, $ip->{address});
+ }
+ }
+
+ return ($ip4, $ip6);
+}
+
+
sub verify_api {
my ($class, $plugin_config) = @_;
@@ -204,6 +269,15 @@ sub get_prefix_id {
return $internalid;
}
+sub get_iprange_id {
+ my ($url, $range, $headers) = @_;
+
+ my $result = PVE::Network::SDN::api_request("GET", "$url/ipam/ip-ranges/?start_address=$range->{'start-address'}&end_address=$range->{'end-address'}", $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::api_request("GET", "$url/ipam/ip-addresses/?q=$ip", $headers);
diff --git a/src/PVE/Network/SDN/Ipams/PVEPlugin.pm b/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
index 3e8ffc5..bcaef9d 100644
--- a/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
+++ b/src/PVE/Network/SDN/Ipams/PVEPlugin.pm
@@ -82,7 +82,7 @@ sub del_subnet {
}
sub add_ip {
- my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
+ my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway) = @_;
my $cidr = $subnet->{cidr};
my $zone = $subnet->{zone};
@@ -96,8 +96,17 @@ sub add_ip {
die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
die "IP '$ip' already exist\n" if (!$is_gateway && defined($dbsubnet->{ips}->{$ip})) || ($is_gateway && defined($dbsubnet->{ips}->{$ip}) && !defined($dbsubnet->{ips}->{$ip}->{gateway}));
- $dbsubnet->{ips}->{$ip} = {};
- $dbsubnet->{ips}->{$ip} = {gateway => 1} if $is_gateway;
+
+ my $data = {};
+ if ($is_gateway) {
+ $data->{gateway} = 1;
+ } else {
+ $data->{ip} = $ip if $ip;
+ $data->{hostname} = $hostname if $hostname;
+ $data->{mac} = $mac if $mac;
+ }
+
+ $dbsubnet->{ips}->{$ip} = $data;
write_db($db);
});
@@ -105,12 +114,12 @@ sub add_ip {
}
sub update_ip {
- my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway) = @_;
+ my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway) = @_;
return;
}
sub add_next_freeip {
- my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description) = @_;
+ my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid, $noerr) = @_;
my $cidr = $subnet->{cidr};
my $network = $subnet->{network};
@@ -156,6 +165,39 @@ sub add_next_freeip {
return "$freeip/$mask";
}
+sub add_range_next_freeip {
+ my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
+
+ my $cidr = $subnet->{cidr};
+ my $zone = $subnet->{zone};
+
+ cfs_lock_file($ipamdb_file, undef, sub {
+ my $db = read_db();
+
+ my $dbzone = $db->{zones}->{$zone};
+ die "zone '$zone' doesn't exist in IPAM DB\n" if !$dbzone;
+
+ my $dbsubnet = $dbzone->{subnets}->{$cidr};
+ die "subnet '$cidr' doesn't exist in IPAM DB\n" if !$dbsubnet;
+
+ my $ip = new Net::IP ("$range->{'start-address'} - $range->{'end-address'}")
+ or die "Invalid IP address(es) in Range!\n";
+ my $mac = $data->{mac};
+
+ do {
+ my $ip_address = $ip->ip();
+ if (!$dbsubnet->{ips}->{$ip_address}) {
+ $dbsubnet->{ips}->{$ip_address} = $data;
+ write_db($db);
+
+ return $ip_address;
+ }
+ } while (++$ip);
+
+ die "No free IP left in Range $range->{'start-address'}:$range->{'end-address'}}\n";
+ });
+}
+
sub del_ip {
my ($class, $plugin_config, $subnetid, $subnet, $ip) = @_;
@@ -172,12 +214,43 @@ sub del_ip {
die "IP '$ip' does not exist in IPAM DB\n" if !defined($dbsubnet->{ips}->{$ip});
delete $dbsubnet->{ips}->{$ip};
-
write_db($db);
});
die "$@" if $@;
}
+sub get_ips_from_mac {
+ my ($class, $plugin_config, $mac, $zoneid) = @_;
+
+ #just in case, as this should already be cached in local macs.db
+
+ my $ip4 = undef;
+ my $ip6 = undef;
+
+ my $db = read_db();
+ die "zone $zoneid don't exist in ipam db" if !$db->{zones}->{$zoneid};
+ my $dbzone = $db->{zones}->{$zoneid};
+ my $subnets = $dbzone->{subnets};
+
+ for my $subnet ( keys %$subnets) {
+ next if Net::IP::ip_is_ipv4($subnet) && $ip4;
+ next if $ip6;
+ my $ips = $subnets->{$subnet}->{ips};
+ for my $ip (keys %$ips) {
+ my $ipobject = $ips->{$ip};
+ if ($ipobject->{mac} && $ipobject->{mac} eq $mac) {
+ if (Net::IP::ip_is_ipv4($ip)) {
+ $ip4 = $ip;
+ } else {
+ $ip6 = $ip;
+ }
+ }
+ }
+ last if $ip4 && $ip6;
+ }
+ return ($ip4, $ip6);
+}
+
#helpers
sub read_db {
diff --git a/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm b/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
index ad5286b..1b7b666 100644
--- a/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
+++ b/src/PVE/Network/SDN/Ipams/PhpIpamPlugin.pm
@@ -204,6 +204,35 @@ sub del_ip {
}
}
+sub get_ips_from_mac {
+ my ($class, $plugin_config, $mac, $zoneid) = @_;
+
+
+ my $url = $plugin_config->{url};
+ my $token = $plugin_config->{token};
+ my $headers = ['Content-Type' => 'application/json; charset=UTF-8', 'Token' => $token];
+
+ my $ip4 = undef;
+ my $ip6 = undef;
+
+ my $ips = PVE::Network::SDN::api_request("GET", "$url/addresses/search_mac/$mac", $headers);
+
+ #fixme
+ die "parsing of result not yet implemented";
+
+ for my $ip (@$ips) {
+# if ($ip->{family}->{value} == 4 && !$ip4) {
+# ($ip4, undef) = split(/\//, $ip->{address});
+# }
+#
+# if ($ip->{family}->{value} == 6 && !$ip6) {
+# ($ip6, undef) = split(/\//, $ip->{address});
+# }
+ }
+
+ return ($ip4, $ip6);
+}
+
sub verify_api {
my ($class, $plugin_config) = @_;
diff --git a/src/PVE/Network/SDN/Ipams/Plugin.pm b/src/PVE/Network/SDN/Ipams/Plugin.pm
index c96eeda..05d1416 100644
--- a/src/PVE/Network/SDN/Ipams/Plugin.pm
+++ b/src/PVE/Network/SDN/Ipams/Plugin.pm
@@ -79,13 +79,13 @@ sub del_subnet {
}
sub add_ip {
- my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+ my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
die "please implement inside plugin";
}
sub update_ip {
- my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $description, $is_gateway, $noerr) = @_;
+ my ($class, $plugin_config, $subnetid, $subnet, $ip, $hostname, $mac, $vmid, $is_gateway, $noerr) = @_;
# only update ip attributes (mac,hostname,..). Don't change the ip addresses itself, as some ipam
# don't allow ip address change without del/add
@@ -93,7 +93,14 @@ sub update_ip {
}
sub add_next_freeip {
- my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $description, $noerr) = @_;
+ my ($class, $plugin_config, $subnetid, $subnet, $hostname, $mac, $vmid, $noerr) = @_;
+
+ die "please implement inside plugin";
+}
+
+
+sub add_range_next_freeip {
+ my ($class, $plugin_config, $subnet, $range, $data, $noerr) = @_;
die "please implement inside plugin";
}
@@ -104,6 +111,12 @@ sub del_ip {
die "please implement inside plugin";
}
+sub get_ips_from_mac {
+ my ($class, $plugin_config, $mac, $zone) = @_;
+
+ die "please implement inside plugin";
+}
+
sub on_update_hook {
my ($class, $plugin_config) = @_;
}
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 07/22] dhcp: add abstract class for DHCP plugins
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (5 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 06/22] ipam: plugins: preparations for DHCP Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 08/22] sdn: dhcp: add dnsmasq plugin Stefan Hanreich
` (14 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/Network/SDN/Dhcp/Makefile | 8 ++++
src/PVE/Network/SDN/Dhcp/Plugin.pm | 65 ++++++++++++++++++++++++++++++
src/PVE/Network/SDN/Makefile | 1 +
3 files changed, 74 insertions(+)
create mode 100644 src/PVE/Network/SDN/Dhcp/Makefile
create mode 100644 src/PVE/Network/SDN/Dhcp/Plugin.pm
diff --git a/src/PVE/Network/SDN/Dhcp/Makefile b/src/PVE/Network/SDN/Dhcp/Makefile
new file mode 100644
index 0000000..6546513
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp/Makefile
@@ -0,0 +1,8 @@
+SOURCES=Plugin.pm Dnsmasq.pm
+
+
+PERL5DIR=${DESTDIR}/usr/share/perl5
+
+.PHONY: install
+install:
+ for i in ${SOURCES}; do install -D -m 0644 $$i ${PERL5DIR}/PVE/Network/SDN/Dhcp/$$i; done
diff --git a/src/PVE/Network/SDN/Dhcp/Plugin.pm b/src/PVE/Network/SDN/Dhcp/Plugin.pm
new file mode 100644
index 0000000..7b9e9b7
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp/Plugin.pm
@@ -0,0 +1,65 @@
+package PVE::Network::SDN::Dhcp::Plugin;
+
+use strict;
+use warnings;
+
+use PVE::Cluster;
+use PVE::JSONSchema qw(get_standard_option);
+
+use base qw(PVE::SectionConfig);
+
+my $defaultData = {
+ propertyList => {
+ type => {
+ description => "Plugin type.",
+ format => 'pve-configid',
+ type => 'string',
+ },
+ },
+};
+
+sub private {
+ return $defaultData;
+}
+
+sub add_ip_mapping {
+ my ($class, $dhcp_config, $mac, $ip) = @_;
+ die 'implement in sub class';
+}
+
+sub del_ip_mapping {
+ my ($class, $dhcp_config, $mac) = @_;
+ die 'implement in sub class';
+}
+
+sub configure_range {
+ my ($class, $dhcp_config, $subnet_config, $range_config) = @_;
+ die 'implement in sub class';
+}
+
+sub configure_subnet {
+ my ($class, $dhcp_config, $subnet_config) = @_;
+ die 'implement in sub class';
+}
+
+sub before_configure {
+ my ($class, $dhcp_config) = @_;
+ die 'implement in sub class';
+}
+
+sub after_configure {
+ my ($class, $dhcp_config) = @_;
+ die 'implement in sub class';
+}
+
+sub before_regenerate {
+ my ($class) = @_;
+ die 'implement in sub class';
+}
+
+sub after_regenerate {
+ my ($class, $dhcp_config) = @_;
+ die 'implement in sub class';
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Makefile b/src/PVE/Network/SDN/Makefile
index 92cfcd0..848f7d4 100644
--- a/src/PVE/Network/SDN/Makefile
+++ b/src/PVE/Network/SDN/Makefile
@@ -10,4 +10,5 @@ install:
make -C Zones install
make -C Ipams install
make -C Dns install
+ make -C Dhcp install
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 08/22] sdn: dhcp: add dnsmasq plugin
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (6 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 07/22] dhcp: add abstract class for DHCP plugins Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 09/22] sdn: dhcp: add helper for creating DHCP leases Stefan Hanreich
` (13 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
debian/control | 1 +
src/PVE/Network/SDN/Dhcp/Dnsmasq.pm | 198 ++++++++++++++++++++++++++++
2 files changed, 199 insertions(+)
create mode 100644 src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
diff --git a/debian/control b/debian/control
index 8b720c3..4424096 100644
--- a/debian/control
+++ b/debian/control
@@ -24,6 +24,7 @@ Depends: libpve-common-perl (>= 5.0-45),
${misc:Depends},
${perl:Depends},
Recommends: frr-pythontools (>= 8.5.1~), ifupdown2
+Suggests: dnsmasq
Description: Proxmox VE's SDN (Software Defined Network) stack
This package contains the Software Defined Network (tech preview) for
Proxmox VE.
diff --git a/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm b/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
new file mode 100644
index 0000000..21a6ddd
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp/Dnsmasq.pm
@@ -0,0 +1,198 @@
+package PVE::Network::SDN::Dhcp::Dnsmasq;
+
+use strict;
+use warnings;
+
+use base qw(PVE::Network::SDN::Dhcp::Plugin);
+
+use Net::IP qw(:PROC);
+use PVE::Tools qw(file_set_contents run_command lock_file);
+
+use File::Copy;
+
+my $DNSMASQ_CONFIG_ROOT = '/etc/dnsmasq.d';
+my $DNSMASQ_DEFAULT_ROOT = '/etc/default';
+my $DNSMASQ_LEASE_ROOT = '/var/lib/misc';
+
+sub type {
+ return 'dnsmasq';
+}
+
+sub del_ip_mapping {
+ my ($class, $dhcpid, $mac) = @_;
+
+ my $ethers_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/ethers";
+ my $ethers_tmp_file = "$ethers_file.tmp";
+
+ my $removeFn = sub {
+ open(my $in, '<', $ethers_file) or die "Could not open file '$ethers_file' $!\n";
+ open(my $out, '>', $ethers_tmp_file) or die "Could not open file '$ethers_tmp_file' $!\n";
+
+ while (my $line = <$in>) {
+ next if $line =~ m/^$mac/;
+ print $out $line;
+ }
+
+ close $in;
+ close $out;
+
+ move $ethers_tmp_file, $ethers_file;
+
+ chmod 0644, $ethers_file;
+ };
+
+ PVE::Tools::lock_file($ethers_file, 10, $removeFn);
+
+ if ($@) {
+ warn "Unable to remove $mac from the dnsmasq configuration: $@\n";
+ return;
+ }
+
+ my $service_name = "dnsmasq\@$dhcpid";
+ PVE::Tools::run_command(['systemctl', 'reload', $service_name]);
+}
+
+sub add_ip_mapping {
+ my ($class, $dhcpid, $mac, $ip) = @_;
+
+ my $ethers_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/ethers";
+ my $ethers_tmp_file = "$ethers_file.tmp";
+
+ my $appendFn = sub {
+ open(my $in, '<', $ethers_file) or die "Could not open file '$ethers_file' $!\n";
+ open(my $out, '>', $ethers_tmp_file) or die "Could not open file '$ethers_tmp_file' $!\n";
+
+ while (my $line = <$in>) {
+ next if $line =~ m/^$mac/;
+ print $out $line;
+ }
+
+ print $out "$mac,$ip\n";
+ close $in;
+ close $out;
+ move $ethers_tmp_file, $ethers_file;
+ chmod 0644, $ethers_file;
+ };
+
+ PVE::Tools::lock_file($ethers_file, 10, $appendFn);
+
+ if ($@) {
+ warn "Unable to add $mac/$ip to the dnsmasq configuration: $@\n";
+ return;
+ }
+
+ my $service_name = "dnsmasq\@$dhcpid";
+ PVE::Tools::run_command(['systemctl', 'reload', $service_name]);
+}
+
+sub configure_subnet {
+ my ($class, $dhcpid, $subnet_config) = @_;
+
+ die "No gateway defined for subnet $subnet_config->{id}"
+ if !$subnet_config->{gateway};
+
+ my $tag = $subnet_config->{id};
+
+ my @dnsmasq_config = (
+ "listen-address=$subnet_config->{gateway}",
+ );
+
+ my $option_string;
+ if (ip_is_ipv6($subnet_config->{network})) {
+ $option_string = 'option6';
+ push @dnsmasq_config, "enable-ra";
+ } else {
+ $option_string = 'option';
+ push @dnsmasq_config, "dhcp-option=tag:$tag,$option_string:router,$subnet_config->{gateway}";
+ }
+
+ push @dnsmasq_config, "dhcp-option=tag:$tag,$option_string:dns-server,$subnet_config->{'dhcp-dns-server'}"
+ if $subnet_config->{'dhcp-dns-server'};
+
+ PVE::Tools::file_set_contents(
+ "$DNSMASQ_CONFIG_ROOT/$dhcpid/10-$subnet_config->{id}.conf",
+ join("\n", @dnsmasq_config) . "\n"
+ );
+}
+
+sub configure_range {
+ my ($class, $dhcpid, $subnet_config, $range_config) = @_;
+
+ my $range_file = "$DNSMASQ_CONFIG_ROOT/$dhcpid/10-$subnet_config->{id}.ranges.conf",
+ my $tag = $subnet_config->{id};
+
+ open(my $fh, '>>', $range_file) or die "Could not open file '$range_file' $!\n";
+ print $fh "dhcp-range=set:$tag,$range_config->{'start-address'},$range_config->{'end-address'}\n";
+ close $fh;
+}
+
+sub before_configure {
+ my ($class, $dhcpid) = @_;
+
+ my $config_directory = "$DNSMASQ_CONFIG_ROOT/$dhcpid";
+
+ mkdir($config_directory, 755) if !-d $config_directory;
+
+ my $default_config = <<CFG;
+CONFIG_DIR='$config_directory,\*.conf'
+DNSMASQ_OPTS="--conf-file=/dev/null"
+CFG
+
+ PVE::Tools::file_set_contents(
+ "$DNSMASQ_DEFAULT_ROOT/dnsmasq.$dhcpid",
+ $default_config
+ );
+
+ my $default_dnsmasq_config = <<CFG;
+except-interface=lo
+bind-dynamic
+no-resolv
+no-hosts
+dhcp-leasefile=$DNSMASQ_LEASE_ROOT/dnsmasq.$dhcpid.leases
+dhcp-hostsfile=$config_directory/ethers
+dhcp-ignore=tag:!known
+
+# Send an empty WPAD option. This may be REQUIRED to get windows 7 to behave.
+dhcp-option=252,"\\n"
+
+# Send microsoft-specific option to tell windows to release the DHCP lease
+# when it shuts down. Note the "i" flag, to tell dnsmasq to send the
+# value as a four-byte integer - that's what microsoft wants.
+dhcp-option=vendor:MSFT,2,1i
+
+# If a DHCP client claims that its name is "wpad", ignore that.
+# This fixes a security hole. see CERT Vulnerability VU#598349
+dhcp-name-match=set:wpad-ignore,wpad
+dhcp-ignore-names=tag:wpad-ignore
+CFG
+
+ PVE::Tools::file_set_contents(
+ "$config_directory/00-default.conf",
+ $default_dnsmasq_config
+ );
+
+ unlink glob "$config_directory/10-*.conf";
+}
+
+sub after_configure {
+ my ($class, $dhcpid) = @_;
+
+ my $service_name = "dnsmasq\@$dhcpid";
+
+ PVE::Tools::run_command(['systemctl', 'enable', $service_name]);
+ PVE::Tools::run_command(['systemctl', 'restart', $service_name]);
+}
+
+sub before_regenerate {
+ my ($class) = @_;
+
+ PVE::Tools::run_command(['systemctl', 'stop', "dnsmasq@*"]);
+ PVE::Tools::run_command(['systemctl', 'disable', 'dnsmasq@']);
+}
+
+sub after_regenerate {
+ my ($class) = @_;
+ # noop
+}
+
+1;
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 09/22] sdn: dhcp: add helper for creating DHCP leases
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (7 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 08/22] sdn: dhcp: add dnsmasq plugin Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 10/22] api: add IPAM endpoints Stefan Hanreich
` (12 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Co-Authored-By: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/Network/SDN/Dhcp.pm | 115 +++++++++++++++++++++++++++++++++++
src/PVE/Network/SDN/Makefile | 2 +-
2 files changed, 116 insertions(+), 1 deletion(-)
create mode 100644 src/PVE/Network/SDN/Dhcp.pm
diff --git a/src/PVE/Network/SDN/Dhcp.pm b/src/PVE/Network/SDN/Dhcp.pm
new file mode 100644
index 0000000..b178927
--- /dev/null
+++ b/src/PVE/Network/SDN/Dhcp.pm
@@ -0,0 +1,115 @@
+package PVE::Network::SDN::Dhcp;
+
+use strict;
+use warnings;
+
+use PVE::Cluster qw(cfs_read_file);
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::SubnetPlugin;
+use PVE::Network::SDN::Dhcp qw(config);
+use PVE::Network::SDN::Subnets qw(sdn_subnets_config config get_dhcp_ranges);
+use PVE::Network::SDN::Dhcp::Plugin;
+use PVE::Network::SDN::Dhcp::Dnsmasq;
+
+use PVE::INotify qw(nodename);
+
+PVE::Network::SDN::Dhcp::Plugin->init();
+
+PVE::Network::SDN::Dhcp::Dnsmasq->register();
+PVE::Network::SDN::Dhcp::Dnsmasq->init();
+
+sub add_mapping {
+ my ($vnetid, $mac, $ip4, $ip6) = @_;
+
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+ return if !$vnet;
+
+ my $zoneid = $vnet->{zone};
+ my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
+
+ return if !$zone->{ipam} || !$zone->{dhcp};
+
+ my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($zone->{dhcp});
+ $dhcp_plugin->add_ip_mapping($zoneid, $mac, $ip4) if $ip4;
+ $dhcp_plugin->add_ip_mapping($zoneid, $mac, $ip6) if $ip6;
+}
+
+sub remove_mapping {
+ my ($vnetid, $mac) = @_;
+
+ my $vnet = PVE::Network::SDN::Vnets::get_vnet($vnetid);
+ return if !$vnet;
+
+ my $zoneid = $vnet->{zone};
+ my $zone = PVE::Network::SDN::Zones::get_zone($zoneid);
+
+ return if !$zone->{ipam} || !$zone->{dhcp};
+
+ my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($zone->{dhcp});
+ $dhcp_plugin->del_ip_mapping($zoneid, $mac);
+}
+
+sub regenerate_config {
+ my ($reload) = @_;
+
+ my $cfg = PVE::Network::SDN::running_config();
+
+ my $zone_cfg = $cfg->{zones};
+ my $subnet_cfg = $cfg->{subnets};
+ return if !$zone_cfg && !$subnet_cfg;
+
+ my $nodename = PVE::INotify::nodename();
+
+ my $plugins = PVE::Network::SDN::Dhcp::Plugin->lookup_types();
+
+ foreach my $plugin_name (@$plugins) {
+ my $plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($plugin_name);
+ eval { $plugin->before_regenerate() };
+ die "Could not run before_regenerate for DHCP plugin $plugin_name $@\n" if $@;
+ }
+
+ foreach my $zoneid (sort keys %{$zone_cfg->{ids}}) {
+ my $zone = $zone_cfg->{ids}->{$zoneid};
+ next if !$zone->{dhcp};
+
+ my $dhcp_plugin_name = $zone->{dhcp};
+ my $dhcp_plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($dhcp_plugin_name);
+
+ die "Could not find DHCP plugin: $dhcp_plugin_name" if !$dhcp_plugin;
+
+ eval { $dhcp_plugin->before_configure($zoneid) };
+ die "Could not run before_configure for DHCP server $zoneid $@\n" if $@;
+
+
+ foreach my $subnet_id (keys %{$subnet_cfg->{ids}}) {
+ my $subnet_config = PVE::Network::SDN::Subnets::sdn_subnets_config($subnet_cfg, $subnet_id);
+ my $dhcp_ranges = PVE::Network::SDN::Subnets::get_dhcp_ranges($subnet_config);
+
+ my ($zone, $subnet_network, $subnet_mask) = split(/-/, $subnet_id);
+ next if $zone ne $zoneid;
+ next if !$dhcp_ranges;
+
+ eval { $dhcp_plugin->configure_subnet($zoneid, $subnet_config) };
+ warn "Could not configure subnet $subnet_id: $@\n" if $@;
+
+ foreach my $dhcp_range (@$dhcp_ranges) {
+ eval { $dhcp_plugin->configure_range($zoneid, $subnet_config, $dhcp_range) };
+ warn "Could not configure DHCP range for $subnet_id: $@\n" if $@;
+ }
+ }
+
+ eval { $dhcp_plugin->after_configure($zoneid) };
+ warn "Could not run after_configure for DHCP server $zoneid $@\n" if $@;
+
+ }
+
+ foreach my $plugin_name (@$plugins) {
+ my $plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($plugin_name);
+
+ eval { $plugin->after_regenerate() };
+ warn "Could not run after_regenerate for DHCP plugin $plugin_name $@\n" if $@;
+ }
+}
+
+1;
diff --git a/src/PVE/Network/SDN/Makefile b/src/PVE/Network/SDN/Makefile
index 848f7d4..3e6e5fb 100644
--- a/src/PVE/Network/SDN/Makefile
+++ b/src/PVE/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm Dns.pm
+SOURCES=Vnets.pm VnetPlugin.pm Zones.pm Controllers.pm Subnets.pm SubnetPlugin.pm Ipams.pm Dns.pm Dhcp.pm
PERL5DIR=${DESTDIR}/usr/share/perl5
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 10/22] api: add IPAM endpoints
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (8 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 09/22] sdn: dhcp: add helper for creating DHCP leases Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 11/22] api: subnet: add dhcp ranges Stefan Hanreich
` (11 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/API2/Network/SDN.pm | 6 ++
src/PVE/API2/Network/SDN/Ipam.pm | 172 ++++++++++++++++++++++++++++++
src/PVE/API2/Network/SDN/Makefile | 2 +-
3 files changed, 179 insertions(+), 1 deletion(-)
create mode 100644 src/PVE/API2/Network/SDN/Ipam.pm
diff --git a/src/PVE/API2/Network/SDN.pm b/src/PVE/API2/Network/SDN.pm
index d216e48..551afcf 100644
--- a/src/PVE/API2/Network/SDN.pm
+++ b/src/PVE/API2/Network/SDN.pm
@@ -15,6 +15,7 @@ use PVE::Network::SDN;
use PVE::API2::Network::SDN::Controllers;
use PVE::API2::Network::SDN::Vnets;
use PVE::API2::Network::SDN::Zones;
+use PVE::API2::Network::SDN::Ipam;
use PVE::API2::Network::SDN::Ipams;
use PVE::API2::Network::SDN::Dns;
@@ -35,6 +36,11 @@ __PACKAGE__->register_method ({
path => 'controllers',
});
+__PACKAGE__->register_method ({
+ subclass => "PVE::API2::Network::SDN::Ipam",
+ path => 'ipam',
+});
+
__PACKAGE__->register_method ({
subclass => "PVE::API2::Network::SDN::Ipams",
path => 'ipams',
diff --git a/src/PVE/API2/Network/SDN/Ipam.pm b/src/PVE/API2/Network/SDN/Ipam.pm
new file mode 100644
index 0000000..66131e3
--- /dev/null
+++ b/src/PVE/API2/Network/SDN/Ipam.pm
@@ -0,0 +1,172 @@
+package PVE::API2::Network::SDN::Ipam;
+
+use strict;
+use warnings;
+
+use PVE::Tools qw(extract_param);
+use PVE::Cluster qw(cfs_read_file cfs_write_file);
+
+use PVE::Network::SDN;
+use PVE::Network::SDN::Dhcp;
+use PVE::Network::SDN::Vnets;
+use PVE::Network::SDN::Ipams::Plugin;
+
+use PVE::JSONSchema qw(get_standard_option);
+use PVE::RPCEnvironment;
+
+use PVE::RESTHandler;
+
+use base qw(PVE::RESTHandler);
+
+__PACKAGE__->register_method ({
+ name => 'dhcpindex',
+ path => '',
+ method => 'GET',
+ description => 'List DHCP Mappings',
+ protected => 1,
+ permissions => {
+ },
+ parameters => {
+ },
+ returns => {
+ type => 'array',
+ },
+ code => sub {
+ my ($param) = @_;
+
+ my $ipam_plugin = PVE::Network::SDN::Ipams::Plugin->lookup('pve');
+ my $ipam_db = $ipam_plugin->read_db();
+
+ my $result = [];
+
+ for my $zone_id (keys %{$ipam_db->{zones}}) {
+ my $zone_config = PVE::Network::SDN::Zones::get_zone($zone_id, 1);
+ next if !$zone_config || $zone_config->{ipam} ne 'pve' || !$zone_config->{dhcp};
+
+ my $zone = $ipam_db->{zones}->{$zone_id};
+
+ my $vnets = PVE::Network::SDN::Zones::get_vnets($zone_id, 1);
+
+ for my $subnet_cidr (keys %{$zone->{subnets}}) {
+ my $subnet = $zone->{subnets}->{$subnet_cidr};
+ my $ip = new Net::IP ($subnet_cidr) or die 'Found invalid CIDR in IPAM';
+
+ my $vnet = undef;
+ for my $vnet_id (keys %$vnets) {
+ eval {
+ my ($zone, $subnetid, $subnet_cfg, $ip) = PVE::Network::SDN::Vnets::get_subnet_from_vnet_ip(
+ $vnet_id,
+ $ip->ip(),
+ );
+
+ $vnet = $subnet_cfg->{vnet};
+ };
+
+ last if $vnet;
+ }
+
+ for my $ip (keys %{$subnet->{ips}}) {
+ my $entry = $subnet->{ips}->{$ip};
+ $entry->{zone} = $zone_id;
+ $entry->{subnet} = $subnet_cidr;
+ $entry->{ip} = $ip;
+
+ if ($vnet) {
+ $entry->{vnet} = $vnet;
+ }
+
+ push @$result, $entry;
+ }
+ }
+ }
+
+ return $result;
+ },
+});
+
+__PACKAGE__->register_method ({
+ name => 'dhcpdelete',
+ path => '{vnet}/{mac}',
+ method => 'DELETE',
+ description => 'Delete DHCP Mapping',
+ protected => 1,
+ permissions => {
+ },
+ parameters => {
+ additionalProperties => 0,
+ mac => { type => 'string' },
+ vnet => { type => 'string' },
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $vnet = extract_param($param, 'vnet');
+ my $mac = extract_param($param, 'mac');
+
+ PVE::Network::SDN::Dhcp::remove_mapping($vnet, $mac);
+ PVE::Network::SDN::Vnets::del_ips_from_mac($vnet, $mac);
+
+ return undef;
+ },
+});
+
+__PACKAGE__->register_method ({
+ name => 'dhcpcreate',
+ path => '{vnet}/{mac}',
+ method => 'POST',
+ description => 'Create DHCP Mapping',
+ protected => 1,
+ permissions => {
+ },
+ parameters => {
+ additionalProperties => 0,
+ mac => { type => 'string' },
+ vnet => { type => 'string' },
+ ip => { type => 'string' },
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $vnet = extract_param($param, 'vnet');
+ my $mac = extract_param($param, 'mac');
+ my $ip = extract_param($param, 'ip');
+
+ PVE::Network::SDN::Vnets::add_ip($vnet, $ip, '', $mac, undef);
+
+ return undef;
+ },
+});
+__PACKAGE__->register_method ({
+ name => 'dhcpupdate',
+ path => '{vnet}/{mac}',
+ method => 'PUT',
+ description => 'Update DHCP Mapping',
+ protected => 1,
+ permissions => {
+ },
+ parameters => {
+ additionalProperties => 0,
+ mac => { type => 'string' },
+ vnet => { type => 'string' },
+ vmid => { type => 'string' },
+ ip => { type => 'string' },
+ },
+ returns => { type => 'null' },
+ code => sub {
+ my ($param) = @_;
+
+ my $vnet = extract_param($param, 'vnet');
+ my $mac = extract_param($param, 'mac');
+ my $vmid = extract_param($param, 'vmid');
+ my $ip = extract_param($param, 'ip');
+
+ PVE::Network::SDN::Vnets::del_ips_from_mac($vnet, $mac, '');
+ PVE::Network::SDN::Vnets::add_ip($vnet, $ip, '', $mac, $vmid);
+
+ return undef;
+ },
+});
+
+1;
diff --git a/src/PVE/API2/Network/SDN/Makefile b/src/PVE/API2/Network/SDN/Makefile
index 3683fa4..2480c09 100644
--- a/src/PVE/API2/Network/SDN/Makefile
+++ b/src/PVE/API2/Network/SDN/Makefile
@@ -1,4 +1,4 @@
-SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Dns.pm
+SOURCES=Vnets.pm Zones.pm Controllers.pm Subnets.pm Ipams.pm Ipam.pm Dns.pm
PERL5DIR=${DESTDIR}/usr/share/perl5
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 11/22] api: subnet: add dhcp ranges
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (9 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 10/22] api: add IPAM endpoints Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 12/22] api: zone: add dhcp options Stefan Hanreich
` (10 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/API2/Network/SDN/Subnets.pm | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/PVE/API2/Network/SDN/Subnets.pm b/src/PVE/API2/Network/SDN/Subnets.pm
index eb6b41b..c263cd5 100644
--- a/src/PVE/API2/Network/SDN/Subnets.pm
+++ b/src/PVE/API2/Network/SDN/Subnets.pm
@@ -29,6 +29,7 @@ my $api_sdn_subnets_config = sub {
my $scfg = dclone(PVE::Network::SDN::Subnets::sdn_subnets_config($cfg, $id));
$scfg->{subnet} = $id;
$scfg->{digest} = $cfg->{digest};
+ $scfg->{'dhcp-range'} = PVE::Network::SDN::Subnets::get_dhcp_ranges($scfg);
return $scfg;
};
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 12/22] api: zone: add dhcp options
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (10 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 11/22] api: subnet: add dhcp ranges Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 13/22] dhcp: regenerate config for DHCP servers on reload Stefan Hanreich
` (9 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/API2/Network/SDN/Zones.pm | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/PVE/API2/Network/SDN/Zones.pm b/src/PVE/API2/Network/SDN/Zones.pm
index 4c8b7e1..1c3356e 100644
--- a/src/PVE/API2/Network/SDN/Zones.pm
+++ b/src/PVE/API2/Network/SDN/Zones.pm
@@ -99,6 +99,7 @@ __PACKAGE__->register_method ({
reversedns => { type => 'string', optional => 1},
dnszone => { type => 'string', optional => 1},
ipam => { type => 'string', optional => 1},
+ dhcp => { type => 'string', optional => 1},
pending => { optional => 1},
state => { type => 'string', optional => 1},
nodes => { type => 'string', optional => 1},
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 13/22] dhcp: regenerate config for DHCP servers on reload
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (11 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 12/22] api: zone: add dhcp options Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 14/22] sdn: fix tests Stefan Hanreich
` (8 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/PVE/Network/SDN.pm | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm
index 057034f..c306527 100644
--- a/src/PVE/Network/SDN.pm
+++ b/src/PVE/Network/SDN.pm
@@ -12,6 +12,7 @@ use PVE::Network::SDN::Vnets;
use PVE::Network::SDN::Zones;
use PVE::Network::SDN::Controllers;
use PVE::Network::SDN::Subnets;
+use PVE::Network::SDN::Dhcp;
use PVE::Tools qw(extract_param dir_glob_regex run_command);
use PVE::Cluster qw(cfs_read_file cfs_write_file cfs_lock_file);
@@ -155,7 +156,7 @@ sub commit_config {
my $controllers = { ids => $controllers_cfg->{ids} };
my $subnets = { ids => $subnets_cfg->{ids} };
- $cfg = { version => $version, vnets => $vnets, zones => $zones, controllers => $controllers, subnets => $subnets };
+ $cfg = { version => $version, vnets => $vnets, zones => $zones, controllers => $controllers, subnets => $subnets };
cfs_write_file($running_cfg, $cfg);
}
@@ -231,6 +232,12 @@ sub generate_controller_config {
PVE::Network::SDN::Controllers::reload_controller() if $reload;
}
+sub generate_dhcp_config {
+ my ($reload) = @_;
+
+ PVE::Network::SDN::Dhcp::regenerate_config($reload);
+}
+
sub encode_value {
my ($type, $key, $value) = @_;
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-network 14/22] sdn: fix tests
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (12 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 13/22] dhcp: regenerate config for DHCP servers on reload Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 15/22] sdn: regenerate DHCP config on reload Stefan Hanreich
` (7 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
From: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
src/test/run_test_subnets.pl | 8 +++++++-
src/test/run_test_vnets.pl | 4 ++--
2 files changed, 9 insertions(+), 3 deletions(-)
diff --git a/src/test/run_test_subnets.pl b/src/test/run_test_subnets.pl
index f6564e1..c98359a 100755
--- a/src/test/run_test_subnets.pl
+++ b/src/test/run_test_subnets.pl
@@ -109,6 +109,12 @@ foreach my $path (@plugins) {
my $ipam_config = read_sdn_config ("$path/ipam_config");
return $ipam_config;
},
+ add_cache_mac_ip => sub {
+ return;
+ },
+ del_cache_mac_ip => sub {
+ return;
+ }
);
## add_subnet
@@ -192,7 +198,7 @@ foreach my $path (@plugins) {
$expected = '{"zones":{"myzone":{"subnets":{"'.$subnet_cidr.'":{"ips":{"'.$ip.'":{"gateway":1},"'.$ipnextfree.'":{},"'.$ip2.'":{}}}}}}}';
eval {
- $ip3 = PVE::Network::SDN::Subnets::next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
+ $ip3 = PVE::Network::SDN::Subnets::add_next_free_ip($zone, $subnetid, $subnet, $hostname, $mac, $description);
};
if ($@) {
diff --git a/src/test/run_test_vnets.pl b/src/test/run_test_vnets.pl
index 5aeb676..dc9da67 100755
--- a/src/test/run_test_vnets.pl
+++ b/src/test/run_test_vnets.pl
@@ -231,7 +231,7 @@ foreach my $path (@plugins) {
$expected = $ipam ? $cidr3 : undef;
eval {
- $result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+ $result = PVE::Network::SDN::Vnets::add_next_free_cidr($vnetid, $hostname, $mac, $description);
};
if ($@) {
@@ -309,7 +309,7 @@ foreach my $path (@plugins) {
$expected = $ipam ? $cidr1 : undef;
eval {
- $result = PVE::Network::SDN::Vnets::get_next_free_cidr($vnetid, $hostname, $mac, $description, $ipversion);
+ $result = PVE::Network::SDN::Vnets::add_next_free_cidr($vnetid, $hostname, $mac, $description);
};
if ($@) {
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-manager 15/22] sdn: regenerate DHCP config on reload
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (13 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-network 14/22] sdn: fix tests Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 16/22] sdn: add DHCP option to Zone dialogue Stefan Hanreich
` (6 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
PVE/API2/Network.pm | 1 +
1 file changed, 1 insertion(+)
diff --git a/PVE/API2/Network.pm b/PVE/API2/Network.pm
index 00d964a79..f39f04f52 100644
--- a/PVE/API2/Network.pm
+++ b/PVE/API2/Network.pm
@@ -660,6 +660,7 @@ __PACKAGE__->register_method({
if ($have_sdn) {
PVE::Network::SDN::generate_zone_config();
+ PVE::Network::SDN::generate_dhcp_config();
}
my $err = sub {
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-manager 16/22] sdn: add DHCP option to Zone dialogue
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (14 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 15/22] sdn: regenerate DHCP config on reload Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 17/22] sdn: subnet: add panel for editing DHCP ranges Stefan Hanreich
` (5 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
www/manager6/sdn/zones/Base.js | 4 ++--
www/manager6/sdn/zones/SimpleEdit.js | 10 ++++++++++
2 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/www/manager6/sdn/zones/Base.js b/www/manager6/sdn/zones/Base.js
index 602e4c16b..80ce51bac 100644
--- a/www/manager6/sdn/zones/Base.js
+++ b/www/manager6/sdn/zones/Base.js
@@ -55,7 +55,7 @@ Ext.define('PVE.panel.SDNZoneBase', {
},
);
- me.advancedItems = [
+ me.advancedItems.unshift(
{
xtype: 'pveSDNDnsSelector',
fieldLabel: gettext('DNS Server'),
@@ -77,7 +77,7 @@ Ext.define('PVE.panel.SDNZoneBase', {
fieldLabel: gettext('DNS Zone'),
allowBlank: true,
},
- ];
+ );
me.callParent();
},
diff --git a/www/manager6/sdn/zones/SimpleEdit.js b/www/manager6/sdn/zones/SimpleEdit.js
index cb7c34035..ee4ac8ec8 100644
--- a/www/manager6/sdn/zones/SimpleEdit.js
+++ b/www/manager6/sdn/zones/SimpleEdit.js
@@ -19,6 +19,16 @@ Ext.define('PVE.sdn.zones.SimpleInputPanel', {
var me = this;
me.items = [];
+ me.advancedItems = [
+ {
+ xtype: 'proxmoxcheckbox',
+ name: 'dhcp',
+ inputValue: 'dnsmasq',
+ uncheckedValue: 0,
+ checked: false,
+ fieldLabel: gettext('automatic DHCP'),
+ },
+ ];
me.callParent();
},
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-manager 17/22] sdn: subnet: add panel for editing DHCP ranges
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (15 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 16/22] sdn: add DHCP option to Zone dialogue Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-15 13:09 ` DERUMIER, Alexandre
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 18/22] sdn: dhcp: add view for DHCP mappings Stefan Hanreich
` (4 subsequent siblings)
21 siblings, 1 reply; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
www/manager6/sdn/SubnetEdit.js | 161 ++++++++++++++++++++++++++++++++-
1 file changed, 160 insertions(+), 1 deletion(-)
diff --git a/www/manager6/sdn/SubnetEdit.js b/www/manager6/sdn/SubnetEdit.js
index b9825d2a3..ab3b9d021 100644
--- a/www/manager6/sdn/SubnetEdit.js
+++ b/www/manager6/sdn/SubnetEdit.js
@@ -56,6 +56,148 @@ Ext.define('PVE.sdn.SubnetInputPanel', {
],
});
+Ext.define('PVE.sdn.SubnetDhcpRangePanel', {
+ extend: 'Ext.form.FieldContainer',
+ mixins: ['Ext.form.field.Field'],
+
+ initComponent: function() {
+ let me = this;
+
+ me.callParent();
+ me.initField();
+ },
+
+ getValue: function() {
+ let me = this;
+ let store = me.lookup('grid').getStore();
+
+ let data = [];
+
+ store.getData().each((item) => console.log(item));
+
+ store.getData()
+ .each((item) =>
+ data.push(`start-address=${item.data['start-address']},end-address=${item.data['end-address']}`),
+ );
+
+ return data;
+ },
+
+ getSubmitData: function() {
+ let me = this;
+ let data = {};
+
+ let value = me.getValue();
+ if (value) {
+ data[me.getName()] = value;
+ }
+
+ return data;
+ },
+
+ setValue: function(dhcpRanges) {
+ let me = this;
+ let store = me.lookup('grid').getStore();
+ store.setData(dhcpRanges);
+ },
+
+ getErrors: function() {
+ let me = this;
+ let errors = [];
+
+ return errors;
+ },
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ addRange: function() {
+ let me = this;
+ me.lookup('grid').getStore().add({});
+ },
+
+ removeRange: function(field) {
+ let me = this;
+ let record = field.getWidgetRecord();
+
+ me.lookup('grid').getStore().remove(record);
+ },
+
+ onValueChange: function(field, value) {
+ let me = this;
+ let record = field.getWidgetRecord();
+ let column = field.getWidgetColumn();
+
+ record.set(column.dataIndex, value);
+ record.commit();
+ },
+
+ control: {
+ 'grid button': {
+ click: 'removeRange',
+ },
+ 'field': {
+ change: 'onValueChange',
+ },
+ },
+ },
+
+ items: [
+ {
+ xtype: 'grid',
+ reference: 'grid',
+ scrollable: true,
+ store: {
+ fields: ['start-address', 'end-address'],
+ },
+ columns: [
+ {
+ text: gettext('Start Address'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'start-address',
+ flex: 1,
+ widget: {
+ xtype: 'textfield',
+ vtype: 'IP64Address',
+ },
+ },
+ {
+ text: gettext('End Address'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'end-address',
+ flex: 1,
+ widget: {
+ xtype: 'textfield',
+ vtype: 'IP64Address',
+ },
+ },
+ {
+ xtype: 'widgetcolumn',
+ width: 40,
+ widget: {
+ xtype: 'button',
+ iconCls: 'fa fa-trash-o',
+ },
+ },
+ ],
+ },
+ {
+ xtype: 'container',
+ layout: {
+ type: 'hbox',
+ },
+ items: [
+ {
+ xtype: 'button',
+ text: gettext('Add'),
+ iconCls: 'fa fa-plus-circle',
+ handler: 'addRange',
+ },
+ ],
+ },
+ ],
+});
+
Ext.define('PVE.sdn.SubnetEdit', {
extend: 'Proxmox.window.Edit',
@@ -67,6 +209,8 @@ Ext.define('PVE.sdn.SubnetEdit', {
base_url: undefined,
+ bodyPadding: 0,
+
initComponent: function() {
var me = this;
@@ -82,11 +226,22 @@ Ext.define('PVE.sdn.SubnetEdit', {
let ipanel = Ext.create('PVE.sdn.SubnetInputPanel', {
isCreate: me.isCreate,
+ title: gettext('General'),
+ });
+
+ let dhcpPanel = Ext.create('PVE.sdn.SubnetDhcpRangePanel', {
+ isCreate: me.isCreate,
+ title: gettext('DHCP Ranges'),
+ name: 'dhcp-range',
});
Ext.apply(me, {
items: [
- ipanel,
+ {
+ xtype: 'tabpanel',
+ bodyPadding: 10,
+ items: [ipanel, dhcpPanel],
+ },
],
});
@@ -97,6 +252,10 @@ Ext.define('PVE.sdn.SubnetEdit', {
success: function(response, options) {
let values = response.result.data;
ipanel.setValues(values);
+
+ if (values['dhcp-range']) {
+ dhcpPanel.setValue(values['dhcp-range']);
+ }
},
});
}
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [pve-devel] [WIP v3 pve-manager 17/22] sdn: subnet: add panel for editing DHCP ranges
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 17/22] sdn: subnet: add panel for editing DHCP ranges Stefan Hanreich
@ 2023-11-15 13:09 ` DERUMIER, Alexandre
2023-11-15 13:24 ` Stefan Hanreich
0 siblings, 1 reply; 27+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-15 13:09 UTC (permalink / raw)
To: pve-devel
Creating a new subnet without dhcp range is failing with
"
Parameter verification failed. (400)
dhcp-range: type check ('array') failed
"
-------- Message initial --------
De: Stefan Hanreich <s.hanreich@proxmox.com>
Répondre à: Proxmox VE development discussion <pve-
devel@lists.proxmox.com>
À: pve-devel@lists.proxmox.com
Objet: [pve-devel] [WIP v3 pve-manager 17/22] sdn: subnet: add panel
for editing DHCP ranges
Date: 14/11/2023 19:06:15
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
www/manager6/sdn/SubnetEdit.js | 161 ++++++++++++++++++++++++++++++++-
1 file changed, 160 insertions(+), 1 deletion(-)
diff --git a/www/manager6/sdn/SubnetEdit.js
b/www/manager6/sdn/SubnetEdit.js
index b9825d2a3..ab3b9d021 100644
--- a/www/manager6/sdn/SubnetEdit.js
+++ b/www/manager6/sdn/SubnetEdit.js
@@ -56,6 +56,148 @@ Ext.define('PVE.sdn.SubnetInputPanel', {
],
});
+Ext.define('PVE.sdn.SubnetDhcpRangePanel', {
+ extend: 'Ext.form.FieldContainer',
+ mixins: ['Ext.form.field.Field'],
+
+ initComponent: function() {
+ let me = this;
+
+ me.callParent();
+ me.initField();
+ },
+
+ getValue: function() {
+ let me = this;
+ let store = me.lookup('grid').getStore();
+
+ let data = [];
+
+ store.getData().each((item) => console.log(item));
+
+ store.getData()
+ .each((item) =>
+ data.push(`start-address=${item.data['start-
address']},end-address=${item.data['end-address']}`),
+ );
+
+ return data;
+ },
+
+ getSubmitData: function() {
+ let me = this;
+ let data = {};
+
+ let value = me.getValue();
+ if (value) {
+ data[me.getName()] = value;
+ }
+
+ return data;
+ },
+
+ setValue: function(dhcpRanges) {
+ let me = this;
+ let store = me.lookup('grid').getStore();
+ store.setData(dhcpRanges);
+ },
+
+ getErrors: function() {
+ let me = this;
+ let errors = [];
+
+ return errors;
+ },
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+
+ addRange: function() {
+ let me = this;
+ me.lookup('grid').getStore().add({});
+ },
+
+ removeRange: function(field) {
+ let me = this;
+ let record = field.getWidgetRecord();
+
+ me.lookup('grid').getStore().remove(record);
+ },
+
+ onValueChange: function(field, value) {
+ let me = this;
+ let record = field.getWidgetRecord();
+ let column = field.getWidgetColumn();
+
+ record.set(column.dataIndex, value);
+ record.commit();
+ },
+
+ control: {
+ 'grid button': {
+ click: 'removeRange',
+ },
+ 'field': {
+ change: 'onValueChange',
+ },
+ },
+ },
+
+ items: [
+ {
+ xtype: 'grid',
+ reference: 'grid',
+ scrollable: true,
+ store: {
+ fields: ['start-address', 'end-address'],
+ },
+ columns: [
+ {
+ text: gettext('Start Address'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'start-address',
+ flex: 1,
+ widget: {
+ xtype: 'textfield',
+ vtype: 'IP64Address',
+ },
+ },
+ {
+ text: gettext('End Address'),
+ xtype: 'widgetcolumn',
+ dataIndex: 'end-address',
+ flex: 1,
+ widget: {
+ xtype: 'textfield',
+ vtype: 'IP64Address',
+ },
+ },
+ {
+ xtype: 'widgetcolumn',
+ width: 40,
+ widget: {
+ xtype: 'button',
+ iconCls: 'fa fa-trash-o',
+ },
+ },
+ ],
+ },
+ {
+ xtype: 'container',
+ layout: {
+ type: 'hbox',
+ },
+ items: [
+ {
+ xtype: 'button',
+ text: gettext('Add'),
+ iconCls: 'fa fa-plus-circle',
+ handler: 'addRange',
+ },
+ ],
+ },
+ ],
+});
+
Ext.define('PVE.sdn.SubnetEdit', {
extend: 'Proxmox.window.Edit',
@@ -67,6 +209,8 @@ Ext.define('PVE.sdn.SubnetEdit', {
base_url: undefined,
+ bodyPadding: 0,
+
initComponent: function() {
var me = this;
@@ -82,11 +226,22 @@ Ext.define('PVE.sdn.SubnetEdit', {
let ipanel = Ext.create('PVE.sdn.SubnetInputPanel', {
isCreate: me.isCreate,
+ title: gettext('General'),
+ });
+
+ let dhcpPanel = Ext.create('PVE.sdn.SubnetDhcpRangePanel', {
+ isCreate: me.isCreate,
+ title: gettext('DHCP Ranges'),
+ name: 'dhcp-range',
});
Ext.apply(me, {
items: [
- ipanel,
+ {
+ xtype: 'tabpanel',
+ bodyPadding: 10,
+ items: [ipanel, dhcpPanel],
+ },
],
});
@@ -97,6 +252,10 @@ Ext.define('PVE.sdn.SubnetEdit', {
success: function(response, options) {
let values = response.result.data;
ipanel.setValues(values);
+
+ if (values['dhcp-range']) {
+ dhcpPanel.setValue(values['dhcp-range']);
+ }
},
});
}
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [pve-devel] [WIP v3 pve-manager 17/22] sdn: subnet: add panel for editing DHCP ranges
2023-11-15 13:09 ` DERUMIER, Alexandre
@ 2023-11-15 13:24 ` Stefan Hanreich
0 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-15 13:24 UTC (permalink / raw)
To: Proxmox VE development discussion, DERUMIER, Alexandre
On 11/15/23 14:09, DERUMIER, Alexandre wrote:
> Creating a new subnet without dhcp range is failing with
>
> "
> Parameter verification failed. (400)
> dhcp-range: type check ('array') failed
Thanks for the report! Already fixed it!
Also, creating a new Zone that is not Simple also failed, this is also
already fixed!
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 pve-manager 18/22] sdn: dhcp: add view for DHCP mappings
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (16 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 17/22] sdn: subnet: add panel for editing DHCP ranges Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-15 12:09 ` DERUMIER, Alexandre
2023-11-14 18:06 ` [pve-devel] [WIP v3 qemu-server 19/22] vmnic add|remove : add|del ip in ipam Stefan Hanreich
` (3 subsequent siblings)
21 siblings, 1 reply; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
www/css/ext6-pve.css | 10 +-
www/manager6/Makefile | 2 +
www/manager6/dc/Config.js | 12 +-
www/manager6/sdn/MappingEdit.js | 65 ++++++++++
www/manager6/tree/DhcpTree.js | 215 ++++++++++++++++++++++++++++++++
5 files changed, 296 insertions(+), 8 deletions(-)
create mode 100644 www/manager6/sdn/MappingEdit.js
create mode 100644 www/manager6/tree/DhcpTree.js
diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css
index e18b173f5..e5e616832 100644
--- a/www/css/ext6-pve.css
+++ b/www/css/ext6-pve.css
@@ -510,23 +510,21 @@ div.right-aligned {
content: ' ';
}
-.fa-sdn:before {
+.x-fa-sdn-treelist:before {
width: 14px;
height: 14px;
position: absolute;
left: 1px;
top: 4px;
+}
+
+.fa-sdn:before {
background-image:url(../images/icon-sdn.svg);
background-size: 14px 14px;
content: ' ';
}
.fa-network-wired:before {
- width: 14px;
- height: 14px;
- position: absolute;
- left: 1px;
- top: 4px;
background-image:url(../images/icon-fa-network-wired.svg);
background-size: 14px 14px;
content: ' ';
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index dccd2ba1c..d226c8faa 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -108,6 +108,7 @@ JSSRC= \
tree/ResourceTree.js \
tree/SnapshotTree.js \
tree/ResourceMapTree.js \
+ tree/DhcpTree.js \
window/Backup.js \
window/BackupConfig.js \
window/BulkAction.js \
@@ -274,6 +275,7 @@ JSSRC= \
sdn/ZoneContentView.js \
sdn/ZoneContentPanel.js \
sdn/ZoneView.js \
+ sdn/MappingEdit.js \
sdn/OptionsPanel.js \
sdn/controllers/Base.js \
sdn/controllers/EvpnEdit.js \
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 7d01da5fb..0e3948ef4 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -195,7 +195,7 @@ Ext.define('PVE.dc.Config', {
groups: ['sdn'],
title: gettext('Zones'),
hidden: true,
- iconCls: 'fa fa-th',
+ iconCls: 'fa fa-th x-fa-sdn-treelist',
itemId: 'sdnzone',
},
{
@@ -203,7 +203,7 @@ Ext.define('PVE.dc.Config', {
groups: ['sdn'],
title: 'VNets',
hidden: true,
- iconCls: 'fa fa-network-wired',
+ iconCls: 'fa fa-network-wired x-fa-sdn-treelist',
itemId: 'sdnvnet',
},
{
@@ -213,6 +213,14 @@ Ext.define('PVE.dc.Config', {
hidden: true,
iconCls: 'fa fa-gear',
itemId: 'sdnoptions',
+ },
+ {
+ xtype: 'pveDhcpTree',
+ groups: ['sdn'],
+ title: 'Dhcp Mappings',
+ hidden: true,
+ iconCls: 'fa fa-gear',
+ itemId: 'sdnmappings',
});
}
diff --git a/www/manager6/sdn/MappingEdit.js b/www/manager6/sdn/MappingEdit.js
new file mode 100644
index 000000000..533fc6249
--- /dev/null
+++ b/www/manager6/sdn/MappingEdit.js
@@ -0,0 +1,65 @@
+Ext.define('PVE.sdn.DhcpMappingInputPanel', {
+ extend: 'Proxmox.panel.InputPanel',
+ mixins: ['Proxmox.Mixin.CBind'],
+
+ isCreate: false,
+
+ items: [
+ {
+ xtype: 'pmxDisplayEditField',
+ name: 'vmid',
+ fieldLabel: gettext('VMID'),
+ allowBlank: false,
+ editable: false,
+ },
+ {
+ xtype: 'pmxDisplayEditField',
+ name: 'mac',
+ fieldLabel: gettext('MAC'),
+ allowBlank: false,
+ cbind: {
+ editable: '{isCreate}',
+ },
+ },
+ {
+ xtype: 'proxmoxtextfield',
+ name: 'ip',
+ fieldLabel: gettext('IP'),
+ allowBlank: false,
+ },
+ ],
+});
+
+Ext.define('PVE.sdn.MappingEdit', {
+ extend: 'Proxmox.window.Edit',
+
+ subject: gettext('DHCP Mapping'),
+ width: 350,
+
+ isCreate: false,
+ mapping: {},
+
+ submitUrl: function(url, values) {
+ return `${url}/${values.vnet}/${values.mac}`;
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ me.method = me.isCreate ? 'POST' : 'PUT';
+
+ let ipanel = Ext.create('PVE.sdn.DhcpMappingInputPanel', {
+ isCreate: me.isCreate,
+ });
+
+ Ext.apply(me, {
+ items: [
+ ipanel,
+ ],
+ });
+
+ me.callParent();
+
+ ipanel.setValues(me.mapping);
+ },
+});
diff --git a/www/manager6/tree/DhcpTree.js b/www/manager6/tree/DhcpTree.js
new file mode 100644
index 000000000..c714f0ff0
--- /dev/null
+++ b/www/manager6/tree/DhcpTree.js
@@ -0,0 +1,215 @@
+Ext.define('PVE.sdn.DhcpTree', {
+ extend: 'Ext.tree.Panel',
+ xtype: 'pveDhcpTree',
+
+ layout: 'fit',
+ rootVisible: false,
+ animate: false,
+
+ store: {
+ sorters: ['ip', 'name'],
+ },
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+ reload: function() {
+ let me = this;
+
+ Proxmox.Utils.API2Request({
+ url: `/cluster/sdn/ipam`,
+ method: 'GET',
+ success: function(response, opts) {
+ let root = {
+ name: '__root',
+ expanded: true,
+ children: [],
+ };
+
+ let zones = {};
+ let vnets = {};
+ let subnets = {};
+
+ response.result.data.forEach((element) => {
+ element.leaf = true;
+
+ if (!(element.zone in zones)) {
+ let zone = {
+ name: element.zone,
+ type: 'zone',
+ iconCls: 'fa fa-th',
+ expanded: true,
+ children: [],
+ };
+
+ zones[element.zone] = zone;
+ root.children.push(zone);
+ }
+
+ if (!(element.vnet in vnets)) {
+ let vnet = {
+ name: element.vnet,
+ type: 'vnet',
+ iconCls: 'fa fa-network-wired',
+ expanded: true,
+ children: [],
+ };
+
+ vnets[element.vnet] = vnet;
+ zones[element.zone].children.push(vnet);
+ }
+
+ if (!(element.subnet in subnets)) {
+ let subnet = {
+ name: element.subnet,
+ type: 'subnet',
+ expanded: true,
+ children: [],
+ };
+
+ subnets[element.subnet] = subnet;
+ vnets[element.vnet].children.push(subnet);
+ }
+
+ element.type = 'mapping';
+ subnets[element.subnet].children.push(element);
+ });
+
+ me.getView().setRootNode(root);
+ },
+ });
+ },
+ init: function(view) {
+ let me = this;
+ me.reload();
+ },
+ onDelete: function(table, rI, cI, item, e, { data }) {
+ let me = this;
+ let view = me.getView();
+
+ Ext.Msg.show({
+ title: gettext('Confirm'),
+ icon: Ext.Msg.WARNING,
+ message: gettext('Are you sure you want to remove DHCP mapping {0}'),
+ buttons: Ext.Msg.YESNO,
+ defaultFocus: 'no',
+ callback: function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+
+ Proxmox.Utils.API2Request({
+ url: `/cluster/sdn/ipam/${data.vnet}/${data.mac}`,
+ method: 'DELETE',
+ waitMsgTarget: view,
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'), response.htmlStatus);
+ },
+ callback: me.reload.bind(me),
+ });
+ },
+ });
+ },
+ },
+
+ columns: [
+ {
+ xtype: 'treecolumn',
+ text: 'Name / VMID',
+ dataIndex: 'name',
+ width: 200,
+ renderer: function(value, meta, record) {
+ if (record.get('gateway')) {
+ return 'Gateway';
+ }
+
+ return record.get('name') ?? record.get('vmid') ?? '';
+ },
+ },
+ {
+ text: 'IP',
+ dataIndex: 'ip',
+ width: 200,
+ },
+ {
+ text: 'MAC',
+ dataIndex: 'mac',
+ width: 200,
+ },
+ {
+ text: 'Gateway',
+ dataIndex: 'gateway',
+ width: 200,
+ },
+ {
+ header: gettext('Actions'),
+ xtype: 'actioncolumn',
+ dataIndex: 'text',
+ width: 150,
+ items: [
+ {
+ handler: function(table, rI, cI, item, e, { data }) {
+ let me = this;
+
+ let win = Ext.create('PVE.sdn.MappingEdit', {
+ autoShow: true,
+ mapping: {},
+ url: `/cluster/sdn/ipam`,
+ method: 'POST',
+ isCreate: true,
+ extraRequestParams: {
+ vnet: data.name,
+ },
+ });
+
+ win.on('destroy', me.reload);
+ },
+ getTip: (v, m, rec) => gettext('Add'),
+ getClass: (v, m, { data }) => {
+ if (data.type === 'vnet') {
+ return 'fa fa-plus-square';
+ }
+
+ return 'pmx-hidden';
+ },
+ },
+ {
+ handler: function(table, rI, cI, item, e, { data }) {
+ let me = this;
+
+ let win = Ext.create('PVE.sdn.MappingEdit', {
+ autoShow: true,
+ mapping: data,
+ url: `/cluster/sdn/ipam`,
+ method: 'PUT',
+ extraRequestParams: {
+ vmid: data.vmid,
+ vnet: data.vnet,
+ },
+ });
+
+ win.on('destroy', me.reload);
+ },
+ getTip: (v, m, rec) => 'Edit',
+ getClass: (v, m, { data }) => {
+ if (data.type === 'mapping' && !data.gateway) {
+ return 'fa fa-pencil fa-fw';
+ }
+
+ return 'pmx-hidden';
+ },
+ },
+ {
+ handler: 'onDelete',
+ getTip: (v, m, rec) => 'Delete',
+ getClass: (v, m, { data }) => {
+ if (data.type === 'mapping' && !data.gateway) {
+ return 'fa critical fa-trash-o';
+ }
+
+ return 'pmx-hidden';
+ },
+ },
+ ],
+ },
+ ],
+});
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* Re: [pve-devel] [WIP v3 pve-manager 18/22] sdn: dhcp: add view for DHCP mappings
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 18/22] sdn: dhcp: add view for DHCP mappings Stefan Hanreich
@ 2023-11-15 12:09 ` DERUMIER, Alexandre
2023-11-15 12:17 ` Stefan Hanreich
0 siblings, 1 reply; 27+ messages in thread
From: DERUMIER, Alexandre @ 2023-11-15 12:09 UTC (permalink / raw)
To: pve-devel
I think that this panel could be named "Ipam" instead "Dhcp Mappings"
As it's don't manage dhcp mapping of dhcp server, but ips in ipam
database
-------- Message initial --------
De: Stefan Hanreich <s.hanreich@proxmox.com>
Répondre à: Proxmox VE development discussion <pve-
devel@lists.proxmox.com>
À: pve-devel@lists.proxmox.com
Objet: [pve-devel] [WIP v3 pve-manager 18/22] sdn: dhcp: add view for
DHCP mappings
Date: 14/11/2023 19:06:16
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
www/css/ext6-pve.css | 10 +-
www/manager6/Makefile | 2 +
www/manager6/dc/Config.js | 12 +-
www/manager6/sdn/MappingEdit.js | 65 ++++++++++
www/manager6/tree/DhcpTree.js | 215 ++++++++++++++++++++++++++++++++
5 files changed, 296 insertions(+), 8 deletions(-)
create mode 100644 www/manager6/sdn/MappingEdit.js
create mode 100644 www/manager6/tree/DhcpTree.js
diff --git a/www/css/ext6-pve.css b/www/css/ext6-pve.css
index e18b173f5..e5e616832 100644
--- a/www/css/ext6-pve.css
+++ b/www/css/ext6-pve.css
@@ -510,23 +510,21 @@ div.right-aligned {
content: ' ';
}
-.fa-sdn:before {
+.x-fa-sdn-treelist:before {
width: 14px;
height: 14px;
position: absolute;
left: 1px;
top: 4px;
+}
+
+.fa-sdn:before {
background-image:url(../images/icon-sdn.svg);
background-size: 14px 14px;
content: ' ';
}
.fa-network-wired:before {
- width: 14px;
- height: 14px;
- position: absolute;
- left: 1px;
- top: 4px;
background-image:url(../images/icon-fa-network-wired.svg);
background-size: 14px 14px;
content: ' ';
diff --git a/www/manager6/Makefile b/www/manager6/Makefile
index dccd2ba1c..d226c8faa 100644
--- a/www/manager6/Makefile
+++ b/www/manager6/Makefile
@@ -108,6 +108,7 @@
JSSRC= \
tree/ResourceTree.js \
tree/SnapshotTree.js \
tree/ResourceMapTree.js \
+ tree/DhcpTree.js \
window/Backup.js \
window/BackupConfig.js \
window/BulkAction.js \
@@ -274,6 +275,7 @@
JSSRC= \
sdn/ZoneContentView.js \
sdn/ZoneContentPanel.js \
sdn/ZoneView.js \
+ sdn/MappingEdit.js \
sdn/OptionsPanel.js \
sdn/controllers/Base.js \
sdn/controllers/EvpnEdit.js \
diff --git a/www/manager6/dc/Config.js b/www/manager6/dc/Config.js
index 7d01da5fb..0e3948ef4 100644
--- a/www/manager6/dc/Config.js
+++ b/www/manager6/dc/Config.js
@@ -195,7 +195,7 @@ Ext.define('PVE.dc.Config', {
groups: ['sdn'],
title: gettext('Zones'),
hidden: true,
- iconCls: 'fa fa-th',
+ iconCls: 'fa fa-th x-fa-sdn-treelist',
itemId: 'sdnzone',
},
{
@@ -203,7 +203,7 @@ Ext.define('PVE.dc.Config', {
groups: ['sdn'],
title: 'VNets',
hidden: true,
- iconCls: 'fa fa-network-wired',
+ iconCls: 'fa fa-network-wired x-fa-sdn-treelist',
itemId: 'sdnvnet',
},
{
@@ -213,6 +213,14 @@ Ext.define('PVE.dc.Config', {
hidden: true,
iconCls: 'fa fa-gear',
itemId: 'sdnoptions',
+ },
+ {
+ xtype: 'pveDhcpTree',
+ groups: ['sdn'],
+ title: 'Dhcp Mappings',
+ hidden: true,
+ iconCls: 'fa fa-gear',
+ itemId: 'sdnmappings',
});
}
diff --git a/www/manager6/sdn/MappingEdit.js
b/www/manager6/sdn/MappingEdit.js
new file mode 100644
index 000000000..533fc6249
--- /dev/null
+++ b/www/manager6/sdn/MappingEdit.js
@@ -0,0 +1,65 @@
+Ext.define('PVE.sdn.DhcpMappingInputPanel', {
+ extend: 'Proxmox.panel.InputPanel',
+ mixins: ['Proxmox.Mixin.CBind'],
+
+ isCreate: false,
+
+ items: [
+ {
+ xtype: 'pmxDisplayEditField',
+ name: 'vmid',
+ fieldLabel: gettext('VMID'),
+ allowBlank: false,
+ editable: false,
+ },
+ {
+ xtype: 'pmxDisplayEditField',
+ name: 'mac',
+ fieldLabel: gettext('MAC'),
+ allowBlank: false,
+ cbind: {
+ editable: '{isCreate}',
+ },
+ },
+ {
+ xtype: 'proxmoxtextfield',
+ name: 'ip',
+ fieldLabel: gettext('IP'),
+ allowBlank: false,
+ },
+ ],
+});
+
+Ext.define('PVE.sdn.MappingEdit', {
+ extend: 'Proxmox.window.Edit',
+
+ subject: gettext('DHCP Mapping'),
+ width: 350,
+
+ isCreate: false,
+ mapping: {},
+
+ submitUrl: function(url, values) {
+ return `${url}/${values.vnet}/${values.mac}`;
+ },
+
+ initComponent: function() {
+ var me = this;
+
+ me.method = me.isCreate ? 'POST' : 'PUT';
+
+ let ipanel = Ext.create('PVE.sdn.DhcpMappingInputPanel', {
+ isCreate: me.isCreate,
+ });
+
+ Ext.apply(me, {
+ items: [
+ ipanel,
+ ],
+ });
+
+ me.callParent();
+
+ ipanel.setValues(me.mapping);
+ },
+});
diff --git a/www/manager6/tree/DhcpTree.js
b/www/manager6/tree/DhcpTree.js
new file mode 100644
index 000000000..c714f0ff0
--- /dev/null
+++ b/www/manager6/tree/DhcpTree.js
@@ -0,0 +1,215 @@
+Ext.define('PVE.sdn.DhcpTree', {
+ extend: 'Ext.tree.Panel',
+ xtype: 'pveDhcpTree',
+
+ layout: 'fit',
+ rootVisible: false,
+ animate: false,
+
+ store: {
+ sorters: ['ip', 'name'],
+ },
+
+ controller: {
+ xclass: 'Ext.app.ViewController',
+ reload: function() {
+ let me = this;
+
+ Proxmox.Utils.API2Request({
+ url: `/cluster/sdn/ipam`,
+ method: 'GET',
+ success: function(response, opts) {
+ let root = {
+ name: '__root',
+ expanded: true,
+ children: [],
+ };
+
+ let zones = {};
+ let vnets = {};
+ let subnets = {};
+
+ response.result.data.forEach((element) => {
+ element.leaf = true;
+
+ if (!(element.zone in zones)) {
+ let zone = {
+ name: element.zone,
+ type: 'zone',
+ iconCls: 'fa fa-th',
+ expanded: true,
+ children: [],
+ };
+
+ zones[element.zone] = zone;
+ root.children.push(zone);
+ }
+
+ if (!(element.vnet in vnets)) {
+ let vnet = {
+ name: element.vnet,
+ type: 'vnet',
+ iconCls: 'fa fa-network-wired',
+ expanded: true,
+ children: [],
+ };
+
+ vnets[element.vnet] = vnet;
+ zones[element.zone].children.push(vnet);
+ }
+
+ if (!(element.subnet in subnets)) {
+ let subnet = {
+ name: element.subnet,
+ type: 'subnet',
+ expanded: true,
+ children: [],
+ };
+
+ subnets[element.subnet] = subnet;
+ vnets[element.vnet].children.push(subnet);
+ }
+
+ element.type = 'mapping';
+ subnets[element.subnet].children.push(element)
;
+ });
+
+ me.getView().setRootNode(root);
+ },
+ });
+ },
+ init: function(view) {
+ let me = this;
+ me.reload();
+ },
+ onDelete: function(table, rI, cI, item, e, { data }) {
+ let me = this;
+ let view = me.getView();
+
+ Ext.Msg.show({
+ title: gettext('Confirm'),
+ icon: Ext.Msg.WARNING,
+ message: gettext('Are you sure you want to remove DHCP
mapping {0}'),
+ buttons: Ext.Msg.YESNO,
+ defaultFocus: 'no',
+ callback: function(btn) {
+ if (btn !== 'yes') {
+ return;
+ }
+
+ Proxmox.Utils.API2Request({
+ url:
`/cluster/sdn/ipam/${data.vnet}/${data.mac}`,
+ method: 'DELETE',
+ waitMsgTarget: view,
+ failure: function(response, opts) {
+ Ext.Msg.alert(gettext('Error'),
response.htmlStatus);
+ },
+ callback: me.reload.bind(me),
+ });
+ },
+ });
+ },
+ },
+
+ columns: [
+ {
+ xtype: 'treecolumn',
+ text: 'Name / VMID',
+ dataIndex: 'name',
+ width: 200,
+ renderer: function(value, meta, record) {
+ if (record.get('gateway')) {
+ return 'Gateway';
+ }
+
+ return record.get('name') ?? record.get('vmid') ?? '';
+ },
+ },
+ {
+ text: 'IP',
+ dataIndex: 'ip',
+ width: 200,
+ },
+ {
+ text: 'MAC',
+ dataIndex: 'mac',
+ width: 200,
+ },
+ {
+ text: 'Gateway',
+ dataIndex: 'gateway',
+ width: 200,
+ },
+ {
+ header: gettext('Actions'),
+ xtype: 'actioncolumn',
+ dataIndex: 'text',
+ width: 150,
+ items: [
+ {
+ handler: function(table, rI, cI, item, e, { data
}) {
+ let me = this;
+
+ let win = Ext.create('PVE.sdn.MappingEdit', {
+ autoShow: true,
+ mapping: {},
+ url: `/cluster/sdn/ipam`,
+ method: 'POST',
+ isCreate: true,
+ extraRequestParams: {
+ vnet: data.name,
+ },
+ });
+
+ win.on('destroy', me.reload);
+ },
+ getTip: (v, m, rec) => gettext('Add'),
+ getClass: (v, m, { data }) => {
+ if (data.type === 'vnet') {
+ return 'fa fa-plus-square';
+ }
+
+ return 'pmx-hidden';
+ },
+ },
+ {
+ handler: function(table, rI, cI, item, e, { data
}) {
+ let me = this;
+
+ let win = Ext.create('PVE.sdn.MappingEdit', {
+ autoShow: true,
+ mapping: data,
+ url: `/cluster/sdn/ipam`,
+ method: 'PUT',
+ extraRequestParams: {
+ vmid: data.vmid,
+ vnet: data.vnet,
+ },
+ });
+
+ win.on('destroy', me.reload);
+ },
+ getTip: (v, m, rec) => 'Edit',
+ getClass: (v, m, { data }) => {
+ if (data.type === 'mapping' && !data.gateway)
{
+ return 'fa fa-pencil fa-fw';
+ }
+
+ return 'pmx-hidden';
+ },
+ },
+ {
+ handler: 'onDelete',
+ getTip: (v, m, rec) => 'Delete',
+ getClass: (v, m, { data }) => {
+ if (data.type === 'mapping' && !data.gateway)
{
+ return 'fa critical fa-trash-o';
+ }
+
+ return 'pmx-hidden';
+ },
+ },
+ ],
+ },
+ ],
+});
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 qemu-server 19/22] vmnic add|remove : add|del ip in ipam
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (17 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 pve-manager 18/22] sdn: dhcp: add view for DHCP mappings Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 qemu-server 20/22] vm_start : vm-network-scripts: get ip from ipam and add dhcp reservation Stefan Hanreich
` (2 subsequent siblings)
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
From: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
PVE/QemuServer.pm | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index c465fb6..1ae1cb0 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -64,6 +64,8 @@ use PVE::QemuServer::USB;
my $have_sdn;
eval {
require PVE::Network::SDN::Zones;
+ require PVE::Network::SDN::Vnets;
+ require PVE::Network::SDN::Dhcp;
$have_sdn = 1;
};
@@ -4998,6 +5000,11 @@ sub vmconfig_hotplug_pending {
} elsif ($opt =~ m/^net(\d+)$/) {
die "skip\n" if !$hotplug_features->{network};
vm_deviceunplug($vmid, $conf, $opt);
+ if($have_sdn) {
+ my $net = PVE::QemuServer::parse_net($conf->{$opt});
+ PVE::Network::SDN::Dhcp::remove_mapping($net->{bridge}, $net->{macaddr});
+ PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{macaddr}, $conf->{name});
+ }
} elsif (is_valid_drivename($opt)) {
die "skip\n" if !$hotplug_features->{disk} || $opt =~ m/(ide|sata)(\d+)/;
vm_deviceunplug($vmid, $conf, $opt);
@@ -5203,6 +5210,12 @@ sub vmconfig_apply_pending {
die "internal error";
} elsif (defined($conf->{$opt}) && is_valid_drivename($opt)) {
vmconfig_delete_or_detach_drive($vmid, $storecfg, $conf, $opt, $force);
+ } elsif (defined($conf->{$opt}) && $opt =~ m/^net\d+$/) {
+ if($have_sdn) {
+ my $net = PVE::QemuServer::parse_net($conf->{$opt});
+ PVE::Network::SDN::Dhcp::remove_mapping($net->{bridge}, $net->{macaddr});
+ PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{macaddr}, $conf->{name});
+ }
}
};
if (my $err = $@) {
@@ -5222,6 +5235,21 @@ sub vmconfig_apply_pending {
eval {
if (defined($conf->{$opt}) && is_valid_drivename($opt)) {
vmconfig_register_unused_drive($storecfg, $vmid, $conf, parse_drive($opt, $conf->{$opt}))
+ } elsif (defined($conf->{pending}->{$opt}) && $opt =~ m/^net\d+$/) {
+ if($have_sdn) {
+ my $new_net = PVE::QemuServer::parse_net($conf->{pending}->{$opt});
+ if ($conf->{$opt}){
+ my $old_net = PVE::QemuServer::parse_net($conf->{$opt});
+
+ if ($old_net->{bridge} ne $new_net->{bridge} ||
+ $old_net->{macaddr} ne $new_net->{macaddr}) {
+ PVE::Network::SDN::Dhcp::remove_mapping($old_net->{bridge}, $old_net->{macaddr});
+ PVE::Network::SDN::Vnets::del_ips_from_mac($old_net->{bridge}, $old_net->{macaddr}, $conf->{name});
+ }
+ }
+ #fixme: reuse ip if mac change && same bridge
+ PVE::Network::SDN::Vnets::add_next_free_cidr($new_net->{bridge}, $conf->{name}, $new_net->{macaddr}, $vmid, undef, 1);
+ }
}
};
if (my $err = $@) {
@@ -5265,6 +5293,13 @@ sub vmconfig_update_net {
# for non online change, we try to hot-unplug
die "skip\n" if !$hotplug;
vm_deviceunplug($vmid, $conf, $opt);
+
+ # fixme: force device_unplug on bridge change if mac is present in dhcp, to force guest os to retrieve a new ip
+ if($have_sdn) {
+ PVE::Network::SDN::Dhcp::remove_mapping($oldnet->{bridge}, $oldnet->{macaddr});
+ PVE::Network::SDN::Vnets::del_ips_from_mac($oldnet->{bridge}, $oldnet->{macaddr}, $conf->{name});
+ }
+
} else {
die "internal error" if $opt !~ m/net(\d+)/;
@@ -5296,6 +5331,9 @@ sub vmconfig_update_net {
}
if ($hotplug) {
+ if ($have_sdn) {
+ PVE::Network::SDN::Vnets::add_next_free_cidr($newnet->{bridge}, $conf->{name}, $newnet->{macaddr}, "vmid:$vmid", undef, 1);
+ }
vm_deviceplug($storecfg, $conf, $vmid, $opt, $newnet, $arch, $machine_type);
} else {
die "skip\n";
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 qemu-server 20/22] vm_start : vm-network-scripts: get ip from ipam and add dhcp reservation
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (18 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 qemu-server 19/22] vmnic add|remove : add|del ip in ipam Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 qemu-server 21/22] api2: create|restore|clone: add_free_ip Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 qemu-server 22/22] vm_destroy: delete ip from ipam && dhcp Stefan Hanreich
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
From: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
vm-network-scripts/pve-bridge | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/vm-network-scripts/pve-bridge b/vm-network-scripts/pve-bridge
index d37ce33..24efaad 100755
--- a/vm-network-scripts/pve-bridge
+++ b/vm-network-scripts/pve-bridge
@@ -10,6 +10,8 @@ use PVE::Network;
my $have_sdn;
eval {
require PVE::Network::SDN::Zones;
+ require PVE::Network::SDN::Vnets;
+ require PVE::Network::SDN::Dhcp;
$have_sdn = 1;
};
@@ -44,6 +46,9 @@ my $net = PVE::QemuServer::parse_net($netconf);
die "unable to parse network config '$netid'\n" if !$net;
if ($have_sdn) {
+ my ($ip4, $ip6) = PVE::Network::SDN::Vnets::get_ips_from_mac($net->{bridge}, $net->{macaddr});
+ PVE::Network::SDN::Dhcp::add_mapping($net->{bridge}, $net->{macaddr}, $ip4, $ip6);
+
PVE::Network::SDN::Zones::tap_create($iface, $net->{bridge});
PVE::Network::SDN::Zones::tap_plug($iface, $net->{bridge}, $net->{tag}, $net->{firewall}, $net->{trunks}, $net->{rate});
} else {
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 qemu-server 21/22] api2: create|restore|clone: add_free_ip
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (19 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 qemu-server 20/22] vm_start : vm-network-scripts: get ip from ipam and add dhcp reservation Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
2023-11-14 18:06 ` [pve-devel] [WIP v3 qemu-server 22/22] vm_destroy: delete ip from ipam && dhcp Stefan Hanreich
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
From: Alexandre Derumier <aderumier@odiso.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
PVE/API2/Qemu.pm | 6 ++++++
PVE/QemuServer.pm | 31 +++++++++++++++++++++++++++++++
2 files changed, 37 insertions(+)
diff --git a/PVE/API2/Qemu.pm b/PVE/API2/Qemu.pm
index 38bdaab..a0f8243 100644
--- a/PVE/API2/Qemu.pm
+++ b/PVE/API2/Qemu.pm
@@ -991,6 +991,8 @@ __PACKAGE__->register_method({
eval { PVE::QemuServer::template_create($vmid, $restored_conf) };
warn $@ if $@;
}
+
+ PVE::QemuServer::create_ifaces_ipams_ips($restored_conf, $vmid) if $unique;
};
# ensure no old replication state are exists
@@ -1066,6 +1068,8 @@ __PACKAGE__->register_method({
}
PVE::AccessControl::add_vm_to_pool($vmid, $pool) if $pool;
+
+ PVE::QemuServer::create_ifaces_ipams_ips($conf, $vmid);
};
PVE::QemuConfig->lock_config_full($vmid, 1, $realcmd);
@@ -3763,6 +3767,8 @@ __PACKAGE__->register_method({
PVE::QemuConfig->write_config($newid, $newconf);
+ PVE::QemuServer::create_ifaces_ipams_ips($newconf, $vmid);
+
if ($target) {
# always deactivate volumes - avoid lvm LVs to be active on several nodes
PVE::Storage::deactivate_volumes($storecfg, $vollist, $snapname) if !$running;
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index 1ae1cb0..fecdb9c 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -8626,4 +8626,35 @@ sub del_nets_bridge_fdb {
}
}
+sub create_ifaces_ipams_ips {
+ my ($conf, $vmid) = @_;
+
+ return if !$have_sdn;
+
+ foreach my $opt (keys %$conf) {
+ if ($opt =~ m/^net(\d+)$/) {
+ my $value = $conf->{$opt};
+ my $net = PVE::QemuServer::parse_net($value);
+ eval { PVE::Network::SDN::Vnets::add_next_free_cidr($net->{bridge}, $conf->{name}, $net->{macaddr}, $vmid, undef, 1) };
+ warn $@ if $@;
+ }
+ }
+}
+
+sub delete_ifaces_ipams_ips {
+ my ($conf, $vmid) = @_;
+
+ return if !$have_sdn;
+
+ foreach my $opt (keys %$conf) {
+ if ($opt =~ m/^net(\d+)$/) {
+ my $net = PVE::QemuServer::parse_net($conf->{$opt});
+ eval { PVE::Network::SDN::Dhcp::remove_mapping($net->{bridge}, $net->{macaddr}) };
+ warn $@ if $@;
+ eval { PVE::Network::SDN::Vnets::del_ips_from_mac($net->{bridge}, $net->{macaddr}, $conf->{name}) };
+ warn $@ if $@;
+ }
+ }
+}
+
1;
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread
* [pve-devel] [WIP v3 qemu-server 22/22] vm_destroy: delete ip from ipam && dhcp
2023-11-14 18:05 [pve-devel] [WIP v3 cluster/network/manager/qemu-server 00/22] Add support for DHCP servers to SDN Stefan Hanreich
` (20 preceding siblings ...)
2023-11-14 18:06 ` [pve-devel] [WIP v3 qemu-server 21/22] api2: create|restore|clone: add_free_ip Stefan Hanreich
@ 2023-11-14 18:06 ` Stefan Hanreich
21 siblings, 0 replies; 27+ messages in thread
From: Stefan Hanreich @ 2023-11-14 18:06 UTC (permalink / raw)
To: pve-devel
From: Alexandre Derumier <aderumier@odiso.com>
Co-Authored-By: Stefan Hanreich <s.hanreich@proxmox.com>
Signed-off-by: Stefan Hanreich <s.hanreich@proxmox.com>
---
PVE/QemuServer.pm | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/PVE/QemuServer.pm b/PVE/QemuServer.pm
index fecdb9c..c9c061c 100644
--- a/PVE/QemuServer.pm
+++ b/PVE/QemuServer.pm
@@ -2342,6 +2342,9 @@ sub destroy_vm {
});
}
+ eval { delete_ifaces_ipams_ips($conf, $vmid)};
+ warn $@ if $@;
+
if (defined $replacement_conf) {
PVE::QemuConfig->write_config($vmid, $replacement_conf);
} else {
@@ -6153,6 +6156,18 @@ sub cleanup_pci_devices {
PVE::QemuServer::PCI::remove_pci_reservation($vmid);
}
+sub cleanup_sdn_dhcp {
+ my ($vmid, $conf) = @_;
+
+ for my $k (keys %$conf) {
+ next if $k !~ /^net(\d+)/;
+ my $netconf = $conf->{$k};
+ my $net = PVE::QemuServer::parse_net($netconf);
+
+ PVE::Network::SDN::Dhcp::remove_mapping($net->{bridge}, $net->{macaddr});
+ }
+}
+
sub vm_stop_cleanup {
my ($storecfg, $vmid, $conf, $keepActive, $apply_pending_changes) = @_;
@@ -6186,6 +6201,8 @@ sub vm_stop_cleanup {
cleanup_pci_devices($vmid, $conf);
+ cleanup_sdn_dhcp($vmid, $conf);
+
vmconfig_apply_pending($vmid, $conf, $storecfg) if $apply_pending_changes;
};
warn $@ if $@; # avoid errors - just warn
--
2.39.2
^ permalink raw reply [flat|nested] 27+ messages in thread