Browse code

Vendoring libnetwork @b2bc1a6

Signed-off-by: Alessandro Boch <aboch@docker.com>

Alessandro Boch authored on 2017/05/14 11:21:58
Showing 29 changed files
... ...
@@ -26,7 +26,7 @@ github.com/imdario/mergo 0.2.1
26 26
 golang.org/x/sync de49d9dcd27d4f764488181bea099dfe6179bcf0
27 27
 
28 28
 #get libnetwork packages
29
-github.com/docker/libnetwork 6786135bf7de08ec26a72a6f7e4291d27d113a3f
29
+github.com/docker/libnetwork b2bc1a68486ccf8ada503162d9f0df7d31bdd8fb
30 30
 github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
31 31
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
32 32
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
... ...
@@ -222,7 +222,7 @@ func (c *controller) agentSetup(clusterProvider cluster.Provider) error {
222 222
 			return err
223 223
 		}
224 224
 		c.drvRegistry.WalkDrivers(func(name string, driver driverapi.Driver, capability driverapi.Capability) bool {
225
-			if capability.DataScope == datastore.GlobalScope {
225
+			if capability.ConnectivityScope == datastore.GlobalScope {
226 226
 				c.agentDriverNotify(driver)
227 227
 			}
228 228
 			return false
... ...
@@ -507,7 +507,7 @@ func (n *network) Services() map[string]ServiceInfo {
507 507
 }
508 508
 
509 509
 func (n *network) isClusterEligible() bool {
510
-	if n.driverScope() != datastore.GlobalScope {
510
+	if n.scope != datastore.SwarmScope || !n.driverIsMultihost() {
511 511
 		return false
512 512
 	}
513 513
 	return n.getController().getAgent() != nil
... ...
@@ -621,7 +621,7 @@ func (c *controller) pushNodeDiscovery(d driverapi.Driver, cap driverapi.Capabil
621 621
 		}
622 622
 	}
623 623
 
624
-	if d == nil || cap.DataScope != datastore.GlobalScope || nodes == nil {
624
+	if d == nil || cap.ConnectivityScope != datastore.GlobalScope || nodes == nil {
625 625
 		return
626 626
 	}
627 627
 
... ...
@@ -722,22 +722,46 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ...
722 722
 	}
723 723
 
724 724
 	network.processOptions(options...)
725
+	if err := network.validateConfiguration(); err != nil {
726
+		return nil, err
727
+	}
728
+
729
+	var (
730
+		cap *driverapi.Capability
731
+		err error
732
+	)
725 733
 
726
-	_, cap, err := network.resolveDriver(networkType, true)
734
+	// Reset network types, force local scope and skip allocation and
735
+	// plumbing for configuration networks. Reset of the config-only
736
+	// network drivers is needed so that this special network is not
737
+	// usable by old engine versions.
738
+	if network.configOnly {
739
+		network.scope = datastore.LocalScope
740
+		network.networkType = "null"
741
+		network.ipamType = ""
742
+		goto addToStore
743
+	}
744
+
745
+	_, cap, err = network.resolveDriver(network.networkType, true)
727 746
 	if err != nil {
728 747
 		return nil, err
729 748
 	}
730 749
 
750
+	if network.scope == datastore.LocalScope && cap.DataScope == datastore.GlobalScope {
751
+		return nil, types.ForbiddenErrorf("cannot downgrade network scope for %s networks", networkType)
752
+
753
+	}
731 754
 	if network.ingress && cap.DataScope != datastore.GlobalScope {
732 755
 		return nil, types.ForbiddenErrorf("Ingress network can only be global scope network")
733 756
 	}
734 757
 
735
-	if cap.DataScope == datastore.GlobalScope && !c.isDistributedControl() && !network.dynamic {
758
+	// At this point the network scope is still unknown if not set by user
759
+	if (cap.DataScope == datastore.GlobalScope || network.scope == datastore.SwarmScope) &&
760
+		!c.isDistributedControl() && !network.dynamic {
736 761
 		if c.isManager() {
737 762
 			// For non-distributed controlled environment, globalscoped non-dynamic networks are redirected to Manager
738 763
 			return nil, ManagerRedirectError(name)
739 764
 		}
740
-
741 765
 		return nil, types.ForbiddenErrorf("Cannot create a multi-host network from a worker node. Please create the network from a manager node.")
742 766
 	}
743 767
 
... ...
@@ -747,6 +771,26 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ...
747 747
 		return nil, err
748 748
 	}
749 749
 
750
+	// From this point on, we need the network specific configuration,
751
+	// which may come from a configuration-only network
752
+	if network.configFrom != "" {
753
+		t, err := c.getConfigNetwork(network.configFrom)
754
+		if err != nil {
755
+			return nil, types.NotFoundErrorf("configuration network %q does not exist", network.configFrom)
756
+		}
757
+		if err := t.applyConfigurationTo(network); err != nil {
758
+			return nil, types.InternalErrorf("Failed to apply configuration: %v", err)
759
+		}
760
+		defer func() {
761
+			if err == nil {
762
+				if err := t.getEpCnt().IncEndpointCnt(); err != nil {
763
+					logrus.Warnf("Failed to update reference count for configuration network %q on creation of network %q: %v",
764
+						t.Name(), network.Name(), err)
765
+				}
766
+			}
767
+		}()
768
+	}
769
+
750 770
 	err = network.ipamAllocate()
751 771
 	if err != nil {
752 772
 		return nil, err
... ...
@@ -769,6 +813,7 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ...
769 769
 		}
770 770
 	}()
771 771
 
772
+addToStore:
772 773
 	// First store the endpoint count, then the network. To avoid to
773 774
 	// end up with a datastore containing a network and not an epCnt,
774 775
 	// in case of an ungraceful shutdown during this function call.
... ...
@@ -788,6 +833,9 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ...
788 788
 	if err = c.updateToStore(network); err != nil {
789 789
 		return nil, err
790 790
 	}
791
+	if network.configOnly {
792
+		return network, nil
793
+	}
791 794
 
792 795
 	joinCluster(network)
793 796
 	if !c.isDistributedControl() {
... ...
@@ -796,11 +844,18 @@ func (c *controller) NewNetwork(networkType, name string, id string, options ...
796 796
 		c.Unlock()
797 797
 	}
798 798
 
799
+	c.Lock()
800
+	arrangeUserFilterRule()
801
+	c.Unlock()
802
+
799 803
 	return network, nil
800 804
 }
801 805
 
802 806
 var joinCluster NetworkWalker = func(nw Network) bool {
803 807
 	n := nw.(*network)
808
+	if n.configOnly {
809
+		return false
810
+	}
804 811
 	if err := n.joinCluster(); err != nil {
805 812
 		logrus.Errorf("Failed to join network %s (%s) into agent cluster: %v", n.Name(), n.ID(), err)
806 813
 	}
... ...
@@ -816,6 +871,9 @@ func (c *controller) reservePools() {
816 816
 	}
817 817
 
818 818
 	for _, n := range networks {
819
+		if n.configOnly {
820
+			continue
821
+		}
819 822
 		if !doReplayPoolReserve(n) {
820 823
 			continue
821 824
 		}
... ...
@@ -115,7 +115,10 @@ const (
115 115
 	// LocalScope indicates to store the KV object in local datastore such as boltdb
116 116
 	LocalScope = "local"
117 117
 	// GlobalScope indicates to store the KV object in global datastore such as consul/etcd/zookeeper
118
-	GlobalScope   = "global"
118
+	GlobalScope = "global"
119
+	// SwarmScope is not indicating a datastore location. It is defined here
120
+	// along with the other two scopes just for consistency.
121
+	SwarmScope    = "swarm"
119 122
 	defaultPrefix = "/var/lib/docker/network/files"
120 123
 )
121 124
 
... ...
@@ -161,7 +161,8 @@ type DriverCallback interface {
161 161
 
162 162
 // Capability represents the high level capabilities of the drivers which libnetwork can make use of
163 163
 type Capability struct {
164
-	DataScope string
164
+	DataScope         string
165
+	ConnectivityScope string
165 166
 }
166 167
 
167 168
 // IPAMData represents the per-network ip related
... ...
@@ -153,7 +153,8 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
153 153
 	}
154 154
 
155 155
 	c := driverapi.Capability{
156
-		DataScope: datastore.LocalScope,
156
+		DataScope:         datastore.LocalScope,
157
+		ConnectivityScope: datastore.LocalScope,
157 158
 	}
158 159
 	return dc.RegisterDriver(networkType, d, c)
159 160
 }
160 161
new file mode 100644
... ...
@@ -0,0 +1,88 @@
0
+package brmanager
1
+
2
+import (
3
+	"github.com/docker/libnetwork/datastore"
4
+	"github.com/docker/libnetwork/discoverapi"
5
+	"github.com/docker/libnetwork/driverapi"
6
+	"github.com/docker/libnetwork/types"
7
+)
8
+
9
+const networkType = "bridge"
10
+
11
+type driver struct{}
12
+
13
+// Init registers a new instance of bridge manager driver
14
+func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
15
+	c := driverapi.Capability{
16
+		DataScope:         datastore.LocalScope,
17
+		ConnectivityScope: datastore.LocalScope,
18
+	}
19
+	return dc.RegisterDriver(networkType, &driver{}, c)
20
+}
21
+
22
+func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
23
+	return nil, types.NotImplementedErrorf("not implemented")
24
+}
25
+
26
+func (d *driver) NetworkFree(id string) error {
27
+	return types.NotImplementedErrorf("not implemented")
28
+}
29
+
30
+func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
31
+	return types.NotImplementedErrorf("not implemented")
32
+}
33
+
34
+func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
35
+}
36
+
37
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
38
+	return "", nil
39
+}
40
+
41
+func (d *driver) DeleteNetwork(nid string) error {
42
+	return types.NotImplementedErrorf("not implemented")
43
+}
44
+
45
+func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
46
+	return types.NotImplementedErrorf("not implemented")
47
+}
48
+
49
+func (d *driver) DeleteEndpoint(nid, eid string) error {
50
+	return types.NotImplementedErrorf("not implemented")
51
+}
52
+
53
+func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
54
+	return nil, types.NotImplementedErrorf("not implemented")
55
+}
56
+
57
+func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
58
+	return types.NotImplementedErrorf("not implemented")
59
+}
60
+
61
+func (d *driver) Leave(nid, eid string) error {
62
+	return types.NotImplementedErrorf("not implemented")
63
+}
64
+
65
+func (d *driver) Type() string {
66
+	return networkType
67
+}
68
+
69
+func (d *driver) IsBuiltIn() bool {
70
+	return true
71
+}
72
+
73
+func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
74
+	return types.NotImplementedErrorf("not implemented")
75
+}
76
+
77
+func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
78
+	return types.NotImplementedErrorf("not implemented")
79
+}
80
+
81
+func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
82
+	return types.NotImplementedErrorf("not implemented")
83
+}
84
+
85
+func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
86
+	return types.NotImplementedErrorf("not implemented")
87
+}
... ...
@@ -53,7 +53,7 @@ func setupIPChains(config *configuration) (*iptables.ChainInfo, *iptables.ChainI
53 53
 		return nil, nil, nil, fmt.Errorf("failed to create FILTER isolation chain: %v", err)
54 54
 	}
