Finish implementation and begin working on tests

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
This commit is contained in:
Michael Crosby 2014-01-22 18:05:20 -08:00
parent 78b3d89f87
commit 6bc05899aa
3 changed files with 89 additions and 61 deletions

View File

@ -5,7 +5,6 @@ import (
"errors" "errors"
"github.com/dotcloud/docker/pkg/netlink" "github.com/dotcloud/docker/pkg/netlink"
"net" "net"
"sort"
"sync" "sync"
) )
@ -20,9 +19,11 @@ var (
ErrNetworkAlreadyAllocated = errors.New("requested network overlaps with existing network") ErrNetworkAlreadyAllocated = errors.New("requested network overlaps with existing network")
ErrNetworkAlreadyRegisterd = errors.New("requested network is already registered") ErrNetworkAlreadyRegisterd = errors.New("requested network is already registered")
ErrNoAvailableIps = errors.New("no available ips on network") ErrNoAvailableIps = errors.New("no available ips on network")
lock = sync.Mutex{} ErrIPAlreadyAllocated = errors.New("ip already allocated")
allocatedIPs = networkSet{}
availableIPS = networkSet{} lock = sync.Mutex{}
allocatedIPs = networkSet{}
availableIPS = networkSet{}
) )
func RegisterNetwork(network *net.IPNet) error { func RegisterNetwork(network *net.IPNet) error {
@ -41,12 +42,15 @@ func RegisterNetwork(network *net.IPNet) error {
if err := checkExistingNetworkOverlaps(network); err != nil { if err := checkExistingNetworkOverlaps(network); err != nil {
return err return err
} }
allocatedIPs[newIPNet(network)] = iPSet{} n := newIPNet(network)
allocatedIPs[n] = iPSet{}
availableIPS[n] = iPSet{}
return nil return nil
} }
func RequestIP(network *net.IPNet, ip *net.IPAddr) (*net.IPAddr, error) { func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
@ -58,71 +62,65 @@ func RequestIP(network *net.IPNet, ip *net.IPAddr) (*net.IPAddr, error) {
return next, nil return next, nil
} }
if err := validateIP(network, ip); err != nil { if err := registerIP(network, ip); err != nil {
return nil, err return nil, err
} }
return ip, nil return ip, nil
} }
func ReleaseIP(network *net.IPNet, ip *net.IPAddr) error { func ReleaseIP(network *net.IPNet, ip *net.IP) error {
lock.Lock() lock.Lock()
defer lock.Unlock() defer lock.Unlock()
n := newIPNet(network) n := newIPNet(network)
existing := allocatedIPs[n] existing := allocatedIPs[n]
delete(existing, ip.String()) i := ipToInt(ip)
availableIPS[n][ip.String()] = struct{}{} existing.Remove(int(i))
available := availableIPS[n]
available.Push(int(i))
return nil return nil
} }
func getNextIp(network *net.IPNet) (net.IPAddr, error) { func getNextIp(network *net.IPNet) (*net.IP, error) {
if available, exists := availableIPS[network]; exists {
return nil, nil
}
var ( var (
firstIP, _ = networkRange(network) n = newIPNet(network)
ipNum = ipToInt(firstIP) available = availableIPS[n]
ownIP = ipToInt(network.IP) next = available.Pop()
size = networkSize(network.Mask) allocated = allocatedIPs[n]
n = newIPNet(network) ownIP = int(ipToInt(&network.IP))
allocated = allocatedIPs[n]
pos = int32(1)
max = size - 2 // -1 for the broadcast address, -1 for the gateway address
ip *net.IP
newNum int32
inUse bool
) )
// Find first unused IP, give up after one whole round if next != 0 {
for attempt := int32(0); attempt < max; attempt++ { ip := intToIP(int32(next))
newNum = ipNum + pos allocated.Push(int(next))
pos = pos%max + 1 return ip, nil
// The network's IP is never okay to use }
if newNum == ownIP { size := int(networkSize(network.Mask))
next = allocated.PullBack() + 1
// size -1 for the broadcast address, -1 for the gateway address
for i := 0; i < size-2; i++ {
if next == ownIP {
next++
continue continue
} }
ip = intToIP(newNum) ip := intToIP(int32(next))
if _, inUse = allocated[ip.String()]; !inUse { allocated.Push(next)
// We found an unused IP
break
}
}
if ip == nil { return ip, nil
return nil, ErrNoAvailableIps
} }
allocated[ip.String()] = struct{}{} return nil, ErrNoAvailableIps
return ip, nil
} }
func validateIP(network *net.IPNet, ip *net.IPAddr) error { func registerIP(network *net.IPNet, ip *net.IP) error {
existing := allocatedIPs[newIPNet(network)]
if existing.Exists(int(ipToInt(ip))) {
return ErrIPAlreadyAllocated
}
return nil
} }
func checkRouteOverlaps(networks []netlink.Route, toCheck *net.IPNet) error { func checkRouteOverlaps(networks []netlink.Route, toCheck *net.IPNet) error {
@ -150,7 +148,9 @@ func checkExistingNetworkOverlaps(network *net.IPNet) error {
if newIPNet(network) == existing { if newIPNet(network) == existing {
return ErrNetworkAlreadyRegisterd return ErrNetworkAlreadyRegisterd
} }
if networkOverlaps(network, existing) {
ex := newNetIPNet(existing)
if networkOverlaps(network, ex) {
return ErrNetworkAlreadyAllocated return ErrNetworkAlreadyAllocated
} }
} }
@ -186,15 +186,16 @@ func newNetIPNet(network iPNet) *net.IPNet {
} }
// Converts a 4 bytes IP into a 32 bit integer // Converts a 4 bytes IP into a 32 bit integer
func ipToInt(ip net.IP) int32 { func ipToInt(ip *net.IP) int32 {
return int32(binary.BigEndian.Uint32(ip.To4())) return int32(binary.BigEndian.Uint32(ip.To4()))
} }
// Converts 32 bit integer into a 4 bytes IP address // Converts 32 bit integer into a 4 bytes IP address
func intToIP(n int32) net.IP { func intToIP(n int32) *net.IP {
b := make([]byte, 4) b := make([]byte, 4)
binary.BigEndian.PutUint32(b, uint32(n)) binary.BigEndian.PutUint32(b, uint32(n))
return net.IP(b) ip := net.IP(b)
return &ip
} }
// Given a netmask, calculates the number of available hosts // Given a netmask, calculates the number of available hosts

