github.com/ncdc/docker@v0.10.1-0.20160129113957-6c6729ef5b74/api/client/network.go (about)

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