github.com/a4a881d4/docker@v1.9.0-rc2/api/client/network.go (about) 1 package client 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "fmt" 7 "io" 8 "net" 9 "strings" 10 "text/tabwriter" 11 12 "github.com/docker/docker/api/types" 13 Cli "github.com/docker/docker/cli" 14 "github.com/docker/docker/daemon/network" 15 "github.com/docker/docker/opts" 16 flag "github.com/docker/docker/pkg/mflag" 17 "github.com/docker/docker/pkg/stringid" 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 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 51 cmd.Require(flag.Exact, 1) 52 err := cmd.ParseFlags(args, true) 53 if err != nil { 54 return err 55 } 56 57 ipamCfg, err := consolidateIpam(flIpamSubnet.GetAll(), flIpamIPRange.GetAll(), flIpamGateway.GetAll(), flIpamAux.GetAll()) 58 if err != nil { 59 return err 60 } 61 62 // Construct network create request body 63 nc := types.NetworkCreate{ 64 Name: cmd.Arg(0), 65 Driver: *flDriver, 66 IPAM: network.IPAM{Driver: *flIpamDriver, Config: ipamCfg}, 67 Options: flOpts.GetAll(), 68 CheckDuplicate: true, 69 } 70 obj, _, err := readBody(cli.call("POST", "/networks/create", nc, nil)) 71 if err != nil { 72 return err 73 } 74 var resp types.NetworkCreateResponse 75 err = json.Unmarshal(obj, &resp) 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 a network 84 // 85 // Usage: docker network rm <NETWORK-NAME | NETWORK-ID> 86 func (cli *DockerCli) CmdNetworkRm(args ...string) error { 87 cmd := Cli.Subcmd("network rm", []string{"NETWORK"}, "Deletes a network", false) 88 cmd.Require(flag.Exact, 1) 89 err := cmd.ParseFlags(args, true) 90 if err != nil { 91 return err 92 } 93 _, _, err = readBody(cli.call("DELETE", "/networks/"+cmd.Arg(0), nil, nil)) 94 if err != nil { 95 return err 96 } 97 return nil 98 } 99 100 // CmdNetworkConnect connects a container to a network 101 // 102 // Usage: docker network connect <NETWORK> <CONTAINER> 103 func (cli *DockerCli) CmdNetworkConnect(args ...string) error { 104 cmd := Cli.Subcmd("network connect", []string{"NETWORK CONTAINER"}, "Connects a container to a network", false) 105 cmd.Require(flag.Exact, 2) 106 err := cmd.ParseFlags(args, true) 107 if err != nil { 108 return err 109 } 110 111 nc := types.NetworkConnect{Container: cmd.Arg(1)} 112 _, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/connect", nc, nil)) 113 return err 114 } 115 116 // CmdNetworkDisconnect disconnects a container from a network 117 // 118 // Usage: docker network disconnect <NETWORK> <CONTAINER> 119 func (cli *DockerCli) CmdNetworkDisconnect(args ...string) error { 120 cmd := Cli.Subcmd("network disconnect", []string{"NETWORK CONTAINER"}, "Disconnects container from a network", false) 121 cmd.Require(flag.Exact, 2) 122 err := cmd.ParseFlags(args, true) 123 if err != nil { 124 return err 125 } 126 127 nc := types.NetworkConnect{Container: cmd.Arg(1)} 128 _, _, err = readBody(cli.call("POST", "/networks/"+cmd.Arg(0)+"/disconnect", nc, nil)) 129 return err 130 } 131 132 // CmdNetworkLs lists all the netorks managed by docker daemon 133 // 134 // Usage: docker network ls [OPTIONS] 135 func (cli *DockerCli) CmdNetworkLs(args ...string) error { 136 cmd := Cli.Subcmd("network ls", nil, "Lists networks", true) 137 quiet := cmd.Bool([]string{"q", "-quiet"}, false, "Only display numeric IDs") 138 noTrunc := cmd.Bool([]string{"-no-trunc"}, false, "Do not truncate the output") 139 140 cmd.Require(flag.Exact, 0) 141 err := cmd.ParseFlags(args, true) 142 143 if err != nil { 144 return err 145 } 146 obj, _, err := readBody(cli.call("GET", "/networks", nil, nil)) 147 if err != nil { 148 return err 149 } 150 151 var networkResources []types.NetworkResource 152 err = json.Unmarshal(obj, &networkResources) 153 if err != nil { 154 return err 155 } 156 157 wr := tabwriter.NewWriter(cli.out, 20, 1, 3, ' ', 0) 158 159 // unless quiet (-q) is specified, print field titles 160 if !*quiet { 161 fmt.Fprintln(wr, "NETWORK ID\tNAME\tDRIVER") 162 } 163 164 for _, networkResource := range networkResources { 165 ID := networkResource.ID 166 netName := networkResource.Name 167 if !*noTrunc { 168 ID = stringid.TruncateID(ID) 169 } 170 if *quiet { 171 fmt.Fprintln(wr, ID) 172 continue 173 } 174 driver := networkResource.Driver 175 fmt.Fprintf(wr, "%s\t%s\t%s\t", 176 ID, 177 netName, 178 driver) 179 fmt.Fprint(wr, "\n") 180 } 181 wr.Flush() 182 return nil 183 } 184 185 // CmdNetworkInspect inspects the network object for more details 186 // 187 // Usage: docker network inspect <NETWORK> [<NETWORK>] 188 // CmdNetworkInspect handles Network inspect UI 189 func (cli *DockerCli) CmdNetworkInspect(args ...string) error { 190 cmd := Cli.Subcmd("network inspect", []string{"NETWORK"}, "Displays detailed information on a network", false) 191 cmd.Require(flag.Min, 1) 192 err := cmd.ParseFlags(args, true) 193 if err != nil { 194 return err 195 } 196 197 status := 0 198 var networks []*types.NetworkResource 199 for _, name := range cmd.Args() { 200 obj, _, err := readBody(cli.call("GET", "/networks/"+name, nil, nil)) 201 if err != nil { 202 if strings.Contains(err.Error(), "not found") { 203 fmt.Fprintf(cli.err, "Error: No such network: %s\n", name) 204 } else { 205 fmt.Fprintf(cli.err, "%s", err) 206 } 207 status = 1 208 continue 209 } 210 networkResource := types.NetworkResource{} 211 if err := json.NewDecoder(bytes.NewReader(obj)).Decode(&networkResource); err != nil { 212 return err 213 } 214 215 networks = append(networks, &networkResource) 216 } 217 218 b, err := json.MarshalIndent(networks, "", " ") 219 if err != nil { 220 return err 221 } 222 223 if _, err := io.Copy(cli.out, bytes.NewReader(b)); err != nil { 224 return err 225 } 226 io.WriteString(cli.out, "\n") 227 228 if status != 0 { 229 return Cli.StatusError{StatusCode: status} 230 } 231 return nil 232 } 233 234 // Consolidates the ipam configuration as a group from differnt related configurations 235 // user can configure network with multiple non-overlapping subnets and hence it is 236 // possible to corelate the various related parameters and consolidate them. 237 // consoidateIpam consolidates subnets, ip-ranges, gateways and auxilary addresses into 238 // structured ipam data. 239 func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) { 240 if len(subnets) < len(ranges) || len(subnets) < len(gateways) { 241 return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet") 242 } 243 iData := map[string]*network.IPAMConfig{} 244 245 // Populate non-overlapping subnets into consolidation map 246 for _, s := range subnets { 247 for k := range iData { 248 ok1, err := subnetMatches(s, k) 249 if err != nil { 250 return nil, err 251 } 252 ok2, err := subnetMatches(k, s) 253 if err != nil { 254 return nil, err 255 } 256 if ok1 || ok2 { 257 return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported") 258 } 259 } 260 iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}} 261 } 262 263 // Validate and add valid ip ranges 264 for _, r := range ranges { 265 match := false 266 for _, s := range subnets { 267 ok, err := subnetMatches(s, r) 268 if err != nil { 269 return nil, err 270 } 271 if !ok { 272 continue 273 } 274 if iData[s].IPRange != "" { 275 return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s) 276 } 277 d := iData[s] 278 d.IPRange = r 279 match = true 280 } 281 if !match { 282 return nil, fmt.Errorf("no matching subnet for range %s", r) 283 } 284 } 285 286 // Validate and add valid gateways 287 for _, g := range gateways { 288 match := false 289 for _, s := range subnets { 290 ok, err := subnetMatches(s, g) 291 if err != nil { 292 return nil, err 293 } 294 if !ok { 295 continue 296 } 297 if iData[s].Gateway != "" { 298 return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s) 299 } 300 d := iData[s] 301 d.Gateway = g 302 match = true 303 } 304 if !match { 305 return nil, fmt.Errorf("no matching subnet for gateway %s", g) 306 } 307 } 308 309 // Validate and add aux-addresses 310 for key, aa := range auxaddrs { 311 match := false 312 for _, s := range subnets { 313 ok, err := subnetMatches(s, aa) 314 if err != nil { 315 return nil, err 316 } 317 if !ok { 318 continue 319 } 320 iData[s].AuxAddress[key] = aa 321 match = true 322 } 323 if !match { 324 return nil, fmt.Errorf("no matching subnet for aux-address %s", aa) 325 } 326 } 327 328 idl := []network.IPAMConfig{} 329 for _, v := range iData { 330 idl = append(idl, *v) 331 } 332 return idl, nil 333 } 334 335 func subnetMatches(subnet, data string) (bool, error) { 336 var ( 337 ip net.IP 338 ) 339 340 _, s, err := net.ParseCIDR(subnet) 341 if err != nil { 342 return false, fmt.Errorf("Invalid subnet %s : %v", s, err) 343 } 344 345 if strings.Contains(data, "/") { 346 ip, _, err = net.ParseCIDR(data) 347 if err != nil { 348 return false, fmt.Errorf("Invalid cidr %s : %v", data, err) 349 } 350 } else { 351 ip = net.ParseIP(data) 352 } 353 354 return s.Contains(ip), nil 355 } 356 357 func networkUsage() string { 358 networkCommands := map[string]string{ 359 "create": "Create a network", 360 "connect": "Connect container to a network", 361 "disconnect": "Disconnect container from a network", 362 "inspect": "Display detailed network information", 363 "ls": "List all networks", 364 "rm": "Remove a network", 365 } 366 367 help := "Commands:\n" 368 369 for cmd, description := range networkCommands { 370 help += fmt.Sprintf(" %-25.25s%s\n", cmd, description) 371 } 372 373 help += fmt.Sprintf("\nRun 'docker network COMMAND --help' for more information on a command.") 374 return help 375 }