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