github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/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 Default: 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("dns_nameservers", s.DNSNameservers) 192 d.Set("host_routes", s.HostRoutes) 193 194 return nil 195 } 196 197 func resourceNetworkingSubnetV2Update(d *schema.ResourceData, meta interface{}) error { 198 config := meta.(*Config) 199 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 200 if err != nil { 201 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 202 } 203 204 // Check if both gateway_ip and no_gateway are set 205 if _, ok := d.GetOk("gateway_ip"); ok { 206 if _, ok2 := d.GetOk("no_gateway"); ok2 { 207 return fmt.Errorf("Both gateway_ip and no_gateway cannot be set.") 208 } 209 } 210 211 var updateOpts subnets.UpdateOpts 212 213 if d.HasChange("name") { 214 updateOpts.Name = d.Get("name").(string) 215 } 216 217 if d.HasChange("gateway_ip") { 218 updateOpts.GatewayIP = d.Get("gateway_ip").(string) 219 } 220 221 if d.HasChange("no_gateway") { 222 updateOpts.NoGateway = d.Get("no_gateway").(bool) 223 } 224 225 if d.HasChange("dns_nameservers") { 226 updateOpts.DNSNameservers = resourceSubnetDNSNameserversV2(d) 227 } 228 229 if d.HasChange("host_routes") { 230 updateOpts.HostRoutes = resourceSubnetHostRoutesV2(d) 231 } 232 233 if d.HasChange("enable_dhcp") { 234 v := d.Get("enable_dhcp").(bool) 235 updateOpts.EnableDHCP = &v 236 } 237 238 log.Printf("[DEBUG] Updating Subnet %s with options: %+v", d.Id(), updateOpts) 239 240 _, err = subnets.Update(networkingClient, d.Id(), updateOpts).Extract() 241 if err != nil { 242 return fmt.Errorf("Error updating OpenStack Neutron Subnet: %s", err) 243 } 244 245 return resourceNetworkingSubnetV2Read(d, meta) 246 } 247 248 func resourceNetworkingSubnetV2Delete(d *schema.ResourceData, meta interface{}) error { 249 config := meta.(*Config) 250 networkingClient, err := config.networkingV2Client(d.Get("region").(string)) 251 if err != nil { 252 return fmt.Errorf("Error creating OpenStack networking client: %s", err) 253 } 254 255 stateConf := &resource.StateChangeConf{ 256 Pending: []string{"ACTIVE"}, 257 Target: []string{"DELETED"}, 258 Refresh: waitForSubnetDelete(networkingClient, d.Id()), 259 Timeout: 10 * time.Minute, 260 Delay: 5 * time.Second, 261 MinTimeout: 3 * time.Second, 262 } 263 264 _, err = stateConf.WaitForState() 265 if err != nil { 266 return fmt.Errorf("Error deleting OpenStack Neutron Subnet: %s", err) 267 } 268 269 d.SetId("") 270 return nil 271 } 272 273 func resourceSubnetAllocationPoolsV2(d *schema.ResourceData) []subnets.AllocationPool { 274 rawAPs := d.Get("allocation_pools").([]interface{}) 275 aps := make([]subnets.AllocationPool, len(rawAPs)) 276 for i, raw := range rawAPs { 277 rawMap := raw.(map[string]interface{}) 278 aps[i] = subnets.AllocationPool{ 279 Start: rawMap["start"].(string), 280 End: rawMap["end"].(string), 281 } 282 } 283 return aps 284 } 285 286 func resourceSubnetDNSNameserversV2(d *schema.ResourceData) []string { 287 rawDNSN := d.Get("dns_nameservers").(*schema.Set) 288 dnsn := make([]string, rawDNSN.Len()) 289 for i, raw := range rawDNSN.List() { 290 dnsn[i] = raw.(string) 291 } 292 return dnsn 293 } 294 295 func resourceSubnetHostRoutesV2(d *schema.ResourceData) []subnets.HostRoute { 296 rawHR := d.Get("host_routes").([]interface{}) 297 hr := make([]subnets.HostRoute, len(rawHR)) 298 for i, raw := range rawHR { 299 rawMap := raw.(map[string]interface{}) 300 hr[i] = subnets.HostRoute{ 301 DestinationCIDR: rawMap["destination_cidr"].(string), 302 NextHop: rawMap["next_hop"].(string), 303 } 304 } 305 return hr 306 } 307 308 func waitForSubnetActive(networkingClient *gophercloud.ServiceClient, subnetId string) resource.StateRefreshFunc { 309 return func() (interface{}, string, error) { 310 s, err := subnets.Get(networkingClient, subnetId).Extract() 311 if err != nil { 312 return nil, "", err 313 } 314 315 log.Printf("[DEBUG] OpenStack Neutron Subnet: %+v", s) 316 return s, "ACTIVE", nil 317 } 318 } 319 320 func waitForSubnetDelete(networkingClient *gophercloud.ServiceClient, subnetId string) resource.StateRefreshFunc { 321 return func() (interface{}, string, error) { 322 log.Printf("[DEBUG] Attempting to delete OpenStack Subnet %s.\n", subnetId) 323 324 s, err := subnets.Get(networkingClient, subnetId).Extract() 325 if err != nil { 326 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 327 if !ok { 328 return s, "ACTIVE", err 329 } 330 if errCode.Actual == 404 { 331 log.Printf("[DEBUG] Successfully deleted OpenStack Subnet %s", subnetId) 332 return s, "DELETED", nil 333 } 334 } 335 336 err = subnets.Delete(networkingClient, subnetId).ExtractErr() 337 if err != nil { 338 errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError) 339 if !ok { 340 return s, "ACTIVE", err 341 } 342 if errCode.Actual == 404 { 343 log.Printf("[DEBUG] Successfully deleted OpenStack Subnet %s", subnetId) 344 return s, "DELETED", nil 345 } 346 } 347 348 log.Printf("[DEBUG] OpenStack Subnet %s still active.\n", subnetId) 349 return s, "ACTIVE", nil 350 } 351 }