浏览代码

Merge pull request #222 from aboch/nlb

Labels support for Network Create
Jana Radhakrishnan 9 年之前
父节点
当前提交
b5145e597b

+ 1 - 30
libnetwork/api/api.go

@@ -8,7 +8,6 @@ import (
 	"strings"
 
 	"github.com/docker/libnetwork"
-	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/types"
 	"github.com/gorilla/mux"
 )
@@ -52,9 +51,6 @@ const (
 	urlSbPID  = "sandbox-partial-id"
 	urlCnID   = "container-id"
 	urlCnPID  = "container-partial-id"
-
-	// BridgeNetworkDriver is the built-in default for Network Driver
-	BridgeNetworkDriver = "bridge"
 )
 
 // NewHTTPHandler creates and initialize the HTTP handler to serve the requests for libnetwork
@@ -222,16 +218,6 @@ func buildSandboxResource(sb libnetwork.Sandbox) *sandboxResource {
  Options Parsers
 *****************/
 
-func (nc *networkCreate) parseOptions() []libnetwork.NetworkOption {
-	var setFctList []libnetwork.NetworkOption
-
-	if nc.Options != nil {
-		setFctList = append(setFctList, libnetwork.NetworkOptionGeneric(nc.Options))
-	}
-
-	return setFctList
-}
-
 func (sc *sandboxCreate) parseOptions() []libnetwork.SandboxOption {
 	var setFctList []libnetwork.SandboxOption
 	if sc.HostName != "" {
@@ -278,21 +264,6 @@ func processCreateDefaults(c libnetwork.NetworkController, nc *networkCreate) {
 	if nc.NetworkType == "" {
 		nc.NetworkType = c.Config().Daemon.DefaultDriver
 	}
-	if nc.NetworkType == BridgeNetworkDriver {
-		if nc.Options == nil {
-			nc.Options = make(map[string]interface{})
-		}
-		genericData, ok := nc.Options[netlabel.GenericData]
-		if !ok {
-			genericData = make(map[string]interface{})
-		}
-		gData := genericData.(map[string]interface{})
-
-		if _, ok := gData["BridgeName"]; !ok {
-			gData["BridgeName"] = nc.Name
-		}
-		nc.Options[netlabel.GenericData] = genericData
-	}
 }
 
 /***************************
@@ -307,7 +278,7 @@ func procCreateNetwork(c libnetwork.NetworkController, vars map[string]string, b
 	}
 	processCreateDefaults(c, &create)
 
-	nw, err := c.NewNetwork(create.NetworkType, create.Name, create.parseOptions()...)
+	nw, err := c.NewNetwork(create.NetworkType, create.Name, libnetwork.NetworkOptionLabels(create.Labels))
 	if err != nil {
 		return "", convertNetworkError(err)
 	}

+ 62 - 57
libnetwork/api/api_test.go

@@ -15,6 +15,7 @@ import (
 	"github.com/docker/docker/pkg/reexec"
 	"github.com/docker/libnetwork"
 	"github.com/docker/libnetwork/datastore"
+	"github.com/docker/libnetwork/drivers/bridge"
 	"github.com/docker/libnetwork/netlabel"
 	"github.com/docker/libnetwork/options"
 	"github.com/docker/libnetwork/testutils"
@@ -26,12 +27,6 @@ const (
 	bridgeName    = "docker0"
 )
 
-func getEmptyGenericOption() map[string]interface{} {
-	genericOption := make(map[string]interface{})
-	genericOption[netlabel.GenericData] = options.Generic{}
-	return genericOption
-}
-
 func i2s(i interface{}) string {
 	s, ok := i.(string)
 	if !ok {
@@ -111,6 +106,22 @@ func createTestNetwork(t *testing.T, network string) (libnetwork.NetworkControll
 	return c, nw
 }
 
+func getExposedPorts() []types.TransportPort {
+	return []types.TransportPort{
+		types.TransportPort{Proto: types.TCP, Port: uint16(5000)},
+		types.TransportPort{Proto: types.UDP, Port: uint16(400)},
+		types.TransportPort{Proto: types.TCP, Port: uint16(600)},
+	}
+}
+
+func getPortMapping() []types.PortBinding {
+	return []types.PortBinding{
+		types.PortBinding{Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
+		types.PortBinding{Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
+		types.PortBinding{Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
+	}
+}
+
 func TestMain(m *testing.M) {
 	if reexec.Init() {
 		return
@@ -214,15 +225,11 @@ func TestCreateDeleteNetwork(t *testing.T) {
 		t.Fatalf("Expected StatusBadRequest status code, got: %v", errRsp)
 	}
 
-	ops := options.Generic{
-		netlabel.EnableIPv6: true,
-		netlabel.GenericData: map[string]string{
-			"BridgeName":  "abc",
-			"FixedCIDRv6": "fe80::1/64",
-			"AddressIP":   "172.28.30.254/24",
-		},
+	ops := []string{
+		bridge.BridgeName + "=abc",
+		netlabel.EnableIPv6 + "=true",
 	}
-	nc := networkCreate{Name: "network_1", NetworkType: bridgeNetType, Options: ops}
+	nc := networkCreate{Name: "network_1", NetworkType: bridgeNetType, Labels: ops}
 	goodBody, err := json.Marshal(nc)
 	if err != nil {
 		t.Fatal(err)
@@ -250,6 +257,29 @@ func TestCreateDeleteNetwork(t *testing.T) {
 	if errRsp != &successResponse {
 		t.Fatalf("Unexepected failure: %v", errRsp)
 	}
+
+	// Create with labels
+	labels := []string{
+		netlabel.EnableIPv6 + "=true",
+		bridge.BridgeName + "=abc",
+	}
+	nc = networkCreate{Name: "network_2", NetworkType: bridgeNetType, Labels: labels}
+	goodBody, err = json.Marshal(nc)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	_, errRsp = procCreateNetwork(c, vars, goodBody)
+	if errRsp != &createdResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
+	vars[urlNwName] = "network_2"
+	_, errRsp = procDeleteNetwork(c, vars, nil)
+	if errRsp != &successResponse {
+		t.Fatalf("Unexepected failure: %v", errRsp)
+	}
+
 }
 
 func TestGetNetworksAndEndpoints(t *testing.T) {
@@ -264,13 +294,10 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
 	}
 	defer c.Stop()
 
-	ops := options.Generic{
-		netlabel.GenericData: map[string]string{
-			"BridgeName": "api_test_nw",
-		},
+	ops := []string{
+		bridge.BridgeName + "=api_test_nw",
 	}
-
-	nc := networkCreate{Name: "sh", NetworkType: bridgeNetType, Options: ops}
+	nc := networkCreate{Name: "sh", NetworkType: bridgeNetType, Labels: ops}
 	body, err := json.Marshal(nc)
 	if err != nil {
 		t.Fatal(err)
@@ -287,17 +314,9 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
 	}
 
 	ec1 := endpointCreate{
-		Name: "ep1",
-		ExposedPorts: []types.TransportPort{
-			types.TransportPort{Proto: types.TCP, Port: uint16(5000)},
-			types.TransportPort{Proto: types.UDP, Port: uint16(400)},
-			types.TransportPort{Proto: types.TCP, Port: uint16(600)},
-		},
-		PortMapping: []types.PortBinding{
-			types.PortBinding{Proto: types.TCP, Port: uint16(230), HostPort: uint16(23000)},
-			types.PortBinding{Proto: types.UDP, Port: uint16(200), HostPort: uint16(22000)},
-			types.PortBinding{Proto: types.TCP, Port: uint16(120), HostPort: uint16(12000)},
-		},
+		Name:         "ep1",
+		ExposedPorts: getExposedPorts(),
+		PortMapping:  getPortMapping(),
 	}
 	b1, err := json.Marshal(ec1)
 	if err != nil {
@@ -439,10 +458,10 @@ func TestGetNetworksAndEndpoints(t *testing.T) {
 	nr1 := i2n(inr1)
 
 	delete(vars, urlNwName)
-	vars[urlNwID] = "cacca"
+	vars[urlNwID] = "acacac"
 	_, errRsp = procGetNetwork(c, vars, nil)
 	if errRsp == &successResponse {
-		t.Fatalf("Unexepected failure: %v", errRsp)
+		t.Fatalf("Expected failure. Got: %v", errRsp)
 	}
 	vars[urlNwID] = nid
 	inr2, errRsp := procGetNetwork(c, vars, nil)
@@ -825,18 +844,10 @@ func TestProcPublishUnpublishService(t *testing.T) {
 	}
 
 	sp := servicePublish{
-		Name:    "web",
-		Network: "network",
-		ExposedPorts: []types.TransportPort{
-			types.TransportPort{Proto: types.TCP, Port: uint16(6000)},
-			types.TransportPort{Proto: types.UDP, Port: uint16(500)},
-			types.TransportPort{Proto: types.TCP, Port: uint16(700)},
-		},
-		PortMapping: []types.PortBinding{
-			types.PortBinding{Proto: types.TCP, Port: uint16(1230), HostPort: uint16(37000)},
-			types.PortBinding{Proto: types.UDP, Port: uint16(1200), HostPort: uint16(36000)},
-			types.PortBinding{Proto: types.TCP, Port: uint16(1120), HostPort: uint16(35000)},
-		},
+		Name:         "web",
+		Network:      "network",
+		ExposedPorts: getExposedPorts(),
+		PortMapping:  getPortMapping(),
 	}
 	b, err = json.Marshal(sp)
 	if err != nil {
@@ -1330,6 +1341,7 @@ func TestJoinLeave(t *testing.T) {
 		t.Fatalf("Expected failure, got: %v", errRsp)
 	}
 
+	// bad labels
 	vars[urlEpName] = "endpoint"
 	key, errRsp := procJoinEndpoint(c, vars, jlb)
 	if errRsp != &successResponse {
@@ -1818,21 +1830,14 @@ func TestEndToEnd(t *testing.T) {
 
 	handleRequest := NewHTTPHandler(c)
 
-	ops := options.Generic{
-		netlabel.EnableIPv6: true,
-		netlabel.GenericData: map[string]string{
-			"BridgeName":          "cdef",
-			"FixedCIDRv6":         "fe80:2000::1/64",
-			"EnableIPv6":          "true",
-			"Mtu":                 "1460",
-			"EnableIPTables":      "true",
-			"AddressIP":           "172.28.30.254/16",
-			"EnableUserlandProxy": "true",
-		},
+	ops := []string{
+		bridge.BridgeName + "=cdef",
+		netlabel.EnableIPv6 + "=true",
+		netlabel.DriverMTU + "=1460",
 	}
 
 	// Create network
-	nc := networkCreate{Name: "network-fiftyfive", NetworkType: bridgeNetType, Options: ops}
+	nc := networkCreate{Name: "network-fiftyfive", NetworkType: bridgeNetType, Labels: ops}
 	body, err := json.Marshal(nc)
 	if err != nil {
 		t.Fatal(err)

+ 3 - 4
libnetwork/api/types.go

@@ -26,7 +26,6 @@ type sandboxResource struct {
 	ID          string `json:"id"`
 	Key         string `json:"key"`
 	ContainerID string `json:"container_id"`
-	// will add more fields once labels change is in
 }
 
 /***********
@@ -35,9 +34,9 @@ type sandboxResource struct {
 
 // networkCreate is the expected body of the "create network" http request message
 type networkCreate struct {
-	Name        string                 `json:"name"`
-	NetworkType string                 `json:"network_type"`
-	Options     map[string]interface{} `json:"options"`
+	Name        string   `json:"name"`
+	NetworkType string   `json:"network_type"`
+	Labels      []string `json:"labels"`
 }
 
 // endpointCreate represents the body of the "create endpoint" http request message

+ 2 - 2
libnetwork/client/network.go

@@ -48,8 +48,8 @@ func (cli *NetworkCli) CmdNetworkCreate(chain string, args ...string) error {
 	}
 
 	// Construct network create request body
-	ops := make(map[string]interface{})
-	nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, Options: ops}
+	var labels []string
+	nc := networkCreate{Name: cmd.Arg(0), NetworkType: *flDriver, Labels: labels}
 	obj, _, err := readBody(cli.call("POST", "/networks", nc, nil))
 	if err != nil {
 		return err

+ 3 - 3
libnetwork/client/types.go

@@ -34,9 +34,9 @@ type SandboxResource struct {
 
 // networkCreate is the expected body of the "create network" http request message
 type networkCreate struct {
-	Name        string                 `json:"name"`
-	NetworkType string                 `json:"network_type"`
-	Options     map[string]interface{} `json:"options"`
+	Name        string   `json:"name"`
+	NetworkType string   `json:"network_type"`
+	Labels      []string `json:"labels"`
 }
 
 // serviceCreate represents the body of the "publish service" http request message

+ 1 - 1
libnetwork/cmd/dnet/dnet.go

@@ -184,7 +184,7 @@ func createDefaultNetwork(c libnetwork.NetworkController) {
 	if nw != "" && d != "" {
 		// Bridge driver is special due to legacy reasons
 		if d == "bridge" {
-			genericOption[netlabel.GenericData] = map[string]interface{}{
+			genericOption[netlabel.GenericData] = map[string]string{
 				"BridgeName":    "docker0",
 				"DefaultBridge": "true",
 			}

+ 36 - 67
libnetwork/drivers/bridge/bridge.go

@@ -178,7 +178,7 @@ func (c *networkConfiguration) Conflicts(o *networkConfiguration) error {
 
 	// Also empty, becasue only one network with empty name is allowed
 	if c.BridgeName == o.BridgeName {
-		return fmt.Errorf("networks have same name")
+		return fmt.Errorf("networks have same bridge name")
 	}
 
 	// They must be in different subnets
@@ -196,79 +196,46 @@ func (c *networkConfiguration) Conflicts(o *networkConfiguration) error {
 	return nil
 }
 
-// fromMap retrieve the configuration data from the map form.
-func (c *networkConfiguration) fromMap(data map[string]interface{}) error {
+func (c *networkConfiguration) fromLabels(labels map[string]string) error {
 	var err error
-
-	if i, ok := data["BridgeName"]; ok && i != nil {
-		if c.BridgeName, ok = i.(string); !ok {
-			return types.BadRequestErrorf("invalid type for BridgeName value")
-		}
-	}
-
-	if i, ok := data["Mtu"]; ok && i != nil {
-		if s, ok := i.(string); ok {
-			if c.Mtu, err = strconv.Atoi(s); err != nil {
-				return types.BadRequestErrorf("failed to parse Mtu value: %s", err.Error())
+	for label, value := range labels {
+		switch label {
+		case BridgeName:
+			c.BridgeName = value
+		case netlabel.DriverMTU:
+			if c.Mtu, err = strconv.Atoi(value); err != nil {
+				return parseErr(label, value, err.Error())
 			}
-		} else {
-			return types.BadRequestErrorf("invalid type for Mtu value")
-		}
-	}
-
-	if i, ok := data["EnableIPv6"]; ok && i != nil {
-		if s, ok := i.(string); ok {
-			if c.EnableIPv6, err = strconv.ParseBool(s); err != nil {
-				return types.BadRequestErrorf("failed to parse EnableIPv6 value: %s", err.Error())
+		case netlabel.EnableIPv6:
+			if c.EnableIPv6, err = strconv.ParseBool(value); err != nil {
+				return parseErr(label, value, err.Error())
 			}
-		} else {
-			return types.BadRequestErrorf("invalid type for EnableIPv6 value")
-		}
-	}
-
-	if i, ok := data["EnableIPMasquerade"]; ok && i != nil {
-		if s, ok := i.(string); ok {
-			if c.EnableIPMasquerade, err = strconv.ParseBool(s); err != nil {
-				return types.BadRequestErrorf("failed to parse EnableIPMasquerade value: %s", err.Error())
+		case EnableIPMasquerade:
+			if c.EnableIPMasquerade, err = strconv.ParseBool(value); err != nil {
+				return parseErr(label, value, err.Error())
 			}
-		} else {
-			return types.BadRequestErrorf("invalid type for EnableIPMasquerade value")
-		}
-	}
-
-	if i, ok := data["EnableICC"]; ok && i != nil {
-		if s, ok := i.(string); ok {
-			if c.EnableICC, err = strconv.ParseBool(s); err != nil {
-				return types.BadRequestErrorf("failed to parse EnableICC value: %s", err.Error())
+		case EnableICC:
+			if c.EnableICC, err = strconv.ParseBool(value); err != nil {
+				return parseErr(label, value, err.Error())
 			}
-		} else {
-			return types.BadRequestErrorf("invalid type for EnableICC value")
-		}
-	}
-
-	if i, ok := data["DefaultBridge"]; ok && i != nil {
-		if s, ok := i.(string); ok {
-			if c.DefaultBridge, err = strconv.ParseBool(s); err != nil {
-				return types.BadRequestErrorf("failed to parse DefaultBridge value: %s", err.Error())
+		case DefaultBridge:
+			if c.DefaultBridge, err = strconv.ParseBool(value); err != nil {
+				return parseErr(label, value, err.Error())
 			}
-		} else {
-			return types.BadRequestErrorf("invalid type for DefaultBridge value")
-		}
-	}
-
-	if i, ok := data["DefaultBindingIP"]; ok && i != nil {
-		if s, ok := i.(string); ok {
-			if c.DefaultBindingIP = net.ParseIP(s); c.DefaultBindingIP == nil {
-				return types.BadRequestErrorf("failed to parse DefaultBindingIP value")
+		case DefaultBindingIP:
+			if c.DefaultBindingIP = net.ParseIP(value); c.DefaultBindingIP == nil {
+				return parseErr(label, value, "nil ip")
 			}
-		} else {
-			return types.BadRequestErrorf("invalid type for DefaultBindingIP value")
 		}
 	}
 
 	return nil
 }
 
+func parseErr(label, value, errString string) error {
+	return types.BadRequestErrorf("failed to parse %s value: %v (%s)", label, value, errString)
+}
+
 func (n *bridgeNetwork) getDriverChains() (*iptables.ChainInfo, *iptables.ChainInfo, error) {
 	n.Lock()
 	defer n.Unlock()
@@ -442,12 +409,12 @@ func parseNetworkGenericOptions(data interface{}) (*networkConfiguration, error)
 	switch opt := data.(type) {
 	case *networkConfiguration:
 		config = opt
-	case map[string]interface{}:
+	case map[string]string:
 		config = &networkConfiguration{
 			EnableICC:          true,
 			EnableIPMasquerade: true,
 		}
-		err = config.fromMap(opt)
+		err = config.fromLabels(opt)
 	case options.Generic:
 		var opaqueConfig interface{}
 		if opaqueConfig, err = options.GenerateFromModel(opt, config); err == nil {
@@ -491,8 +458,10 @@ func (c *networkConfiguration) processIPAM(id string, ipamV4Data, ipamV6Data []d
 }
 
 func parseNetworkOptions(id string, option options.Generic) (*networkConfiguration, error) {
-	var err error
-	config := &networkConfiguration{}
+	var (
+		err    error
+		config = &networkConfiguration{}
+	)
 
 	// Parse generic label first, config will be re-assigned
 	if genData, ok := option[netlabel.GenericData]; ok && genData != nil {
@@ -502,8 +471,8 @@ func parseNetworkOptions(id string, option options.Generic) (*networkConfigurati
 	}
 
 	// Process well-known labels next
-	if _, ok := option[netlabel.EnableIPv6]; ok {
-		config.EnableIPv6 = option[netlabel.EnableIPv6].(bool)
+	if val, ok := option[netlabel.EnableIPv6]; ok {
+		config.EnableIPv6 = val.(bool)
 	}
 
 	// Finally validate the configuration

+ 52 - 0
libnetwork/drivers/bridge/bridge_test.go

@@ -97,6 +97,58 @@ func TestCreateNoConfig(t *testing.T) {
 	}
 }
 
+func TestCreateFullOptionsLabels(t *testing.T) {
+	defer testutils.SetupTestOSContext(t)()
+	d := newDriver()
+
+	config := &configuration{
+		EnableIPForwarding: true,
+	}
+	genericOption := make(map[string]interface{})
+	genericOption[netlabel.GenericData] = config
+
+	if err := d.configure(genericOption); err != nil {
+		t.Fatalf("Failed to setup driver config: %v", err)
+	}
+
+	labels := map[string]string{
+		BridgeName:          "cu",
+		netlabel.EnableIPv6: "true",
+		EnableICC:           "true",
+		EnableIPMasquerade:  "true",
+		DefaultBindingIP:    "127.0.0.1",
+	}
+
+	netOption := make(map[string]interface{})
+	netOption[netlabel.GenericData] = labels
+
+	err := d.CreateNetwork("dummy", netOption, getIPv4Data(t), nil)
+	if err != nil {
+		t.Fatalf("Failed to create bridge: %v", err)
+	}
+
+	nw, ok := d.networks["dummy"]
+	if !ok {
+		t.Fatalf("Cannot find dummy network in bridge driver")
+	}
+
+	if nw.config.BridgeName != "cu" {
+		t.Fatalf("incongruent name in bridge network")
+	}
+
+	if !nw.config.EnableIPv6 {
+		t.Fatalf("incongruent EnableIPv6 in bridge network")
+	}
+
+	if !nw.config.EnableICC {
+		t.Fatalf("incongruent EnableICC in bridge network")
+	}
+
+	if !nw.config.EnableIPMasquerade {
+		t.Fatalf("incongruent EnableIPMasquerade in bridge network")
+	}
+}
+
 func TestCreate(t *testing.T) {
 	defer testutils.SetupTestOSContext(t)()
 	d := newDriver()

+ 18 - 0
libnetwork/drivers/bridge/labels.go

@@ -0,0 +1,18 @@
+package bridge
+
+const (
+	// BridgeName label for bridge driver
+	BridgeName = "com.docker.network.bridge.name"
+
+	// EnableIPMasquerade label for bridge driver
+	EnableIPMasquerade = "com.docker.network.bridge.enable_ip_masquerade"
+
+	// EnableICC label
+	EnableICC = "com.docker.network.bridge.enable_icc"
+
+	// DefaultBindingIP label
+	DefaultBindingIP = "com.docker.network.bridge.host_binding_ipv4"
+
+	// DefaultBridge label
+	DefaultBridge = "com.docker.network.bridge.default_bridge"
+)

+ 50 - 9
libnetwork/netlabel/labels.go

@@ -1,6 +1,8 @@
 package netlabel
 
-import "strings"
+import (
+	"strings"
+)
 
 const (
 	// Prefix constant marks the reserved label space for libnetwork
@@ -22,12 +24,15 @@ const (
 	// MacAddress constant represents Mac Address config of a Container
 	MacAddress = Prefix + ".endpoint.macaddress"
 
-	// ExposedPorts constant represents exposedports of a Container
+	// ExposedPorts constant represents the container's Exposed Ports
 	ExposedPorts = Prefix + ".endpoint.exposedports"
 
 	//EnableIPv6 constant represents enabling IPV6 at network level
 	EnableIPv6 = Prefix + ".enable_ipv6"
 
+	// DriverMTU constant represents the MTU size for the network driver
+	DriverMTU = DriverPrefix + ".mtu"
+
 	// OverlayBindInterface constant represents overlay driver bind interface
 	OverlayBindInterface = DriverPrefix + ".overlay.bind_interface"
 
@@ -74,15 +79,51 @@ func MakeKVProviderConfig(scope string) string {
 }
 
 // Key extracts the key portion of the label
-func Key(label string) string {
-	kv := strings.SplitN(label, "=", 2)
-
-	return kv[0]
+func Key(label string) (key string) {
+	if kv := strings.SplitN(label, "=", 2); len(kv) > 0 {
+		key = kv[0]
+	}
+	return
 }
 
 // Value extracts the value portion of the label
-func Value(label string) string {
-	kv := strings.SplitN(label, "=", 2)
+func Value(label string) (value string) {
+	if kv := strings.SplitN(label, "=", 2); len(kv) > 1 {
+		value = kv[1]
+	}
+	return
+}
+
+// KeyValue decomposes the label in the (key,value) pair
+func KeyValue(label string) (key string, value string) {
+	if kv := strings.SplitN(label, "=", 2); len(kv) > 0 {
+		key = kv[0]
+		if len(kv) > 1 {
+			value = kv[1]
+		}
+	}
+	return
+}
+
+// ToMap converts a list of labels in a map of (key,value) pairs
+func ToMap(labels []string) map[string]string {
+	m := make(map[string]string, len(labels))
+	for _, l := range labels {
+		k, v := KeyValue(l)
+		m[k] = v
+	}
+	return m
+}
 
-	return kv[1]
+// FromMap converts a map of (key,value) pairs in a lsit of labels
+func FromMap(m map[string]string) []string {
+	l := make([]string, 0, len(m))
+	for k, v := range m {
+		s := k
+		if v != "" {
+			s = s + "=" + v
+		}
+		l = append(l, s)
+	}
+	return l
 }

+ 77 - 0
libnetwork/netlabel/labels_test.go

@@ -0,0 +1,77 @@
+package netlabel
+
+import (
+	"testing"
+
+	_ "github.com/docker/libnetwork/testutils"
+)
+
+var input = []struct {
+	label string
+	key   string
+	value string
+}{
+	{"com.directory.person.name=joe", "com.directory.person.name", "joe"},
+	{"com.directory.person.age=24", "com.directory.person.age", "24"},
+	{"com.directory.person.address=1234 First st.", "com.directory.person.address", "1234 First st."},
+	{"com.directory.person.friends=", "com.directory.person.friends", ""},
+	{"com.directory.person.nickname=o=u=8", "com.directory.person.nickname", "o=u=8"},
+	{"", "", ""},
+	{"com.directory.person.student", "com.directory.person.student", ""},
+}
+
+func TestKeyValue(t *testing.T) {
+	for _, i := range input {
+		k, v := KeyValue(i.label)
+		if k != i.key || v != i.value {
+			t.Fatalf("unexpected: %s, %s", k, v)
+		}
+	}
+}
+
+func TestToMap(t *testing.T) {
+	lista := make([]string, len(input))
+	for ind, i := range input {
+		lista[ind] = i.label
+	}
+
+	mappa := ToMap(lista)
+
+	if len(mappa) != len(lista) {
+		t.Fatalf("Incorrect map length. Expected %d. Got %d", len(lista), len(mappa))
+	}
+
+	for _, i := range input {
+		if v, ok := mappa[i.key]; !ok || v != i.value {
+			t.Fatalf("Cannot find key or value for key: %s", i.key)
+		}
+	}
+}
+
+func TestFromMap(t *testing.T) {
+	var m map[string]string
+	lbls := FromMap(m)
+	if len(lbls) != 0 {
+		t.Fatalf("unexpected lbls length")
+	}
+
+	m = make(map[string]string, 3)
+	m["peso"] = "85"
+	m["statura"] = "170"
+	m["maschio"] = ""
+
+	lbls = FromMap(m)
+	if len(lbls) != 3 {
+		t.Fatalf("unexpected lbls length")
+	}
+
+	for _, l := range lbls {
+		switch l {
+		case "peso=85":
+		case "statura=170":
+		case "maschio":
+		default:
+			t.Fatalf("unexpected label: %s", l)
+		}
+	}
+}

+ 60 - 2
libnetwork/network.go

@@ -4,6 +4,7 @@ import (
 	"encoding/json"
 	"fmt"
 	"net"
+	"strconv"
 	"sync"
 
 	log "github.com/Sirupsen/logrus"
@@ -49,6 +50,15 @@ type Network interface {
 
 	// EndpointByID returns the Endpoint which has the passed id. If not found, the error ErrNoSuchEndpoint is returned.
 	EndpointByID(id string) (Endpoint, error)
+
+	// Return certain operational data belonging to this network
+	Info() NetworkInfo
+}
+
+// NetworkInfo returns operational information about the network
+type NetworkInfo interface {
+	Labels() []string
+	Scope() string
 }
 
 // EndpointWalker is a client provided function which will be used to walk the Endpoints.
@@ -150,7 +160,6 @@ type network struct {
 	dbExists     bool
 	persist      bool
 	stopWatchCh  chan struct{}
-	scope        string
 	drvOnce      *sync.Once
 	sync.Mutex
 }
@@ -382,6 +391,18 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
 
 	if v, ok := netMap["generic"]; ok {
 		n.generic = v.(map[string]interface{})
+		// Restore labels in their map[string]string form
+		if v, ok := n.generic[netlabel.GenericData]; ok {
+			var lmap map[string]string
+			ba, err := json.Marshal(v)
+			if err != nil {
+				return err
+			}
+			if err := json.Unmarshal(ba, &lmap); err != nil {
+				return err
+			}
+			n.generic[netlabel.GenericData] = lmap
+		}
 	}
 	if v, ok := netMap["persist"]; ok {
 		n.persist = v.(bool)
@@ -391,7 +412,6 @@ func (n *network) UnmarshalJSON(b []byte) (err error) {
 	} else {
 		n.ipamType = ipamapi.DefaultIPAM
 	}
-
 	if v, ok := netMap["addrSpace"]; ok {
 		n.addrSpace = v.(string)
 	}
@@ -451,6 +471,25 @@ func NetworkOptionIpam(ipamDriver string, addrSpace string, ipV4 []*IpamConf, ip
 	}
 }
 
+// NetworkOptionLabels function returns an option setter for any parameter described by a map
+func NetworkOptionLabels(labels []string) NetworkOption {
+	return func(n *network) {
+		if n.generic == nil {
+			n.generic = make(map[string]interface{})
+		}
+		opts := netlabel.ToMap(labels)
+		// Store the options
+		n.generic[netlabel.GenericData] = opts
+		// Decode and store the endpoint options of libnetwork interest
+		if val, ok := opts[netlabel.EnableIPv6]; ok {
+			var err error
+			if n.enableIPv6, err = strconv.ParseBool(val); err != nil {
+				log.Warnf("Failed to parse %s' value: %s (%s)", netlabel.EnableIPv6, val, err.Error())
+			}
+		}
+	}
+}
+
 func (n *network) processOptions(options ...NetworkOption) {
 	for _, opt := range options {
 		if opt != nil {
@@ -1003,3 +1042,22 @@ func (n *network) deriveAddressSpace() (string, error) {
 	}
 	return ipd.defaultLocalAddressSpace, nil
 }
+
+func (n *network) Info() NetworkInfo {
+	return n
+}
+
+func (n *network) Labels() []string {
+	n.Lock()
+	defer n.Unlock()
+	if n.generic != nil {
+		if m, ok := n.generic[netlabel.GenericData]; ok {
+			return netlabel.FromMap(m.(map[string]string))
+		}
+	}
+	return []string{}
+}
+
+func (n *network) Scope() string {
+	return n.driverScope()
+}

+ 92 - 0
libnetwork/types/types.go

@@ -5,6 +5,7 @@ import (
 	"bytes"
 	"fmt"
 	"net"
+	"strconv"
 	"strings"
 )
 
@@ -17,11 +18,46 @@ type TransportPort struct {
 	Port  uint16
 }
 
+// Equal checks if this instance of Transportport is equal to the passed one
+func (t *TransportPort) Equal(o *TransportPort) bool {
+	if t == o {
+		return true
+	}
+
+	if o == nil {
+		return false
+	}
+
+	if t.Proto != o.Proto || t.Port != o.Port {
+		return false
+	}
+
+	return true
+}
+
 // GetCopy returns a copy of this TransportPort structure instance
 func (t *TransportPort) GetCopy() TransportPort {
 	return TransportPort{Proto: t.Proto, Port: t.Port}
 }
 
+// String returns the TransportPort structure in string form
+func (t *TransportPort) String() string {
+	return fmt.Sprintf("%s/%d", t.Proto.String(), t.Port)
+}
+
+// FromString reads the TransportPort structure from string
+func (t *TransportPort) FromString(s string) error {
+	ps := strings.Split(s, "/")
+	if len(ps) == 2 {
+		t.Proto = ParseProtocol(ps[0])
+		if p, err := strconv.ParseUint(ps[1], 10, 16); err == nil {
+			t.Port = uint16(p)
+			return nil
+		}
+	}
+	return BadRequestErrorf("invalid format for transport port: %s", s)
+}
+
 // PortBinding represent a port binding between the container and the host
 type PortBinding struct {
 	Proto       Protocol
@@ -68,6 +104,62 @@ func (p *PortBinding) GetCopy() PortBinding {
 	}
 }
 
+// String return the PortBinding structure in string form
+func (p *PortBinding) String() string {
+	ret := fmt.Sprintf("%s/", p.Proto)
+	if p.IP != nil {
+		ret = fmt.Sprintf("%s%s", ret, p.IP.String())
+	}
+	ret = fmt.Sprintf("%s:%d/", ret, p.Port)
+	if p.HostIP != nil {
+		ret = fmt.Sprintf("%s%s", ret, p.HostIP.String())
+	}
+	ret = fmt.Sprintf("%s:%d", ret, p.HostPort)
+	return ret
+}
+
+// FromString reads the TransportPort structure from string
+func (p *PortBinding) FromString(s string) error {
+	ps := strings.Split(s, "/")
+	if len(ps) != 3 {
+		return BadRequestErrorf("invalid format for port binding: %s", s)
+	}
+
+	p.Proto = ParseProtocol(ps[0])
+
+	var err error
+	if p.IP, p.Port, err = parseIPPort(ps[1]); err != nil {
+		return BadRequestErrorf("failed to parse Container IP/Port in port binding: %s", err.Error())
+	}
+
+	if p.HostIP, p.HostPort, err = parseIPPort(ps[2]); err != nil {
+		return BadRequestErrorf("failed to parse Host IP/Port in port binding: %s", err.Error())
+	}
+
+	return nil
+}
+
+func parseIPPort(s string) (net.IP, uint16, error) {
+	pp := strings.Split(s, ":")
+	if len(pp) != 2 {
+		return nil, 0, BadRequestErrorf("invalid format: %s", s)
+	}
+
+	var ip net.IP
+	if pp[0] != "" {
+		if ip = net.ParseIP(pp[0]); ip == nil {
+			return nil, 0, BadRequestErrorf("invalid ip: %s", pp[0])
+		}
+	}
+
+	port, err := strconv.ParseUint(pp[1], 10, 16)
+	if err != nil {
+		return nil, 0, BadRequestErrorf("invalid port: %s", pp[1])
+	}
+
+	return ip, uint16(port), nil
+}
+
 // Equal checks if this instance of PortBinding is equal to the passed one
 func (p *PortBinding) Equal(o *PortBinding) bool {
 	if p == o {

+ 36 - 0
libnetwork/types/types_test.go

@@ -8,6 +8,42 @@ import (
 
 var runningInContainer = flag.Bool("incontainer", false, "Indicates if the test is running in a container")
 
+func TestTransportPortConv(t *testing.T) {
+	sform := "tcp/23"
+	tp := &TransportPort{Proto: TCP, Port: uint16(23)}
+
+	if sform != tp.String() {
+		t.Fatalf("String() method failed")
+	}
+
+	rc := new(TransportPort)
+	if err := rc.FromString(sform); err != nil {
+		t.Fatal(err)
+	}
+	if !tp.Equal(rc) {
+		t.Fatalf("FromString() method failed")
+	}
+}
+
+func TestTransportPortBindingConv(t *testing.T) {
+	sform := "tcp/172.28.30.23:80/112.0.43.56:8001"
+	pb := &PortBinding{
+		Proto:    TCP,
+		IP:       net.IPv4(172, 28, 30, 23),
+		Port:     uint16(80),
+		HostIP:   net.IPv4(112, 0, 43, 56),
+		HostPort: uint16(8001),
+	}
+
+	rc := new(PortBinding)
+	if err := rc.FromString(sform); err != nil {
+		t.Fatal(err)
+	}
+	if !pb.Equal(rc) {
+		t.Fatalf("FromString() method failed")
+	}
+}
+
 func TestErrorConstructors(t *testing.T) {
 	var err error