|
@@ -2,7 +2,9 @@ package libnetwork
|
|
|
|
|
|
import (
|
|
import (
|
|
"encoding/json"
|
|
"encoding/json"
|
|
|
|
+ "fmt"
|
|
"net"
|
|
"net"
|
|
|
|
+ "strconv"
|
|
"sync"
|
|
"sync"
|
|
|
|
|
|
log "github.com/Sirupsen/logrus"
|
|
log "github.com/Sirupsen/logrus"
|
|
@@ -11,6 +13,7 @@ import (
|
|
"github.com/docker/libnetwork/datastore"
|
|
"github.com/docker/libnetwork/datastore"
|
|
"github.com/docker/libnetwork/driverapi"
|
|
"github.com/docker/libnetwork/driverapi"
|
|
"github.com/docker/libnetwork/etchosts"
|
|
"github.com/docker/libnetwork/etchosts"
|
|
|
|
+ "github.com/docker/libnetwork/ipamapi"
|
|
"github.com/docker/libnetwork/netlabel"
|
|
"github.com/docker/libnetwork/netlabel"
|
|
"github.com/docker/libnetwork/options"
|
|
"github.com/docker/libnetwork/options"
|
|
"github.com/docker/libnetwork/types"
|
|
"github.com/docker/libnetwork/types"
|
|
@@ -47,6 +50,16 @@ type Network interface {
|
|
|
|
|
|
// EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned.
|
|
// EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned.
|
|
EndpointByID(id string) (Endpoint, error)
|
|
EndpointByID(id string) (Endpoint, error)
|
|
|
|
+
|
|
|
|
+ // Return certain operational data belonging to this network
|
|
|
|
+ Info() NetworkInfo
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// NetworkInfo returns some configuration and operational information about the network
|
|
|
|
+type NetworkInfo interface {
|
|
|
|
+ IpamConfig() (string, []*IpamConf, []*IpamConf)
|
|
|
|
+ Labels() map[string]string
|
|
|
|
+ Scope() string
|
|
}
|
|
}
|
|
|
|
|
|
// EndpointWalker is a client provided function which will be used to walk the Endpoints.
|
|
// EndpointWalker is a client provided function which will be used to walk the Endpoints.
|
|
@@ -55,22 +68,98 @@ type EndpointWalker func(ep Endpoint) bool
|
|
|
|
|
|
type svcMap map[string]net.IP
|
|
type svcMap map[string]net.IP
|
|
|
|
|
|
|
|
+// IpamConf contains all the ipam related configurations for a network
|
|
|
|
+type IpamConf struct {
|
|
|
|
+ // The master address pool for containers and network interfaces
|
|
|
|
+ PreferredPool string
|
|
|
|
+ // A subset of the master pool. If specified,
|
|
|
|
+ // this becomes the container pool
|
|
|
|
+ SubPool string
|
|
|
|
+ // Input options for IPAM Driver (optional)
|
|
|
|
+ Options map[string]string
|
|
|
|
+ // Preferred Network Gateway address (optional)
|
|
|
|
+ Gateway string
|
|
|
|
+ // Auxiliary addresses for network driver. Must be within the master pool.
|
|
|
|
+ // libnetwork will reserve them if they fall into the container pool
|
|
|
|
+ AuxAddresses map[string]string
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// Validate checks whether the configuration is valid
|
|
|
|
+func (c *IpamConf) Validate() error {
|
|
|
|
+ if c.Gateway != "" && nil == net.ParseIP(c.Gateway) {
|
|
|
|
+ return types.BadRequestErrorf("invalid gateway address %s in Ipam configuration", c.Gateway)
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// IpamInfo contains all the ipam related operational info for a network
|
|
|
|
+type IpamInfo struct {
|
|
|
|
+ PoolID string
|
|
|
|
+ Meta map[string]string
|
|
|
|
+ driverapi.IPAMData
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// MarshalJSON encodes IpamInfo into json message
|
|
|
|
+func (i *IpamInfo) MarshalJSON() ([]byte, error) {
|
|
|
|
+ m := map[string]interface{}{
|
|
|
|
+ "PoolID": i.PoolID,
|
|
|
|
+ }
|
|
|
|
+ v, err := json.Marshal(&i.IPAMData)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ m["IPAMData"] = string(v)
|
|
|
|
+
|
|
|
|
+ if i.Meta != nil {
|
|
|
|
+ m["Meta"] = i.Meta
|
|
|
|
+ }
|
|
|
|
+ return json.Marshal(m)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// UnmarshalJSON decodes json message into PoolData
|
|
|
|
+func (i *IpamInfo) UnmarshalJSON(data []byte) error {
|
|
|
|
+ var (
|
|
|
|
+ m map[string]interface{}
|
|
|
|
+ err error
|
|
|
|
+ )
|
|
|
|
+ if err = json.Unmarshal(data, &m); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ i.PoolID = m["PoolID"].(string)
|
|
|
|
+ if v, ok := m["Meta"]; ok {
|
|
|
|
+ b, _ := json.Marshal(v)
|
|
|
|
+ if err = json.Unmarshal(b, &i.Meta); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if v, ok := m["IPAMData"]; ok {
|
|
|
|
+ if err = json.Unmarshal([]byte(v.(string)), &i.IPAMData); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
type network struct {
|
|
type network struct {
|
|
- ctrlr *controller
|
|
|
|
- name string
|
|
|
|
- networkType string
|
|
|
|
- id string
|
|
|
|
- driver driverapi.Driver
|
|
|
|
- enableIPv6 bool
|
|
|
|
- endpointCnt uint64
|
|
|
|
- endpoints endpointTable
|
|
|
|
- generic options.Generic
|
|
|
|
- dbIndex uint64
|
|
|
|
- svcRecords svcMap
|
|
|
|
- dbExists bool
|
|
|
|
- persist bool
|
|
|
|
- stopWatchCh chan struct{}
|
|
|
|
- dataScope datastore.DataScope
|
|
|
|
|
|
+ ctrlr *controller
|
|
|
|
+ name string
|
|
|
|
+ networkType string
|
|
|
|
+ id string
|
|
|
|
+ ipamType string
|
|
|
|
+ addrSpace string
|
|
|
|
+ ipamV4Config []*IpamConf
|
|
|
|
+ ipamV6Config []*IpamConf
|
|
|
|
+ ipamV4Info []*IpamInfo
|
|
|
|
+ ipamV6Info []*IpamInfo
|
|
|
|
+ enableIPv6 bool
|
|
|
|
+ epCnt *endpointCnt
|
|
|
|
+ generic options.Generic
|
|
|
|
+ dbIndex uint64
|
|
|
|
+ svcRecords svcMap
|
|
|
|
+ dbExists bool
|
|
|
|
+ persist bool
|
|
|
|
+ stopWatchCh chan struct{}
|
|
|
|
+ drvOnce *sync.Once
|
|
sync.Mutex
|
|
sync.Mutex
|
|
}
|
|
}
|
|
|
|
|
|
@@ -92,11 +181,7 @@ func (n *network) Type() string {
|
|
n.Lock()
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
defer n.Unlock()
|
|
|
|
|
|
- if n.driver == nil {
|
|
|
|
- return ""
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return n.driver.Type()
|
|
|
|
|
|
+ return n.networkType
|
|
}
|
|
}
|
|
|
|
|
|
func (n *network) Key() []string {
|
|
func (n *network) Key() []string {
|
|
@@ -148,28 +233,116 @@ func (n *network) Skip() bool {
|
|
return !n.persist
|
|
return !n.persist
|
|
}
|
|
}
|
|
|
|
|
|
-func (n *network) DataScope() datastore.DataScope {
|
|
|
|
|
|
+func (n *network) New() datastore.KVObject {
|
|
n.Lock()
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
defer n.Unlock()
|
|
- return n.dataScope
|
|
|
|
|
|
+
|
|
|
|
+ return &network{
|
|
|
|
+ ctrlr: n.ctrlr,
|
|
|
|
+ drvOnce: &sync.Once{},
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
-func (n *network) EndpointCnt() uint64 {
|
|
|
|
|
|
+// CopyTo deep copies to the destination IpamConfig
|
|
|
|
+func (c *IpamConf) CopyTo(dstC *IpamConf) error {
|
|
|
|
+ dstC.PreferredPool = c.PreferredPool
|
|
|
|
+ dstC.SubPool = c.SubPool
|
|
|
|
+ dstC.Gateway = c.Gateway
|
|
|
|
+ if c.Options != nil {
|
|
|
|
+ dstC.Options = make(map[string]string, len(c.Options))
|
|
|
|
+ for k, v := range c.Options {
|
|
|
|
+ dstC.Options[k] = v
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if c.AuxAddresses != nil {
|
|
|
|
+ dstC.AuxAddresses = make(map[string]string, len(c.AuxAddresses))
|
|
|
|
+ for k, v := range c.AuxAddresses {
|
|
|
|
+ dstC.AuxAddresses[k] = v
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// CopyTo deep copies to the destination IpamInfo
|
|
|
|
+func (i *IpamInfo) CopyTo(dstI *IpamInfo) error {
|
|
|
|
+ dstI.PoolID = i.PoolID
|
|
|
|
+ if i.Meta != nil {
|
|
|
|
+ dstI.Meta = make(map[string]string)
|
|
|
|
+ for k, v := range i.Meta {
|
|
|
|
+ dstI.Meta[k] = v
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dstI.AddressSpace = i.AddressSpace
|
|
|
|
+ dstI.Pool = types.GetIPNetCopy(i.Pool)
|
|
|
|
+ dstI.Gateway = types.GetIPNetCopy(i.Gateway)
|
|
|
|
+
|
|
|
|
+ if i.AuxAddresses != nil {
|
|
|
|
+ dstI.AuxAddresses = make(map[string]*net.IPNet)
|
|
|
|
+ for k, v := range i.AuxAddresses {
|
|
|
|
+ dstI.AuxAddresses[k] = types.GetIPNetCopy(v)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) CopyTo(o datastore.KVObject) error {
|
|
n.Lock()
|
|
n.Lock()
|
|
defer n.Unlock()
|
|
defer n.Unlock()
|
|
- return n.endpointCnt
|
|
|
|
|
|
+
|
|
|
|
+ dstN := o.(*network)
|
|
|
|
+ dstN.name = n.name
|
|
|
|
+ dstN.id = n.id
|
|
|
|
+ dstN.networkType = n.networkType
|
|
|
|
+ dstN.ipamType = n.ipamType
|
|
|
|
+ dstN.enableIPv6 = n.enableIPv6
|
|
|
|
+ dstN.persist = n.persist
|
|
|
|
+ dstN.dbIndex = n.dbIndex
|
|
|
|
+ dstN.dbExists = n.dbExists
|
|
|
|
+ dstN.drvOnce = n.drvOnce
|
|
|
|
+
|
|
|
|
+ for _, v4conf := range n.ipamV4Config {
|
|
|
|
+ dstV4Conf := &IpamConf{}
|
|
|
|
+ v4conf.CopyTo(dstV4Conf)
|
|
|
|
+ dstN.ipamV4Config = append(dstN.ipamV4Config, dstV4Conf)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for _, v4info := range n.ipamV4Info {
|
|
|
|
+ dstV4Info := &IpamInfo{}
|
|
|
|
+ v4info.CopyTo(dstV4Info)
|
|
|
|
+ dstN.ipamV4Info = append(dstN.ipamV4Info, dstV4Info)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for _, v6conf := range n.ipamV6Config {
|
|
|
|
+ dstV6Conf := &IpamConf{}
|
|
|
|
+ v6conf.CopyTo(dstV6Conf)
|
|
|
|
+ dstN.ipamV6Config = append(dstN.ipamV6Config, dstV6Conf)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for _, v6info := range n.ipamV6Info {
|
|
|
|
+ dstV6Info := &IpamInfo{}
|
|
|
|
+ v6info.CopyTo(dstV6Info)
|
|
|
|
+ dstN.ipamV6Info = append(dstN.ipamV6Info, dstV6Info)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ dstN.generic = options.Generic{}
|
|
|
|
+ for k, v := range n.generic {
|
|
|
|
+ dstN.generic[k] = v
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (n *network) IncEndpointCnt() {
|
|
|
|
- n.Lock()
|
|
|
|
- n.endpointCnt++
|
|
|
|
- n.Unlock()
|
|
|
|
|
|
+func (n *network) DataScope() string {
|
|
|
|
+ return n.driverScope()
|
|
}
|
|
}
|
|
|
|
|
|
-func (n *network) DecEndpointCnt() {
|
|
|
|
|
|
+func (n *network) getEpCnt() *endpointCnt {
|
|
n.Lock()
|
|
n.Lock()
|
|
- n.endpointCnt--
|
|
|
|
- n.Unlock()
|
|
|
|
|
|
+ defer n.Unlock()
|
|
|
|
+
|
|
|
|
+ return n.epCnt
|
|
}
|
|
}
|
|
|
|
|
|
// TODO : Can be made much more generic with the help of reflection (but has some golang limitations)
|
|
// TODO : Can be made much more generic with the help of reflection (but has some golang limitations)
|
|
@@ -178,10 +351,41 @@ func (n *network) MarshalJSON() ([]byte, error) {
|
|
netMap["name"] = n.name
|
|
netMap["name"] = n.name
|
|
netMap["id"] = n.id
|
|
netMap["id"] = n.id
|
|
netMap["networkType"] = n.networkType
|
|
netMap["networkType"] = n.networkType
|
|
- netMap["endpointCnt"] = n.endpointCnt
|
|
|
|
|
|
+ netMap["ipamType"] = n.ipamType
|
|
|
|
+ netMap["addrSpace"] = n.addrSpace
|
|
netMap["enableIPv6"] = n.enableIPv6
|
|
netMap["enableIPv6"] = n.enableIPv6
|
|
- netMap["generic"] = n.generic
|
|
|
|
|
|
+ if n.generic != nil {
|
|
|
|
+ netMap["generic"] = n.generic
|
|
|
|
+ }
|
|
netMap["persist"] = n.persist
|
|
netMap["persist"] = n.persist
|
|
|
|
+ if len(n.ipamV4Config) > 0 {
|
|
|
|
+ ics, err := json.Marshal(n.ipamV4Config)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ netMap["ipamV4Config"] = string(ics)
|
|
|
|
+ }
|
|
|
|
+ if len(n.ipamV4Info) > 0 {
|
|
|
|
+ iis, err := json.Marshal(n.ipamV4Info)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ netMap["ipamV4Info"] = string(iis)
|
|
|
|
+ }
|
|
|
|
+ if len(n.ipamV6Config) > 0 {
|
|
|
|
+ ics, err := json.Marshal(n.ipamV6Config)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ netMap["ipamV6Config"] = string(ics)
|
|
|
|
+ }
|
|
|
|
+ if len(n.ipamV6Info) > 0 {
|
|
|
|
+ iis, err := json.Marshal(n.ipamV6Info)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ netMap["ipamV6Info"] = string(iis)
|
|
|
|
+ }
|
|
return json.Marshal(netMap)
|
|
return json.Marshal(netMap)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -194,13 +398,53 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
|
|
n.name = netMap["name"].(string)
|
|
n.name = netMap["name"].(string)
|
|
n.id = netMap["id"].(string)
|
|
n.id = netMap["id"].(string)
|
|
n.networkType = netMap["networkType"].(string)
|
|
n.networkType = netMap["networkType"].(string)
|
|
- n.endpointCnt = uint64(netMap["endpointCnt"].(float64))
|
|
|
|
n.enableIPv6 = netMap["enableIPv6"].(bool)
|
|
n.enableIPv6 = netMap["enableIPv6"].(bool)
|
|
- if netMap["generic"] != nil {
|
|
|
|
- n.generic = netMap["generic"].(map[string]interface{})
|
|
|
|
|
|
+
|
|
|
|
+ if v, ok := netMap["generic"]; ok {
|
|
|
|
+ n.generic = v.(map[string]interface{})
|
|
|
|
+ // Restore labels in their map[string]string form
|
|
|
|
+ if v, ok := n.generic[netlabel.GenericData]; ok {
|
|
|
|
+ var lmap map[string]string
|
|
|
|
+ ba, err := json.Marshal(v)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ if err := json.Unmarshal(ba, &lmap); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ n.generic[netlabel.GenericData] = lmap
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if v, ok := netMap["persist"]; ok {
|
|
|
|
+ n.persist = v.(bool)
|
|
|
|
+ }
|
|
|
|
+ if v, ok := netMap["ipamType"]; ok {
|
|
|
|
+ n.ipamType = v.(string)
|
|
|
|
+ } else {
|
|
|
|
+ n.ipamType = ipamapi.DefaultIPAM
|
|
|
|
+ }
|
|
|
|
+ if v, ok := netMap["addrSpace"]; ok {
|
|
|
|
+ n.addrSpace = v.(string)
|
|
|
|
+ }
|
|
|
|
+ if v, ok := netMap["ipamV4Config"]; ok {
|
|
|
|
+ if err := json.Unmarshal([]byte(v.(string)), &n.ipamV4Config); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if v, ok := netMap["ipamV4Info"]; ok {
|
|
|
|
+ if err := json.Unmarshal([]byte(v.(string)), &n.ipamV4Info); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if v, ok := netMap["ipamV6Config"]; ok {
|
|
|
|
+ if err := json.Unmarshal([]byte(v.(string)), &n.ipamV6Config); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- if netMap["persist"] != nil {
|
|
|
|
- n.persist = netMap["persist"].(bool)
|
|
|
|
|
|
+ if v, ok := netMap["ipamV6Info"]; ok {
|
|
|
|
+ if err := json.Unmarshal([]byte(v.(string)), &n.ipamV6Info); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
}
|
|
}
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
@@ -228,6 +472,39 @@ func NetworkOptionPersist(persist bool) NetworkOption {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+// NetworkOptionIpam function returns an option setter for the ipam configuration for this network
|
|
|
|
+func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf) NetworkOption {
|
|
|
|
+ return func(n *network) {
|
|
|
|
+ if ipamDriver != "" {
|
|
|
|
+ n.ipamType = ipamDriver
|
|
|
|
+ }
|
|
|
|
+ n.addrSpace = addrSpace
|
|
|
|
+ n.ipamV4Config = ipV4
|
|
|
|
+ n.ipamV6Config = ipV6
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+// NetworkOptionLabels function returns an option setter for any parameter described by a map
|
|
|
|
+func NetworkOptionLabels(labels map[string]string) NetworkOption {
|
|
|
|
+ return func(n *network) {
|
|
|
|
+ if n.generic == nil {
|
|
|
|
+ n.generic = make(map[string]interface{})
|
|
|
|
+ }
|
|
|
|
+ if labels == nil {
|
|
|
|
+ labels = make(map[string]string)
|
|
|
|
+ }
|
|
|
|
+ // Store the options
|
|
|
|
+ n.generic[netlabel.GenericData] = labels
|
|
|
|
+ // Decode and store the endpoint options of libnetwork interest
|
|
|
|
+ if val, ok := labels[netlabel.EnableIPv6]; ok {
|
|
|
|
+ var err error
|
|
|
|
+ if n.enableIPv6, err = strconv.ParseBool(val); err != nil {
|
|
|
|
+ log.Warnf("Failed to parse %s' value: %s (%s)", netlabel.EnableIPv6, val, err.Error())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
func (n *network) processOptions(options ...NetworkOption) {
|
|
func (n *network) processOptions(options ...NetworkOption) {
|
|
for _, opt := range options {
|
|
for _, opt := range options {
|
|
if opt != nil {
|
|
if opt != nil {
|
|
@@ -236,95 +513,121 @@ func (n *network) processOptions(options ...NetworkOption) {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
-func (n *network) Delete() error {
|
|
|
|
- var err error
|
|
|
|
|
|
+func (n *network) driverScope() string {
|
|
|
|
+ c := n.getController()
|
|
|
|
|
|
- n.Lock()
|
|
|
|
- ctrlr := n.ctrlr
|
|
|
|
- n.Unlock()
|
|
|
|
|
|
+ c.Lock()
|
|
|
|
+ // Check if a driver for the specified network type is available
|
|
|
|
+ dd, ok := c.drivers[n.networkType]
|
|
|
|
+ c.Unlock()
|
|
|
|
+
|
|
|
|
+ if !ok {
|
|
|
|
+ var err error
|
|
|
|
+ dd, err = c.loadDriver(n.networkType)
|
|
|
|
+ if err != nil {
|
|
|
|
+ // If driver could not be resolved simply return an empty string
|
|
|
|
+ return ""
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return dd.capability.DataScope
|
|
|
|
+}
|
|
|
|
|
|
- ctrlr.Lock()
|
|
|
|
- _, ok := ctrlr.networks[n.id]
|
|
|
|
- ctrlr.Unlock()
|
|
|
|
|
|
+func (n *network) driver() (driverapi.Driver, error) {
|
|
|
|
+ c := n.getController()
|
|
|
|
+
|
|
|
|
+ c.Lock()
|
|
|
|
+ // Check if a driver for the specified network type is available
|
|
|
|
+ dd, ok := c.drivers[n.networkType]
|
|
|
|
+ c.Unlock()
|
|
|
|
|
|
if !ok {
|
|
if !ok {
|
|
- return &UnknownNetworkError{name: n.name, id: n.id}
|
|
|
|
|
|
+ var err error
|
|
|
|
+ dd, err = c.loadDriver(n.networkType)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- numEps := n.EndpointCnt()
|
|
|
|
|
|
+ return dd.driver, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) Delete() error {
|
|
|
|
+ n.Lock()
|
|
|
|
+ c := n.ctrlr
|
|
|
|
+ name := n.name
|
|
|
|
+ id := n.id
|
|
|
|
+ n.Unlock()
|
|
|
|
+
|
|
|
|
+ n, err := c.getNetworkFromStore(id)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return &UnknownNetworkError{name: name, id: id}
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ numEps := n.getEpCnt().EndpointCnt()
|
|
if numEps != 0 {
|
|
if numEps != 0 {
|
|
return &ActiveEndpointsError{name: n.name, id: n.id}
|
|
return &ActiveEndpointsError{name: n.name, id: n.id}
|
|
}
|
|
}
|
|
|
|
|
|
- // deleteNetworkFromStore performs an atomic delete operation and the network.endpointCnt field will help
|
|
|
|
- // prevent any possible race between endpoint join and network delete
|
|
|
|
- if err = ctrlr.deleteFromStore(n); err != nil {
|
|
|
|
- if err == datastore.ErrKeyModified {
|
|
|
|
- return types.InternalErrorf("operation in progress. delete failed for network %s. Please try again.")
|
|
|
|
- }
|
|
|
|
|
|
+ if err = n.deleteNetwork(); err != nil {
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
-
|
|
|
|
defer func() {
|
|
defer func() {
|
|
if err != nil {
|
|
if err != nil {
|
|
- n.dbExists = false
|
|
|
|
- if e := ctrlr.updateToStore(n); e != nil {
|
|
|
|
- log.Warnf("failed to recreate network in store %s : %v", n.name, e)
|
|
|
|
|
|
+ if e := c.addNetwork(n); e != nil {
|
|
|
|
+ log.Warnf("failed to rollback deleteNetwork for network %s: %v",
|
|
|
|
+ n.Name(), err)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}()
|
|
|
|
|
|
- if err = n.deleteNetwork(); err != nil {
|
|
|
|
- return err
|
|
|
|
|
|
+ // deleteFromStore performs an atomic delete operation and the
|
|
|
|
+ // network.epCnt will help prevent any possible
|
|
|
|
+ // race between endpoint join and network delete
|
|
|
|
+ if err = n.getController().deleteFromStore(n.getEpCnt()); err != nil {
|
|
|
|
+ return fmt.Errorf("error deleting network endpoint count from store: %v", err)
|
|
|
|
+ }
|
|
|
|
+ if err = n.getController().deleteFromStore(n); err != nil {
|
|
|
|
+ return fmt.Errorf("error deleting network from store: %v", err)
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ n.ipamRelease()
|
|
|
|
+
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
func (n *network) deleteNetwork() error {
|
|
func (n *network) deleteNetwork() error {
|
|
- n.Lock()
|
|
|
|
- id := n.id
|
|
|
|
- d := n.driver
|
|
|
|
- n.ctrlr.Lock()
|
|
|
|
- delete(n.ctrlr.networks, id)
|
|
|
|
- n.ctrlr.Unlock()
|
|
|
|
- n.Unlock()
|
|
|
|
|
|
+ d, err := n.driver()
|
|
|
|
+ if err != nil {
|
|
|
|
+ return fmt.Errorf("failed deleting network: %v", err)
|
|
|
|
+ }
|
|
|
|
|
|
- if err := d.DeleteNetwork(n.id); err != nil {
|
|
|
|
|
|
+ if err := d.DeleteNetwork(n.ID()); err != nil {
|
|
// Forbidden Errors should be honored
|
|
// Forbidden Errors should be honored
|
|
if _, ok := err.(types.ForbiddenError); ok {
|
|
if _, ok := err.(types.ForbiddenError); ok {
|
|
- n.ctrlr.Lock()
|
|
|
|
- n.ctrlr.networks[n.id] = n
|
|
|
|
- n.ctrlr.Unlock()
|
|
|
|
return err
|
|
return err
|
|
}
|
|
}
|
|
- log.Warnf("driver error deleting network %s : %v", n.name, err)
|
|
|
|
|
|
+
|
|
|
|
+ if _, ok := err.(types.MaskableError); !ok {
|
|
|
|
+ log.Warnf("driver error deleting network %s : %v", n.name, err)
|
|
|
|
+ }
|
|
}
|
|
}
|
|
- n.stopWatch()
|
|
|
|
|
|
+
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
func (n *network) addEndpoint(ep *endpoint) error {
|
|
func (n *network) addEndpoint(ep *endpoint) error {
|
|
- var err error
|
|
|
|
- n.Lock()
|
|
|
|
- n.endpoints[ep.id] = ep
|
|
|
|
- d := n.driver
|
|
|
|
- n.Unlock()
|
|
|
|
-
|
|
|
|
- defer func() {
|
|
|
|
- if err != nil {
|
|
|
|
- n.Lock()
|
|
|
|
- delete(n.endpoints, ep.id)
|
|
|
|
- n.Unlock()
|
|
|
|
- }
|
|
|
|
- }()
|
|
|
|
|
|
+ d, err := n.driver()
|
|
|
|
+ if err != nil {
|
|
|
|
+ return fmt.Errorf("failed to add endpoint: %v", err)
|
|
|
|
+ }
|
|
|
|
|
|
- err = d.CreateEndpoint(n.id, ep.id, ep, ep.generic)
|
|
|
|
|
|
+ err = d.CreateEndpoint(n.id, ep.id, ep.Interface(), ep.generic)
|
|
if err != nil {
|
|
if err != nil {
|
|
- return types.InternalErrorf("failed to create endpoint %s on network %s: %v", ep.Name(), n.Name(), err)
|
|
|
|
|
|
+ return types.InternalErrorf("failed to create endpoint %s on network %s: %v",
|
|
|
|
+ ep.Name(), n.Name(), err)
|
|
}
|
|
}
|
|
|
|
|
|
- n.updateSvcRecord(ep, true)
|
|
|
|
return nil
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
@@ -338,54 +641,69 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
|
|
return nil, types.ForbiddenErrorf("service endpoint with name %s already exists", name)
|
|
return nil, types.ForbiddenErrorf("service endpoint with name %s already exists", name)
|
|
}
|
|
}
|
|
|
|
|
|
- ep := &endpoint{name: name,
|
|
|
|
- generic: make(map[string]interface{})}
|
|
|
|
|
|
+ ep := &endpoint{name: name, generic: make(map[string]interface{}), iface: &endpointInterface{}}
|
|
ep.id = stringid.GenerateRandomID()
|
|
ep.id = stringid.GenerateRandomID()
|
|
|
|
+
|
|
|
|
+ // Initialize ep.network with a possibly stale copy of n. We need this to get network from
|
|
|
|
+ // store. But once we get it from store we will have the most uptodate copy possible.
|
|
ep.network = n
|
|
ep.network = n
|
|
- ep.processOptions(options...)
|
|
|
|
|
|
+ ep.network, err = ep.getNetworkFromStore()
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, fmt.Errorf("failed to get network during CreateEndpoint: %v", err)
|
|
|
|
+ }
|
|
|
|
+ n = ep.network
|
|
|
|
|
|
- n.Lock()
|
|
|
|
- ctrlr := n.ctrlr
|
|
|
|
- n.Unlock()
|
|
|
|
|
|
+ ep.processOptions(options...)
|
|
|
|
|
|
- n.IncEndpointCnt()
|
|
|
|
- if err = ctrlr.updateToStore(n); err != nil {
|
|
|
|
|
|
+ if err = ep.assignAddress(); err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
defer func() {
|
|
defer func() {
|
|
if err != nil {
|
|
if err != nil {
|
|
- n.DecEndpointCnt()
|
|
|
|
- if err = ctrlr.updateToStore(n); err != nil {
|
|
|
|
- log.Warnf("endpoint count cleanup failed when updating network for %s : %v", name, err)
|
|
|
|
- }
|
|
|
|
|
|
+ ep.releaseAddress()
|
|
}
|
|
}
|
|
}()
|
|
}()
|
|
|
|
+
|
|
if err = n.addEndpoint(ep); err != nil {
|
|
if err = n.addEndpoint(ep); err != nil {
|
|
return nil, err
|
|
return nil, err
|
|
}
|
|
}
|
|
defer func() {
|
|
defer func() {
|
|
if err != nil {
|
|
if err != nil {
|
|
- if e := ep.Delete(); ep != nil {
|
|
|
|
|
|
+ if e := ep.deleteEndpoint(); e != nil {
|
|
log.Warnf("cleaning up endpoint failed %s : %v", name, e)
|
|
log.Warnf("cleaning up endpoint failed %s : %v", name, e)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}()
|
|
|
|
|
|
- if !ep.isLocalScoped() {
|
|
|
|
- if err = ctrlr.updateToStore(ep); err != nil {
|
|
|
|
- return nil, err
|
|
|
|
|
|
+ if err = n.getController().updateToStore(ep); err != nil {
|
|
|
|
+ return nil, err
|
|
|
|
+ }
|
|
|
|
+ defer func() {
|
|
|
|
+ if err != nil {
|
|
|
|
+ if e := n.getController().deleteFromStore(ep); e != nil {
|
|
|
|
+ log.Warnf("error rolling back endpoint %s from store: %v", name, e)
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ // Increment endpoint count to indicate completion of endpoint addition
|
|
|
|
+ if err = n.getEpCnt().IncEndpointCnt(); err != nil {
|
|
|
|
+ return nil, err
|
|
}
|
|
}
|
|
|
|
|
|
return ep, nil
|
|
return ep, nil
|
|
}
|
|
}
|
|
|
|
|
|
func (n *network) Endpoints() []Endpoint {
|
|
func (n *network) Endpoints() []Endpoint {
|
|
- n.Lock()
|
|
|
|
- defer n.Unlock()
|
|
|
|
- list := make([]Endpoint, 0, len(n.endpoints))
|
|
|
|
- for _, e := range n.endpoints {
|
|
|
|
- list = append(list, e)
|
|
|
|
|
|
+ var list []Endpoint
|
|
|
|
+
|
|
|
|
+ endpoints, err := n.getEndpointsFromStore()
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Error(err)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for _, ep := range endpoints {
|
|
|
|
+ list = append(list, ep)
|
|
}
|
|
}
|
|
|
|
|
|
return list
|
|
return list
|
|
@@ -426,28 +744,32 @@ func (n *network) EndpointByID(id string) (Endpoint, error) {
|
|
if id == "" {
|
|
if id == "" {
|
|
return nil, ErrInvalidID(id)
|
|
return nil, ErrInvalidID(id)
|
|
}
|
|
}
|
|
- n.Lock()
|
|
|
|
- defer n.Unlock()
|
|
|
|
- if e, ok := n.endpoints[id]; ok {
|
|
|
|
- return e, nil
|
|
|
|
|
|
+
|
|
|
|
+ ep, err := n.getEndpointFromStore(id)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, ErrNoSuchEndpoint(id)
|
|
}
|
|
}
|
|
- return nil, ErrNoSuchEndpoint(id)
|
|
|
|
-}
|
|
|
|
|
|
|
|
-func (n *network) isGlobalScoped() bool {
|
|
|
|
- return n.DataScope() == datastore.GlobalScope
|
|
|
|
|
|
+ return ep, nil
|
|
}
|
|
}
|
|
|
|
|
|
-func (n *network) updateSvcRecord(ep *endpoint, isAdd bool) {
|
|
|
|
|
|
+func (n *network) updateSvcRecord(ep *endpoint, localEps []*endpoint, isAdd bool) {
|
|
|
|
+ c := n.getController()
|
|
|
|
+ sr, ok := c.svcDb[n.ID()]
|
|
|
|
+ if !ok {
|
|
|
|
+ c.svcDb[n.ID()] = svcMap{}
|
|
|
|
+ sr = c.svcDb[n.ID()]
|
|
|
|
+ }
|
|
|
|
+
|
|
n.Lock()
|
|
n.Lock()
|
|
var recs []etchosts.Record
|
|
var recs []etchosts.Record
|
|
- if iface := ep.Iface(); iface != nil {
|
|
|
|
|
|
+ if iface := ep.Iface(); iface.Address() != nil {
|
|
if isAdd {
|
|
if isAdd {
|
|
- n.svcRecords[ep.Name()] = iface.Address().IP
|
|
|
|
- n.svcRecords[ep.Name()+"."+n.name] = iface.Address().IP
|
|
|
|
|
|
+ sr[ep.Name()] = iface.Address().IP
|
|
|
|
+ sr[ep.Name()+"."+n.name] = iface.Address().IP
|
|
} else {
|
|
} else {
|
|
- delete(n.svcRecords, ep.Name())
|
|
|
|
- delete(n.svcRecords, ep.Name()+"."+n.name)
|
|
|
|
|
|
+ delete(sr, ep.Name())
|
|
|
|
+ delete(sr, ep.Name()+"."+n.name)
|
|
}
|
|
}
|
|
|
|
|
|
recs = append(recs, etchosts.Record{
|
|
recs = append(recs, etchosts.Record{
|
|
@@ -468,12 +790,11 @@ func (n *network) updateSvcRecord(ep *endpoint, isAdd bool) {
|
|
}
|
|
}
|
|
|
|
|
|
var sbList []*sandbox
|
|
var sbList []*sandbox
|
|
- n.WalkEndpoints(func(e Endpoint) bool {
|
|
|
|
- if sb, hasSandbox := e.(*endpoint).getSandbox(); hasSandbox {
|
|
|
|
|
|
+ for _, ep := range localEps {
|
|
|
|
+ if sb, hasSandbox := ep.getSandbox(); hasSandbox {
|
|
sbList = append(sbList, sb)
|
|
sbList = append(sbList, sb)
|
|
}
|
|
}
|
|
- return false
|
|
|
|
- })
|
|
|
|
|
|
+ }
|
|
|
|
|
|
for _, sb := range sbList {
|
|
for _, sb := range sbList {
|
|
if isAdd {
|
|
if isAdd {
|
|
@@ -489,7 +810,9 @@ func (n *network) getSvcRecords() []etchosts.Record {
|
|
defer n.Unlock()
|
|
defer n.Unlock()
|
|
|
|
|
|
var recs []etchosts.Record
|
|
var recs []etchosts.Record
|
|
- for h, ip := range n.svcRecords {
|
|
|
|
|
|
+ sr, _ := n.ctrlr.svcDb[n.id]
|
|
|
|
+
|
|
|
|
+ for h, ip := range sr {
|
|
recs = append(recs, etchosts.Record{
|
|
recs = append(recs, etchosts.Record{
|
|
Hosts: h,
|
|
Hosts: h,
|
|
IP: ip.String(),
|
|
IP: ip.String(),
|
|
@@ -504,3 +827,268 @@ func (n *network) getController() *controller {
|
|
defer n.Unlock()
|
|
defer n.Unlock()
|
|
return n.ctrlr
|
|
return n.ctrlr
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+func (n *network) ipamAllocate() error {
|
|
|
|
+ // For now also exclude bridge from using new ipam
|
|
|
|
+ if n.Type() == "host" || n.Type() == "null" {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ipam, err := n.getController().getIpamDriver(n.ipamType)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if n.addrSpace == "" {
|
|
|
|
+ if n.addrSpace, err = n.deriveAddressSpace(); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ err = n.ipamAllocateVersion(4, ipam)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ defer func() {
|
|
|
|
+ if err != nil {
|
|
|
|
+ n.ipamReleaseVersion(4, ipam)
|
|
|
|
+ }
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ return n.ipamAllocateVersion(6, ipam)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) ipamAllocateVersion(ipVer int, ipam ipamapi.Ipam) error {
|
|
|
|
+ var (
|
|
|
|
+ cfgList *[]*IpamConf
|
|
|
|
+ infoList *[]*IpamInfo
|
|
|
|
+ err error
|
|
|
|
+ )
|
|
|
|
+
|
|
|
|
+ switch ipVer {
|
|
|
|
+ case 4:
|
|
|
|
+ cfgList = &n.ipamV4Config
|
|
|
|
+ infoList = &n.ipamV4Info
|
|
|
|
+ case 6:
|
|
|
|
+ cfgList = &n.ipamV6Config
|
|
|
|
+ infoList = &n.ipamV6Info
|
|
|
|
+ default:
|
|
|
|
+ return types.InternalErrorf("incorrect ip version passed to ipam allocate: %d", ipVer)
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if len(*cfgList) == 0 {
|
|
|
|
+ if ipVer == 6 {
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ *cfgList = []*IpamConf{&IpamConf{}}
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ *infoList = make([]*IpamInfo, len(*cfgList))
|
|
|
|
+
|
|
|
|
+ log.Debugf("Allocating IPv%d pools for network %s (%s)", ipVer, n.Name(), n.ID())
|
|
|
|
+
|
|
|
|
+ for i, cfg := range *cfgList {
|
|
|
|
+ if err = cfg.Validate(); err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+ d := &IpamInfo{}
|
|
|
|
+ (*infoList)[i] = d
|
|
|
|
+
|
|
|
|
+ d.PoolID, d.Pool, d.Meta, err = ipam.RequestPool(n.addrSpace, cfg.PreferredPool, cfg.SubPool, cfg.Options, ipVer == 6)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return err
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ defer func() {
|
|
|
|
+ if err != nil {
|
|
|
|
+ if err := ipam.ReleasePool(d.PoolID); err != nil {
|
|
|
|
+ log.Warnf("Failed to release address pool %s after failure to create network %s (%s)", d.PoolID, n.Name(), n.ID())
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }()
|
|
|
|
+
|
|
|
|
+ if gws, ok := d.Meta[netlabel.Gateway]; ok {
|
|
|
|
+ if d.Gateway, err = types.ParseCIDR(gws); err != nil {
|
|
|
|
+ return types.BadRequestErrorf("failed to parse gateway address (%v) returned by ipam driver: %v", gws, err)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // If user requested a specific gateway, libnetwork will allocate it
|
|
|
|
+ // irrespective of whether ipam driver returned a gateway already.
|
|
|
|
+ // If none of the above is true, libnetwork will allocate one.
|
|
|
|
+ if cfg.Gateway != "" || d.Gateway == nil {
|
|
|
|
+ if d.Gateway, _, err = ipam.RequestAddress(d.PoolID, net.ParseIP(cfg.Gateway), nil); err != nil {
|
|
|
|
+ return types.InternalErrorf("failed to allocate gateway (%v): %v", cfg.Gateway, err)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Auxiliary addresses must be part of the master address pool
|
|
|
|
+ // If they fall into the container addressable pool, libnetwork will reserve them
|
|
|
|
+ if cfg.AuxAddresses != nil {
|
|
|
|
+ var ip net.IP
|
|
|
|
+ d.IPAMData.AuxAddresses = make(map[string]*net.IPNet, len(cfg.AuxAddresses))
|
|
|
|
+ for k, v := range cfg.AuxAddresses {
|
|
|
|
+ if ip = net.ParseIP(v); ip == nil {
|
|
|
|
+ return types.BadRequestErrorf("non parsable secondary ip address (%s:%s) passed for network %s", k, v, n.Name())
|
|
|
|
+ }
|
|
|
|
+ if !d.Pool.Contains(ip) {
|
|
|
|
+ return types.ForbiddenErrorf("auxilairy address: (%s:%s) must belong to the master pool: %s", k, v, d.Pool)
|
|
|
|
+ }
|
|
|
|
+ // Attempt reservation in the container addressable pool, silent the error if address does not belong to that pool
|
|
|
|
+ if d.IPAMData.AuxAddresses[k], _, err = ipam.RequestAddress(d.PoolID, ip, nil); err != nil && err != ipamapi.ErrIPOutOfRange {
|
|
|
|
+ return types.InternalErrorf("failed to allocate secondary ip address (%s:%s): %v", k, v, err)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) ipamRelease() {
|
|
|
|
+ // For now exclude host and null
|
|
|
|
+ if n.Type() == "host" || n.Type() == "null" {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ ipam, err := n.getController().getIpamDriver(n.ipamType)
|
|
|
|
+ if err != nil {
|
|
|
|
+ log.Warnf("Failed to retrieve ipam driver to release address pool(s) on delete of network %s (%s): %v", n.Name(), n.ID(), err)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+ n.ipamReleaseVersion(4, ipam)
|
|
|
|
+ n.ipamReleaseVersion(6, ipam)
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) ipamReleaseVersion(ipVer int, ipam ipamapi.Ipam) {
|
|
|
|
+ var infoList []*IpamInfo
|
|
|
|
+
|
|
|
|
+ switch ipVer {
|
|
|
|
+ case 4:
|
|
|
|
+ infoList = n.ipamV4Info
|
|
|
|
+ case 6:
|
|
|
|
+ infoList = n.ipamV6Info
|
|
|
|
+ default:
|
|
|
|
+ log.Warnf("incorrect ip version passed to ipam release: %d", ipVer)
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if infoList == nil {
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ log.Debugf("releasing IPv%d pools from network %s (%s)", ipVer, n.Name(), n.ID())
|
|
|
|
+
|
|
|
|
+ for _, d := range infoList {
|
|
|
|
+ if d.Gateway != nil {
|
|
|
|
+ if err := ipam.ReleaseAddress(d.PoolID, d.Gateway.IP); err != nil {
|
|
|
|
+ log.Warnf("Failed to release gateway ip address %s on delete of network %s (%s): %v", d.Gateway.IP, n.Name(), n.ID(), err)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if d.IPAMData.AuxAddresses != nil {
|
|
|
|
+ for k, nw := range d.IPAMData.AuxAddresses {
|
|
|
|
+ if d.Pool.Contains(nw.IP) {
|
|
|
|
+ if err := ipam.ReleaseAddress(d.PoolID, nw.IP); err != nil && err != ipamapi.ErrIPOutOfRange {
|
|
|
|
+ log.Warnf("Failed to release secondary ip address %s (%v) on delete of network %s (%s): %v", k, nw.IP, n.Name(), n.ID(), err)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if err := ipam.ReleasePool(d.PoolID); err != nil {
|
|
|
|
+ log.Warnf("Failed to release address pool %s on delete of network %s (%s): %v", d.PoolID, n.Name(), n.ID(), err)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) getIPInfo(ipVer int) []*IpamInfo {
|
|
|
|
+ var info []*IpamInfo
|
|
|
|
+ switch ipVer {
|
|
|
|
+ case 4:
|
|
|
|
+ info = n.ipamV4Info
|
|
|
|
+ case 6:
|
|
|
|
+ info = n.ipamV6Info
|
|
|
|
+ default:
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ l := make([]*IpamInfo, 0, len(info))
|
|
|
|
+ n.Lock()
|
|
|
|
+ for _, d := range info {
|
|
|
|
+ l = append(l, d)
|
|
|
|
+ }
|
|
|
|
+ n.Unlock()
|
|
|
|
+ return l
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) getIPData(ipVer int) []driverapi.IPAMData {
|
|
|
|
+ var info []*IpamInfo
|
|
|
|
+ switch ipVer {
|
|
|
|
+ case 4:
|
|
|
|
+ info = n.ipamV4Info
|
|
|
|
+ case 6:
|
|
|
|
+ info = n.ipamV6Info
|
|
|
|
+ default:
|
|
|
|
+ return nil
|
|
|
|
+ }
|
|
|
|
+ l := make([]driverapi.IPAMData, 0, len(info))
|
|
|
|
+ n.Lock()
|
|
|
|
+ for _, d := range info {
|
|
|
|
+ l = append(l, d.IPAMData)
|
|
|
|
+ }
|
|
|
|
+ n.Unlock()
|
|
|
|
+ return l
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) deriveAddressSpace() (string, error) {
|
|
|
|
+ c := n.getController()
|
|
|
|
+ c.Lock()
|
|
|
|
+ ipd, ok := c.ipamDrivers[n.ipamType]
|
|
|
|
+ c.Unlock()
|
|
|
|
+ if !ok {
|
|
|
|
+ return "", types.NotFoundErrorf("could not find ipam driver %s to get default address space", n.ipamType)
|
|
|
|
+ }
|
|
|
|
+ if n.DataScope() == datastore.GlobalScope {
|
|
|
|
+ return ipd.defaultGlobalAddressSpace, nil
|
|
|
|
+ }
|
|
|
|
+ return ipd.defaultLocalAddressSpace, nil
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) Info() NetworkInfo {
|
|
|
|
+ return n
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) Labels() map[string]string {
|
|
|
|
+ n.Lock()
|
|
|
|
+ defer n.Unlock()
|
|
|
|
+ if n.generic != nil {
|
|
|
|
+ if m, ok := n.generic[netlabel.GenericData]; ok {
|
|
|
|
+ return m.(map[string]string)
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return map[string]string{}
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) Scope() string {
|
|
|
|
+ return n.driverScope()
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+func (n *network) IpamConfig() (string, []*IpamConf, []*IpamConf) {
|
|
|
|
+ n.Lock()
|
|
|
|
+ defer n.Unlock()
|
|
|
|
+
|
|
|
|
+ v4L := make([]*IpamConf, len(n.ipamV4Config))
|
|
|
|
+ v6L := make([]*IpamConf, len(n.ipamV6Config))
|
|
|
|
+
|
|
|
|
+ for i, c := range n.ipamV4Config {
|
|
|
|
+ cc := &IpamConf{}
|
|
|
|
+ c.CopyTo(cc)
|
|
|
|
+ v4L[i] = cc
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ for i, c := range n.ipamV6Config {
|
|
|
|
+ cc := &IpamConf{}
|
|
|
|
+ c.CopyTo(cc)
|
|
|
|
+ v6L[i] = cc
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return n.ipamType, v4L, v6L
|
|
|
|
+}
|