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