Bläddra i källkod

Merge pull request #31710 from sanimej/drillerrr

Add verbose flag to network inspect to show all services & tasks in swarm mode
Madhu Venugopal 8 år sedan
förälder
incheckning
cdf66ba715
37 ändrade filer med 697 tillägg och 93 borttagningar
  1. 39 5
      api/server/router/network/network_routes.go
  2. 5 0
      api/swagger.yaml
  3. 16 0
      api/types/network/network.go
  4. 14 13
      api/types/types.go
  5. 5 3
      cli/command/network/inspect.go
  6. 1 1
      cli/command/stack/deploy_composefile.go
  7. 1 1
      cli/command/system/inspect.go
  8. 2 2
      client/interface.go
  9. 14 5
      client/network_inspect.go
  10. 33 6
      client/network_inspect_test.go
  11. 1 0
      docs/api/version-history.md
  12. 96 1
      docs/reference/commandline/network_inspect.md
  13. 93 0
      man/src/network/inspect.md
  14. 1 1
      vendor.conf
  15. 114 7
      vendor/github.com/docker/libnetwork/agent.go
  16. 36 1
      vendor/github.com/docker/libnetwork/driverapi/driverapi.go
  17. 4 0
      vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go
  18. 0 6
      vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go
  19. 4 0
      vendor/github.com/docker/libnetwork/drivers/host/host.go
  20. 4 0
      vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan.go
  21. 4 0
      vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan.go
  22. 4 0
      vendor/github.com/docker/libnetwork/drivers/null/null.go
  23. 55 15
      vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go
  24. 17 0
      vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go
  25. 1 1
      vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go
  26. 4 0
      vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go
  27. 4 0
      vendor/github.com/docker/libnetwork/drivers/remote/driver.go
  28. 4 0
      vendor/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go
  29. 4 0
      vendor/github.com/docker/libnetwork/drivers/solaris/overlay/joinleave.go
  30. 1 1
      vendor/github.com/docker/libnetwork/drivers/solaris/overlay/ov_network.go
  31. 4 0
      vendor/github.com/docker/libnetwork/drivers/windows/overlay/joinleave_windows.go
  32. 1 1
      vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_network_windows.go
  33. 4 0
      vendor/github.com/docker/libnetwork/drivers/windows/windows.go
  34. 52 19
      vendor/github.com/docker/libnetwork/iptables/iptables.go
  35. 19 4
      vendor/github.com/docker/libnetwork/network.go
  36. 16 0
      vendor/github.com/docker/libnetwork/networkdb/networkdb.go
  37. 20 0
      vendor/github.com/docker/libnetwork/service_common.go

+ 39 - 5
api/server/router/network/network_routes.go

@@ -4,10 +4,12 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"fmt"
 	"fmt"
 	"net/http"
 	"net/http"
+	"strconv"
 	"strings"
 	"strings"
 
 
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 
 
+	"github.com/docker/docker/api/errors"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/server/httputils"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types/filters"
 	"github.com/docker/docker/api/types/filters"
@@ -65,7 +67,7 @@ SKIP:
 		// run across all the networks. Starting API version 1.27, this detailed
 		// run across all the networks. Starting API version 1.27, this detailed
 		// info is available for network specific GET API (equivalent to inspect)
 		// info is available for network specific GET API (equivalent to inspect)
 		if versions.LessThan(httputils.VersionFromContext(ctx), "1.27") {
 		if versions.LessThan(httputils.VersionFromContext(ctx), "1.27") {
-			nr = n.buildDetailedNetworkResources(nw)
+			nr = n.buildDetailedNetworkResources(nw, false)
 		} else {
 		} else {
 			nr = n.buildNetworkResource(nw)
 			nr = n.buildNetworkResource(nw)
 		}
 		}
@@ -85,6 +87,16 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 	}
 	}
 
 
 	term := vars["id"]
 	term := vars["id"]
+	var (
+		verbose bool
+		err     error
+	)
+	if v := r.URL.Query().Get("verbose"); v != "" {
+		if verbose, err = strconv.ParseBool(v); err != nil {
+			err = fmt.Errorf("invalid value for verbose: %s", v)
+			return errors.NewBadRequestError(err)
+		}
+	}
 
 
 	// In case multiple networks have duplicate names, return error.
 	// In case multiple networks have duplicate names, return error.
 	// TODO (yongtang): should we wrap with version here for backward compatibility?
 	// TODO (yongtang): should we wrap with version here for backward compatibility?
@@ -100,17 +112,17 @@ func (n *networkRouter) getNetwork(ctx context.Context, w http.ResponseWriter, r
 	nw := n.backend.GetNetworks()
 	nw := n.backend.GetNetworks()
 	for _, network := range nw {
 	for _, network := range nw {
 		if network.ID() == term {
 		if network.ID() == term {
-			return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network))
+			return httputils.WriteJSON(w, http.StatusOK, *n.buildDetailedNetworkResources(network, verbose))
 		}
 		}
 		if network.Name() == term {
 		if network.Name() == term {
 			// No need to check the ID collision here as we are still in
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
 			// local scope and the network ID is unique in this scope.
-			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network)
+			listByFullName[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
 		}
 		}
 		if strings.HasPrefix(network.ID(), term) {
 		if strings.HasPrefix(network.ID(), term) {
 			// No need to check the ID collision here as we are still in
 			// No need to check the ID collision here as we are still in
 			// local scope and the network ID is unique in this scope.
 			// local scope and the network ID is unique in this scope.
-			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network)
+			listByPartialID[network.ID()] = *n.buildDetailedNetworkResources(network, verbose)
 		}
 		}
 	}
 	}
 
 
@@ -294,7 +306,7 @@ func (n *networkRouter) buildNetworkResource(nw libnetwork.Network) *types.Netwo
 	return r
 	return r
 }
 }
 
 
-func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network) *types.NetworkResource {
+func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network, verbose bool) *types.NetworkResource {
 	if nw == nil {
 	if nw == nil {
 		return &types.NetworkResource{}
 		return &types.NetworkResource{}
 	}
 	}
@@ -315,6 +327,28 @@ func (n *networkRouter) buildDetailedNetworkResources(nw libnetwork.Network) *ty
 
 
 		r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei)
 		r.Containers[key] = buildEndpointResource(tmpID, e.Name(), ei)
 	}
 	}
+	if !verbose {
+		return r
+	}
+	services := nw.Info().Services()
+	r.Services = make(map[string]network.ServiceInfo)
+	for name, service := range services {
+		tasks := []network.Task{}
+		for _, t := range service.Tasks {
+			tasks = append(tasks, network.Task{
+				Name:       t.Name,
+				EndpointID: t.EndpointID,
+				EndpointIP: t.EndpointIP,
+				Info:       t.Info,
+			})
+		}
+		r.Services[name] = network.ServiceInfo{
+			VIP:          service.VIP,
+			Ports:        service.Ports,
+			Tasks:        tasks,
+			LocalLBIndex: service.LocalLBIndex,
+		}
+	}
 	return r
 	return r
 }
 }
 
 

+ 5 - 0
api/swagger.yaml

@@ -6271,6 +6271,11 @@ paths:
           description: "Network ID or name"
           description: "Network ID or name"
           required: true
           required: true
           type: "string"
           type: "string"
+        - name: "verbose"
+          in: "query"
+          description: "Detailed inspect output for troubleshooting"
+          type: "boolean"
+          default: false
       tags: ["Network"]
       tags: ["Network"]
 
 
     delete:
     delete:

+ 16 - 0
api/types/network/network.go

@@ -60,6 +60,22 @@ type EndpointSettings struct {
 	MacAddress          string
 	MacAddress          string
 }
 }
 
 
