github.com/danp/terraform@v0.9.5-0.20170426144147-39d740081351/builtin/providers/openstack/resource_openstack_networking_port_v2.go (about)

     1  package openstack
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"time"
     8  
     9  	"github.com/hashicorp/terraform/helper/hashcode"
    10  	"github.com/hashicorp/terraform/helper/resource"
    11  	"github.com/hashicorp/terraform/helper/schema"
    12  
    13  	"github.com/gophercloud/gophercloud"
    14  	"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
    15  )
    16  
    17  func resourceNetworkingPortV2() *schema.Resource {
    18  	return &schema.Resource{
    19  		Create: resourceNetworkingPortV2Create,
    20  		Read:   resourceNetworkingPortV2Read,
    21  		Update: resourceNetworkingPortV2Update,
    22  		Delete: resourceNetworkingPortV2Delete,
    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  			"network_id": &schema.Schema{
    45  				Type:     schema.TypeString,
    46  				Required: true,
    47  				ForceNew: true,
    48  			},
    49  			"admin_state_up": &schema.Schema{
    50  				Type:     schema.TypeBool,
    51  				Optional: true,
    52  				ForceNew: false,
    53  				Computed: true,
    54  			},
    55  			"mac_address": &schema.Schema{
    56  				Type:     schema.TypeString,
    57  				Optional: true,
    58  				ForceNew: true,
    59  				Computed: true,
    60  			},
    61  			"tenant_id": &schema.Schema{
    62  				Type:     schema.TypeString,
    63  				Optional: true,
    64  				ForceNew: true,
    65  				Computed: true,
    66  			},
    67  			"device_owner": &schema.Schema{
    68  				Type:     schema.TypeString,
    69  				Optional: true,
    70  				ForceNew: true,
    71  				Computed: true,
    72  			},
    73  			"security_group_ids": &schema.Schema{
    74  				Type:     schema.TypeSet,
    75  				Optional: true,
    76  				ForceNew: false,
    77  				Computed: true,
    78  				Elem:     &schema.Schema{Type: schema.TypeString},
    79  				Set:      schema.HashString,
    80  			},
    81  			"device_id": &schema.Schema{
    82  				Type:     schema.TypeString,
    83  				Optional: true,
    84  				ForceNew: true,
    85  				Computed: true,
    86  			},
    87  			"fixed_ip": &schema.Schema{
    88  				Type:     schema.TypeList,
    89  				Optional: true,
    90  				ForceNew: false,
    91  				Elem: &schema.Resource{
    92  					Schema: map[string]*schema.Schema{
    93  						"subnet_id": &schema.Schema{
    94  							Type:     schema.TypeString,
    95  							Required: true,
    96  						},
    97  						"ip_address": &schema.Schema{
    98  							Type:     schema.TypeString,
    99  							Optional: true,
   100  						},
   101  					},
   102  				},
   103  			},
   104  			"allowed_address_pairs": &schema.Schema{
   105  				Type:     schema.TypeSet,
   106  				Optional: true,
   107  				ForceNew: false,
   108  				Computed: true,
   109  				Set:      allowedAddressPairsHash,
   110  				Elem: &schema.Resource{
   111  					Schema: map[string]*schema.Schema{
   112  						"ip_address": &schema.Schema{
   113  							Type:     schema.TypeString,
   114  							Required: true,
   115  						},
   116  						"mac_address": &schema.Schema{
   117  							Type:     schema.TypeString,
   118  							Optional: true,
   119  							Computed: true,
   120  						},
   121  					},
   122  				},
   123  			},
   124  			"value_specs": &schema.Schema{
   125  				Type:     schema.TypeMap,
   126  				Optional: true,
   127  				ForceNew: true,
   128  			},
   129  			"all_fixed_ips": &schema.Schema{
   130  				Type:     schema.TypeList,
   131  				Computed: true,
   132  				Elem:     &schema.Schema{Type: schema.TypeString},
   133  			},
   134  		},
   135  	}
   136  }
   137  
   138  func resourceNetworkingPortV2Create(d *schema.ResourceData, meta interface{}) error {
   139  	config := meta.(*Config)
   140  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   141  	if err != nil {
   142  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   143  	}
   144  
   145  	createOpts := PortCreateOpts{
   146  		ports.CreateOpts{
   147  			Name:                d.Get("name").(string),
   148  			AdminStateUp:        resourcePortAdminStateUpV2(d),
   149  			NetworkID:           d.Get("network_id").(string),
   150  			MACAddress:          d.Get("mac_address").(string),
   151  			TenantID:            d.Get("tenant_id").(string),
   152  			DeviceOwner:         d.Get("device_owner").(string),
   153  			SecurityGroups:      resourcePortSecurityGroupsV2(d),
   154  			DeviceID:            d.Get("device_id").(string),
   155  			FixedIPs:            resourcePortFixedIpsV2(d),
   156  			AllowedAddressPairs: resourceAllowedAddressPairsV2(d),
   157  		},
   158  		MapValueSpecs(d),
   159  	}
   160  
   161  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   162  	p, err := ports.Create(networkingClient, createOpts).Extract()
   163  	if err != nil {
   164  		return fmt.Errorf("Error creating OpenStack Neutron network: %s", err)
   165  	}
   166  	log.Printf("[INFO] Network ID: %s", p.ID)
   167  
   168  	log.Printf("[DEBUG] Waiting for OpenStack Neutron Port (%s) to become available.", p.ID)
   169  
   170  	stateConf := &resource.StateChangeConf{
   171  		Target:     []string{"ACTIVE"},
   172  		Refresh:    waitForNetworkPortActive(networkingClient, p.ID),
   173  		Timeout:    d.Timeout(schema.TimeoutCreate),
   174  		Delay:      5 * time.Second,
   175  		MinTimeout: 3 * time.Second,
   176  	}
   177  
   178  	_, err = stateConf.WaitForState()
   179  
   180  	d.SetId(p.ID)
   181  
   182  	return resourceNetworkingPortV2Read(d, meta)
   183  }
   184  
   185  func resourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) error {
   186  	config := meta.(*Config)
   187  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   188  	if err != nil {
   189  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   190  	}
   191  
   192  	p, err := ports.Get(networkingClient, d.Id()).Extract()
   193  	if err != nil {
   194  		return CheckDeleted(d, err, "port")
   195  	}
   196  
   197  	log.Printf("[DEBUG] Retrieved Port %s: %+v", d.Id(), p)
   198  
   199  	d.Set("name", p.Name)
   200  	d.Set("admin_state_up", p.AdminStateUp)
   201  	d.Set("network_id", p.NetworkID)
   202  	d.Set("mac_address", p.MACAddress)
   203  	d.Set("tenant_id", p.TenantID)
   204  	d.Set("device_owner", p.DeviceOwner)
   205  	d.Set("security_group_ids", p.SecurityGroups)
   206  	d.Set("device_id", p.DeviceID)
   207  
   208  	// Create a slice of all returned Fixed IPs.
   209  	// This will be in the order returned by the API,
   210  	// which is usually alpha-numeric.
   211  	var ips []string
   212  	for _, ipObject := range p.FixedIPs {
   213  		ips = append(ips, ipObject.IPAddress)
   214  	}
   215  	d.Set("all_fixed_ips", ips)
   216  
   217  	// Convert AllowedAddressPairs to list of map
   218  	var pairs []map[string]interface{}
   219  	for _, pairObject := range p.AllowedAddressPairs {
   220  		pair := make(map[string]interface{})
   221  		pair["ip_address"] = pairObject.IPAddress
   222  		pair["mac_address"] = pairObject.MACAddress
   223  		pairs = append(pairs, pair)
   224  	}
   225  	d.Set("allowed_address_pairs", pairs)
   226  
   227  	d.Set("region", GetRegion(d))
   228  
   229  	return nil
   230  }
   231  
   232  func resourceNetworkingPortV2Update(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  	// security_group_ids and allowed_address_pairs are able to send empty arrays
   240  	// to denote the removal of each. But their default zero-value is translated
   241  	// to "null", which has been reported to cause problems in vendor-modified
   242  	// OpenStack clouds. Therefore, we must set them in each request update.
   243  	updateOpts := ports.UpdateOpts{
   244  		AllowedAddressPairs: resourceAllowedAddressPairsV2(d),
   245  		SecurityGroups:      resourcePortSecurityGroupsV2(d),
   246  	}
   247  
   248  	if d.HasChange("name") {
   249  		updateOpts.Name = d.Get("name").(string)
   250  	}
   251  
   252  	if d.HasChange("admin_state_up") {
   253  		updateOpts.AdminStateUp = resourcePortAdminStateUpV2(d)
   254  	}
   255  
   256  	if d.HasChange("device_owner") {
   257  		updateOpts.DeviceOwner = d.Get("device_owner").(string)
   258  	}
   259  
   260  	if d.HasChange("device_id") {
   261  		updateOpts.DeviceID = d.Get("device_id").(string)
   262  	}
   263  
   264  	if d.HasChange("fixed_ip") {
   265  		updateOpts.FixedIPs = resourcePortFixedIpsV2(d)
   266  	}
   267  
   268  	log.Printf("[DEBUG] Updating Port %s with options: %+v", d.Id(), updateOpts)
   269  
   270  	_, err = ports.Update(networkingClient, d.Id(), updateOpts).Extract()
   271  	if err != nil {
   272  		return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err)
   273  	}
   274  
   275  	return resourceNetworkingPortV2Read(d, meta)
   276  }
   277  
   278  func resourceNetworkingPortV2Delete(d *schema.ResourceData, meta interface{}) error {
   279  	config := meta.(*Config)
   280  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   281  	if err != nil {
   282  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   283  	}
   284  
   285  	stateConf := &resource.StateChangeConf{
   286  		Pending:    []string{"ACTIVE"},
   287  		Target:     []string{"DELETED"},
   288  		Refresh:    waitForNetworkPortDelete(networkingClient, d.Id()),
   289  		Timeout:    d.Timeout(schema.TimeoutDelete),
   290  		Delay:      5 * time.Second,
   291  		MinTimeout: 3 * time.Second,
   292  	}
   293  
   294  	_, err = stateConf.WaitForState()
   295  	if err != nil {
   296  		return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err)
   297  	}
   298  
   299  	d.SetId("")
   300  	return nil
   301  }
   302  
   303  func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string {
   304  	rawSecurityGroups := d.Get("security_group_ids").(*schema.Set)
   305  	groups := make([]string, rawSecurityGroups.Len())
   306  	for i, raw := range rawSecurityGroups.List() {
   307  		groups[i] = raw.(string)
   308  	}
   309  	return groups
   310  }
   311  
   312  func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} {
   313  	rawIP := d.Get("fixed_ip").([]interface{})
   314  
   315  	if len(rawIP) == 0 {
   316  		return nil
   317  	}
   318  
   319  	ip := make([]ports.IP, len(rawIP))
   320  	for i, raw := range rawIP {
   321  		rawMap := raw.(map[string]interface{})
   322  		ip[i] = ports.IP{
   323  			SubnetID:  rawMap["subnet_id"].(string),
   324  			IPAddress: rawMap["ip_address"].(string),
   325  		}
   326  	}
   327  	return ip
   328  }
   329  
   330  func resourceAllowedAddressPairsV2(d *schema.ResourceData) []ports.AddressPair {
   331  	// ports.AddressPair
   332  	rawPairs := d.Get("allowed_address_pairs").(*schema.Set).List()
   333  
   334  	pairs := make([]ports.AddressPair, len(rawPairs))
   335  	for i, raw := range rawPairs {
   336  		rawMap := raw.(map[string]interface{})
   337  		pairs[i] = ports.AddressPair{
   338  			IPAddress:  rawMap["ip_address"].(string),
   339  			MACAddress: rawMap["mac_address"].(string),
   340  		}
   341  	}
   342  	return pairs
   343  }
   344  
   345  func resourcePortAdminStateUpV2(d *schema.ResourceData) *bool {
   346  	value := false
   347  
   348  	if raw, ok := d.GetOk("admin_state_up"); ok && raw == true {
   349  		value = true
   350  	}
   351  
   352  	return &value
   353  }
   354  
   355  func allowedAddressPairsHash(v interface{}) int {
   356  	var buf bytes.Buffer
   357  	m := v.(map[string]interface{})
   358  	buf.WriteString(fmt.Sprintf("%s", m["ip_address"].(string)))
   359  
   360  	return hashcode.String(buf.String())
   361  }
   362  
   363  func waitForNetworkPortActive(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
   364  	return func() (interface{}, string, error) {
   365  		p, err := ports.Get(networkingClient, portId).Extract()
   366  		if err != nil {
   367  			return nil, "", err
   368  		}
   369  
   370  		log.Printf("[DEBUG] OpenStack Neutron Port: %+v", p)
   371  		if p.Status == "DOWN" || p.Status == "ACTIVE" {
   372  			return p, "ACTIVE", nil
   373  		}
   374  
   375  		return p, p.Status, nil
   376  	}
   377  }
   378  
   379  func waitForNetworkPortDelete(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
   380  	return func() (interface{}, string, error) {
   381  		log.Printf("[DEBUG] Attempting to delete OpenStack Neutron Port %s", portId)
   382  
   383  		p, err := ports.Get(networkingClient, portId).Extract()
   384  		if err != nil {
   385  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   386  				log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
   387  				return p, "DELETED", nil
   388  			}
   389  			return p, "ACTIVE", err
   390  		}
   391  
   392  		err = ports.Delete(networkingClient, portId).ExtractErr()
   393  		if err != nil {
   394  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   395  				log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
   396  				return p, "DELETED", nil
   397  			}
   398  			return p, "ACTIVE", err
   399  		}
   400  
   401  		log.Printf("[DEBUG] OpenStack Port %s still active.\n", portId)
   402  		return p, "ACTIVE", nil
   403  	}
   404  }