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