+// Task carries the information about one backend task
+type Task struct {
+	Name       string
+	EndpointID string
+	EndpointIP string
+	Info       map[string]string
+}
+
+// ServiceInfo represents service parameters with the list of service's tasks
+type ServiceInfo struct {
+	VIP          string
+	Ports        []string
+	LocalLBIndex int
+	Tasks        []Task
+}
+
 // Copy makes a deep copy of `EndpointSettings`
 // Copy makes a deep copy of `EndpointSettings`
 func (es *EndpointSettings) Copy() *EndpointSettings {
 func (es *EndpointSettings) Copy() *EndpointSettings {
 	epCopy := *es
 	epCopy := *es

+ 14 - 13
api/types/types.go

@@ -391,19 +391,20 @@ type MountPoint struct {
 
 
 // NetworkResource is the body of the "get network" http response message
 // NetworkResource is the body of the "get network" http response message
 type NetworkResource struct {
 type NetworkResource struct {
-	Name       string                      // Name is the requested name of the network
-	ID         string                      `json:"Id"` // ID uniquely identifies a network on a single machine
-	Created    time.Time                   // Created is the time the network created
-	Scope      string                      // Scope describes the level at which the network exists (e.g. `global` for cluster-wide or `local` for machine level)
-	Driver     string                      // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`)
-	EnableIPv6 bool                        // EnableIPv6 represents whether to enable IPv6
-	IPAM       network.IPAM                // IPAM is the network's IP Address Management
-	Internal   bool                        // Internal represents if the network is used internal only
-	Attachable bool                        // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode.
-	Containers map[string]EndpointResource // Containers contains endpoints belonging to the network
-	Options    map[string]string           // Options holds the network specific options to use for when creating the network
-	Labels     map[string]string           // Labels holds metadata specific to the network being created
-	Peers      []network.PeerInfo          `json:",omitempty"` // List of peer nodes for an overlay network
+	Name       string                         // Name is the requested name of the network
+	ID         string                         `json:"Id"` // ID uniquely identifies a network on a single machine
+	Created    time.Time                      // Created is the time the network created
+	Scope      string                         // Scope describes the level at which the network exists (e.g. `global` for cluster-wide or `local` for machine level)
+	Driver     string                         // Driver is the Driver name used to create the network (e.g. `bridge`, `overlay`)
+	EnableIPv6 bool                           // EnableIPv6 represents whether to enable IPv6
+	IPAM       network.IPAM                   // IPAM is the network's IP Address Management
+	Internal   bool                           // Internal represents if the network is used internal only
+	Attachable bool                           // Attachable represents if the global scope is manually attachable by regular containers from workers in swarm mode.
+	Containers map[string]EndpointResource    // Containers contains endpoints belonging to the network
+	Options    map[string]string              // Options holds the network specific options to use for when creating the network
+	Labels     map[string]string              // Labels holds metadata specific to the network being created
+	Peers      []network.PeerInfo             `json:",omitempty"` // List of peer nodes for an overlay network
+	Services   map[string]network.ServiceInfo `json:",omitempty"`
 }
 }
 
 
 // EndpointResource contains network resources allocated and used for a container in a network
 // EndpointResource contains network resources allocated and used for a container in a network

+ 5 - 3
cli/command/network/inspect.go

@@ -10,8 +10,9 @@ import (
 )
 )
 
 
 type inspectOptions struct {
 type inspectOptions struct {
-	format string
-	names  []string
+	format  string
+	names   []string
+	verbose bool
 }
 }
 
 
 func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
 func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
@@ -28,6 +29,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
 	}
 	}
 
 
 	cmd.Flags().StringVarP(&opts.format, "format", "f", "", "Format the output using the given Go template")
 	cmd.Flags().StringVarP(&opts.format, "format", "f", "", "Format the output using the given Go template")
+	cmd.Flags().BoolVarP(&opts.verbose, "verbose", "v", false, "Verbose output for diagnostics")
 
 
 	return cmd
 	return cmd
 }
 }
@@ -38,7 +40,7 @@ func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
 	ctx := context.Background()
 	ctx := context.Background()
 
 
 	getNetFunc := func(name string) (interface{}, []byte, error) {
 	getNetFunc := func(name string) (interface{}, []byte, error) {
-		return client.NetworkInspectWithRaw(ctx, name)
+		return client.NetworkInspectWithRaw(ctx, name, opts.verbose)
 	}
 	}
 
 
 	return inspect.Inspect(dockerCli.Out(), opts.names, opts.format, getNetFunc)
 	return inspect.Inspect(dockerCli.Out(), opts.names, opts.format, getNetFunc)

+ 1 - 1
cli/command/stack/deploy_composefile.go

