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 }