Skip to content

Commit

Permalink
Implement new configuration strategy for IPAM plugin (#219)
Browse files Browse the repository at this point in the history
* Implement new configuration strategy for `IPAM` plugin

* Fix linter issues
  • Loading branch information
damyan authored Nov 27, 2024
1 parent fb95f87 commit 0ae085a
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 15 deletions.
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,16 @@ The IPAM plugin acts as a Kubernetes persistence plugin for IronCore's in-band n

The IPAM plugin does not modify DHCP responses to the client, it rather creates (or updates) IP objects in Kubernetes. For each created IP object, the in-band plugin `onmetal` will lease an IP address to the client. Due to the nature of the IronCore's in-band network - `/127` client networks connected to each switch port - the IP object created has and address calculated by a simple "plus one" rule. In such a way each client gets a "plus one" of the switch port address it is connected to.
### Configuration
A kubernetes namespace shall be passed as a string. All IPAM processing (subnet identification, IP object creation/update) are done in that namespace.
Further, as a second parameter, a comma-separated list of subnet names shall be passed. The IPAM plugin will do the subnet creation based on the IP address of the object to be created as well as on the vacant range of the corresponding subnet.
The IPAM configuration consists of two parameters. First, a kubernetes namespace shall be defined. All IPAM processing (subnet identification, IP object creation/update) are done in that namespace.
Further, a list of subnet names shall be passed. The IPAM plugin will do the subnet creation based on the IP address of the object to be created, as well as on the vacant range of the corresponding subnet.
Providing those in `ipam_config.yaml` goes as follows:
```yaml
namespace: ipam-ns
subnets:
- ipam-subnet1
- ipam-subnet2
- some-other-subnet
```
### Notes
- supports only IPv6
- IPv6 relays are mandatory
Expand Down
2 changes: 1 addition & 1 deletion example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ server6:
# implement HTTPBoot
- httpboot: http://[2001:db8::1]/image.uki
# add leased IPs to ironcore's IPAM
- ipam: ipam-ns ipam-subnet1,ipam-subnet2,some-other-subnet
- ipam: ipam_config.yaml
# lease IPs based on /127 subnets coming from relays running on the switches
- onmetal: onmetal_config.yaml
# announce DNS servers per DHCP
Expand Down
5 changes: 5 additions & 0 deletions example/ipam_config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
namespace: ipam-ns
subnets:
- ipam-subnet1
- ipam-subnet2
- some-other-subnet
9 changes: 9 additions & 0 deletions internal/api/ipam_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and IronCore contributors
// SPDX-License-Identifier: MIT

package api

type IPAMConfig struct {
Namespace string `yaml:"namespace"`
Subnets []string `yaml:"subnets"`
}
42 changes: 31 additions & 11 deletions plugins/ipam/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,17 @@ package ipam

import (
"fmt"
"net"
"strings"

"github.com/coredhcp/coredhcp/handler"
"github.com/coredhcp/coredhcp/logger"
"github.com/coredhcp/coredhcp/plugins"

"net"
"os"

"github.com/insomniacslk/dhcp/dhcpv6"
"github.com/ironcore-dev/fedhcp/internal/api"
"gopkg.in/yaml.v3"

"github.com/mdlayher/netx/eui64"
)
Expand All @@ -27,24 +31,40 @@ var (
k8sClient *K8sClient
)

func parseArgs(args ...string) (string, []string, error) {
if len(args) < 2 {
return "", []string{""}, fmt.Errorf("at least two arguments must be passed to ipam plugin, a namespace "+
"and a comma-separated subnet names list, got %d", len(args))
// args[0] = path to config file
func parseArgs(args ...string) (string, error) {
if len(args) != 1 {
return "", fmt.Errorf("exactly one argument must be passed to the metal plugin, got %d", len(args))
}
return args[0], nil
}

func loadConfig(args ...string) (*api.IPAMConfig, error) {
path, err := parseArgs(args...)
if err != nil {
return nil, fmt.Errorf("invalid configuration: %v", err)
}

log.Debugf("Reading ipam config file %s", path)
configData, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %v", err)
}

namespace := args[0]
subnetNames := strings.Split(args[1], ",")
return namespace, subnetNames, nil
config := &api.IPAMConfig{}
if err = yaml.Unmarshal(configData, config); err != nil {
return nil, fmt.Errorf("failed to parse config file: %v", err)
}
return config, nil
}

func setup6(args ...string) (handler.Handler6, error) {
namespace, subnetNames, err := parseArgs(args...)
ipamConfig, err := loadConfig(args...)
if err != nil {
return nil, err
}

k8sClient, err = NewK8sClient(namespace, subnetNames)
k8sClient, err = NewK8sClient(ipamConfig.Namespace, ipamConfig.Subnets)
if err != nil {
return nil, fmt.Errorf("failed to create k8s client: %w", err)
}
Expand Down
2 changes: 1 addition & 1 deletion plugins/onmetal/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func loadConfig(args ...string) (*api.OnMetalConfig, error) {
return nil, fmt.Errorf("invalid configuration: %v", err)
}

log.Debugf("Reading metal config file %s", path)
log.Debugf("Reading onmetal config file %s", path)
configData, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("failed to read config file: %v", err)
Expand Down

0 comments on commit 0ae085a

Please sign in to comment.