@@ -140,7 +140,7 @@ func validateExternalNetworks(
 	client := dockerCli.Client()
 	client := dockerCli.Client()
 
 
 	for _, networkName := range externalNetworks {
 	for _, networkName := range externalNetworks {
-		network, err := client.NetworkInspect(ctx, networkName)
+		network, err := client.NetworkInspect(ctx, networkName, false)
 		if err != nil {
 		if err != nil {
 			if dockerclient.IsErrNetworkNotFound(err) {
 			if dockerclient.IsErrNetworkNotFound(err) {
 				return fmt.Errorf("network %q is declared as external, but could not be found. You need to create the network before the stack is deployed (with overlay driver)", networkName)
 				return fmt.Errorf("network %q is declared as external, but could not be found. You need to create the network before the stack is deployed (with overlay driver)", networkName)

+ 1 - 1
cli/command/system/inspect.go

@@ -67,7 +67,7 @@ func inspectImages(ctx context.Context, dockerCli *command.DockerCli) inspect.Ge
 
 
 func inspectNetwork(ctx context.Context, dockerCli *command.DockerCli) inspect.GetRefFunc {
 func inspectNetwork(ctx context.Context, dockerCli *command.DockerCli) inspect.GetRefFunc {
 	return func(ref string) (interface{}, []byte, error) {
 	return func(ref string) (interface{}, []byte, error) {
-		return dockerCli.Client().NetworkInspectWithRaw(ctx, ref)
+		return dockerCli.Client().NetworkInspectWithRaw(ctx, ref, false)
 	}
 	}
 }
 }
 
 

+ 2 - 2
client/interface.go

@@ -91,8 +91,8 @@ type NetworkAPIClient interface {
 	NetworkConnect(ctx context.Context, networkID, container string, config *network.EndpointSettings) error
 	NetworkConnect(ctx context.Context, networkID, container string, config *network.EndpointSettings) error
 	NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
 	NetworkCreate(ctx context.Context, name string, options types.NetworkCreate) (types.NetworkCreateResponse, error)
 	NetworkDisconnect(ctx context.Context, networkID, container string, force bool) error
 	NetworkDisconnect(ctx context.Context, networkID, container string, force bool) error
-	NetworkInspect(ctx context.Context, networkID string) (types.NetworkResource, error)
-	NetworkInspectWithRaw(ctx context.Context, networkID string) (types.NetworkResource, []byte, error)
+	NetworkInspect(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, error)
+	NetworkInspectWithRaw(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, []byte, error)
 	NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
 	NetworkList(ctx context.Context, options types.NetworkListOptions) ([]types.NetworkResource, error)
 	NetworkRemove(ctx context.Context, networkID string) error
 	NetworkRemove(ctx context.Context, networkID string) error
 	NetworksPrune(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error)
 	NetworksPrune(ctx context.Context, pruneFilter filters.Args) (types.NetworksPruneReport, error)

+ 14 - 5
client/network_inspect.go

@@ -5,21 +5,30 @@ import (
 	"encoding/json"
 	"encoding/json"
 	"io/ioutil"
 	"io/ioutil"
 	"net/http"
 	"net/http"
+	"net/url"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
 // NetworkInspect returns the information for a specific network configured in the docker host.
 // NetworkInspect returns the information for a specific network configured in the docker host.
-func (cli *Client) NetworkInspect(ctx context.Context, networkID string) (types.NetworkResource, error) {
-	networkResource, _, err := cli.NetworkInspectWithRaw(ctx, networkID)
+func (cli *Client) NetworkInspect(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, error) {
+	networkResource, _, err := cli.NetworkInspectWithRaw(ctx, networkID, verbose)
 	return networkResource, err
 	return networkResource, err
 }
 }
 
 
 // NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
 // NetworkInspectWithRaw returns the information for a specific network configured in the docker host and its raw representation.
-func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string) (types.NetworkResource, []byte, error) {
-	var networkResource types.NetworkResource
-	resp, err := cli.get(ctx, "/networks/"+networkID, nil, nil)
+func (cli *Client) NetworkInspectWithRaw(ctx context.Context, networkID string, verbose bool) (types.NetworkResource, []byte, error) {
+	var (
+		networkResource types.NetworkResource
+		resp            serverResponse
+		err             error
+	)
+	query := url.Values{}
+	if verbose {
+		query.Set("verbose", "true")
+	}
+	resp, err = cli.get(ctx, "/networks/"+networkID, query, nil)
 	if err != nil {
 	if err != nil {
 		if resp.statusCode == http.StatusNotFound {
 		if resp.statusCode == http.StatusNotFound {
 			return networkResource, nil, networkNotFoundError{networkID}
 			return networkResource, nil, networkNotFoundError{networkID}

+ 33 - 6
client/network_inspect_test.go

@@ -10,6 +10,7 @@ import (
 	"testing"
 	"testing"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/api/types/network"
 	"golang.org/x/net/context"
 	"golang.org/x/net/context"
 )
 )
 
 
@@ -18,7 +19,7 @@ func TestNetworkInspectError(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 		client: newMockClient(errorMock(http.StatusInternalServerError, "Server error")),
 	}
 	}
 
 
-	_, err := client.NetworkInspect(context.Background(), "nothing")
+	_, err := client.NetworkInspect(context.Background(), "nothing", false)
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 	if err == nil || err.Error() != "Error response from daemon: Server error" {
 		t.Fatalf("expected a Server Error, got %v", err)
 		t.Fatalf("expected a Server Error, got %v", err)
 	}
 	}
@@ -29,7 +30,7 @@ func TestNetworkInspectContainerNotFound(t *testing.T) {
 		client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
 		client: newMockClient(errorMock(http.StatusNotFound, "Server error")),
 	}
 	}
 
 
-	_, err := client.NetworkInspect(context.Background(), "unknown")
+	_, err := client.NetworkInspect(context.Background(), "unknown", false)
 	if err == nil || !IsErrNetworkNotFound(err) {
 	if err == nil || !IsErrNetworkNotFound(err) {
 		t.Fatalf("expected a networkNotFound error, got %v", err)
 		t.Fatalf("expected a networkNotFound error, got %v", err)
 	}
 	}
@@ -46,9 +47,23 @@ func TestNetworkInspect(t *testing.T) {
 				return nil, fmt.Errorf("expected GET method, got %s", req.Method)
 				return nil, fmt.Errorf("expected GET method, got %s", req.Method)
 			}
 			}
 
 
-			content, err := json.Marshal(types.NetworkResource{
-				Name: "mynetwork",
-			})
+			var (
+				content []byte
+				err     error
+			)
+			if strings.HasPrefix(req.URL.RawQuery, "verbose=true") {
+				s := map[string]network.ServiceInfo{
+					"web": {},
+				}
+				content, err = json.Marshal(types.NetworkResource{
+					Name:     "mynetwork",
+					Services: s,
+				})
+			} else {
+				content, err = json.Marshal(types.NetworkResource{
+					Name: "mynetwork",
+				})
+			}
 			if err != nil {
 			if err != nil {
 				return nil, err
 				return nil, err
 			}
 			}
@@ -59,11 +74,23 @@ func TestNetworkInspect(t *testing.T) {
 		}),
 		}),
 	}
 	}
 
 
-	r, err := client.NetworkInspect(context.Background(), "network_id")
+	r, err := client.NetworkInspect(context.Background(), "network_id", false)
 	if err != nil {
 	if err != nil {
 		t.Fatal(err)
 		t.Fatal(err)
 	}
 	}
 	if r.Name != "mynetwork" {
 	if r.Name != "mynetwork" {
 		t.Fatalf("expected `mynetwork`, got %s", r.Name)
 		t.Fatalf("expected `mynetwork`, got %s", r.Name)
 	}
 	}
+
+	r, err = client.NetworkInspect(context.Background(), "network_id", true)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if r.Name != "mynetwork" {
+		t.Fatalf("expected `mynetwork`, got %s", r.Name)
+	}
+	_, ok := r.Services["web"]
+	if !ok {
+		t.Fatalf("expected service `web` missing in the verbose output")
+	}
 }
 }

+ 1 - 0
docs/api/version-history.md

@@ -17,6 +17,7 @@ keywords: "API, Docker, rcli, REST, documentation"
 
 
 [Docker Engine API v1.27](https://docs.docker.com/engine/api/v1.27/) documentation
 [Docker Engine API v1.27](https://docs.docker.com/engine/api/v1.27/) documentation
 
 
+* Optional query parameter `verbose` for `GET /networks/(id or name)` will now list all services with all the tasks, including the non-local tasks on the given network.
 * `GET /containers/(id or name)/attach/ws` now returns WebSocket in binary frame format for API version >= v1.27, and returns WebSocket in text frame format for API version< v1.27, for the purpose of backward-compatibility.
 * `GET /containers/(id or name)/attach/ws` now returns WebSocket in binary frame format for API version >= v1.27, and returns WebSocket in text frame format for API version< v1.27, for the purpose of backward-compatibility.
 * `GET /networks` is optimised only to return list of all networks and network specific information. List of all containers attached to a specific network is removed from this API and is only available using the network specific `GET /networks/{network-id}.
 * `GET /networks` is optimised only to return list of all networks and network specific information. List of all containers attached to a specific network is removed from this API and is only available using the network specific `GET /networks/{network-id}.
 * `GET /containers/json` now supports `publish` and `expose` filters to filter containers that expose or publish certain ports.
 * `GET /containers/json` now supports `publish` and `expose` filters to filter containers that expose or publish certain ports.

+ 96 - 1
docs/reference/commandline/network_inspect.md

@@ -48,7 +48,7 @@ The `network inspect` command shows the containers, by id, in its
 results. For networks backed by multi-host network driver, such as Overlay,
 results. For networks backed by multi-host network driver, such as Overlay,
 this command also shows the container endpoints in other hosts in the
 this command also shows the container endpoints in other hosts in the
 cluster. These endpoints are represented as "ep-{endpoint-id}" in the output.
 cluster. These endpoints are represented as "ep-{endpoint-id}" in the output.
-However, for swarm-scoped networks, only the endpoints that are local to the
+However, for swarm mode networks, only the endpoints that are local to the
 node are shown.
 node are shown.
 
 
 You can specify an alternate format to execute a given
 You can specify an alternate format to execute a given
@@ -201,6 +201,101 @@ $ docker network inspect ingress
 ]
 ]
 ```
 ```
 
 
+### Using `verbose` option for `network inspect`
+
+`docker network inspect --verbose` for swarm mode overlay networks shows service-specific
+details such as the service's VIP and port mappings. It also shows IPs of service tasks,
+and the IPs of the nodes where the tasks are running.
+
+Following is an example output for a overlay network `ov1` that has one service `s1`
+attached to. service `s1` in this case has three replicas.
+
+```bash
+$ docker network inspect --verbose ov1
+[
+    {
+        "Name": "ov1",
+        "Id": "ybmyjvao9vtzy3oorxbssj13b",
+        "Created": "2017-03-13T17:04:39.776106792Z",
+        "Scope": "swarm",
+        "Driver": "overlay",
+        "EnableIPv6": false,
+        "IPAM": {
+            "Driver": "default",
+            "Options": null,
+            "Config": [
+                {
+                    "Subnet": "10.0.0.0/24",
+                    "Gateway": "10.0.0.1"
+                }
+            ]
+        },
+        "Internal": false,
+        "Attachable": false,
+        "Containers": {
+            "020403bd88a15f60747fd25d1ad5fa1272eb740e8a97fc547d8ad07b2f721c5e": {
+                "Name": "s1.1.pjn2ik0sfgkfzed3h0s00gs9o",
+                "EndpointID": "ad16946f416562d658f3bb30b9830d73ad91ccf6feae44411269cd0ff674714e",
+                "MacAddress": "02:42:0a:00:00:04",
+                "IPv4Address": "10.0.0.4/24",
+                "IPv6Address": ""
+            }
+        },
+        "Options": {
+            "com.docker.network.driver.overlay.vxlanid_list": "4097"
+        },
+        "Labels": {},
+        "Peers": [
+            {
+                "Name": "net-3-5d3cfd30a58c",
+                "IP": "192.168.33.13"
+            },
+            {
+                "Name": "net-1-6ecbc0040a73",
+                "IP": "192.168.33.11"
+            },
+            {
+                "Name": "net-2-fb80208efd75",
+                "IP": "192.168.33.12"
+            }
+        ],
+        "Services": {
+            "s1": {
+                "VIP": "10.0.0.2",
+                "Ports": [],
+                "LocalLBIndex": 257,
+                "Tasks": [
+                    {
+                        "Name": "s1.2.q4hcq2aiiml25ubtrtg4q1txt",
+                        "EndpointID": "040879b027e55fb658e8b60ae3b87c6cdac7d291e86a190a3b5ac6567b26511a",
+                        "EndpointIP": "10.0.0.5",
+                        "Info": {
+                            "Host IP": "192.168.33.11"
+                        }
+                    },
+                    {
+                        "Name": "s1.3.yawl4cgkp7imkfx469kn9j6lm",
+                        "EndpointID": "106edff9f120efe44068b834e1cddb5b39dd4a3af70211378b2f7a9e562bbad8",
+                        "EndpointIP": "10.0.0.3",
+                        "Info": {
+                            "Host IP": "192.168.33.12"
+                        }
+                    },
+                    {
+                        "Name": "s1.1.pjn2ik0sfgkfzed3h0s00gs9o",
+                        "EndpointID": "ad16946f416562d658f3bb30b9830d73ad91ccf6feae44411269cd0ff674714e",
+                        "EndpointIP": "10.0.0.4",
+                        "Info": {
+                            "Host IP": "192.168.33.13"
+                        }
+                    }
+                ]
+            }
+        }
+    }
+]
+```
+
 ## Related commands
 ## Related commands
 
 
 * [network disconnect ](network_disconnect.md)
 * [network disconnect ](network_disconnect.md)

+ 93 - 0
man/src/network/inspect.md

@@ -86,3 +86,96 @@ $ docker network inspect simple-network
     }
     }
 ]
 ]
 ```
 ```
