github.com/pmcatominey/terraform@v0.7.0-rc2.0.20160708105029-1401a52a5cc5/builtin/providers/openstack/resource_openstack_networking_port_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/ports"
    13  )
    14  
    15  func resourceNetworkingPortV2() *schema.Resource {
    16  	return &schema.Resource{
    17  		Create: resourceNetworkingPortV2Create,
    18  		Read:   resourceNetworkingPortV2Read,
    19  		Update: resourceNetworkingPortV2Update,
    20  		Delete: resourceNetworkingPortV2Delete,
    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  			"name": &schema.Schema{
    30  				Type:     schema.TypeString,
    31  				Optional: true,
    32  				ForceNew: false,
    33  			},
    34  			"network_id": &schema.Schema{
    35  				Type:     schema.TypeString,
    36  				Required: true,
    37  				ForceNew: true,
    38  			},
    39  			"admin_state_up": &schema.Schema{
    40  				Type:     schema.TypeBool,
    41  				Optional: true,
    42  				ForceNew: false,
    43  				Computed: true,
    44  			},
    45  			"mac_address": &schema.Schema{
    46  				Type:     schema.TypeString,
    47  				Optional: true,
    48  				ForceNew: true,
    49  				Computed: true,
    50  			},
    51  			"tenant_id": &schema.Schema{
    52  				Type:     schema.TypeString,
    53  				Optional: true,
    54  				ForceNew: true,
    55  				Computed: true,
    56  			},
    57  			"device_owner": &schema.Schema{
    58  				Type:     schema.TypeString,
    59  				Optional: true,
    60  				ForceNew: true,
    61  				Computed: true,
    62  			},
    63  			"security_group_ids": &schema.Schema{
    64  				Type:     schema.TypeSet,
    65  				Optional: true,
    66  				ForceNew: false,
    67  				Computed: true,
    68  				Elem:     &schema.Schema{Type: schema.TypeString},
    69  				Set:      schema.HashString,
    70  			},
    71  			"device_id": &schema.Schema{
    72  				Type:     schema.TypeString,
    73  				Optional: true,
    74  				ForceNew: true,
    75  				Computed: true,
    76  			},
    77  			"fixed_ip": &schema.Schema{
    78  				Type:     schema.TypeList,
    79  				Optional: true,
    80  				ForceNew: false,
    81  				Computed: true,
    82  				Elem: &schema.Resource{
    83  					Schema: map[string]*schema.Schema{
    84  						"subnet_id": &schema.Schema{
    85  							Type:     schema.TypeString,
    86  							Required: true,
    87  						},
    88  						"ip_address": &schema.Schema{
    89  							Type:     schema.TypeString,
    90  							Optional: true,
    91  							Computed: true,
    92  						},
    93  					},
    94  				},
    95  			},
    96  		},
    97  	}
    98  }
    99  
   100  func resourceNetworkingPortV2Create(d *schema.ResourceData, meta interface{}) error {
   101  	config := meta.(*Config)
   102  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   103  	if err != nil {
   104  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   105  	}
   106  
   107  	createOpts := ports.CreateOpts{
   108  		Name:           d.Get("name").(string),
   109  		AdminStateUp:   resourcePortAdminStateUpV2(d),
   110  		NetworkID:      d.Get("network_id").(string),
   111  		MACAddress:     d.Get("mac_address").(string),
   112  		TenantID:       d.Get("tenant_id").(string),
   113  		DeviceOwner:    d.Get("device_owner").(string),
   114  		SecurityGroups: resourcePortSecurityGroupsV2(d),
   115  		DeviceID:       d.Get("device_id").(string),
   116  		FixedIPs:       resourcePortFixedIpsV2(d),
   117  	}
   118  
   119  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   120  	p, err := ports.Create(networkingClient, createOpts).Extract()
   121  	if err != nil {
   122  		return fmt.Errorf("Error creating OpenStack Neutron network: %s", err)
   123  	}
   124  	log.Printf("[INFO] Network ID: %s", p.ID)
   125  
   126  	log.Printf("[DEBUG] Waiting for OpenStack Neutron Port (%s) to become available.", p.ID)
   127  
   128  	stateConf := &resource.StateChangeConf{
   129  		Target:     []string{"ACTIVE"},
   130  		Refresh:    waitForNetworkPortActive(networkingClient, p.ID),
   131  		Timeout:    2 * time.Minute,
   132  		Delay:      5 * time.Second,
   133  		MinTimeout: 3 * time.Second,
   134  	}
   135  
   136  	_, err = stateConf.WaitForState()
   137  
   138  	d.SetId(p.ID)
   139  
   140  	return resourceNetworkingPortV2Read(d, meta)
   141  }
   142  
   143  func resourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) error {
   144  	config := meta.(*Config)
   145  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   146  	if err != nil {
   147  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   148  	}
   149  
   150  	p, err := ports.Get(networkingClient, d.Id()).Extract()
   151  	if err != nil {
   152  		return CheckDeleted(d, err, "port")
   153  	}
   154  
   155  	log.Printf("[DEBUG] Retreived Port %s: %+v", d.Id(), p)
   156  
   157  	d.Set("name", p.Name)
   158  	d.Set("admin_state_up", p.AdminStateUp)
   159  	d.Set("network_id", p.NetworkID)
   160  	d.Set("mac_address", p.MACAddress)
   161  	d.Set("tenant_id", p.TenantID)
   162  	d.Set("device_owner", p.DeviceOwner)
   163  	d.Set("security_group_ids", p.SecurityGroups)
   164  	d.Set("device_id", p.DeviceID)
   165  
   166  	// Convert FixedIPs to list of map
   167  	var ips []map[string]interface{}
   168  	for _, ipObject := range p.FixedIPs {
   169  		ip := make(map[string]interface{})
   170  		ip["subnet_id"] = ipObject.SubnetID
   171  		ip["ip_address"] = ipObject.IPAddress
   172  		ips = append(ips, ip)
   173  	}
   174  	d.Set("fixed_ip", ips)
   175  
   176  	return nil
   177  }
   178  
   179  func resourceNetworkingPortV2Update(d *schema.ResourceData, meta interface{}) error {
   180  	config := meta.(*Config)
   181  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   182  	if err != nil {
   183  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   184  	}
   185  
   186  	var updateOpts ports.UpdateOpts
   187  
   188  	if d.HasChange("name") {
   189  		updateOpts.Name = d.Get("name").(string)
   190  	}
   191  
   192  	if d.HasChange("admin_state_up") {
   193  		updateOpts.AdminStateUp = resourcePortAdminStateUpV2(d)
   194  	}
   195  
   196  	if d.HasChange("device_owner") {
   197  		updateOpts.DeviceOwner = d.Get("device_owner").(string)
   198  	}
   199  
   200  	if d.HasChange("security_group_ids") {
   201  		updateOpts.SecurityGroups = resourcePortSecurityGroupsV2(d)
   202  	}
   203  
   204  	if d.HasChange("device_id") {
   205  		updateOpts.DeviceID = d.Get("device_id").(string)
   206  	}
   207  
   208  	if d.HasChange("fixed_ip") {
   209  		updateOpts.FixedIPs = resourcePortFixedIpsV2(d)
   210  	}
   211  
   212  	log.Printf("[DEBUG] Updating Port %s with options: %+v", d.Id(), updateOpts)
   213  
   214  	_, err = ports.Update(networkingClient, d.Id(), updateOpts).Extract()
   215  	if err != nil {
   216  		return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err)
   217  	}
   218  
   219  	return resourceNetworkingPortV2Read(d, meta)
   220  }
   221  
   222  func resourceNetworkingPortV2Delete(d *schema.ResourceData, meta interface{}) error {
   223  	config := meta.(*Config)
   224  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   225  	if err != nil {
   226  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   227  	}
   228  
   229  	stateConf := &resource.StateChangeConf{
   230  		Pending:    []string{"ACTIVE"},
   231  		Target:     []string{"DELETED"},
   232  		Refresh:    waitForNetworkPortDelete(networkingClient, d.Id()),
   233  		Timeout:    2 * time.Minute,
   234  		Delay:      5 * time.Second,
   235  		MinTimeout: 3 * time.Second,
   236  	}
   237  
   238  	_, err = stateConf.WaitForState()
   239  	if err != nil {
   240  		return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err)
   241  	}
   242  
   243  	d.SetId("")
   244  	return nil
   245  }
   246  
   247  func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string {
   248  	rawSecurityGroups := d.Get("security_group_ids").(*schema.Set)
   249  	groups := make([]string, rawSecurityGroups.Len())
   250  	for i, raw := range rawSecurityGroups.List() {
   251  		groups[i] = raw.(string)
   252  	}
   253  	return groups
   254  }
   255  
   256  func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} {
   257  	rawIP := d.Get("fixed_ip").([]interface{})
   258  
   259  	if len(rawIP) == 0 {
   260  		return nil
   261  	}
   262  
   263  	ip := make([]ports.IP, len(rawIP))
   264  	for i, raw := range rawIP {
   265  		rawMap := raw.(map[string]interface{})
   266  		ip[i] = ports.IP{
   267  			SubnetID:  rawMap["subnet_id"].(string),
   268  			IPAddress: rawMap["ip_address"].(string),
   269  		}
   270  	}
   271  	return ip
   272  
   273  }
   274  
   275  func resourcePortAdminStateUpV2(d *schema.ResourceData) *bool {
   276  	value := false
   277  
   278  	if raw, ok := d.GetOk("admin_state_up"); ok && raw == true {
   279  		value = true
   280  	}
   281  
   282  	return &value
   283  }
   284  
   285  func waitForNetworkPortActive(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
   286  	return func() (interface{}, string, error) {
   287  		p, err := ports.Get(networkingClient, portId).Extract()
   288  		if err != nil {
   289  			return nil, "", err
   290  		}
   291  
   292  		log.Printf("[DEBUG] OpenStack Neutron Port: %+v", p)
   293  		if p.Status == "DOWN" || p.Status == "ACTIVE" {
   294  			return p, "ACTIVE", nil
   295  		}
   296  
   297  		return p, p.Status, nil
   298  	}
   299  }
   300  
   301  func waitForNetworkPortDelete(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
   302  	return func() (interface{}, string, error) {
   303  		log.Printf("[DEBUG] Attempting to delete OpenStack Neutron Port %s", portId)
   304  
   305  		p, err := ports.Get(networkingClient, portId).Extract()
   306  		if err != nil {
   307  			errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
   308  			if !ok {
   309  				return p, "ACTIVE", err
   310  			}
   311  			if errCode.Actual == 404 {
   312  				log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
   313  				return p, "DELETED", nil
   314  			}
   315  		}
   316  
   317  		err = ports.Delete(networkingClient, portId).ExtractErr()
   318  		if err != nil {
   319  			errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
   320  			if !ok {
   321  				return p, "ACTIVE", err
   322  			}
   323  			if errCode.Actual == 404 {
   324  				log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
   325  				return p, "DELETED", nil
   326  			}
   327  		}
   328  
   329  		log.Printf("[DEBUG] OpenStack Port %s still active.\n", portId)
   330  		return p, "ACTIVE", nil
   331  	}
   332  }