github.com/walkingsparrow/docker@v1.4.2-0.20151218153551-b708a2249bfa/api/client/network.go (about) 1 package client 2 3 import ( 4 "fmt" 5 "net" 6 "strings" 7 "text/tabwriter" 8 9 "github.com/docker/docker/api/types" 10 "github.com/docker/docker/api/types/network" 11 Cli "github.com/docker/docker/cli" 12 "github.com/docker/docker/opts" 13 flag "github.com/docker/docker/pkg/mflag" 14 "github.com/docker/docker/pkg/stringid" 15 ) 16 17 // CmdNetwork is the parent subcommand for all network commands 18 // 19 // Usage: docker network <COMMAND> [OPTIONS] 20 func (cli *DockerCli) CmdNetwork(args ...string) error { 21 cmd := Cli.Subcmd("network", []string{"COMMAND [OPTIONS]"}, networkUsage(), false) 22 cmd.Require(flag.Min, 1) 23 err := cmd.ParseFlags(args, true) 24 cmd.Usage() 25 return err 26 } 27 28 // CmdNetworkCreate creates a new network with a given name 29 // 30 // Usage: docker network create [OPTIONS] <NETWORK-NAME> 31 func (cli *DockerCli) CmdNetworkCreate(args ...string) error { 32 cmd := Cli.Subcmd("network create", []string{"NETWORK-NAME"}, "Creates a new network with a name specified by the user", false) 33 flDriver := cmd.String([]string{"d", "-driver"}, "bridge", "Driver to manage the Network") 34 flOpts := opts.NewMapOpts(nil, nil) 35 36 flIpamDriver := cmd.String([]string{"-ipam-driver"}, "default", "IP Address Management Driver") 37 flIpamSubnet := opts.NewListOpts(nil) 38 flIpamIPRange := opts.NewListOpts(nil) 39 flIpamGateway := opts.NewListOpts(nil) 40 flIpamAux := opts.NewMapOpts(nil, nil) 41 42 cmd.Var(&flIpamSubnet, []string{"-subnet"}, "subnet in CIDR format that represents a network segment") 43 cmd.Var(&flIpamIPRange, []string{"-ip-range"}, "allocate container ip from a sub-range") 44 cmd.Var(&flIpamGateway, []string{"-gateway"}, "ipv4 or ipv6 Gateway for the master subnet") 45 cmd.Var(flIpamAux, []string{"-aux-address"}, "auxiliary ipv4 or ipv6 addresses used by Network driver") 46 cmd.Var(flOpts, []string{"o", "-opt"}, "set driver specific options") 47 48 cmd.Require(flag.Exact, 1) 49 err := cmd.ParseFlags(args, true) 50 if err != nil { 51 return err 52 } 53 54 // Set the default driver to "" if the user didn't set the value. 55 // That way we can know whether it was user input or not. 56 driver := *flDriver 57 if !cmd.IsSet("-driver") && !cmd.IsSet("d") { 58 driver = "" 59 } 60 61 ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll()) 62 if err != nil { 63 return err 64 } 65 66 // Construct network create request body 67 nc := types.NetworkCreate{ 68 Name: cmd.Arg(0), 69 Driver: driver, 70 IPAM: network.IPAM{Driver: *flIpamDriver, Config: ipamCfg}, 71 Options: flOpts.GetAll(), 72 CheckDuplicate: true, 73 } 74 75 resp, err := cli.client.NetworkCreate(nc) 76 if err != nil { 77 return err 78 } 79 fmt.Fprintf(cli.out, "%s\n", resp.ID) 80 return nil 81 } 82 83 // CmdNetworkRm deletes one or more networks 84 // 85 // Usage: docker network rm NETWORK-NAME|NETWORK-ID [NETWORK-NAME|NETWORK-ID...] 86 func (cli *DockerCli) CmdNetworkRm(args ...string) error { 87 cmd := Cli.Subcmd("network rm", []string{"NETWORK [NETWORK...]"}, "Deletes one or more networks", false) 88 cmd.Require(flag.Min, 1) 89 if err := cmd.ParseFlags(args, true); err != nil { 90 return err 91 } 92 93 status := 0 94 for _, net := range cmd.Args() { 95 if err := cli.client.NetworkRemove(net); err != nil { 96 fmt.Fprintf(cli.err, "%s\n", err) 97 status = 1 98 continue 99 } 100 } 101 if status != 0 { 102 return Cli.StatusError{StatusCode: status} 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 if err := cmd.ParseFlags(args, true); err != nil { 114 return err 115 } 116 117 return cli.client.NetworkConnect(cmd.Arg(0), cmd.Arg(1)) 118 } 119 120 // CmdNetworkDisconnect disconnects a container from a network 121 // 122 // Usage: docker network disconnect <NETWORK> <CONTAINER> 123 func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error { 124 cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false) 125 cmd.Require(flag.Exact, 2) 126 if err := cmd.ParseFlags(args, true); err != nil { 127 return err 128 } 129 130 return cli.client.NetworkDisconnect(cmd.Arg(0), cmd.Arg(1)) 131 } 132 133 // CmdNetworkLs lists all the networks managed by docker daemon 134 // 135 // Usage: docker network ls [OPTIONS] 136 func (cli *DockerCli) CmdNetworkLs(args ...string) error { 137 cmd := Cli.Subcmd("network ls", nil, "Lists networks", true) 138 quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") 139 noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output") 140 141 cmd.Require(flag.Exact, 0) 142 if err := cmd.ParseFlags(args, true); err != nil { 143 return err 144 } 145 146 networkResources, err := cli.client.NetworkList() 147 if err != nil { 148 return err 149 } 150 151 wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 152 153 // unless quiet (-q) is specified, print field titles 154 if !*quiet { 155 fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER") 156 } 157 158 for _, networkResource := range networkResources { 159 ID := networkResource.ID 160 netName := networkResource.Name 161 if !*noTrunc { 162 ID = stringid.TruncateID(ID) 163 } 164 if *quiet { 165 fmt.Fprintln(wr, ID) 166 continue 167 } 168 driver := networkResource.Driver 169 fmt.Fprintf(wr, "%s\t%s\t%s\t", 170 ID, 171 netName, 172 driver) 173 fmt.Fprint(wr, "\n") 174 } 175 wr.Flush() 176 return nil 177 } 178 179 // CmdNetworkInspect inspects the network object for more details 180 // 181 // Usage: docker network inspect [OPTIONS] <NETWORK> [NETWORK...] 182 func (cli *DockerCli) CmdNetworkInspect(args ...string) error { 183 cmd := Cli.Subcmd("network inspect", []string{"NETWORK [NETWORK...]"}, "Displays detailed information on one or more networks", false) 184 tmplStr := cmd.String([]string{"f", "-format"}, "", "Format the output using the given go template") 185 cmd.Require(flag.Min, 1) 186 187 if err := cmd.ParseFlags(args, true); err != nil { 188 return err 189 } 190 191 inspectSearcher := func(name string) (interface{}, []byte, error) { 192 i, err := cli.client.NetworkInspect(name) 193 return i, nil, err 194 } 195 196 return cli.inspectElements(*tmplStr, cmd.Args(), inspectSearcher) 197 } 198 199 // Consolidates the ipam configuration as a group from different related configurations 200 // user can configure network with multiple non-overlapping subnets and hence it is 201 // possible to correlate the various related parameters and consolidate them. 202 // consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into 203 // structured ipam data. 204 func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) { 205 if len(subnets) < len(ranges) || len(subnets) < len(gateways) { 206 return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet") 207 } 208 iData := map[string]*network.IPAMConfig{} 209 210 // Populate non-overlapping subnets into consolidation map 211 for _, s := range subnets { 212 for k := range iData { 213 ok1, err := subnetMatches(s, k) 214 if err != nil { 215 return nil, err 216 } 217 ok2, err := subnetMatches(k, s) 218 if err != nil { 219 return nil, err 220 } 221 if ok1 || ok2 { 222 return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported") 223 } 224 } 225 iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}} 226 } 227 228 // Validate and add valid ip ranges 229 for _, r := range ranges { 230 match := false 231 for _, s := range subnets { 232 ok, err := subnetMatches(s, r) 233 if err != nil { 234 return nil, err 235 } 236 if !ok { 237 continue 238 } 239 if iData[s].IPRange != "" { 240 return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s) 241 } 242 d := iData[s] 243 d.IPRange = r 244 match = true 245 } 246 if !match { 247 return nil, fmt.Errorf("no matching subnet for range %s", r) 248 } 249 } 250 251 // Validate and add valid gateways 252 for _, g := range gateways { 253 match := false 254 for _, s := range subnets { 255 ok, err := subnetMatches(s, g) 256 if err != nil { 257 return nil, err 258 } 259 if !ok { 260 continue 261 } 262 if iData[s].Gateway != "" { 263 return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s) 264 } 265 d := iData[s] 266 d.Gateway = g 267 match = true 268 } 269 if !match { 270 return nil, fmt.Errorf("no matching subnet for gateway %s", g) 271 } 272 } 273 274 // Validate and add aux-addresses 275 for key, aa := range auxaddrs { 276 match := false 277 for _, s := range subnets { 278 ok, err := subnetMatches(s, aa) 279 if err != nil { 280 return nil, err 281 } 282 if !ok { 283 continue 284 } 285 iData[s].AuxAddress[key] = aa 286 match = true 287 } 288 if !match { 289 return nil, fmt.Errorf("no matching subnet for aux-address %s", aa) 290 } 291 } 292 293 idl := []network.IPAMConfig{} 294 for _, v := range iData { 295 idl = append(idl, *v) 296 } 297 return idl, nil 298 } 299 300 func subnetMatches(subnet, data string) (bool, error) { 301 var ( 302 ip net.IP 303 ) 304 305 _, s, err := net.ParseCIDR(subnet) 306 if err != nil { 307 return false, fmt.Errorf("Invalid subnet %s : %v", s, err) 308 } 309 310 if strings.Contains(data, "/") { 311 ip, _, err = net.ParseCIDR(data) 312 if err != nil { 313 return false, fmt.Errorf("Invalid cidr %s : %v", data, err) 314 } 315 } else { 316 ip = net.ParseIP(data) 317 } 318 319 return s.Contains(ip), nil 320 } 321 322 func networkUsage() string { 323 networkCommands := map[string]string{ 324 "create": "Create a network", 325 "connect": "Connect container to a network", 326 "disconnect": "Disconnect container from a network", 327 "inspect": "Display detailed network information", 328 "ls": "List all networks", 329 "rm": "Remove a network", 330 } 331 332 help := "Commands:\n" 333 334 for cmd, description := range networkCommands { 335 help += fmt.Sprintf(" %-25.25s%s\n", cmd, description) 336 } 337 338 help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.") 339 return help 340 }