github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/openstack/resource_openstack_networking_network_v2.go (about)

     1  package openstack
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"strconv"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/resource"
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  
    12  	"github.com/gophercloud/gophercloud"
    13  	"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/provider"
    14  	"github.com/gophercloud/gophercloud/openstack/networking/v2/networks"
    15  )
    16  
    17  func resourceNetworkingNetworkV2() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceNetworkingNetworkV2Create,
    20  		Read:   resourceNetworkingNetworkV2Read,
    21  		Update: resourceNetworkingNetworkV2Update,
    22  		Delete: resourceNetworkingNetworkV2Delete,
    23  		Importer: &schema.ResourceImporter{
    24  			State: schema.ImportStatePassthrough,
    25  		},
    26  
    27  		Timeouts: &schema.ResourceTimeout{
    28  			Create: schema.DefaultTimeout(10 * time.Minute),
    29  			Delete: schema.DefaultTimeout(10 * time.Minute),
    30  		},
    31  
    32  		Schema: map[string]*schema.Schema{
    33  			"region": &schema.Schema{
    34  				Type:        schema.TypeString,
    35  				Required:    true,
    36  				ForceNew:    true,
    37  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    38  			},
    39  			"name": &schema.Schema{
    40  				Type:     schema.TypeString,
    41  				Optional: true,
    42  				ForceNew: false,
    43  			},
    44  			"admin_state_up": &schema.Schema{
    45  				Type:     schema.TypeString,
    46  				Optional: true,
    47  				ForceNew: false,
    48  				Computed: true,
    49  			},
    50  			"shared": &schema.Schema{
    51  				Type:     schema.TypeString,
    52  				Optional: true,
    53  				ForceNew: false,
    54  				Computed: true,
    55  			},
    56  			"tenant_id": &schema.Schema{
    57  				Type:     schema.TypeString,
    58  				Optional: true,
    59  				ForceNew: true,
    60  				Computed: true,
    61  			},
    62  			"segments": &schema.Schema{
    63  				Type:     schema.TypeList,
    64  				Optional: true,
    65  				ForceNew: true,
    66  				Elem: &schema.Resource{
    67  					Schema: map[string]*schema.Schema{
    68  						"physical_network": &schema.Schema{
    69  							Type:     schema.TypeString,
    70  							Optional: true,
    71  							ForceNew: true,
    72  						},
    73  						"network_type": &schema.Schema{
    74  							Type:     schema.TypeString,
    75  							Optional: true,
    76  							ForceNew: true,
    77  						},
    78  						"segmentation_id": &schema.Schema{
    79  							Type:     schema.TypeInt,
    80  							Optional: true,
    81  							ForceNew: true,
    82  						},
    83  					},
    84  				},
    85  			},
    86  			"value_specs": &schema.Schema{
    87  				Type:     schema.TypeMap,
    88  				Optional: true,
    89  				ForceNew: true,
    90  			},
    91  		},
    92  	}
    93  }
    94  
    95  func resourceNetworkingNetworkV2Create(d *schema.ResourceData, meta interface{}) error {
    96  	config := meta.(*Config)
    97  	networkingClient, err := config.networkingV2Client(GetRegion(d))
    98  	if err != nil {
    99  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   100  	}
   101  
   102  	createOpts := NetworkCreateOpts{
   103  		networks.CreateOpts{
   104  			Name:     d.Get("name").(string),
   105  			TenantID: d.Get("tenant_id").(string),
   106  		},
   107  		MapValueSpecs(d),
   108  	}
   109  
   110  	asuRaw := d.Get("admin_state_up").(string)
   111  	if asuRaw != "" {
   112  		asu, err := strconv.ParseBool(asuRaw)
   113  		if err != nil {
   114  			return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
   115  		}
   116  		createOpts.AdminStateUp = &asu
   117  	}
   118  
   119  	sharedRaw := d.Get("shared").(string)
   120  	if sharedRaw != "" {
   121  		shared, err := strconv.ParseBool(sharedRaw)
   122  		if err != nil {
   123  			return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err)
   124  		}
   125  		createOpts.Shared = &shared
   126  	}
   127  
   128  	segments := resourceNetworkingNetworkV2Segments(d)
   129  
   130  	n := &networks.Network{}
   131  	if len(segments) > 0 {
   132  		providerCreateOpts := provider.CreateOptsExt{
   133  			CreateOptsBuilder: createOpts,
   134  			Segments:          segments,
   135  		}
   136  		log.Printf("[DEBUG] Create Options: %#v", providerCreateOpts)
   137  		n, err = networks.Create(networkingClient, providerCreateOpts).Extract()
   138  	} else {
   139  		log.Printf("[DEBUG] Create Options: %#v", createOpts)
   140  		n, err = networks.Create(networkingClient, createOpts).Extract()
   141  	}
   142  
   143  	if err != nil {
   144  		return fmt.Errorf("Error creating OpenStack Neutron network: %s", err)
   145  	}
   146  
   147  	log.Printf("[INFO] Network ID: %s", n.ID)
   148  
   149  	log.Printf("[DEBUG] Waiting for Network (%s) to become available", n.ID)
   150  
   151  	stateConf := &resource.StateChangeConf{
   152  		Pending:    []string{"BUILD"},
   153  		Target:     []string{"ACTIVE"},
   154  		Refresh:    waitForNetworkActive(networkingClient, n.ID),
   155  		Timeout:    d.Timeout(schema.TimeoutCreate),
   156  		Delay:      5 * time.Second,
   157  		MinTimeout: 3 * time.Second,
   158  	}
   159  
   160  	_, err = stateConf.WaitForState()
   161  
   162  	d.SetId(n.ID)
   163  
   164  	return resourceNetworkingNetworkV2Read(d, meta)
   165  }
   166  
   167  func resourceNetworkingNetworkV2Read(d *schema.ResourceData, meta interface{}) error {
   168  	config := meta.(*Config)
   169  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   170  	if err != nil {
   171  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   172  	}
   173  
   174  	n, err := networks.Get(networkingClient, d.Id()).Extract()
   175  	if err != nil {
   176  		return CheckDeleted(d, err, "network")
   177  	}
   178  
   179  	log.Printf("[DEBUG] Retrieved Network %s: %+v", d.Id(), n)
   180  
   181  	d.Set("name", n.Name)
   182  	d.Set("admin_state_up", strconv.FormatBool(n.AdminStateUp))
   183  	d.Set("shared", strconv.FormatBool(n.Shared))
   184  	d.Set("tenant_id", n.TenantID)
   185  	d.Set("region", GetRegion(d))
   186  
   187  	return nil
   188  }
   189  
   190  func resourceNetworkingNetworkV2Update(d *schema.ResourceData, meta interface{}) error {
   191  	config := meta.(*Config)
   192  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   193  	if err != nil {
   194  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   195  	}
   196  
   197  	var updateOpts networks.UpdateOpts
   198  	if d.HasChange("name") {
   199  		updateOpts.Name = d.Get("name").(string)
   200  	}
   201  	if d.HasChange("admin_state_up") {
   202  		asuRaw := d.Get("admin_state_up").(string)
   203  		if asuRaw != "" {
   204  			asu, err := strconv.ParseBool(asuRaw)
   205  			if err != nil {
   206  				return fmt.Errorf("admin_state_up, if provided, must be either 'true' or 'false'")
   207  			}
   208  			updateOpts.AdminStateUp = &asu
   209  		}
   210  	}
   211  	if d.HasChange("shared") {
   212  		sharedRaw := d.Get("shared").(string)
   213  		if sharedRaw != "" {
   214  			shared, err := strconv.ParseBool(sharedRaw)
   215  			if err != nil {
   216  				return fmt.Errorf("shared, if provided, must be either 'true' or 'false': %v", err)
   217  			}
   218  			updateOpts.Shared = &shared
   219  		}
   220  	}
   221  
   222  	log.Printf("[DEBUG] Updating Network %s with options: %+v", d.Id(), updateOpts)
   223  
   224  	_, err = networks.Update(networkingClient, d.Id(), updateOpts).Extract()
   225  	if err != nil {
   226  		return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err)
   227  	}
   228  
   229  	return resourceNetworkingNetworkV2Read(d, meta)
   230  }
   231  
   232  func resourceNetworkingNetworkV2Delete(d *schema.ResourceData, meta interface{}) error {
   233  	config := meta.(*Config)
   234  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   235  	if err != nil {
   236  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   237  	}
   238  
   239  	stateConf := &resource.StateChangeConf{
   240  		Pending:    []string{"ACTIVE"},
   241  		Target:     []string{"DELETED"},
   242  		Refresh:    waitForNetworkDelete(networkingClient, d.Id()),
   243  		Timeout:    d.Timeout(schema.TimeoutDelete),
   244  		Delay:      5 * time.Second,
   245  		MinTimeout: 3 * time.Second,
   246  	}
   247  
   248  	_, err = stateConf.WaitForState()
   249  	if err != nil {
   250  		return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err)
   251  	}
   252  
   253  	d.SetId("")
   254  	return nil
   255  }
   256  
   257  func resourceNetworkingNetworkV2Segments(d *schema.ResourceData) (providerSegments []provider.Segment) {
   258  	segments := d.Get("segments").([]interface{})
   259  	for _, v := range segments {
   260  		var segment provider.Segment
   261  		segmentMap := v.(map[string]interface{})
   262  
   263  		if v, ok := segmentMap["physical_network"].(string); ok {
   264  			segment.PhysicalNetwork = v
   265  		}
   266  
   267  		if v, ok := segmentMap["network_type"].(string); ok {
   268  			segment.NetworkType = v
   269  		}
   270  
   271  		if v, ok := segmentMap["segmentation_id"].(int); ok {
   272  			segment.SegmentationID = v
   273  		}
   274  
   275  		providerSegments = append(providerSegments, segment)
   276  	}
   277  	return
   278  }
   279  
   280  func waitForNetworkActive(networkingClient *gophercloud.ServiceClient, networkId string) resource.StateRefreshFunc {
   281  	return func() (interface{}, string, error) {
   282  		n, err := networks.Get(networkingClient, networkId).Extract()
   283  		if err != nil {
   284  			return nil, "", err
   285  		}
   286  
   287  		log.Printf("[DEBUG] OpenStack Neutron Network: %+v", n)
   288  		if n.Status == "DOWN" || n.Status == "ACTIVE" {
   289  			return n, "ACTIVE", nil
   290  		}
   291  
   292  		return n, n.Status, nil
   293  	}
   294  }
   295  
   296  func waitForNetworkDelete(networkingClient *gophercloud.ServiceClient, networkId string) resource.StateRefreshFunc {
   297  	return func() (interface{}, string, error) {
   298  		log.Printf("[DEBUG] Attempting to delete OpenStack Network %s.\n", networkId)
   299  
   300  		n, err := networks.Get(networkingClient, networkId).Extract()
   301  		if err != nil {
   302  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   303  				log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId)
   304  				return n, "DELETED", nil
   305  			}
   306  			return n, "ACTIVE", err
   307  		}
   308  
   309  		err = networks.Delete(networkingClient, networkId).ExtractErr()
   310  		if err != nil {
   311  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   312  				log.Printf("[DEBUG] Successfully deleted OpenStack Network %s", networkId)
   313  				return n, "DELETED", nil
   314  			}
   315  			if errCode, ok := err.(gophercloud.ErrUnexpectedResponseCode); ok {
   316  				if errCode.Actual == 409 {
   317  					return n, "ACTIVE", nil
   318  				}
   319  			}
   320  			return n, "ACTIVE", err
   321  		}
   322  
   323  		log.Printf("[DEBUG] OpenStack Network %s still active.\n", networkId)
   324  		return n, "ACTIVE", nil
   325  	}
   326  }