Signed-off-by: Alessandro Boch <aboch@docker.com>
| ... | ... |
@@ -1,14 +1,11 @@ |
| 1 | 1 |
package ipam |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
- "encoding/json" |
|
| 5 | 4 |
"fmt" |
| 6 | 5 |
"net" |
| 7 |
- "strings" |
|
| 8 | 6 |
"sync" |
| 9 | 7 |
|
| 10 | 8 |
log "github.com/Sirupsen/logrus" |
| 11 |
- |
|
| 12 | 9 |
"github.com/docker/libkv/store" |
| 13 | 10 |
"github.com/docker/libnetwork/bitseq" |
| 14 | 11 |
"github.com/docker/libnetwork/datastore" |
| ... | ... |
@@ -34,209 +31,86 @@ type Allocator struct {
|
| 34 | 34 |
// Predefined pools for default address spaces |
| 35 | 35 |
predefined map[string][]*net.IPNet |
| 36 | 36 |
// Static subnet information |
| 37 |
- subnets map[SubnetKey]*PoolData |
|
| 37 |
+ localSubnets *PoolsConfig |
|
| 38 |
+ globalSubnets *PoolsConfig |
|
| 38 | 39 |
// Allocated addresses in each address space's subnet |
| 39 | 40 |
addresses map[SubnetKey]*bitseq.Handle |
| 40 | 41 |
// Datastore |
| 41 |
- store datastore.DataStore |
|
| 42 |
- dbIndex uint64 |
|
| 43 |
- dbExists bool |
|
| 42 |
+ addrSpace2Configs map[string]*PoolsConfig |
|
| 44 | 43 |
sync.Mutex |
| 45 | 44 |
} |
| 46 | 45 |
|
| 47 | 46 |
// NewAllocator returns an instance of libnetwork ipam |
| 48 | 47 |
func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
|
| 49 | 48 |
a := &Allocator{}
|
| 50 |
- a.subnets = make(map[SubnetKey]*PoolData) |
|
| 51 |
- a.addresses = make(map[SubnetKey]*bitseq.Handle) |
|
| 52 |
- a.predefined = make(map[string][]*net.IPNet, 2) |
|
| 53 |
- a.predefined[localAddressSpace] = initLocalPredefinedPools() |
|
| 54 |
- a.predefined[globalAddressSpace] = initGlobalPredefinedPools() |
|
| 55 |
- a.store = glDs |
|
| 56 |
- |
|
| 57 |
- if a.store == nil {
|
|
| 58 |
- return a, nil |
|
| 59 |
- } |
|
| 60 |
- |
|
| 61 |
- // Register for status changes |
|
| 62 |
- a.watchForChanges() |
|
| 63 |
- |
|
| 64 |
- // Get the initial subnet configs status from the ds if present. |
|
| 65 |
- kvPair, err := a.store.KVStore().Get(datastore.Key(a.Key()...)) |
|
| 66 |
- if err != nil {
|
|
| 67 |
- if err != store.ErrKeyNotFound {
|
|
| 68 |
- return nil, fmt.Errorf("failed to retrieve the ipam subnet configs from datastore: %v", err)
|
|
| 69 |
- } |
|
| 70 |
- return a, nil |
|
| 71 |
- } |
|
| 72 |
- a.subnetConfigFromStore(kvPair) |
|
| 73 |
- |
|
| 74 |
- // Now retrieve the bitmasks for the master pools |
|
| 75 |
- var inserterList []func() error |
|
| 76 |
- a.Lock() |
|
| 77 |
- for k, v := range a.subnets {
|
|
| 78 |
- if v.Range == nil {
|
|
| 79 |
- inserterList = append(inserterList, func() error { return a.insertBitMask(k, v.Pool) })
|
|
| 80 |
- } |
|
| 81 |
- } |
|
| 82 |
- a.Unlock() |
|
| 83 |
- |
|
| 84 |
- // Add the bitmasks, data could come from datastore |
|
| 85 |
- for _, f := range inserterList {
|
|
| 86 |
- if err := f(); err != nil {
|
|
| 87 |
- return nil, err |
|
| 88 |
- } |
|
| 89 |
- } |
|
| 90 |
- |
|
| 91 |
- return a, nil |
|
| 92 |
-} |
|
| 93 |
- |
|
| 94 |
-func (a *Allocator) subnetConfigFromStore(kvPair *store.KVPair) {
|
|
| 95 |
- a.Lock() |
|
| 96 |
- if a.dbIndex < kvPair.LastIndex {
|
|
| 97 |
- a.SetValue(kvPair.Value) |
|
| 98 |
- a.dbIndex = kvPair.LastIndex |
|
| 99 |
- a.dbExists = true |
|
| 100 |
- } |
|
| 101 |
- a.Unlock() |
|
| 102 |
-} |
|
| 103 |
- |
|
| 104 |
-// SubnetKey is the pointer to the configured pools in each address space |
|
| 105 |
-type SubnetKey struct {
|
|
| 106 |
- AddressSpace string |
|
| 107 |
- Subnet string |
|
| 108 |
- ChildSubnet string |
|
| 109 |
-} |
|
| 110 |
- |
|
| 111 |
-// String returns the string form of the SubnetKey object |
|
| 112 |
-func (s *SubnetKey) String() string {
|
|
| 113 |
- k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet)
|
|
| 114 |
- if s.ChildSubnet != "" {
|
|
| 115 |
- k = fmt.Sprintf("%s/%s", k, s.ChildSubnet)
|
|
| 116 |
- } |
|
| 117 |
- return k |
|
| 118 |
-} |
|
| 119 | 49 |
|
| 120 |
-// FromString populate the SubnetKey object reading it from string |
|
| 121 |
-func (s *SubnetKey) FromString(str string) error {
|
|
| 122 |
- if str == "" || !strings.Contains(str, "/") {
|
|
| 123 |
- return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
|
| 50 |
+ a.localSubnets = &PoolsConfig{
|
|
| 51 |
+ subnets: map[SubnetKey]*PoolData{},
|
|
| 52 |
+ id: dsConfigKey + "/Pools", |
|
| 53 |
+ scope: datastore.LocalScope, |
|
| 54 |
+ ds: lcDs, |
|
| 55 |
+ alloc: a, |
|
| 124 | 56 |
} |
| 125 | 57 |
|
| 126 |
- p := strings.Split(str, "/") |
|
| 127 |
- if len(p) != 3 && len(p) != 5 {
|
|
| 128 |
- return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
|
| 129 |
- } |
|
| 130 |
- s.AddressSpace = p[0] |
|
| 131 |
- s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2])
|
|
| 132 |
- if len(p) == 5 {
|
|
| 133 |
- s.ChildSubnet = fmt.Sprintf("%s/%s", p[3], p[4])
|
|
| 58 |
+ a.globalSubnets = &PoolsConfig{
|
|
| 59 |
+ subnets: map[SubnetKey]*PoolData{},
|
|
| 60 |
+ id: dsConfigKey + "/Pools", |
|
| 61 |
+ scope: datastore.GlobalScope, |
|
| 62 |
+ ds: glDs, |
|
| 63 |
+ alloc: a, |
|
| 134 | 64 |
} |
| 135 | 65 |
|
| 136 |
- return nil |
|
| 137 |
-} |
|
| 138 |
- |
|
| 139 |
-// AddressRange specifies first and last ip ordinal which |
|
| 140 |
-// identify a range in a a pool of addresses |
|
| 141 |
-type AddressRange struct {
|
|
| 142 |
- Sub *net.IPNet |
|
| 143 |
- Start, End uint32 |
|
| 144 |
-} |
|
| 145 |
- |
|
| 146 |
-// String returns the string form of the AddressRange object |
|
| 147 |
-func (r *AddressRange) String() string {
|
|
| 148 |
- return fmt.Sprintf("Sub: %s, range [%d, %d]", r.Sub, r.Start, r.End)
|
|
| 149 |
-} |
|
| 150 |
- |
|
| 151 |
-// MarshalJSON returns the JSON encoding of the Range object |
|
| 152 |
-func (r *AddressRange) MarshalJSON() ([]byte, error) {
|
|
| 153 |
- m := map[string]interface{}{
|
|
| 154 |
- "Sub": r.Sub.String(), |
|
| 155 |
- "Start": r.Start, |
|
| 156 |
- "End": r.End, |
|
| 66 |
+ a.predefined = map[string][]*net.IPNet{
|
|
| 67 |
+ localAddressSpace: initLocalPredefinedPools(), |
|
| 68 |
+ globalAddressSpace: initGlobalPredefinedPools(), |
|
| 157 | 69 |
} |
| 158 |
- return json.Marshal(m) |
|
| 159 |
-} |
|
| 160 | 70 |
|
| 161 |
-// UnmarshalJSON decodes data into the Range object |
|
| 162 |
-func (r *AddressRange) UnmarshalJSON(data []byte) error {
|
|
| 163 |
- m := map[string]interface{}{}
|
|
| 164 |
- err := json.Unmarshal(data, &m) |
|
| 165 |
- if err != nil {
|
|
| 166 |
- return err |
|
| 71 |
+ a.addrSpace2Configs = map[string]*PoolsConfig{
|
|
| 72 |
+ localAddressSpace: a.localSubnets, |
|
| 73 |
+ globalAddressSpace: a.globalSubnets, |
|
| 167 | 74 |
} |
| 168 |
- if r.Sub, err = types.ParseCIDR(m["Sub"].(string)); err != nil {
|
|
| 169 |
- return err |
|
| 170 |
- } |
|
| 171 |
- r.Start = uint32(m["Start"].(float64)) |
|
| 172 |
- r.End = uint32(m["End"].(float64)) |
|
| 173 |
- return nil |
|
| 174 |
-} |
|
| 175 |
- |
|
| 176 |
-// PoolData contains the configured pool data |
|
| 177 |
-type PoolData struct {
|
|
| 178 |
- ParentKey SubnetKey |
|
| 179 |
- Pool *net.IPNet |
|
| 180 |
- Range *AddressRange `json:",omitempty"` |
|
| 181 |
- RefCount int |
|
| 182 |
-} |
|
| 183 | 75 |
|
| 184 |
-// String returns the string form of the PoolData object |
|
| 185 |
-func (p *PoolData) String() string {
|
|
| 186 |
- return fmt.Sprintf("ParentKey: %s, Pool: %s, Range: %s, RefCount: %d",
|
|
| 187 |
- p.ParentKey.String(), p.Pool.String(), p.Range, p.RefCount) |
|
| 188 |
-} |
|
| 76 |
+ a.addresses = make(map[SubnetKey]*bitseq.Handle) |
|
| 189 | 77 |
|
| 190 |
-// MarshalJSON returns the JSON encoding of the PoolData object |
|
| 191 |
-func (p *PoolData) MarshalJSON() ([]byte, error) {
|
|
| 192 |
- m := map[string]interface{}{
|
|
| 193 |
- "ParentKey": p.ParentKey, |
|
| 194 |
- "RefCount": p.RefCount, |
|
| 195 |
- } |
|
| 196 |
- if p.Pool != nil {
|
|
| 197 |
- m["Pool"] = p.Pool.String() |
|
| 78 |
+ cfgs := []struct {
|
|
| 79 |
+ cfg *PoolsConfig |
|
| 80 |
+ dsc string |
|
| 81 |
+ }{
|
|
| 82 |
+ {a.localSubnets, "local"},
|
|
| 83 |
+ {a.globalSubnets, "global"},
|
|
| 198 | 84 |
} |
| 199 |
- if p.Range != nil {
|
|
| 200 |
- m["Range"] = p.Range |
|
| 201 |
- } |
|
| 202 |
- return json.Marshal(m) |
|
| 203 |
-} |
|
| 204 |
- |
|
| 205 |
-// UnmarshalJSON decodes data into the PoolData object |
|
| 206 |
-func (p *PoolData) UnmarshalJSON(data []byte) error {
|
|
| 207 |
- var ( |
|
| 208 |
- err error |
|
| 209 |
- t struct {
|
|
| 210 |
- ParentKey SubnetKey |
|
| 211 |
- Pool string |
|
| 212 |
- Range *AddressRange `json:",omitempty"` |
|
| 213 |
- RefCount int |
|
| 85 |
+ // Get the initial local/global pools configfrom the datastores |
|
| 86 |
+ var inserterList []func() error |
|
| 87 |
+ for _, e := range cfgs {
|
|
| 88 |
+ if e.cfg.ds == nil {
|
|
| 89 |
+ continue |
|
| 214 | 90 |
} |
| 215 |
- ) |
|
| 216 |
- |
|
| 217 |
- if err = json.Unmarshal(data, &t); err != nil {
|
|
| 218 |
- return err |
|
| 91 |
+ if err := e.cfg.watchForChanges(); err != nil {
|
|
| 92 |
+ log.Warnf("Error on registering watch for %s datastore: %v", e.dsc, err)
|
|
| 93 |
+ } |
|
| 94 |
+ if err := e.cfg.readFromStore(); err != nil && err != store.ErrKeyNotFound {
|
|
| 95 |
+ return nil, fmt.Errorf("failed to retrieve the ipam %s pools config from datastore: %v", e.dsc, err)
|
|
| 96 |
+ } |
|
| 97 |
+ e.cfg.Lock() |
|
| 98 |
+ for k, v := range e.cfg.subnets {
|
|
| 99 |
+ if v.Range == nil {
|
|
| 100 |
+ inserterList = append(inserterList, func() error { return a.insertBitMask(e.cfg.ds, k, v.Pool) })
|
|
| 101 |
+ } |
|
| 102 |
+ } |
|
| 103 |
+ e.cfg.Unlock() |
|
| 219 | 104 |
} |
| 220 |
- |
|
| 221 |
- p.ParentKey = t.ParentKey |
|
| 222 |
- p.Range = t.Range |
|
| 223 |
- p.RefCount = t.RefCount |
|
| 224 |
- if t.Pool != "" {
|
|
| 225 |
- if p.Pool, err = types.ParseCIDR(t.Pool); err != nil {
|
|
| 226 |
- return err |
|
| 105 |
+ // Add the bitmasks (data could come from datastore) |
|
| 106 |
+ if inserterList != nil {
|
|
| 107 |
+ for _, f := range inserterList {
|
|
| 108 |
+ if err := f(); err != nil {
|
|
| 109 |
+ return nil, err |
|
| 110 |
+ } |
|
| 227 | 111 |
} |
| 228 | 112 |
} |
| 229 | 113 |
|
| 230 |
- return nil |
|
| 114 |
+ return a, nil |
|
| 231 | 115 |
} |
| 232 | 116 |
|
| 233 |
-type ipVersion int |
|
| 234 |
- |
|
| 235 |
-const ( |
|
| 236 |
- v4 = 4 |
|
| 237 |
- v6 = 6 |
|
| 238 |
-) |
|
| 239 |
- |
|
| 240 | 117 |
// GetDefaultAddressSpaces returns the local and global default address spaces |
| 241 | 118 |
func (a *Allocator) GetDefaultAddressSpaces() (string, string, error) {
|
| 242 | 119 |
return localAddressSpace, globalAddressSpace, nil |
| ... | ... |
@@ -248,16 +122,22 @@ func (a *Allocator) RequestPool(addressSpace, pool, subPool string, options map[ |
| 248 | 248 |
if err != nil {
|
| 249 | 249 |
return "", nil, nil, ipamapi.ErrInvalidPool |
| 250 | 250 |
} |
| 251 |
+ |
|
| 252 |
+ cfg, err := a.getPoolsConfig(addressSpace) |
|
| 253 |
+ if err != nil {
|
|
| 254 |
+ return "", nil, nil, err |
|
| 255 |
+ } |
|
| 256 |
+ |
|
| 251 | 257 |
retry: |
| 252 |
- insert, err := a.updatePoolDBOnAdd(*k, nw, ipr) |
|
| 258 |
+ insert, err := cfg.updatePoolDBOnAdd(*k, nw, ipr) |
|
| 253 | 259 |
if err != nil {
|
| 254 | 260 |
return "", nil, nil, err |
| 255 | 261 |
} |
| 256 |
- if err := a.writeToStore(); err != nil {
|
|
| 262 |
+ if err := cfg.writeToStore(); err != nil {
|
|
| 257 | 263 |
if _, ok := err.(types.RetryError); !ok {
|
| 258 | 264 |
return "", nil, nil, types.InternalErrorf("pool configuration failed because of %s", err.Error())
|
| 259 | 265 |
} |
| 260 |
- if erru := a.readFromStore(); erru != nil {
|
|
| 266 |
+ if erru := cfg.readFromStore(); erru != nil {
|
|
| 261 | 267 |
return "", nil, nil, fmt.Errorf("failed to get updated pool config from datastore (%v) after (%v)", erru, err)
|
| 262 | 268 |
} |
| 263 | 269 |
goto retry |
| ... | ... |
@@ -272,16 +152,21 @@ func (a *Allocator) ReleasePool(poolID string) error {
|
| 272 | 272 |
return types.BadRequestErrorf("invalid pool id: %s", poolID)
|
| 273 | 273 |
} |
| 274 | 274 |
|
| 275 |
+ cfg, err := a.getPoolsConfig(k.AddressSpace) |
|
| 276 |
+ if err != nil {
|
|
| 277 |
+ return err |
|
| 278 |
+ } |
|
| 279 |
+ |
|
| 275 | 280 |
retry: |
| 276 |
- remove, err := a.updatePoolDBOnRemoval(k) |
|
| 281 |
+ remove, err := cfg.updatePoolDBOnRemoval(k) |
|
| 277 | 282 |
if err != nil {
|
| 278 | 283 |
return err |
| 279 | 284 |
} |
| 280 |
- if err = a.writeToStore(); err != nil {
|
|
| 285 |
+ if err = cfg.writeToStore(); err != nil {
|
|
| 281 | 286 |
if _, ok := err.(types.RetryError); !ok {
|
| 282 | 287 |
return types.InternalErrorf("pool (%s) removal failed because of %v", poolID, err)
|
| 283 | 288 |
} |
| 284 |
- if erru := a.readFromStore(); erru != nil {
|
|
| 289 |
+ if erru := cfg.readFromStore(); erru != nil {
|
|
| 285 | 290 |
return fmt.Errorf("failed to get updated pool config from datastore (%v) after (%v)", erru, err)
|
| 286 | 291 |
} |
| 287 | 292 |
goto retry |
| ... | ... |
@@ -290,6 +175,18 @@ retry: |
| 290 | 290 |
return remove() |
| 291 | 291 |
} |
| 292 | 292 |
|
| 293 |
+// Given the address space, returns the local or global PoolConfig based on the |
|
| 294 |
+// address space is local or global. AddressSpace locality is being registered with IPAM out of band. |
|
| 295 |
+func (a *Allocator) getPoolsConfig(addrSpace string) (*PoolsConfig, error) {
|
|
| 296 |
+ a.Lock() |
|
| 297 |
+ defer a.Unlock() |
|
| 298 |
+ cfg, ok := a.addrSpace2Configs[addrSpace] |
|
| 299 |
+ if !ok {
|
|
| 300 |
+ return nil, types.BadRequestErrorf("cannot find locality of address space: %s", addrSpace)
|
|
| 301 |
+ } |
|
| 302 |
+ return cfg, nil |
|
| 303 |
+} |
|
| 304 |
+ |
|
| 293 | 305 |
func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool) (*SubnetKey, *net.IPNet, *net.IPNet, *AddressRange, error) {
|
| 294 | 306 |
var ( |
| 295 | 307 |
nw, aw *net.IPNet |
| ... | ... |
@@ -327,89 +224,7 @@ func (a *Allocator) parsePoolRequest(addressSpace, pool, subPool string, v6 bool |
| 327 | 327 |
return &SubnetKey{AddressSpace: addressSpace, Subnet: nw.String(), ChildSubnet: subPool}, nw, aw, ipr, nil
|
| 328 | 328 |
} |
| 329 | 329 |
|
| 330 |
-func (a *Allocator) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange) (func() error, error) {
|
|
| 331 |
- a.Lock() |
|
| 332 |
- defer a.Unlock() |
|
| 333 |
- |
|
| 334 |
- // Check if already allocated |
|
| 335 |
- if p, ok := a.subnets[k]; ok {
|
|
| 336 |
- a.incRefCount(p, 1) |
|
| 337 |
- return func() error { return nil }, nil
|
|
| 338 |
- } |
|
| 339 |
- |
|
| 340 |
- // If master pool, check for overlap |
|
| 341 |
- if ipr == nil {
|
|
| 342 |
- if a.contains(k.AddressSpace, nw) {
|
|
| 343 |
- return nil, ipamapi.ErrPoolOverlap |
|
| 344 |
- } |
|
| 345 |
- // This is a new master pool, add it along with corresponding bitmask |
|
| 346 |
- a.subnets[k] = &PoolData{Pool: nw, RefCount: 1}
|
|
| 347 |
- return func() error { return a.insertBitMask(k, nw) }, nil
|
|
| 348 |
- } |
|
| 349 |
- |
|
| 350 |
- // This is a new non-master pool |
|
| 351 |
- p := &PoolData{
|
|
| 352 |
- ParentKey: SubnetKey{AddressSpace: k.AddressSpace, Subnet: k.Subnet},
|
|
| 353 |
- Pool: nw, |
|
| 354 |
- Range: ipr, |
|
| 355 |
- RefCount: 1, |
|
| 356 |
- } |
|
| 357 |
- a.subnets[k] = p |
|
| 358 |
- |
|
| 359 |
- // Look for parent pool |
|
| 360 |
- pp, ok := a.subnets[p.ParentKey] |
|
| 361 |
- if ok {
|
|
| 362 |
- a.incRefCount(pp, 1) |
|
| 363 |
- return func() error { return nil }, nil
|
|
| 364 |
- } |
|
| 365 |
- |
|
| 366 |
- // Parent pool does not exist, add it along with corresponding bitmask |
|
| 367 |
- a.subnets[p.ParentKey] = &PoolData{Pool: nw, RefCount: 1}
|
|
| 368 |
- return func() error { return a.insertBitMask(p.ParentKey, nw) }, nil
|
|
| 369 |
-} |
|
| 370 |
- |
|
| 371 |
-func (a *Allocator) updatePoolDBOnRemoval(k SubnetKey) (func() error, error) {
|
|
| 372 |
- a.Lock() |
|
| 373 |
- defer a.Unlock() |
|
| 374 |
- |
|
| 375 |
- p, ok := a.subnets[k] |
|
| 376 |
- if !ok {
|
|
| 377 |
- return nil, ipamapi.ErrBadPool |
|
| 378 |
- } |
|
| 379 |
- |
|
| 380 |
- a.incRefCount(p, -1) |
|
| 381 |
- |
|
| 382 |
- c := p |
|
| 383 |
- for ok {
|
|
| 384 |
- if c.RefCount == 0 {
|
|
| 385 |
- delete(a.subnets, k) |
|
| 386 |
- if c.Range == nil {
|
|
| 387 |
- return func() error {
|
|
| 388 |
- bm, err := a.retrieveBitmask(k, c.Pool) |
|
| 389 |
- if err != nil {
|
|
| 390 |
- return fmt.Errorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
|
|
| 391 |
- } |
|
| 392 |
- return bm.Destroy() |
|
| 393 |
- }, nil |
|
| 394 |
- } |
|
| 395 |
- } |
|
| 396 |
- k = c.ParentKey |
|
| 397 |
- c, ok = a.subnets[k] |
|
| 398 |
- } |
|
| 399 |
- |
|
| 400 |
- return func() error { return nil }, nil
|
|
| 401 |
-} |
|
| 402 |
- |
|
| 403 |
-func (a *Allocator) incRefCount(p *PoolData, delta int) {
|
|
| 404 |
- c := p |
|
| 405 |
- ok := true |
|
| 406 |
- for ok {
|
|
| 407 |
- c.RefCount += delta |
|
| 408 |
- c, ok = a.subnets[c.ParentKey] |
|
| 409 |
- } |
|
| 410 |
-} |
|
| 411 |
- |
|
| 412 |
-func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
|
|
| 330 |
+func (a *Allocator) insertBitMask(store datastore.DataStore, key SubnetKey, pool *net.IPNet) error {
|
|
| 413 | 331 |
log.Debugf("Inserting bitmask (%s, %s)", key.String(), pool.String())
|
| 414 | 332 |
ipVer := getAddressVersion(pool.IP) |
| 415 | 333 |
ones, bits := pool.Mask.Size() |
| ... | ... |
@@ -421,7 +236,7 @@ func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
|
| 421 | 421 |
} |
| 422 | 422 |
|
| 423 | 423 |
// Generate the new address masks. AddressMask content may come from datastore |
| 424 |
- h, err := bitseq.NewHandle(dsDataKey, a.store, key.String(), numAddresses) |
|
| 424 |
+ h, err := bitseq.NewHandle(dsDataKey, store, key.String(), numAddresses) |
|
| 425 | 425 |
if err != nil {
|
| 426 | 426 |
return err |
| 427 | 427 |
} |
| ... | ... |
@@ -434,17 +249,16 @@ func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
|
| 434 | 434 |
a.Lock() |
| 435 | 435 |
a.addresses[key] = h |
| 436 | 436 |
a.Unlock() |
| 437 |
- |
|
| 438 | 437 |
return nil |
| 439 | 438 |
} |
| 440 | 439 |
|
| 441 |
-func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle, error) {
|
|
| 440 |
+func (a *Allocator) retrieveBitmask(ds datastore.DataStore, k SubnetKey, n *net.IPNet) (*bitseq.Handle, error) {
|
|
| 442 | 441 |
a.Lock() |
| 443 | 442 |
bm, ok := a.addresses[k] |
| 444 | 443 |
a.Unlock() |
| 445 | 444 |
if !ok {
|
| 446 | 445 |
log.Debugf("Retrieving bitmask (%s, %s)", k.String(), n.String())
|
| 447 |
- if err := a.insertBitMask(k, n); err != nil {
|
|
| 446 |
+ if err := a.insertBitMask(ds, k, n); err != nil {
|
|
| 448 | 447 |
return nil, fmt.Errorf("could not find bitmask in datastore for %s", k.String())
|
| 449 | 448 |
} |
| 450 | 449 |
a.Lock() |
| ... | ... |
@@ -475,18 +289,23 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) |
| 475 | 475 |
return nil, fmt.Errorf("no default pool availbale for non-default addresss spaces")
|
| 476 | 476 |
} |
| 477 | 477 |
|
| 478 |
+ cfg, err := a.getPoolsConfig(as) |
|
| 479 |
+ if err != nil {
|
|
| 480 |
+ return nil, err |
|
| 481 |
+ } |
|
| 482 |
+ |
|
| 478 | 483 |
for _, nw := range a.getPredefineds(as) {
|
| 479 | 484 |
if v != getAddressVersion(nw.IP) {
|
| 480 | 485 |
continue |
| 481 | 486 |
} |
| 482 |
- a.Lock() |
|
| 483 |
- _, ok := a.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]
|
|
| 484 |
- a.Unlock() |
|
| 487 |
+ cfg.Lock() |
|
| 488 |
+ _, ok := cfg.subnets[SubnetKey{AddressSpace: as, Subnet: nw.String()}]
|
|
| 489 |
+ cfg.Unlock() |
|
| 485 | 490 |
if ok {
|
| 486 | 491 |
continue |
| 487 | 492 |
} |
| 488 | 493 |
|
| 489 |
- if !a.contains(as, nw) {
|
|
| 494 |
+ if !cfg.contains(as, nw) {
|
|
| 490 | 495 |
if as == localAddressSpace {
|
| 491 | 496 |
if err := netutils.CheckRouteOverlaps(nw); err == nil {
|
| 492 | 497 |
return nw, nil |
| ... | ... |
@@ -500,38 +319,6 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) |
| 500 | 500 |
return nil, types.NotFoundErrorf("could not find an available predefined network")
|
| 501 | 501 |
} |
| 502 | 502 |
|
| 503 |
-// Check subnets size. In case configured subnet is v6 and host size is |
|
| 504 |
-// greater than 32 bits, adjust subnet to /96. |
|
| 505 |
-func adjustAndCheckSubnetSize(subnet *net.IPNet) (*net.IPNet, error) {
|
|
| 506 |
- ones, bits := subnet.Mask.Size() |
|
| 507 |
- if v6 == getAddressVersion(subnet.IP) {
|
|
| 508 |
- if ones < minNetSizeV6 {
|
|
| 509 |
- return nil, ipamapi.ErrInvalidPool |
|
| 510 |
- } |
|
| 511 |
- if ones < minNetSizeV6Eff {
|
|
| 512 |
- newMask := net.CIDRMask(minNetSizeV6Eff, bits) |
|
| 513 |
- return &net.IPNet{IP: subnet.IP, Mask: newMask}, nil
|
|
| 514 |
- } |
|
| 515 |
- } else {
|
|
| 516 |
- if ones < minNetSize {
|
|
| 517 |
- return nil, ipamapi.ErrInvalidPool |
|
| 518 |
- } |
|
| 519 |
- } |
|
| 520 |
- return subnet, nil |
|
| 521 |
-} |
|
| 522 |
- |
|
| 523 |
-// Checks whether the passed subnet is a superset or subset of any of the subset in the db |
|
| 524 |
-func (a *Allocator) contains(space string, nw *net.IPNet) bool {
|
|
| 525 |
- for k, v := range a.subnets {
|
|
| 526 |
- if space == k.AddressSpace && k.ChildSubnet == "" {
|
|
| 527 |
- if nw.Contains(v.Pool.IP) || v.Pool.Contains(nw.IP) {
|
|
| 528 |
- return true |
|
| 529 |
- } |
|
| 530 |
- } |
|
| 531 |
- } |
|
| 532 |
- return false |
|
| 533 |
-} |
|
| 534 |
- |
|
| 535 | 503 |
// RequestAddress returns an address from the specified pool ID |
| 536 | 504 |
func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[string]string) (*net.IPNet, map[string]string, error) {
|
| 537 | 505 |
k := SubnetKey{}
|
| ... | ... |
@@ -539,26 +326,31 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s |
| 539 | 539 |
return nil, nil, types.BadRequestErrorf("invalid pool id: %s", poolID)
|
| 540 | 540 |
} |
| 541 | 541 |
|
| 542 |
- a.Lock() |
|
| 543 |
- p, ok := a.subnets[k] |
|
| 542 |
+ cfg, err := a.getPoolsConfig(k.AddressSpace) |
|
| 543 |
+ if err != nil {
|
|
| 544 |
+ return nil, nil, err |
|
| 545 |
+ } |
|
| 546 |
+ |
|
| 547 |
+ cfg.Lock() |
|
| 548 |
+ p, ok := cfg.subnets[k] |
|
| 544 | 549 |
if !ok {
|
| 545 |
- a.Unlock() |
|
| 550 |
+ cfg.Unlock() |
|
| 546 | 551 |
return nil, nil, types.NotFoundErrorf("cannot find address pool for poolID:%s", poolID)
|
| 547 | 552 |
} |
| 548 | 553 |
|
| 549 | 554 |
if prefAddress != nil && !p.Pool.Contains(prefAddress) {
|
| 550 |
- a.Unlock() |
|
| 555 |
+ cfg.Unlock() |
|
| 551 | 556 |
return nil, nil, ipamapi.ErrIPOutOfRange |
| 552 | 557 |
} |
| 553 | 558 |
|
| 554 | 559 |
c := p |
| 555 | 560 |
for c.Range != nil {
|
| 556 | 561 |
k = c.ParentKey |
| 557 |
- c, ok = a.subnets[k] |
|
| 562 |
+ c, ok = cfg.subnets[k] |
|
| 558 | 563 |
} |
| 559 |
- a.Unlock() |
|
| 564 |
+ cfg.Unlock() |
|
| 560 | 565 |
|
| 561 |
- bm, err := a.retrieveBitmask(k, c.Pool) |
|
| 566 |
+ bm, err := a.retrieveBitmask(cfg.ds, k, c.Pool) |
|
| 562 | 567 |
if err != nil {
|
| 563 | 568 |
return nil, nil, fmt.Errorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v",
|
| 564 | 569 |
k.String(), prefAddress, poolID, err) |
| ... | ... |
@@ -578,24 +370,29 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
|
| 578 | 578 |
return types.BadRequestErrorf("invalid pool id: %s", poolID)
|
| 579 | 579 |
} |
| 580 | 580 |
|
| 581 |
- a.Lock() |
|
| 582 |
- p, ok := a.subnets[k] |
|
| 581 |
+ cfg, err := a.getPoolsConfig(k.AddressSpace) |
|
| 582 |
+ if err != nil {
|
|
| 583 |
+ return err |
|
| 584 |
+ } |
|
| 585 |
+ |
|
| 586 |
+ cfg.Lock() |
|
| 587 |
+ p, ok := cfg.subnets[k] |
|
| 583 | 588 |
if !ok {
|
| 584 |
- a.Unlock() |
|
| 589 |
+ cfg.Unlock() |
|
| 585 | 590 |
return ipamapi.ErrBadPool |
| 586 | 591 |
} |
| 587 | 592 |
|
| 588 | 593 |
if address == nil || !p.Pool.Contains(address) {
|
| 589 |
- a.Unlock() |
|
| 594 |
+ cfg.Unlock() |
|
| 590 | 595 |
return ipamapi.ErrInvalidRequest |
| 591 | 596 |
} |
| 592 | 597 |
|
| 593 | 598 |
c := p |
| 594 | 599 |
for c.Range != nil {
|
| 595 | 600 |
k = c.ParentKey |
| 596 |
- c = a.subnets[k] |
|
| 601 |
+ c = cfg.subnets[k] |
|
| 597 | 602 |
} |
| 598 |
- a.Unlock() |
|
| 603 |
+ cfg.Unlock() |
|
| 599 | 604 |
|
| 600 | 605 |
mask := p.Pool.Mask |
| 601 | 606 |
if p.Range != nil {
|
| ... | ... |
@@ -606,7 +403,7 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
|
| 606 | 606 |
return fmt.Errorf("failed to release address %s: %v", address.String(), err)
|
| 607 | 607 |
} |
| 608 | 608 |
|
| 609 |
- bm, err := a.retrieveBitmask(k, c.Pool) |
|
| 609 |
+ bm, err := cfg.alloc.retrieveBitmask(cfg.ds, k, c.Pool) |
|
| 610 | 610 |
if err != nil {
|
| 611 | 611 |
return fmt.Errorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v",
|
| 612 | 612 |
k.String(), address, poolID, err) |
| ... | ... |
@@ -652,10 +449,19 @@ func (a *Allocator) DumpDatabase() string {
|
| 652 | 652 |
a.Lock() |
| 653 | 653 |
defer a.Unlock() |
| 654 | 654 |
|
| 655 |
- s := fmt.Sprintf("\n\nPoolData")
|
|
| 656 |
- for k, config := range a.subnets {
|
|
| 655 |
+ s := fmt.Sprintf("\n\nLocal Pool Config")
|
|
| 656 |
+ a.localSubnets.Lock() |
|
| 657 |
+ for k, config := range a.localSubnets.subnets {
|
|
| 657 | 658 |
s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n%v: %v", k, config))
|
| 658 | 659 |
} |
| 660 |
+ a.localSubnets.Unlock() |
|
| 661 |
+ |
|
| 662 |
+ s = fmt.Sprintf("%s\n\nGlobal Pool Config", s)
|
|
| 663 |
+ a.globalSubnets.Lock() |
|
| 664 |
+ for k, config := range a.globalSubnets.subnets {
|
|
| 665 |
+ s = fmt.Sprintf("%s%s", s, fmt.Sprintf("\n%v: %v", k, config))
|
|
| 666 |
+ } |
|
| 667 |
+ a.globalSubnets.Unlock() |
|
| 659 | 668 |
|
| 660 | 669 |
s = fmt.Sprintf("%s\n\nBitmasks", s)
|
| 661 | 670 |
for k, bm := range a.addresses {
|
| ... | ... |
@@ -663,95 +469,3 @@ func (a *Allocator) DumpDatabase() string {
|
| 663 | 663 |
} |
| 664 | 664 |
return s |
| 665 | 665 |
} |
| 666 |
- |
|
| 667 |
-// It generates the ip address in the passed subnet specified by |
|
| 668 |
-// the passed host address ordinal |
|
| 669 |
-func generateAddress(ordinal uint32, network *net.IPNet) net.IP {
|
|
| 670 |
- var address [16]byte |
|
| 671 |
- |
|
| 672 |
- // Get network portion of IP |
|
| 673 |
- if getAddressVersion(network.IP) == v4 {
|
|
| 674 |
- copy(address[:], network.IP.To4()) |
|
| 675 |
- } else {
|
|
| 676 |
- copy(address[:], network.IP) |
|
| 677 |
- } |
|
| 678 |
- |
|
| 679 |
- end := len(network.Mask) |
|
| 680 |
- addIntToIP(address[:end], ordinal) |
|
| 681 |
- |
|
| 682 |
- return net.IP(address[:end]) |
|
| 683 |
-} |
|
| 684 |
- |
|
| 685 |
-func getAddressVersion(ip net.IP) ipVersion {
|
|
| 686 |
- if ip.To4() == nil {
|
|
| 687 |
- return v6 |
|
| 688 |
- } |
|
| 689 |
- return v4 |
|
| 690 |
-} |
|
| 691 |
- |
|
| 692 |
-// Adds the ordinal IP to the current array |
|
| 693 |
-// 192.168.0.0 + 53 => 192.168.53 |
|
| 694 |
-func addIntToIP(array []byte, ordinal uint32) {
|
|
| 695 |
- for i := len(array) - 1; i >= 0; i-- {
|
|
| 696 |
- array[i] |= (byte)(ordinal & 0xff) |
|
| 697 |
- ordinal >>= 8 |
|
| 698 |
- } |
|
| 699 |
-} |
|
| 700 |
- |
|
| 701 |
-// Convert an ordinal to the respective IP address |
|
| 702 |
-func ipToUint32(ip []byte) uint32 {
|
|
| 703 |
- value := uint32(0) |
|
| 704 |
- for i := 0; i < len(ip); i++ {
|
|
| 705 |
- j := len(ip) - 1 - i |
|
| 706 |
- value += uint32(ip[i]) << uint(j*8) |
|
| 707 |
- } |
|
| 708 |
- return value |
|
| 709 |
-} |
|
| 710 |
- |
|
| 711 |
-func initLocalPredefinedPools() []*net.IPNet {
|
|
| 712 |
- pl := make([]*net.IPNet, 0, 274) |
|
| 713 |
- mask := []byte{255, 255, 0, 0}
|
|
| 714 |
- for i := 17; i < 32; i++ {
|
|
| 715 |
- pl = append(pl, &net.IPNet{IP: []byte{172, byte(i), 0, 0}, Mask: mask})
|
|
| 716 |
- } |
|
| 717 |
- for i := 0; i < 256; i++ {
|
|
| 718 |
- pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), 0, 0}, Mask: mask})
|
|
| 719 |
- } |
|
| 720 |
- mask24 := []byte{255, 255, 255, 0}
|
|
| 721 |
- for i := 42; i < 45; i++ {
|
|
| 722 |
- pl = append(pl, &net.IPNet{IP: []byte{192, 168, byte(i), 0}, Mask: mask24})
|
|
| 723 |
- } |
|
| 724 |
- return pl |
|
| 725 |
-} |
|
| 726 |
- |
|
| 727 |
-func initGlobalPredefinedPools() []*net.IPNet {
|
|
| 728 |
- pl := make([]*net.IPNet, 0, 256*256) |
|
| 729 |
- mask := []byte{255, 255, 255, 0}
|
|
| 730 |
- for i := 0; i < 256; i++ {
|
|
| 731 |
- for j := 0; j < 256; j++ {
|
|
| 732 |
- pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), byte(j), 0}, Mask: mask})
|
|
| 733 |
- } |
|
| 734 |
- } |
|
| 735 |
- return pl |
|
| 736 |
-} |
|
| 737 |
- |
|
| 738 |
-func getAddressRange(pool string) (*AddressRange, error) {
|
|
| 739 |
- ip, nw, err := net.ParseCIDR(pool) |
|
| 740 |
- if err != nil {
|
|
| 741 |
- return nil, ipamapi.ErrInvalidSubPool |
|
| 742 |
- } |
|
| 743 |
- lIP, e := types.GetHostPartIP(nw.IP, nw.Mask) |
|
| 744 |
- if e != nil {
|
|
| 745 |
- return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e)
|
|
| 746 |
- } |
|
| 747 |
- bIP, e := types.GetBroadcastIP(nw.IP, nw.Mask) |
|
| 748 |
- if e != nil {
|
|
| 749 |
- return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e)
|
|
| 750 |
- } |
|
| 751 |
- hIP, e := types.GetHostPartIP(bIP, nw.Mask) |
|
| 752 |
- if e != nil {
|
|
| 753 |
- return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e)
|
|
| 754 |
- } |
|
| 755 |
- nw.IP = ip |
|
| 756 |
- return &AddressRange{nw, ipToUint32(types.GetMinimalIP(lIP)), ipToUint32(types.GetMinimalIP(hIP))}, nil
|
|
| 757 |
-} |
| ... | ... |
@@ -1,14 +1,15 @@ |
| 1 | 1 |
package ipam |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 |
+ "encoding/json" |
|
| 4 | 5 |
"fmt" |
| 6 |
+ "io/ioutil" |
|
| 5 | 7 |
"net" |
| 6 | 8 |
"os" |
| 7 | 9 |
"testing" |
| 8 | 10 |
"time" |
| 9 | 11 |
|
| 10 |
- "encoding/json" |
|
| 11 |
- |
|
| 12 |
+ "github.com/docker/libkv/store" |
|
| 12 | 13 |
"github.com/docker/libnetwork/bitseq" |
| 13 | 14 |
"github.com/docker/libnetwork/config" |
| 14 | 15 |
"github.com/docker/libnetwork/datastore" |
| ... | ... |
@@ -18,12 +19,35 @@ import ( |
| 18 | 18 |
"github.com/docker/libnetwork/types" |
| 19 | 19 |
) |
| 20 | 20 |
|
| 21 |
-var ds datastore.DataStore |
|
| 21 |
+const ( |
|
| 22 |
+ defaultPrefix = "/tmp/libnetwork/test/ipam" |
|
| 23 |
+) |
|
| 24 |
+ |
|
| 25 |
+// OptionBoltdbWithRandomDBFile function returns a random dir for local store backend |
|
| 26 |
+func randomLocalStore() (datastore.DataStore, error) {
|
|
| 27 |
+ tmp, err := ioutil.TempFile("", "libnetwork-")
|
|
| 28 |
+ if err != nil {
|
|
| 29 |
+ return nil, fmt.Errorf("Error creating temp file: %v", err)
|
|
| 30 |
+ } |
|
| 31 |
+ if err := tmp.Close(); err != nil {
|
|
| 32 |
+ return nil, fmt.Errorf("Error closing temp file: %v", err)
|
|
| 33 |
+ } |
|
| 34 |
+ return datastore.NewDataStore(&config.DatastoreCfg{
|
|
| 35 |
+ Embedded: true, |
|
| 36 |
+ Client: config.DatastoreClientCfg{
|
|
| 37 |
+ Provider: "boltdb", |
|
| 38 |
+ Address: defaultPrefix + tmp.Name(), |
|
| 39 |
+ Config: &store.Config{
|
|
| 40 |
+ Bucket: "libnetwork", |
|
| 41 |
+ ConnectionTimeout: 3 * time.Second, |
|
| 42 |
+ }, |
|
| 43 |
+ }, |
|
| 44 |
+ }) |
|
| 45 |
+} |
|
| 22 | 46 |
|
| 23 | 47 |
// enable w/ upper case |
| 24 |
-func testMain(m *testing.M) {
|
|
| 48 |
+func TestMain(m *testing.M) {
|
|
| 25 | 49 |
var err error |
| 26 |
- ds, err = datastore.NewDataStore(&config.DatastoreCfg{Embedded: false, Client: config.DatastoreClientCfg{Provider: "consul", Address: "127.0.0.1:8500"}})
|
|
| 27 | 50 |
if err != nil {
|
| 28 | 51 |
fmt.Println(err) |
| 29 | 52 |
} |
| ... | ... |
@@ -31,16 +55,16 @@ func testMain(m *testing.M) {
|
| 31 | 31 |
os.Exit(m.Run()) |
| 32 | 32 |
} |
| 33 | 33 |
|
| 34 |
-func getAllocator(t *testing.T, subnet string) (*Allocator, string) {
|
|
| 35 |
- a, err := NewAllocator(nil, ds) |
|
| 34 |
+func getAllocator() (*Allocator, error) {
|
|
| 35 |
+ ds, err := randomLocalStore() |
|
| 36 | 36 |
if err != nil {
|
| 37 |
- t.Fatal(err) |
|
| 37 |
+ return nil, err |
|
| 38 | 38 |
} |
| 39 |
- poolID, _, _, err := a.RequestPool("default", subnet, "", nil, false)
|
|
| 39 |
+ a, err := NewAllocator(ds, nil) |
|
| 40 | 40 |
if err != nil {
|
| 41 |
- t.Fatal(err) |
|
| 41 |
+ return nil, err |
|
| 42 | 42 |
} |
| 43 |
- return a, poolID |
|
| 43 |
+ return a, nil |
|
| 44 | 44 |
} |
| 45 | 45 |
|
| 46 | 46 |
func TestInt2IP2IntConversion(t *testing.T) {
|
| ... | ... |
@@ -70,7 +94,6 @@ func TestGetAddressVersion(t *testing.T) {
|
| 70 | 70 |
} |
| 71 | 71 |
|
| 72 | 72 |
func TestKeyString(t *testing.T) {
|
| 73 |
- |
|
| 74 | 73 |
k := &SubnetKey{AddressSpace: "default", Subnet: "172.27.0.0/16"}
|
| 75 | 74 |
expected := "default/172.27.0.0/16" |
| 76 | 75 |
if expected != k.String() {
|
| ... | ... |
@@ -151,8 +174,10 @@ func TestPoolDataMarshal(t *testing.T) {
|
| 151 | 151 |
} |
| 152 | 152 |
|
| 153 | 153 |
func TestSubnetsMarshal(t *testing.T) {
|
| 154 |
- a, _ := NewAllocator(nil, nil) |
|
| 155 |
- |
|
| 154 |
+ a, err := getAllocator() |
|
| 155 |
+ if err != nil {
|
|
| 156 |
+ t.Fatal(err) |
|
| 157 |
+ } |
|
| 156 | 158 |
pid0, _, _, err := a.RequestPool(localAddressSpace, "192.168.0.0/16", "", nil, false) |
| 157 | 159 |
if err != nil {
|
| 158 | 160 |
t.Fatal(err) |
| ... | ... |
@@ -166,9 +191,9 @@ func TestSubnetsMarshal(t *testing.T) {
|
| 166 | 166 |
t.Fatal(err) |
| 167 | 167 |
} |
| 168 | 168 |
|
| 169 |
- ba := a.Value() |
|
| 170 |
- a.subnets = make(map[SubnetKey]*PoolData, 0) |
|
| 171 |
- if err := a.SetValue(ba); err != nil {
|
|
| 169 |
+ cfg := a.localSubnets |
|
| 170 |
+ ba := cfg.Value() |
|
| 171 |
+ if err := cfg.SetValue(ba); err != nil {
|
|
| 172 | 172 |
t.Fatal(err) |
| 173 | 173 |
} |
| 174 | 174 |
|
| ... | ... |
@@ -192,12 +217,13 @@ func TestSubnetsMarshal(t *testing.T) {
|
| 192 | 192 |
} |
| 193 | 193 |
|
| 194 | 194 |
func TestAddSubnets(t *testing.T) {
|
| 195 |
- a, err := NewAllocator(nil, nil) |
|
| 195 |
+ a, err := getAllocator() |
|
| 196 | 196 |
if err != nil {
|
| 197 | 197 |
t.Fatal(err) |
| 198 | 198 |
} |
| 199 |
+ a.addrSpace2Configs["abc"] = a.addrSpace2Configs[localAddressSpace] |
|
| 199 | 200 |
|
| 200 |
- pid0, _, _, err := a.RequestPool("default", "10.0.0.0/8", "", nil, false)
|
|
| 201 |
+ pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) |
|
| 201 | 202 |
if err != nil {
|
| 202 | 203 |
t.Fatalf("Unexpected failure in adding subnet")
|
| 203 | 204 |
} |
| ... | ... |
@@ -236,22 +262,22 @@ func TestAddSubnets(t *testing.T) {
|
| 236 | 236 |
t.Fatalf("returned different pool id for same sub pool requests")
|
| 237 | 237 |
} |
| 238 | 238 |
|
| 239 |
- pid, _, _, err = a.RequestPool("default", "10.20.2.0/24", "", nil, false)
|
|
| 239 |
+ pid, _, _, err = a.RequestPool(localAddressSpace, "10.20.2.0/24", "", nil, false) |
|
| 240 | 240 |
if err == nil {
|
| 241 | 241 |
t.Fatalf("Failed to detect overlapping subnets")
|
| 242 | 242 |
} |
| 243 | 243 |
|
| 244 |
- _, _, _, err = a.RequestPool("default", "10.128.0.0/9", "", nil, false)
|
|
| 244 |
+ _, _, _, err = a.RequestPool(localAddressSpace, "10.128.0.0/9", "", nil, false) |
|
| 245 | 245 |
if err == nil {
|
| 246 | 246 |
t.Fatalf("Failed to detect overlapping subnets")
|
| 247 | 247 |
} |
| 248 | 248 |
|
| 249 |
- _, _, _, err = a.RequestPool("default", "1003:1:2:3:4:5:6::/112", "", nil, false)
|
|
| 249 |
+ _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3:4:5:6::/112", "", nil, false) |
|
| 250 | 250 |
if err != nil {
|
| 251 | 251 |
t.Fatalf("Failed to add v6 subnet: %s", err.Error())
|
| 252 | 252 |
} |
| 253 | 253 |
|
| 254 |
- _, _, _, err = a.RequestPool("default", "1003:1:2:3::/64", "", nil, false)
|
|
| 254 |
+ _, _, _, err = a.RequestPool(localAddressSpace, "1003:1:2:3::/64", "", nil, false) |
|
| 255 | 255 |
if err == nil {
|
| 256 | 256 |
t.Fatalf("Failed to detect overlapping v6 subnet")
|
| 257 | 257 |
} |
| ... | ... |
@@ -259,34 +285,35 @@ func TestAddSubnets(t *testing.T) {
|
| 259 | 259 |
|
| 260 | 260 |
func TestAddReleasePoolID(t *testing.T) {
|
| 261 | 261 |
var k0, k1, k2 SubnetKey |
| 262 |
- a, err := NewAllocator(nil, nil) |
|
| 262 |
+ |
|
| 263 |
+ a, err := getAllocator() |
|
| 263 | 264 |
if err != nil {
|
| 264 | 265 |
t.Fatal(err) |
| 265 | 266 |
} |
| 266 |
- |
|
| 267 |
- pid0, _, _, err := a.RequestPool("default", "10.0.0.0/8", "", nil, false)
|
|
| 267 |
+ subnets := a.localSubnets.subnets |
|
| 268 |
+ pid0, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) |
|
| 268 | 269 |
if err != nil {
|
| 269 | 270 |
t.Fatalf("Unexpected failure in adding pool")
|
| 270 | 271 |
} |
| 271 | 272 |
if err := k0.FromString(pid0); err != nil {
|
| 272 | 273 |
t.Fatal(err) |
| 273 | 274 |
} |
| 274 |
- if a.subnets[k0].RefCount != 1 {
|
|
| 275 |
- t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
|
| 275 |
+ if subnets[k0].RefCount != 1 {
|
|
| 276 |
+ t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
|
| 276 | 277 |
} |
| 277 | 278 |
|
| 278 |
- pid1, _, _, err := a.RequestPool("default", "10.0.0.0/8", "10.0.0.0/16", nil, false)
|
|
| 279 |
+ pid1, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) |
|
| 279 | 280 |
if err != nil {
|
| 280 | 281 |
t.Fatalf("Unexpected failure in adding sub pool")
|
| 281 | 282 |
} |
| 282 | 283 |
if err := k1.FromString(pid1); err != nil {
|
| 283 | 284 |
t.Fatal(err) |
| 284 | 285 |
} |
| 285 |
- if a.subnets[k1].RefCount != 1 {
|
|
| 286 |
- t.Fatalf("Unexpected ref count for %s: %d", k1, a.subnets[k1].RefCount)
|
|
| 286 |
+ if subnets[k1].RefCount != 1 {
|
|
| 287 |
+ t.Fatalf("Unexpected ref count for %s: %d", k1, subnets[k1].RefCount)
|
|
| 287 | 288 |
} |
| 288 | 289 |
|
| 289 |
- pid2, _, _, err := a.RequestPool("default", "10.0.0.0/8", "10.0.0.0/16", nil, false)
|
|
| 290 |
+ pid2, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "10.0.0.0/16", nil, false) |
|
| 290 | 291 |
if err != nil {
|
| 291 | 292 |
t.Fatalf("Unexpected failure in adding sub pool")
|
| 292 | 293 |
} |
| ... | ... |
@@ -296,63 +323,63 @@ func TestAddReleasePoolID(t *testing.T) {
|
| 296 | 296 |
if err := k2.FromString(pid2); err != nil {
|
| 297 | 297 |
t.Fatal(err) |
| 298 | 298 |
} |
| 299 |
- if a.subnets[k2].RefCount != 2 {
|
|
| 300 |
- t.Fatalf("Unexpected ref count for %s: %d", k2, a.subnets[k2].RefCount)
|
|
| 299 |
+ if subnets[k2].RefCount != 2 {
|
|
| 300 |
+ t.Fatalf("Unexpected ref count for %s: %d", k2, subnets[k2].RefCount)
|
|
| 301 | 301 |
} |
| 302 | 302 |
|
| 303 |
- if a.subnets[k0].RefCount != 3 {
|
|
| 304 |
- t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
|
| 303 |
+ if subnets[k0].RefCount != 3 {
|
|
| 304 |
+ t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
|
| 305 | 305 |
} |
| 306 | 306 |
|
| 307 | 307 |
if err := a.ReleasePool(pid1); err != nil {
|
| 308 | 308 |
t.Fatal(err) |
| 309 | 309 |
} |
| 310 |
- if a.subnets[k0].RefCount != 2 {
|
|
| 311 |
- t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
|
| 310 |
+ if subnets[k0].RefCount != 2 {
|
|
| 311 |
+ t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
|
| 312 | 312 |
} |
| 313 | 313 |
if err := a.ReleasePool(pid0); err != nil {
|
| 314 | 314 |
t.Fatal(err) |
| 315 | 315 |
} |
| 316 |
- if a.subnets[k0].RefCount != 1 {
|
|
| 317 |
- t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
|
| 316 |
+ if subnets[k0].RefCount != 1 {
|
|
| 317 |
+ t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
|
| 318 | 318 |
} |
| 319 | 319 |
|
| 320 |
- pid00, _, _, err := a.RequestPool("default", "10.0.0.0/8", "", nil, false)
|
|
| 320 |
+ pid00, _, _, err := a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) |
|
| 321 | 321 |
if err != nil {
|
| 322 | 322 |
t.Fatalf("Unexpected failure in adding pool")
|
| 323 | 323 |
} |
| 324 | 324 |
if pid00 != pid0 {
|
| 325 | 325 |
t.Fatalf("main pool should still exist")
|
| 326 | 326 |
} |
| 327 |
- if a.subnets[k0].RefCount != 2 {
|
|
| 328 |
- t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
|
| 327 |
+ if subnets[k0].RefCount != 2 {
|
|
| 328 |
+ t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
|
| 329 | 329 |
} |
| 330 | 330 |
|
| 331 | 331 |
if err := a.ReleasePool(pid2); err != nil {
|
| 332 | 332 |
t.Fatal(err) |
| 333 | 333 |
} |
| 334 |
- if a.subnets[k0].RefCount != 1 {
|
|
| 335 |
- t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
|
| 334 |
+ if subnets[k0].RefCount != 1 {
|
|
| 335 |
+ t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
|
| 336 | 336 |
} |
| 337 | 337 |
|
| 338 | 338 |
if err := a.ReleasePool(pid00); err != nil {
|
| 339 | 339 |
t.Fatal(err) |
| 340 | 340 |
} |
| 341 |
- if bp, ok := a.subnets[k0]; ok {
|
|
| 341 |
+ if bp, ok := subnets[k0]; ok {
|
|
| 342 | 342 |
t.Fatalf("Base pool %s is still present: %v", k0, bp)
|
| 343 | 343 |
} |
| 344 | 344 |
|
| 345 |
- _, _, _, err = a.RequestPool("default", "10.0.0.0/8", "", nil, false)
|
|
| 345 |
+ _, _, _, err = a.RequestPool(localAddressSpace, "10.0.0.0/8", "", nil, false) |
|
| 346 | 346 |
if err != nil {
|
| 347 | 347 |
t.Fatalf("Unexpected failure in adding pool")
|
| 348 | 348 |
} |
| 349 |
- if a.subnets[k0].RefCount != 1 {
|
|
| 350 |
- t.Fatalf("Unexpected ref count for %s: %d", k0, a.subnets[k0].RefCount)
|
|
| 349 |
+ if subnets[k0].RefCount != 1 {
|
|
| 350 |
+ t.Fatalf("Unexpected ref count for %s: %d", k0, subnets[k0].RefCount)
|
|
| 351 | 351 |
} |
| 352 | 352 |
} |
| 353 | 353 |
|
| 354 | 354 |
func TestPredefinedPool(t *testing.T) {
|
| 355 |
- a, err := NewAllocator(nil, nil) |
|
| 355 |
+ a, err := getAllocator() |
|
| 356 | 356 |
if err != nil {
|
| 357 | 357 |
t.Fatal(err) |
| 358 | 358 |
} |
| ... | ... |
@@ -444,29 +471,31 @@ func TestAdjustAndCheckSubnet(t *testing.T) {
|
| 444 | 444 |
} |
| 445 | 445 |
|
| 446 | 446 |
func TestRemoveSubnet(t *testing.T) {
|
| 447 |
- a, err := NewAllocator(nil, nil) |
|
| 447 |
+ a, err := getAllocator() |
|
| 448 | 448 |
if err != nil {
|
| 449 | 449 |
t.Fatal(err) |
| 450 | 450 |
} |
| 451 |
+ a.addrSpace2Configs["splane"] = a.addrSpace2Configs[localAddressSpace] |
|
| 451 | 452 |
|
| 452 | 453 |
input := []struct {
|
| 453 | 454 |
addrSpace string |
| 454 | 455 |
subnet string |
| 456 |
+ v6 bool |
|
| 455 | 457 |
}{
|
| 456 |
- {"default", "192.168.0.0/16"},
|
|
| 457 |
- {"default", "172.17.0.0/16"},
|
|
| 458 |
- {"default", "10.0.0.0/8"},
|
|
| 459 |
- {"default", "2002:1:2:3:4:5:ffff::/112"},
|
|
| 460 |
- {"splane", "172.17.0.0/16"},
|
|
| 461 |
- {"splane", "10.0.0.0/8"},
|
|
| 462 |
- {"splane", "2002:1:2:3:4:5:6::/112"},
|
|
| 463 |
- {"splane", "2002:1:2:3:4:5:ffff::/112"},
|
|
| 458 |
+ {localAddressSpace, "192.168.0.0/16", false},
|
|
| 459 |
+ {localAddressSpace, "172.17.0.0/16", false},
|
|
| 460 |
+ {localAddressSpace, "10.0.0.0/8", false},
|
|
| 461 |
+ {localAddressSpace, "2002:1:2:3:4:5:ffff::/112", false},
|
|
| 462 |
+ {"splane", "172.17.0.0/16", false},
|
|
| 463 |
+ {"splane", "10.0.0.0/8", false},
|
|
| 464 |
+ {"splane", "2002:1:2:3:4:5:6::/112", true},
|
|
| 465 |
+ {"splane", "2002:1:2:3:4:5:ffff::/112", true},
|
|
| 464 | 466 |
} |
| 465 | 467 |
|
| 466 | 468 |
poolIDs := make([]string, len(input)) |
| 467 | 469 |
|
| 468 | 470 |
for ind, i := range input {
|
| 469 |
- if poolIDs[ind], _, _, err = a.RequestPool(i.addrSpace, i.subnet, "", nil, false); err != nil {
|
|
| 471 |
+ if poolIDs[ind], _, _, err = a.RequestPool(i.addrSpace, i.subnet, "", nil, i.v6); err != nil {
|
|
| 470 | 472 |
t.Fatalf("Failed to apply input. Can't proceed: %s", err.Error())
|
| 471 | 473 |
} |
| 472 | 474 |
} |
| ... | ... |
@@ -479,10 +508,11 @@ func TestRemoveSubnet(t *testing.T) {
|
| 479 | 479 |
} |
| 480 | 480 |
|
| 481 | 481 |
func TestGetSameAddress(t *testing.T) {
|
| 482 |
- a, err := NewAllocator(nil, nil) |
|
| 482 |
+ a, err := getAllocator() |
|
| 483 | 483 |
if err != nil {
|
| 484 | 484 |
t.Fatal(err) |
| 485 | 485 |
} |
| 486 |
+ a.addrSpace2Configs["giallo"] = a.addrSpace2Configs[localAddressSpace] |
|
| 486 | 487 |
|
| 487 | 488 |
pid, _, _, err := a.RequestPool("giallo", "192.168.100.0/24", "", nil, false)
|
| 488 | 489 |
if err != nil {
|
| ... | ... |
@@ -502,10 +532,11 @@ func TestGetSameAddress(t *testing.T) {
|
| 502 | 502 |
} |
| 503 | 503 |
|
| 504 | 504 |
func TestRequestReleaseAddressFromSubPool(t *testing.T) {
|
| 505 |
- a, err := NewAllocator(nil, nil) |
|
| 505 |
+ a, err := getAllocator() |
|
| 506 | 506 |
if err != nil {
|
| 507 | 507 |
t.Fatal(err) |
| 508 | 508 |
} |
| 509 |
+ a.addrSpace2Configs["rosso"] = a.addrSpace2Configs[localAddressSpace] |
|
| 509 | 510 |
|
| 510 | 511 |
poolID, _, _, err := a.RequestPool("rosso", "172.28.0.0/16", "172.28.30.0/24", nil, false)
|
| 511 | 512 |
if err != nil {
|
| ... | ... |
@@ -587,11 +618,16 @@ func TestRequestSyntaxCheck(t *testing.T) {
|
| 587 | 587 |
pool = "192.168.0.0/16" |
| 588 | 588 |
subPool = "192.168.0.0/24" |
| 589 | 589 |
addrSpace = "green" |
| 590 |
+ err error |
|
| 590 | 591 |
) |
| 591 | 592 |
|
| 592 |
- a, _ := NewAllocator(nil, nil) |
|
| 593 |
+ a, err := getAllocator() |
|
| 594 |
+ if err != nil {
|
|
| 595 |
+ t.Fatal(err) |
|
| 596 |
+ } |
|
| 597 |
+ a.addrSpace2Configs[addrSpace] = a.addrSpace2Configs[localAddressSpace] |
|
| 593 | 598 |
|
| 594 |
- _, _, _, err := a.RequestPool("", pool, "", nil, false)
|
|
| 599 |
+ _, _, _, err = a.RequestPool("", pool, "", nil, false)
|
|
| 595 | 600 |
if err == nil {
|
| 596 | 601 |
t.Fatalf("Failed to detect wrong request: empty address space")
|
| 597 | 602 |
} |
| ... | ... |
@@ -661,12 +697,14 @@ func TestRequest(t *testing.T) {
|
| 661 | 661 |
{"10.0.0.0/8", 256, "10.0.1.0"},
|
| 662 | 662 |
|
| 663 | 663 |
{"192.168.128.0/18", 4*256 - 1, "192.168.131.255"},
|
| 664 |
- {"192.168.240.0/20", 16*256 - 2, "192.168.255.254"},
|
|
| 664 |
+ /* |
|
| 665 |
+ {"192.168.240.0/20", 16*256 - 2, "192.168.255.254"},
|
|
| 665 | 666 |
|
| 666 |
- {"192.168.0.0/16", 256*256 - 2, "192.168.255.254"},
|
|
| 667 |
- {"10.0.0.0/8", 2 * 256, "10.0.2.0"},
|
|
| 668 |
- {"10.0.0.0/8", 5 * 256, "10.0.5.0"},
|
|
| 669 |
- //{"10.0.0.0/8", 100 * 256 * 254, "10.99.255.254"},
|
|
| 667 |
+ {"192.168.0.0/16", 256*256 - 2, "192.168.255.254"},
|
|
| 668 |
+ {"10.0.0.0/8", 2 * 256, "10.0.2.0"},
|
|
| 669 |
+ {"10.0.0.0/8", 5 * 256, "10.0.5.0"},
|
|
| 670 |
+ {"10.0.0.0/8", 100 * 256 * 254, "10.99.255.254"},
|
|
| 671 |
+ */ |
|
| 670 | 672 |
} |
| 671 | 673 |
|
| 672 | 674 |
for _, d := range input {
|
| ... | ... |
@@ -676,12 +714,19 @@ func TestRequest(t *testing.T) {
|
| 676 | 676 |
|
| 677 | 677 |
func TestRelease(t *testing.T) {
|
| 678 | 678 |
var ( |
| 679 |
- err error |
|
| 680 |
- subnet = "192.168.0.0/16" |
|
| 679 |
+ subnet = "192.168.0.0/23" |
|
| 681 | 680 |
) |
| 682 | 681 |
|
| 683 |
- a, pid := getAllocator(t, subnet) |
|
| 684 |
- bm := a.addresses[SubnetKey{"default", subnet, ""}]
|
|
| 682 |
+ a, err := getAllocator() |
|
| 683 |
+ if err != nil {
|
|
| 684 |
+ t.Fatal(err) |
|
| 685 |
+ } |
|
| 686 |
+ pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) |
|
| 687 |
+ if err != nil {
|
|
| 688 |
+ t.Fatal(err) |
|
| 689 |
+ } |
|
| 690 |
+ |
|
| 691 |
+ bm := a.addresses[SubnetKey{localAddressSpace, subnet, ""}]
|
|
| 685 | 692 |
|
| 686 | 693 |
// Allocate all addresses |
| 687 | 694 |
for err != ipamapi.ErrNoAvailableIPs {
|
| ... | ... |
@@ -711,8 +756,8 @@ func TestRelease(t *testing.T) {
|
| 711 | 711 |
|
| 712 | 712 |
{"192.168.1.3"},
|
| 713 | 713 |
|
| 714 |
- {"192.168.255.253"},
|
|
| 715 |
- {"192.168.255.254"},
|
|
| 714 |
+ {"192.168.1.253"},
|
|
| 715 |
+ {"192.168.1.254"},
|
|
| 716 | 716 |
} |
| 717 | 717 |
|
| 718 | 718 |
// 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) {
|
| 773 | 773 |
|
| 774 | 774 |
func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP string) {
|
| 775 | 775 |
var ( |
| 776 |
- err error |
|
| 777 | 776 |
nw *net.IPNet |
| 778 | 777 |
printTime = false |
| 779 | 778 |
) |
| 780 | 779 |
|
| 781 | 780 |
lastIP := net.ParseIP(lastExpectedIP) |
| 782 |
- a, pid := getAllocator(t, subnet) |
|
| 781 |
+ a, err := getAllocator() |
|
| 782 |
+ if err != nil {
|
|
| 783 |
+ t.Fatal(err) |
|
| 784 |
+ } |
|
| 785 |
+ pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) |
|
| 786 |
+ if err != nil {
|
|
| 787 |
+ t.Fatal(err) |
|
| 788 |
+ } |
|
| 783 | 789 |
|
| 784 | 790 |
i := 0 |
| 785 | 791 |
start := time.Now() |
| ... | ... |
@@ -795,31 +846,31 @@ func assertNRequests(t *testing.T, subnet string, numReq int, lastExpectedIP str |
| 795 | 795 |
} |
| 796 | 796 |
} |
| 797 | 797 |
|
| 798 |
-func benchmarkRequest(subnet string) {
|
|
| 799 |
- var err error |
|
| 800 |
- |
|
| 801 |
- a, _ := NewAllocator(nil, nil) |
|
| 802 |
- pid, _, _, _ := a.RequestPool("default", subnet, "", nil, false)
|
|
| 803 |
- |
|
| 798 |
+func benchmarkRequest(b *testing.B, a *Allocator, subnet string) {
|
|
| 799 |
+ pid, _, _, err := a.RequestPool(localAddressSpace, subnet, "", nil, false) |
|
| 804 | 800 |
for err != ipamapi.ErrNoAvailableIPs {
|
| 805 | 801 |
_, _, err = a.RequestAddress(pid, nil, nil) |
| 806 | 802 |
} |
| 807 | 803 |
} |
| 808 | 804 |
|
| 809 | 805 |
func benchMarkRequest(subnet string, b *testing.B) {
|
| 806 |
+ a, _ := getAllocator() |
|
| 810 | 807 |
for n := 0; n < b.N; n++ {
|
| 811 |
- benchmarkRequest(subnet) |
|
| 808 |
+ benchmarkRequest(b, a, subnet) |
|
| 812 | 809 |
} |
| 813 | 810 |
} |
| 814 | 811 |
|
| 815 | 812 |
func BenchmarkRequest_24(b *testing.B) {
|
| 816 |
- benchmarkRequest("10.0.0.0/24")
|
|
| 813 |
+ a, _ := getAllocator() |
|
| 814 |
+ benchmarkRequest(b, a, "10.0.0.0/24") |
|
| 817 | 815 |
} |
| 818 | 816 |
|
| 819 | 817 |
func BenchmarkRequest_16(b *testing.B) {
|
| 820 |
- benchmarkRequest("10.0.0.0/16")
|
|
| 818 |
+ a, _ := getAllocator() |
|
| 819 |
+ benchmarkRequest(b, a, "10.0.0.0/16") |
|
| 821 | 820 |
} |
| 822 | 821 |
|
| 823 | 822 |
func BenchmarkRequest_8(b *testing.B) {
|
| 824 |
- benchmarkRequest("10.0.0.0/8")
|
|
| 823 |
+ a, _ := getAllocator() |
|
| 824 |
+ benchmarkRequest(b, a, "10.0.0.0/8") |
|
| 825 | 825 |
} |
| ... | ... |
@@ -4,95 +4,77 @@ import ( |
| 4 | 4 |
"encoding/json" |
| 5 | 5 |
|
| 6 | 6 |
log "github.com/Sirupsen/logrus" |
| 7 |
+ "github.com/docker/libkv/store" |
|
| 7 | 8 |
"github.com/docker/libnetwork/datastore" |
| 8 | 9 |
"github.com/docker/libnetwork/types" |
| 9 | 10 |
) |
| 10 | 11 |
|
| 11 | 12 |
// Key provides the Key to be used in KV Store |
| 12 |
-func (a *Allocator) Key() []string {
|
|
| 13 |
- a.Lock() |
|
| 14 |
- defer a.Unlock() |
|
| 15 |
- return []string{dsConfigKey}
|
|
| 13 |
+func (cfg *PoolsConfig) Key() []string {
|
|
| 14 |
+ cfg.Lock() |
|
| 15 |
+ defer cfg.Unlock() |
|
| 16 |
+ return []string{cfg.id}
|
|
| 16 | 17 |
} |
| 17 | 18 |
|
| 18 | 19 |
// KeyPrefix returns the immediate parent key that can be used for tree walk |
| 19 |
-func (a *Allocator) KeyPrefix() []string {
|
|
| 20 |
- a.Lock() |
|
| 21 |
- defer a.Unlock() |
|
| 20 |
+func (cfg *PoolsConfig) KeyPrefix() []string {
|
|
| 21 |
+ cfg.Lock() |
|
| 22 |
+ defer cfg.Unlock() |
|
| 22 | 23 |
return []string{dsConfigKey}
|
| 23 | 24 |
} |
| 24 | 25 |
|
| 25 | 26 |
// Value marshals the data to be stored in the KV store |
| 26 |
-func (a *Allocator) Value() []byte {
|
|
| 27 |
- a.Lock() |
|
| 28 |
- defer a.Unlock() |
|
| 29 |
- |
|
| 30 |
- if a.subnets == nil {
|
|
| 31 |
- return []byte{}
|
|
| 32 |
- } |
|
| 33 |
- m := map[string]interface{}{}
|
|
| 34 |
- for k, v := range a.subnets {
|
|
| 35 |
- m[k.String()] = v |
|
| 36 |
- } |
|
| 37 |
- |
|
| 38 |
- b, err := json.Marshal(m) |
|
| 27 |
+func (cfg *PoolsConfig) Value() []byte {
|
|
| 28 |
+ b, err := json.Marshal(cfg) |
|
| 39 | 29 |
if err != nil {
|
| 40 |
- log.Warnf("Failed to marshal ipam configured subnets")
|
|
| 30 |
+ log.Warnf("Failed to marshal ipam configured pools: %v", err)
|
|
| 41 | 31 |
return nil |
| 42 | 32 |
} |
| 43 | 33 |
return b |
| 44 | 34 |
} |
| 45 | 35 |
|
| 46 | 36 |
// SetValue unmarshalls the data from the KV store. |
| 47 |
-func (a *Allocator) SetValue(value []byte) error {
|
|
| 48 |
- var m map[string]*PoolData |
|
| 49 |
- err := json.Unmarshal(value, &m) |
|
| 50 |
- if err != nil {
|
|
| 37 |
+func (cfg *PoolsConfig) SetValue(value []byte) error {
|
|
| 38 |
+ rc := &PoolsConfig{subnets: make(map[SubnetKey]*PoolData)}
|
|
| 39 |
+ if err := json.Unmarshal(value, rc); err != nil {
|
|
| 51 | 40 |
return err |
| 52 | 41 |
} |
| 53 |
- for ks, d := range m {
|
|
| 54 |
- k := SubnetKey{}
|
|
| 55 |
- if err := k.FromString(ks); err != nil {
|
|
| 56 |
- return err |
|
| 57 |
- } |
|
| 58 |
- a.subnets[k] = d |
|
| 59 |
- } |
|
| 42 |
+ cfg.subnets = rc.subnets |
|
| 60 | 43 |
return nil |
| 61 | 44 |
} |
| 62 | 45 |
|
| 63 | 46 |
// Index returns the latest DB Index as seen by this object |
| 64 |
-func (a *Allocator) Index() uint64 {
|
|
| 65 |
- a.Lock() |
|
| 66 |
- defer a.Unlock() |
|
| 67 |
- return a.dbIndex |
|
| 47 |
+func (cfg *PoolsConfig) Index() uint64 {
|
|
| 48 |
+ cfg.Lock() |
|
| 49 |
+ defer cfg.Unlock() |
|
| 50 |
+ return cfg.dbIndex |
|
| 68 | 51 |
} |
| 69 | 52 |
|
| 70 | 53 |
// SetIndex method allows the datastore to store the latest DB Index into this object |
| 71 |
-func (a *Allocator) SetIndex(index uint64) {
|
|
| 72 |
- a.Lock() |
|
| 73 |
- a.dbIndex = index |
|
| 74 |
- a.dbExists = true |
|
| 75 |
- a.Unlock() |
|
| 54 |
+func (cfg *PoolsConfig) SetIndex(index uint64) {
|
|
| 55 |
+ cfg.Lock() |
|
| 56 |
+ cfg.dbIndex = index |
|
| 57 |
+ cfg.dbExists = true |
|
| 58 |
+ cfg.Unlock() |
|
| 76 | 59 |
} |
| 77 | 60 |
|
| 78 | 61 |
// Exists method is true if this object has been stored in the DB. |
| 79 |
-func (a *Allocator) Exists() bool {
|
|
| 80 |
- a.Lock() |
|
| 81 |
- defer a.Unlock() |
|
| 82 |
- return a.dbExists |
|
| 62 |
+func (cfg *PoolsConfig) Exists() bool {
|
|
| 63 |
+ cfg.Lock() |
|
| 64 |
+ defer cfg.Unlock() |
|
| 65 |
+ return cfg.dbExists |
|
| 83 | 66 |
} |
| 84 | 67 |
|
| 85 | 68 |
// Skip provides a way for a KV Object to avoid persisting it in the KV Store |
| 86 |
-func (a *Allocator) Skip() bool {
|
|
| 69 |
+func (cfg *PoolsConfig) Skip() bool {
|
|
| 87 | 70 |
return false |
| 88 | 71 |
} |
| 89 | 72 |
|
| 90 |
-func (a *Allocator) watchForChanges() error {
|
|
| 91 |
- if a.store == nil {
|
|
| 73 |
+func (cfg *PoolsConfig) watchForChanges() error {
|
|
| 74 |
+ if cfg.ds == nil {
|
|
| 92 | 75 |
return nil |
| 93 | 76 |
} |
| 94 |
- |
|
| 95 |
- kvpChan, err := a.store.KVStore().Watch(datastore.Key(a.Key()...), nil) |
|
| 77 |
+ kvpChan, err := cfg.ds.KVStore().Watch(datastore.Key(cfg.Key()...), nil) |
|
| 96 | 78 |
if err != nil {
|
| 97 | 79 |
return err |
| 98 | 80 |
} |
| ... | ... |
@@ -101,7 +83,7 @@ func (a *Allocator) watchForChanges() error {
|
| 101 | 101 |
select {
|
| 102 | 102 |
case kvPair := <-kvpChan: |
| 103 | 103 |
if kvPair != nil {
|
| 104 |
- a.subnetConfigFromStore(kvPair) |
|
| 104 |
+ cfg.readFromKey(kvPair) |
|
| 105 | 105 |
} |
| 106 | 106 |
} |
| 107 | 107 |
} |
| ... | ... |
@@ -109,50 +91,40 @@ func (a *Allocator) watchForChanges() error {
|
| 109 | 109 |
return nil |
| 110 | 110 |
} |
| 111 | 111 |
|
| 112 |
-func (a *Allocator) readFromStore() error {
|
|
| 113 |
- a.Lock() |
|
| 114 |
- store := a.store |
|
| 115 |
- a.Unlock() |
|
| 116 |
- |
|
| 117 |
- if store == nil {
|
|
| 112 |
+func (cfg *PoolsConfig) writeToStore() error {
|
|
| 113 |
+ if cfg.ds == nil {
|
|
| 118 | 114 |
return nil |
| 119 | 115 |
} |
| 120 |
- |
|
| 121 |
- kvPair, err := a.store.KVStore().Get(datastore.Key(a.Key()...)) |
|
| 122 |
- if err != nil {
|
|
| 123 |
- return err |
|
| 116 |
+ err := cfg.ds.PutObjectAtomic(cfg) |
|
| 117 |
+ if err == datastore.ErrKeyModified {
|
|
| 118 |
+ return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err)
|
|
| 124 | 119 |
} |
| 125 |
- |
|
| 126 |
- a.subnetConfigFromStore(kvPair) |
|
| 127 |
- |
|
| 128 |
- return nil |
|
| 120 |
+ return err |
|
| 129 | 121 |
} |
| 130 | 122 |
|
| 131 |
-func (a *Allocator) writeToStore() error {
|
|
| 132 |
- a.Lock() |
|
| 133 |
- store := a.store |
|
| 134 |
- a.Unlock() |
|
| 135 |
- if store == nil {
|
|
| 123 |
+func (cfg *PoolsConfig) readFromStore() error {
|
|
| 124 |
+ if cfg.ds == nil {
|
|
| 136 | 125 |
return nil |
| 137 | 126 |
} |
| 138 |
- err := store.PutObjectAtomic(a) |
|
| 139 |
- if err == datastore.ErrKeyModified {
|
|
| 140 |
- return types.RetryErrorf("failed to perform atomic write (%v). retry might fix the error", err)
|
|
| 127 |
+ return cfg.ds.GetObject(datastore.Key(cfg.Key()...), cfg) |
|
| 128 |
+} |
|
| 129 |
+ |
|
| 130 |
+func (cfg *PoolsConfig) readFromKey(kvPair *store.KVPair) {
|
|
| 131 |
+ if cfg.dbIndex < kvPair.LastIndex {
|
|
| 132 |
+ cfg.SetValue(kvPair.Value) |
|
| 133 |
+ cfg.dbIndex = kvPair.LastIndex |
|
| 134 |
+ cfg.dbExists = true |
|
| 141 | 135 |
} |
| 142 |
- return err |
|
| 143 | 136 |
} |
| 144 | 137 |
|
| 145 |
-func (a *Allocator) deleteFromStore() error {
|
|
| 146 |
- a.Lock() |
|
| 147 |
- store := a.store |
|
| 148 |
- a.Unlock() |
|
| 149 |
- if store == nil {
|
|
| 138 |
+func (cfg *PoolsConfig) deleteFromStore() error {
|
|
| 139 |
+ if cfg.ds == nil {
|
|
| 150 | 140 |
return nil |
| 151 | 141 |
} |
| 152 |
- return store.DeleteObjectAtomic(a) |
|
| 142 |
+ return cfg.ds.DeleteObjectAtomic(cfg) |
|
| 153 | 143 |
} |
| 154 | 144 |
|
| 155 | 145 |
// DataScope method returns the storage scope of the datastore |
| 156 |
-func (a *Allocator) DataScope() datastore.DataScope {
|
|
| 157 |
- return datastore.GlobalScope |
|
| 146 |
+func (cfg *PoolsConfig) DataScope() datastore.DataScope {
|
|
| 147 |
+ return cfg.scope |
|
| 158 | 148 |
} |
| 159 | 149 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,302 @@ |
| 0 |
+package ipam |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "encoding/json" |
|
| 4 |
+ "fmt" |
|
| 5 |
+ "net" |
|
| 6 |
+ "strings" |
|
| 7 |
+ "sync" |
|
| 8 |
+ |
|
| 9 |
+ "github.com/docker/libnetwork/datastore" |
|
| 10 |
+ "github.com/docker/libnetwork/ipamapi" |
|
| 11 |
+ "github.com/docker/libnetwork/types" |
|
| 12 |
+) |
|
| 13 |
+ |
|
| 14 |
+// SubnetKey is the pointer to the configured pools in each address space |
|
| 15 |
+type SubnetKey struct {
|
|
| 16 |
+ AddressSpace string |
|
| 17 |
+ Subnet string |
|
| 18 |
+ ChildSubnet string |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+// PoolData contains the configured pool data |
|
| 22 |
+type PoolData struct {
|
|
| 23 |
+ ParentKey SubnetKey |
|
| 24 |
+ Pool *net.IPNet |
|
| 25 |
+ Range *AddressRange `json:",omitempty"` |
|
| 26 |
+ RefCount int |
|
| 27 |
+} |
|
| 28 |
+ |
|
| 29 |
+// PoolsConfig contains the pool configurations |
|
| 30 |
+type PoolsConfig struct {
|
|
| 31 |
+ subnets map[SubnetKey]*PoolData |
|
| 32 |
+ dbIndex uint64 |
|
| 33 |
+ dbExists bool |
|
| 34 |
+ id string |
|
| 35 |
+ scope datastore.DataScope |
|
| 36 |
+ ds datastore.DataStore |
|
| 37 |
+ alloc *Allocator |
|
| 38 |
+ sync.Mutex |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+// AddressRange specifies first and last ip ordinal which |
|
| 42 |
+// identify a range in a a pool of addresses |
|
| 43 |
+type AddressRange struct {
|
|
| 44 |
+ Sub *net.IPNet |
|
| 45 |
+ Start, End uint32 |
|
| 46 |
+} |
|
| 47 |
+ |
|
| 48 |
+// String returns the string form of the AddressRange object |
|
| 49 |
+func (r *AddressRange) String() string {
|
|
| 50 |
+ return fmt.Sprintf("Sub: %s, range [%d, %d]", r.Sub, r.Start, r.End)
|
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// MarshalJSON returns the JSON encoding of the Range object |
|
| 54 |
+func (r *AddressRange) MarshalJSON() ([]byte, error) {
|
|
| 55 |
+ m := map[string]interface{}{
|
|
| 56 |
+ "Sub": r.Sub.String(), |
|
| 57 |
+ "Start": r.Start, |
|
| 58 |
+ "End": r.End, |
|
| 59 |
+ } |
|
| 60 |
+ return json.Marshal(m) |
|
| 61 |
+} |
|
| 62 |
+ |
|
| 63 |
+// UnmarshalJSON decodes data into the Range object |
|
| 64 |
+func (r *AddressRange) UnmarshalJSON(data []byte) error {
|
|
| 65 |
+ m := map[string]interface{}{}
|
|
| 66 |
+ err := json.Unmarshal(data, &m) |
|
| 67 |
+ if err != nil {
|
|
| 68 |
+ return err |
|
| 69 |
+ } |
|
| 70 |
+ if r.Sub, err = types.ParseCIDR(m["Sub"].(string)); err != nil {
|
|
| 71 |
+ return err |
|
| 72 |
+ } |
|
| 73 |
+ r.Start = uint32(m["Start"].(float64)) |
|
| 74 |
+ r.End = uint32(m["End"].(float64)) |
|
| 75 |
+ return nil |
|
| 76 |
+} |
|
| 77 |
+ |
|
| 78 |
+// String returns the string form of the SubnetKey object |
|
| 79 |
+func (s *SubnetKey) String() string {
|
|
| 80 |
+ k := fmt.Sprintf("%s/%s", s.AddressSpace, s.Subnet)
|
|
| 81 |
+ if s.ChildSubnet != "" {
|
|
| 82 |
+ k = fmt.Sprintf("%s/%s", k, s.ChildSubnet)
|
|
| 83 |
+ } |
|
| 84 |
+ return k |
|
| 85 |
+} |
|
| 86 |
+ |
|
| 87 |
+// FromString populate the SubnetKey object reading it from string |
|
| 88 |
+func (s *SubnetKey) FromString(str string) error {
|
|
| 89 |
+ if str == "" || !strings.Contains(str, "/") {
|
|
| 90 |
+ return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
|
| 91 |
+ } |
|
| 92 |
+ |
|
| 93 |
+ p := strings.Split(str, "/") |
|
| 94 |
+ if len(p) != 3 && len(p) != 5 {
|
|
| 95 |
+ return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
|
| 96 |
+ } |
|
| 97 |
+ s.AddressSpace = p[0] |
|
| 98 |
+ s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2])
|
|
| 99 |
+ if len(p) == 5 {
|
|
| 100 |
+ s.ChildSubnet = fmt.Sprintf("%s/%s", p[3], p[4])
|
|
| 101 |
+ } |
|
| 102 |
+ |
|
| 103 |
+ return nil |
|
| 104 |
+} |
|
| 105 |
+ |
|
| 106 |
+// String returns the string form of the PoolData object |
|
| 107 |
+func (p *PoolData) String() string {
|
|
| 108 |
+ return fmt.Sprintf("ParentKey: %s, Pool: %s, Range: %s, RefCount: %d",
|
|
| 109 |
+ p.ParentKey.String(), p.Pool.String(), p.Range, p.RefCount) |
|
| 110 |
+} |
|
| 111 |
+ |
|
| 112 |
+// MarshalJSON returns the JSON encoding of the PoolData object |
|
| 113 |
+func (p *PoolData) MarshalJSON() ([]byte, error) {
|
|
| 114 |
+ m := map[string]interface{}{
|
|
| 115 |
+ "ParentKey": p.ParentKey, |
|
| 116 |
+ "RefCount": p.RefCount, |
|
| 117 |
+ } |
|
| 118 |
+ if p.Pool != nil {
|
|
| 119 |
+ m["Pool"] = p.Pool.String() |
|
| 120 |
+ } |
|
| 121 |
+ if p.Range != nil {
|
|
| 122 |
+ m["Range"] = p.Range |
|
| 123 |
+ } |
|
| 124 |
+ return json.Marshal(m) |
|
| 125 |
+} |
|
| 126 |
+ |
|
| 127 |
+// UnmarshalJSON decodes data into the PoolData object |
|
| 128 |
+func (p *PoolData) UnmarshalJSON(data []byte) error {
|
|
| 129 |
+ var ( |
|
| 130 |
+ err error |
|
| 131 |
+ t struct {
|
|
| 132 |
+ ParentKey SubnetKey |
|
| 133 |
+ Pool string |
|
| 134 |
+ Range *AddressRange `json:",omitempty"` |
|
| 135 |
+ RefCount int |
|
| 136 |
+ } |
|
| 137 |
+ ) |
|
| 138 |
+ |
|
| 139 |
+ if err = json.Unmarshal(data, &t); err != nil {
|
|
| 140 |
+ return err |
|
| 141 |
+ } |
|
| 142 |
+ |
|
| 143 |
+ p.ParentKey = t.ParentKey |
|
| 144 |
+ p.Range = t.Range |
|
| 145 |
+ p.RefCount = t.RefCount |
|
| 146 |
+ if t.Pool != "" {
|
|
| 147 |
+ if p.Pool, err = types.ParseCIDR(t.Pool); err != nil {
|
|
| 148 |
+ return err |
|
| 149 |
+ } |
|
| 150 |
+ } |
|
| 151 |
+ |
|
| 152 |
+ return nil |
|
| 153 |
+} |
|
| 154 |
+ |
|
| 155 |
+// MarshalJSON returns the JSON encoding of the PoolsConfig object |
|
| 156 |
+func (cfg *PoolsConfig) MarshalJSON() ([]byte, error) {
|
|
| 157 |
+ cfg.Lock() |
|
| 158 |
+ defer cfg.Unlock() |
|
| 159 |
+ |
|
| 160 |
+ m := map[string]interface{}{
|
|
| 161 |
+ "Scope": string(cfg.scope), |
|
| 162 |
+ } |
|
| 163 |
+ |
|
| 164 |
+ if cfg.subnets != nil {
|
|
| 165 |
+ s := map[string]*PoolData{}
|
|
| 166 |
+ for k, v := range cfg.subnets {
|
|
| 167 |
+ s[k.String()] = v |
|
| 168 |
+ } |
|
| 169 |
+ m["Subnets"] = s |
|
| 170 |
+ } |
|
| 171 |
+ |
|
| 172 |
+ return json.Marshal(m) |
|
| 173 |
+} |
|
| 174 |
+ |
|
| 175 |
+// UnmarshalJSON decodes data into the PoolsConfig object |
|
| 176 |
+func (cfg *PoolsConfig) UnmarshalJSON(data []byte) error {
|
|
| 177 |
+ cfg.Lock() |
|
| 178 |
+ defer cfg.Unlock() |
|
| 179 |
+ |
|
| 180 |
+ m := map[string]interface{}{}
|
|
| 181 |
+ err := json.Unmarshal(data, &m) |
|
| 182 |
+ if err != nil {
|
|
| 183 |
+ return err |
|
| 184 |
+ } |
|
| 185 |
+ |
|
| 186 |
+ cfg.scope = datastore.LocalScope |
|
| 187 |
+ s := m["Scope"].(string) |
|
| 188 |
+ if s == string(datastore.GlobalScope) {
|
|
| 189 |
+ cfg.scope = datastore.GlobalScope |
|
| 190 |
+ } |
|
| 191 |
+ |
|
| 192 |
+ if v, ok := m["Subnets"]; ok {
|
|
| 193 |
+ sb, _ := json.Marshal(v) |
|
| 194 |
+ var s map[string]*PoolData |
|
| 195 |
+ err := json.Unmarshal(sb, &s) |
|
| 196 |
+ if err != nil {
|
|
| 197 |
+ return err |
|
| 198 |
+ } |
|
| 199 |
+ for ks, v := range s {
|
|
| 200 |
+ k := SubnetKey{}
|
|
| 201 |
+ k.FromString(ks) |
|
| 202 |
+ cfg.subnets[k] = v |
|
| 203 |
+ } |
|
| 204 |
+ } |
|
| 205 |
+ |
|
| 206 |
+ return nil |
|
| 207 |
+} |
|
| 208 |
+ |
|
| 209 |
+func (cfg *PoolsConfig) updatePoolDBOnAdd(k SubnetKey, nw *net.IPNet, ipr *AddressRange) (func() error, error) {
|
|
| 210 |
+ cfg.Lock() |
|
| 211 |
+ defer cfg.Unlock() |
|
| 212 |
+ |
|
| 213 |
+ // Check if already allocated |
|
| 214 |
+ if p, ok := cfg.subnets[k]; ok {
|
|
| 215 |
+ cfg.incRefCount(p, 1) |
|
| 216 |
+ return func() error { return nil }, nil
|
|
| 217 |
+ } |
|
| 218 |
+ |
|
| 219 |
+ // If master pool, check for overlap |
|
| 220 |
+ if ipr == nil {
|
|
| 221 |
+ if cfg.contains(k.AddressSpace, nw) {
|
|
| 222 |
+ return nil, ipamapi.ErrPoolOverlap |
|
| 223 |
+ } |
|
| 224 |
+ // This is a new master pool, add it along with corresponding bitmask |
|
| 225 |
+ cfg.subnets[k] = &PoolData{Pool: nw, RefCount: 1}
|
|
| 226 |
+ return func() error { return cfg.alloc.insertBitMask(cfg.ds, k, nw) }, nil
|
|
| 227 |
+ } |
|
| 228 |
+ |
|
| 229 |
+ // This is a new non-master pool |
|
| 230 |
+ p := &PoolData{
|
|
| 231 |
+ ParentKey: SubnetKey{AddressSpace: k.AddressSpace, Subnet: k.Subnet},
|
|
| 232 |
+ Pool: nw, |
|
| 233 |
+ Range: ipr, |
|
| 234 |
+ RefCount: 1, |
|
| 235 |
+ } |
|
| 236 |
+ cfg.subnets[k] = p |
|
| 237 |
+ |
|
| 238 |
+ // Look for parent pool |
|
| 239 |
+ pp, ok := cfg.subnets[p.ParentKey] |
|
| 240 |
+ if ok {
|
|
| 241 |
+ cfg.incRefCount(pp, 1) |
|
| 242 |
+ return func() error { return nil }, nil
|
|
| 243 |
+ } |
|
| 244 |
+ |
|
| 245 |
+ // Parent pool does not exist, add it along with corresponding bitmask |
|
| 246 |
+ cfg.subnets[p.ParentKey] = &PoolData{Pool: nw, RefCount: 1}
|
|
| 247 |
+ return func() error { return cfg.alloc.insertBitMask(cfg.ds, p.ParentKey, nw) }, nil
|
|
| 248 |
+} |
|
| 249 |
+ |
|
| 250 |
+func (cfg *PoolsConfig) updatePoolDBOnRemoval(k SubnetKey) (func() error, error) {
|
|
| 251 |
+ cfg.Lock() |
|
| 252 |
+ defer cfg.Unlock() |
|
| 253 |
+ |
|
| 254 |
+ p, ok := cfg.subnets[k] |
|
| 255 |
+ if !ok {
|
|
| 256 |
+ return nil, ipamapi.ErrBadPool |
|
| 257 |
+ } |
|
| 258 |
+ |
|
| 259 |
+ cfg.incRefCount(p, -1) |
|
| 260 |
+ |
|
| 261 |
+ c := p |
|
| 262 |
+ for ok {
|
|
| 263 |
+ if c.RefCount == 0 {
|
|
| 264 |
+ delete(cfg.subnets, k) |
|
| 265 |
+ if c.Range == nil {
|
|
| 266 |
+ return func() error {
|
|
| 267 |
+ bm, err := cfg.alloc.retrieveBitmask(cfg.ds, k, c.Pool) |
|
| 268 |
+ if err != nil {
|
|
| 269 |
+ return fmt.Errorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
|
|
| 270 |
+ } |
|
| 271 |
+ return bm.Destroy() |
|
| 272 |
+ }, nil |
|
| 273 |
+ } |
|
| 274 |
+ } |
|
| 275 |
+ k = c.ParentKey |
|
| 276 |
+ c, ok = cfg.subnets[k] |
|
| 277 |
+ } |
|
| 278 |
+ |
|
| 279 |
+ return func() error { return nil }, nil
|
|
| 280 |
+} |
|
| 281 |
+ |
|
| 282 |
+func (cfg *PoolsConfig) incRefCount(p *PoolData, delta int) {
|
|
| 283 |
+ c := p |
|
| 284 |
+ ok := true |
|
| 285 |
+ for ok {
|
|
| 286 |
+ c.RefCount += delta |
|
| 287 |
+ c, ok = cfg.subnets[c.ParentKey] |
|
| 288 |
+ } |
|
| 289 |
+} |
|
| 290 |
+ |
|
| 291 |
+// Checks whether the passed subnet is a superset or subset of any of the subset in this config db |
|
| 292 |
+func (cfg *PoolsConfig) contains(space string, nw *net.IPNet) bool {
|
|
| 293 |
+ for k, v := range cfg.subnets {
|
|
| 294 |
+ if space == k.AddressSpace && k.ChildSubnet == "" {
|
|
| 295 |
+ if nw.Contains(v.Pool.IP) || v.Pool.Contains(nw.IP) {
|
|
| 296 |
+ return true |
|
| 297 |
+ } |
|
| 298 |
+ } |
|
| 299 |
+ } |
|
| 300 |
+ return false |
|
| 301 |
+} |
| 0 | 302 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,128 @@ |
| 0 |
+package ipam |
|
| 1 |
+ |
|
| 2 |
+import ( |
|
| 3 |
+ "fmt" |
|
| 4 |
+ "net" |
|
| 5 |
+ |
|
| 6 |
+ "github.com/docker/libnetwork/ipamapi" |
|
| 7 |
+ "github.com/docker/libnetwork/types" |
|
| 8 |
+) |
|
| 9 |
+ |
|
| 10 |
+type ipVersion int |
|
| 11 |
+ |
|
| 12 |
+const ( |
|
| 13 |
+ v4 = 4 |
|
| 14 |
+ v6 = 6 |
|
| 15 |
+) |
|
| 16 |
+ |
|
| 17 |
+func getAddressRange(pool string) (*AddressRange, error) {
|
|
| 18 |
+ ip, nw, err := net.ParseCIDR(pool) |
|
| 19 |
+ if err != nil {
|
|
| 20 |
+ return nil, ipamapi.ErrInvalidSubPool |
|
| 21 |
+ } |
|
| 22 |
+ lIP, e := types.GetHostPartIP(nw.IP, nw.Mask) |
|
| 23 |
+ if e != nil {
|
|
| 24 |
+ return nil, fmt.Errorf("failed to compute range's lowest ip address: %v", e)
|
|
| 25 |
+ } |
|
| 26 |
+ bIP, e := types.GetBroadcastIP(nw.IP, nw.Mask) |
|
| 27 |
+ if e != nil {
|
|
| 28 |
+ return nil, fmt.Errorf("failed to compute range's broadcast ip address: %v", e)
|
|
| 29 |
+ } |
|
| 30 |
+ hIP, e := types.GetHostPartIP(bIP, nw.Mask) |
|
| 31 |
+ if e != nil {
|
|
| 32 |
+ return nil, fmt.Errorf("failed to compute range's highest ip address: %v", e)
|
|
| 33 |
+ } |
|
| 34 |
+ nw.IP = ip |
|
| 35 |
+ return &AddressRange{nw, ipToUint32(types.GetMinimalIP(lIP)), ipToUint32(types.GetMinimalIP(hIP))}, nil
|
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func initLocalPredefinedPools() []*net.IPNet {
|
|
| 39 |
+ pl := make([]*net.IPNet, 0, 274) |
|
| 40 |
+ mask := []byte{255, 255, 0, 0}
|
|
| 41 |
+ for i := 17; i < 32; i++ {
|
|
| 42 |
+ pl = append(pl, &net.IPNet{IP: []byte{172, byte(i), 0, 0}, Mask: mask})
|
|
| 43 |
+ } |
|
| 44 |
+ for i := 0; i < 256; i++ {
|
|
| 45 |
+ pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), 0, 0}, Mask: mask})
|
|
| 46 |
+ } |
|
| 47 |
+ mask24 := []byte{255, 255, 255, 0}
|
|
| 48 |
+ for i := 42; i < 45; i++ {
|
|
| 49 |
+ pl = append(pl, &net.IPNet{IP: []byte{192, 168, byte(i), 0}, Mask: mask24})
|
|
| 50 |
+ } |
|
| 51 |
+ return pl |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func initGlobalPredefinedPools() []*net.IPNet {
|
|
| 55 |
+ pl := make([]*net.IPNet, 0, 256*256) |
|
| 56 |
+ mask := []byte{255, 255, 255, 0}
|
|
| 57 |
+ for i := 0; i < 256; i++ {
|
|
| 58 |
+ for j := 0; j < 256; j++ {
|
|
| 59 |
+ pl = append(pl, &net.IPNet{IP: []byte{10, byte(i), byte(j), 0}, Mask: mask})
|
|
| 60 |
+ } |
|
| 61 |
+ } |
|
| 62 |
+ return pl |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+// Check subnets size. In case configured subnet is v6 and host size is |
|
| 66 |
+// greater than 32 bits, adjust subnet to /96. |
|
| 67 |
+func adjustAndCheckSubnetSize(subnet *net.IPNet) (*net.IPNet, error) {
|
|
| 68 |
+ ones, bits := subnet.Mask.Size() |
|
| 69 |
+ if v6 == getAddressVersion(subnet.IP) {
|
|
| 70 |
+ if ones < minNetSizeV6 {
|
|
| 71 |
+ return nil, ipamapi.ErrInvalidPool |
|
| 72 |
+ } |
|
| 73 |
+ if ones < minNetSizeV6Eff {
|
|
| 74 |
+ newMask := net.CIDRMask(minNetSizeV6Eff, bits) |
|
| 75 |
+ return &net.IPNet{IP: subnet.IP, Mask: newMask}, nil
|
|
| 76 |
+ } |
|
| 77 |
+ } else {
|
|
| 78 |
+ if ones < minNetSize {
|
|
| 79 |
+ return nil, ipamapi.ErrInvalidPool |
|
| 80 |
+ } |
|
| 81 |
+ } |
|
| 82 |
+ return subnet, nil |
|
| 83 |
+} |
|
| 84 |
+ |
|
| 85 |
+// It generates the ip address in the passed subnet specified by |
|
| 86 |
+// the passed host address ordinal |
|
| 87 |
+func generateAddress(ordinal uint32, network *net.IPNet) net.IP {
|
|
| 88 |
+ var address [16]byte |
|
| 89 |
+ |
|
| 90 |
+ // Get network portion of IP |
|
| 91 |
+ if getAddressVersion(network.IP) == v4 {
|
|
| 92 |
+ copy(address[:], network.IP.To4()) |
|
| 93 |
+ } else {
|
|
| 94 |
+ copy(address[:], network.IP) |
|
| 95 |
+ } |
|
| 96 |
+ |
|
| 97 |
+ end := len(network.Mask) |
|
| 98 |
+ addIntToIP(address[:end], ordinal) |
|
| 99 |
+ |
|
| 100 |
+ return net.IP(address[:end]) |
|
| 101 |
+} |
|
| 102 |
+ |
|
| 103 |
+func getAddressVersion(ip net.IP) ipVersion {
|
|
| 104 |
+ if ip.To4() == nil {
|
|
| 105 |
+ return v6 |
|
| 106 |
+ } |
|
| 107 |
+ return v4 |
|
| 108 |
+} |
|
| 109 |
+ |
|
| 110 |
+// Adds the ordinal IP to the current array |
|
| 111 |
+// 192.168.0.0 + 53 => 192.168.53 |
|
| 112 |
+func addIntToIP(array []byte, ordinal uint32) {
|
|
| 113 |
+ for i := len(array) - 1; i >= 0; i-- {
|
|
| 114 |
+ array[i] |= (byte)(ordinal & 0xff) |
|
| 115 |
+ ordinal >>= 8 |
|
| 116 |
+ } |
|
| 117 |
+} |
|
| 118 |
+ |
|
| 119 |
+// Convert an ordinal to the respective IP address |
|
| 120 |
+func ipToUint32(ip []byte) uint32 {
|
|
| 121 |
+ value := uint32(0) |
|
| 122 |
+ for i := 0; i < len(ip); i++ {
|
|
| 123 |
+ j := len(ip) - 1 - i |
|
| 124 |
+ value += uint32(ip[i]) << uint(j*8) |
|
| 125 |
+ } |
|
| 126 |
+ return value |
|
| 127 |
+} |