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  }