- Remove from contract predefined errors which are no longer
valid (ex. ErrInvalidIpamService, ErrInvalidIpamConfigService)
- Do not use network driver error for ipam load failure in controller.go
- Bitseq to expose two well-known errors (no more bit available, bit is already set)
- Default ipam to report proper well-known error on RequestAddress()
based on bitseq returned error
- Default ipam errors to comply with types error interface
Signed-off-by: Alessandro Boch <aboch@docker.com>
| ... | ... |
@@ -24,7 +24,10 @@ const ( |
| 24 | 24 |
) |
| 25 | 25 |
|
| 26 | 26 |
var ( |
| 27 |
- errNoBitAvailable = fmt.Errorf("no bit available")
|
|
| 27 |
+ // ErrNoBitAvailable is returned when no more bits are available to set |
|
| 28 |
+ ErrNoBitAvailable = fmt.Errorf("no bit available")
|
|
| 29 |
+ // ErrBitAllocated is returned when the specific bit requested is already set |
|
| 30 |
+ ErrBitAllocated = fmt.Errorf("requested bit is already allocated")
|
|
| 28 | 31 |
) |
| 29 | 32 |
|
| 30 | 33 |
// Handle contains the sequece representing the bitmask and its identifier |
| ... | ... |
@@ -94,7 +97,7 @@ func (s *sequence) toString() string {
|
| 94 | 94 |
// GetAvailableBit returns the position of the first unset bit in the bitmask represented by this sequence |
| 95 | 95 |
func (s *sequence) getAvailableBit(from uint64) (uint64, uint64, error) {
|
| 96 | 96 |
if s.block == blockMAX || s.count == 0 {
|
| 97 |
- return invalidPos, invalidPos, errNoBitAvailable |
|
| 97 |
+ return invalidPos, invalidPos, ErrNoBitAvailable |
|
| 98 | 98 |
} |
| 99 | 99 |
bits := from |
| 100 | 100 |
bitSel := blockFirstBit >> from |
| ... | ... |
@@ -197,7 +200,7 @@ func (h *Handle) SetAnyInRange(start, end uint64) (uint64, error) {
|
| 197 | 197 |
return invalidPos, fmt.Errorf("invalid bit range [%d, %d]", start, end)
|
| 198 | 198 |
} |
| 199 | 199 |
if h.Unselected() == 0 {
|
| 200 |
- return invalidPos, errNoBitAvailable |
|
| 200 |
+ return invalidPos, ErrNoBitAvailable |
|
| 201 | 201 |
} |
| 202 | 202 |
return h.set(0, start, end, true, false) |
| 203 | 203 |
} |
| ... | ... |
@@ -205,7 +208,7 @@ func (h *Handle) SetAnyInRange(start, end uint64) (uint64, error) {
|
| 205 | 205 |
// SetAny atomically sets the first unset bit in the sequence and returns the corresponding ordinal |
| 206 | 206 |
func (h *Handle) SetAny() (uint64, error) {
|
| 207 | 207 |
if h.Unselected() == 0 {
|
| 208 |
- return invalidPos, errNoBitAvailable |
|
| 208 |
+ return invalidPos, ErrNoBitAvailable |
|
| 209 | 209 |
} |
| 210 | 210 |
return h.set(0, 0, h.bits-1, true, false) |
| 211 | 211 |
} |
| ... | ... |
@@ -269,7 +272,7 @@ func (h *Handle) set(ordinal, start, end uint64, any bool, release bool) (uint64 |
| 269 | 269 |
bytePos, bitPos, err = getFirstAvailable(h.head, start) |
| 270 | 270 |
ret = posToOrdinal(bytePos, bitPos) |
| 271 | 271 |
if end < ret {
|
| 272 |
- err = errNoBitAvailable |
|
| 272 |
+ err = ErrNoBitAvailable |
|
| 273 | 273 |
} |
| 274 | 274 |
} else {
|
| 275 | 275 |
bytePos, bitPos, err = checkIfAvailable(h.head, ordinal) |
| ... | ... |
@@ -449,7 +452,7 @@ func getFirstAvailable(head *sequence, start uint64) (uint64, uint64, error) {
|
| 449 | 449 |
byteOffset += current.count * blockBytes |
| 450 | 450 |
current = current.next |
| 451 | 451 |
} |
| 452 |
- return invalidPos, invalidPos, errNoBitAvailable |
|
| 452 |
+ return invalidPos, invalidPos, ErrNoBitAvailable |
|
| 453 | 453 |
} |
| 454 | 454 |
|
| 455 | 455 |
// checkIfAvailable checks if the bit correspondent to the specified ordinal is unset |
| ... | ... |
@@ -467,7 +470,7 @@ func checkIfAvailable(head *sequence, ordinal uint64) (uint64, uint64, error) {
|
| 467 | 467 |
} |
| 468 | 468 |
} |
| 469 | 469 |
|
| 470 |
- return invalidPos, invalidPos, fmt.Errorf("requested bit is not available")
|
|
| 470 |
+ return invalidPos, invalidPos, ErrBitAllocated |
|
| 471 | 471 |
} |
| 472 | 472 |
|
| 473 | 473 |
// Given the byte position and the sequences list head, return the pointer to the |
| ... | ... |
@@ -657,7 +657,7 @@ func TestSetInRange(t *testing.T) {
|
| 657 | 657 |
if err == nil {
|
| 658 | 658 |
t.Fatalf("Expected failure. Got success with ordinal:%d", o)
|
| 659 | 659 |
} |
| 660 |
- if err != errNoBitAvailable {
|
|
| 660 |
+ if err != ErrNoBitAvailable {
|
|
| 661 | 661 |
t.Fatalf("Unexpected error: %v", err)
|
| 662 | 662 |
} |
| 663 | 663 |
|
| ... | ... |
@@ -673,7 +673,7 @@ func TestSetInRange(t *testing.T) {
|
| 673 | 673 |
if err == nil {
|
| 674 | 674 |
t.Fatalf("Expected failure. Got success with ordinal:%d", o)
|
| 675 | 675 |
} |
| 676 |
- if err != errNoBitAvailable {
|
|
| 676 |
+ if err != ErrNoBitAvailable {
|
|
| 677 | 677 |
t.Fatalf("Unexpected error: %v", err)
|
| 678 | 678 |
} |
| 679 | 679 |
|
| ... | ... |
@@ -687,7 +687,7 @@ func TestSetInRange(t *testing.T) {
|
| 687 | 687 |
if err == nil {
|
| 688 | 688 |
t.Fatalf("Expected failure. Got success with ordinal:%d", o)
|
| 689 | 689 |
} |
| 690 |
- if err != errNoBitAvailable {
|
|
| 690 |
+ if err != ErrNoBitAvailable {
|
|
| 691 | 691 |
t.Fatalf("Unexpected error: %v", err)
|
| 692 | 692 |
} |
| 693 | 693 |
} |
| ... | ... |
@@ -315,17 +315,17 @@ func (c *controller) RegisterIpamDriver(name string, driver ipamapi.Ipam) error |
| 315 | 315 |
_, ok := c.ipamDrivers[name] |
| 316 | 316 |
c.Unlock() |
| 317 | 317 |
if ok {
|
| 318 |
- return driverapi.ErrActiveRegistration(name) |
|
| 318 |
+ return types.ForbiddenErrorf("ipam driver %q already registered", name)
|
|
| 319 | 319 |
} |
| 320 | 320 |
locAS, glbAS, err := driver.GetDefaultAddressSpaces() |
| 321 | 321 |
if err != nil {
|
| 322 |
- return fmt.Errorf("ipam driver %s failed to return default address spaces: %v", name, err)
|
|
| 322 |
+ return types.InternalErrorf("ipam driver %q failed to return default address spaces: %v", name, err)
|
|
| 323 | 323 |
} |
| 324 | 324 |
c.Lock() |
| 325 | 325 |
c.ipamDrivers[name] = &ipamData{driver: driver, defaultLocalAddressSpace: locAS, defaultGlobalAddressSpace: glbAS}
|
| 326 | 326 |
c.Unlock() |
| 327 | 327 |
|
| 328 |
- log.Debugf("Registering ipam provider: %s", name)
|
|
| 328 |
+ log.Debugf("Registering ipam driver: %q", name)
|
|
| 329 | 329 |
|
| 330 | 330 |
return nil |
| 331 | 331 |
} |
| ... | ... |
@@ -667,7 +667,7 @@ func (c *controller) loadIpamDriver(name string) (*ipamData, error) {
|
| 667 | 667 |
id, ok := c.ipamDrivers[name] |
| 668 | 668 |
c.Unlock() |
| 669 | 669 |
if !ok {
|
| 670 |
- return nil, ErrInvalidNetworkDriver(name) |
|
| 670 |
+ return nil, types.BadRequestErrorf("invalid ipam driver: %q", name)
|
|
| 671 | 671 |
} |
| 672 | 672 |
return id, nil |
| 673 | 673 |
} |
| ... | ... |
@@ -76,8 +76,7 @@ func NewAllocator(lcDs, glDs datastore.DataStore) (*Allocator, error) {
|
| 76 | 76 |
func (a *Allocator) refresh(as string) error {
|
| 77 | 77 |
aSpace, err := a.getAddressSpaceFromStore(as) |
| 78 | 78 |
if err != nil {
|
| 79 |
- return fmt.Errorf("error getting pools config from store during init: %v",
|
|
| 80 |
- err) |
|
| 79 |
+ return types.InternalErrorf("error getting pools config from store: %v", err)
|
|
| 81 | 80 |
} |
| 82 | 81 |
|
| 83 | 82 |
if aSpace == nil {
|
| ... | ... |
@@ -239,7 +238,7 @@ func (a *Allocator) insertBitMask(key SubnetKey, pool *net.IPNet) error {
|
| 239 | 239 |
|
| 240 | 240 |
store := a.getStore(key.AddressSpace) |
| 241 | 241 |
if store == nil {
|
| 242 |
- return fmt.Errorf("could not find store for address space %s while inserting bit mask", key.AddressSpace)
|
|
| 242 |
+ return types.InternalErrorf("could not find store for address space %s while inserting bit mask", key.AddressSpace)
|
|
| 243 | 243 |
} |
| 244 | 244 |
|
| 245 | 245 |
ipVer := getAddressVersion(pool.IP) |
| ... | ... |
@@ -279,7 +278,7 @@ func (a *Allocator) retrieveBitmask(k SubnetKey, n *net.IPNet) (*bitseq.Handle, |
| 279 | 279 |
if !ok {
|
| 280 | 280 |
log.Debugf("Retrieving bitmask (%s, %s)", k.String(), n.String())
|
| 281 | 281 |
if err := a.insertBitMask(k, n); err != nil {
|
| 282 |
- return nil, fmt.Errorf("could not find bitmask in datastore for %s", k.String())
|
|
| 282 |
+ return nil, types.InternalErrorf("could not find bitmask in datastore for %s", k.String())
|
|
| 283 | 283 |
} |
| 284 | 284 |
a.Lock() |
| 285 | 285 |
bm = a.addresses[k] |
| ... | ... |
@@ -306,7 +305,7 @@ func (a *Allocator) getPredefinedPool(as string, ipV6 bool) (*net.IPNet, error) |
| 306 | 306 |
} |
| 307 | 307 |
|
| 308 | 308 |
if as != localAddressSpace && as != globalAddressSpace {
|
| 309 |
- return nil, fmt.Errorf("no default pool available for non-default address spaces")
|
|
| 309 |
+ return nil, types.NotImplementedErrorf("no default pool availbale for non-default addresss spaces")
|
|
| 310 | 310 |
} |
| 311 | 311 |
|
| 312 | 312 |
aSpace, err := a.getAddrSpace(as) |
| ... | ... |
@@ -378,7 +377,7 @@ func (a *Allocator) RequestAddress(poolID string, prefAddress net.IP, opts map[s |
| 378 | 378 |
|
| 379 | 379 |
bm, err := a.retrieveBitmask(k, c.Pool) |
| 380 | 380 |
if err != nil {
|
| 381 |
- return nil, nil, fmt.Errorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v",
|
|
| 381 |
+ return nil, nil, types.InternalErrorf("could not find bitmask in datastore for %s on address %v request from pool %s: %v",
|
|
| 382 | 382 |
k.String(), prefAddress, poolID, err) |
| 383 | 383 |
} |
| 384 | 384 |
ip, err := a.getAddress(p.Pool, bm, prefAddress, p.Range) |
| ... | ... |
@@ -410,12 +409,12 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
|
| 410 | 410 |
p, ok := aSpace.subnets[k] |
| 411 | 411 |
if !ok {
|
| 412 | 412 |
aSpace.Unlock() |
| 413 |
- return ipamapi.ErrBadPool |
|
| 413 |
+ return types.NotFoundErrorf("cannot find address pool for poolID:%s", poolID)
|
|
| 414 | 414 |
} |
| 415 | 415 |
|
| 416 | 416 |
if address == nil {
|
| 417 | 417 |
aSpace.Unlock() |
| 418 |
- return ipamapi.ErrInvalidRequest |
|
| 418 |
+ return types.BadRequestErrorf("invalid address: nil")
|
|
| 419 | 419 |
} |
| 420 | 420 |
|
| 421 | 421 |
if !p.Pool.Contains(address) {
|
| ... | ... |
@@ -434,12 +433,12 @@ func (a *Allocator) ReleaseAddress(poolID string, address net.IP) error {
|
| 434 | 434 |
|
| 435 | 435 |
h, err := types.GetHostPartIP(address, mask) |
| 436 | 436 |
if err != nil {
|
| 437 |
- return fmt.Errorf("failed to release address %s: %v", address.String(), err)
|
|
| 437 |
+ return types.InternalErrorf("failed to release address %s: %v", address.String(), err)
|
|
| 438 | 438 |
} |
| 439 | 439 |
|
| 440 | 440 |
bm, err := a.retrieveBitmask(k, c.Pool) |
| 441 | 441 |
if err != nil {
|
| 442 |
- return fmt.Errorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v",
|
|
| 442 |
+ return types.InternalErrorf("could not find bitmask in datastore for %s on address %v release from pool %s: %v",
|
|
| 443 | 443 |
k.String(), address, poolID, err) |
| 444 | 444 |
} |
| 445 | 445 |
|
| ... | ... |
@@ -463,19 +462,25 @@ func (a *Allocator) getAddress(nw *net.IPNet, bitmask *bitseq.Handle, prefAddres |
| 463 | 463 |
} else if prefAddress != nil {
|
| 464 | 464 |
hostPart, e := types.GetHostPartIP(prefAddress, base.Mask) |
| 465 | 465 |
if e != nil {
|
| 466 |
- return nil, fmt.Errorf("failed to allocate preferred address %s: %v", prefAddress.String(), e)
|
|
| 466 |
+ return nil, types.InternalErrorf("failed to allocate preferred address %s: %v", prefAddress.String(), e)
|
|
| 467 | 467 |
} |
| 468 | 468 |
ordinal = ipToUint64(types.GetMinimalIP(hostPart)) |
| 469 | 469 |
err = bitmask.Set(ordinal) |
| 470 | 470 |
} else {
|
| 471 | 471 |
ordinal, err = bitmask.SetAnyInRange(ipr.Start, ipr.End) |
| 472 | 472 |
} |
| 473 |
- if err != nil {
|
|
| 473 |
+ |
|
| 474 |
+ switch err {
|
|
| 475 |
+ case nil: |
|
| 476 |
+ // Convert IP ordinal for this subnet into IP address |
|
| 477 |
+ return generateAddress(ordinal, base), nil |
|
| 478 |
+ case bitseq.ErrBitAllocated: |
|
| 479 |
+ return nil, ipamapi.ErrIPAlreadyAllocated |
|
| 480 |
+ case bitseq.ErrNoBitAvailable: |
|
| 474 | 481 |
return nil, ipamapi.ErrNoAvailableIPs |
| 482 |
+ default: |
|
| 483 |
+ return nil, err |
|
| 475 | 484 |
} |
| 476 |
- |
|
| 477 |
- // Convert IP ordinal for this subnet into IP address |
|
| 478 |
- return generateAddress(ordinal, base), nil |
|
| 479 | 485 |
} |
| 480 | 486 |
|
| 481 | 487 |
// DumpDatabase dumps the internal info |
| ... | ... |
@@ -2,7 +2,6 @@ package ipam |
| 2 | 2 |
|
| 3 | 3 |
import ( |
| 4 | 4 |
"encoding/json" |
| 5 |
- "fmt" |
|
| 6 | 5 |
|
| 7 | 6 |
log "github.com/Sirupsen/logrus" |
| 8 | 7 |
"github.com/docker/libnetwork/datastore" |
| ... | ... |
@@ -84,7 +83,7 @@ func (a *Allocator) getStore(as string) datastore.DataStore {
|
| 84 | 84 |
func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
|
| 85 | 85 |
store := a.getStore(as) |
| 86 | 86 |
if store == nil {
|
| 87 |
- return nil, fmt.Errorf("store for address space %s not found", as)
|
|
| 87 |
+ return nil, types.InternalErrorf("store for address space %s not found", as)
|
|
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 | 90 |
pc := &addrSpace{id: dsConfigKey + "/" + as, ds: store, alloc: a}
|
| ... | ... |
@@ -93,7 +92,7 @@ func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
|
| 93 | 93 |
return nil, nil |
| 94 | 94 |
} |
| 95 | 95 |
|
| 96 |
- return nil, fmt.Errorf("could not get pools config from store: %v", err)
|
|
| 96 |
+ return nil, types.InternalErrorf("could not get pools config from store: %v", err)
|
|
| 97 | 97 |
} |
| 98 | 98 |
|
| 99 | 99 |
return pc, nil |
| ... | ... |
@@ -102,7 +101,7 @@ func (a *Allocator) getAddressSpaceFromStore(as string) (*addrSpace, error) {
|
| 102 | 102 |
func (a *Allocator) writeToStore(aSpace *addrSpace) error {
|
| 103 | 103 |
store := aSpace.store() |
| 104 | 104 |
if store == nil {
|
| 105 |
- return fmt.Errorf("invalid store while trying to write %s address space", aSpace.DataScope())
|
|
| 105 |
+ return types.InternalErrorf("invalid store while trying to write %s address space", aSpace.DataScope())
|
|
| 106 | 106 |
} |
| 107 | 107 |
|
| 108 | 108 |
err := store.PutObjectAtomic(aSpace) |
| ... | ... |
@@ -116,7 +115,7 @@ func (a *Allocator) writeToStore(aSpace *addrSpace) error {
|
| 116 | 116 |
func (a *Allocator) deleteFromStore(aSpace *addrSpace) error {
|
| 117 | 117 |
store := aSpace.store() |
| 118 | 118 |
if store == nil {
|
| 119 |
- return fmt.Errorf("invalid store while trying to delete %s address space", aSpace.DataScope())
|
|
| 119 |
+ return types.InternalErrorf("invalid store while trying to delete %s address space", aSpace.DataScope())
|
|
| 120 | 120 |
} |
| 121 | 121 |
|
| 122 | 122 |
return store.DeleteObjectAtomic(aSpace) |
| ... | ... |
@@ -88,12 +88,12 @@ func (s *SubnetKey) String() string {
|
| 88 | 88 |
// FromString populate the SubnetKey object reading it from string |
| 89 | 89 |
func (s *SubnetKey) FromString(str string) error {
|
| 90 | 90 |
if str == "" || !strings.Contains(str, "/") {
|
| 91 |
- return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
|
| 91 |
+ return types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
|
|
| 92 | 92 |
} |
| 93 | 93 |
|
| 94 | 94 |
p := strings.Split(str, "/") |
| 95 | 95 |
if len(p) != 3 && len(p) != 5 {
|
| 96 |
- return fmt.Errorf("invalid string form for subnetkey: %s", str)
|
|
| 96 |
+ return types.BadRequestErrorf("invalid string form for subnetkey: %s", str)
|
|
| 97 | 97 |
} |
| 98 | 98 |
s.AddressSpace = p[0] |
| 99 | 99 |
s.Subnet = fmt.Sprintf("%s/%s", p[1], p[2])
|
| ... | ... |
@@ -317,7 +317,7 @@ func (aSpace *addrSpace) updatePoolDBOnRemoval(k SubnetKey) (func() error, error |
| 317 | 317 |
return func() error {
|
| 318 | 318 |
bm, err := aSpace.alloc.retrieveBitmask(k, c.Pool) |
| 319 | 319 |
if err != nil {
|
| 320 |
- return fmt.Errorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
|
|
| 320 |
+ return types.InternalErrorf("could not find bitmask in datastore for pool %s removal: %v", k.String(), err)
|
|
| 321 | 321 |
} |
| 322 | 322 |
return bm.Destroy() |
| 323 | 323 |
}, nil |
| ... | ... |
@@ -2,8 +2,9 @@ |
| 2 | 2 |
package ipamapi |
| 3 | 3 |
|
| 4 | 4 |
import ( |
| 5 |
- "errors" |
|
| 6 | 5 |
"net" |
| 6 |
+ |
|
| 7 |
+ "github.com/docker/libnetwork/types" |
|
| 7 | 8 |
) |
| 8 | 9 |
|
| 9 | 10 |
/******************** |
| ... | ... |
@@ -29,22 +30,19 @@ type Callback interface {
|
| 29 | 29 |
|
| 30 | 30 |
// Weel-known errors returned by IPAM |
| 31 | 31 |
var ( |
| 32 |
- ErrInvalidIpamService = errors.New("Invalid IPAM Service")
|
|
| 33 |
- ErrInvalidIpamConfigService = errors.New("Invalid IPAM Config Service")
|
|
| 34 |
- ErrIpamNotAvailable = errors.New("IPAM Service not available")
|
|
| 35 |
- ErrIpamInternalError = errors.New("IPAM Internal Error")
|
|
| 36 |
- ErrInvalidAddressSpace = errors.New("Invalid Address Space")
|
|
| 37 |
- ErrInvalidPool = errors.New("Invalid Address Pool")
|
|
| 38 |
- ErrInvalidSubPool = errors.New("Invalid Address SubPool")
|
|
| 39 |
- ErrInvalidRequest = errors.New("Invalid Request")
|
|
| 40 |
- ErrPoolNotFound = errors.New("Address Pool not found")
|
|
| 41 |
- ErrOverlapPool = errors.New("Address pool overlaps with existing pool on this address space")
|
|
| 42 |
- ErrNoAvailablePool = errors.New("No available pool")
|
|
| 43 |
- ErrNoAvailableIPs = errors.New("No available addresses on this pool")
|
|
| 44 |
- ErrIPAlreadyAllocated = errors.New("Address already in use")
|
|
| 45 |
- ErrIPOutOfRange = errors.New("Requested address is out of range")
|
|
| 46 |
- ErrPoolOverlap = errors.New("Pool overlaps with other one on this address space")
|
|
| 47 |
- ErrBadPool = errors.New("Address space does not contain specified address pool")
|
|
| 32 |
+ ErrIpamInternalError = types.InternalErrorf("IPAM Internal Error")
|
|
| 33 |
+ ErrInvalidAddressSpace = types.BadRequestErrorf("Invalid Address Space")
|
|
| 34 |
+ ErrInvalidPool = types.BadRequestErrorf("Invalid Address Pool")
|
|
| 35 |
+ ErrInvalidSubPool = types.BadRequestErrorf("Invalid Address SubPool")
|
|
| 36 |
+ ErrInvalidRequest = types.BadRequestErrorf("Invalid Request")
|
|
| 37 |
+ ErrPoolNotFound = types.BadRequestErrorf("Address Pool not found")
|
|
| 38 |
+ ErrOverlapPool = types.ForbiddenErrorf("Address pool overlaps with existing pool on this address space")
|
|
| 39 |
+ ErrNoAvailablePool = types.NoServiceErrorf("No available pool")
|
|
| 40 |
+ ErrNoAvailableIPs = types.NoServiceErrorf("No available addresses on this pool")
|
|
| 41 |
+ ErrIPAlreadyAllocated = types.ForbiddenErrorf("Address already in use")
|
|
| 42 |
+ ErrIPOutOfRange = types.BadRequestErrorf("Requested address is out of range")
|
|
| 43 |
+ ErrPoolOverlap = types.ForbiddenErrorf("Pool overlaps with other one on this address space")
|
|
| 44 |
+ ErrBadPool = types.BadRequestErrorf("Address space does not contain specified address pool")
|
|
| 48 | 45 |
) |
| 49 | 46 |
|
| 50 | 47 |
/******************************* |