Go-based network automation with YANG models, gRPC, Ansible, Terraform, and Kubernetes integration. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
99 lines
2.7 KiB
Go
99 lines
2.7 KiB
Go
package vlan
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/vishvananda/netlink"
|
|
"go.uber.org/zap"
|
|
|
|
"github.com/guildhouse-co/kedge/internal/config"
|
|
)
|
|
|
|
// Manager manages VLAN interfaces on the node for underlay mode.
|
|
type Manager struct {
|
|
cfg config.VLANConfig
|
|
log *zap.SugaredLogger
|
|
}
|
|
|
|
// NewManager creates a new VLAN interface manager.
|
|
func NewManager(cfg config.VLANConfig, log *zap.SugaredLogger) *Manager {
|
|
return &Manager{cfg: cfg, log: log}
|
|
}
|
|
|
|
// Run starts the VLAN manager. It ensures all configured VLAN interfaces
|
|
// and bridges exist on the node.
|
|
func (m *Manager) Run(ctx context.Context) error {
|
|
if !m.cfg.Enabled {
|
|
m.log.Info("underlay mode disabled, vlan manager idle")
|
|
<-ctx.Done()
|
|
return nil
|
|
}
|
|
|
|
// Ensure all configured VLANs exist.
|
|
for _, v := range m.cfg.VLANs {
|
|
if err := m.ensureVLAN(v); err != nil {
|
|
m.log.Warnw("failed to ensure VLAN", "vlan_id", v.ID, "error", err)
|
|
}
|
|
}
|
|
|
|
m.log.Infof("vlan manager started, managing %d VLANs", len(m.cfg.VLANs))
|
|
<-ctx.Done()
|
|
return nil
|
|
}
|
|
|
|
// VLANCount returns the number of managed VLAN interfaces.
|
|
func (m *Manager) VLANCount() int {
|
|
return len(m.cfg.VLANs)
|
|
}
|
|
|
|
// ensureVLAN creates a VLAN interface and bridge if they don't already exist.
|
|
func (m *Manager) ensureVLAN(v config.VLANEntry) error {
|
|
bridgeName := fmt.Sprintf("vlan%d", v.ID)
|
|
|
|
// Check if bridge already exists.
|
|
if _, err := netlink.LinkByName(bridgeName); err == nil {
|
|
m.log.Debugw("bridge already exists", "name", bridgeName)
|
|
return nil
|
|
}
|
|
|
|
// Get the parent interface.
|
|
parent, err := netlink.LinkByName(v.ParentInterface)
|
|
if err != nil {
|
|
return fmt.Errorf("parent interface %s not found: %w", v.ParentInterface, err)
|
|
}
|
|
|
|
// Create VLAN sub-interface.
|
|
vlanLink := &netlink.Vlan{
|
|
LinkAttrs: netlink.LinkAttrs{
|
|
Name: fmt.Sprintf("%s.%d", v.ParentInterface, v.ID),
|
|
ParentIndex: parent.Attrs().Index,
|
|
},
|
|
VlanId: v.ID,
|
|
}
|
|
if err := netlink.LinkAdd(vlanLink); err != nil {
|
|
return fmt.Errorf("failed to create VLAN %d: %w", v.ID, err)
|
|
}
|
|
if err := netlink.LinkSetUp(vlanLink); err != nil {
|
|
return fmt.Errorf("failed to bring up VLAN %d: %w", v.ID, err)
|
|
}
|
|
|
|
// Create bridge for the VLAN.
|
|
bridge := &netlink.Bridge{
|
|
LinkAttrs: netlink.LinkAttrs{Name: bridgeName},
|
|
}
|
|
if err := netlink.LinkAdd(bridge); err != nil {
|
|
return fmt.Errorf("failed to create bridge %s: %w", bridgeName, err)
|
|
}
|
|
if err := netlink.LinkSetUp(bridge); err != nil {
|
|
return fmt.Errorf("failed to bring up bridge %s: %w", bridgeName, err)
|
|
}
|
|
|
|
// Add VLAN interface to bridge.
|
|
if err := netlink.LinkSetMaster(vlanLink, bridge); err != nil {
|
|
return fmt.Errorf("failed to add VLAN to bridge: %w", err)
|
|
}
|
|
|
|
m.log.Infow("VLAN created", "id", v.ID, "bridge", bridgeName, "parent", v.ParentInterface)
|
|
return nil
|
|
}
|