github.com/vtorhonen/terraform@v0.9.0-beta2.0.20170307220345-5d894e4ffda7/builtin/providers/openstack/resource_openstack_networking_subnet_v2.go (about) 1 package openstack 2 3 import ( 4 "fmt" 5 "log" 6 "time" 7 8 "github.com/hashicorp/terraform/helper/resource" 9 "github.com/hashicorp/terraform/helper/schema" 10 11 "github.com/gophercloud/gophercloud" 12 "github.com/gophercloud/gophercloud/openstack/networking/v2/subnets" 13 ) 14 15 func resourceNetworkingSubnetV2() *schema.Resource { 16 return &schema.Resource{ 17 Create: resourceNetworkingSubnetV2Create, 18 Read: resourceNetworkingSubnetV2Read, 19 Update: resourceNetworkingSubnetV2Update, 20 Delete: resourceNetworkingSubnetV2Delete, 21 Importer: &schema.ResourceImporter{ 22 State: schema.ImportStatePassthrough, 23 }, 24 25 Schema: map[string]*schema.Schema{ 26 "region": &schema.Schema{ 27 Type: schema.TypeString, 28 Required: true, 29 ForceNew: true, 30 DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""), 31 }, 32 "network_id": &schema.Schema{ 33 Type: schema.TypeString, 34 Required: true, 35 ForceNew: true, 36 }, 37 "cidr": &schema.Schema{ 38 Type: schema.TypeString, 39 Required: true, 40 ForceNew: true, 41 }, 42 "name": &schema.Schema{ 43 Type: schema.TypeString, 44 Optional: true, 45 ForceNew: false, 46 }, 47 "tenant_id": &schema.Schema{ 48 Type: schema.TypeString, 49 Optional: true, 50 ForceNew: true, 51 Computed: true, 52 }, 53 "allocation_pools": &schema.Schema{ 54 Type: schema.TypeList, 55 Optional: true, 56 ForceNew: true, 57 Computed: true, 58 Elem: &schema.Resource{ 59 Schema: map[string]*schema.Schema{ 60 "start": &schema.Schema{ 61 Type: schema.TypeString, 62 Required: true, 63 }, 64 "end": &schema.Schema{ 65 Type: schema.TypeString, 66 Required: true, 67 }, 68 }, 69 }, 70 }, 71 "gateway_ip": &schema.Schema{ 72 Type: schema.TypeString, 73 Optional: true, 74 ForceNew: false, 75 Computed: true, 76 }, 77 "no_gateway": &schema.Schema{ 78 Type: schema.TypeBool, 79 Optional: true, 80 ForceNew: false, 81 }, 82 "ip_version": &schema.Schema{ 83 Type: schema.TypeInt, 84 Optional: true, 85 Default: 4, 86 ForceNew: true, 87 }, 88 "enable_dhcp": &schema.Schema{ 89 Type: schema.TypeBool, 90 Optional: true, 91 ForceNew: false, 92 Default: true, 93 }, 94 "dns_nameservers": &schema.Schema{ 95 Type: schema.TypeSet, 96 Optional: true, 97 ForceNew: false, 98 Elem: &schema.Schema{Type: schema.TypeString}, 99 Set: schema.HashString, 100 }, 101 "host_routes": &schema.Schema{ 102 Type: schema.TypeList, 103 Optional: true, 104 ForceNew: false, 105 Elem: &schema.Resource{ 106 Schema: map[string]*schema.Schema{ 107 "destination_cidr": &schema.Schema{ 108 Type: schema.TypeString, 109 Required: true, 110 }, 111 "next_hop": &schema.Schema{ 112 Type: schema.TypeString, 113 Required: true, 114 }, 115 }, 116 }, 117 }, 118 "value_specs": &schema.Schema{ 119 Type: schema.TypeMap, 120 Optional: true, 121 ForceNew: true, 122 }, 123 }, 124 } 125 } 126 127 func resourceNetworkingSubnetV2Create(d *schema.ResourceData, meta interface{}) error { 128 config := meta.(*Config) 129 networkingClient, err := config.networkingV2Client(GetRegion(d)) 130 if err != nil { 131 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 132 } 133 134 createOpts := SubnetCreateOpts{ 135 subnets.CreateOpts{ 136 NetworkID: d.Get("network_id").(string), 137 CIDR: d.Get("cidr").(string), 138 Name: d.Get("name").(string), 139 TenantID: d.Get("tenant_id").(string), 140 AllocationPools: resourceSubnetAllocationPoolsV2(d), 141 DNSNameservers: resourceSubnetDNSNameserversV2(d), 142 HostRoutes: resourceSubnetHostRoutesV2(d), 143 EnableDHCP: nil, 144 }, 145 MapValueSpecs(d), 146 } 147 148 noGateway := d.Get("no_gateway").(bool) 149 gatewayIP := d.Get("gateway_ip").(string) 150 151 if gatewayIP != "" && noGateway { 152 return fmt.Errorf("Both gateway_ip and no_gateway cannot be set") 153 } 154 155 if gatewayIP != "" { 156 createOpts.GatewayIP = &gatewayIP 157 } 158 159 if noGateway { 160 disableGateway := "" 161 createOpts.GatewayIP = &disableGateway 162 } 163 164 enableDHCP := d.Get("enable_dhcp").(bool) 165 createOpts.EnableDHCP = &enableDHCP 166 167 if v, ok := d.GetOk("ip_version"); ok { 168 ipVersion := resourceNetworkingSubnetV2DetermineIPVersion(v.(int)) 169 createOpts.IPVersion = ipVersion 170 } 171 172 s, err := subnets.Create(networkingClient, createOpts).Extract() 173 if err != nil { 174 return fmt.Errorf("Error creating OpenStack Neutron subnet: %s", err) 175 } 176 177 log.Printf("[DEBUG] Waiting for Subnet (%s) to become available", s.ID) 178 stateConf := &resource.StateChangeConf{ 179 Target: []string{"ACTIVE"}, 180 Refresh: waitForSubnetActive(networkingClient, s.ID), 181 Timeout: 10 * time.Minute, 182 Delay: 5 * time.Second, 183 MinTimeout: 3 * time.Second, 184 } 185 186 _, err = stateConf.WaitForState() 187 188 d.SetId(s.ID) 189 190 log.Printf("[DEBUG] Created Subnet %s: %#v", s.ID, s) 191 return resourceNetworkingSubnetV2Read(d, meta) 192 } 193 194 func resourceNetworkingSubnetV2Read(d *schema.ResourceData, meta interface{}) error { 195 config := meta.(*Config) 196 networkingClient, err := config.networkingV2Client(GetRegion(d)) 197 if err != nil { 198 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 199 } 200 201 s, err := subnets.Get(networkingClient, d.Id()).Extract() 202 if err != nil { 203 return CheckDeleted(d, err, "subnet") 204 } 205 206 log.Printf("[DEBUG] Retrieved Subnet %s: %#v", d.Id(), s) 207 208 d.Set("network_id", s.NetworkID) 209 d.Set("cidr", s.CIDR) 210 d.Set("ip_version", s.IPVersion) 211 d.Set("name", s.Name) 212 d.Set("tenant_id", s.TenantID) 213 d.Set("gateway_ip", s.GatewayIP) 214 d.Set("dns_nameservers", s.DNSNameservers) 215 d.Set("host_routes", s.HostRoutes) 216 d.Set("enable_dhcp", s.EnableDHCP) 217 d.Set("network_id", s.NetworkID) 218 219 // Set the allocation_pools 220 var allocationPools []map[string]interface{} 221 for _, v := range s.AllocationPools { 222 pool := make(map[string]interface{}) 223 pool["start"] = v.Start 224 pool["end"] = v.End 225 226 allocationPools = append(allocationPools, pool) 227 } 228 d.Set("allocation_pools", allocationPools) 229 230 d.Set("region", GetRegion(d)) 231 232 return nil 233 } 234 235 func resourceNetworkingSubnetV2Update(d *schema.ResourceData, meta interface{}) error { 236 config := meta.(*Config) 237 networkingClient, err := config.networkingV2Client(GetRegion(d)) 238 if err != nil { 239 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 240 } 241 242 // Check if both gateway_ip and no_gateway are set 243 if _, ok := d.GetOk("gateway_ip"); ok { 244 noGateway := d.Get("no_gateway").(bool) 245 if noGateway { 246 return fmt.Errorf("Both gateway_ip and no_gateway cannot be set.") 247 } 248 } 249 250 var updateOpts subnets.UpdateOpts 251 252 noGateway := d.Get("no_gateway").(bool) 253 gatewayIP := d.Get("gateway_ip").(string) 254 255 if gatewayIP != "" && noGateway { 256 return fmt.Errorf("Both gateway_ip and no_gateway cannot be set") 257 } 258 259 if d.HasChange("name") { 260 updateOpts.Name = d.Get("name").(string) 261 } 262 263 if d.HasChange("gateway_ip") { 264 updateOpts.GatewayIP = nil 265 if v, ok := d.GetOk("gateway_ip"); ok { 266 gatewayIP := v.(string) 267 updateOpts.GatewayIP = &gatewayIP 268 } 269 } 270 271 if d.HasChange("no_gateway") { 272 if d.Get("no_gateway").(bool) { 273 gatewayIP := "" 274 updateOpts.GatewayIP = &gatewayIP 275 } 276 } 277 278 if d.HasChange("dns_nameservers") { 279 updateOpts.DNSNameservers = resourceSubnetDNSNameserversV2(d) 280 } 281 282 if d.HasChange("host_routes") { 283 updateOpts.HostRoutes = resourceSubnetHostRoutesV2(d) 284 } 285 286 if d.HasChange("enable_dhcp") { 287 v := d.Get("enable_dhcp").(bool) 288 updateOpts.EnableDHCP = &v 289 } 290 291 log.Printf("[DEBUG] Updating Subnet %s with options: %+v", d.Id(), updateOpts) 292 293 _, err = subnets.Update(networkingClient, d.Id(), updateOpts).Extract() 294 if err != nil { 295 return fmt.Errorf("Error updating OpenStack Neutron Subnet: %s", err) 296 } 297 298 return resourceNetworkingSubnetV2Read(d, meta) 299 } 300 301 func resourceNetworkingSubnetV2Delete(d *schema.ResourceData, meta interface{}) error { 302 config := meta.(*Config) 303 networkingClient, err := config.networkingV2Client(GetRegion(d)) 304 if err != nil { 305 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 306 } 307 308 stateConf := &resource.StateChangeConf{ 309 Pending: []string{"ACTIVE"}, 310 Target: []string{"DELETED"}, 311 Refresh: waitForSubnetDelete(networkingClient, d.Id()), 312 Timeout: 10 * time.Minute, 313 Delay: 5 * time.Second, 314 MinTimeout: 3 * time.Second, 315 } 316 317 _, err = stateConf.WaitForState() 318 if err != nil { 319 return fmt.Errorf("Error deleting OpenStack Neutron Subnet: %s", err) 320 } 321 322 d.SetId("") 323 return nil 324 } 325 326 func resourceSubnetAllocationPoolsV2(d *schema.ResourceData) []subnets.AllocationPool { 327 rawAPs := d.Get("allocation_pools").([]interface{}) 328 aps := make([]subnets.AllocationPool, len(rawAPs)) 329 for i, raw := range rawAPs { 330 rawMap := raw.(map[string]interface{}) 331 aps[i] = subnets.AllocationPool{ 332 Start: rawMap["start"].(string), 333 End: rawMap["end"].(string), 334 } 335 } 336 return aps 337 } 338 339 func resourceSubnetDNSNameserversV2(d *schema.ResourceData) []string { 340 rawDNSN := d.Get("dns_nameservers").(*schema.Set) 341 dnsn := make([]string, rawDNSN.Len()) 342 for i, raw := range rawDNSN.List() { 343 dnsn[i] = raw.(string) 344 } 345 return dnsn 346 } 347 348 func resourceSubnetHostRoutesV2(d *schema.ResourceData) []subnets.HostRoute { 349 rawHR := d.Get("host_routes").([]interface{}) 350 hr := make([]subnets.HostRoute, len(rawHR)) 351 for i, raw := range rawHR { 352 rawMap := raw.(map[string]interface{}) 353 hr[i] = subnets.HostRoute{ 354 DestinationCIDR: rawMap["destination_cidr"].(string), 355 NextHop: rawMap["next_hop"].(string), 356 } 357 } 358 return hr 359 } 360 361 func resourceNetworkingSubnetV2DetermineIPVersion(v int) gophercloud.IPVersion { 362 var ipVersion gophercloud.IPVersion 363 switch v { 364 case 4: 365 ipVersion = gophercloud.IPv4 366 case 6: 367 ipVersion = gophercloud.IPv6 368 } 369 370 return ipVersion 371 } 372 373 func waitForSubnetActive(networkingClient *gophercloud.ServiceClient, subnetId string) resource.StateRefreshFunc { 374 return func() (interface{}, string, error) { 375 s, err := subnets.Get(networkingClient, subnetId).Extract() 376 if err != nil { 377 return nil, "", err 378 } 379 380 log.Printf("[DEBUG] OpenStack Neutron Subnet: %+v", s) 381 return s, "ACTIVE", nil 382 } 383 } 384 385 func waitForSubnetDelete(networkingClient *gophercloud.ServiceClient, subnetId string) resource.StateRefreshFunc { 386 return func() (interface{}, string, error) { 387 log.Printf("[DEBUG] Attempting to delete OpenStack Subnet %s.\n", subnetId) 388 389 s, err := subnets.Get(networkingClient, subnetId).Extract() 390 if err != nil { 391 if _, ok := err.(gophercloud.ErrDefault404); ok { 392 log.Printf("[DEBUG] Successfully deleted OpenStack Subnet %s", subnetId) 393 return s, "DELETED", nil 394 } 395 return s, "ACTIVE", err 396 } 397 398 err = subnets.Delete(networkingClient, subnetId).ExtractErr() 399 if err != nil { 400 if _, ok := err.(gophercloud.ErrDefault404); ok { 401 log.Printf("[DEBUG] Successfully deleted OpenStack Subnet %s", subnetId) 402 return s, "DELETED", nil 403 } 404 if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok { 405 if errCode.Actual == 409 { 406 return s, "ACTIVE", nil 407 } 408 } 409 return s, "ACTIVE", err 410 } 411 412 log.Printf("[DEBUG] OpenStack Subnet %s still active.\n", subnetId) 413 return s, "ACTIVE", nil 414 } 415 }