Anpassungen und Persistenz
All checks were successful
build-binaries / build (, amd64, linux) (push) Has been skipped
build-binaries / build (, arm, 7, linux) (push) Has been skipped
build-binaries / build (.exe, amd64, windows) (push) Has been skipped
build-binaries / build (, arm64, linux) (push) Has been skipped
build-binaries / release (push) Has been skipped
release-tag / release-image (push) Successful in 3m28s
All checks were successful
build-binaries / build (, amd64, linux) (push) Has been skipped
build-binaries / build (, arm, 7, linux) (push) Has been skipped
build-binaries / build (.exe, amd64, windows) (push) Has been skipped
build-binaries / build (, arm64, linux) (push) Has been skipped
build-binaries / release (push) Has been skipped
release-tag / release-image (push) Successful in 3m28s
This commit is contained in:
168
main.go
168
main.go
@@ -30,6 +30,7 @@ import (
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -54,6 +55,11 @@ var (
|
||||
dhcpScope string
|
||||
dhcpNamePrefix string
|
||||
dhcpDomain string
|
||||
|
||||
// Persistenter Teil 👇
|
||||
DuidHostnameList []payload
|
||||
duidFile string
|
||||
duidMu sync.RWMutex
|
||||
)
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -74,8 +80,6 @@ type rangeData struct {
|
||||
HaveResult bool
|
||||
}
|
||||
|
||||
var DuidHostnameList []payload
|
||||
|
||||
type payload struct {
|
||||
Hostname string `json:"hostname"`
|
||||
DUIDs []string `json:"duids"`
|
||||
@@ -94,6 +98,98 @@ type payloadHelper struct {
|
||||
Dhcp6Scope string
|
||||
}
|
||||
|
||||
func loadDuidList() {
|
||||
duidMu.Lock()
|
||||
defer duidMu.Unlock()
|
||||
|
||||
f, err := os.Open(duidFile)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
DuidHostnameList = nil
|
||||
return
|
||||
}
|
||||
log.Printf("duid-load: %v", err)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var tmp []payload
|
||||
if err := json.NewDecoder(f).Decode(&tmp); err != nil {
|
||||
log.Printf("duid-load: decode: %v", err)
|
||||
return
|
||||
}
|
||||
DuidHostnameList = tmp
|
||||
log.Printf("duid-load: %d Einträge geladen", len(DuidHostnameList))
|
||||
}
|
||||
|
||||
func saveDuidList() {
|
||||
duidMu.RLock()
|
||||
data, err := json.MarshalIndent(DuidHostnameList, "", " ")
|
||||
duidMu.RUnlock()
|
||||
if err != nil {
|
||||
log.Printf("duid-save: marshal: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
tmpFile := duidFile + ".tmp"
|
||||
if err := os.WriteFile(tmpFile, data, 0o644); err != nil {
|
||||
log.Printf("duid-save: write tmp: %v", err)
|
||||
return
|
||||
}
|
||||
if err := os.Rename(tmpFile, duidFile); err != nil {
|
||||
log.Printf("duid-save: rename: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func uniqueStrings(in []string) []string {
|
||||
m := make(map[string]struct{}, len(in))
|
||||
var out []string
|
||||
for _, s := range in {
|
||||
if s == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := m[s]; ok {
|
||||
continue
|
||||
}
|
||||
m[s] = struct{}{}
|
||||
out = append(out, s)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func uniqueU32(in []uint32) []uint32 {
|
||||
m := make(map[uint32]struct{}, len(in))
|
||||
var out []uint32
|
||||
for _, v := range in {
|
||||
if _, ok := m[v]; ok {
|
||||
continue
|
||||
}
|
||||
m[v] = struct{}{}
|
||||
out = append(out, v)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// fügt neu ein ODER aktualisiert bestehenden Host
|
||||
func upsertPayload(p payload) {
|
||||
duidMu.Lock()
|
||||
defer duidMu.Unlock()
|
||||
|
||||
for i := range DuidHostnameList {
|
||||
if strings.EqualFold(DuidHostnameList[i].Hostname, p.Hostname) {
|
||||
// merge
|
||||
DuidHostnameList[i].DUIDs = uniqueStrings(append(DuidHostnameList[i].DUIDs, p.DUIDs...))
|
||||
DuidHostnameList[i].IAIDs = uniqueU32(append(DuidHostnameList[i].IAIDs, p.IAIDs...))
|
||||
return
|
||||
}
|
||||
}
|
||||
// neu
|
||||
p.DUIDs = uniqueStrings(p.DUIDs)
|
||||
p.IAIDs = uniqueU32(p.IAIDs)
|
||||
DuidHostnameList = append(DuidHostnameList, p)
|
||||
}
|
||||
|
||||
func octetsRaw(ip string) ([]string, error) {
|
||||
parts := strings.Split(ip, ".")
|
||||
if len(parts) != 4 {
|
||||
@@ -118,19 +214,34 @@ func enabled(k string, def bool) bool {
|
||||
}
|
||||
|
||||
func DhcpHelperFunc(xHostname string, xDUIDs []string, xIAIDs []uint32) []payloadHelper {
|
||||
/*IPv4*/
|
||||
Ipv4Octets, _ := octetsRaw(defaultIP)
|
||||
Ipv4Octets, _ := octetsRaw(defaultIP) // evtl. pageIP nehmen?
|
||||
|
||||
rHostname := []rune(xHostname)
|
||||
qDUID := xDUIDs[0]
|
||||
if len(rHostname) < 6 { // 2+4
|
||||
// fallback: nimm letzte zwei Oktette 0.0
|
||||
qCalculatedIPv4 := Ipv4Octets[0] + "." + Ipv4Octets[1] + ".0.0"
|
||||
qCalculatedIPv6, _ := embedIPv4(qCalculatedIPv4)
|
||||
return []payloadHelper{{
|
||||
Hostname: xHostname,
|
||||
DUID: firstOrEmpty(xDUIDs),
|
||||
CalculatedIPv4: qCalculatedIPv4,
|
||||
CalculatedIPv6: qCalculatedIPv6,
|
||||
Dhcp4Scope: dhcpScope,
|
||||
Dhcp6Scope: ulaPrefix,
|
||||
DomainName: dhcpDomain,
|
||||
DhcpServer: dhcpServer,
|
||||
IAID: "0",
|
||||
}}
|
||||
}
|
||||
|
||||
qDUID := firstOrEmpty(xDUIDs)
|
||||
qSegment1 := string(rHostname[2:4])
|
||||
qSegment2 := string(rHostname[4:])
|
||||
|
||||
qCalculatedIPv4 := Ipv4Octets[0] + "." + Ipv4Octets[1] + "." + qSegment1 + "." + qSegment2
|
||||
qCalculatedIPv6, _ := embedIPv4(qCalculatedIPv4)
|
||||
var res []payloadHelper
|
||||
for _, t := range xIAIDs {
|
||||
r := payloadHelper{
|
||||
res = append(res, payloadHelper{
|
||||
Hostname: xHostname,
|
||||
DUID: qDUID,
|
||||
CalculatedIPv4: qCalculatedIPv4,
|
||||
@@ -140,17 +251,28 @@ func DhcpHelperFunc(xHostname string, xDUIDs []string, xIAIDs []uint32) []payloa
|
||||
DomainName: dhcpDomain,
|
||||
DhcpServer: dhcpServer,
|
||||
IAID: fmt.Sprintf("%d", t),
|
||||
}
|
||||
res = append(res, r)
|
||||
})
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func firstOrEmpty(xs []string) string {
|
||||
if len(xs) == 0 {
|
||||
return ""
|
||||
}
|
||||
return xs[0]
|
||||
}
|
||||
|
||||
func getDhcp() []payloadHelper {
|
||||
duidMu.RLock()
|
||||
defer duidMu.RUnlock()
|
||||
|
||||
cp := make([]payload, len(DuidHostnameList))
|
||||
copy(cp, DuidHostnameList)
|
||||
sortByHostname(cp)
|
||||
|
||||
var result []payloadHelper
|
||||
sortByHostname(DuidHostnameList)
|
||||
for _, b := range DuidHostnameList {
|
||||
for _, b := range cp {
|
||||
result = append(result, DhcpHelperFunc(b.Hostname, b.DUIDs, b.IAIDs)...)
|
||||
}
|
||||
return result
|
||||
@@ -166,12 +288,20 @@ func register(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "ungültiges JSON", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if p.Hostname == "" {
|
||||
http.Error(w, "hostname fehlt", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if len(p.DUIDs) == 0 {
|
||||
// optional: nicht hart abbrechen – aber wenigstens loggen
|
||||
log.Printf("register: Host %s ohne DUIDs", p.Hostname)
|
||||
}
|
||||
|
||||
// --- hier kannst du speichern, weiterverarbeiten, loggen … ---
|
||||
log.Printf("neuer Client: %s → DUIDs=%v", p.Hostname, p.DUIDs)
|
||||
DuidHostnameList = append(DuidHostnameList, p)
|
||||
upsertPayload(p)
|
||||
saveDuidList() // 👈 direkt nach Update persistieren
|
||||
|
||||
w.WriteHeader(http.StatusNoContent) // 204
|
||||
log.Printf("client registriert/aktualisiert: %s → DUIDs=%v IAIDs=%v", p.Hostname, p.DUIDs, p.IAIDs)
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func getdhcp6(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -194,6 +324,10 @@ func sortByHostname(p []payload) {
|
||||
func main() {
|
||||
initConfigAndTemplates()
|
||||
|
||||
// Persistenz initialisieren
|
||||
duidFile = getenv("DUID_DB_FILE", "duids.json")
|
||||
loadDuidList()
|
||||
|
||||
http.HandleFunc("/", handleSingle)
|
||||
http.HandleFunc("/convert", handleSingleConvert)
|
||||
http.HandleFunc("/range", handleRange)
|
||||
@@ -466,6 +600,7 @@ var rangePageHTML = `<!DOCTYPE html>
|
||||
<table>
|
||||
<tr><th>IPv4</th><th>IPv6</th><th>DHCP-IPv4</th><th>DHCP-IPv6</th></tr>
|
||||
{{range .Rows}}<tr><td>{{.IPv4}}</td><td>{{.IPv6}}</td><td>netsh DHCP Server {{$.DhcpServer}} Scope {{$.DhcpScope}} Add reservedip {{.IPv4}} "{{.Name}}.{{$.DhcpDomain}}" "" "DHCP"</td><td>---</td></tr>{{end}}
|
||||
//Add-DhcpServerv4Reservation -ComputerName $Srv -ScopeId $Prefix -IPAddress IPv6Address -ClientId $l.ClientDuid -Name $l.HostName -Description "Auto-reserved after rollout" -Type Dhcp
|
||||
</table>
|
||||
{{end}}
|
||||
{{if .Error}}<p style="color:#b00">Fehler: {{.Error}}</p>{{end}}
|
||||
@@ -492,4 +627,5 @@ var rangeDHCP6HTML = `<!DOCTYPE html>
|
||||
</table>
|
||||
</body></html>`
|
||||
|
||||
//Add-DhcpServerv4Reservation -ComputerName $Srv -ScopeId $Prefix -IPAddress IPv6Address -ClientId $l.ClientDuid -Name $l.HostName -Description "Auto-reserved after rollout" -Type Dhcp
|
||||
//Add-DhcpServerv6Reservation -ComputerName $Srv -Prefix $Prefix -IPAddress IPv6Address -ClientDuid $l.ClientDuid -Iaid $l.Iaid -Name $l.HostName -Description "Auto-reserved after rollout"
|
||||
|
||||
Reference in New Issue
Block a user