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