added sample config

- simplified configuration file
- simplified to only use ipv4 and macaddresses for now
- added update query
- if insert fails with already exists error, try to update instead
This commit is contained in:
Matthew Stobbs 2024-12-02 12:20:39 -07:00
parent 8bcb14a8af
commit e68723738b
5 changed files with 92 additions and 47 deletions

View File

@ -7,6 +7,7 @@ import (
"errors" "errors"
"log/slog" "log/slog"
"os" "os"
"strconv"
"git.staur.ca/stobbsm/kea-manage/ipv4/reservation" "git.staur.ca/stobbsm/kea-manage/ipv4/reservation"
"git.staur.ca/stobbsm/kea-manage/lib/database" "git.staur.ca/stobbsm/kea-manage/lib/database"
@ -33,15 +34,6 @@ func Execute() {
} }
func init() { 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 // Manage kea dhcp host reservations in postgresql directly
@ -51,26 +43,33 @@ func init() {
func runMain(cmd *cobra.Command, args []string) { func runMain(cmd *cobra.Command, args []string) {
reslist := []*reservation.ReservationV4{} reslist := []*reservation.ReservationV4{}
vlist := viper.Get("reservationV4").([]any) vlist := viper.Get("reservationV4").(map[string]any)
for _, v := range vlist { for k, v := range vlist {
v := v.(map[string]any) v := v.([]any)
r := reservation.ReserveV4MacAddr( ki, err := strconv.ParseInt(k, 10, 0)
v["type"].(string), if err != nil {
v["identifier"].(string), slog.Error("reservation", slog.Any("subnetid", err.Error()))
v["ipv4"].(string),
v["hostname"].(string),
v["subnetid"].(int),
)
if !r.Validate() {
slog.Error("reservation", slog.Any("validate", r))
continue 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 = append(
reslist, reslist,
r, r,
) )
}
} }
slog.Debug("reslist", slog.Any("slice", reslist)) slog.Debug("reslist", slog.Any("slice", reslist))
@ -82,7 +81,11 @@ func runMain(cmd *cobra.Command, args []string) {
v := v v := v
if err := pg.InsertResV4(v); err != nil { if err := pg.InsertResV4(v); err != nil {
if errors.As(err, &database.Exists) { 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 { } else {
slog.Error(err.Error()) slog.Error(err.Error())
} }

View File

@ -3,29 +3,24 @@ package reservation
import ( import (
"log/slog" "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/ipv4"
"git.staur.ca/stobbsm/kea-manage/lib/validators/macaddr" "git.staur.ca/stobbsm/kea-manage/lib/validators/macaddr"
typeValidator "git.staur.ca/stobbsm/kea-manage/lib/validators/types"
) )
type ReservationV4 struct { type ReservationV4 struct {
IdentifierType types.Identifier SubnetID int
SubnetID int Identifier string
Identifier string Ipv4 string
Ipv4 string Hostname string
Hostname string typeName 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{ r := &ReservationV4{
typeName: identifierType, SubnetID: subnet,
IdentifierType: types.LookupIdentifier(identifierType), Identifier: identifier,
SubnetID: subnet, Ipv4: addr,
Identifier: identifier, Hostname: hostname,
Ipv4: addr,
Hostname: hostname,
} }
return r 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 // This does not validate against the database, just validates that the written configuration
// is correct // is correct
func (r *ReservationV4) Validate() bool { 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) { if !ipv4.IsValid(r.Ipv4) {
slog.Error("validate", slog.Any("reservationV4.ipv4", r.Ipv4)) slog.Error("validate", slog.Any("reservationV4.ipv4", r.Ipv4))
return false return false

16
kea-manage.sample.yaml Normal file
View File

@ -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:
<subnetid1>:
- identifier: "11:11:11:11:11:11"
hostname: hostname-to-reserve
ipv4: 192.168.111.2
<subnetid2>:
- identifier: "11:11:11:11:11:12"
hostname: another-hostname-to-reserve
ipv4: 192.168.112.2

View File

@ -6,6 +6,7 @@ import (
"git.staur.ca/stobbsm/kea-manage/ipv4/reservation" "git.staur.ca/stobbsm/kea-manage/ipv4/reservation"
"git.staur.ca/stobbsm/kea-manage/lib/database" "git.staur.ca/stobbsm/kea-manage/lib/database"
"git.staur.ca/stobbsm/kea-manage/lib/types"
"github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5"
) )
@ -46,7 +47,35 @@ func (p *Postgres) InsertResV4(r *reservation.ReservationV4) error {
} }
defer tx.Rollback(context.Background()) 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 { if err != nil {
return err return err
} }

View File

@ -24,3 +24,10 @@ const insertIfNotExists = `INSERT INTO hosts (dhcp_identifier,
WHERE dhcp4_subnet_id = $3 WHERE dhcp4_subnet_id = $3
AND dhcp_identifier_type = $2 AND dhcp_identifier_type = $2
AND dhcp_identifier = DECODE(REPLACE($1, ':', ''), 'hex'));` 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;`