github.com/cbroglie/terraform@v0.7.0-rc3.0.20170410193827-735dfc416d46/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  	var updateOpts ports.UpdateOpts
   240  
   241  	if d.HasChange("name") {
   242  		updateOpts.Name = d.Get("name").(string)
   243  	}
   244  
   245  	if d.HasChange("admin_state_up") {
   246  		updateOpts.AdminStateUp = resourcePortAdminStateUpV2(d)
   247  	}
   248  
   249  	if d.HasChange("device_owner") {
   250  		updateOpts.DeviceOwner = d.Get("device_owner").(string)
   251  	}
   252  
   253  	if d.HasChange("security_group_ids") {
   254  		updateOpts.SecurityGroups = resourcePortSecurityGroupsV2(d)
   255  	}
   256  
   257  	if d.HasChange("device_id") {
   258  		updateOpts.DeviceID = d.Get("device_id").(string)
   259  	}
   260  
   261  	if d.HasChange("fixed_ip") {
   262  		updateOpts.FixedIPs = resourcePortFixedIpsV2(d)
   263  	}
   264  
   265  	if d.HasChange("allowed_address_pairs") {
   266  		updateOpts.AllowedAddressPairs = resourceAllowedAddressPairsV2(d)
   267  	}
   268  
   269  	log.Printf("[DEBUG] Updating Port %s with options: %+v", d.Id(), updateOpts)
   270  
   271  	_, err = ports.Update(networkingClient, d.Id(), updateOpts).Extract()
   272  	if err != nil {
   273  		return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err)
   274  	}
   275  
   276  	return resourceNetworkingPortV2Read(d, meta)
   277  }
   278  
   279  func resourceNetworkingPortV2Delete(d *schema.ResourceData, meta interface{}) error {
   280  	config := meta.(*Config)
   281  	networkingClient, err := config.networkingV2Client(GetRegion(d))
   282  	if err != nil {
   283  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   284  	}
   285  
   286  	stateConf := &resource.StateChangeConf{
   287  		Pending:    []string{"ACTIVE"},
   288  		Target:     []string{"DELETED"},
   289  		Refresh:    waitForNetworkPortDelete(networkingClient, d.Id()),
   290  		Timeout:    d.Timeout(schema.TimeoutDelete),
   291  		Delay:      5 * time.Second,
   292  		MinTimeout: 3 * time.Second,
   293  	}
   294  
   295  	_, err = stateConf.WaitForState()
   296  	if err != nil {
   297  		return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err)
   298  	}
   299  
   300  	d.SetId("")
   301  	return nil
   302  }
   303  
   304  func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string {
   305  	rawSecurityGroups := d.Get("security_group_ids").(*schema.Set)
   306  	groups := make([]string, rawSecurityGroups.Len())
   307  	for i, raw := range rawSecurityGroups.List() {
   308  		groups[i] = raw.(string)
   309  	}
   310  	return groups
   311  }
   312  
   313  func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} {
   314  	rawIP := d.Get("fixed_ip").([]interface{})
   315  
   316  	if len(rawIP) == 0 {
   317  		return nil
   318  	}
   319  
   320  	ip := make([]ports.IP, len(rawIP))
   321  	for i, raw := range rawIP {
   322  		rawMap := raw.(map[string]interface{})
   323  		ip[i] = ports.IP{
   324  			SubnetID:  rawMap["subnet_id"].(string),
   325  			IPAddress: rawMap["ip_address"].(string),
   326  		}
   327  	}
   328  	return ip
   329  }
   330  
   331  func resourceAllowedAddressPairsV2(d *schema.ResourceData) []ports.AddressPair {
   332  	// ports.AddressPair
   333  	rawPairs := d.Get("allowed_address_pairs").(*schema.Set).List()
   334  
   335  	if len(rawPairs) == 0 {
   336  		return nil
   337  	}
   338  
   339  	pairs := make([]ports.AddressPair, len(rawPairs))
   340  	for i, raw := range rawPairs {
   341  		rawMap := raw.(map[string]interface{})
   342  		pairs[i] = ports.AddressPair{
   343  			IPAddress:  rawMap["ip_address"].(string),
   344  			MACAddress: rawMap["mac_address"].(string),
   345  		}
   346  	}
   347  	return pairs
   348  }
   349  
   350  func resourcePortAdminStateUpV2(d *schema.ResourceData) *bool {
   351  	value := false
   352  
   353  	if raw, ok := d.GetOk("admin_state_up"); ok && raw == true {
   354  		value = true
   355  	}
   356  
   357  	return &value
   358  }
   359  
   360  func allowedAddressPairsHash(v interface{}) int {
   361  	var buf bytes.Buffer
   362  	m := v.(map[string]interface{})
   363  	buf.WriteString(fmt.Sprintf("%s", m["ip_address"].(string)))
   364  
   365  	return hashcode.String(buf.String())
   366  }
   367  
   368  func waitForNetworkPortActive(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
   369  	return func() (interface{}, string, error) {
   370  		p, err := ports.Get(networkingClient, portId).Extract()
   371  		if err != nil {
   372  			return nil, "", err
   373  		}
   374  
   375  		log.Printf("[DEBUG] OpenStack Neutron Port: %+v", p)
   376  		if p.Status == "DOWN" || p.Status == "ACTIVE" {
   377  			return p, "ACTIVE", nil
   378  		}
   379  
   380  		return p, p.Status, nil
   381  	}
   382  }
   383  
   384  func waitForNetworkPortDelete(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
   385  	return func() (interface{}, string, error) {
   386  		log.Printf("[DEBUG] Attempting to delete OpenStack Neutron Port %s", portId)
   387  
   388  		p, err := ports.Get(networkingClient, portId).Extract()
   389  		if err != nil {
   390  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   391  				log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
   392  				return p, "DELETED", nil
   393  			}
   394  			return p, "ACTIVE", err
   395  		}
   396  
   397  		err = ports.Delete(networkingClient, portId).ExtractErr()
   398  		if err != nil {
   399  			if _, ok := err.(gophercloud.ErrDefault404); ok {
   400  				log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
   401  				return p, "DELETED", nil
   402  			}
   403  			return p, "ACTIVE", err
   404  		}
   405  
   406  		log.Printf("[DEBUG] OpenStack Port %s still active.\n", portId)
   407  		return p, "ACTIVE", nil
   408  	}
   409  }