mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
Ip allocator refactoring
We don't need ordered set anymore, also some cleanings and simple benchmark. Docker-DCO-1.1-Signed-off-by: Alexandr Morozov <lk4d4math@gmail.com> (github: LK4D4)
This commit is contained in:
parent
a22622b022
commit
ef94ac7d2f
2 changed files with 60 additions and 71 deletions
|
@ -4,19 +4,18 @@ import (
|
|||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/dotcloud/docker/daemon/networkdriver"
|
||||
"github.com/dotcloud/docker/pkg/collections"
|
||||
"net"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// allocatedMap is thread-unsafe set of allocated IP
|
||||
type allocatedMap struct {
|
||||
*collections.OrderedIntSet
|
||||
p map[int32]struct{}
|
||||
last int32
|
||||
}
|
||||
|
||||
func newAllocatedMap() *allocatedMap {
|
||||
return &allocatedMap{OrderedIntSet: collections.NewOrderedIntSet()}
|
||||
return &allocatedMap{p: make(map[int32]struct{})}
|
||||
}
|
||||
|
||||
type networkSet map[string]*allocatedMap
|
||||
|
@ -35,70 +34,65 @@ var (
|
|||
// 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
|
||||
// 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()
|
||||
defer lock.Unlock()
|
||||
|
||||
checkAddress(address)
|
||||
key := network.String()
|
||||
allocated, ok := allocatedIPs[key]
|
||||
if !ok {
|
||||
allocated = newAllocatedMap()
|
||||
allocatedIPs[key] = allocated
|
||||
}
|
||||
|
||||
if ip == nil {
|
||||
next, err := getNextIp(address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return allocated.getNextIP(network)
|
||||
}
|
||||
return next, nil
|
||||
}
|
||||
|
||||
if err := registerIP(address, ip); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ip, nil
|
||||
return allocated.checkIP(network, ip)
|
||||
}
|
||||
|
||||
// ReleaseIP adds the provided ip back into the pool of
|
||||
// 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()
|
||||
defer lock.Unlock()
|
||||
|
||||
checkAddress(address)
|
||||
|
||||
var (
|
||||
allocated = allocatedIPs[address.String()]
|
||||
pos = getPosition(address, ip)
|
||||
)
|
||||
|
||||
allocated.Remove(int(pos))
|
||||
|
||||
if allocated, exists := allocatedIPs[network.String()]; exists {
|
||||
pos := getPosition(network, ip)
|
||||
delete(allocated.p, pos)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// convert the ip into the position in the subnet. Only
|
||||
// position are saved in the set
|
||||
func getPosition(address *net.IPNet, ip *net.IP) int32 {
|
||||
var (
|
||||
first, _ = networkdriver.NetworkRange(address)
|
||||
base = ipToInt(&first)
|
||||
i = ipToInt(ip)
|
||||
)
|
||||
return i - base
|
||||
func getPosition(network *net.IPNet, ip *net.IP) int32 {
|
||||
first, _ := networkdriver.NetworkRange(network)
|
||||
return ipToInt(ip) - ipToInt(&first)
|
||||
}
|
||||
|
||||
func (allocated *allocatedMap) checkIP(network *net.IPNet, ip *net.IP) (*net.IP, error) {
|
||||
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 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 (
|
||||
ownIP = ipToInt(&address.IP)
|
||||
allocated = allocatedIPs[address.String()]
|
||||
first, _ = networkdriver.NetworkRange(address)
|
||||
ownIP = ipToInt(&network.IP)
|
||||
first, _ = networkdriver.NetworkRange(network)
|
||||
base = ipToInt(&first)
|
||||
size = int(networkdriver.NetworkSize(address.Mask))
|
||||
max = int32(size - 2) // size -1 for the broadcast address, -1 for the gateway address
|
||||
pos = atomic.LoadInt32(&allocated.last)
|
||||
size = int(networkdriver.NetworkSize(network.Mask))
|
||||
max = int32(size - 2) // size -1 for the broadcast network, -1 for the gateway network
|
||||
pos = allocated.last
|
||||
)
|
||||
|
||||
var (
|
||||
firstNetIP = address.IP.To4().Mask(address.Mask)
|
||||
firstNetIP = network.IP.To4().Mask(network.Mask)
|
||||
firstAsInt = ipToInt(&firstNetIP) + 1
|
||||
)
|
||||
|
||||
|
@ -109,31 +103,16 @@ func getNextIp(address *net.IPNet) (*net.IP, error) {
|
|||
if next == ownIP || next == firstAsInt {
|
||||
continue
|
||||
}
|
||||
|
||||
if !allocated.Exists(int(pos)) {
|
||||
ip := intToIP(next)
|
||||
allocated.Push(int(pos))
|
||||
atomic.StoreInt32(&allocated.last, pos)
|
||||
return ip, nil
|
||||
if _, ok := allocated.p[pos]; ok {
|
||||
continue
|
||||
}
|
||||
allocated.p[pos] = struct{}{}
|
||||
allocated.last = pos
|
||||
return intToIP(next), nil
|
||||
}
|
||||
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
|
||||
func ipToInt(ip *net.IP) int32 {
|
||||
return int32(binary.BigEndian.Uint32(ip.To4()))
|
||||
|
@ -146,10 +125,3 @@ func intToIP(n int32) *net.IP {
|
|||
ip := net.IP(b)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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