View File

@ -0,0 +1,26 @@
package ipallocator
import (
"net"
"testing"
)
func TestRegisterNetwork(t *testing.T) {
network := &net.IPNet{
IP: []byte{192, 168, 0, 1},
Mask: []byte{255, 255, 255, 0},
}
if err := RegisterNetwork(network); err != nil {
t.Fatal(err)
}
n := newIPNet(network)
if _, exists := allocatedIPs[n]; !exists {
t.Fatal("IPNet should exist in allocated IPs")
}
if _, exists := availableIPS[n]; !exists {
t.Fatal("IPNet should exist in available IPs")
}
}

View File

@ -8,13 +8,13 @@ import (
// iPSet is a thread-safe sorted set and a stack. // iPSet is a thread-safe sorted set and a stack.
type iPSet struct { type iPSet struct {
sync.RWMutex sync.RWMutex
set []int32 set []int
} }
// Push takes a string and adds it to the set. If the elem aready exists, it has no effect. // Push takes a string and adds it to the set. If the elem aready exists, it has no effect.
func (s *iPSet) Push(elem int32) { func (s *iPSet) Push(elem int) {
s.RLock() s.RLock()
for i, e := range s.set { for _, e := range s.set {
if e == elem { if e == elem {
s.RUnlock() s.RUnlock()
return return
@ -30,13 +30,13 @@ func (s *iPSet) Push(elem int32) {
} }
// Pop is an alias to PopFront() // Pop is an alias to PopFront()
func (s *iPSet) Pop() int32 { func (s *iPSet) Pop() int {
return s.PopFront() return s.PopFront()
} }
// Pop returns the first elemen from the list and removes it. // Pop returns the first elemen from the list and removes it.
// If the list is empty, it returns 0 // If the list is empty, it returns 0
func (s *iPSet) PopFront() int32 { func (s *iPSet) PopFront() int {
s.RLock() s.RLock()
for i, e := range s.set { for i, e := range s.set {
@ -45,24 +45,25 @@ func (s *iPSet) PopFront() int32 {
s.Lock() s.Lock()
s.set = append(s.set[:i], s.set[i+1:]...) s.set = append(s.set[:i], s.set[i+1:]...)
s.Unlock() s.Unlock()
return e return ret
} }
s.RUnlock() s.RUnlock()
return ""
return 0
} }
// PullBack retrieve the last element of the list. // PullBack retrieve the last element of the list.
// The element is not removed. // The element is not removed.
// If the list is empty, an empty element is returned. // If the list is empty, an empty element is returned.
func (s *iPSet) PullBack() int32 { func (s *iPSet) PullBack() int {
if len(s.set) == 0 { if len(s.set) == 0 {
return "" return 0
} }
return s.set[len(s.set)-1] return s.set[len(s.set)-1]
} }
// Exists checks if the given element present in the list. // Exists checks if the given element present in the list.
func (s *iPSet) Exists(elem int32) bool { func (s *iPSet) Exists(elem int) bool {
for _, e := range s.set { for _, e := range s.set {
if e == elem { if e == elem {
return true return true
@ -73,7 +74,7 @@ func (s *iPSet) Exists(elem int32) bool {
// Remove removes an element from the list. // Remove removes an element from the list.
// If the element is not found, it has no effect. // If the element is not found, it has no effect.
func (s *iPSet) Remove(elem int32) { func (s *iPSet) Remove(elem int) {
for i, e := range s.set { for i, e := range s.set {
if e == elem { if e == elem {
s.set = append(s.set[:i], s.set[i+1:]...) s.set = append(s.set[:i], s.set[i+1:]...)