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