+
+`docker network inspect --verbose` for swarm mode overlay networks shows service-specific
+details such as the service's VIP and port mappings. It also shows IPs of service tasks,
+and the IPs of the nodes where the tasks are running.
+
+Following is an example output for a overlay network `ov1` that has one service `s1`
+attached to. service `s1` in this case has three replicas.
+
+```bash
+$ docker network inspect --verbose ov1
+[
+    {
+        "Name": "ov1",
+        "Id": "ybmyjvao9vtzy3oorxbssj13b",
+        "Created": "2017-03-13T17:04:39.776106792Z",
+        "Scope": "swarm",
+        "Driver": "overlay",
+        "EnableIPv6": false,
+        "IPAM": {
+            "Driver": "default",
+            "Options": null,
+            "Config": [
+                {
+                    "Subnet": "10.0.0.0/24",
+                    "Gateway": "10.0.0.1"
+                }
+            ]
+        },
+        "Internal": false,
+        "Attachable": false,
+        "Containers": {
+            "020403bd88a15f60747fd25d1ad5fa1272eb740e8a97fc547d8ad07b2f721c5e": {
+                "Name": "s1.1.pjn2ik0sfgkfzed3h0s00gs9o",
+                "EndpointID": "ad16946f416562d658f3bb30b9830d73ad91ccf6feae44411269cd0ff674714e",
+                "MacAddress": "02:42:0a:00:00:04",
+                "IPv4Address": "10.0.0.4/24",
+                "IPv6Address": ""
+            }
+        },
+        "Options": {
+            "com.docker.network.driver.overlay.vxlanid_list": "4097"
+        },
+        "Labels": {},
+        "Peers": [
+            {
+                "Name": "net-3-5d3cfd30a58c",
+                "IP": "192.168.33.13"
+            },
+            {
+                "Name": "net-1-6ecbc0040a73",
+                "IP": "192.168.33.11"
+            },
+            {
+                "Name": "net-2-fb80208efd75",
+                "IP": "192.168.33.12"
+            }
+        ],
+        "Services": {
+            "s1": {
+                "VIP": "10.0.0.2",
+                "Ports": [],
+                "LocalLBIndex": 257,
+                "Tasks": [
+                    {
+                        "Name": "s1.2.q4hcq2aiiml25ubtrtg4q1txt",
+                        "EndpointID": "040879b027e55fb658e8b60ae3b87c6cdac7d291e86a190a3b5ac6567b26511a",
+                        "EndpointIP": "10.0.0.5",
+                        "Info": {
+                            "Host IP": "192.168.33.11"
+                        }
+                    },
+                    {
+                        "Name": "s1.3.yawl4cgkp7imkfx469kn9j6lm",
+                        "EndpointID": "106edff9f120efe44068b834e1cddb5b39dd4a3af70211378b2f7a9e562bbad8",
+                        "EndpointIP": "10.0.0.3",
+                        "Info": {
+                            "Host IP": "192.168.33.12"
+                        }
+                    },
+                    {
+                        "Name": "s1.1.pjn2ik0sfgkfzed3h0s00gs9o",
+                        "EndpointID": "ad16946f416562d658f3bb30b9830d73ad91ccf6feae44411269cd0ff674714e",
+                        "EndpointIP": "10.0.0.4",
+                        "Info": {
+                            "Host IP": "192.168.33.13"
+                        }
+                    }
+                ]
+            }
+        }
+    }
+]
+```

+ 1 - 1
vendor.conf

@@ -23,7 +23,7 @@ github.com/RackSec/srslog 456df3a81436d29ba874f3590eeeee25d666f8a5
 github.com/imdario/mergo 0.2.1
 github.com/imdario/mergo 0.2.1
 
 
 #get libnetwork packages
 #get libnetwork packages
-github.com/docker/libnetwork 1a019214c9cb80bd56219e5d6994a22caf302895
+github.com/docker/libnetwork 4610dd67c7b9828bb4719d8aa2ac53a7f1f739d2
 github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
 github.com/docker/go-events 18b43f1bc85d9cdd42c05a6cd2d444c7a200a894
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
 github.com/armon/go-radix e39d623f12e8e41c7b5529e9a9dd67a1e2261f80
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec
 github.com/armon/go-metrics eb0af217e5e9747e41dd5303755356b62d28e3ec

+ 114 - 7
vendor/github.com/docker/libnetwork/agent.go

@@ -44,6 +44,8 @@ type agent struct {
 	sync.Mutex
 	sync.Mutex
 }
 }
 
 
