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
|
||||
*.dylib
|
||||
|
||||
# Build files
|
||||
build/
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.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