55 55
 
56
-	if err := addReturnRule(IsolationChain); err != nil {
56
+	if err := iptables.AddReturnRule(IsolationChain); err != nil {
57 57
 		return nil, nil, nil, err
58 58
 	}
59 59
 
... ...
@@ -117,7 +117,7 @@ func (n *bridgeNetwork) setupIPTables(config *networkConfiguration, i *bridgeInt
117 117
 	}
118 118
 
119 119
 	d.Lock()
120
-	err = ensureJumpRule("FORWARD", IsolationChain)
120
+	err = iptables.EnsureJumpRule("FORWARD", IsolationChain)
121 121
 	d.Unlock()
122 122
 	if err != nil {
123 123
 		return err
... ...
@@ -280,46 +280,6 @@ func setINC(iface1, iface2 string, enable bool) error {
280 280
 	return nil
281 281
 }
282 282
 
283
-func addReturnRule(chain string) error {
284
-	var (
285
-		table = iptables.Filter
286
-		args  = []string{"-j", "RETURN"}
287
-	)
288
-
289
-	if iptables.Exists(table, chain, args...) {
290
-		return nil
291
-	}
292
-
293
-	err := iptables.RawCombinedOutput(append([]string{"-I", chain}, args...)...)
294
-	if err != nil {
295
-		return fmt.Errorf("unable to add return rule in %s chain: %s", chain, err.Error())
296
-	}
297
-
298
-	return nil
299
-}
300
-
301
-// Ensure the jump rule is on top
302
-func ensureJumpRule(fromChain, toChain string) error {
303
-	var (
304
-		table = iptables.Filter
305
-		args  = []string{"-j", toChain}
306
-	)
307
-
308
-	if iptables.Exists(table, fromChain, args...) {
309
-		err := iptables.RawCombinedOutput(append([]string{"-D", fromChain}, args...)...)
310
-		if err != nil {
311
-			return fmt.Errorf("unable to remove jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
312
-		}
313
-	}
314
-
315
-	err := iptables.RawCombinedOutput(append([]string{"-I", fromChain}, args...)...)
316
-	if err != nil {
317
-		return fmt.Errorf("unable to insert jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
318
-	}
319
-
320
-	return nil
321
-}
322
-
323 283
 func removeIPChains() {
324 284
 	for _, chainInfo := range []iptables.ChainInfo{
325 285
 		{Name: DockerChain, Table: iptables.Nat},
... ...
@@ -19,7 +19,8 @@ type driver struct {
19 19
 // Init registers a new instance of host driver
20 20
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
21 21
 	c := driverapi.Capability{
22
-		DataScope: datastore.LocalScope,
22
+		DataScope:         datastore.LocalScope,
23
+		ConnectivityScope: datastore.LocalScope,
23 24
 	}
24 25
 	return dc.RegisterDriver(networkType, &driver{}, c)
25 26
 }
... ...
@@ -58,7 +58,8 @@ type network struct {
58 58
 // Init initializes and registers the libnetwork ipvlan driver
59 59
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
60 60
 	c := driverapi.Capability{
61
-		DataScope: datastore.LocalScope,
61
+		DataScope:         datastore.LocalScope,
62
+		ConnectivityScope: datastore.GlobalScope,
62 63
 	}
63 64
 	d := &driver{
64 65
 		networks: networkTable{},
65 66
new file mode 100644
... ...
@@ -0,0 +1,88 @@
0
+package ivmanager
1
+
2
+import (
3
+	"github.com/docker/libnetwork/datastore"
4
+	"github.com/docker/libnetwork/discoverapi"
5
+	"github.com/docker/libnetwork/driverapi"
6
+	"github.com/docker/libnetwork/types"
7
+)
8
+
9
+const networkType = "ipvlan"
10
+
11
+type driver struct{}
12
+
13
+// Init registers a new instance of ipvlan manager driver
14
+func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
15
+	c := driverapi.Capability{
16
+		DataScope:         datastore.LocalScope,
17
+		ConnectivityScope: datastore.GlobalScope,
18
+	}
19
+	return dc.RegisterDriver(networkType, &driver{}, c)
20
+}
21
+
22
+func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
23
+	return nil, types.NotImplementedErrorf("not implemented")
24
+}
25
+
26
+func (d *driver) NetworkFree(id string) error {
27
+	return types.NotImplementedErrorf("not implemented")
28
+}
29
+
30
+func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
31
+	return types.NotImplementedErrorf("not implemented")
32
+}
33
+
34
+func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
35
+}
36
+
37
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
38
+	return "", nil
39
+}
40
+
41
+func (d *driver) DeleteNetwork(nid string) error {
42
+	return types.NotImplementedErrorf("not implemented")
43
+}
44
+
45
+func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
46
+	return types.NotImplementedErrorf("not implemented")
47
+}
48
+
49
+func (d *driver) DeleteEndpoint(nid, eid string) error {
50
+	return types.NotImplementedErrorf("not implemented")
51
+}
52
+
53
+func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
54
+	return nil, types.NotImplementedErrorf("not implemented")
55
+}
56
+
57
+func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
58
+	return types.NotImplementedErrorf("not implemented")
59
+}
60
+
61
+func (d *driver) Leave(nid, eid string) error {
62
+	return types.NotImplementedErrorf("not implemented")
63
+}
64
+
65
+func (d *driver) Type() string {
66
+	return networkType
67
+}
68
+
69
+func (d *driver) IsBuiltIn() bool {
70
+	return true
71
+}
72
+
73
+func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
74
+	return types.NotImplementedErrorf("not implemented")
75
+}
76
+
77
+func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
78
+	return types.NotImplementedErrorf("not implemented")
79
+}
80
+
81
+func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
82
+	return types.NotImplementedErrorf("not implemented")
83
+}
84
+
85
+func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
86
+	return types.NotImplementedErrorf("not implemented")
87
+}
... ...
@@ -60,7 +60,8 @@ type network struct {
60 60
 // Init initializes and registers the libnetwork macvlan driver
61 61
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
62 62
 	c := driverapi.Capability{
63
-		DataScope: datastore.LocalScope,
63
+		DataScope:         datastore.LocalScope,
64
+		ConnectivityScope: datastore.GlobalScope,
64 65
 	}
65 66
 	d := &driver{
66 67
 		networks: networkTable{},
67 68
new file mode 100644
... ...
@@ -0,0 +1,88 @@
0
+package mvmanager
1
+
2
+import (
3
+	"github.com/docker/libnetwork/datastore"
4
+	"github.com/docker/libnetwork/discoverapi"
5
+	"github.com/docker/libnetwork/driverapi"
6
+	"github.com/docker/libnetwork/types"
7
+)
8
+
9
+const networkType = "macvlan"
10
+
11
+type driver struct{}
12
+
13
+// Init registers a new instance of macvlan manager driver
14
+func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
15
+	c := driverapi.Capability{
16
+		DataScope:         datastore.LocalScope,
17
+		ConnectivityScope: datastore.GlobalScope,
18
+	}
19
+	return dc.RegisterDriver(networkType, &driver{}, c)
20
+}
21
+
22
+func (d *driver) NetworkAllocate(id string, option map[string]string, ipV4Data, ipV6Data []driverapi.IPAMData) (map[string]string, error) {
23
+	return nil, types.NotImplementedErrorf("not implemented")
24
+}
25
+
26
+func (d *driver) NetworkFree(id string) error {
27
+	return types.NotImplementedErrorf("not implemented")
28
+}
29
+
30
+func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
31
+	return types.NotImplementedErrorf("not implemented")
32
+}
33
+
34
+func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
35
+}
36
+
37
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
38
+	return "", nil
39
+}
40
+
41
+func (d *driver) DeleteNetwork(nid string) error {
42
+	return types.NotImplementedErrorf("not implemented")
43
+}
44
+
45
+func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo, epOptions map[string]interface{}) error {
46
+	return types.NotImplementedErrorf("not implemented")
47
+}
48
+
49
+func (d *driver) DeleteEndpoint(nid, eid string) error {
50
+	return types.NotImplementedErrorf("not implemented")
51
+}
52
+
53
+func (d *driver) EndpointOperInfo(nid, eid string) (map[string]interface{}, error) {
54
+	return nil, types.NotImplementedErrorf("not implemented")
55
+}
56
+
57
+func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo, options map[string]interface{}) error {
58
+	return types.NotImplementedErrorf("not implemented")
59
+}
60
+
61
+func (d *driver) Leave(nid, eid string) error {
62
+	return types.NotImplementedErrorf("not implemented")
63
+}
64
+
65
+func (d *driver) Type() string {
66
+	return networkType
67
+}
68
+
69
+func (d *driver) IsBuiltIn() bool {
70
+	return true
71
+}
72
+
73
+func (d *driver) DiscoverNew(dType discoverapi.DiscoveryType, data interface{}) error {
74
+	return types.NotImplementedErrorf("not implemented")
75
+}
76
+
77
+func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{}) error {
78
+	return types.NotImplementedErrorf("not implemented")
79
+}
80
+
81
+func (d *driver) ProgramExternalConnectivity(nid, eid string, options map[string]interface{}) error {
82
+	return types.NotImplementedErrorf("not implemented")
83
+}
84
+
85
+func (d *driver) RevokeExternalConnectivity(nid, eid string) error {
86
+	return types.NotImplementedErrorf("not implemented")
87
+}
... ...
@@ -56,7 +56,8 @@ type driver struct {
56 56
 // Init registers a new instance of overlay driver
57 57
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
58 58
 	c := driverapi.Capability{
59
-		DataScope: datastore.GlobalScope,
59
+		DataScope:         datastore.GlobalScope,
60
+		ConnectivityScope: datastore.GlobalScope,
60 61
 	}
61 62
 	d := &driver{
62 63
 		networks: networkTable{},
... ...
@@ -49,7 +49,8 @@ type network struct {
49 49
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
50 50
 	var err error
51 51
 	c := driverapi.Capability{
52
-		DataScope: datastore.GlobalScope,
52
+		DataScope:         datastore.GlobalScope,
53
+		ConnectivityScope: datastore.GlobalScope,
53 54
 	}
54 55
 
55 56
 	d := &driver{
... ...
@@ -24,7 +24,8 @@ func (r *Response) GetError() string {
24 24
 // GetCapabilityResponse is the response of GetCapability request
25 25
 type GetCapabilityResponse struct {
26 26
 	Response
27
-	Scope string
27
+	Scope             string
28
+	ConnectivityScope string
28 29
 }
29 30
 
30 31
 // AllocateNetworkRequest requests allocation of new network by manager
... ...
@@ -74,6 +74,17 @@ func (d *driver) getCapabilities() (*driverapi.Capability, error) {
74 74
 		return nil, fmt.Errorf("invalid capability: expecting 'local' or 'global', got %s", capResp.Scope)
75 75
 	}
76 76
 
77
+	switch capResp.ConnectivityScope {
78
+	case "global":
79
+		c.ConnectivityScope = datastore.GlobalScope
80
+	case "local":
81
+		c.ConnectivityScope = datastore.LocalScope
82
+	case "":
83
+		c.ConnectivityScope = c.DataScope
84
+	default:
85
+		return nil, fmt.Errorf("invalid capability: expecting 'local' or 'global', got %s", capResp.Scope)
86
+	}
87
+
77 88
 	return c, nil
78 89
 }
79 90
 
... ...
@@ -159,7 +159,8 @@ func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
159 159
 	}
160 160
 
161 161
 	c := driverapi.Capability{
162
-		DataScope: datastore.LocalScope,
162
+		DataScope:         datastore.LocalScope,
163
+		ConnectivityScope: datastore.LocalScope,
163 164
 	}
164 165
 	return dc.RegisterDriver(networkType, d, c)
165 166
 }
... ...
@@ -57,7 +57,8 @@ type driver struct {
57 57
 // Init registers a new instance of overlay driver
58 58
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
59 59
 	c := driverapi.Capability{
60
-		DataScope: datastore.GlobalScope,
60
+		DataScope:         datastore.GlobalScope,
61
+		ConnectivityScope: datastore.GlobalScope,
61 62
 	}
62 63
 	d := &driver{
63 64
 		networks: networkTable{},
... ...
@@ -36,7 +36,8 @@ type driver struct {
36 36
 // Init registers a new instance of overlay driver
37 37
 func Init(dc driverapi.DriverCallback, config map[string]interface{}) error {
38 38
 	c := driverapi.Capability{
39
-		DataScope: datastore.GlobalScope,
39
+		DataScope:         datastore.GlobalScope,
40
+		ConnectivityScope: datastore.GlobalScope,
40 41
 	}
41 42
 
42 43
 	d := &driver{
... ...
@@ -41,6 +41,8 @@ type networkConfiguration struct {
41 41
 	DNSSuffix          string
42 42
 	SourceMac          string
43 43
 	NetworkAdapterName string
44
+	dbIndex            uint64
45
+	dbExists           bool
44 46
 }
45 47
 
46 48
 // endpointConfiguration represents the user specified configuration for the sandbox endpoint
... ...
@@ -59,17 +61,22 @@ type endpointConnectivity struct {
59 59
 
60 60
 type hnsEndpoint struct {
61 61
 	id             string
62
+	nid            string
62 63
 	profileID      string
64
+	Type           string
63 65
 	macAddress     net.HardwareAddr
64 66
 	epOption       *endpointOption       // User specified parameters
65 67
 	epConnectivity *endpointConnectivity // User specified parameters
66 68
 	portMapping    []types.PortBinding   // Operation port bindings
67 69
 	addr           *net.IPNet
68 70
 	gateway        net.IP
71
+	dbIndex        uint64
72
+	dbExists       bool
69 73
 }
70 74
 
71 75
 type hnsNetwork struct {
72 76
 	id        string
77
+	created   bool
73 78
 	config    *networkConfiguration
74 79
 	endpoints map[string]*hnsEndpoint // key: endpoint id
75 80
 	driver    *driver                 // The network's driver
... ...
@@ -79,9 +86,14 @@ type hnsNetwork struct {
79 79
 type driver struct {
80 80
 	name     string
81 81
 	networks map[string]*hnsNetwork
82
+	store    datastore.DataStore
82 83
 	sync.Mutex
83 84
 }
84 85
 
86
+const (
87
+	errNotFound = "HNS failed with error : The object identifier does not represent a valid object. "
88
+)
89
+
85 90
 // IsBuiltinWindowsDriver vaidates if network-type is a builtin local-scoped driver
86 91
 func IsBuiltinLocalDriver(networkType string) bool {
87 92
 	if "l2bridge" == networkType || "l2tunnel" == networkType || "nat" == networkType || "ics" == networkType || "transparent" == networkType {
... ...
@@ -103,8 +115,16 @@ func GetInit(networkType string) func(dc driverapi.DriverCallback, config map[st
103 103
 			return types.BadRequestErrorf("Network type not supported: %s", networkType)
104 104
 		}
105 105
 
106
-		return dc.RegisterDriver(networkType, newDriver(networkType), driverapi.Capability{
107
-			DataScope: datastore.LocalScope,
106
+		d := newDriver(networkType)
107
+
108
+		err := d.initStore(config)
109
+		if err != nil {
110
+			return err
111
+		}
112
+
113
+		return dc.RegisterDriver(networkType, d, driverapi.Capability{
114
+			DataScope:         datastore.LocalScope,
115
+			ConnectivityScope: datastore.LocalScope,
108 116
 		})
109 117
 	}
110 118
 }
... ...
@@ -132,7 +152,7 @@ func (n *hnsNetwork) getEndpoint(eid string) (*hnsEndpoint, error) {
132 132
 }
133 133
 
134 134
 func (d *driver) parseNetworkOptions(id string, genericOptions map[string]string) (*networkConfiguration, error) {
135
-	config := &networkConfiguration{}
135
+	config := &networkConfiguration{Type: d.name}
136 136
 
137 137
 	for label, value := range genericOptions {
138 138
 		switch label {
... ...
@@ -187,6 +207,21 @@ func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (s
187 187
 	return "", nil
188 188
 }
189 189
 
190
+func (d *driver) createNetwork(config *networkConfiguration) error {
191
+	network := &hnsNetwork{
192
+		id:        config.ID,
193
+		endpoints: make(map[string]*hnsEndpoint),
194
+		config:    config,
195
+		driver:    d,
196
+	}
197
+
198
+	d.Lock()
199
+	d.networks[config.ID] = network
200
+	d.Unlock()
201
+
202
+	return nil
203
+}
204
+
190 205
 // Create a new network
191 206
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
192 207
 	if _, err := d.getNetwork(id); err == nil {
... ...
@@ -209,16 +244,11 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
209 209
 		return err
210 210
 	}
211 211
 
212
-	network := &hnsNetwork{
213
-		id:        config.ID,
214
-		endpoints: make(map[string]*hnsEndpoint),
215
-		config:    config,
216
-		driver:    d,
217
-	}
212
+	err = d.createNetwork(config)
218 213
 
219
-	d.Lock()
220
-	d.networks[config.ID] = network
221
-	d.Unlock()
214
+	if err != nil {
215
+		return err
216
+	}
222 217
 
223 218
 	// A non blank hnsid indicates that the network was discovered
224 219
 	// from HNS. No need to call HNS if this network was discovered
... ...
@@ -293,7 +323,9 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
293 293
 		genData[HNSID] = config.HnsID
294 294
 	}
295 295
 
296
-	return nil
296
+	n, err := d.getNetwork(id)
297
+	n.created = true
298
+	return d.storeUpdate(config)
297 299
 }
298 300
 
299 301
 func (d *driver) DeleteNetwork(nid string) error {
... ...
@@ -306,21 +338,25 @@ func (d *driver) DeleteNetwork(nid string) error {
306 306
 	config := n.config
307 307
 	n.Unlock()
308 308
 
309
-	// Cannot remove network if endpoints are still present
310
-	if len(n.endpoints) != 0 {
311
-		return fmt.Errorf("network %s has active endpoint", n.id)
312
-	}
313
-
314
-	_, err = hcsshim.HNSNetworkRequest("DELETE", config.HnsID, "")
315
-	if err != nil {
316
-		return types.ForbiddenErrorf(err.Error())
309
+	if n.created {
310
+		_, err = hcsshim.HNSNetworkRequest("DELETE", config.HnsID, "")
311
+		if err != nil && err.Error() != errNotFound {
312
+			return types.ForbiddenErrorf(err.Error())
313
+		}
317 314
 	}
318 315
 
319 316
 	d.Lock()
320 317
 	delete(d.networks, nid)
321 318
 	d.Unlock()
322 319
 
323
-	return nil
320
+	// delele endpoints belong to this network
321
+	for _, ep := range n.endpoints {
322
+		if err := d.storeDelete(ep); err != nil {
323
+			logrus.Warnf("Failed to remove bridge endpoint %s from store: %v", ep.id[0:7], err)
324
+		}
325
+	}
326
+
327
+	return d.storeDelete(config)
324 328
 }
325 329
 
326 330
 func convertQosPolicies(qosPolicies []types.QosPolicy) ([]json.RawMessage, error) {
... ...
@@ -543,6 +579,8 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
543 543
 	// TODO For now the ip mask is not in the info generated by HNS
544 544
 	endpoint := &hnsEndpoint{
545 545
 		id:         eid,
546
+		nid:        n.id,
547
+		Type:       d.name,
546 548
 		addr:       &net.IPNet{IP: hnsresponse.IPAddress, Mask: hnsresponse.IPAddress.DefaultMask()},
547 549
 		macAddress: mac,
548 550
 	}
... ...
@@ -573,6 +611,10 @@ func (d *driver) CreateEndpoint(nid, eid string, ifInfo driverapi.InterfaceInfo,
573 573
 		ifInfo.SetMacAddress(endpoint.macAddress)
574 574
 	}
575 575
 
576
+	if err = d.storeUpdate(endpoint); err != nil {
577
+		return fmt.Errorf("failed to save endpoint %s to store: %v", endpoint.id[0:7], err)
578
+	}
579
+
576 580
 	return nil
577 581
 }
578 582
 
... ...
@@ -592,10 +634,13 @@ func (d *driver) DeleteEndpoint(nid, eid string) error {
592 592
 	n.Unlock()
593 593
 
594 594
 	_, err = hcsshim.HNSEndpointRequest("DELETE", ep.profileID, "")
595
-	if err != nil {
595
+	if err != nil && err.Error() != errNotFound {
596 596
 		return err
597 597
 	}
598 598
 
599
+	if err := d.storeDelete(ep); err != nil {
600
+		logrus.Warnf("Failed to remove bridge endpoint %s from store: %v", ep.id[0:7], err)
601
+	}
599 602
 	return nil
600 603
 }
601 604
 
602 605
new file mode 100644
... ...
@@ -0,0 +1,335 @@
0
+// +build windows
1
+
2
+package windows
3
+
4
+import (
5
+	"encoding/json"
6
+	"fmt"
7
+	"net"
8
+
9
+	"github.com/Sirupsen/logrus"
10
+	"github.com/docker/libnetwork/datastore"
11
+	"github.com/docker/libnetwork/discoverapi"
12
+	"github.com/docker/libnetwork/netlabel"
13
+	"github.com/docker/libnetwork/types"
14
+)
15
+
16
+const (
17
+	windowsPrefix         = "windows"
18
+	windowsEndpointPrefix = "windows-endpoint"
19
+)
20
+
21
+func (d *driver) initStore(option map[string]interface{}) error {
22
+	if data, ok := option[netlabel.LocalKVClient]; ok {
23
+		var err error
24
+		dsc, ok := data.(discoverapi.DatastoreConfigData)
25
+		if !ok {
26
+			return types.InternalErrorf("incorrect data in datastore configuration: %v", data)
27
+		}
28
+		d.store, err = datastore.NewDataStoreFromConfig(dsc)
29
+		if err != nil {
30
+			return types.InternalErrorf("windows driver failed to initialize data store: %v", err)
31
+		}
32
+
33
+		err = d.populateNetworks()
34
+		if err != nil {
35
+			return err
36
+		}
37
+
38
+		err = d.populateEndpoints()
39
+		if err != nil {
40
+			return err
41
+		}
42
+	}
43
+
44
+	return nil
45
+}
46
+
47
+func (d *driver) populateNetworks() error {
48
+	kvol, err := d.store.List(datastore.Key(windowsPrefix), &networkConfiguration{Type: d.name})
49
+	if err != nil && err != datastore.ErrKeyNotFound {
50
+		return fmt.Errorf("failed to get windows network configurations from store: %v", err)
51
+	}
52
+
53
+	// It's normal for network configuration state to be empty. Just return.
54
+	if err == datastore.ErrKeyNotFound {
55
+		return nil
56
+	}
57
+
58
+	for _, kvo := range kvol {
59
+		ncfg := kvo.(*networkConfiguration)
60
+		if ncfg.Type != d.name {
61
+			continue
62
+		}
63
+		if err = d.createNetwork(ncfg); err != nil {
64
+			logrus.Warnf("could not create windows network for id %s hnsid %s while booting up from persistent state: %v", ncfg.ID, ncfg.HnsID, err)
65
+		}
66
+		logrus.Debugf("Network (%s) restored", ncfg.ID[0:7])
67
+	}
68
+
69
+	return nil
70
+}
71
+
72
+func (d *driver) populateEndpoints() error {
73
+	kvol, err := d.store.List(datastore.Key(windowsEndpointPrefix), &hnsEndpoint{Type: d.name})
74
+	if err != nil && err != datastore.ErrKeyNotFound {
75
+		return fmt.Errorf("failed to get endpoints from store: %v", err)
76
+	}
77
+
78
+	if err == datastore.ErrKeyNotFound {
79
+		return nil
80
+	}
81
+
82
+	for _, kvo := range kvol {
83
+		ep := kvo.(*hnsEndpoint)
84
+		if ep.Type != d.name {
85
+			continue
86
+		}
87
+		n, ok := d.networks[ep.nid]
88
+		if !ok {
89
+			logrus.Debugf("Network (%s) not found for restored endpoint (%s)", ep.nid[0:7], ep.id[0:7])
90
+			logrus.Debugf("Deleting stale endpoint (%s) from store", ep.id[0:7])
91
+			if err := d.storeDelete(ep); err != nil {
92
+				logrus.Debugf("Failed to delete stale endpoint (%s) from store", ep.id[0:7])
93
+			}
94
+			continue
95
+		}
96
+		n.endpoints[ep.id] = ep
97
+		logrus.Debugf("Endpoint (%s) restored to network (%s)", ep.id[0:7], ep.nid[0:7])
98
+	}
99
+
100
+	return nil
101
+}
102
+
103
+func (d *driver) storeUpdate(kvObject datastore.KVObject) error {
104
+	if d.store == nil {
105
+		logrus.Warnf("store not initialized. kv object %s is not added to the store", datastore.Key(kvObject.Key()...))
106
+		return nil
107
+	}
108
+
109
+	if err := d.store.PutObjectAtomic(kvObject); err != nil {
110
+		return fmt.Errorf("failed to update store for object type %T: %v", kvObject, err)
111
+	}
112
+
113
+	return nil
114
+}
115
+
116
+func (d *driver) storeDelete(kvObject datastore.KVObject) error {
117
+	if d.store == nil {
118
+		logrus.Debugf("store not initialized. kv object %s is not deleted from store", datastore.Key(kvObject.Key()...))
119
+		return nil
120
+	}
121
+
122
+retry:
123
+	if err := d.store.DeleteObjectAtomic(kvObject); err != nil {
124
+		if err == datastore.ErrKeyModified {
125
+			if err := d.store.GetObject(datastore.Key(kvObject.Key()...), kvObject); err != nil {
126
+				return fmt.Errorf("could not update the kvobject to latest when trying to delete: %v", err)
127
+			}
128
+			goto retry
129
+		}
130
+		return err
131
+	}
132
+
133
+	return nil
134
+}
135
+
136
+func (ncfg *networkConfiguration) MarshalJSON() ([]byte, error) {
137
+	nMap := make(map[string]interface{})
138
+
139
+	nMap["ID"] = ncfg.ID
140
+	nMap["Type"] = ncfg.Type
141
+	nMap["Name"] = ncfg.Name
142
+	nMap["HnsID"] = ncfg.HnsID
143
+	nMap["VLAN"] = ncfg.VLAN
144
+	nMap["VSID"] = ncfg.VSID
145
+	nMap["DNSServers"] = ncfg.DNSServers
146
+	nMap["DNSSuffix"] = ncfg.DNSSuffix
147
+	nMap["SourceMac"] = ncfg.SourceMac
148
+	nMap["NetworkAdapterName"] = ncfg.NetworkAdapterName
149
+
150
+	return json.Marshal(nMap)
151
+}
152
+
153
+func (ncfg *networkConfiguration) UnmarshalJSON(b []byte) error {
154
+	var (
155
+		err  error
156
+		nMap map[string]interface{}
157
+	)
158
+
159
+	if err = json.Unmarshal(b, &nMap); err != nil {
160
+		return err
161
+	}
162
+
163
+	ncfg.ID = nMap["ID"].(string)
164
+	ncfg.Type = nMap["Type"].(string)
165
+	ncfg.Name = nMap["Name"].(string)
166
+	ncfg.HnsID = nMap["HnsID"].(string)
167
+	ncfg.VLAN = uint(nMap["VLAN"].(float64))
168
+	ncfg.VSID = uint(nMap["VSID"].(float64))
169
+	ncfg.DNSServers = nMap["DNSServers"].(string)
170
+	ncfg.DNSSuffix = nMap["DNSSuffix"].(string)
171
+	ncfg.SourceMac = nMap["SourceMac"].(string)
172
+	ncfg.NetworkAdapterName = nMap["NetworkAdapterName"].(string)
173
+	return nil
174
+}
175
+
176
+func (ncfg *networkConfiguration) Key() []string {
177
+	return []string{windowsPrefix + ncfg.Type, ncfg.ID}
178
+}
179
+
180
+func (ncfg *networkConfiguration) KeyPrefix() []string {
181
+	return []string{windowsPrefix + ncfg.Type}
182
+}
183
+
184
+func (ncfg *networkConfiguration) Value() []byte {
185
+	b, err := json.Marshal(ncfg)
186
+	if err != nil {
187
+		return nil
188
+	}
189
+	return b
190
+}
191
+
192
+func (ncfg *networkConfiguration) SetValue(value []byte) error {
193
+	return json.Unmarshal(value, ncfg)
194
+}
195
+
196
+func (ncfg *networkConfiguration) Index() uint64 {
197
+	return ncfg.dbIndex
198
+}
199
+
200
+func (ncfg *networkConfiguration) SetIndex(index uint64) {
201
+	ncfg.dbIndex = index
202
+	ncfg.dbExists = true
203
+}
204
+
205
+func (ncfg *networkConfiguration) Exists() bool {
206
+	return ncfg.dbExists
207
+}
208
+
209
+func (ncfg *networkConfiguration) Skip() bool {
210
+	return false
211
+}
212
+
213
+func (ncfg *networkConfiguration) New() datastore.KVObject {
214
+	return &networkConfiguration{Type: ncfg.Type}
215
+}
216
+
217
+func (ncfg *networkConfiguration) CopyTo(o datastore.KVObject) error {
218
+	dstNcfg := o.(*networkConfiguration)
219
+	*dstNcfg = *ncfg
220
+	return nil
221
+}
222
+
223
+func (ncfg *networkConfiguration) DataScope() string {
224
+	return datastore.LocalScope
225
+}
226
+
227
+func (ep *hnsEndpoint) MarshalJSON() ([]byte, error) {
228
+	epMap := make(map[string]interface{})
229
+	epMap["id"] = ep.id
230
+	epMap["nid"] = ep.nid
231
+	epMap["Type"] = ep.Type
232
+	epMap["profileID"] = ep.profileID
233
+	epMap["MacAddress"] = ep.macAddress.String()
234
+	epMap["Addr"] = ep.addr.String()
235
+	epMap["gateway"] = ep.gateway.String()
236
+
237
+	epMap["epOption"] = ep.epOption
238
+	epMap["epConnectivity"] = ep.epConnectivity
239
+	epMap["PortMapping"] = ep.portMapping
240
+
241
+	return json.Marshal(epMap)
242
+}
243
+
244
+func (ep *hnsEndpoint) UnmarshalJSON(b []byte) error {
245
+	var (
246
+		err   error
247
+		epMap map[string]interface{}
248
+	)
249
+
250
+	if err = json.Unmarshal(b, &epMap); err != nil {
251
+		return fmt.Errorf("Failed to unmarshal to endpoint: %v", err)
252
+	}
253
+
254
+	if v, ok := epMap["MacAddress"]; ok {
255
+		if ep.macAddress, err = net.ParseMAC(v.(string)); err != nil {
256
+			return types.InternalErrorf("failed to decode endpoint MAC address (%s) after json unmarshal: %v", v.(string), err)
257
+		}
258
+	}
259
+	if v, ok := epMap["Addr"]; ok {
260
+		if ep.addr, err = types.ParseCIDR(v.(string)); err != nil {
261
+			return types.InternalErrorf("failed to decode endpoint IPv4 address (%s) after json unmarshal: %v", v.(string), err)
262
+		}
263
+	}
264
+
265
+	ep.id = epMap["id"].(string)
266
+	ep.Type = epMap["Type"].(string)
267
+	ep.nid = epMap["nid"].(string)
268
+	ep.profileID = epMap["profileID"].(string)
269
+	d, _ := json.Marshal(epMap["epOption"])
270
+	if err := json.Unmarshal(d, &ep.epOption); err != nil {
271
+		logrus.Warnf("Failed to decode endpoint container config %v", err)
272
+	}
273
+	d, _ = json.Marshal(epMap["epConnectivity"])
274
+	if err := json.Unmarshal(d, &ep.epConnectivity); err != nil {
275
+		logrus.Warnf("Failed to decode endpoint external connectivity configuration %v", err)
276
+	}
277
+	d, _ = json.Marshal(epMap["PortMapping"])
278
+	if err := json.Unmarshal(d, &ep.portMapping); err != nil {
279
+		logrus.Warnf("Failed to decode endpoint port mapping %v", err)
280
+	}
281
+
282
+	return nil
283
+}
284
+
285
+func (ep *hnsEndpoint) Key() []string {
286
+	return []string{windowsEndpointPrefix + ep.Type, ep.id}
287
+}
288
+
289
+func (ep *hnsEndpoint) KeyPrefix() []string {
290
+	return []string{windowsEndpointPrefix + ep.Type}
291
+}
292
+
293
+func (ep *hnsEndpoint) Value() []byte {
294
+	b, err := json.Marshal(ep)
295
+	if err != nil {
296
+		return nil
297
+	}
298
+	return b
299
+}
300
+
301
+func (ep *hnsEndpoint) SetValue(value []byte) error {
302
+	return json.Unmarshal(value, ep)
303
+}
304
+
305
+func (ep *hnsEndpoint) Index() uint64 {
306
+	return ep.dbIndex
307
+}
308
+
309
+func (ep *hnsEndpoint) SetIndex(index uint64) {
310
+	ep.dbIndex = index
311
+	ep.dbExists = true
312
+}
313
+
314
+func (ep *hnsEndpoint) Exists() bool {
315
+	return ep.dbExists
316
+}
317
+
318
+func (ep *hnsEndpoint) Skip() bool {
319
+	return false
320
+}
321
+
322
+func (ep *hnsEndpoint) New() datastore.KVObject {
323
+	return &hnsEndpoint{Type: ep.Type}
324
+}
325
+
326
+func (ep *hnsEndpoint) CopyTo(o datastore.KVObject) error {
327
+	dstEp := o.(*hnsEndpoint)
328
+	*dstEp = *ep
329
+	return nil
330
+}
331
+
332
+func (ep *hnsEndpoint) DataScope() string {
333
+	return datastore.LocalScope
334
+}
... ...
@@ -1152,6 +1152,9 @@ func (c *controller) cleanupLocalEndpoints() {
1152 1152
 	}
1153 1153
 
1154 1154
 	for _, n := range nl {
1155
+		if n.ConfigOnly() {
1156
+			continue
1157
+		}
1155 1158
 		epl, err := n.getEndpointsFromStore()
1156 1159
 		if err != nil {
1157 1160
 			logrus.Warnf("Could not get list of endpoints in network %s during endpoint cleanup: %v", n.name, err)
1158 1161
new file mode 100644
... ...
@@ -0,0 +1,29 @@
0
+package libnetwork
1
+
2
+import (
3
+	"github.com/Sirupsen/logrus"
4
+	"github.com/docker/libnetwork/iptables"
5
+)
6
+
7
+const userChain = "DOCKER-USER"
8
+
9
+// This chain allow users to configure firewall policies in a way that persists
10
+// docker operations/restarts. Docker will not delete or modify any pre-existing
11
+// rules from the DOCKER-USER filter chain.
12
+func arrangeUserFilterRule() {
13
+	_, err := iptables.NewChain(userChain, iptables.Filter, false)
14
+	if err != nil {
15
+		logrus.Warnf("Failed to create %s chain: %v", userChain, err)
16
+		return
17
+	}
18
+
19
+	if err = iptables.AddReturnRule(userChain); err != nil {
20
+		logrus.Warnf("Failed to add the RETURN rule for %s: %v", userChain, err)
21
+		return
22
+	}
23
+
24
+	err = iptables.EnsureJumpRule("FORWARD", userChain)
25
+	if err != nil {
26
+		logrus.Warnf("Failed to ensure the jump rule for %s: %v", userChain, err)
27
+	}
28
+}
0 29
new file mode 100644
... ...
@@ -0,0 +1,6 @@
0
+// +build !linux
1
+
2
+package libnetwork
3
+
4
+func arrangeUserFilterRule() {
5
+}
... ...
@@ -498,3 +498,44 @@ func parseVersionNumbers(input string) (major, minor, micro int) {
498 498
 func supportsCOption(mj, mn, mc int) bool {
499 499
 	return mj > 1 || (mj == 1 && (mn > 4 || (mn == 4 && mc >= 11)))
500 500
 }
501
+
502
+// AddReturnRule adds a return rule for the chain in the filter table
503
+func AddReturnRule(chain string) error {
504
+	var (
505
+		table = Filter
506
+		args  = []string{"-j", "RETURN"}
507
+	)
508
+
509
+	if Exists(table, chain, args...) {
510
+		return nil
511
+	}
512
+
513
+	err := RawCombinedOutput(append([]string{"-A", chain}, args...)...)
514
+	if err != nil {
515
+		return fmt.Errorf("unable to add return rule in %s chain: %s", chain, err.Error())
516
+	}
517
+
518
+	return nil
519
+}
520
+
521
+// EnsureJumpRule ensures the jump rule is on top
522
+func EnsureJumpRule(fromChain, toChain string) error {
523
+	var (
524
+		table = Filter
525
+		args  = []string{"-j", toChain}
526
+	)
527
+
528
+	if Exists(table, fromChain, args...) {
529
+		err := RawCombinedOutput(append([]string{"-D", fromChain}, args...)...)
530
+		if err != nil {
531
+			return fmt.Errorf("unable to remove jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
532
+		}
533
+	}
534
+
535
+	err := RawCombinedOutput(append([]string{"-I", fromChain}, args...)...)
536
+	if err != nil {
537
+		return fmt.Errorf("unable to insert jump to %s rule in %s chain: %s", toChain, fromChain, err.Error())
538
+	}
539
+
540
+	return nil
541
+}
... ...
@@ -67,6 +67,8 @@ type NetworkInfo interface {
67 67
 	Internal() bool
68 68
 	Attachable() bool
69 69
 	Ingress() bool
70
+	ConfigFrom() string
71
+	ConfigOnly() bool
70 72
 	Labels() map[string]string
71 73
 	Dynamic() bool
72 74
 	Created() time.Time
... ...
@@ -193,7 +195,7 @@ type network struct {
193 193
 	networkType  string
194 194
 	id           string
195 195
 	created      time.Time
196
-	scope        string
196
+	scope        string // network data scope
197 197
 	labels       map[string]string
198 198
 	ipamType     string
199 199
 	ipamOptions  map[string]string
... ...
@@ -219,6 +221,8 @@ type network struct {
219 219
 	ingress      bool
220 220
 	driverTables []networkDBTable
221 221
 	dynamic      bool
222
+	configOnly   bool
223
+	configFrom   string
222 224
 	sync.Mutex
223 225
 }
224 226
 
... ...
@@ -348,6 +352,95 @@ func (i *IpamInfo) CopyTo(dstI *IpamInfo) error {
348 348
 	return nil
349 349
 }
350 350
 
351
+func (n *network) validateConfiguration() error {
352
+	if n.configOnly {
353
+		// Only supports network specific configurations.
354
+		// Network operator configurations are not supported.
355
+		if n.ingress || n.internal || n.attachable {
356
+			return types.ForbiddenErrorf("configuration network can only contain network " +
357
+				"specific fields. Network operator fields like " +
358
+				"[ ingress | internal | attachable ] are not supported.")
359
+		}
360
+	}
361
+	if n.configFrom != "" {
362
+		if n.configOnly {
363
+			return types.ForbiddenErrorf("a configuration network cannot depend on another configuration network")
364
+		}
365
+		if n.ipamType != "" &&
366
+			n.ipamType != defaultIpamForNetworkType(n.networkType) ||
367
+			n.enableIPv6 ||
368
+			len(n.labels) > 0 || len(n.ipamOptions) > 0 ||
369
+			len(n.ipamV4Config) > 0 || len(n.ipamV6Config) > 0 {
370
+			return types.ForbiddenErrorf("user specified configurations are not supported if the network depends on a configuration network")
371
+		}
372
+		if len(n.generic) > 0 {
373
+			if data, ok := n.generic[netlabel.GenericData]; ok {
374
+				var (
375
+					driverOptions map[string]string
376
+					opts          interface{}
377
+				)
378
+				switch data.(type) {
379
+				case map[string]interface{}:
380
+					opts = data.(map[string]interface{})
381
+				case map[string]string:
382
+					opts = data.(map[string]string)
383
+				}
384
+				ba, err := json.Marshal(opts)
385
+				if err != nil {
386
+					return fmt.Errorf("failed to validate network configuration: %v", err)
387
+				}
388
+				if err := json.Unmarshal(ba, &driverOptions); err != nil {
389
+					return fmt.Errorf("failed to validate network configuration: %v", err)
390
+				}
391
+				if len(driverOptions) > 0 {
392
+					return types.ForbiddenErrorf("network driver options are not supported if the network depends on a configuration network")
393
+				}
394
+			}
395
+		}
396
+	}
397
+	return nil
398
+}
399
+
400
+// Applies network specific configurations
401
+func (n *network) applyConfigurationTo(to *network) error {
402
+	to.enableIPv6 = n.enableIPv6
403
+	if len(n.labels) > 0 {
404
+		to.labels = make(map[string]string, len(n.labels))
405
+		for k, v := range n.labels {
406
+			if _, ok := to.labels[k]; !ok {
407
+				to.labels[k] = v
408
+			}
409
+		}
410
+	}
411
+	if len(n.ipamOptions) > 0 {
412
+		to.ipamOptions = make(map[string]string, len(n.ipamOptions))
413
+		for k, v := range n.ipamOptions {
414
+			if _, ok := to.ipamOptions[k]; !ok {
415
+				to.ipamOptions[k] = v
416
+			}
417
+		}
418
+	}
419
+	if len(n.ipamV4Config) > 0 {
420
+		to.ipamV4Config = make([]*IpamConf, 0, len(n.ipamV4Config))
421
+		for _, v4conf := range n.ipamV4Config {
422
+			to.ipamV4Config = append(to.ipamV4Config, v4conf)
423
+		}
424
+	}
425
+	if len(n.ipamV6Config) > 0 {
426
+		to.ipamV6Config = make([]*IpamConf, 0, len(n.ipamV6Config))
427
+		for _, v6conf := range n.ipamV6Config {
428
+			to.ipamV6Config = append(to.ipamV6Config, v6conf)
429
+		}
430
+	}
431
+	if len(n.generic) > 0 {
432
+		to.generic = options.Generic{}
433
+		for k, v := range n.generic {
434
+			to.generic[k] = v
435
+		}
436
+	}
437
+	return nil
438
+}
439
+
351 440
 func (n *network) CopyTo(o datastore.KVObject) error {
352 441
 	n.Lock()
353 442
 	defer n.Unlock()
... ...
@@ -370,6 +463,8 @@ func (n *network) CopyTo(o datastore.KVObject) error {
370 370
 	dstN.attachable = n.attachable
371 371
 	dstN.inDelete = n.inDelete
372 372
 	dstN.ingress = n.ingress
373
+	dstN.configOnly = n.configOnly
374
+	dstN.configFrom = n.configFrom
373 375
 
374 376
 	// copy labels
375 377
 	if dstN.labels == nil {
... ...
@@ -419,7 +514,12 @@ func (n *network) CopyTo(o datastore.KVObject) error {
419 419
 }
420 420
 
421 421
 func (n *network) DataScope() string {
422
-	return n.Scope()
422
+	s := n.Scope()
423
+	// All swarm scope networks have local datascope
424
+	if s == datastore.SwarmScope {
425
+		s = datastore.LocalScope
426
+	}
427
+	return s
423 428
 }
424 429
 
425 430
 func (n *network) getEpCnt() *endpointCnt {
... ...
@@ -479,6 +579,8 @@ func (n *network) MarshalJSON() ([]byte, error) {
479 479
 	netMap["attachable"] = n.attachable
480 480
 	netMap["inDelete"] = n.inDelete
481 481
 	netMap["ingress"] = n.ingress
482
+	netMap["configOnly"] = n.configOnly
483
+	netMap["configFrom"] = n.configFrom
482 484
 	return json.Marshal(netMap)
483 485
 }
484 486
 
... ...
@@ -583,6 +685,12 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
583 583
 	if v, ok := netMap["ingress"]; ok {
584 584
 		n.ingress = v.(bool)
585 585
 	}
586
+	if v, ok := netMap["configOnly"]; ok {
587
+		n.configOnly = v.(bool)
588
+	}
589
+	if v, ok := netMap["configFrom"]; ok {
590
+		n.configFrom = v.(string)
591
+	}
586 592
 	// Reconcile old networks with the recently added `--ipv6` flag
587 593
 	if !n.enableIPv6 {
588 594
 		n.enableIPv6 = len(n.ipamV6Info) > 0
... ...
@@ -659,6 +767,14 @@ func NetworkOptionAttachable(attachable bool) NetworkOption {
659 659
 	}
660 660
 }
661 661
 
662
+// NetworkOptionScope returns an option setter to overwrite the network's scope.
663
+// By default the network's scope is set to the network driver's datascope.
664
+func NetworkOptionScope(scope string) NetworkOption {
665
+	return func(n *network) {
666
+		n.scope = scope
667
+	}
668
+}
669
+
662 670
 // NetworkOptionIpam function returns an option setter for the ipam configuration for this network
663 671
 func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ipV6 []*IpamConf, opts map[string]string) NetworkOption {
664 672
 	return func(n *network) {
... ...
@@ -713,6 +829,23 @@ func NetworkOptionDeferIPv6Alloc(enable bool) NetworkOption {
713 713
 	}
714 714
 }
715 715
 
716
+// NetworkOptionConfigOnly tells controller this network is
717
+// a configuration only network. It serves as a configuration
718
+// for other networks.
719
+func NetworkOptionConfigOnly() NetworkOption {
720
+	return func(n *network) {
721
+		n.configOnly = true
722
+	}
723
+}
724
+
725
+// NetworkOptionConfigFrom tells controller to pick the
726
+// network configuration from a configuration only network
727
+func NetworkOptionConfigFrom(name string) NetworkOption {
728
+	return func(n *network) {
729
+		n.configFrom = name
730
+	}
731
+}
732
+
716 733
 func (n *network) processOptions(options ...NetworkOption) {
717 734
 	for _, opt := range options {
718 735
 		if opt != nil {
... ...
@@ -757,6 +890,14 @@ func (n *network) driverScope() string {
757 757
 	return cap.DataScope
758 758
 }
759 759
 
760
+func (n *network) driverIsMultihost() bool {
761
+	_, cap, err := n.resolveDriver(n.networkType, true)
762
+	if err != nil {
763
+		return false
764
+	}
765
+	return cap.ConnectivityScope == datastore.GlobalScope
766
+}
767
+
760 768
 func (n *network) driver(load bool) (driverapi.Driver, error) {
761 769
 	d, cap, err := n.resolveDriver(n.networkType, load)
762 770
 	if err != nil {
... ...
@@ -767,14 +908,14 @@ func (n *network) driver(load bool) (driverapi.Driver, error) {
767 767
 	isAgent := c.isAgent()
768 768
 	n.Lock()
769 769
 	// If load is not required, driver, cap and err may all be nil
770
-	if cap != nil {
770
+	if n.scope == "" && cap != nil {
771 771
 		n.scope = cap.DataScope
772 772
 	}
773
-	if isAgent || n.dynamic {
774
-		// If we are running in agent mode then all networks
775
-		// in libnetwork are local scope regardless of the
776
-		// backing driver.
777
-		n.scope = datastore.LocalScope
773
+	if isAgent && n.dynamic {
774
+		// If we are running in agent mode and the network
775
+		// is dynamic, then the networks are swarm scoped
776
+		// regardless of the backing driver.
777
+		n.scope = datastore.SwarmScope
778 778
 	}
779 779
 	n.Unlock()
780 780
 	return d, nil
... ...
@@ -797,6 +938,9 @@ func (n *network) delete(force bool) error {
797 797
 	}
798 798
 
799 799
 	if !force && n.getEpCnt().EndpointCnt() != 0 {
800
+		if n.configOnly {
801
+			return types.ForbiddenErrorf("configuration network %q is in use", n.Name())
802
+		}
800 803
 		return &ActiveEndpointsError{name: n.name, id: n.id}
801 804
 	}
802 805
 
... ...
@@ -806,6 +950,21 @@ func (n *network) delete(force bool) error {
806 806
 		return fmt.Errorf("error marking network %s (%s) for deletion: %v", n.Name(), n.ID(), err)
807 807
 	}
808 808
 
809
+	if n.ConfigFrom() != "" {
810
+		if t, err := c.getConfigNetwork(n.ConfigFrom()); err == nil {
811
+			if err := t.getEpCnt().DecEndpointCnt(); err != nil {
812
+				logrus.Warnf("Failed to update reference count for configuration network %q on removal of network %q: %v",
813
+					t.Name(), n.Name(), err)
814
+			}
815
+		} else {
816
+			logrus.Warnf("Could not find configuration network %q during removal of network %q", n.configOnly, n.Name())
817
+		}
818
+	}
819
+
820
+	if n.configOnly {
821
+		goto removeFromStore
822
+	}
823
+
809 824
 	if err = n.deleteNetwork(); err != nil {
810 825
 		if !force {
811 826
 			return err
... ...
@@ -831,6 +990,7 @@ func (n *network) delete(force bool) error {
831 831
 
832 832
 	c.cleanupServiceBindings(n.ID())
833 833
 
834
+removeFromStore:
834 835
 	// deleteFromStore performs an atomic delete operation and the
835 836
 	// network.epCnt will help prevent any possible
836 837
 	// race between endpoint join and network delete
... ...
@@ -892,6 +1052,10 @@ func (n *network) CreateEndpoint(name string, options ...EndpointOption) (Endpoi
892 892
 		return nil, ErrInvalidName(name)
893 893
 	}
894 894
 
895
+	if n.ConfigOnly() {
896
+		return nil, types.ForbiddenErrorf("cannot create endpoint on configuration-only network")
897
+	}
898
+
895 899
 	if _, err = n.EndpointByName(name); err == nil {
896 900
 		return nil, types.ForbiddenErrorf("endpoint with name %s already exists in network %s", name, n.Name())
897 901
 	}
... ...
@@ -1611,6 +1775,20 @@ func (n *network) IPv6Enabled() bool {
1611 1611
 	return n.enableIPv6
1612 1612
 }
1613 1613
 
1614
+func (n *network) ConfigFrom() string {
1615
+	n.Lock()
1616
+	defer n.Unlock()
1617
+
1618
+	return n.configFrom
1619
+}
1620
+
1621
+func (n *network) ConfigOnly() bool {
1622
+	n.Lock()
1623
+	defer n.Unlock()
1624
+
1625
+	return n.configOnly
1626
+}
1627
+
1614 1628
 func (n *network) Labels() map[string]string {
1615 1629
 	n.Lock()
1616 1630
 	defer n.Unlock()
... ...
@@ -1778,3 +1956,24 @@ func (n *network) ExecFunc(f func()) error {
1778 1778
 func (n *network) NdotsSet() bool {
1779 1779
 	return false
1780 1780
 }
1781
+
1782
+// config-only network is looked up by name
1783
+func (c *controller) getConfigNetwork(name string) (*network, error) {
1784
+	var n Network
1785
+
1786
+	s := func(current Network) bool {
1787
+		if current.Info().ConfigOnly() && current.Name() == name {
1788
+			n = current
1789
+			return true
1790
+		}
1791
+		return false
1792
+	}
1793
+
1794
+	c.WalkNetworks(s)
1795
+
1796
+	if n == nil {
1797
+		return nil, types.NotFoundErrorf("configuration network %q not found", name)
1798
+	}
1799
+
1800
+	return n.(*network), nil
1801
+}
... ...
@@ -480,26 +480,31 @@ func (nDB *NetworkDB) bulkSyncTables() {
480 480
 
481 481
 func (nDB *NetworkDB) bulkSync(nodes []string, all bool) ([]string, error) {
482 482
 	if !all {
483
-		// If not all, then just pick one.
484
-		nodes = nDB.mRandomNodes(1, nodes)
483
+		// Get 2 random nodes. 2nd node will be tried if the bulk sync to
484
+		// 1st node fails.
485
+		nodes = nDB.mRandomNodes(2, nodes)
485 486
 	}
486 487
 
487 488
 	if len(nodes) == 0 {
488 489
 		return nil, nil
489 490
 	}
490 491
 
491
-	logrus.Debugf("%s: Initiating bulk sync with nodes %v", nDB.config.NodeName, nodes)
492 492
 	var err error
493 493
 	var networks []string
494 494
 	for _, node := range nodes {
495 495
 		if node == nDB.config.NodeName {
496 496
 			continue
497 497
 		}
498
-
498
+		logrus.Debugf("%s: Initiating bulk sync with node %v", nDB.config.NodeName, node)
499 499
 		networks = nDB.findCommonNetworks(node)
500 500
 		err = nDB.bulkSyncNode(networks, node, true)
501
+		// if its periodic bulksync stop after the first successful sync
502
+		if !all && err == nil {
503
+			break
504
+		}
501 505
 		if err != nil {
502
-			err = fmt.Errorf("bulk sync failed on node %s: %v", node, err)
506
+			err = fmt.Errorf("bulk sync to node %s failed: %v", node, err)
507
+			logrus.Warn(err.Error())
503 508
 		}
504 509
 	}
505 510
 
... ...
@@ -98,7 +98,9 @@ func (c *controller) getNetworkFromStore(nid string) (*network, error) {
98 98
 		}
99 99
 
100 100
 		n.epCnt = ec
101
-		n.scope = store.Scope()
101
+		if n.scope == "" {
102
+			n.scope = store.Scope()
103
+		}
102 104
 		return n, nil
103 105
 	}
104 106
 
... ...
@@ -132,7 +134,9 @@ func (c *controller) getNetworksForScope(scope string) ([]*network, error) {
132 132
 		}
133 133
 
134 134
 		n.epCnt = ec
135
-		n.scope = scope
135
+		if n.scope == "" {
136
+			n.scope = scope
137
+		}
136 138
 		nl = append(nl, n)
137 139
 	}
138 140
 
... ...
@@ -171,7 +175,9 @@ func (c *controller) getNetworksFromStore() ([]*network, error) {
171 171
 				ec.n = n
172 172
 				n.epCnt = ec
173 173
 			}
174
-			n.scope = store.Scope()
174
+			if n.scope == "" {
175
+				n.scope = store.Scope()
176
+			}
175 177
 			n.Unlock()
176 178
 			nl = append(nl, n)
177 179
 		}
... ...
@@ -350,17 +356,18 @@ func (c *controller) networkWatchLoop(nw *netWatch, ep *endpoint, ecCh <-chan da
350 350
 }
351 351
 
352 352
 func (c *controller) processEndpointCreate(nmap map[string]*netWatch, ep *endpoint) {
353
-	if !c.isDistributedControl() && ep.getNetwork().driverScope() == datastore.GlobalScope {
353
+	n := ep.getNetwork()
354
+	if !c.isDistributedControl() && n.Scope() == datastore.SwarmScope && n.driverIsMultihost() {
354 355
 		return
355 356
 	}
356 357
 
357 358
 	c.Lock()
358
-	nw, ok := nmap[ep.getNetwork().ID()]
359
+	nw, ok := nmap[n.ID()]
359 360
 	c.Unlock()
360 361
 
361 362
 	if ok {
362 363
 		// Update the svc db for the local endpoint join right away
363
-		ep.getNetwork().updateSvcRecord(ep, c.getLocalEps(nw), true)
364
+		n.updateSvcRecord(ep, c.getLocalEps(nw), true)
364 365
 
365 366
 		c.Lock()
366 367
 		nw.localEps[ep.ID()] = ep
... ...
@@ -381,15 +388,15 @@ func (c *controller) processEndpointCreate(nmap map[string]*netWatch, ep *endpoi
381 381
 	// Update the svc db for the local endpoint join right away
382 382
 	// Do this before adding this ep to localEps so that we don't
383 383
 	// try to update this ep's container's svc records
384
-	ep.getNetwork().updateSvcRecord(ep, c.getLocalEps(nw), true)
384
+	n.updateSvcRecord(ep, c.getLocalEps(nw), true)
385 385
 
386 386
 	c.Lock()
387 387
 	nw.localEps[ep.ID()] = ep
388
-	nmap[ep.getNetwork().ID()] = nw
388
+	nmap[n.ID()] = nw
389 389
 	nw.stopCh = make(chan struct{})
390 390
 	c.Unlock()
391 391
 
392
-	store := c.getStore(ep.getNetwork().DataScope())
392
+	store := c.getStore(n.DataScope())
393 393
 	if store == nil {
394 394
 		return
395 395
 	}
... ...
@@ -398,7 +405,7 @@ func (c *controller) processEndpointCreate(nmap map[string]*netWatch, ep *endpoi
398 398
 		return
399 399
 	}
400 400
 
401
-	ch, err := store.Watch(ep.getNetwork().getEpCnt(), nw.stopCh)
401
+	ch, err := store.Watch(n.getEpCnt(), nw.stopCh)
402 402
 	if err != nil {
403 403
 		logrus.Warnf("Error creating watch for network: %v", err)
404 404
 		return
... ...
@@ -408,12 +415,13 @@ func (c *controller) processEndpointCreate(nmap map[string]*netWatch, ep *endpoi
408 408
 }
409 409
 
410 410
 func (c *controller) processEndpointDelete(nmap map[string]*netWatch, ep *endpoint) {
411
-	if !c.isDistributedControl() && ep.getNetwork().driverScope() == datastore.GlobalScope {
411
+	n := ep.getNetwork()
412
+	if !c.isDistributedControl() && n.Scope() == datastore.SwarmScope && n.driverIsMultihost() {
412 413
 		return
413 414
 	}
414 415
 
415 416
 	c.Lock()
416
-	nw, ok := nmap[ep.getNetwork().ID()]
417
+	nw, ok := nmap[n.ID()]
417 418
 
418 419
 	if ok {
419 420
 		delete(nw.localEps, ep.ID())
... ...
@@ -422,7 +430,7 @@ func (c *controller) processEndpointDelete(nmap map[string]*netWatch, ep *endpoi
422 422
 		// Update the svc db about local endpoint leave right away
423 423
 		// Do this after we remove this ep from localEps so that we
424 424
 		// don't try to remove this svc record from this ep's container.
425
-		ep.getNetwork().updateSvcRecord(ep, c.getLocalEps(nw), false)
425
+		n.updateSvcRecord(ep, c.getLocalEps(nw), false)
426 426
 
427 427
 		c.Lock()
428 428
 		if len(nw.localEps) == 0 {
... ...
@@ -430,9 +438,9 @@ func (c *controller) processEndpointDelete(nmap map[string]*netWatch, ep *endpoi
430 430
 
431 431
 			// This is the last container going away for the network. Destroy
432 432
 			// this network's svc db entry
433
-			delete(c.svcRecords, ep.getNetwork().ID())
433
+			delete(c.svcRecords, n.ID())
434 434
 
435
-			delete(nmap, ep.getNetwork().ID())
435
+			delete(nmap, n.ID())
436 436
 		}
437 437
 	}
438 438
 	c.Unlock()
... ...
@@ -478,7 +486,7 @@ func (c *controller) networkCleanup() {
478 478
 }
479 479
 
480 480
 var populateSpecial NetworkWalker = func(nw Network) bool {
481
-	if n := nw.(*network); n.hasSpecialDriver() {
481
+	if n := nw.(*network); n.hasSpecialDriver() && !n.ConfigOnly() {
482 482
 		if err := n.getController().addNetwork(n); err != nil {
483 483
 			logrus.Warnf("Failed to populate network %q with driver %q", nw.Name(), nw.Type())
484 484
 		}