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