github.com/gondor/docker@v1.9.0-rc1/api/client/network.go (about)

     1  package client
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"net"
     9  	"strings"
    10  	"text/tabwriter"
    11  
    12  	"github.com/docker/docker/api/types"
    13  	Cli "github.com/docker/docker/cli"
    14  	"github.com/docker/docker/daemon/network"
    15  	"github.com/docker/docker/opts"
    16  	flag "github.com/docker/docker/pkg/mflag"
    17  	"github.com/docker/docker/pkg/stringid"
    18  )
    19  
    20  // CmdNetwork is the parent subcommand for all network commands
    21  //
    22  // Usage: docker network <COMMAND> [OPTIONS]
    23  func (cli *DockerCli) CmdNetwork(args ...string) error {
    24  	cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false)
    25  	cmd.Require(flag.Min, 1)
    26  	err := cmd.ParseFlags(args, true)
    27  	cmd.Usage()
    28  	return err
    29  }
    30  
    31  // CmdNetworkCreate creates a new network with a given name
    32  //
    33  // Usage: docker network create [OPTIONS] <NETWORK-NAME>
    34  func (cli *DockerCli) CmdNetworkCreate(args ...string) error {
    35  	cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false)
    36  	flDriver := cmd.String([]string{"d", "-driver"}, "bridge", "Driver to manage the Network")
    37  
    38  	flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver")
    39  	flIpamSubnet := opts.NewListOpts(nil)
    40  	flIpamIPRange := opts.NewListOpts(nil)
    41  	flIpamGateway := opts.NewListOpts(nil)
    42  	flIpamAux := opts.NewMapOpts(nil, nil)
    43  
    44  	cmd.Var(&flIpamSubnet, []string{"-subnet"}, "Subnet in CIDR format that represents a network segment")
    45  	cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range")
    46  	cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet")
    47  	cmd.Var(flIpamAux, []string{"-aux-address"}, "Auxiliary ipv4 or ipv6 addresses used by network driver")
    48  
    49  	cmd.Require(flag.Exact, 1)
    50  	err := cmd.ParseFlags(args, true)
    51  	if err != nil {
    52  		return err
    53  	}
    54  
    55  	ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	// Construct network create request body
    61  	nc := types.NetworkCreate{
    62  		Name:           cmd.Arg(0),
    63  		Driver:         *flDriver,
    64  		IPAM:           network.IPAM{Driver: *flIpamDriver, Config: ipamCfg},
    65  		CheckDuplicate: true,
    66  	}
    67  	obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil))
    68  	if err != nil {
    69  		return err
    70  	}
    71  	var resp types.NetworkCreateResponse
    72  	err = json.Unmarshal(obj, &resp)
    73  	if err != nil {
    74  		return err
    75  	}
    76  	fmt.Fprintf(cli.out, "%s\n", resp.ID)
    77  	return nil
    78  }
    79  
    80  // CmdNetworkRm deletes a network
    81  //
    82  // Usage: docker network rm <NETWORK-NAME | NETWORK-ID>
    83  func (cli *DockerCli) CmdNetworkRm(args ...string) error {
    84  	cmd := Cli.Subcmd("network rm", []string{"NETWORK"}, "Deletes a network", false)
    85  	cmd.Require(flag.Exact, 1)
    86  	err := cmd.ParseFlags(args, true)
    87  	if err != nil {
    88  		return err
    89  	}
    90  	_, _, err = readBody(cli.call("DELETE", "/networks/"+cmd.Arg(0), nil, nil))
    91  	if err != nil {
    92  		return err
    93  	}
    94  	return nil
    95  }
    96  
    97  // CmdNetworkConnect connects a container to a network
    98  //
    99  // Usage: docker network connect <NETWORK> <CONTAINER>
   100  func (cli *DockerCli) CmdNetworkConnect(args ...string) error {
   101  	cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false)
   102  	cmd.Require(flag.Exact, 2)
   103  	err := cmd.ParseFlags(args, true)
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	nc := types.NetworkConnect{Container: cmd.Arg(1)}
   109  	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/connect", nc, nil))
   110  	return err
   111  }
   112  
   113  // CmdNetworkDisconnect disconnects a container from a network
   114  //
   115  // Usage: docker network disconnect <NETWORK> <CONTAINER>
   116  func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
   117  	cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
   118  	cmd.Require(flag.Exact, 2)
   119  	err := cmd.ParseFlags(args, true)
   120  	if err != nil {
   121  		return err
   122  	}
   123  
   124  	nc := types.NetworkConnect{Container: cmd.Arg(1)}
   125  	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/disconnect", nc, nil))
   126  	return err
   127  }
   128  
   129  // CmdNetworkLs lists all the netorks managed by docker daemon
   130  //
   131  // Usage: docker network ls [OPTIONS]
   132  func (cli *DockerCli) CmdNetworkLs(args ...string) error {
   133  	cmd := Cli.Subcmd("network ls", nil, "Lists networks", true)
   134  	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
   135  	noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")
   136  
   137  	cmd.Require(flag.Exact, 0)
   138  	err := cmd.ParseFlags(args, true)
   139  
   140  	if err != nil {
   141  		return err
   142  	}
   143  	obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
   144  	if err != nil {
   145  		return err
   146  	}
   147  
   148  	var networkResources []types.NetworkResource
   149  	err = json.Unmarshal(obj, &networkResources)
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
   155  
   156  	// unless quiet (-q) is specified, print field titles
   157  	if !*quiet {
   158  		fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER")
   159  	}
   160  
   161  	for _, networkResource := range networkResources {
   162  		ID := networkResource.ID
   163  		netName := networkResource.Name
   164  		if !*noTrunc {
   165  			ID = stringid.TruncateID(ID)
   166  		}
   167  		if *quiet {
   168  			fmt.Fprintln(wr, ID)
   169  			continue
   170  		}
   171  		driver := networkResource.Driver
   172  		fmt.Fprintf(wr, "%s\t%s\t%s\t",
   173  			ID,
   174  			netName,
   175  			driver)
   176  		fmt.Fprint(wr, "\n")
   177  	}
   178  	wr.Flush()
   179  	return nil
   180  }
   181  
   182  // CmdNetworkInspect inspects the network object for more details
   183  //
   184  // Usage: docker network inspect <NETWORK>
   185  // CmdNetworkInspect handles Network inspect UI
   186  func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
   187  	cmd := Cli.Subcmd("network inspect", []string{"NETWORK"}, "Displays detailed information on a network", false)
   188  	cmd.Require(flag.Exact, 1)
   189  	err := cmd.ParseFlags(args, true)
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	obj, _, err := readBody(cli.call("GET", "/networks/"+cmd.Arg(0), nil, nil))
   195  	if err != nil {
   196  		return err
   197  	}
   198  	networkResource := &types.NetworkResource{}
   199  	if err := json.NewDecoder(bytes.NewReader(obj)).Decode(networkResource); err != nil {
   200  		return err
   201  	}
   202  
   203  	indented := new(bytes.Buffer)
   204  	if err := json.Indent(indented, obj, "", "    "); err != nil {
   205  		return err
   206  	}
   207  	if _, err := io.Copy(cli.out, indented); err != nil {
   208  		return err
   209  	}
   210  	return nil
   211  }
   212  
   213  // Consolidates the ipam configuration as a group from differnt related configurations
   214  // user can configure network with multiple non-overlapping subnets and hence it is
   215  // possible to corelate the various related parameters and consolidate them.
   216  // consoidateIpam consolidates subnets, ip-ranges, gateways and auxilary addresses into
   217  // structured ipam data.
   218  func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
   219  	if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
   220  		return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
   221  	}
   222  	iData := map[string]*network.IPAMConfig{}
   223  
   224  	// Populate non-overlapping subnets into consolidation map
   225  	for _, s := range subnets {
   226  		for k := range iData {
   227  			ok1, err := subnetMatches(s, k)
   228  			if err != nil {
   229  				return nil, err
   230  			}
   231  			ok2, err := subnetMatches(k, s)
   232  			if err != nil {
   233  				return nil, err
   234  			}
   235  			if ok1 || ok2 {
   236  				return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
   237  			}
   238  		}
   239  		iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
   240  	}
   241  
   242  	// Validate and add valid ip ranges
   243  	for _, r := range ranges {
   244  		match := false
   245  		for _, s := range subnets {
   246  			ok, err := subnetMatches(s, r)
   247  			if err != nil {
   248  				return nil, err
   249  			}
   250  			if !ok {
   251  				continue
   252  			}
   253  			if iData[s].IPRange != "" {
   254  				return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
   255  			}
   256  			d := iData[s]
   257  			d.IPRange = r
   258  			match = true
   259  		}
   260  		if !match {
   261  			return nil, fmt.Errorf("no matching subnet for range %s", r)
   262  		}
   263  	}
   264  
   265  	// Validate and add valid gateways
   266  	for _, g := range gateways {
   267  		match := false
   268  		for _, s := range subnets {
   269  			ok, err := subnetMatches(s, g)
   270  			if err != nil {
   271  				return nil, err
   272  			}
   273  			if !ok {
   274  				continue
   275  			}
   276  			if iData[s].Gateway != "" {
   277  				return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
   278  			}
   279  			d := iData[s]
   280  			d.Gateway = g
   281  			match = true
   282  		}
   283  		if !match {
   284  			return nil, fmt.Errorf("no matching subnet for gateway %s", g)
   285  		}
   286  	}
   287  
   288  	// Validate and add aux-addresses
   289  	for key, aa := range auxaddrs {
   290  		match := false
   291  		for _, s := range subnets {
   292  			ok, err := subnetMatches(s, aa)
   293  			if err != nil {
   294  				return nil, err
   295  			}
   296  			if !ok {
   297  				continue
   298  			}
   299  			iData[s].AuxAddress[key] = aa
   300  			match = true
   301  		}
   302  		if !match {
   303  			return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
   304  		}
   305  	}
   306  
   307  	idl := []network.IPAMConfig{}
   308  	for _, v := range iData {
   309  		idl = append(idl, *v)
   310  	}
   311  	return idl, nil
   312  }
   313  
   314  func subnetMatches(subnet, data string) (bool, error) {
   315  	var (
   316  		ip net.IP
   317  	)
   318  
   319  	_, s, err := net.ParseCIDR(subnet)
   320  	if err != nil {
   321  		return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
   322  	}
   323  
   324  	if strings.Contains(data, "/") {
   325  		ip, _, err = net.ParseCIDR(data)
   326  		if err != nil {
   327  			return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
   328  		}
   329  	} else {
   330  		ip = net.ParseIP(data)
   331  	}
   332  
   333  	return s.Contains(ip), nil
   334  }
   335  
   336  func networkUsage() string {
   337  	networkCommands := map[string]string{
   338  		"create":     "Create a network",
   339  		"connect":    "Connect container to a network",
   340  		"disconnect": "Disconnect container from a network",
   341  		"inspect":    "Display detailed network information",
   342  		"ls":         "List all networks",
   343  		"rm":         "Remove a network",
   344  	}
   345  
   346  	help := "Commands:\n"
   347  
   348  	for cmd, description := range networkCommands {
   349  		help += fmt.Sprintf("  %-25.25s%s\n", cmd, description)
   350  	}
   351  
   352  	help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
   353  	return help
   354  }