diff --git a/cmd/root.go b/cmd/root.go index 9708ab1..f8352ff 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -7,6 +7,7 @@ import ( "errors" "log/slog" "os" + "strconv" "git.staur.ca/stobbsm/kea-manage/ipv4/reservation" "git.staur.ca/stobbsm/kea-manage/lib/database" @@ -33,15 +34,6 @@ func Execute() { } func init() { - // Here you will define your flags and configuration settings. - // Cobra supports persistent flags, which, if defined here, - // will be global for your application. - - // rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.kea-manage.yaml)") - - // Cobra also supports local flags, which will only run - // when this action is called directly. - rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") } // Manage kea dhcp host reservations in postgresql directly @@ -51,26 +43,33 @@ func init() { func runMain(cmd *cobra.Command, args []string) { reslist := []*reservation.ReservationV4{} - vlist := viper.Get("reservationV4").([]any) + vlist := viper.Get("reservationV4").(map[string]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), - ) - if !r.Validate() { - slog.Error("reservation", slog.Any("validate", r)) + for k, v := range vlist { + v := v.([]any) + ki, err := strconv.ParseInt(k, 10, 0) + if err != nil { + slog.Error("reservation", slog.Any("subnetid", err.Error())) continue } + for _, vi := range v { + vi := vi.(map[string]any) + r := reservation.ReserveV4MacAddr( + vi["identifier"].(string), + vi["ipv4"].(string), + vi["hostname"].(string), + int(ki), + ) + if !r.Validate() { + slog.Error("reservation", slog.Any("validate", r)) + continue + } - reslist = append( - reslist, - r, - ) + reslist = append( + reslist, + r, + ) + } } slog.Debug("reslist", slog.Any("slice", reslist)) @@ -82,7 +81,11 @@ func runMain(cmd *cobra.Command, args []string) { v := v if err := pg.InsertResV4(v); err != nil { if errors.As(err, &database.Exists) { - slog.Info("exists", slog.Any("reservation", *v)) + if err := pg.UpdateResV4(v); err != nil { + slog.Error("update", slog.Any("reservation", *v)) + continue + } + slog.Info("updated", slog.Any("hostname", v.Hostname)) } else { slog.Error(err.Error()) } diff --git a/ipv4/reservation/reservation.go b/ipv4/reservation/reservation.go index ee5301c..57e6375 100644 --- a/ipv4/reservation/reservation.go +++ b/ipv4/reservation/reservation.go @@ -3,29 +3,24 @@ package reservation import ( "log/slog" - "git.staur.ca/stobbsm/kea-manage/lib/types" "git.staur.ca/stobbsm/kea-manage/lib/validators/ipv4" "git.staur.ca/stobbsm/kea-manage/lib/validators/macaddr" - typeValidator "git.staur.ca/stobbsm/kea-manage/lib/validators/types" ) type ReservationV4 struct { - IdentifierType types.Identifier - SubnetID int - Identifier string - Ipv4 string - Hostname string - typeName string + SubnetID int + Identifier string + Ipv4 string + Hostname string + typeName string } -func ReserveV4MacAddr(identifierType string, identifier string, addr string, hostname string, subnet int) *ReservationV4 { +func ReserveV4MacAddr(identifier string, addr string, hostname string, subnet int) *ReservationV4 { r := &ReservationV4{ - typeName: identifierType, - IdentifierType: types.LookupIdentifier(identifierType), - SubnetID: subnet, - Identifier: identifier, - Ipv4: addr, - Hostname: hostname, + SubnetID: subnet, + Identifier: identifier, + Ipv4: addr, + Hostname: hostname, } return r @@ -36,11 +31,6 @@ func ReserveV4MacAddr(identifierType string, identifier string, addr string, hos // This does not validate against the database, just validates that the written configuration // is correct func (r *ReservationV4) Validate() bool { - if !typeValidator.IsValid(r.typeName) { - slog.Error("validate", slog.Any("reservationV4.type", "invalid")) - return false - } - if !ipv4.IsValid(r.Ipv4) { slog.Error("validate", slog.Any("reservationV4.ipv4", r.Ipv4)) return false diff --git a/kea-manage.sample.yaml b/kea-manage.sample.yaml new file mode 100644 index 0000000..63b07cc --- /dev/null +++ b/kea-manage.sample.yaml @@ -0,0 +1,16 @@ +database: + type: pgx + host: postgres.example.net + port: 5432 + user: keauser + password: secure-kea-postgres-password + name: kea-database-name +reservationV4: + : + - identifier: "11:11:11:11:11:11" + hostname: hostname-to-reserve + ipv4: 192.168.111.2 + : + - identifier: "11:11:11:11:11:12" + hostname: another-hostname-to-reserve + ipv4: 192.168.112.2 diff --git a/lib/database/postgres/postgres.go b/lib/database/postgres/postgres.go index edcf58a..9bcd5e1 100644 --- a/lib/database/postgres/postgres.go +++ b/lib/database/postgres/postgres.go @@ -6,6 +6,7 @@ import ( "git.staur.ca/stobbsm/kea-manage/ipv4/reservation" "git.staur.ca/stobbsm/kea-manage/lib/database" + "git.staur.ca/stobbsm/kea-manage/lib/types" "github.com/jackc/pgx/v5" ) @@ -46,7 +47,35 @@ func (p *Postgres) InsertResV4(r *reservation.ReservationV4) error { } defer tx.Rollback(context.Background()) - ct, err := tx.Exec(context.Background(), insertIfNotExists, r.Identifier, r.IdentifierType, r.SubnetID, r.Ipv4, r.Hostname) + ct, err := tx.Exec(context.Background(), insertIfNotExists, r.Identifier, types.HwAddress.Int(), 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 +} + +func (p *Postgres) UpdateResV4(r *reservation.ReservationV4) error { + if err := p.connect(context.Background()); err != nil { + return err + } + defer p.close() + + tx, err := p.conn.Begin(context.Background()) + if err != nil { + return err + } + defer tx.Rollback(context.Background()) + + ct, err := tx.Exec(context.Background(), update, r.Identifier, types.HwAddress.Int(), r.SubnetID, r.Ipv4, r.Hostname) if err != nil { return err } diff --git a/lib/database/postgres/queries.go b/lib/database/postgres/queries.go index 2b04d2a..a79c051 100644 --- a/lib/database/postgres/queries.go +++ b/lib/database/postgres/queries.go @@ -24,3 +24,10 @@ const insertIfNotExists = `INSERT INTO hosts (dhcp_identifier, WHERE dhcp4_subnet_id = $3 AND dhcp_identifier_type = $2 AND dhcp_identifier = DECODE(REPLACE($1, ':', ''), 'hex'));` + +const update = `UPDATE hosts + SET ipv4_address = (SELECT ($4::inet - '0.0.0.0'::inet)), + hostname = $5 + WHERE dhcp_identifier = (SELECT DECODE(REPLACE($1, ':', ''), 'hex')) AND + dhcp_identifier_type = $2 AND + dhcp4_subnet_id = $3;`