github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/api/client/network.go (about)

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