Add first version of script to parse leases

This commit is contained in:
Melora Hugues 2024-10-03 15:34:15 +02:00
parent 195cf99a20
commit ac040ac6f7
7 changed files with 212 additions and 0 deletions

3
.gitignore vendored
View file

@ -9,6 +9,9 @@
*.so
*.dylib
# Build files
build/
# Test binary, built with `go test -c`
*.test

8
go.mod Normal file
View file

@ -0,0 +1,8 @@
module git.faercol.me/faercol/dnsmasq-netbox-connector
go 1.23.1
require (
git.faercol.me/faercol/go-healthchecks v0.0.0-20241003132603-97f47d027454 // indirect
github.com/google/uuid v1.6.0 // indirect
)

8
go.sum Normal file
View file

@ -0,0 +1,8 @@
git.faercol.me/faercol/go-healthchecks v0.0.0-20241002141124-24c3d5c5b25a h1:/TuqUOdAUxHQehPOY7zOUMLvQc3W60xXUZWieYiCJyY=
git.faercol.me/faercol/go-healthchecks v0.0.0-20241002141124-24c3d5c5b25a/go.mod h1:d69XefG7I7Y7XyRajFoNpeyWgt89+OFvxIqjlJwia1E=
git.faercol.me/faercol/go-healthchecks v0.0.0-20241003132603-97f47d027454 h1:sWDu4XNMwODSzKjmIZOIWLPhEOhReRUVpo4fEVywp6g=
git.faercol.me/faercol/go-healthchecks v0.0.0-20241003132603-97f47d027454/go.mod h1:d69XefG7I7Y7XyRajFoNpeyWgt89+OFvxIqjlJwia1E=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=

44
internal/config/config.go Normal file
View file

@ -0,0 +1,44 @@
package config
import (
"encoding/json"
"errors"
"fmt"
"os"
)
type HealthchecksConfig struct {
Enabled bool `json:"enabled"`
HostPingAPI string `json:"host_ping_api"`
PingKey string `json:"ping_key"`
}
type Config struct {
HealthchecksConfig `json:"healthchecks"`
LeasesPath string `json:"leases_path"`
}
func defaultConfig() *Config {
return &Config{
HealthchecksConfig: HealthchecksConfig{
Enabled: false,
},
LeasesPath: "/var/lib/misc/dnsmasq.leases",
}
}
func New(path string) (*Config, error) {
fileContent, err := os.ReadFile(path)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return defaultConfig(), nil
}
return nil, fmt.Errorf("failed to read config file %w", err)
}
var c Config
if err := json.Unmarshal(fileContent, &c); err != nil {
return nil, fmt.Errorf("failed to parse config file: %w", err)
}
return &c, nil
}

View file

@ -0,0 +1,61 @@
package dnsmasq
import (
"fmt"
"os"
"strconv"
"strings"
"time"
)
type Lease struct {
ExpireDate time.Time
Mac string
IP string
Hostname string
ClientID string
}
func (l Lease) String() string {
return fmt.Sprintf("[expire on %v]: host %s IP %s (mac %s - clientID %s)", l.ExpireDate, l.Hostname, l.IP, l.Mac, l.ClientID)
}
func parseLeaseLine(rawLine string) (*Lease, error) {
lineValues := strings.Split(rawLine, " ")
if len(lineValues) != 5 {
return nil, fmt.Errorf("unexpected number of values, expected 5, got %d", len(lineValues))
}
expireTimeInt, err := strconv.Atoi(lineValues[0])
if err != nil {
return nil, fmt.Errorf("unexpected unix timestamp value %s", lineValues[0])
}
return &Lease{
ExpireDate: time.Unix(int64(expireTimeInt), 0),
Mac: lineValues[1],
IP: lineValues[2],
Hostname: lineValues[3],
ClientID: lineValues[4],
}, nil
}
func GetLeases(leasesPath string) ([]*Lease, error) {
fileContent, err := os.ReadFile(leasesPath)
if err != nil {
return nil, fmt.Errorf("failed to read leases file: %w", err)
}
leases := []*Lease{}
for i, l := range strings.Split(string(fileContent), "\n") {
if l == "" {
continue
}
lease, err := parseLeaseLine(l)
if err != nil {
return nil, fmt.Errorf("failed to parse line %d: %w", i, err)
}
leases = append(leases, lease)
}
return leases, nil
}

View file

@ -0,0 +1,38 @@
package healthchecks
import (
"context"
"fmt"
"git.faercol.me/faercol/dnsmasq-netbox-connector/internal/config"
go_healthchecks "git.faercol.me/faercol/go-healthchecks"
"github.com/google/uuid"
)
func Start(ctx context.Context, conf config.HealthchecksConfig, clt go_healthchecks.PingClient) (go_healthchecks.Check, error) {
if !conf.Enabled {
return nil, nil
}
runID := uuid.New()
check, err := go_healthchecks.NewSlugCheck(conf.PingKey, "dnsmasq-netbox-connector", true, runID.String())
if err != nil {
return nil, fmt.Errorf("failed to create check: %w", err)
}
return check, clt.ReportStart(ctx, check)
}
func Success(ctx context.Context, conf config.HealthchecksConfig, clt go_healthchecks.PingClient, check go_healthchecks.Check) error {
if !conf.Enabled {
return nil
}
return clt.ReportSuccess(ctx, check)
}
func Failure(ctx context.Context, conf config.HealthchecksConfig, clt go_healthchecks.PingClient, check go_healthchecks.Check, msg string) error {
if !conf.Enabled {
return nil
}
return clt.ReportFailure(ctx, check, msg)
}

50
main.go Normal file
View file

@ -0,0 +1,50 @@
package main
import (
"context"
"fmt"
"os"
"git.faercol.me/faercol/dnsmasq-netbox-connector/internal/config"
"git.faercol.me/faercol/dnsmasq-netbox-connector/internal/dnsmasq"
"git.faercol.me/faercol/dnsmasq-netbox-connector/internal/healthchecks"
gohealthchecks "git.faercol.me/faercol/go-healthchecks"
)
func main() {
configPath := "/etc/dnmasq-netbox/config.json"
if len(os.Args) > 1 {
configPath = os.Args[1]
}
conf, err := config.New(configPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to read configuration: %s\n", err)
os.Exit(1)
}
healtchecksClt := gohealthchecks.NewPingClient(conf.HostPingAPI)
check, err := healthchecks.Start(context.Background(), conf.HealthchecksConfig, healtchecksClt)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to notify process start: %s\n", err)
}
leases, err := dnsmasq.GetLeases(conf.LeasesPath)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to parse leases: %s\n", err)
if err := healthchecks.Failure(context.Background(), conf.HealthchecksConfig, healtchecksClt, check, "failed to parse lease file"); err != nil {
fmt.Fprintf(os.Stderr, "Failed to notify failure: %s\n", err)
}
os.Exit(1)
}
for _, l := range leases {
fmt.Printf("Got lease %s\n", l)
}
if err := healthchecks.Success(context.Background(), conf.HealthchecksConfig, healtchecksClt, check); err != nil {
fmt.Fprintf(os.Stderr, "Failed to notify process success: %s\n", err)
}
}