wip v1.0.0
This commit is contained in:
90
pkg/config/config.go
Normal file
90
pkg/config/config.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Global Global `yaml:"global"`
|
||||
VRRP []VRRPInstance `yaml:"vrrp_instances"`
|
||||
Health []HealthChecker `yaml:"health_checkers"`
|
||||
}
|
||||
|
||||
type Global struct {
|
||||
RouterID string `yaml:"router_id"`
|
||||
NotificationMail string `yaml:"notification_email"`
|
||||
}
|
||||
|
||||
type VRRPInstance struct {
|
||||
Name string `yaml:"name"`
|
||||
Interface string `yaml:"interface"`
|
||||
State string `yaml:"state"`
|
||||
VirtualRouterID int `yaml:"virtual_router_id"`
|
||||
Priority int `yaml:"priority"`
|
||||
VirtualIPs []string `yaml:"virtual_ips"`
|
||||
AdvertInterval int `yaml:"advert_interval"`
|
||||
AuthType string `yaml:"auth_type"`
|
||||
AuthPass string `yaml:"auth_pass"`
|
||||
NotifyMaster string `yaml:"notify_master"`
|
||||
NotifyBackup string `yaml:"notify_backup"`
|
||||
NotifyFault string `yaml:"notify_fault"`
|
||||
TrackScripts []string `yaml:"track_scripts"`
|
||||
}
|
||||
|
||||
type HealthChecker struct {
|
||||
Name string `yaml:"name"`
|
||||
Type string `yaml:"type"`
|
||||
Interval time.Duration `yaml:"interval"`
|
||||
Timeout time.Duration `yaml:"timeout"`
|
||||
Rise int `yaml:"rise"`
|
||||
Fall int `yaml:"fall"`
|
||||
Config interface{} `yaml:"config"`
|
||||
}
|
||||
|
||||
func Load(path string) (*Config, error) {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read config file: %w", err)
|
||||
}
|
||||
|
||||
var cfg Config
|
||||
if err := yaml.Unmarshal(data, &cfg); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse config file: %w", err)
|
||||
}
|
||||
|
||||
if err := validate(&cfg); err != nil {
|
||||
return nil, fmt.Errorf("invalid configuration: %w", err)
|
||||
}
|
||||
|
||||
return &cfg, nil
|
||||
}
|
||||
|
||||
func validate(cfg *Config) error {
|
||||
if cfg.Global.RouterID == "" {
|
||||
return fmt.Errorf("global.router_id is required")
|
||||
}
|
||||
|
||||
for i, vrrp := range cfg.VRRP {
|
||||
if vrrp.Name == "" {
|
||||
return fmt.Errorf("vrrp_instances[%d].name is required", i)
|
||||
}
|
||||
if vrrp.Interface == "" {
|
||||
return fmt.Errorf("vrrp_instances[%d].interface is required", i)
|
||||
}
|
||||
if vrrp.VirtualRouterID < 1 || vrrp.VirtualRouterID > 255 {
|
||||
return fmt.Errorf("vrrp_instances[%d].virtual_router_id must be between 1 and 255", i)
|
||||
}
|
||||
if vrrp.Priority < 1 || vrrp.Priority > 255 {
|
||||
return fmt.Errorf("vrrp_instances[%d].priority must be between 1 and 255", i)
|
||||
}
|
||||
if len(vrrp.VirtualIPs) == 0 {
|
||||
return fmt.Errorf("vrrp_instances[%d].virtual_ips cannot be empty", i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
44
pkg/logger/logger.go
Normal file
44
pkg/logger/logger.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Logger struct {
|
||||
debug bool
|
||||
logger *log.Logger
|
||||
}
|
||||
|
||||
func New(debug bool) *Logger {
|
||||
return &Logger{
|
||||
debug: debug,
|
||||
logger: log.New(os.Stdout, "", 0),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Info(format string, args ...interface{}) {
|
||||
l.log("INFO", format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(format string, args ...interface{}) {
|
||||
l.log("ERROR", format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(format string, args ...interface{}) {
|
||||
if l.debug {
|
||||
l.log("DEBUG", format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(format string, args ...interface{}) {
|
||||
l.log("WARN", format, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) log(level string, format string, args ...interface{}) {
|
||||
timestamp := time.Now().Format("2006-01-02 15:04:05")
|
||||
message := fmt.Sprintf(format, args...)
|
||||
l.logger.Printf("[%s] %s: %s", timestamp, level, message)
|
||||
}
|
||||
81
pkg/netif/interface.go
Normal file
81
pkg/netif/interface.go
Normal file
@@ -0,0 +1,81 @@
|
||||
package netif
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/vishvananda/netlink"
|
||||
)
|
||||
|
||||
type Interface struct {
|
||||
Name string
|
||||
Index int
|
||||
Link netlink.Link
|
||||
}
|
||||
|
||||
func GetInterface(name string) (*Interface, error) {
|
||||
link, err := netlink.LinkByName(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to find interface %s: %w", name, err)
|
||||
}
|
||||
|
||||
return &Interface{
|
||||
Name: name,
|
||||
Index: link.Attrs().Index,
|
||||
Link: link,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (iface *Interface) AddIP(ipCIDR string) error {
|
||||
addr, err := netlink.ParseAddr(ipCIDR)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid IP address %s: %w", ipCIDR, err)
|
||||
}
|
||||
|
||||
if err := netlink.AddrAdd(iface.Link, addr); err != nil {
|
||||
return fmt.Errorf("failed to add IP %s to %s: %w", ipCIDR, iface.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iface *Interface) DeleteIP(ipCIDR string) error {
|
||||
addr, err := netlink.ParseAddr(ipCIDR)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid IP address %s: %w", ipCIDR, err)
|
||||
}
|
||||
|
||||
if err := netlink.AddrDel(iface.Link, addr); err != nil {
|
||||
return fmt.Errorf("failed to delete IP %s from %s: %w", ipCIDR, iface.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (iface *Interface) HasIP(ipCIDR string) (bool, error) {
|
||||
targetAddr, err := netlink.ParseAddr(ipCIDR)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invalid IP address %s: %w", ipCIDR, err)
|
||||
}
|
||||
|
||||
addrs, err := netlink.AddrList(iface.Link, 0)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to list addresses on %s: %w", iface.Name, err)
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
if addr.IPNet.String() == targetAddr.IPNet.String() {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (iface *Interface) GetHardwareAddr() (net.HardwareAddr, error) {
|
||||
return iface.Link.Attrs().HardwareAddr, nil
|
||||
}
|
||||
|
||||
func (iface *Interface) IsUp() bool {
|
||||
return iface.Link.Attrs().Flags&net.FlagUp != 0
|
||||
}
|
||||
Reference in New Issue
Block a user