working on adding lease handouts

This commit is contained in:
2023-05-06 05:21:27 +00:00
parent c475287fb1
commit a53d392ed8
15 changed files with 477 additions and 76 deletions

View File

@@ -15,7 +15,7 @@ type LeaseInfo struct {
Expires time.Time `yaml:"expires"`
}
type sortableLease []LeaseInfo
type sortableLease []*LeaseInfo
func (a sortableLease) Len() int { return len(a) }
func (a sortableLease) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
@@ -33,14 +33,14 @@ func (li *LeaseInfo) Expire() {
li.Expires = time.Time{}
}
func SortLeases(leases []LeaseInfo) []LeaseInfo {
func SortLeases(leases []*LeaseInfo) []*LeaseInfo {
sortable := sortableLease(leases)
sort.Sort(sortable)
return []LeaseInfo(sortable)
return []*LeaseInfo(sortable)
}
func (lease LeaseInfo) Reset() LeaseInfo {
func (lease *LeaseInfo) Reset() *LeaseInfo {
lease.Expires = time.Time{}
lease.HardwareAddress = nil
return lease

View File

@@ -1,58 +1,141 @@
package iptools
import (
"fmt"
"net/netip"
"sync"
"time"
"git.wntrmute.dev/kyle/goutils/assert"
)
const DefaultExpiry = 168 * time.Hour
type Pool struct {
Name string `yaml:"name"`
Range *Range `yaml:"range"`
Expiry time.Duration `yaml:"expiry"`
Available []LeaseInfo `yaml:"available"`
Active map[netip.Addr]LeaseInfo `yaml:"active"`
Limbo map[netip.Addr]LeaseInfo `yaml:"limbo"` // leases that are currently being offered
Name string `yaml:"name"`
Range *Range `yaml:"range"`
Expiry time.Duration `yaml:"expiry"`
Available []*LeaseInfo `yaml:"available"`
Active map[netip.Addr]*LeaseInfo `yaml:"active"`
Limbo map[netip.Addr]*LeaseInfo `yaml:"limbo"` // leases that are currently being offered
NoHostName bool `yaml:"no_hostname"` // don't set the hostname
mtx *sync.Mutex
}
func NewPool(name string, exp time.Duration, r *Range) (*Pool, error) {
func (p *Pool) lock() {
if p.lock == nil {
p.mtx = &sync.Mutex{}
}
p.mtx.Lock()
}
func (p *Pool) unlock() {
if p.mtx == nil {
panic("pool: attempted to unlock uninitialized mutex")
}
p.mtx.Unlock()
}
func NewPool(name string, r *Range) (*Pool, error) {
if r.Expiry == 0 {
r.Expiry = DefaultExpiry
}
p := &Pool{
Name: name,
Expiry: exp,
Available: enumerateRange(name, r, true),
Name: name,
Expiry: r.Expiry,
NoHostName: r.NoHostName,
Available: enumerateRange(name, r, true),
}
return p, nil
}
func (p *Pool) Sort() {
func (p *Pool) sort() {
p.Available = SortLeases(p.Available)
}
func (p *Pool) Sort() {
p.lock()
defer p.unlock()
p.sort()
}
func (p *Pool) IsAddressAvailable() bool {
return len(p.Available) > 0
}
func (p *Pool) Peek(t time.Time, waitFor time.Duration) netip.Addr {
// Peek returns the first available address from the pool and moves it
// from available to limbo. When the client is given the address, Accept
// should be called on the address to move it from limbo to active.
func (p *Pool) Peek(t time.Time, waitFor time.Duration) *LeaseInfo {
p.lock()
defer p.unlock()
li := p.peek(t, waitFor)
return li
}
func (p *Pool) peek(t time.Time, waitFor time.Duration) *LeaseInfo {
if len(p.Available) == 0 {
return nil
}
lease := p.Available[0]
p.Available = p.Available[1:]
lease.ResetExpiry(t, waitFor)
p.Limbo[lease.Addr] = lease
return lease.Addr
return lease
}
func (p *Pool) accept(addr netip.Addr) error {
assert.Bool(p.Active[addr] == nil, fmt.Sprintf("limbo address %s is already active: %#v", addr, *p.Active[addr]))
p.Active[addr] = p.Limbo[addr]
delete(p.Limbo, addr)
return nil
}
// Accept will move an address out limbo and mark it as active.
func (p *Pool) Accept(addr netip.Addr, t time.Time) error {
p.lock()
defer p.unlock()
p.update(t)
p.accept(addr)
return nil
}
// Update checks expirations on leases, moving any expired leases back
// to the available pool and removing any limbo leases that are expired.
func (p *Pool) Update(t time.Time) {
p.lock()
defer p.unlock()
p.update(t)
}
func (p *Pool) update(t time.Time) {
evictedHosts := []netip.Addr{}
for k, v := range p.Active {
if v.IsExpired(t) {
delete(p.Active, k)
evictedHosts = append(evictedHosts, k)
v = v.Reset()
p.Available = append(p.Available, v)
}
}
for _, ip := range evictedHosts {
delete(p.Active, ip)
}
for k, v := range p.Limbo {
if v.IsExpired(t) {
delete(p.Limbo, k)

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"net/netip"
"testing"
"time"
)
var (
@@ -18,7 +17,7 @@ func TestBasicPool(t *testing.T) {
End: poolTestIP2,
}
p, err := NewPool("cluster", 24*time.Hour, r)
p, err := NewPool("cluster", r)
if err != nil {
t.Fatal(err)
}

View File

@@ -3,6 +3,7 @@ package iptools
import (
"fmt"
"net/netip"
"time"
)
const (
@@ -10,9 +11,11 @@ const (
)
type Range struct {
Start netip.Addr `yaml:"start"`
End netip.Addr `yaml:"end"`
Network netip.Prefix `yaml:"network"`
Start netip.Addr `yaml:"start"`
End netip.Addr `yaml:"end"`
Network netip.Prefix `yaml:"network"`
Expiry time.Duration `yaml:"expiry"`
NoHostName bool `yaml:"no_hostname"` // don't set the hostname
}
func (r *Range) Validate() error {
@@ -54,7 +57,7 @@ func (r *Range) numHosts() int {
hosts := 0
for cur.Compare(r.End) < 1 {
hosts++
cur.Next()
cur = cur.Next()
}
return hosts

View File

@@ -47,3 +47,16 @@ func TestBasicValidation(t *testing.T) {
t.Fatal("range 4 should be invalid")
}
}
func TestNumHosts(t *testing.T) {
r := &Range{
Start: rangeTestIP1,
End: rangeTestIP2,
}
expected := 15
numHosts := r.numHosts()
if numHosts != expected {
t.Fatalf("have %d hosts, expected %d", numHosts, expected)
}
}

View File

@@ -1,8 +1,10 @@
package iptools
import "fmt"
import (
"fmt"
)
func enumerateRange(name string, r *Range, startFromOne bool) []LeaseInfo {
func enumerateRange(name string, r *Range, startFromOne bool) []*LeaseInfo {
start := r.Start
cur := start
lenfmt := fmt.Sprintf("%%s%%0%dd", len(fmt.Sprintf("%d", r.numHosts())))
@@ -10,11 +12,12 @@ func enumerateRange(name string, r *Range, startFromOne bool) []LeaseInfo {
if startFromOne {
i++
}
leases := []LeaseInfo{}
leases := []*LeaseInfo{}
for r.End.Compare(cur) >= 0 {
leases = append(leases, LeaseInfo{
HostName: fmt.Sprintf(lenfmt, name, i),
hostName := fmt.Sprintf(lenfmt, name, i)
leases = append(leases, &LeaseInfo{
HostName: hostName,
Addr: cur,
})
i++