From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 06151B016 for ; Fri, 8 Sep 2023 15:43:38 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id DB2C112B2F for ; Fri, 8 Sep 2023 15:43:07 +0200 (CEST) Received: from lana.proxmox.com (unknown [94.136.29.99]) by firstgate.proxmox.com (Proxmox) with ESMTP for ; Fri, 8 Sep 2023 15:43:07 +0200 (CEST) Received: by lana.proxmox.com (Postfix, from userid 10043) id 825072C0928; Fri, 8 Sep 2023 15:43:05 +0200 (CEST) From: Stefan Hanreich To: pve-devel@lists.proxmox.com Date: Fri, 8 Sep 2023 15:43:04 +0200 Message-Id: <20230908134304.2009415-7-s.hanreich@proxmox.com> X-Mailer: git-send-email 2.39.2 In-Reply-To: <20230908134304.2009415-1-s.hanreich@proxmox.com> References: <20230908134304.2009415-1-s.hanreich@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -0.422 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment KAM_LAZY_DOMAIN_SECURITY 1 Sending domain does not have any anti-forgery methods RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_NONE 0.001 SPF: sender does not publish an SPF Record Subject: [pve-devel] [RFC pve-network 6/6] sdn: dhcp: regenerate config for DHCP servers on reload X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 08 Sep 2023 13:43:38 -0000 During config regeneration parsing of the SDN configuration happens in one pass before generating the configuration files via the plugins in order to avoid having to parse property strings in the subnet configuration multiple times. Then we call the respective hooks of the plugin responsible for configuring a DHCP instance. The plugin should then handle the config generation accordingly. Signed-off-by: Stefan Hanreich --- src/PVE/Network/SDN.pm | 11 +++- src/PVE/Network/SDN/Dhcp.pm | 122 +++++++++++++++++++++++++++++++++++ src/PVE/Network/SDN/Makefile | 3 +- 3 files changed, 133 insertions(+), 3 deletions(-) create mode 100644 src/PVE/Network/SDN/Dhcp.pm diff --git a/src/PVE/Network/SDN.pm b/src/PVE/Network/SDN.pm index 057034f..952f9dc 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); @@ -149,13 +150,15 @@ sub commit_config { my $zones_cfg = PVE::Network::SDN::Zones::config(); my $controllers_cfg = PVE::Network::SDN::Controllers::config(); my $subnets_cfg = PVE::Network::SDN::Subnets::config(); + my $dhcp_cfg = PVE::Network::SDN::Dhcp::config(); my $vnets = { ids => $vnets_cfg->{ids} }; my $zones = { ids => $zones_cfg->{ids} }; my $controllers = { ids => $controllers_cfg->{ids} }; my $subnets = { ids => $subnets_cfg->{ids} }; + my $dhcp = { ids => $dhcp_cfg->{ids} }; - $cfg = { version => $version, vnets => $vnets, zones => $zones, controllers => $controllers, subnets => $subnets }; + $cfg = { version => $version, vnets => $vnets, zones => $zones, controllers => $controllers, subnets => $subnets, dhcp => $dhcp }; cfs_write_file($running_cfg, $cfg); } @@ -231,6 +234,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) = @_; diff --git a/src/PVE/Network/SDN/Dhcp.pm b/src/PVE/Network/SDN/Dhcp.pm new file mode 100644 index 0000000..8c8a437 --- /dev/null +++ b/src/PVE/Network/SDN/Dhcp.pm @@ -0,0 +1,122 @@ +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); +use PVE::Network::SDN::Dhcp::Plugin; +use PVE::Network::SDN::Dhcp::Dnsmasq; +use PVE::JSONSchema qw(parse_property_string); + +use PVE::INotify qw(nodename); + +PVE::Network::SDN::Dhcp::Plugin->init(); + +PVE::Network::SDN::Dhcp::Dnsmasq->register(); +PVE::Network::SDN::Dhcp::Dnsmasq->init(); + +sub config { + return cfs_read_file('sdn/dhcp.cfg'); +} + +sub parse_config { + my ($dhcps, $subnets, $nodename) = @_; + + my %parsed_config; + + for my $subnet_id (keys %{$subnets->{ids}}) { + my $subnet_config = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets, $subnet_id); + + next 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; + } + + my $dhcp_config = $dhcps->{ids}->{$dhcp_range->{server}}; + + if (!$dhcp_config) { + warn "Cannot find configuration for DHCP server $dhcp_range->{server}"; + next; + } + + next if $dhcp_config->{node} && !grep(/^$nodename$/, @{$dhcp_config->{node}}); + + push @{$parsed_config{$dhcp_range->{server}}{$subnet_id}}, $dhcp_range; + } + } + + return \%parsed_config; +} + +sub regenerate_config { + my ($reload) = @_; + + my $dhcps = PVE::Network::SDN::Dhcp::config(); + my $subnets = PVE::Network::SDN::Subnets::config(); + my $nodename = PVE::INotify::nodename(); + my $parsed_config = parse_config($dhcps, $subnets, $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 $@; + } + + for my $dhcp_id (keys %$parsed_config) { + my $parsed_subnets = $parsed_config->{$dhcp_id}; + + next if !%$parsed_subnets; + + my $dhcp_config = $dhcps->{ids}->{$dhcp_id}; + $dhcp_config->{id} = $dhcp_id; + + my $plugin = PVE::Network::SDN::Dhcp::Plugin->lookup($dhcp_config->{type}); + + eval { $plugin->before_configure($dhcp_config) }; + + if ($@) { + warn "Could not run before_configure for DHCP server $dhcp_id $@\n" if $@; + next; + } + + for my $subnet_id (keys %$parsed_subnets) { + my $subnet_config = PVE::Network::SDN::Subnets::sdn_subnets_config($subnets, $subnet_id); + $subnet_config->{id} = $subnet_id; + + eval { + $plugin->configure_subnet( + $dhcp_config, + $subnet_config, + $parsed_subnets->{$subnet_id}, + ); + }; + warn "Could not configure Subnet $subnet_id: $@\n" if $@; + } + + eval { $plugin->after_configure($dhcp_config) }; + warn "Could not run after_configure for DHCP server $dhcp_id $@\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..86c3b9d 100644 --- a/src/PVE/Network/SDN/Makefile +++ b/src/PVE/Network/SDN/Makefile @@ -1,5 +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