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