diff --git a/cloudstack/resource_cloudstack_network.go b/cloudstack/resource_cloudstack_network.go index e7329f82..2adb21a3 100644 --- a/cloudstack/resource_cloudstack_network.go +++ b/cloudstack/resource_cloudstack_network.go @@ -78,6 +78,12 @@ func resourceCloudStackNetwork() *schema.Resource { ForceNew: true, }, + "ip6cidr": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + }, + "gateway": { Type: schema.TypeString, Optional: true, @@ -85,6 +91,13 @@ func resourceCloudStackNetwork() *schema.Resource { ForceNew: true, }, + "ip6gateway": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "startip": { Type: schema.TypeString, Optional: true, @@ -99,6 +112,20 @@ func resourceCloudStackNetwork() *schema.Resource { ForceNew: true, }, + "startipv6": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + + "endipv6": { + Type: schema.TypeString, + Optional: true, + Computed: true, + ForceNew: true, + }, + "network_domain": { Type: schema.TypeString, Optional: true, @@ -209,6 +236,31 @@ func resourceCloudStackNetworkCreate(d *schema.ResourceData, meta interface{}) e p.SetEndip(endip) } + // IPv6 support + if ip6cidr, ok := d.GetOk("ip6cidr"); ok && ip6cidr.(string) != none { + m6, err := parseCIDRv6(d, no.Specifyipranges) + if err != nil { + return err + } + + p.SetIp6cidr(ip6cidr) + + // Only set the start IPv6 if we have one + if startipv6, ok := m6["startipv6"]; ok { + p.SetStartipv6(startipv6) + } + + // Only set the ipv6 gateway if we have one + if ip6gateway, ok := m6["ip6gateway"]; ok { + p.SetIp6gateway(ip6gateway) + } + + // Only set the end IPv6 if we have one + if endipv6, ok := m6["endipv6"]; ok { + p.SetEndipv6(endipv6) + } + } + // Set the network domain if we have one if networkDomain, ok := d.GetOk("network_domain"); ok { p.SetNetworkdomain(networkDomain.(string)) @@ -306,6 +358,16 @@ func resourceCloudStackNetworkRead(d *schema.ResourceData, meta interface{}) err d.Set("network_domain", n.Networkdomain) d.Set("vpc_id", n.Vpcid) + if n.Ip6cidr == "" { + n.Ip6cidr = none + } + d.Set("ip6cidr", n.Ip6cidr) + + if n.Ip6gateway == "" { + n.Ip6gateway = none + } + d.Set("ip6gateway", n.Ip6gateway) + if n.Aclid == "" { n.Aclid = none } @@ -471,3 +533,53 @@ func parseCIDR(d *schema.ResourceData, specifyiprange bool) (map[string]string, return m, nil } + +func parseCIDRv6(d *schema.ResourceData, specifyiprange bool) (map[string]string, error) { + m := make(map[string]string, 4) + + cidr := d.Get("ip6cidr").(string) + _, ipnet, err := net.ParseCIDR(cidr) + if err != nil { + return nil, fmt.Errorf("Unable to parse cidr %s: %s", cidr, err) + } + + if gateway, ok := d.GetOk("ip6gateway"); ok { + m["ip6gateway"] = gateway.(string) + } else { + m["ip6gateway"] = ipnet.IP.String() + } + + if startip, ok := d.GetOk("startip"); ok { + m["startip"] = startip.(string) + } else if specifyiprange { + ip16 := ipnet.IP.To16() + if ip16 == nil { + return nil, fmt.Errorf("cidr not valid for ipv6") + } + + myip := make(net.IP, len(ip16)) + copy(myip, ip16) + myip[len(ip16)-1] = 2 + m["startip"] = myip.String() + } + + if endip, ok := d.GetOk("endipv6"); ok { + m["endipv6"] = endip.(string) + } else if specifyiprange { + ip16 := ipnet.IP.To16() + if ip16 == nil { + return nil, fmt.Errorf("cidr not valid for ipv6") + } + + last := make(net.IP, len(ip16)) + copy(last, ip16) + + for i := range ip16 { + // Perform bitwise OR with the inverse of the mask + last[i] |= ^ipnet.Mask[i] + } + m["endipv6"] = last.String() + } + + return m, nil +} diff --git a/website/docs/r/network.html.markdown b/website/docs/r/network.html.markdown index d83ebb1a..528ae681 100644 --- a/website/docs/r/network.html.markdown +++ b/website/docs/r/network.html.markdown @@ -43,6 +43,18 @@ The following arguments are supported: * `endip` - (Optional) End of the IP block that will be available on the network. Defaults to the last available IP in the range. +* `ip6cidr` - (Optional) The IPv6 CIDR block for the network. Changing this + forces a new resource to be created. + +* `ip6gateway` - (Optional) IPv6 Gateway that will be provided to the instances + in this network. Defaults to the first usable IP in the range. + +* `startipv6` - (Optional) Start of the IPv6 block that will be available on the + network. Defaults to the second available IP in the range. + +* `endipv6` - (Optional) End of the IPv6 block that will be available on the + network. Defaults to the last available IP in the range. + * `network_domain` - (Optional) DNS domain for the network. * `network_offering` - (Required) The name or ID of the network offering to use