github.com/tompao/docker@v1.9.1/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  	flOpts := opts.NewMapOpts(nil, nil)
    38  
    39  	flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver")
    40  	flIpamSubnet := opts.NewListOpts(nil)
    41  	flIpamIPRange := opts.NewListOpts(nil)
    42  	flIpamGateway := opts.NewListOpts(nil)
    43  	flIpamAux := 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  
    51  	cmd.Require(flag.Exact, 1)
    52  	err := cmd.ParseFlags(args, true)
    53  	if err != nil {
    54  		return err
    55  	}
    56  
    57  	// Set the default driver to "" if the user didn't set the value.
    58  	// That way we can know whether it was user input or not.
    59  	driver := *flDriver
    60  	if !cmd.IsSet("-driver") && !cmd.IsSet("d") {
    61  		driver = ""
    62  	}
    63  
    64  	ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll())
    65  	if err != nil {
    66  		return err
    67  	}
    68  
    69  	// Construct network create request body
    70  	nc := types.NetworkCreate{
    71  		Name:           cmd.Arg(0),
    72  		Driver:         driver,
    73  		IPAM:           network.IPAM{Driver: *flIpamDriver, Config: ipamCfg},
    74  		Options:        flOpts.GetAll(),
    75  		CheckDuplicate: true,
    76  	}
    77  	obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil))
    78  	if err != nil {
    79  		return err
    80  	}
    81  	var resp types.NetworkCreateResponse
    82  	err = json.Unmarshal(obj, &resp)
    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 a network
    91  //
    92  // Usage: docker network rm <NETWORK-NAME | NETWORK-ID>
    93  func (cli *DockerCli) CmdNetworkRm(args ...string) error {
    94  	cmd := Cli.Subcmd("network rm", []string{"NETWORK"}, "Deletes a network", false)
    95  	cmd.Require(flag.Exact, 1)
    96  	err := cmd.ParseFlags(args, true)
    97  	if err != nil {
    98  		return err
    99  	}
   100  	_, _, err = readBody(cli.call("DELETE", "/networks/"+cmd.Arg(0), nil, nil))
   101  	if err != nil {
   102  		return err
   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  	err := cmd.ParseFlags(args, true)
   114  	if err != nil {
   115  		return err
   116  	}
   117  
   118  	nc := types.NetworkConnect{Container: cmd.Arg(1)}
   119  	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/connect", nc, nil))
   120  	return err
   121  }
   122  
   123  // CmdNetworkDisconnect disconnects a container from a network
   124  //
   125  // Usage: docker network disconnect <NETWORK> <CONTAINER>
   126  func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error {
   127  	cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false)
   128  	cmd.Require(flag.Exact, 2)
   129  	err := cmd.ParseFlags(args, true)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	nc := types.NetworkConnect{Container: cmd.Arg(1)}
   135  	_, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/disconnect", nc, nil))
   136  	return err
   137  }
   138  
   139  // CmdNetworkLs lists all the netorks managed by docker daemon
   140  //
   141  // Usage: docker network ls [OPTIONS]
   142  func (cli *DockerCli) CmdNetworkLs(args ...string) error {
   143  	cmd := Cli.Subcmd("network ls", nil, "Lists networks", true)
   144  	quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs")
   145  	noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output")
   146  
   147  	cmd.Require(flag.Exact, 0)
   148  	err := cmd.ParseFlags(args, true)
   149  
   150  	if err != nil {
   151  		return err
   152  	}
   153  	obj, _, err := readBody(cli.call("GET", "/networks", nil, nil))
   154  	if err != nil {
   155  		return err
   156  	}
   157  
   158  	var networkResources []types.NetworkResource
   159  	err = json.Unmarshal(obj, &networkResources)
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0)
   165  
   166  	// unless quiet (-q) is specified, print field titles
   167  	if !*quiet {
   168  		fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER")
   169  	}
   170  
   171  	for _, networkResource := range networkResources {
   172  		ID := networkResource.ID
   173  		netName := networkResource.Name
   174  		if !*noTrunc {
   175  			ID = stringid.TruncateID(ID)
   176  		}
   177  		if *quiet {
   178  			fmt.Fprintln(wr, ID)
   179  			continue
   180  		}
   181  		driver := networkResource.Driver
   182  		fmt.Fprintf(wr, "%s\t%s\t%s\t",
   183  			ID,
   184  			netName,
   185  			driver)
   186  		fmt.Fprint(wr, "\n")
   187  	}
   188  	wr.Flush()
   189  	return nil
   190  }
   191  
   192  // CmdNetworkInspect inspects the network object for more details
   193  //
   194  // Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...]
   195  func (cli *DockerCli) CmdNetworkInspect(args ...string) error {
   196  	cmd := Cli.Subcmd("network inspect", []string{"NETWORK [NETWORK...]"}, "Displays detailed information on a network", false)
   197  	cmd.Require(flag.Min, 1)
   198  	err := cmd.ParseFlags(args, true)
   199  	if err != nil {
   200  		return err
   201  	}
   202  
   203  	status := 0
   204  	var networks []*types.NetworkResource
   205  	for _, name := range cmd.Args() {
   206  		obj, _, err := readBody(cli.call("GET", "/networks/"+name, nil, nil))
   207  		if err != nil {
   208  			if strings.Contains(err.Error(), "not found") {
   209  				fmt.Fprintf(cli.err, "Error: No such network: %s\n", name)
   210  			} else {
   211  				fmt.Fprintf(cli.err, "%s", err)
   212  			}
   213  			status = 1
   214  			continue
   215  		}
   216  		networkResource := types.NetworkResource{}
   217  		if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&networkResource); err != nil {
   218  			return err
   219  		}
   220  
   221  		networks = append(networks, &networkResource)
   222  	}
   223  
   224  	b, err := json.MarshalIndent(networks, "", "    ")
   225  	if err != nil {
   226  		return err
   227  	}
   228  
   229  	if _, err := io.Copy(cli.out, bytes.NewReader(b)); err != nil {
   230  		return err
   231  	}
   232  	io.WriteString(cli.out, "\n")
   233  
   234  	if status != 0 {
   235  		return Cli.StatusError{StatusCode: status}
   236  	}
   237  	return nil
   238  }
   239  
   240  // Consolidates the ipam configuration as a group from differnt related configurations
   241  // user can configure network with multiple non-overlapping subnets and hence it is
   242  // possible to corelate the various related parameters and consolidate them.
   243  // consoidateIpam consolidates subnets, ip-ranges, gateways and auxilary addresses into
   244  // structured ipam data.
   245  func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) {
   246  	if len(subnets) < len(ranges) || len(subnets) < len(gateways) {
   247  		return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet")
   248  	}
   249  	iData := map[string]*network.IPAMConfig{}
   250  
   251  	// Populate non-overlapping subnets into consolidation map
   252  	for _, s := range subnets {
   253  		for k := range iData {
   254  			ok1, err := subnetMatches(s, k)
   255  			if err != nil {
   256  				return nil, err
   257  			}
   258  			ok2, err := subnetMatches(k, s)
   259  			if err != nil {
   260  				return nil, err
   261  			}
   262  			if ok1 || ok2 {
   263  				return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported")
   264  			}
   265  		}
   266  		iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}}
   267  	}
   268  
   269  	// Validate and add valid ip ranges
   270  	for _, r := range ranges {
   271  		match := false
   272  		for _, s := range subnets {
   273  			ok, err := subnetMatches(s, r)
   274  			if err != nil {
   275  				return nil, err
   276  			}
   277  			if !ok {
   278  				continue
   279  			}
   280  			if iData[s].IPRange != "" {
   281  				return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s)
   282  			}
   283  			d := iData[s]
   284  			d.IPRange = r
   285  			match = true
   286  		}
   287  		if !match {
   288  			return nil, fmt.Errorf("no matching subnet for range %s", r)
   289  		}
   290  	}
   291  
   292  	// Validate and add valid gateways
   293  	for _, g := range gateways {
   294  		match := false
   295  		for _, s := range subnets {
   296  			ok, err := subnetMatches(s, g)
   297  			if err != nil {
   298  				return nil, err
   299  			}
   300  			if !ok {
   301  				continue
   302  			}
   303  			if iData[s].Gateway != "" {
   304  				return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s)
   305  			}
   306  			d := iData[s]
   307  			d.Gateway = g
   308  			match = true
   309  		}
   310  		if !match {
   311  			return nil, fmt.Errorf("no matching subnet for gateway %s", g)
   312  		}
   313  	}
   314  
   315  	// Validate and add aux-addresses
   316  	for key, aa := range auxaddrs {
   317  		match := false
   318  		for _, s := range subnets {
   319  			ok, err := subnetMatches(s, aa)
   320  			if err != nil {
   321  				return nil, err
   322  			}
   323  			if !ok {
   324  				continue
   325  			}
   326  			iData[s].AuxAddress[key] = aa
   327  			match = true
   328  		}
   329  		if !match {
   330  			return nil, fmt.Errorf("no matching subnet for aux-address %s", aa)
   331  		}
   332  	}
   333  
   334  	idl := []network.IPAMConfig{}
   335  	for _, v := range iData {
   336  		idl = append(idl, *v)
   337  	}
   338  	return idl, nil
   339  }
   340  
   341  func subnetMatches(subnet, data string) (bool, error) {
   342  	var (
   343  		ip net.IP
   344  	)
   345  
   346  	_, s, err := net.ParseCIDR(subnet)
   347  	if err != nil {
   348  		return false, fmt.Errorf("Invalid subnet %s : %v", s, err)
   349  	}
   350  
   351  	if strings.Contains(data, "/") {
   352  		ip, _, err = net.ParseCIDR(data)
   353  		if err != nil {
   354  			return false, fmt.Errorf("Invalid cidr %s : %v", data, err)
   355  		}
   356  	} else {
   357  		ip = net.ParseIP(data)
   358  	}
   359  
   360  	return s.Contains(ip), nil
   361  }
   362  
   363  func networkUsage() string {
   364  	networkCommands := map[string]string{
   365  		"create":     "Create a network",
   366  		"connect":    "Connect container to a network",
   367  		"disconnect": "Disconnect container from a network",
   368  		"inspect":    "Display detailed network information",
   369  		"ls":         "List all networks",
   370  		"rm":         "Remove a network",
   371  	}
   372  
   373  	help := "Commands:\n"
   374  
   375  	for cmd, description := range networkCommands {
   376  		help += fmt.Sprintf("  %-25.25s%s\n", cmd, description)
   377  	}
   378  
   379  	help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.")
   380  	return help
   381  }