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