github.com/vieux/docker@v0.6.3-0.20161004191708-e097c2a938c7/cli/command/network/create.go (about) 1 package network 2 3 import ( 4 "fmt" 5 "net" 6 "strings" 7 8 "golang.org/x/net/context" 9 10 "github.com/docker/docker/api/types" 11 "github.com/docker/docker/api/types/network" 12 "github.com/docker/docker/cli" 13 "github.com/docker/docker/cli/command" 14 "github.com/docker/docker/opts" 15 runconfigopts "github.com/docker/docker/runconfig/opts" 16 "github.com/spf13/cobra" 17 ) 18 19 type createOptions struct { 20 name string 21 driver string 22 driverOpts opts.MapOpts 23 labels []string 24 internal bool 25 ipv6 bool 26 attachable bool 27 28 ipamDriver string 29 ipamSubnet []string 30 ipamIPRange []string 31 ipamGateway []string 32 ipamAux opts.MapOpts 33 ipamOpt opts.MapOpts 34 } 35 36 func newCreateCommand(dockerCli *command.DockerCli) *cobra.Command { 37 opts := createOptions{ 38 driverOpts: *opts.NewMapOpts(nil, nil), 39 ipamAux: *opts.NewMapOpts(nil, nil), 40 ipamOpt: *opts.NewMapOpts(nil, nil), 41 } 42 43 cmd := &cobra.Command{ 44 Use: "create [OPTIONS] NETWORK", 45 Short: "Create a network", 46 Args: cli.ExactArgs(1), 47 RunE: func(cmd *cobra.Command, args []string) error { 48 opts.name = args[0] 49 return runCreate(dockerCli, opts) 50 }, 51 } 52 53 flags := cmd.Flags() 54 flags.StringVarP(&opts.driver, "driver", "d", "bridge", "Driver to manage the Network") 55 flags.VarP(&opts.driverOpts, "opt", "o", "Set driver specific options") 56 flags.StringSliceVar(&opts.labels, "label", []string{}, "Set metadata on a network") 57 flags.BoolVar(&opts.internal, "internal", false, "Restrict external access to the network") 58 flags.BoolVar(&opts.ipv6, "ipv6", false, "Enable IPv6 networking") 59 flags.BoolVar(&opts.attachable, "attachable", false, "Enable manual container attachment") 60 61 flags.StringVar(&opts.ipamDriver, "ipam-driver", "default", "IP Address Management Driver") 62 flags.StringSliceVar(&opts.ipamSubnet, "subnet", []string{}, "Subnet in CIDR format that represents a network segment") 63 flags.StringSliceVar(&opts.ipamIPRange, "ip-range", []string{}, "Allocate container ip from a sub-range") 64 flags.StringSliceVar(&opts.ipamGateway, "gateway", []string{}, "IPv4 or IPv6 Gateway for the master subnet") 65 66 flags.Var(&opts.ipamAux, "aux-address", "Auxiliary IPv4 or IPv6 addresses used by Network driver") 67 flags.Var(&opts.ipamOpt, "ipam-opt", "Set IPAM driver specific options") 68 69 return cmd 70 } 71 72 func runCreate(dockerCli *command.DockerCli, opts createOptions) error { 73 client := dockerCli.Client() 74 75 ipamCfg, err := consolidateIpam(opts.ipamSubnet, opts.ipamIPRange, opts.ipamGateway, opts.ipamAux.GetAll()) 76 if err != nil { 77 return err 78 } 79 80 // Construct network create request body 81 nc := types.NetworkCreate{ 82 Driver: opts.driver, 83 Options: opts.driverOpts.GetAll(), 84 IPAM: &network.IPAM{ 85 Driver: opts.ipamDriver, 86 Config: ipamCfg, 87 Options: opts.ipamOpt.GetAll(), 88 }, 89 CheckDuplicate: true, 90 Internal: opts.internal, 91 EnableIPv6: opts.ipv6, 92 Attachable: opts.attachable, 93 Labels: runconfigopts.ConvertKVStringsToMap(opts.labels), 94 } 95 96 resp, err := client.NetworkCreate(context.Background(), opts.name, nc) 97 if err != nil { 98 return err 99 } 100 fmt.Fprintf(dockerCli.Out(), "%s\n", resp.ID) 101 return nil 102 } 103 104 // Consolidates the ipam configuration as a group from different related configurations 105 // user can configure network with multiple non-overlapping subnets and hence it is 106 // possible to correlate the various related parameters and consolidate them. 107 // consoidateIpam consolidates subnets, ip-ranges, gateways and auxiliary addresses into 108 // structured ipam data. 109 func consolidateIpam(subnets, ranges, gateways []string, auxaddrs map[string]string) ([]network.IPAMConfig, error) { 110 if len(subnets) < len(ranges) || len(subnets) < len(gateways) { 111 return nil, fmt.Errorf("every ip-range or gateway must have a corresponding subnet") 112 } 113 iData := map[string]*network.IPAMConfig{} 114 115 // Populate non-overlapping subnets into consolidation map 116 for _, s := range subnets { 117 for k := range iData { 118 ok1, err := subnetMatches(s, k) 119 if err != nil { 120 return nil, err 121 } 122 ok2, err := subnetMatches(k, s) 123 if err != nil { 124 return nil, err 125 } 126 if ok1 || ok2 { 127 return nil, fmt.Errorf("multiple overlapping subnet configuration is not supported") 128 } 129 } 130 iData[s] = &network.IPAMConfig{Subnet: s, AuxAddress: map[string]string{}} 131 } 132 133 // Validate and add valid ip ranges 134 for _, r := range ranges { 135 match := false 136 for _, s := range subnets { 137 ok, err := subnetMatches(s, r) 138 if err != nil { 139 return nil, err 140 } 141 if !ok { 142 continue 143 } 144 if iData[s].IPRange != "" { 145 return nil, fmt.Errorf("cannot configure multiple ranges (%s, %s) on the same subnet (%s)", r, iData[s].IPRange, s) 146 } 147 d := iData[s] 148 d.IPRange = r 149 match = true 150 } 151 if !match { 152 return nil, fmt.Errorf("no matching subnet for range %s", r) 153 } 154 } 155 156 // Validate and add valid gateways 157 for _, g := range gateways { 158 match := false 159 for _, s := range subnets { 160 ok, err := subnetMatches(s, g) 161 if err != nil { 162 return nil, err 163 } 164 if !ok { 165 continue 166 } 167 if iData[s].Gateway != "" { 168 return nil, fmt.Errorf("cannot configure multiple gateways (%s, %s) for the same subnet (%s)", g, iData[s].Gateway, s) 169 } 170 d := iData[s] 171 d.Gateway = g 172 match = true 173 } 174 if !match { 175 return nil, fmt.Errorf("no matching subnet for gateway %s", g) 176 } 177 } 178 179 // Validate and add aux-addresses 180 for key, aa := range auxaddrs { 181 match := false 182 for _, s := range subnets { 183 ok, err := subnetMatches(s, aa) 184 if err != nil { 185 return nil, err 186 } 187 if !ok { 188 continue 189 } 190 iData[s].AuxAddress[key] = aa 191 match = true 192 } 193 if !match { 194 return nil, fmt.Errorf("no matching subnet for aux-address %s", aa) 195 } 196 } 197 198 idl := []network.IPAMConfig{} 199 for _, v := range iData { 200 idl = append(idl, *v) 201 } 202 return idl, nil 203 } 204 205 func subnetMatches(subnet, data string) (bool, error) { 206 var ( 207 ip net.IP 208 ) 209 210 _, s, err := net.ParseCIDR(subnet) 211 if err != nil { 212 return false, fmt.Errorf("Invalid subnet %s : %v", s, err) 213 } 214 215 if strings.Contains(data, "/") { 216 ip, _, err = net.ParseCIDR(data) 217 if err != nil { 218 return false, fmt.Errorf("Invalid cidr %s : %v", data, err) 219 } 220 } else { 221 ip = net.ParseIP(data) 222 } 223 224 return s.Contains(ip), nil 225 }