+const libnetworkEPTable = "endpoint_table"
+
 func getBindAddr(ifaceName string) (string, error) {
 func getBindAddr(ifaceName string) (string, error) {
 	iface, err := net.InterfaceByName(ifaceName)
 	iface, err := net.InterfaceByName(ifaceName)
 	if err != nil {
 	if err != nil {
@@ -285,7 +287,7 @@ func (c *controller) agentInit(listenAddr, bindAddrOrInterface, advertiseAddr st
 		return err
 		return err
 	}
 	}
 
 
-	ch, cancel := nDB.Watch("endpoint_table", "", "")
+	ch, cancel := nDB.Watch(libnetworkEPTable, "", "")
 	nodeCh, cancel := nDB.Watch(networkdb.NodeTable, "", "")
 	nodeCh, cancel := nDB.Watch(networkdb.NodeTable, "", "")
 
 
 	c.Lock()
 	c.Lock()
@@ -385,6 +387,111 @@ func (c *controller) agentClose() {
 	agent.networkDB.Close()
 	agent.networkDB.Close()
 }
 }
 
 
+// Task has the backend container details
+type Task struct {
+	Name       string
+	EndpointID string
+	EndpointIP string
+	Info       map[string]string
+}
+
+// ServiceInfo has service specific details along with the list of backend tasks
+type ServiceInfo struct {
+	VIP          string
+	LocalLBIndex int
+	Tasks        []Task
+	Ports        []string
+}
+
+type epRecord struct {
+	ep      EndpointRecord
+	info    map[string]string
+	lbIndex int
+}
+
+func (n *network) Services() map[string]ServiceInfo {
+	eps := make(map[string]epRecord)
+
+	if !n.isClusterEligible() {
+		return nil
+	}
+	agent := n.getController().getAgent()
+	if agent == nil {
+		return nil
+	}
+
+	// Walk through libnetworkEPTable and fetch the driver agnostic endpoint info
+	entries := agent.networkDB.GetTableByNetwork(libnetworkEPTable, n.id)
+	for eid, value := range entries {
+		var epRec EndpointRecord
+		nid := n.ID()
+		if err := proto.Unmarshal(value.([]byte), &epRec); err != nil {
+			logrus.Errorf("Unmarshal of libnetworkEPTable failed for endpoint %s in network %s, %v", eid, nid, err)
+			continue
+		}
+		i := n.getController().getLBIndex(epRec.ServiceID, nid, epRec.IngressPorts)
+		eps[eid] = epRecord{
+			ep:      epRec,
+			lbIndex: i,
+		}
+	}
+
+	// Walk through the driver's tables, have the driver decode the entries
+	// and return the tuple {ep ID, value}. value is a string that coveys
+	// relevant info about the endpoint.
+	d, err := n.driver(true)
+	if err != nil {
+		logrus.Errorf("Could not resolve driver for network %s/%s while fetching services: %v", n.networkType, n.ID(), err)
+		return nil
+	}
+	for _, table := range n.driverTables {
+		if table.objType != driverapi.EndpointObject {
+			continue
+		}
+		entries := agent.networkDB.GetTableByNetwork(table.name, n.id)
+		for key, value := range entries {
+			epID, info := d.DecodeTableEntry(table.name, key, value.([]byte))
+			if ep, ok := eps[epID]; !ok {
+				logrus.Errorf("Inconsistent driver and libnetwork state for endpoint %s", epID)
+			} else {
+				ep.info = info
+				eps[epID] = ep
+			}
+		}
+	}
+
+	// group the endpoints into a map keyed by the service name
+	sinfo := make(map[string]ServiceInfo)
+	for ep, epr := range eps {
+		var (
+			s  ServiceInfo
+			ok bool
+		)
+		if s, ok = sinfo[epr.ep.ServiceName]; !ok {
+			s = ServiceInfo{
+				VIP:          epr.ep.VirtualIP,
+				LocalLBIndex: epr.lbIndex,
+			}
+		}
+		ports := []string{}
+		if s.Ports == nil {
+			for _, port := range epr.ep.IngressPorts {
+				p := fmt.Sprintf("Target: %d, Publish: %d", port.TargetPort, port.PublishedPort)
+				ports = append(ports, p)
+			}
+			s.Ports = ports
+		}
+		s.Tasks = append(s.Tasks, Task{
+			Name:       epr.ep.Name,
+			EndpointID: ep,
+			EndpointIP: epr.ep.EndpointIP,
+			Info:       epr.info,
+		})
+		sinfo[epr.ep.ServiceName] = s
+	}
+	return sinfo
+}
+
 func (n *network) isClusterEligible() bool {
 func (n *network) isClusterEligible() bool {
 	if n.driverScope() != datastore.GlobalScope {
 	if n.driverScope() != datastore.GlobalScope {
 		return false
 		return false
@@ -508,7 +615,7 @@ func (ep *endpoint) addServiceInfoToCluster() error {
 	}
 	}
 
 
 	if agent != nil {
 	if agent != nil {
-		if err := agent.networkDB.CreateEntry("endpoint_table", n.ID(), ep.ID(), buf); err != nil {
+		if err := agent.networkDB.CreateEntry(libnetworkEPTable, n.ID(), ep.ID(), buf); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -541,7 +648,7 @@ func (ep *endpoint) deleteServiceInfoFromCluster() error {
 	}
 	}
 
 
 	if agent != nil {
 	if agent != nil {
-		if err := agent.networkDB.DeleteEntry("endpoint_table", n.ID(), ep.ID()); err != nil {
+		if err := agent.networkDB.DeleteEntry(libnetworkEPTable, n.ID(), ep.ID()); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -559,8 +666,8 @@ func (n *network) addDriverWatches() {
 	if agent == nil {
 	if agent == nil {
 		return
 		return
 	}
 	}
-	for _, tableName := range n.driverTables {
-		ch, cancel := agent.networkDB.Watch(tableName, n.ID(), "")
+	for _, table := range n.driverTables {
+		ch, cancel := agent.networkDB.Watch(table.name, n.ID(), "")
 		agent.Lock()
 		agent.Lock()
 		agent.driverCancelFuncs[n.ID()] = append(agent.driverCancelFuncs[n.ID()], cancel)
 		agent.driverCancelFuncs[n.ID()] = append(agent.driverCancelFuncs[n.ID()], cancel)
 		agent.Unlock()
 		agent.Unlock()
@@ -571,9 +678,9 @@ func (n *network) addDriverWatches() {
 			return
 			return
 		}
 		}
 
 
-		agent.networkDB.WalkTable(tableName, func(nid, key string, value []byte) bool {
+		agent.networkDB.WalkTable(table.name, func(nid, key string, value []byte) bool {
 			if nid == n.ID() {
 			if nid == n.ID() {
-				d.EventNotify(driverapi.Create, nid, tableName, key, value)
+				d.EventNotify(driverapi.Create, nid, table.name, key, value)
 			}
 			}
 
 
 			return false
 			return false

+ 36 - 1
vendor/github.com/docker/libnetwork/driverapi/driverapi.go

@@ -72,6 +72,16 @@ type Driver interface {
 	// only invoked for the global scope driver.
 	// only invoked for the global scope driver.
 	EventNotify(event EventType, nid string, tableName string, key string, value []byte)
 	EventNotify(event EventType, nid string, tableName string, key string, value []byte)
 
 
+	// DecodeTableEntry passes the driver a key, value pair from table it registered
+	// with libnetwork. Driver should return {object ID, map[string]string} tuple.
+	// If DecodeTableEntry is called for a table associated with NetworkObject or
+	// EndpointObject the return object ID should be the network id or endppoint id
+	// associated with that entry. map should have information about the object that
+	// can be presented to the user.
+	// For exampe: overlay driver returns the VTEP IP of the host that has the endpoint
+	// which is shown in 'network inspect --verbose'
+	DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string)
+
 	// Type returns the type of this driver, the network type this driver manages
 	// Type returns the type of this driver, the network type this driver manages
 	Type() string
 	Type() string
 
 
@@ -84,7 +94,7 @@ type Driver interface {
 type NetworkInfo interface {
 type NetworkInfo interface {
 	// TableEventRegister registers driver interest in a given
 	// TableEventRegister registers driver interest in a given
 	// table name.
 	// table name.
-	TableEventRegister(tableName string) error
+	TableEventRegister(tableName string, objType ObjectType) error
 }
 }
 
 
 // InterfaceInfo provides a go interface for drivers to retrive
 // InterfaceInfo provides a go interface for drivers to retrive
@@ -175,3 +185,28 @@ const (
 	// Delete event is generated when a table entry is deleted.
 	// Delete event is generated when a table entry is deleted.
 	Delete
 	Delete
 )
 )
+
+// ObjectType represents the type of object driver wants to store in libnetwork's networkDB
+type ObjectType int
+
+const (
+	// EndpointObject should be set for libnetwork endpoint object related data
+	EndpointObject ObjectType = 1 + iota
+	// NetworkObject should be set for libnetwork network object related data
+	NetworkObject
+	// OpaqueObject is for driver specific data with no corresponding libnetwork object
+	OpaqueObject
+)
+
+// IsValidType validates the passed in type against the valid object types
+func IsValidType(objType ObjectType) bool {
+	switch objType {
+	case EndpointObject:
+		fallthrough
+	case NetworkObject:
+		fallthrough
+	case OpaqueObject:
+		return true
+	}
+	return false
+}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/bridge/bridge.go

@@ -575,6 +575,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 // Create a new network using bridge plugin
 // Create a new network using bridge plugin
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
 	if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {

+ 0 - 6
vendor/github.com/docker/libnetwork/drivers/bridge/setup_ip_tables.go

@@ -140,7 +140,6 @@ func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairp
 		hpNatRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}}
 		hpNatRule = iptRule{table: iptables.Nat, chain: "POSTROUTING", preArgs: []string{"-t", "nat"}, args: []string{"-m", "addrtype", "--src-type", "LOCAL", "-o", bridgeIface, "-j", "MASQUERADE"}}
 		skipDNAT  = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}}
 		skipDNAT  = iptRule{table: iptables.Nat, chain: DockerChain, preArgs: []string{"-t", "nat"}, args: []string{"-i", bridgeIface, "-j", "RETURN"}}
 		outRule   = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
 		outRule   = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-i", bridgeIface, "!", "-o", bridgeIface, "-j", "ACCEPT"}}
-		inRule    = iptRule{table: iptables.Filter, chain: "FORWARD", args: []string{"-o", bridgeIface, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}
 	)
 	)
 
 
 	// Set NAT.
 	// Set NAT.
@@ -173,11 +172,6 @@ func setupIPTablesInternal(bridgeIface string, addr net.Addr, icc, ipmasq, hairp
 		return err
 		return err
 	}
 	}
 
 
-	// Set Accept on incoming packets for existing connections.
-	if err := programChainRule(inRule, "ACCEPT INCOMING", enable); err != nil {
-		return err
-	}
-
 	return nil
 	return nil
 }
 }
 
 

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/host/host.go

@@ -35,6 +35,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	d.Lock()
 	d.Lock()
 	defer d.Unlock()
 	defer d.Unlock()

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/ipvlan/ipvlan.go

@@ -108,3 +108,7 @@ func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{
 
 
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 }
+
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/macvlan/macvlan.go

@@ -110,3 +110,7 @@ func (d *driver) DiscoverDelete(dType discoverapi.DiscoveryType, data interface{
 
 
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 }
+
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/null/null.go

@@ -35,6 +35,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	d.Lock()
 	d.Lock()
 	defer d.Unlock()
 	defer d.Unlock()

+ 55 - 15
vendor/github.com/docker/libnetwork/drivers/overlay/encryption.go

@@ -20,7 +20,7 @@ import (
 )
 )
 
 
 const (
 const (
-	mark         = uint32(0xD0C4E3)
+	r            = 0xD0C4E3
 	timeout      = 30
 	timeout      = 30
 	pktExpansion = 26 // SPI(4) + SeqN(4) + IV(8) + PadLength(1) + NextHeader(1) + ICV(8)
 	pktExpansion = 26 // SPI(4) + SeqN(4) + IV(8) + PadLength(1) + NextHeader(1) + ICV(8)
 )
 )
@@ -31,6 +31,8 @@ const (
 	bidir
 	bidir
 )
 )
 
 
+var spMark = netlink.XfrmMark{Value: uint32(r), Mask: 0xffffffff}
+
 type key struct {
 type key struct {
 	value []byte
 	value []byte
 	tag   uint32
 	tag   uint32
@@ -201,7 +203,7 @@ func programMangle(vni uint32, add bool) (err error) {
 	var (
 	var (
 		p      = strconv.FormatUint(uint64(vxlanPort), 10)
 		p      = strconv.FormatUint(uint64(vxlanPort), 10)
 		c      = fmt.Sprintf("0>>22&0x3C@12&0xFFFFFF00=%d", int(vni)<<8)
 		c      = fmt.Sprintf("0>>22&0x3C@12&0xFFFFFF00=%d", int(vni)<<8)
-		m      = strconv.FormatUint(uint64(mark), 10)
+		m      = strconv.FormatUint(uint64(r), 10)
 		chain  = "OUTPUT"
 		chain  = "OUTPUT"
 		rule   = []string{"-p", "udp", "--dport", p, "-m", "u32", "--u32", c, "-j", "MARK", "--set-mark", m}
 		rule   = []string{"-p", "udp", "--dport", p, "-m", "u32", "--u32", c, "-j", "MARK", "--set-mark", m}
 		a      = "-A"
 		a      = "-A"
@@ -271,6 +273,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f
 			Proto: netlink.XFRM_PROTO_ESP,
 			Proto: netlink.XFRM_PROTO_ESP,
 			Spi:   spi.reverse,
 			Spi:   spi.reverse,
 			Mode:  netlink.XFRM_MODE_TRANSPORT,
 			Mode:  netlink.XFRM_MODE_TRANSPORT,
+			Reqid: r,
 		}
 		}
 		if add {
 		if add {
 			rSA.Aead = buildAeadAlgo(k, spi.reverse)
 			rSA.Aead = buildAeadAlgo(k, spi.reverse)
@@ -296,6 +299,7 @@ func programSA(localIP, remoteIP net.IP, spi *spi, k *key, dir int, add bool) (f
 			Proto: netlink.XFRM_PROTO_ESP,
 			Proto: netlink.XFRM_PROTO_ESP,
 			Spi:   spi.forward,
 			Spi:   spi.forward,
 			Mode:  netlink.XFRM_MODE_TRANSPORT,
 			Mode:  netlink.XFRM_MODE_TRANSPORT,
+			Reqid: r,
 		}
 		}
 		if add {
 		if add {
 			fSA.Aead = buildAeadAlgo(k, spi.forward)
 			fSA.Aead = buildAeadAlgo(k, spi.forward)
@@ -325,17 +329,18 @@ func programSP(fSA *netlink.XfrmState, rSA *netlink.XfrmState, add bool) error {
 		xfrmProgram = ns.NlHandle().XfrmPolicyAdd
 		xfrmProgram = ns.NlHandle().XfrmPolicyAdd
 	}
 	}
 
 
-	fullMask := net.CIDRMask(8*len(fSA.Src), 8*len(fSA.Src))
+	// Create a congruent cidr
+	s := types.GetMinimalIP(fSA.Src)
+	d := types.GetMinimalIP(fSA.Dst)
+	fullMask := net.CIDRMask(8*len(s), 8*len(s))
 
 
 	fPol := &netlink.XfrmPolicy{
 	fPol := &netlink.XfrmPolicy{
-		Src:     &net.IPNet{IP: fSA.Src, Mask: fullMask},
-		Dst:     &net.IPNet{IP: fSA.Dst, Mask: fullMask},
+		Src:     &net.IPNet{IP: s, Mask: fullMask},
+		Dst:     &net.IPNet{IP: d, Mask: fullMask},
 		Dir:     netlink.XFRM_DIR_OUT,
 		Dir:     netlink.XFRM_DIR_OUT,
 		Proto:   17,
 		Proto:   17,
 		DstPort: 4789,
 		DstPort: 4789,
-		Mark: &netlink.XfrmMark{
-			Value: mark,
-		},
+		Mark:    &spMark,
 		Tmpls: []netlink.XfrmPolicyTmpl{
 		Tmpls: []netlink.XfrmPolicyTmpl{
 			{
 			{
 				Src:   fSA.Src,
 				Src:   fSA.Src,
@@ -343,6 +348,7 @@ func programSP(fSA *netlink.XfrmState, rSA *netlink.XfrmState, add bool) error {
 				Proto: netlink.XFRM_PROTO_ESP,
 				Proto: netlink.XFRM_PROTO_ESP,
 				Mode:  netlink.XFRM_MODE_TRANSPORT,
 				Mode:  netlink.XFRM_MODE_TRANSPORT,
 				Spi:   fSA.Spi,
 				Spi:   fSA.Spi,
+				Reqid: r,
 			},
 			},
 		},
 		},
 	}
 	}
@@ -426,6 +432,8 @@ func (d *driver) secMapWalk(f func(string, []*spi) ([]*spi, bool)) error {
 }
 }
 
 
 func (d *driver) setKeys(keys []*key) error {
 func (d *driver) setKeys(keys []*key) error {
+	// Remove any stale policy, state
+	clearEncryptionStates()
 	// Accept the encryption keys and clear any stale encryption map
 	// Accept the encryption keys and clear any stale encryption map
 	d.Lock()
 	d.Lock()
 	d.keys = keys
 	d.keys = keys
@@ -526,7 +534,7 @@ func updateNodeKey(lIP, aIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, pr
 	}
 	}
 
 
 	if newIdx > -1 {
 	if newIdx > -1 {
-		// +RSA2
+		// +rSA2
 		programSA(lIP, rIP, spis[newIdx], curKeys[newIdx], reverse, true)
 		programSA(lIP, rIP, spis[newIdx], curKeys[newIdx], reverse, true)
 	}
 	}
 
 
@@ -535,16 +543,17 @@ func updateNodeKey(lIP, aIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, pr
 		fSA2, _, _ := programSA(lIP, rIP, spis[priIdx], curKeys[priIdx], forward, true)
 		fSA2, _, _ := programSA(lIP, rIP, spis[priIdx], curKeys[priIdx], forward, true)
 
 
 		// +fSP2, -fSP1
 		// +fSP2, -fSP1
-		fullMask := net.CIDRMask(8*len(fSA2.Src), 8*len(fSA2.Src))
+		s := types.GetMinimalIP(fSA2.Src)
+		d := types.GetMinimalIP(fSA2.Dst)
+		fullMask := net.CIDRMask(8*len(s), 8*len(s))
+
 		fSP1 := &netlink.XfrmPolicy{
 		fSP1 := &netlink.XfrmPolicy{
-			Src:     &net.IPNet{IP: fSA2.Src, Mask: fullMask},
-			Dst:     &net.IPNet{IP: fSA2.Dst, Mask: fullMask},
+			Src:     &net.IPNet{IP: s, Mask: fullMask},
+			Dst:     &net.IPNet{IP: d, Mask: fullMask},
 			Dir:     netlink.XFRM_DIR_OUT,
 			Dir:     netlink.XFRM_DIR_OUT,
 			Proto:   17,
 			Proto:   17,
 			DstPort: 4789,
 			DstPort: 4789,
-			Mark: &netlink.XfrmMark{
-				Value: mark,
-			},
+			Mark:    &spMark,
 			Tmpls: []netlink.XfrmPolicyTmpl{
 			Tmpls: []netlink.XfrmPolicyTmpl{
 				{
 				{
 					Src:   fSA2.Src,
 					Src:   fSA2.Src,
@@ -552,6 +561,7 @@ func updateNodeKey(lIP, aIP, rIP net.IP, idxs []*spi, curKeys []*key, newIdx, pr
 					Proto: netlink.XFRM_PROTO_ESP,
 					Proto: netlink.XFRM_PROTO_ESP,
 					Mode:  netlink.XFRM_MODE_TRANSPORT,
 					Mode:  netlink.XFRM_MODE_TRANSPORT,
 					Spi:   fSA2.Spi,
 					Spi:   fSA2.Spi,
+					Reqid: r,
 				},
 				},
 			},
 			},
 		}
 		}
@@ -597,3 +607,33 @@ func (n *network) maxMTU() int {
 	}
 	}
 	return mtu
 	return mtu
 }
 }
+
+func clearEncryptionStates() {
+	nlh := ns.NlHandle()
+	spList, err := nlh.XfrmPolicyList(netlink.FAMILY_ALL)
+	if err != nil {
+		logrus.Warnf("Failed to retrieve SP list for cleanup: %v", err)
+	}
+	saList, err := nlh.XfrmStateList(netlink.FAMILY_ALL)
+	if err != nil {
+		logrus.Warnf("Failed to retrieve SA list for cleanup: %v", err)
+	}
+	for _, sp := range spList {
+		if sp.Mark != nil && sp.Mark.Value == spMark.Value {
+			if err := nlh.XfrmPolicyDel(&sp); err != nil {
+				logrus.Warnf("Failed to delete stale SP %s: %v", sp, err)
+				continue
+			}
+			logrus.Debugf("Removed stale SP: %s", sp)
+		}
+	}
+	for _, sa := range saList {
+		if sa.Reqid == r {
+			if err := nlh.XfrmStateDel(&sa); err != nil {
+				logrus.Warnf("Failed to delete stale SA %s: %v", sa, err)
+				continue
+			}
+			logrus.Debugf("Removed stale SA: %s", sa)
+		}
+	}
+}

+ 17 - 0
vendor/github.com/docker/libnetwork/drivers/overlay/joinleave.go

@@ -145,6 +145,23 @@ func (d *driver) Join(nid, eid string, sboxKey string, jinfo driverapi.JoinInfo,
 	return nil
 	return nil
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	if tablename != ovPeerTable {
+		logrus.Errorf("DecodeTableEntry: unexpected table name %s", tablename)
+		return "", nil
+	}
+
+	var peer PeerRecord
+	if err := proto.Unmarshal(value, &peer); err != nil {
+		logrus.Errorf("DecodeTableEntry: failed to unmarshal peer record for key %s: %v", key, err)
+		return "", nil
+	}
+
+	return key, map[string]string{
+		"Host IP": peer.TunnelEndpointIP,
+	}
+}
+
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 	if tableName != ovPeerTable {
 	if tableName != ovPeerTable {
 		logrus.Errorf("Unexpected table notification for table %s received", tableName)
 		logrus.Errorf("Unexpected table notification for table %s received", tableName)

+ 1 - 1
vendor/github.com/docker/libnetwork/drivers/overlay/ov_network.go

@@ -159,7 +159,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 	}
 	}
 
 
 	if nInfo != nil {
 	if nInfo != nil {
-		if err := nInfo.TableEventRegister(ovPeerTable); err != nil {
+		if err := nInfo.TableEventRegister(ovPeerTable, driverapi.EndpointObject); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/overlay/ovmanager/ovmanager.go

@@ -199,6 +199,10 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) DeleteNetwork(nid string) error {
 func (d *driver) DeleteNetwork(nid string) error {
 	return types.NotImplementedErrorf("not implemented")
 	return types.NotImplementedErrorf("not implemented")
 }
 }

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/remote/driver.go

@@ -116,6 +116,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) CreateNetwork(id string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 func (d *driver) CreateNetwork(id string, options map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	create := &api.CreateNetworkRequest{
 	create := &api.CreateNetworkRequest{
 		NetworkID: id,
 		NetworkID: id,

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/solaris/bridge/bridge.go

@@ -175,6 +175,10 @@ func (d *driver) NetworkFree(id string) error {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
 	if len(ipV4Data) == 0 || ipV4Data[0].Pool.String() == "0.0.0.0/0" {
 		return types.BadRequestErrorf("ipv4 pool is empty")
 		return types.BadRequestErrorf("ipv4 pool is empty")

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/solaris/overlay/joinleave.go

@@ -149,6 +149,10 @@ func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key stri
 	d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
 	d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 // Leave method is invoked when a Sandbox detaches from an endpoint.
 // Leave method is invoked when a Sandbox detaches from an endpoint.
 func (d *driver) Leave(nid, eid string) error {
 func (d *driver) Leave(nid, eid string) error {
 	if err := validateID(nid, eid); err != nil {
 	if err := validateID(nid, eid); err != nil {

+ 1 - 1
vendor/github.com/docker/libnetwork/drivers/solaris/overlay/ov_network.go

@@ -153,7 +153,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 	}
 	}
 
 
 	if nInfo != nil {
 	if nInfo != nil {
-		if err := nInfo.TableEventRegister(ovPeerTable); err != nil {
+		if err := nInfo.TableEventRegister(ovPeerTable, driverapi.EndpointObject); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/windows/overlay/joinleave_windows.go

@@ -93,6 +93,10 @@ func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key stri
 	d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
 	d.peerAdd(nid, eid, addr.IP, addr.Mask, mac, vtep, true)
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 // Leave method is invoked when a Sandbox detaches from an endpoint.
 // Leave method is invoked when a Sandbox detaches from an endpoint.
 func (d *driver) Leave(nid, eid string) error {
 func (d *driver) Leave(nid, eid string) error {
 	if err := validateID(nid, eid); err != nil {
 	if err := validateID(nid, eid); err != nil {

+ 1 - 1
vendor/github.com/docker/libnetwork/drivers/windows/overlay/ov_network_windows.go

@@ -169,7 +169,7 @@ func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo d
 	n.interfaceName = interfaceName
 	n.interfaceName = interfaceName
 
 
 	if nInfo != nil {
 	if nInfo != nil {
-		if err := nInfo.TableEventRegister(ovPeerTable); err != nil {
+		if err := nInfo.TableEventRegister(ovPeerTable, driverapi.EndpointObject); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}

+ 4 - 0
vendor/github.com/docker/libnetwork/drivers/windows/windows.go

@@ -183,6 +183,10 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 func (d *driver) EventNotify(etype driverapi.EventType, nid, tableName, key string, value []byte) {
 }
 }
 
 
+func (d *driver) DecodeTableEntry(tablename string, key string, value []byte) (string, map[string]string) {
+	return "", nil
+}
+
 // Create a new network
 // Create a new network
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 func (d *driver) CreateNetwork(id string, option map[string]interface{}, nInfo driverapi.NetworkInfo, ipV4Data, ipV6Data []driverapi.IPAMData) error {
 	if _, err := d.getNetwork(id); err == nil {
 	if _, err := d.getNetwork(id); err == nil {

+ 52 - 19
vendor/github.com/docker/libnetwork/iptables/iptables.go

@@ -50,8 +50,7 @@ var (
 	bestEffortLock sync.Mutex
 	bestEffortLock sync.Mutex
 	// ErrIptablesNotFound is returned when the rule is not found.
 	// ErrIptablesNotFound is returned when the rule is not found.
 	ErrIptablesNotFound = errors.New("Iptables not found")
 	ErrIptablesNotFound = errors.New("Iptables not found")
-	probeOnce           sync.Once
-	firewalldOnce       sync.Once
+	initOnce            sync.Once
 )
 )
 
 
 // ChainInfo defines the iptables chain.
 // ChainInfo defines the iptables chain.
@@ -86,22 +85,32 @@ func initFirewalld() {
 	}
 	}
 }
 }
 
 
+func detectIptables() {
+	path, err := exec.LookPath("iptables")
+	if err != nil {
+		return
+	}
+	iptablesPath = path
+	supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil
+	mj, mn, mc, err := GetVersion()
+	if err != nil {
+		logrus.Warnf("Failed to read iptables version: %v", err)
+		return
+	}
+	supportsCOpt = supportsCOption(mj, mn, mc)
+}
+
+func initIptables() {
+	probe()
+	initFirewalld()
+	detectIptables()
+}
+
 func initCheck() error {
 func initCheck() error {
+	initOnce.Do(initIptables)
+
 	if iptablesPath == "" {
 	if iptablesPath == "" {
-		probeOnce.Do(probe)
-		firewalldOnce.Do(initFirewalld)
-		path, err := exec.LookPath("iptables")
-		if err != nil {
-			return ErrIptablesNotFound
-		}
-		iptablesPath = path
-		supportsXlock = exec.Command(iptablesPath, "--wait", "-L", "-n").Run() == nil
-		mj, mn, mc, err := GetVersion()
-		if err != nil {
-			logrus.Warnf("Failed to read iptables version: %v", err)
-			return nil
-		}
-		supportsCOpt = supportsCOption(mj, mn, mc)
+		return ErrIptablesNotFound
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -189,6 +198,26 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) err
 			}
 			}
 
 
 		}
 		}
+		establish := []string{
+			"-o", bridgeName,
+			"-m", "conntrack",
+			"--ctstate", "RELATED,ESTABLISHED",
+			"-j", "ACCEPT"}
+		if !Exists(Filter, "FORWARD", establish...) && enable {
+			insert := append([]string{string(Insert), "FORWARD"}, establish...)
+			if output, err := Raw(insert...); err != nil {
+				return err
+			} else if len(output) != 0 {
+				return fmt.Errorf("Could not create establish rule to %s: %s", c.Table, output)
+			}
+		} else if Exists(Filter, "FORWARD", establish...) && !enable {
+			del := append([]string{string(Delete), "FORWARD"}, establish...)
+			if output, err := Raw(del...); err != nil {
+				return err
+			} else if len(output) != 0 {
+				return fmt.Errorf("Could not delete establish rule from %s: %s", c.Table, output)
+			}
+		}
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -353,7 +382,11 @@ func exists(native bool, table Table, chain string, rule ...string) bool {
 		table = Filter
 		table = Filter
 	}
 	}
 
 
-	initCheck()
+	if err := initCheck(); err != nil {
+		// The exists() signature does not allow us to return an error, but at least
+		// we can skip the (likely invalid) exec invocation.
+		return false
+	}
 
 
 	if supportsCOpt {
 	if supportsCOpt {
 		// if exit status is 0 then return true, the rule exists
 		// if exit status is 0 then return true, the rule exists
@@ -436,9 +469,9 @@ func ExistChain(chain string, table Table) bool {
 	return false
 	return false
 }
 }
 
 
-// GetVersion reads the iptables version numbers
+// GetVersion reads the iptables version numbers during initialization
 func GetVersion() (major, minor, micro int, err error) {
 func GetVersion() (major, minor, micro int, err error) {
-	out, err := Raw("--version")
+	out, err := exec.Command(iptablesPath, "--version").CombinedOutput()
 	if err == nil {
 	if err == nil {
 		major, minor, micro = parseVersionNumbers(string(out))
 		major, minor, micro = parseVersionNumbers(string(out))
 	}
 	}

+ 19 - 4
vendor/github.com/docker/libnetwork/network.go

@@ -74,6 +74,9 @@ type NetworkInfo interface {
 	// gossip cluster. For non-dynamic overlay networks and bridge networks it returns an
 	// gossip cluster. For non-dynamic overlay networks and bridge networks it returns an
 	// empty slice
 	// empty slice
 	Peers() []networkdb.PeerInfo
 	Peers() []networkdb.PeerInfo
+	//Services returns a map of services keyed by the service name with the details
+	//of all the tasks that belong to the service. Applicable only in swarm mode.
+	Services() map[string]ServiceInfo
 }
 }
 
 
 // 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.
@@ -108,6 +111,11 @@ type servicePorts struct {
 	target   []serviceTarget
 	target   []serviceTarget
 }
 }
 
 
+type networkDBTable struct {
+	name    string
+	objType driverapi.ObjectType
+}
+
 // IpamConf contains all the ipam related configurations for a network
 // IpamConf contains all the ipam related configurations for a network
 type IpamConf struct {
 type IpamConf struct {
 	// The master address pool for containers and network interfaces
 	// The master address pool for containers and network interfaces
@@ -208,7 +216,7 @@ type network struct {
 	attachable   bool
 	attachable   bool
 	inDelete     bool
 	inDelete     bool
 	ingress      bool
 	ingress      bool
-	driverTables []string
+	driverTables []networkDBTable
 	dynamic      bool
 	dynamic      bool
 	sync.Mutex
 	sync.Mutex
 }
 }
@@ -1607,11 +1615,18 @@ func (n *network) Labels() map[string]string {
 	return lbls
 	return lbls
 }
 }
 
 
-func (n *network) TableEventRegister(tableName string) error {
+func (n *network) TableEventRegister(tableName string, objType driverapi.ObjectType) error {
+	if !driverapi.IsValidType(objType) {
+		return fmt.Errorf("invalid object type %v in registering table, %s", objType, tableName)
+	}
+
+	t := networkDBTable{
+		name:    tableName,
+		objType: objType,
+	}
 	n.Lock()
 	n.Lock()
 	defer n.Unlock()
 	defer n.Unlock()
-
-	n.driverTables = append(n.driverTables, tableName)
+	n.driverTables = append(n.driverTables, t)
 	return nil
 	return nil
 }
 }
 
 

+ 16 - 0
vendor/github.com/docker/libnetwork/networkdb/networkdb.go

@@ -307,6 +307,22 @@ func (nDB *NetworkDB) UpdateEntry(tname, nid, key string, value []byte) error {
 	return nil
 	return nil
 }
 }
 
 
+// GetTableByNetwork walks the networkdb by the give table and network id and
+// returns a map of keys and values
+func (nDB *NetworkDB) GetTableByNetwork(tname, nid string) map[string]interface{} {
+	entries := make(map[string]interface{})
+	nDB.indexes[byTable].WalkPrefix(fmt.Sprintf("/%s/%s", tname, nid), func(k string, v interface{}) bool {
+		entry := v.(*entry)
+		if entry.deleting {
+			return false
+		}
+		key := k[strings.LastIndex(k, "/")+1:]
+		entries[key] = entry.value
+		return false
+	})
+	return entries
+}
+
 // DeleteEntry deletes a table entry in NetworkDB for given (network,
 // DeleteEntry deletes a table entry in NetworkDB for given (network,
 // table, key) tuple and if the NetworkDB is part of the cluster
 // table, key) tuple and if the NetworkDB is part of the cluster
 // propagates this event to the cluster.
 // propagates this event to the cluster.

+ 20 - 0
vendor/github.com/docker/libnetwork/service_common.go

@@ -18,6 +18,26 @@ func newService(name string, id string, ingressPorts []*PortConfig, aliases []st
 	}
 	}
 }
 }
 
 
+func (c *controller) getLBIndex(sid, nid string, ingressPorts []*PortConfig) int {
+	skey := serviceKey{
+		id:    sid,
+		ports: portConfigs(ingressPorts).String(),
+	}
+	c.Lock()
+	s, ok := c.serviceBindings[skey]
+	c.Unlock()
+
+	if !ok {
+		return 0
+	}
+
+	s.Lock()
+	lb := s.loadBalancers[nid]
+	s.Unlock()
+
+	return int(lb.fwMark)
+}
+
 func (c *controller) cleanupServiceBindings(cleanupNID string) {
 func (c *controller) cleanupServiceBindings(cleanupNID string) {
 	var cleanupFuncs []func()
 	var cleanupFuncs []func()