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  }