Add first version of script to parse leases
This commit is contained in:
parent
195cf99a20
commit
ac040ac6f7
7 changed files with 212 additions and 0 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -9,6 +9,9 @@
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
|
||||||
|
# Build files
|
||||||
|
build/
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|
||||||
|
|
8
go.mod
Normal file
8
go.mod
Normal 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
8
go.sum
Normal 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
44
internal/config/config.go
Normal 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
|
||||||
|
}
|
61
internal/dnsmasq/dnsmasq.go
Normal file
61
internal/dnsmasq/dnsmasq.go
Normal 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
|
||||||
|
}
|
38
internal/healthchecks/healthchecks.go
Normal file
38
internal/healthchecks/healthchecks.go
Normal 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
50
main.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue