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