Merge pull request #5937 from LK4D4/ipallocator_refactoring
Ip allocator refactoring
This commit is contained in:
commit
bdb5aa4c27
2 changed files with 60 additions and 71 deletions
|
@ -4,19 +4,18 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/dotcloud/docker/daemon/networkdriver"
|
"github.com/dotcloud/docker/daemon/networkdriver"
|
||||||
"github.com/dotcloud/docker/pkg/collections"
|
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// allocatedMap is thread-unsafe set of allocated IP
|
||||||
type allocatedMap struct {
|
type allocatedMap struct {
|
||||||
*collections.OrderedIntSet
|
p map[int32]struct{}
|
||||||
last int32
|
last int32
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAllocatedMap() *allocatedMap {
|
func newAllocatedMap() *allocatedMap {
|
||||||
return &allocatedMap{OrderedIntSet: collections.NewOrderedIntSet()}
|
return &allocatedMap{p: make(map[int32]struct{})}
|
||||||
}
|
}
|
||||||
|
|
||||||
type networkSet map[string]*allocatedMap
|
type networkSet map[string]*allocatedMap
|
||||||
|
@ -35,70 +34,65 @@ var (
|
||||||
// will return the next available ip if the ip provided is nil. If the
|
// will return the next available ip if the ip provided is nil. If the
|
||||||
// ip provided is not nil it will validate that the provided ip is available
|
// ip provided is not nil it will validate that the provided ip is available
|
||||||
// for use or return an error
|
// for use or return an error
|
||||||
func RequestIP(address *net.IPNet, ip *net.IP) (*net.IP, error) {
|
func RequestIP(network *net.IPNet, ip *net.IP) (*net.IP, error) {
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
|
key := network.String()
|
||||||
checkAddress(address)
|
allocated, ok := allocatedIPs[key]
|
||||||
|
if !ok {
|
||||||
|
allocated = newAllocatedMap()
|
||||||
|
allocatedIPs[key] = allocated
|
||||||
|
}
|
||||||
|
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
next, err := getNextIp(address)
|
return allocated.getNextIP(network)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return next, nil
|
|
||||||
}
|
}
|
||||||
|
return allocated.checkIP(network, ip)
|
||||||
if err := registerIP(address, ip); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return ip, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReleaseIP adds the provided ip back into the pool of
|
// ReleaseIP adds the provided ip back into the pool of
|
||||||
// available ips to be returned for use.
|
// available ips to be returned for use.
|
||||||
func ReleaseIP(address *net.IPNet, ip *net.IP) error {
|
func ReleaseIP(network *net.IPNet, ip *net.IP) error {
|
||||||
lock.Lock()
|
lock.Lock()
|
||||||
defer lock.Unlock()
|
defer lock.Unlock()
|
||||||
|
if allocated, exists := allocatedIPs[network.String()]; exists {
|
||||||
checkAddress(address)
|
pos := getPosition(network, ip)
|
||||||
|
delete(allocated.p, pos)
|
||||||
var (
|
}
|
||||||
allocated = allocatedIPs[address.String()]
|
|
||||||
pos = getPosition(address, ip)
|
|
||||||
)
|
|
||||||
|
|
||||||
allocated.Remove(int(pos))
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// convert the ip into the position in the subnet. Only
|
// convert the ip into the position in the subnet. Only
|
||||||
// position are saved in the set
|
// position are saved in the set
|
||||||
func getPosition(address *net.IPNet, ip *net.IP) int32 {
|
func getPosition(network *net.IPNet, ip *net.IP) int32 {
|
||||||
var (
|
first, _ := networkdriver.NetworkRange(network)
|
||||||
first, _ = networkdriver.NetworkRange(address)
|
return ipToInt(ip) - ipToInt(&first)
|
||||||
base = ipToInt(&first)
|
}
|
||||||
i = ipToInt(ip)
|
|
||||||
)
|
func (allocated *allocatedMap) checkIP(network *net.IPNet, ip *net.IP) (*net.IP, error) {
|
||||||
return i - base
|
pos := getPosition(network, ip)
|
||||||
|
if _, ok := allocated.p[pos]; ok {
|
||||||
|
return nil, ErrIPAlreadyAllocated
|
||||||
|
}
|
||||||
|
allocated.p[pos] = struct{}{}
|
||||||
|
allocated.last = pos
|
||||||
|
return ip, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// return an available ip if one is currently available. If not,
|
// return an available ip if one is currently available. If not,
|
||||||
// return the next available ip for the nextwork
|
// return the next available ip for the nextwork
|
||||||
func getNextIp(address *net.IPNet) (*net.IP, error) {
|
func (allocated *allocatedMap) getNextIP(network *net.IPNet) (*net.IP, error) {
|
||||||
var (
|
var (
|
||||||
ownIP = ipToInt(&address.IP)
|
ownIP = ipToInt(&network.IP)
|
||||||
allocated = allocatedIPs[address.String()]
|
first, _ = networkdriver.NetworkRange(network)
|
||||||
first, _ = networkdriver.NetworkRange(address)
|
base = ipToInt(&first)
|
||||||
base = ipToInt(&first)
|
size = int(networkdriver.NetworkSize(network.Mask))
|
||||||
size = int(networkdriver.NetworkSize(address.Mask))
|
max = int32(size - 2) // size -1 for the broadcast network, -1 for the gateway network
|
||||||
max = int32(size - 2) // size -1 for the broadcast address, -1 for the gateway address
|
pos = allocated.last
|
||||||
pos = atomic.LoadInt32(&allocated.last)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
firstNetIP = address.IP.To4().Mask(address.Mask)
|
firstNetIP = network.IP.To4().Mask(network.Mask)
|
||||||
firstAsInt = ipToInt(&firstNetIP) + 1
|
firstAsInt = ipToInt(&firstNetIP) + 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -109,31 +103,16 @@ func getNextIp(address *net.IPNet) (*net.IP, error) {
|
||||||
if next == ownIP || next == firstAsInt {
|
if next == ownIP || next == firstAsInt {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if _, ok := allocated.p[pos]; ok {
|
||||||
if !allocated.Exists(int(pos)) {
|
continue
|
||||||
ip := intToIP(next)
|
|
||||||
allocated.Push(int(pos))
|
|
||||||
atomic.StoreInt32(&allocated.last, pos)
|
|
||||||
return ip, nil
|
|
||||||
}
|
}
|
||||||
|
allocated.p[pos] = struct{}{}
|
||||||
|
allocated.last = pos
|
||||||
|
return intToIP(next), nil
|
||||||
}
|
}
|
||||||
return nil, ErrNoAvailableIPs
|
return nil, ErrNoAvailableIPs
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerIP(address *net.IPNet, ip *net.IP) error {
|
|
||||||
var (
|
|
||||||
allocated = allocatedIPs[address.String()]
|
|
||||||
pos = getPosition(address, ip)
|
|
||||||
)
|
|
||||||
|
|
||||||
if allocated.Exists(int(pos)) {
|
|
||||||
return ErrIPAlreadyAllocated
|
|
||||||
}
|
|
||||||
atomic.StoreInt32(&allocated.last, pos)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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()))
|
||||||
|
@ -146,10 +125,3 @@ func intToIP(n int32) *net.IP {
|
||||||
ip := net.IP(b)
|
ip := net.IP(b)
|
||||||
return &ip
|
return &ip
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkAddress(address *net.IPNet) {
|
|
||||||
key := address.String()
|
|
||||||
if _, exists := allocatedIPs[key]; !exists {
|
|
||||||
allocatedIPs[key] = newAllocatedMap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -324,3 +324,20 @@ func assertIPEquals(t *testing.T, ip1, ip2 *net.IP) {
|
||||||
t.Fatalf("Expected IP %s, got %s", ip1, ip2)
|
t.Fatalf("Expected IP %s, got %s", ip1, ip2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BenchmarkRequestIP(b *testing.B) {
|
||||||
|
network := &net.IPNet{
|
||||||
|
IP: []byte{192, 168, 0, 1},
|
||||||
|
Mask: []byte{255, 255, 255, 0},
|
||||||
|
}
|
||||||
|
b.ResetTimer()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
for j := 0; j < 253; j++ {
|
||||||
|
_, err := RequestIP(network, nil)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue