Default IPAM to handle local ds
Signed-off-by: Alessandro Boch <aboch@docker.com>
This commit is contained in:
parent
fa47cb8da2
commit
b061382c5e
5 changed files with 762 additions and 595 deletions
|
@ -1,14 +1,11 @@
|
|||
package ipam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libnetwork/bitseq"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
|
@ -34,209 +31,86 @@ type Allocator struct {
|
|||
// Predefined pools for default address spaces
|
||||
predefined map[string][]*net.IPNet
|
||||
// Static subnet information
|
||||
subnets map[SubnetKey]*PoolData
|
||||
localSubnets *PoolsConfig
|
||||
globalSubnets *PoolsConfig
|
||||
// Allocated addresses in each address space's subnet
|
||||
addresses map[SubnetKey]*bitseq.Handle
|
||||
// Datastore
|
||||
store datastore.DataStore
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
addrSpace2Configs map[string]*PoolsConfig
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// NewAllocator returns an instance of libnetwork ipam
|
||||
func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
|
||||
a := &Allocator{}
|
||||
a.subnets = make(map[SubnetKey]*PoolData)
|
||||
|
||||
a.localSubnets = &PoolsConfig{
|
||||
subnets: map[SubnetKey]*PoolData{},
|
||||
id: dsConfigKey + "/Pools",
|
||||
scope: datastore.LocalScope,
|
||||
ds: lcDs,
|
||||
alloc: a,
|
||||
}
|
||||
|
||||
a.globalSubnets = &PoolsConfig{
|
||||
subnets: map[SubnetKey]*PoolData{},
|
||||
id: dsConfigKey + "/Pools",
|
||||
scope: datastore.GlobalScope,
|
||||
ds: glDs,
|
||||
alloc: a,
|
||||
}
|
||||
|
||||
a.predefined = map[string][]*net.IPNet{
|
||||
localAddressSpace: initLocalPredefinedPools(),
|
||||
globalAddressSpace: initGlobalPredefinedPools(),
|
||||
}
|
||||
|
||||
a.addrSpace2Configs = map[string]*PoolsConfig{
|
||||
localAddressSpace: a.localSubnets,
|
||||
globalAddressSpace: a.globalSubnets,
|
||||
}
|
||||
|
||||
a.addresses = make(map[SubnetKey]*bitseq.Handle)
|
||||
a.predefined = make(map[string][]*net.IPNet, 2)
|
||||
a.predefined[localAddressSpace] = initLocalPredefinedPools()
|
||||
a.predefined[globalAddressSpace] = initGlobalPredefinedPools()
|
||||
a.store = glDs
|
||||
|
||||
if a.store == nil {
|
||||
return a, nil
|
||||
cfgs := []struct {
|
||||
cfg *PoolsConfig
|
||||
dsc string
|
||||
}{
|
||||
{a.localSubnets, "local"},
|
||||
{a.globalSubnets, "global"},
|
||||
}
|
||||
|
||||
// Register for status changes
|
||||
a.watchForChanges()
|
||||
|
||||
// Get the initial subnet configs status from the ds if present.
|
||||
kvPair, err := a.store.KVStore().Get(datastore.Key(a.Key()...))
|
||||
if err != nil {
|
||||
if err != store.ErrKeyNotFound {
|
||||
return nil, fmt.Errorf("failed to retrieve the ipam subnet configs from datastore: %v", err)
|
||||
}
|
||||
return a, nil
|
||||
}
|
||||
a.subnetConfigFromStore(kvPair)
|
||||
|
||||
// Now retrieve the bitmasks for the master pools
|
||||
// Get the initial local/global pools configfrom the datastores
|
||||
var inserterList []func() error
|
||||
a.Lock()
|
||||
for k, v := range a.subnets {
|
||||
if v.Range == nil {
|
||||
inserterList = append(inserterList, func() error { return a.insertBitMask(k, v.Pool) })
|
||||
for _, e := range cfgs {
|
||||
if e.cfg.ds == nil {
|
||||
continue
|
||||
}
|
||||
if err := e.cfg.watchForChanges(); err != nil {
|
||||
log.Warnf("Error on registering watch for %s datastore: %v", e.dsc, err)
|
||||
}
|
||||
if err := e.cfg.readFromStore(); err != nil && err != store.ErrKeyNotFound {
|
||||
return nil, fmt.Errorf("failed to retrieve the ipam %s pools config from datastore: %v", e.dsc, err)
|
||||
}
|
||||
e.cfg.Lock()
|
||||
for k, v := range e.cfg.subnets {
|
||||
if v.Range == nil {
|
||||
inserterList = append(inserterList, func() error { return a.insertBitMask(e.cfg.ds, k, v.Pool) })
|
||||
}
|
||||
}
|
||||
e.cfg.Unlock()
|
||||
}
|
||||
a.Unlock()
|
||||
|
||||
// Add the bitmasks, data could come from datastore
|
||||
for _, f := range inserterList {
|
||||
if err := f(); err != nil {
|
||||
return nil, err
|
||||
// Add the bitmasks (data could come from datastore)
|
||||
if inserterList != nil {
|
||||
for _, f := range inserterList {
|
||||
if err := f(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func (a *Allocator) subnetConfigFromStore(kvPair *store.KVPair) {
|
||||
a.Lock()
|
||||
if a.dbIndex < kvPair.LastIndex {
|
||||
a.SetValue(kvPair.Value)
|
||||
a.dbIndex = kvPair.LastIndex
|
||||
a.dbExists = true
|
||||
}
|
||||
a.Unlock()
|
||||
}
|
||||
|
||||
// SubnetKey is the pointer to the configured pools in each address space
|
||||
type SubnetKey struct {
|
||||
AddressSpace string
|
||||
Subnet string
|
||||
ChildSubnet string
|
||||
}
|
||||
|
||||
// String returns the string form of the SubnetKey object
|
||||
func (s *SubnetKey) String() string {
|
||||
k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet)
|
||||
if s.ChildSubnet != "" {
|
||||
k = fmt.Sprintf("%s/%s", k, s.ChildSubnet)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
// FromString populate the SubnetKey object reading it from string
|
||||
func (s *SubnetKey) FromString(str string) error {
|
||||
if str == "" || !strings.Contains(str, "/") {
|
||||
return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
||||
}
|
||||
|
||||
p := strings.Split(str, "/")
|
||||
if len(p) != 3 && len(p) != 5 {
|
||||
return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
||||
}
|
||||
s.AddressSpace = p[0]
|
||||
s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2])
|
||||
if len(p) == 5 {
|
||||
s.ChildSubnet = fmt.Sprintf("%s/%s", p[3], p[4])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddressRange specifies first and last ip ordinal which
|
||||
// identify a range in a a pool of addresses
|
||||
type AddressRange struct {
|
||||
Sub *net.IPNet
|
||||
Start, End uint32
|
||||
}
|
||||
|
||||
// String returns the string form of the AddressRange object
|
||||
func (r *AddressRange) String() string {
|
||||
return fmt.Sprintf("Sub: %s, range [%d, %d]", r.Sub, r.Start, r.End)
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the Range object
|
||||
func (r *AddressRange) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{
|
||||
"Sub": r.Sub.String(),
|
||||
"Start": r.Start,
|
||||
"End": r.End,
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes data into the Range object
|
||||
func (r *AddressRange) UnmarshalJSON(data []byte) error {
|
||||
m := map[string]interface{}{}
|
||||
err := json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Sub, err = types.ParseCIDR(m["Sub"].(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Start = uint32(m["Start"].(float64))
|
||||
r.End = uint32(m["End"].(float64))
|
||||
return nil
|
||||
}
|
||||
|
||||
// PoolData contains the configured pool data
|
||||
type PoolData struct {
|
||||
ParentKey SubnetKey
|
||||
Pool *net.IPNet
|
||||
Range *AddressRange `json:",omitempty"`
|
||||
RefCount int
|
||||
}
|
||||
|
||||
// String returns the string form of the PoolData object
|
||||
func (p *PoolData) String() string {
|
||||
return fmt.Sprintf("ParentKey: %s, Pool: %s, Range: %s, RefCount: %d",
|
||||
p.ParentKey.String(), p.Pool.String(), p.Range, p.RefCount)
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the PoolData object
|
||||
func (p *PoolData) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{
|
||||
"ParentKey": p.ParentKey,
|
||||
"RefCount": p.RefCount,
|
||||
}
|
||||
if p.Pool != nil {
|
||||
m["Pool"] = p.Pool.String()
|
||||
}
|
||||
if p.Range != nil {
|
||||
m["Range"] = p.Range
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes data into the PoolData object
|
||||
func (p *PoolData) UnmarshalJSON(data []byte) error {
|
||||
var (
|
||||
err error
|
||||
t struct {
|
||||
ParentKey SubnetKey
|
||||
Pool string
|
||||
Range *AddressRange `json:",omitempty"`
|
||||
RefCount int
|
||||
}
|
||||
)
|
||||
|
||||
if err = json.Unmarshal(data, &t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ParentKey = t.ParentKey
|
||||
p.Range = t.Range
|
||||
p.RefCount = t.RefCount
|
||||
if t.Pool != "" {
|
||||
if p.Pool, err = types.ParseCIDR(t.Pool); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ipVersion int
|
||||
|
||||
const (
|
||||
v4 = 4
|
||||
v6 = 6
|
||||
)
|
||||
|
||||
// GetDefaultAddressSpaces returns the local and global default address spaces
|
||||
func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
|
||||
return localAddressSpace, globalAddressSpace, nil
|
||||
|
@ -248,16 +122,22 @@ func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[
|
|||
if err != nil {
|
||||
return "", nil, nil, ipamapi.ErrInvalidPool
|
||||
}
|
||||
retry:
|
||||
insert, err := a.updatePoolDBOnAdd(*k, nw, ipr)
|
||||
|
||||
cfg, err := a.getPoolsConfig(addressSpace)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
if err := a.writeToStore(); err != nil {
|
||||
|
||||
retry:
|
||||
insert, err := cfg.updatePoolDBOnAdd(*k, nw, ipr)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
if err := cfg.writeToStore(); err != nil {
|
||||
if _, ok := err.(types.RetryError); !ok {
|
||||
return "", nil, nil, types.InternalErrorf("pool configuration failed because of %s", err.Error())
|
||||
}
|
||||
if erru := a.readFromStore(); erru != nil {
|
||||
if erru := cfg.readFromStore(); erru != nil {
|
||||
return "", nil, nil, fmt.Errorf("failed to get updated pool config from datastore (%v) after (%v)", erru, err)
|
||||
}
|
||||
goto retry
|
||||
|
@ -272,16 +152,21 @@ func (a *Allocator) ReleasePool(poolID string) error {
|
|||
return types.BadRequestErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
retry:
|
||||
remove, err := a.updatePoolDBOnRemoval(k)
|
||||
cfg, err := a.getPoolsConfig(k.AddressSpace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = a.writeToStore(); err != nil {
|
||||
|
||||
retry:
|
||||
remove, err := cfg.updatePoolDBOnRemoval(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = cfg.writeToStore(); err != nil {
|
||||
if _, ok := err.(types.RetryError); !ok {
|
||||
return types.InternalErrorf("pool (%s) removal failed because of %v", poolID, err)
|
||||
}
|
||||
if erru := a.readFromStore(); erru != nil {
|
||||
if erru := cfg.readFromStore(); erru != nil {
|
||||
return fmt.Errorf("failed to get updated pool config from datastore (%v) after (%v)", erru, err)
|
||||
}
|
||||
goto retry
|
||||
|
@ -290,6 +175,18 @@ retry:
|
|||
return remove()
|
||||
}
|
||||
|
||||
// Given the address space, returns the local or global PoolConfig based on the
|
||||
// address space is local or global. AddressSpace locality is being registered with IPAM out of band.
|
||||
func (a *Allocator) getPoolsConfig(addrSpace string) (*PoolsConfig, error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
cfg, ok := a.addrSpace2Configs[addrSpace]
|
||||
if !ok {
|
||||
return nil, types.BadRequestErrorf("cannot find locality of address space: %s", addrSpace)
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool) (*SubnetKey, *net.IPNet, *net.IPNet, *AddressRange, error) {
|
||||
var (
|
||||
nw, aw *net.IPNet
|
||||
|
@ -327,89 +224,7 @@ func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool
|
|||
return &SubnetKey{AddressSpace: addressSpace, Subnet: nw.String(), ChildSubnet: subPool}, nw, aw, ipr, nil
|
||||
}
|
||||
|
||||
func (a *Allocator) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange) (func() error, error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
// Check if already allocated
|
||||
if p, ok := a.subnets[k]; ok {
|
||||
a.incRefCount(p, 1)
|
||||
return func() error { return nil }, nil
|
||||
}
|
||||
|
||||
// If master pool, check for overlap
|
||||
if ipr == nil {
|
||||
if a.contains(k.AddressSpace, nw) {
|
||||
return nil, ipamapi.ErrPoolOverlap
|
||||
}
|
||||
// This is a new master pool, add it along with corresponding bitmask
|
||||
a.subnets[k] = &PoolData{Pool: nw, RefCount: 1}
|
||||
return func() error { return a.insertBitMask(k, nw) }, nil
|
||||
}
|
||||
|
||||
// This is a new non-master pool
|
||||
p := &PoolData{
|
||||
ParentKey: SubnetKey{AddressSpace: k.AddressSpace, Subnet: k.Subnet},
|
||||
Pool: nw,
|
||||
Range: ipr,
|
||||
RefCount: 1,
|
||||
}
|
||||
a.subnets[k] = p
|
||||
|
||||
// Look for parent pool
|
||||
pp, ok := a.subnets[p.ParentKey]
|
||||
if ok {
|
||||
a.incRefCount(pp, 1)
|
||||
return func() error { return nil }, nil
|
||||
}
|
||||
|
||||
// Parent pool does not exist, add it along with corresponding bitmask
|
||||
a.subnets[p.ParentKey] = &PoolData{Pool: nw, RefCount: 1}
|
||||
return func() error { return a.insertBitMask(p.ParentKey, nw) }, nil
|
||||
}
|
||||
|
||||
func (a *Allocator) updatePoolDBOnRemoval(k SubnetKey) (func() error, error) {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
p, ok := a.subnets[k]
|
||||
if !ok {
|
||||
return nil, ipamapi.ErrBadPool
|
||||
}
|
||||
|
||||
a.incRefCount(p, -1)
|
||||
|
||||
c := p
|
||||
for ok {
|
||||
if c.RefCount == 0 {
|
||||
delete(a.subnets, k)
|
||||
if c.Range == nil {
|
||||
return func() error {
|
||||
bm, err := a.retrieveBitmask(k, c.Pool)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
|
||||
}
|
||||
return bm.Destroy()
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
k = c.ParentKey
|
||||
c, ok = a.subnets[k]
|
||||
}
|
||||
|
||||
return func() error { return nil }, nil
|
||||
}
|
||||
|
||||
func (a *Allocator) incRefCount(p *PoolData, delta int) {
|
||||
c := p
|
||||
ok := true
|
||||
for ok {
|
||||
c.RefCount += delta
|
||||
c, ok = a.subnets[c.ParentKey]
|
||||
}
|
||||
}
|
||||
|
||||
func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
|
||||
func (a *Allocator) insertBitMask(store datastore.DataStore, key SubnetKey, pool *net.IPNet) error {
|
||||
log.Debugf("Inserting bitmask (%s, %s)", key.String(), pool.String())
|
||||
ipVer := getAddressVersion(pool.IP)
|
||||
ones, bits := pool.Mask.Size()
|
||||
|
@ -421,7 +236,7 @@ func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
|
|||
}
|
||||
|
||||
// Generate the new address masks. AddressMask content may come from datastore
|
||||
h, err := bitseq.NewHandle(dsDataKey, a.store, key.String(), numAddresses)
|
||||
h, err := bitseq.NewHandle(dsDataKey, store, key.String(), numAddresses)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -434,17 +249,16 @@ func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
|
|||
a.Lock()
|
||||
a.addresses[key] = h
|
||||
a.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle, error) {
|
||||
func (a *Allocator) retrieveBitmask(ds datastore.DataStore, k SubnetKey, n *net.IPNet) (*bitseq.Handle, error) {
|
||||
a.Lock()
|
||||
bm, ok := a.addresses[k]
|
||||
a.Unlock()
|
||||
if !ok {
|
||||
log.Debugf("Retrieving bitmask (%s, %s)", k.String(), n.String())
|
||||
if err := a.insertBitMask(k, n); err != nil {
|
||||
if err := a.insertBitMask(ds, k, n); err != nil {
|
||||
return nil, fmt.Errorf("could not find bitmask in datastore for %s", k.String())
|
||||
}
|
||||
a.Lock()
|
||||
|
@ -475,18 +289,23 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
|
|||
return nil, fmt.Errorf("no default pool availbale for non-default addresss spaces")
|
||||
}
|
||||
|
||||
cfg, err := a.getPoolsConfig(as)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, nw := range a.getPredefineds(as) {
|
||||
if v != getAddressVersion(nw.IP) {
|
||||
continue
|
||||
}
|
||||
a.Lock()
|
||||
_, ok := a.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]
|
||||
a.Unlock()
|
||||
cfg.Lock()
|
||||
_, ok := cfg.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]
|
||||
cfg.Unlock()
|
||||
if ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if !a.contains(as, nw) {
|
||||
if !cfg.contains(as, nw) {
|
||||
if as == localAddressSpace {
|
||||
if err := netutils.CheckRouteOverlaps(nw); err == nil {
|
||||
return nw, nil
|
||||
|
@ -500,38 +319,6 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error)
|
|||
return nil, types.NotFoundErrorf("could not find an available predefined network")
|
||||
}
|
||||
|
||||
// Check subnets size. In case configured subnet is v6 and host size is
|
||||
// greater than 32 bits, adjust subnet to /96.
|
||||
func adjustAndCheckSubnetSize(subnet *net.IPNet) (*net.IPNet, error) {
|
||||
ones, bits := subnet.Mask.Size()
|
||||
if v6 == getAddressVersion(subnet.IP) {
|
||||
if ones < minNetSizeV6 {
|
||||
return nil, ipamapi.ErrInvalidPool
|
||||
}
|
||||
if ones < minNetSizeV6Eff {
|
||||
newMask := net.CIDRMask(minNetSizeV6Eff, bits)
|
||||
return &net.IPNet{IP: subnet.IP, Mask: newMask}, nil
|
||||
}
|
||||
} else {
|
||||
if ones < minNetSize {
|
||||
return nil, ipamapi.ErrInvalidPool
|
||||
}
|
||||
}
|
||||
return subnet, nil
|
||||
}
|
||||
|
||||
// Checks whether the passed subnet is a superset or subset of any of the subset in the db
|
||||
func (a *Allocator) contains(space string, nw *net.IPNet) bool {
|
||||
for k, v := range a.subnets {
|
||||
if space == k.AddressSpace && k.ChildSubnet == "" {
|
||||
if nw.Contains(v.Pool.IP) || v.Pool.Contains(nw.IP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RequestAddress returns an address from the specified pool ID
|
||||
func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
|
||||
k := SubnetKey{}
|
||||
|
@ -539,26 +326,31 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s
|
|||
return nil, nil, types.BadRequestErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
a.Lock()
|
||||
p, ok := a.subnets[k]
|
||||
cfg, err := a.getPoolsConfig(k.AddressSpace)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cfg.Lock()
|
||||
p, ok := cfg.subnets[k]
|
||||
if !ok {
|
||||
a.Unlock()
|
||||
cfg.Unlock()
|
||||
return nil, nil, types.NotFoundErrorf("cannot find address pool for poolID:%s", poolID)
|
||||
}
|
||||
|
||||
if prefAddress != nil && !p.Pool.Contains(prefAddress) {
|
||||
a.Unlock()
|
||||
cfg.Unlock()
|
||||
return nil, nil, ipamapi.ErrIPOutOfRange
|
||||
}
|
||||
|
||||
c := p
|
||||
for c.Range != nil {
|
||||
k = c.ParentKey
|
||||
c, ok = a.subnets[k]
|
||||
c, ok = cfg.subnets[k]
|
||||
}
|
||||
a.Unlock()
|
||||
cfg.Unlock()
|
||||
|
||||
bm, err := a.retrieveBitmask(k, c.Pool)
|
||||
bm, err := a.retrieveBitmask(cfg.ds, k, c.Pool)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v",
|
||||
k.String(), prefAddress, poolID, err)
|
||||
|
@ -578,24 +370,29 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
|
|||
return types.BadRequestErrorf("invalid pool id: %s", poolID)
|
||||
}
|
||||
|
||||
a.Lock()
|
||||
p, ok := a.subnets[k]
|
||||
cfg, err := a.getPoolsConfig(k.AddressSpace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.Lock()
|
||||
p, ok := cfg.subnets[k]
|
||||
if !ok {
|
||||
a.Unlock()
|
||||
cfg.Unlock()
|
||||
return ipamapi.ErrBadPool
|
||||
}
|
||||
|
||||
if address == nil || !p.Pool.Contains(address) {
|
||||
a.Unlock()
|
||||
cfg.Unlock()
|
||||
return ipamapi.ErrInvalidRequest
|
||||
}
|
||||
|
||||
c := p
|
||||
for c.Range != nil {
|
||||
k = c.ParentKey
|
||||
c = a.subnets[k]
|
||||
c = cfg.subnets[k]
|
||||
}
|
||||
a.Unlock()
|
||||
cfg.Unlock()
|
||||
|
||||
mask := p.Pool.Mask
|
||||
if p.Range != nil {
|
||||
|
@ -606,7 +403,7 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
|
|||
return fmt.Errorf("failed to release address %s: %v", address.String(), err)
|
||||
}
|
||||
|
||||
bm, err := a.retrieveBitmask(k, c.Pool)
|
||||
bm, err := cfg.alloc.retrieveBitmask(cfg.ds, k, c.Pool)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v",
|
||||
k.String(), address, poolID, err)
|
||||
|
@ -652,10 +449,19 @@ func (a *Allocator) DumpDatabase() string {
|
|||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
s := fmt.Sprintf("\n\nPoolData")
|
||||
for k, config := range a.subnets {
|
||||
s := fmt.Sprintf("\n\nLocal Pool Config")
|
||||
a.localSubnets.Lock()
|
||||
for k, config := range a.localSubnets.subnets {
|
||||
s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n%v: %v", k, config))
|
||||
}
|
||||
a.localSubnets.Unlock()
|
||||
|
||||
s = fmt.Sprintf("%s\n\nGlobal Pool Config", s)
|
||||
a.globalSubnets.Lock()
|
||||
for k, config := range a.globalSubnets.subnets {
|
||||
s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n%v: %v", k, config))
|
||||
}
|
||||
a.globalSubnets.Unlock()
|
||||
|
||||
s = fmt.Sprintf("%s\n\nBitmasks", s)
|
||||
for k, bm := range a.addresses {
|
||||
|
@ -663,95 +469,3 @@ func (a *Allocator) DumpDatabase() string {
|
|||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// It generates the ip address in the passed subnet specified by
|
||||
// the passed host address ordinal
|
||||
func generateAddress(ordinal uint32, network *net.IPNet) net.IP {
|
||||
var address [16]byte
|
||||
|
||||
// Get network portion of IP
|
||||
if getAddressVersion(network.IP) == v4 {
|
||||
copy(address[:], network.IP.To4())
|
||||
} else {
|
||||
copy(address[:], network.IP)
|
||||
}
|
||||
|
||||
end := len(network.Mask)
|
||||
addIntToIP(address[:end], ordinal)
|
||||
|
||||
return net.IP(address[:end])
|
||||
}
|
||||
|
||||
func getAddressVersion(ip net.IP) ipVersion {
|
||||
if ip.To4() == nil {
|
||||
return v6
|
||||
}
|
||||
return v4
|
||||
}
|
||||
|
||||
// Adds the ordinal IP to the current array
|
||||
// 192.168.0.0 + 53 => 192.168.53
|
||||
func addIntToIP(array []byte, ordinal uint32) {
|
||||
for i := len(array) - 1; i >= 0; i-- {
|
||||
array[i] |= (byte)(ordinal & 0xff)
|
||||
ordinal >>= 8
|
||||
}
|
||||
}
|
||||
|
||||
// Convert an ordinal to the respective IP address
|
||||
func ipToUint32(ip []byte) uint32 {
|
||||
value := uint32(0)
|
||||
for i := 0; i < len(ip); i++ {
|
||||
j := len(ip) - 1 - i
|
||||
value += uint32(ip[i]) << uint(j*8)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func initLocalPredefinedPools() []*net.IPNet {
|
||||
pl := make([]*net.IPNet, 0, 274)
|
||||
mask := []byte{255, 255, 0, 0}
|
||||
for i := 17; i < 32; i++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{172, byte(i), 0, 0}, Mask: mask})
|
||||
}
|
||||
for i := 0; i < 256; i++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), 0, 0}, Mask: mask})
|
||||
}
|
||||
mask24 := []byte{255, 255, 255, 0}
|
||||
for i := 42; i < 45; i++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{192, 168, byte(i), 0}, Mask: mask24})
|
||||
}
|
||||
return pl
|
||||
}
|
||||
|
||||
func initGlobalPredefinedPools() []*net.IPNet {
|
||||
pl := make([]*net.IPNet, 0, 256*256)
|
||||
mask := []byte{255, 255, 255, 0}
|
||||
for i := 0; i < 256; i++ {
|
||||
for j := 0; j < 256; j++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), byte(j), 0}, Mask: mask})
|
||||
}
|
||||
}
|
||||
return pl
|
||||
}
|
||||
|
||||
func getAddressRange(pool string) (*AddressRange, error) {
|
||||
ip, nw, err := net.ParseCIDR(pool)
|
||||
if err != nil {
|
||||
return nil, ipamapi.ErrInvalidSubPool
|
||||
}
|
||||
lIP, e := types.GetHostPartIP(nw.IP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e)
|
||||
}
|
||||
bIP, e := types.GetBroadcastIP(nw.IP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e)
|
||||
}
|
||||
hIP, e := types.GetHostPartIP(bIP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e)
|
||||
}
|
||||
nw.IP = ip
|
||||
return &AddressRange{nw, ipToUint32(types.GetMinimalIP(lIP)), ipToUint32(types.GetMinimalIP(hIP))}, nil
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
package ipam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libnetwork/bitseq"
|
||||
"github.com/docker/libnetwork/config"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
|
@ -18,12 +19,35 @@ import (
|
|||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
var ds datastore.DataStore
|
||||
const (
|
||||
defaultPrefix = "/tmp/libnetwork/test/ipam"
|
||||
)
|
||||
|
||||
// OptionBoltdbWithRandomDBFile function returns a random dir for local store backend
|
||||
func randomLocalStore() (datastore.DataStore, error) {
|
||||
tmp, err := ioutil.TempFile("", "libnetwork-")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error creating temp file: %v", err)
|
||||
}
|
||||
if err := tmp.Close(); err != nil {
|
||||
return nil, fmt.Errorf("Error closing temp file: %v", err)
|
||||
}
|
||||
return datastore.NewDataStore(&config.DatastoreCfg{
|
||||
Embedded: true,
|
||||
Client: config.DatastoreClientCfg{
|
||||
Provider: "boltdb",
|
||||
Address: defaultPrefix + tmp.Name(),
|
||||
Config: &store.Config{
|
||||
Bucket: "libnetwork",
|
||||
ConnectionTimeout: 3 * time.Second,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// enable w/ upper case
|
||||
func testMain(m *testing.M) {
|
||||
func TestMain(m *testing.M) {
|
||||
var err error
|
||||
ds, err = datastore.NewDataStore(&config.DatastoreCfg{Embedded: false, Client: config.DatastoreClientCfg{Provider: "consul", Address: "127.0.0.1:8500"}})
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
@ -31,16 +55,16 @@ func testMain(m *testing.M) {
|
|||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
func getAllocator(t *testing.T, subnet string) (*Allocator, string) {
|
||||
a, err := NewAllocator(nil, ds)
|
||||
func getAllocator() (*Allocator, error) {
|
||||
ds, err := randomLocalStore()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
poolID, _, _, err := a.RequestPool("default", subnet, "", nil, false)
|
||||
a, err := NewAllocator(ds, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
return nil, err
|
||||
}
|
||||
return a, poolID
|
||||
return a, nil
|
||||
}
|
||||
|
||||
func TestInt2IP2IntConversion(t *testing.T) {
|
||||
|
@ -70,7 +94,6 @@ func TestGetAddressVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestKeyString(t *testing.T) {
|
||||
|
||||
k := &SubnetKey{AddressSpace: "default", Subnet: "172.27.0.0/16"}
|
||||
expected := "default/172.27.0.0/16"
|
||||
if expected != k.String() {
|
||||
|
@ -151,8 +174,10 @@ func TestPoolDataMarshal(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSubnetsMarshal(t *testing.T) {
|
||||
a, _ := NewAllocator(nil, nil)
|
||||
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pid0, _, _, err := a.RequestPool(localAddressSpace, "192.168.0.0/16", "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -166,9 +191,9 @@ func TestSubnetsMarshal(t *testing.T) {
|
|||
t.Fatal(err)
|
||||
}
|
||||
|
||||
ba := a.Value()
|
||||
a.subnets = make(map[SubnetKey]*PoolData, 0)
|
||||
if err := a.SetValue(ba); err != nil {
|
||||
cfg := a.localSubnets
|
||||
ba := cfg.Value()
|
||||
if err := cfg.SetValue(ba); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
|
@ -192,12 +217,13 @@ func TestSubnetsMarshal(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestAddSubnets(t *testing.T) {
|
||||
a, err := NewAllocator(nil, nil)
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.addrSpace2Configs["abc"] = a.addrSpace2Configs[localAddressSpace]
|
||||
|
||||
pid0, _, _, err := a.RequestPool("default", "10.0.0.0/8", "", nil, false)
|
||||
pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure in adding subnet")
|
||||
}
|
||||
|
@ -236,22 +262,22 @@ func TestAddSubnets(t *testing.T) {
|
|||
t.Fatalf("returned different pool id for same sub pool requests")
|
||||
}
|
||||
|
||||
pid, _, _, err = a.RequestPool("default", "10.20.2.0/24", "", nil, false)
|
||||
pid, _, _, err = a.RequestPool(localAddressSpace, "10.20.2.0/24", "", nil, false)
|
||||
if err == nil {
|
||||
t.Fatalf("Failed to detect overlapping subnets")
|
||||
}
|
||||
|
||||
_, _, _, err = a.RequestPool("default", "10.128.0.0/9", "", nil, false)
|
||||
_, _, _, err = a.RequestPool(localAddressSpace, "10.128.0.0/9", "", nil, false)
|
||||
if err == nil {
|
||||
t.Fatalf("Failed to detect overlapping subnets")
|
||||
}
|
||||
|
||||
_, _, _, err = a.RequestPool("default", "1003:1:2:3:4:5:6::/112", "", nil, false)
|
||||
_, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3:4:5:6::/112", "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to add v6 subnet: %s", err.Error())
|
||||
}
|
||||
|
||||
_, _, _, err = a.RequestPool("default", "1003:1:2:3::/64", "", nil, false)
|
||||
_, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3::/64", "", nil, false)
|
||||
if err == nil {
|
||||
t.Fatalf("Failed to detect overlapping v6 subnet")
|
||||
}
|
||||
|
@ -259,34 +285,35 @@ func TestAddSubnets(t *testing.T) {
|
|||
|
||||
func TestAddReleasePoolID(t *testing.T) {
|
||||
var k0, k1, k2 SubnetKey
|
||||
a, err := NewAllocator(nil, nil)
|
||||
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
pid0, _, _, err := a.RequestPool("default", "10.0.0.0/8", "", nil, false)
|
||||
subnets := a.localSubnets.subnets
|
||||
pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure in adding pool")
|
||||
}
|
||||
if err := k0.FromString(pid0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if a.subnets[k0].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
||||
if subnets[k0].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
||||
}
|
||||
|
||||
pid1, _, _, err := a.RequestPool("default", "10.0.0.0/8", "10.0.0.0/16", nil, false)
|
||||
pid1, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure in adding sub pool")
|
||||
}
|
||||
if err := k1.FromString(pid1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if a.subnets[k1].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k1, a.subnets[k1].RefCount)
|
||||
if subnets[k1].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k1, subnets[k1].RefCount)
|
||||
}
|
||||
|
||||
pid2, _, _, err := a.RequestPool("default", "10.0.0.0/8", "10.0.0.0/16", nil, false)
|
||||
pid2, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure in adding sub pool")
|
||||
}
|
||||
|
@ -296,63 +323,63 @@ func TestAddReleasePoolID(t *testing.T) {
|
|||
if err := k2.FromString(pid2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if a.subnets[k2].RefCount != 2 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k2, a.subnets[k2].RefCount)
|
||||
if subnets[k2].RefCount != 2 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k2, subnets[k2].RefCount)
|
||||
}
|
||||
|
||||
if a.subnets[k0].RefCount != 3 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
||||
if subnets[k0].RefCount != 3 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
||||
}
|
||||
|
||||
if err := a.ReleasePool(pid1); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if a.subnets[k0].RefCount != 2 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
||||
if subnets[k0].RefCount != 2 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
||||
}
|
||||
if err := a.ReleasePool(pid0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if a.subnets[k0].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
||||
if subnets[k0].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
||||
}
|
||||
|
||||
pid00, _, _, err := a.RequestPool("default", "10.0.0.0/8", "", nil, false)
|
||||
pid00, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure in adding pool")
|
||||
}
|
||||
if pid00 != pid0 {
|
||||
t.Fatalf("main pool should still exist")
|
||||
}
|
||||
if a.subnets[k0].RefCount != 2 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
||||
if subnets[k0].RefCount != 2 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
||||
}
|
||||
|
||||
if err := a.ReleasePool(pid2); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if a.subnets[k0].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
||||
if subnets[k0].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
||||
}
|
||||
|
||||
if err := a.ReleasePool(pid00); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if bp, ok := a.subnets[k0]; ok {
|
||||
if bp, ok := subnets[k0]; ok {
|
||||
t.Fatalf("Base pool %s is still present: %v", k0, bp)
|
||||
}
|
||||
|
||||
_, _, _, err = a.RequestPool("default", "10.0.0.0/8", "", nil, false)
|
||||
_, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure in adding pool")
|
||||
}
|
||||
if a.subnets[k0].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
||||
if subnets[k0].RefCount != 1 {
|
||||
t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPredefinedPool(t *testing.T) {
|
||||
a, err := NewAllocator(nil, nil)
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -444,29 +471,31 @@ func TestAdjustAndCheckSubnet(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRemoveSubnet(t *testing.T) {
|
||||
a, err := NewAllocator(nil, nil)
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.addrSpace2Configs["splane"] = a.addrSpace2Configs[localAddressSpace]
|
||||
|
||||
input := []struct {
|
||||
addrSpace string
|
||||
subnet string
|
||||
v6 bool
|
||||
}{
|
||||
{"default", "192.168.0.0/16"},
|
||||
{"default", "172.17.0.0/16"},
|
||||
{"default", "10.0.0.0/8"},
|
||||
{"default", "2002:1:2:3:4:5:ffff::/112"},
|
||||
{"splane", "172.17.0.0/16"},
|
||||
{"splane", "10.0.0.0/8"},
|
||||
{"splane", "2002:1:2:3:4:5:6::/112"},
|
||||
{"splane", "2002:1:2:3:4:5:ffff::/112"},
|
||||
{localAddressSpace, "192.168.0.0/16", false},
|
||||
{localAddressSpace, "172.17.0.0/16", false},
|
||||
{localAddressSpace, "10.0.0.0/8", false},
|
||||
{localAddressSpace, "2002:1:2:3:4:5:ffff::/112", false},
|
||||
{"splane", "172.17.0.0/16", false},
|
||||
{"splane", "10.0.0.0/8", false},
|
||||
{"splane", "2002:1:2:3:4:5:6::/112", true},
|
||||
{"splane", "2002:1:2:3:4:5:ffff::/112", true},
|
||||
}
|
||||
|
||||
poolIDs := make([]string, len(input))
|
||||
|
||||
for ind, i := range input {
|
||||
if poolIDs[ind], _, _, err = a.RequestPool(i.addrSpace, i.subnet, "", nil, false); err != nil {
|
||||
if poolIDs[ind], _, _, err = a.RequestPool(i.addrSpace, i.subnet, "", nil, i.v6); err != nil {
|
||||
t.Fatalf("Failed to apply input. Can't proceed: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
@ -479,10 +508,11 @@ func TestRemoveSubnet(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestGetSameAddress(t *testing.T) {
|
||||
a, err := NewAllocator(nil, nil)
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.addrSpace2Configs["giallo"] = a.addrSpace2Configs[localAddressSpace]
|
||||
|
||||
pid, _, _, err := a.RequestPool("giallo", "192.168.100.0/24", "", nil, false)
|
||||
if err != nil {
|
||||
|
@ -502,10 +532,11 @@ func TestGetSameAddress(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestRequestReleaseAddressFromSubPool(t *testing.T) {
|
||||
a, err := NewAllocator(nil, nil)
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.addrSpace2Configs["rosso"] = a.addrSpace2Configs[localAddressSpace]
|
||||
|
||||
poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false)
|
||||
if err != nil {
|
||||
|
@ -587,11 +618,16 @@ func TestRequestSyntaxCheck(t *testing.T) {
|
|||
pool = "192.168.0.0/16"
|
||||
subPool = "192.168.0.0/24"
|
||||
addrSpace = "green"
|
||||
err error
|
||||
)
|
||||
|
||||
a, _ := NewAllocator(nil, nil)
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
a.addrSpace2Configs[addrSpace] = a.addrSpace2Configs[localAddressSpace]
|
||||
|
||||
_, _, _, err := a.RequestPool("", pool, "", nil, false)
|
||||
_, _, _, err = a.RequestPool("", pool, "", nil, false)
|
||||
if err == nil {
|
||||
t.Fatalf("Failed to detect wrong request: empty address space")
|
||||
}
|
||||
|
@ -661,12 +697,14 @@ func TestRequest(t *testing.T) {
|
|||
{"10.0.0.0/8", 256, "10.0.1.0"},
|
||||
|
||||
{"192.168.128.0/18", 4*256 - 1, "192.168.131.255"},
|
||||
{"192.168.240.0/20", 16*256 - 2, "192.168.255.254"},
|
||||
/*
|
||||
{"192.168.240.0/20", 16*256 - 2, "192.168.255.254"},
|
||||
|
||||
{"192.168.0.0/16", 256*256 - 2, "192.168.255.254"},
|
||||
{"10.0.0.0/8", 2 * 256, "10.0.2.0"},
|
||||
{"10.0.0.0/8", 5 * 256, "10.0.5.0"},
|
||||
//{"10.0.0.0/8", 100 * 256 * 254, "10.99.255.254"},
|
||||
{"192.168.0.0/16", 256*256 - 2, "192.168.255.254"},
|
||||
{"10.0.0.0/8", 2 * 256, "10.0.2.0"},
|
||||
{"10.0.0.0/8", 5 * 256, "10.0.5.0"},
|
||||
{"10.0.0.0/8", 100 * 256 * 254, "10.99.255.254"},
|
||||
*/
|
||||
}
|
||||
|
||||
for _, d := range input {
|
||||
|
@ -676,12 +714,19 @@ func TestRequest(t *testing.T) {
|
|||
|
||||
func TestRelease(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
subnet = "192.168.0.0/16"
|
||||
subnet = "192.168.0.0/23"
|
||||
)
|
||||
|
||||
a, pid := getAllocator(t, subnet)
|
||||
bm := a.addresses[SubnetKey{"default", subnet, ""}]
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
bm := a.addresses[SubnetKey{localAddressSpace, subnet, ""}]
|
||||
|
||||
// Allocate all addresses
|
||||
for err != ipamapi.ErrNoAvailableIPs {
|
||||
|
@ -711,8 +756,8 @@ func TestRelease(t *testing.T) {
|
|||
|
||||
{"192.168.1.3"},
|
||||
|
||||
{"192.168.255.253"},
|
||||
{"192.168.255.254"},
|
||||
{"192.168.1.253"},
|
||||
{"192.168.1.254"},
|
||||
}
|
||||
|
||||
// One by one, relase the address and request again. We should get the same IP
|
||||
|
@ -773,13 +818,19 @@ func assertGetAddress(t *testing.T, subnet string) {
|
|||
|
||||
func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP string) {
|
||||
var (
|
||||
err error
|
||||
nw *net.IPNet
|
||||
printTime = false
|
||||
)
|
||||
|
||||
lastIP := net.ParseIP(lastExpectedIP)
|
||||
a, pid := getAllocator(t, subnet)
|
||||
a, err := getAllocator()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
i := 0
|
||||
start := time.Now()
|
||||
|
@ -795,31 +846,31 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str
|
|||
}
|
||||
}
|
||||
|
||||
func benchmarkRequest(subnet string) {
|
||||
var err error
|
||||
|
||||
a, _ := NewAllocator(nil, nil)
|
||||
pid, _, _, _ := a.RequestPool("default", subnet, "", nil, false)
|
||||
|
||||
func benchmarkRequest(b *testing.B, a *Allocator, subnet string) {
|
||||
pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false)
|
||||
for err != ipamapi.ErrNoAvailableIPs {
|
||||
_, _, err = a.RequestAddress(pid, nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func benchMarkRequest(subnet string, b *testing.B) {
|
||||
a, _ := getAllocator()
|
||||
for n := 0; n < b.N; n++ {
|
||||
benchmarkRequest(subnet)
|
||||
benchmarkRequest(b, a, subnet)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRequest_24(b *testing.B) {
|
||||
benchmarkRequest("10.0.0.0/24")
|
||||
a, _ := getAllocator()
|
||||
benchmarkRequest(b, a, "10.0.0.0/24")
|
||||
}
|
||||
|
||||
func BenchmarkRequest_16(b *testing.B) {
|
||||
benchmarkRequest("10.0.0.0/16")
|
||||
a, _ := getAllocator()
|
||||
benchmarkRequest(b, a, "10.0.0.0/16")
|
||||
}
|
||||
|
||||
func BenchmarkRequest_8(b *testing.B) {
|
||||
benchmarkRequest("10.0.0.0/8")
|
||||
a, _ := getAllocator()
|
||||
benchmarkRequest(b, a, "10.0.0.0/8")
|
||||
}
|
||||
|
|
|
@ -4,95 +4,77 @@ import (
|
|||
"encoding/json"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/docker/libkv/store"
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// Key provides the Key to be used in KV Store
|
||||
func (a *Allocator) Key() []string {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return []string{dsConfigKey}
|
||||
func (cfg *PoolsConfig) Key() []string {
|
||||
cfg.Lock()
|
||||
defer cfg.Unlock()
|
||||
return []string{cfg.id}
|
||||
}
|
||||
|
||||
// KeyPrefix returns the immediate parent key that can be used for tree walk
|
||||
func (a *Allocator) KeyPrefix() []string {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
func (cfg *PoolsConfig) KeyPrefix() []string {
|
||||
cfg.Lock()
|
||||
defer cfg.Unlock()
|
||||
return []string{dsConfigKey}
|
||||
}
|
||||
|
||||
// Value marshals the data to be stored in the KV store
|
||||
func (a *Allocator) Value() []byte {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
|
||||
if a.subnets == nil {
|
||||
return []byte{}
|
||||
}
|
||||
m := map[string]interface{}{}
|
||||
for k, v := range a.subnets {
|
||||
m[k.String()] = v
|
||||
}
|
||||
|
||||
b, err := json.Marshal(m)
|
||||
func (cfg *PoolsConfig) Value() []byte {
|
||||
b, err := json.Marshal(cfg)
|
||||
if err != nil {
|
||||
log.Warnf("Failed to marshal ipam configured subnets")
|
||||
log.Warnf("Failed to marshal ipam configured pools: %v", err)
|
||||
return nil
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// SetValue unmarshalls the data from the KV store.
|
||||
func (a *Allocator) SetValue(value []byte) error {
|
||||
var m map[string]*PoolData
|
||||
err := json.Unmarshal(value, &m)
|
||||
if err != nil {
|
||||
func (cfg *PoolsConfig) SetValue(value []byte) error {
|
||||
rc := &PoolsConfig{subnets: make(map[SubnetKey]*PoolData)}
|
||||
if err := json.Unmarshal(value, rc); err != nil {
|
||||
return err
|
||||
}
|
||||
for ks, d := range m {
|
||||
k := SubnetKey{}
|
||||
if err := k.FromString(ks); err != nil {
|
||||
return err
|
||||
}
|
||||
a.subnets[k] = d
|
||||
}
|
||||
cfg.subnets = rc.subnets
|
||||
return nil
|
||||
}
|
||||
|
||||
// Index returns the latest DB Index as seen by this object
|
||||
func (a *Allocator) Index() uint64 {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return a.dbIndex
|
||||
func (cfg *PoolsConfig) Index() uint64 {
|
||||
cfg.Lock()
|
||||
defer cfg.Unlock()
|
||||
return cfg.dbIndex
|
||||
}
|
||||
|
||||
// SetIndex method allows the datastore to store the latest DB Index into this object
|
||||
func (a *Allocator) SetIndex(index uint64) {
|
||||
a.Lock()
|
||||
a.dbIndex = index
|
||||
a.dbExists = true
|
||||
a.Unlock()
|
||||
func (cfg *PoolsConfig) SetIndex(index uint64) {
|
||||
cfg.Lock()
|
||||
cfg.dbIndex = index
|
||||
cfg.dbExists = true
|
||||
cfg.Unlock()
|
||||
}
|
||||
|
||||
// Exists method is true if this object has been stored in the DB.
|
||||
func (a *Allocator) Exists() bool {
|
||||
a.Lock()
|
||||
defer a.Unlock()
|
||||
return a.dbExists
|
||||
func (cfg *PoolsConfig) Exists() bool {
|
||||
cfg.Lock()
|
||||
defer cfg.Unlock()
|
||||
return cfg.dbExists
|
||||
}
|
||||
|
||||
// Skip provides a way for a KV Object to avoid persisting it in the KV Store
|
||||
func (a *Allocator) Skip() bool {
|
||||
func (cfg *PoolsConfig) Skip() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *Allocator) watchForChanges() error {
|
||||
if a.store == nil {
|
||||
func (cfg *PoolsConfig) watchForChanges() error {
|
||||
if cfg.ds == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
kvpChan, err := a.store.KVStore().Watch(datastore.Key(a.Key()...), nil)
|
||||
kvpChan, err := cfg.ds.KVStore().Watch(datastore.Key(cfg.Key()...), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -101,7 +83,7 @@ func (a *Allocator) watchForChanges() error {
|
|||
select {
|
||||
case kvPair := <-kvpChan:
|
||||
if kvPair != nil {
|
||||
a.subnetConfigFromStore(kvPair)
|
||||
cfg.readFromKey(kvPair)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,50 +91,40 @@ func (a *Allocator) watchForChanges() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (a *Allocator) readFromStore() error {
|
||||
a.Lock()
|
||||
store := a.store
|
||||
a.Unlock()
|
||||
|
||||
if store == nil {
|
||||
func (cfg *PoolsConfig) writeToStore() error {
|
||||
if cfg.ds == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
kvPair, err := a.store.KVStore().Get(datastore.Key(a.Key()...))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
a.subnetConfigFromStore(kvPair)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *Allocator) writeToStore() error {
|
||||
a.Lock()
|
||||
store := a.store
|
||||
a.Unlock()
|
||||
if store == nil {
|
||||
return nil
|
||||
}
|
||||
err := store.PutObjectAtomic(a)
|
||||
err := cfg.ds.PutObjectAtomic(cfg)
|
||||
if err == datastore.ErrKeyModified {
|
||||
return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (a *Allocator) deleteFromStore() error {
|
||||
a.Lock()
|
||||
store := a.store
|
||||
a.Unlock()
|
||||
if store == nil {
|
||||
func (cfg *PoolsConfig) readFromStore() error {
|
||||
if cfg.ds == nil {
|
||||
return nil
|
||||
}
|
||||
return store.DeleteObjectAtomic(a)
|
||||
return cfg.ds.GetObject(datastore.Key(cfg.Key()...), cfg)
|
||||
}
|
||||
|
||||
func (cfg *PoolsConfig) readFromKey(kvPair *store.KVPair) {
|
||||
if cfg.dbIndex < kvPair.LastIndex {
|
||||
cfg.SetValue(kvPair.Value)
|
||||
cfg.dbIndex = kvPair.LastIndex
|
||||
cfg.dbExists = true
|
||||
}
|
||||
}
|
||||
|
||||
func (cfg *PoolsConfig) deleteFromStore() error {
|
||||
if cfg.ds == nil {
|
||||
return nil
|
||||
}
|
||||
return cfg.ds.DeleteObjectAtomic(cfg)
|
||||
}
|
||||
|
||||
// DataScope method returns the storage scope of the datastore
|
||||
func (a *Allocator) DataScope() datastore.DataScope {
|
||||
return datastore.GlobalScope
|
||||
func (cfg *PoolsConfig) DataScope() datastore.DataScope {
|
||||
return cfg.scope
|
||||
}
|
||||
|
|
302
libnetwork/ipam/structures.go
Normal file
302
libnetwork/ipam/structures.go
Normal file
|
@ -0,0 +1,302 @@
|
|||
package ipam
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/docker/libnetwork/datastore"
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
// SubnetKey is the pointer to the configured pools in each address space
|
||||
type SubnetKey struct {
|
||||
AddressSpace string
|
||||
Subnet string
|
||||
ChildSubnet string
|
||||
}
|
||||
|
||||
// PoolData contains the configured pool data
|
||||
type PoolData struct {
|
||||
ParentKey SubnetKey
|
||||
Pool *net.IPNet
|
||||
Range *AddressRange `json:",omitempty"`
|
||||
RefCount int
|
||||
}
|
||||
|
||||
// PoolsConfig contains the pool configurations
|
||||
type PoolsConfig struct {
|
||||
subnets map[SubnetKey]*PoolData
|
||||
dbIndex uint64
|
||||
dbExists bool
|
||||
id string
|
||||
scope datastore.DataScope
|
||||
ds datastore.DataStore
|
||||
alloc *Allocator
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
// AddressRange specifies first and last ip ordinal which
|
||||
// identify a range in a a pool of addresses
|
||||
type AddressRange struct {
|
||||
Sub *net.IPNet
|
||||
Start, End uint32
|
||||
}
|
||||
|
||||
// String returns the string form of the AddressRange object
|
||||
func (r *AddressRange) String() string {
|
||||
return fmt.Sprintf("Sub: %s, range [%d, %d]", r.Sub, r.Start, r.End)
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the Range object
|
||||
func (r *AddressRange) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{
|
||||
"Sub": r.Sub.String(),
|
||||
"Start": r.Start,
|
||||
"End": r.End,
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes data into the Range object
|
||||
func (r *AddressRange) UnmarshalJSON(data []byte) error {
|
||||
m := map[string]interface{}{}
|
||||
err := json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if r.Sub, err = types.ParseCIDR(m["Sub"].(string)); err != nil {
|
||||
return err
|
||||
}
|
||||
r.Start = uint32(m["Start"].(float64))
|
||||
r.End = uint32(m["End"].(float64))
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string form of the SubnetKey object
|
||||
func (s *SubnetKey) String() string {
|
||||
k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet)
|
||||
if s.ChildSubnet != "" {
|
||||
k = fmt.Sprintf("%s/%s", k, s.ChildSubnet)
|
||||
}
|
||||
return k
|
||||
}
|
||||
|
||||
// FromString populate the SubnetKey object reading it from string
|
||||
func (s *SubnetKey) FromString(str string) error {
|
||||
if str == "" || !strings.Contains(str, "/") {
|
||||
return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
||||
}
|
||||
|
||||
p := strings.Split(str, "/")
|
||||
if len(p) != 3 && len(p) != 5 {
|
||||
return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
||||
}
|
||||
s.AddressSpace = p[0]
|
||||
s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2])
|
||||
if len(p) == 5 {
|
||||
s.ChildSubnet = fmt.Sprintf("%s/%s", p[3], p[4])
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the string form of the PoolData object
|
||||
func (p *PoolData) String() string {
|
||||
return fmt.Sprintf("ParentKey: %s, Pool: %s, Range: %s, RefCount: %d",
|
||||
p.ParentKey.String(), p.Pool.String(), p.Range, p.RefCount)
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the PoolData object
|
||||
func (p *PoolData) MarshalJSON() ([]byte, error) {
|
||||
m := map[string]interface{}{
|
||||
"ParentKey": p.ParentKey,
|
||||
"RefCount": p.RefCount,
|
||||
}
|
||||
if p.Pool != nil {
|
||||
m["Pool"] = p.Pool.String()
|
||||
}
|
||||
if p.Range != nil {
|
||||
m["Range"] = p.Range
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes data into the PoolData object
|
||||
func (p *PoolData) UnmarshalJSON(data []byte) error {
|
||||
var (
|
||||
err error
|
||||
t struct {
|
||||
ParentKey SubnetKey
|
||||
Pool string
|
||||
Range *AddressRange `json:",omitempty"`
|
||||
RefCount int
|
||||
}
|
||||
)
|
||||
|
||||
if err = json.Unmarshal(data, &t); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.ParentKey = t.ParentKey
|
||||
p.Range = t.Range
|
||||
p.RefCount = t.RefCount
|
||||
if t.Pool != "" {
|
||||
if p.Pool, err = types.ParseCIDR(t.Pool); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the PoolsConfig object
|
||||
func (cfg *PoolsConfig) MarshalJSON() ([]byte, error) {
|
||||
cfg.Lock()
|
||||
defer cfg.Unlock()
|
||||
|
||||
m := map[string]interface{}{
|
||||
"Scope": string(cfg.scope),
|
||||
}
|
||||
|
||||
if cfg.subnets != nil {
|
||||
s := map[string]*PoolData{}
|
||||
for k, v := range cfg.subnets {
|
||||
s[k.String()] = v
|
||||
}
|
||||
m["Subnets"] = s
|
||||
}
|
||||
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes data into the PoolsConfig object
|
||||
func (cfg *PoolsConfig) UnmarshalJSON(data []byte) error {
|
||||
cfg.Lock()
|
||||
defer cfg.Unlock()
|
||||
|
||||
m := map[string]interface{}{}
|
||||
err := json.Unmarshal(data, &m)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cfg.scope = datastore.LocalScope
|
||||
s := m["Scope"].(string)
|
||||
if s == string(datastore.GlobalScope) {
|
||||
cfg.scope = datastore.GlobalScope
|
||||
}
|
||||
|
||||
if v, ok := m["Subnets"]; ok {
|
||||
sb, _ := json.Marshal(v)
|
||||
var s map[string]*PoolData
|
||||
err := json.Unmarshal(sb, &s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for ks, v := range s {
|
||||
k := SubnetKey{}
|
||||
k.FromString(ks)
|
||||
cfg.subnets[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cfg *PoolsConfig) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange) (func() error, error) {
|
||||
cfg.Lock()
|
||||
defer cfg.Unlock()
|
||||
|
||||
// Check if already allocated
|
||||
if p, ok := cfg.subnets[k]; ok {
|
||||
cfg.incRefCount(p, 1)
|
||||
return func() error { return nil }, nil
|
||||
}
|
||||
|
||||
// If master pool, check for overlap
|
||||
if ipr == nil {
|
||||
if cfg.contains(k.AddressSpace, nw) {
|
||||
return nil, ipamapi.ErrPoolOverlap
|
||||
}
|
||||
// This is a new master pool, add it along with corresponding bitmask
|
||||
cfg.subnets[k] = &PoolData{Pool: nw, RefCount: 1}
|
||||
return func() error { return cfg.alloc.insertBitMask(cfg.ds, k, nw) }, nil
|
||||
}
|
||||
|
||||
// This is a new non-master pool
|
||||
p := &PoolData{
|
||||
ParentKey: SubnetKey{AddressSpace: k.AddressSpace, Subnet: k.Subnet},
|
||||
Pool: nw,
|
||||
Range: ipr,
|
||||
RefCount: 1,
|
||||
}
|
||||
cfg.subnets[k] = p
|
||||
|
||||
// Look for parent pool
|
||||
pp, ok := cfg.subnets[p.ParentKey]
|
||||
if ok {
|
||||
cfg.incRefCount(pp, 1)
|
||||
return func() error { return nil }, nil
|
||||
}
|
||||
|
||||
// Parent pool does not exist, add it along with corresponding bitmask
|
||||
cfg.subnets[p.ParentKey] = &PoolData{Pool: nw, RefCount: 1}
|
||||
return func() error { return cfg.alloc.insertBitMask(cfg.ds, p.ParentKey, nw) }, nil
|
||||
}
|
||||
|
||||
func (cfg *PoolsConfig) updatePoolDBOnRemoval(k SubnetKey) (func() error, error) {
|
||||
cfg.Lock()
|
||||
defer cfg.Unlock()
|
||||
|
||||
p, ok := cfg.subnets[k]
|
||||
if !ok {
|
||||
return nil, ipamapi.ErrBadPool
|
||||
}
|
||||
|
||||
cfg.incRefCount(p, -1)
|
||||
|
||||
c := p
|
||||
for ok {
|
||||
if c.RefCount == 0 {
|
||||
delete(cfg.subnets, k)
|
||||
if c.Range == nil {
|
||||
return func() error {
|
||||
bm, err := cfg.alloc.retrieveBitmask(cfg.ds, k, c.Pool)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
|
||||
}
|
||||
return bm.Destroy()
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
k = c.ParentKey
|
||||
c, ok = cfg.subnets[k]
|
||||
}
|
||||
|
||||
return func() error { return nil }, nil
|
||||
}
|
||||
|
||||
func (cfg *PoolsConfig) incRefCount(p *PoolData, delta int) {
|
||||
c := p
|
||||
ok := true
|
||||
for ok {
|
||||
c.RefCount += delta
|
||||
c, ok = cfg.subnets[c.ParentKey]
|
||||
}
|
||||
}
|
||||
|
||||
// Checks whether the passed subnet is a superset or subset of any of the subset in this config db
|
||||
func (cfg *PoolsConfig) contains(space string, nw *net.IPNet) bool {
|
||||
for k, v := range cfg.subnets {
|
||||
if space == k.AddressSpace && k.ChildSubnet == "" {
|
||||
if nw.Contains(v.Pool.IP) || v.Pool.Contains(nw.IP) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
128
libnetwork/ipam/utils.go
Normal file
128
libnetwork/ipam/utils.go
Normal file
|
@ -0,0 +1,128 @@
|
|||
package ipam
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
|
||||
"github.com/docker/libnetwork/ipamapi"
|
||||
"github.com/docker/libnetwork/types"
|
||||
)
|
||||
|
||||
type ipVersion int
|
||||
|
||||
const (
|
||||
v4 = 4
|
||||
v6 = 6
|
||||
)
|
||||
|
||||
func getAddressRange(pool string) (*AddressRange, error) {
|
||||
ip, nw, err := net.ParseCIDR(pool)
|
||||
if err != nil {
|
||||
return nil, ipamapi.ErrInvalidSubPool
|
||||
}
|
||||
lIP, e := types.GetHostPartIP(nw.IP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e)
|
||||
}
|
||||
bIP, e := types.GetBroadcastIP(nw.IP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e)
|
||||
}
|
||||
hIP, e := types.GetHostPartIP(bIP, nw.Mask)
|
||||
if e != nil {
|
||||
return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e)
|
||||
}
|
||||
nw.IP = ip
|
||||
return &AddressRange{nw, ipToUint32(types.GetMinimalIP(lIP)), ipToUint32(types.GetMinimalIP(hIP))}, nil
|
||||
}
|
||||
|
||||
func initLocalPredefinedPools() []*net.IPNet {
|
||||
pl := make([]*net.IPNet, 0, 274)
|
||||
mask := []byte{255, 255, 0, 0}
|
||||
for i := 17; i < 32; i++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{172, byte(i), 0, 0}, Mask: mask})
|
||||
}
|
||||
for i := 0; i < 256; i++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), 0, 0}, Mask: mask})
|
||||
}
|
||||
mask24 := []byte{255, 255, 255, 0}
|
||||
for i := 42; i < 45; i++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{192, 168, byte(i), 0}, Mask: mask24})
|
||||
}
|
||||
return pl
|
||||
}
|
||||
|
||||
func initGlobalPredefinedPools() []*net.IPNet {
|
||||
pl := make([]*net.IPNet, 0, 256*256)
|
||||
mask := []byte{255, 255, 255, 0}
|
||||
for i := 0; i < 256; i++ {
|
||||
for j := 0; j < 256; j++ {
|
||||
pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), byte(j), 0}, Mask: mask})
|
||||
}
|
||||
}
|
||||
return pl
|
||||
}
|
||||
|
||||
// Check subnets size. In case configured subnet is v6 and host size is
|
||||
// greater than 32 bits, adjust subnet to /96.
|
||||
func adjustAndCheckSubnetSize(subnet *net.IPNet) (*net.IPNet, error) {
|
||||
ones, bits := subnet.Mask.Size()
|
||||
if v6 == getAddressVersion(subnet.IP) {
|
||||
if ones < minNetSizeV6 {
|
||||
return nil, ipamapi.ErrInvalidPool
|
||||
}
|
||||
if ones < minNetSizeV6Eff {
|
||||
newMask := net.CIDRMask(minNetSizeV6Eff, bits)
|
||||
return &net.IPNet{IP: subnet.IP, Mask: newMask}, nil
|
||||
}
|
||||
} else {
|
||||
if ones < minNetSize {
|
||||
return nil, ipamapi.ErrInvalidPool
|
||||
}
|
||||
}
|
||||
return subnet, nil
|
||||
}
|
||||
|
||||
// It generates the ip address in the passed subnet specified by
|
||||
// the passed host address ordinal
|
||||
func generateAddress(ordinal uint32, network *net.IPNet) net.IP {
|
||||
var address [16]byte
|
||||
|
||||
// Get network portion of IP
|
||||
if getAddressVersion(network.IP) == v4 {
|
||||
copy(address[:], network.IP.To4())
|
||||
} else {
|
||||
copy(address[:], network.IP)
|
||||
}
|
||||
|
||||
end := len(network.Mask)
|
||||
addIntToIP(address[:end], ordinal)
|
||||
|
||||
return net.IP(address[:end])
|
||||
}
|
||||
|
||||
func getAddressVersion(ip net.IP) ipVersion {
|
||||
if ip.To4() == nil {
|
||||
return v6
|
||||
}
|
||||
return v4
|
||||
}
|
||||
|
||||
// Adds the ordinal IP to the current array
|
||||
// 192.168.0.0 + 53 => 192.168.53
|
||||
func addIntToIP(array []byte, ordinal uint32) {
|
||||
for i := len(array) - 1; i >= 0; i-- {
|
||||
array[i] |= (byte)(ordinal & 0xff)
|
||||
ordinal >>= 8
|
||||
}
|
||||
}
|
||||
|
||||
// Convert an ordinal to the respective IP address
|
||||
func ipToUint32(ip []byte) uint32 {
|
||||
value := uint32(0)
|
||||
for i := 0; i < len(ip); i++ {
|
||||
j := len(ip) - 1 - i
|
||||
value += uint32(ip[i]) << uint(j*8)
|
||||
}
|
||||
return value
|
||||
}
|
Loading…
Add table
Reference in a new issue