Przeglądaj źródła

Finish implementation and begin working on tests

Docker-DCO-1.1-Signed-off-by: Michael Crosby <michael@crosbymichael.com> (github: crosbymichael)
Michael Crosby 11 lat temu
rodzic
commit
6bc05899aa

+ 51 - 50
networkdriver/portallocator/allocator.go

@@ -5,7 +5,6 @@ import (
 	"errors"
 	"github.com/dotcloud/docker/pkg/netlink"
 	"net"
-	"sort"
 	"sync"
 )
 
@@ -20,9 +19,11 @@ var (
 	ErrNetworkAlreadyAllocated = errors.New("requested network overlaps with existing network")
 	ErrNetworkAlreadyRegisterd = errors.New("requested network is already registered")
 	ErrNoAvailableIps          = errors.New("no available ips on network")
-	lock                       = sync.Mutex{}
-	allocatedIPs               = networkSet{}
-	availableIPS               = networkSet{}
+	ErrIPAlreadyAllocated      = errors.New("ip already allocated")
+
+	lock         = sync.Mutex{}
+	allocatedIPs = networkSet{}
+	availableIPS = networkSet{}
 )
 
 func RegisterNetwork(network *net.IPNet) error {
@@ -41,12 +42,15 @@ func RegisterNetwork(network *net.IPNet) error {
 	if err := checkExistingNetworkOverlaps(network); err != nil {
 		return err
 	}
-	allocatedIPs[newIPNet(network)] = iPSet{}
+	n := newIPNet(network)
+
+	allocatedIPs[n] = iPSet{}
+	availableIPS[n] = iPSet{}
 
 	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()
 	defer lock.Unlock()
 
@@ -58,71 +62,65 @@ func RequestIP(network *net.IPNet, ip *net.IPAddr) (*net.IPAddr, error) {
 		return next, nil
 	}
 
-	if err := validateIP(network, ip); err != nil {
+	if err := registerIP(network, ip); err != nil {
 		return nil, err
 	}
 	return ip, nil
 }
 
-func ReleaseIP(network *net.IPNet, ip *net.IPAddr) error {
+func ReleaseIP(network *net.IPNet, ip *net.IP) error {
 	lock.Lock()
 	defer lock.Unlock()
 
 	n := newIPNet(network)
 	existing := allocatedIPs[n]
 
-	delete(existing, ip.String())
-	availableIPS[n][ip.String()] = struct{}{}
+	i := ipToInt(ip)
+	existing.Remove(int(i))
+	available := availableIPS[n]
+	available.Push(int(i))
 
 	return nil
 }
 
-func getNextIp(network *net.IPNet) (net.IPAddr, error) {
-	if available, exists := availableIPS[network]; exists {
-		return nil, nil
-	}
-
+func getNextIp(network *net.IPNet) (*net.IP, error) {
 	var (
-		firstIP, _ = networkRange(network)
-		ipNum      = ipToInt(firstIP)
-		ownIP      = ipToInt(network.IP)
-		size       = networkSize(network.Mask)
-		n          = newIPNet(network)
-		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
+		n         = newIPNet(network)
+		available = availableIPS[n]
+		next      = available.Pop()
+		allocated = allocatedIPs[n]
+		ownIP     = int(ipToInt(&network.IP))
 	)
 
-	// Find first unused IP, give up after one whole round
-	for attempt := int32(0); attempt < max; attempt++ {
-		newNum = ipNum + pos
-		pos = pos%max + 1
-		// The network's IP is never okay to use
-		if newNum == ownIP {
+	if next != 0 {
+		ip := intToIP(int32(next))
+		allocated.Push(int(next))
+		return ip, nil
+	}
+	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
 		}
 
-		ip = intToIP(newNum)
-		if _, inUse = allocated[ip.String()]; !inUse {
-			// We found an unused IP
-			break
-		}
-	}
+		ip := intToIP(int32(next))
+		allocated.Push(next)
 
-	if ip == nil {
-		return nil, ErrNoAvailableIps
+		return ip, nil
 	}
-	allocated[ip.String()] = struct{}{}
-
-	return ip, nil
+	return nil, ErrNoAvailableIps
 }
 
-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 {
@@ -150,7 +148,9 @@ func checkExistingNetworkOverlaps(network *net.IPNet) error {
 		if newIPNet(network) == existing {
 			return ErrNetworkAlreadyRegisterd
 		}
-		if networkOverlaps(network, existing) {
+
+		ex := newNetIPNet(existing)
+		if networkOverlaps(network, ex) {
 			return ErrNetworkAlreadyAllocated
 		}
 	}
@@ -186,15 +186,16 @@ func newNetIPNet(network iPNet) *net.IPNet {
 }
 
 // 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()))
 }
 
 // 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)
 	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

+ 26 - 0
networkdriver/portallocator/allocator_test.go

@@ -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")
+	}
+}

+ 12 - 11
networkdriver/portallocator/ipset.go

@@ -8,13 +8,13 @@ import (
 // iPSet is a thread-safe sorted set and a stack.
 type iPSet struct {
 	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.
-func (s *iPSet) Push(elem int32) {
+func (s *iPSet) Push(elem int) {
 	s.RLock()
-	for i, e := range s.set {
+	for _, e := range s.set {
 		if e == elem {
 			s.RUnlock()
 			return
@@ -30,13 +30,13 @@ func (s *iPSet) Push(elem int32) {
 }
 
 // Pop is an alias to PopFront()
-func (s *iPSet) Pop() int32 {
+func (s *iPSet) Pop() int {
 	return s.PopFront()
 }
 
 // Pop returns the first elemen from the list and removes it.
 // If the list is empty, it returns 0
-func (s *iPSet) PopFront() int32 {
+func (s *iPSet) PopFront() int {
 	s.RLock()
 
 	for i, e := range s.set {
@@ -45,24 +45,25 @@ func (s *iPSet) PopFront() int32 {
 		s.Lock()
 		s.set = append(s.set[:i], s.set[i+1:]...)
 		s.Unlock()
-		return e
+		return ret
 	}
 	s.RUnlock()
-	return ""
+
+	return 0
 }
 
 // PullBack retrieve the last element of the list.
 // The element is not removed.
 // 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 {
-		return ""
+		return 0
 	}
 	return s.set[len(s.set)-1]
 }
 
 // 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 {
 		if e == elem {
 			return true
@@ -73,7 +74,7 @@ func (s *iPSet) Exists(elem int32) bool {
 
 // Remove removes an element from the list.
 // 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 {
 		if e == elem {
 			s.set = append(s.set[:i], s.set[i+1:]...)