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