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