github.com/hamo/docker@v1.11.1/api/client/network.go (about)

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