From a78f001a41fbc9667e8510ab534dbeb08ba3c21c Mon Sep 17 00:00:00 2001 From: Matthew Stobbs Date: Tue, 24 Sep 2024 14:43:54 -0600 Subject: [PATCH] working configuration for reservations - Make ipv4 reservations in kea by directly writing to the database - uses same configuration file as database configuration under the heading reservationV4 --- .gitignore | 1 + cmd/root.go | 71 ++++++++++-------------- ipv4/reservation/reservation.go | 29 +--------- kea-manage.yaml | 91 ------------------------------- lib/database/database.go | 8 +++ lib/database/postgres/postgres.go | 14 ++++- lib/database/postgres/queries.go | 13 +++++ lib/types/types.go | 19 +++++-- main.go | 9 ++- 9 files changed, 85 insertions(+), 170 deletions(-) create mode 100644 .gitignore delete mode 100644 kea-manage.yaml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c2844d5 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +kea-manage.yaml diff --git a/cmd/root.go b/cmd/root.go index b2c3ed3..6775c67 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -4,14 +4,15 @@ Copyright © 2024 NAME HERE package cmd import ( - "context" + "errors" "log/slog" "os" "git.staur.ca/stobbsm/kea-manage/ipv4/reservation" + "git.staur.ca/stobbsm/kea-manage/lib/database" "git.staur.ca/stobbsm/kea-manage/lib/database/postgres" - "github.com/jackc/pgx/v5" "github.com/spf13/cobra" + "github.com/spf13/viper" ) // rootCmd represents the base command when called without any subcommands @@ -49,53 +50,39 @@ func init() { // This will only work with hw-address identifiers. If I need support for more, I'll add it func runMain(cmd *cobra.Command, args []string) { - reslist := []*reservation.ReservationV4{ - reservation.ReserveV4MacAddr( - "90:e2:ba:19:25:70", - "10.0.255.2", - "mars", - 100, - ), - reservation.ReserveV4MacAddr( - "90:e2:ba:19:25:70", - "10.1.255.2", - "mars", - 101, - ), - reservation.ReserveV4MacAddr( - "90:e2:ba:19:25:70", - "10.2.255.2", - "mars", - 102, - ), - reservation.ReserveV4MacAddr( - "90:e2:ba:19:25:70", - "10.3.255.2", - "mars", - 103, - ), - reservation.ReserveV4MacAddr( - "BC:24:11:E5:C5:AF", - "10.0.0.4", - "ns01", - 100, - ), - reservation.ReserveV4MacAddr( - "BC:24:11:B1:1C:AD", - "10.1.0.6", - "db01", - 101, - ), + reslist := []*reservation.ReservationV4{} + vlist := viper.Get("reservationV4").([]any) + + for _, v := range vlist { + v := v.(map[string]any) + r := reservation.ReserveV4MacAddr( + v["type"].(string), + v["identifier"].(string), + v["ipv4"].(string), + v["hostname"].(string), + v["subnetid"].(int), + ) + reslist = append( + reslist, + r, + ) } - pg := postgres.Open("10.1.0.6", 5432, "kea", "xGq42ZMeMUIWRK", "kea") + slog.Debug("reslist", slog.Any("slice", reslist)) + + dbconf := viper.GetStringMapString("database") + pg := postgres.Open(dbconf["host"], dbconf["port"], dbconf["user"], dbconf["password"], dbconf["name"]) for _, v := range reslist { v := v if err := pg.InsertResV4(v); err != nil { - slog.Error(err.Error()) + if errors.As(err, &database.Exists) { + slog.Info("exists", slog.Any("reservation", *v)) + } else { + slog.Error(err.Error()) + } continue } - slog.Info("inserted reservation", *v) + slog.Info("inserted", slog.Any("reservation", *v)) } } diff --git a/ipv4/reservation/reservation.go b/ipv4/reservation/reservation.go index 4737724..c0a2e72 100644 --- a/ipv4/reservation/reservation.go +++ b/ipv4/reservation/reservation.go @@ -4,31 +4,6 @@ import ( "git.staur.ca/stobbsm/kea-manage/lib/types" ) -const insertHostReservation = `INSERT INTO hosts (dhcp_identifier, - dhcp_identifier_type, - dhcp4_subnet_id, - ipv4_address, - hostname) - VALUES (DECODE(REPLACE($1, ':', ''), 'hex'), $2, $3, (SELECT ($4::inet - '0.0.0.0'::inet)), $5);` - -const getHostIDWithMacAddr = `SELECT host_id - FROM hosts - WHERE dhcp4_subnet_id = $1 - AND dhcp_identifier_type = $2 - AND dhcp_identifier = (DECODE(REPLACE($3, ':', ''), 'hex'));` - -const insertIfNotExists = `INSERT INTO hosts (dhcp_identifier, - dhcp_identifier_type, - dhcp4_subnet_id, - ipv4_address, - hostname) - SELECT DECODE(REPLACE($1, ':', ''), 'hex'), $2, $3, (SELECT ($4::inet - '0.0.0.0'::inet)), $5 - WHERE NOT EXISTS ( - SELECT host_id FROM hosts - WHERE dhcp4_subnet_id = $3 - AND dhcp_identifier_type = $2 - AND dhcp_identifier = DECODE(REPLACE($1, ':', ''), 'hex'));` - type ReservationV4 struct { Type int SubnetID int @@ -37,10 +12,10 @@ type ReservationV4 struct { Hostname string } -func ReserveV4MacAddr(mac string, addr string, hostname string, subnet int) *ReservationV4 { +func ReserveV4MacAddr(rtype string, mac string, addr string, hostname string, subnet int) *ReservationV4 { r := &ReservationV4{ MacAddr: mac, - Type: int(types.MacAddress), + Type: types.Lookup(rtype), SubnetID: subnet, Ipv4: addr, Hostname: hostname, diff --git a/kea-manage.yaml b/kea-manage.yaml deleted file mode 100644 index d92f6a3..0000000 --- a/kea-manage.yaml +++ /dev/null @@ -1,91 +0,0 @@ -database: - type: pgx - host: 10.1.0.6 - port: 5432 - user: kea - password: xGq42ZMeMUIWRK - database: kea -reservationV4: - - type: macaddr - identifer: "90:e2:ba:19:25:70" - subnetid: 100 - hostname: mars - ipv4: 10.0.255.2 - - - type: macaddr - identifer: "90:e2:ba:19:25:70" - subnetid: 101 - hostname: mars - ipv4: 10.1.255.2 - - - type: macaddr - identifer: "90:e2:ba:19:25:70" - subnetid: 102 - hostname: mars - ipv4: 10.2.255.2 - - - type: macaddr - identifer: "90:e2:ba:19:25:70" - subnetid: 103 - hostname: mars - ipv4: 10.3.255.2 - - - type: macaddr - identifer: "BC:24:11:E5:C5:AF" - subnetid: 100 - hostname: ns01 - ipv4: 10.0.0.4 - - - type: macaddr - identifer: "BC:24:11:2F:4B:23" - subnetid: 101 - hostname: ns01 - ipv4: 10.1.0.4 - - - type: macaddr - identifer: "BC:24:11:99:68:40" - subnetid: 102 - hostname: ns01 - ipv4: 10.2.0.4 - - - type: macaddr - identifer: "BC:24:11:79:BF:B4" - subnetid: 103 - hostname: ns01 - ipv4: 10.3.0.4 - - - type: macaddr - identifer: "BC:24:11:B1:1C:AD" - subnetid: 101 - hostname: db01 - ipv4: 10.1.0.6 - - - type: macaddr - identifier: "BC:21:11:52:FA:73" - subnetid: 100 - hostname: dhcp01 - ipv4: 10.0.0.5 - - - type: macaddr - identifier: "BC:21:11:E6:45:0B" - subnetid: 101 - hostname: dhcp01 - ipv4: 10.1.0.5 - - - type: macaddr - identifier: "BC:24:11:D2:6D:38" - subnetid: 102 - hostname: dhcp01 - ipv4: 10.2.0.5 - - - type: macaddr - identifier: "BC:24:11:A2:C5:94" - subnetid: 103 - hostname: dhcp01 - ipv4: 10.3.0.5 - - - type: macaddr - identifier: "BC:24:11:B8:BC:CF" - subnetid: 100 - hostname: jellyfin - ipv4: 10.0.0.20 diff --git a/lib/database/database.go b/lib/database/database.go index 2f18446..9f829f5 100644 --- a/lib/database/database.go +++ b/lib/database/database.go @@ -7,3 +7,11 @@ import ( type StoreV4 interface { InsertResV4(*reservation.ReservationV4) error } + +type ExistsError struct{} + +func (m *ExistsError) Error() string { + return "exists" +} + +var Exists = &ExistsError{} diff --git a/lib/database/postgres/postgres.go b/lib/database/postgres/postgres.go index d173583..610d239 100644 --- a/lib/database/postgres/postgres.go +++ b/lib/database/postgres/postgres.go @@ -5,6 +5,7 @@ import ( "fmt" "git.staur.ca/stobbsm/kea-manage/ipv4/reservation" + "git.staur.ca/stobbsm/kea-manage/lib/database" "github.com/jackc/pgx/v5" ) @@ -15,9 +16,9 @@ type Postgres struct { // Open receives the host, port, username, password and database name of a postgresql // database, and manages connections to it. -func Open(host string, port int, user string, password string, name string) *Postgres { +func Open(host, port, user, password, name string) *Postgres { var p = &Postgres{ - connString: fmt.Sprintf("postgres://%s:%s@%s:%d/%s", user, password, host, port, name), + connString: fmt.Sprintf("postgres://%s:%s@%s:%s/%s", user, password, host, port, name), } return p } @@ -32,6 +33,7 @@ func (p *Postgres) close() { p.conn.Close(context.Background()) } +// InsertResV4 adds IPv4 reservations to the kea database func (p *Postgres) InsertResV4(r *reservation.ReservationV4) error { if err := p.connect(context.Background()); err != nil { return err @@ -44,12 +46,18 @@ func (p *Postgres) InsertResV4(r *reservation.ReservationV4) error { } defer tx.Rollback(context.Background()) - if _, err = tx.Exec(context.Background(), insertIfNotExists, r.MacAddr, r.Type, r.SubnetID, r.Ipv4, r.Hostname); err != nil { + ct, err := tx.Exec(context.Background(), insertIfNotExists, r.MacAddr, r.Type, r.SubnetID, r.Ipv4, r.Hostname) + if err != nil { return err } if err = tx.Commit(context.Background()); err != nil { return err } + + if ct.RowsAffected() == 0 { + return database.Exists + } + return nil } diff --git a/lib/database/postgres/queries.go b/lib/database/postgres/queries.go index 51ad283..2b04d2a 100644 --- a/lib/database/postgres/queries.go +++ b/lib/database/postgres/queries.go @@ -1,5 +1,18 @@ package postgres +const insertHostReservation = `INSERT INTO hosts (dhcp_identifier, + dhcp_identifier_type, + dhcp4_subnet_id, + ipv4_address, + hostname) + VALUES (DECODE(REPLACE($1, ':', ''), 'hex'), $2, $3, (SELECT ($4::inet - '0.0.0.0'::inet)), $5);` + +const getHostIDWithMacAddr = `SELECT host_id + FROM hosts + WHERE dhcp4_subnet_id = $1 + AND dhcp_identifier_type = $2 + AND dhcp_identifier = (DECODE(REPLACE($3, ':', ''), 'hex'));` + const insertIfNotExists = `INSERT INTO hosts (dhcp_identifier, dhcp_identifier_type, dhcp4_subnet_id, diff --git a/lib/types/types.go b/lib/types/types.go index f9cdfbb..b6c1a3e 100644 --- a/lib/types/types.go +++ b/lib/types/types.go @@ -1,8 +1,17 @@ package types -type IdentifierType int +import "strings" -const ( - HWAddress = 0 - MacAddress = 0 -) +var types = map[string]int{ + "macaddr": 0, + "hwaddr": 0, + "invalid": 255, +} + +func Lookup(rtype string) int { + rtype = strings.ToLower(rtype) + if t, ok := types[rtype]; ok == true { + return t + } + return types["invalid"] +} diff --git a/main.go b/main.go index 86a88db..47324f5 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "log/slog" "os" "git.staur.ca/stobbsm/kea-manage/cmd" @@ -8,10 +9,14 @@ import ( ) func main() { - + slog.SetLogLoggerLevel(slog.LevelDebug) viper.SetConfigName("kea-manage") viper.AddConfigPath("/etc") viper.AddConfigPath(os.Getenv("HOME") + "/.config/kea-manage") - + viper.AddConfigPath(".") + if err := viper.ReadInConfig(); err != nil { + slog.Error("error while reading configuration", err) + os.Exit(1) + } cmd.Execute() }