github.com/richardbowden/terraform@v0.6.12-0.20160901200758-30ea22c25211/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  		Importer: &schema.ResourceImporter{
    22  			State: schema.ImportStatePassthrough,
    23  		},
    24  
    25  		Schema: map[string]*schema.Schema{
    26  			"region": &schema.Schema{
    27  				Type:        schema.TypeString,
    28  				Required:    true,
    29  				ForceNew:    true,
    30  				DefaultFunc: schema.EnvDefaultFunc("OS_REGION_NAME", ""),
    31  			},
    32  			"name": &schema.Schema{
    33  				Type:     schema.TypeString,
    34  				Optional: true,
    35  				ForceNew: false,
    36  			},
    37  			"network_id": &schema.Schema{
    38  				Type:     schema.TypeString,
    39  				Required: true,
    40  				ForceNew: true,
    41  			},
    42  			"admin_state_up": &schema.Schema{
    43  				Type:     schema.TypeBool,
    44  				Optional: true,
    45  				ForceNew: false,
    46  				Computed: true,
    47  			},
    48  			"mac_address": &schema.Schema{
    49  				Type:     schema.TypeString,
    50  				Optional: true,
    51  				ForceNew: true,
    52  				Computed: true,
    53  			},
    54  			"tenant_id": &schema.Schema{
    55  				Type:     schema.TypeString,
    56  				Optional: true,
    57  				ForceNew: true,
    58  				Computed: true,
    59  			},
    60  			"device_owner": &schema.Schema{
    61  				Type:     schema.TypeString,
    62  				Optional: true,
    63  				ForceNew: true,
    64  				Computed: true,
    65  			},
    66  			"security_group_ids": &schema.Schema{
    67  				Type:     schema.TypeSet,
    68  				Optional: true,
    69  				ForceNew: false,
    70  				Computed: true,
    71  				Elem:     &schema.Schema{Type: schema.TypeString},
    72  				Set:      schema.HashString,
    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  			"allowed_address_pairs": &schema.Schema{
   100  				Type:     schema.TypeList,
   101  				Optional: true,
   102  				ForceNew: false,
   103  				Computed: true,
   104  				Elem: &schema.Resource{
   105  					Schema: map[string]*schema.Schema{
   106  						"ip_address": &schema.Schema{
   107  							Type:     schema.TypeString,
   108  							Required: true,
   109  						},
   110  						"mac_address": &schema.Schema{
   111  							Type:     schema.TypeString,
   112  							Optional: true,
   113  							Computed: true,
   114  						},
   115  					},
   116  				},
   117  			},
   118  		},
   119  	}
   120  }
   121  
   122  func resourceNetworkingPortV2Create(d *schema.ResourceData, meta interface{}) error {
   123  	config := meta.(*Config)
   124  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   125  	if err != nil {
   126  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   127  	}
   128  
   129  	createOpts := ports.CreateOpts{
   130  		Name:                d.Get("name").(string),
   131  		AdminStateUp:        resourcePortAdminStateUpV2(d),
   132  		NetworkID:           d.Get("network_id").(string),
   133  		MACAddress:          d.Get("mac_address").(string),
   134  		TenantID:            d.Get("tenant_id").(string),
   135  		DeviceOwner:         d.Get("device_owner").(string),
   136  		SecurityGroups:      resourcePortSecurityGroupsV2(d),
   137  		DeviceID:            d.Get("device_id").(string),
   138  		FixedIPs:            resourcePortFixedIpsV2(d),
   139  		AllowedAddressPairs: resourceAllowedAddressPairsV2(d),
   140  	}
   141  
   142  	log.Printf("[DEBUG] Create Options: %#v", createOpts)
   143  	p, err := ports.Create(networkingClient, createOpts).Extract()
   144  	if err != nil {
   145  		return fmt.Errorf("Error creating OpenStack Neutron network: %s", err)
   146  	}
   147  	log.Printf("[INFO] Network ID: %s", p.ID)
   148  
   149  	log.Printf("[DEBUG] Waiting for OpenStack Neutron Port (%s) to become available.", p.ID)
   150  
   151  	stateConf := &resource.StateChangeConf{
   152  		Target:     []string{"ACTIVE"},
   153  		Refresh:    waitForNetworkPortActive(networkingClient, p.ID),
   154  		Timeout:    2 * time.Minute,
   155  		Delay:      5 * time.Second,
   156  		MinTimeout: 3 * time.Second,
   157  	}
   158  
   159  	_, err = stateConf.WaitForState()
   160  
   161  	d.SetId(p.ID)
   162  
   163  	return resourceNetworkingPortV2Read(d, meta)
   164  }
   165  
   166  func resourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) error {
   167  	config := meta.(*Config)
   168  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   169  	if err != nil {
   170  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   171  	}
   172  
   173  	p, err := ports.Get(networkingClient, d.Id()).Extract()
   174  	if err != nil {
   175  		return CheckDeleted(d, err, "port")
   176  	}
   177  
   178  	log.Printf("[DEBUG] Retreived Port %s: %+v", d.Id(), p)
   179  
   180  	d.Set("name", p.Name)
   181  	d.Set("admin_state_up", p.AdminStateUp)
   182  	d.Set("network_id", p.NetworkID)
   183  	d.Set("mac_address", p.MACAddress)
   184  	d.Set("tenant_id", p.TenantID)
   185  	d.Set("device_owner", p.DeviceOwner)
   186  	d.Set("security_group_ids", p.SecurityGroups)
   187  	d.Set("device_id", p.DeviceID)
   188  
   189  	// Convert FixedIPs to list of map
   190  	var ips []map[string]interface{}
   191  	for _, ipObject := range p.FixedIPs {
   192  		ip := make(map[string]interface{})
   193  		ip["subnet_id"] = ipObject.SubnetID
   194  		ip["ip_address"] = ipObject.IPAddress
   195  		ips = append(ips, ip)
   196  	}
   197  	d.Set("fixed_ip", ips)
   198  
   199  	// Convert AllowedAddressPairs to list of map
   200  	var pairs []map[string]interface{}
   201  	for _, pairObject := range p.AllowedAddressPairs {
   202  		pair := make(map[string]interface{})
   203  		pair["ip_address"] = pairObject.IPAddress
   204  		pair["mac_address"] = pairObject.MACAddress
   205  		pairs = append(pairs, pair)
   206  	}
   207  	d.Set("allowed_address_pairs", pairs)
   208  
   209  	return nil
   210  }
   211  
   212  func resourceNetworkingPortV2Update(d *schema.ResourceData, meta interface{}) error {
   213  	config := meta.(*Config)
   214  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   215  	if err != nil {
   216  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   217  	}
   218  
   219  	var updateOpts ports.UpdateOpts
   220  
   221  	if d.HasChange("name") {
   222  		updateOpts.Name = d.Get("name").(string)
   223  	}
   224  
   225  	if d.HasChange("admin_state_up") {
   226  		updateOpts.AdminStateUp = resourcePortAdminStateUpV2(d)
   227  	}
   228  
   229  	if d.HasChange("device_owner") {
   230  		updateOpts.DeviceOwner = d.Get("device_owner").(string)
   231  	}
   232  
   233  	if d.HasChange("security_group_ids") {
   234  		updateOpts.SecurityGroups = resourcePortSecurityGroupsV2(d)
   235  	}
   236  
   237  	if d.HasChange("device_id") {
   238  		updateOpts.DeviceID = d.Get("device_id").(string)
   239  	}
   240  
   241  	if d.HasChange("fixed_ip") {
   242  		updateOpts.FixedIPs = resourcePortFixedIpsV2(d)
   243  	}
   244  
   245  	if d.HasChange("allowed_address_pairs") {
   246  		updateOpts.AllowedAddressPairs = resourceAllowedAddressPairsV2(d)
   247  	}
   248  
   249  	log.Printf("[DEBUG] Updating Port %s with options: %+v", d.Id(), updateOpts)
   250  
   251  	_, err = ports.Update(networkingClient, d.Id(), updateOpts).Extract()
   252  	if err != nil {
   253  		return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err)
   254  	}
   255  
   256  	return resourceNetworkingPortV2Read(d, meta)
   257  }
   258  
   259  func resourceNetworkingPortV2Delete(d *schema.ResourceData, meta interface{}) error {
   260  	config := meta.(*Config)
   261  	networkingClient, err := config.networkingV2Client(d.Get("region").(string))
   262  	if err != nil {
   263  		return fmt.Errorf("Error creating OpenStack networking client: %s", err)
   264  	}
   265  
   266  	stateConf := &resource.StateChangeConf{
   267  		Pending:    []string{"ACTIVE"},
   268  		Target:     []string{"DELETED"},
   269  		Refresh:    waitForNetworkPortDelete(networkingClient, d.Id()),
   270  		Timeout:    2 * time.Minute,
   271  		Delay:      5 * time.Second,
   272  		MinTimeout: 3 * time.Second,
   273  	}
   274  
   275  	_, err = stateConf.WaitForState()
   276  	if err != nil {
   277  		return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err)
   278  	}
   279  
   280  	d.SetId("")
   281  	return nil
   282  }
   283  
   284  func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string {
   285  	rawSecurityGroups := d.Get("security_group_ids").(*schema.Set)
   286  	groups := make([]string, rawSecurityGroups.Len())
   287  	for i, raw := range rawSecurityGroups.List() {
   288  		groups[i] = raw.(string)
   289  	}
   290  	return groups
   291  }
   292  
   293  func resourcePortFixedIpsV2(d *schema.ResourceData) interface{} {
   294  	rawIP := d.Get("fixed_ip").([]interface{})
   295  
   296  	if len(rawIP) == 0 {
   297  		return nil
   298  	}
   299  
   300  	ip := make([]ports.IP, len(rawIP))
   301  	for i, raw := range rawIP {
   302  		rawMap := raw.(map[string]interface{})
   303  		ip[i] = ports.IP{
   304  			SubnetID:  rawMap["subnet_id"].(string),
   305  			IPAddress: rawMap["ip_address"].(string),
   306  		}
   307  	}
   308  	return ip
   309  }
   310  
   311  func resourceAllowedAddressPairsV2(d *schema.ResourceData) []ports.AddressPair {
   312  	// ports.AddressPair
   313  	rawPairs := d.Get("allowed_address_pairs").([]interface{})
   314  
   315  	if len(rawPairs) == 0 {
   316  		return nil
   317  	}
   318  
   319  	pairs := make([]ports.AddressPair, len(rawPairs))
   320  	for i, raw := range rawPairs {
   321  		rawMap := raw.(map[string]interface{})
   322  		pairs[i] = ports.AddressPair{
   323  			IPAddress:  rawMap["ip_address"].(string),
   324  			MACAddress: rawMap["mac_address"].(string),
   325  		}
   326  	}
   327  	return pairs
   328  }
   329  
   330  func resourcePortAdminStateUpV2(d *schema.ResourceData) *bool {
   331  	value := false
   332  
   333  	if raw, ok := d.GetOk("admin_state_up"); ok && raw == true {
   334  		value = true
   335  	}
   336  
   337  	return &value
   338  }
   339  
   340  func waitForNetworkPortActive(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
   341  	return func() (interface{}, string, error) {
   342  		p, err := ports.Get(networkingClient, portId).Extract()
   343  		if err != nil {
   344  			return nil, "", err
   345  		}
   346  
   347  		log.Printf("[DEBUG] OpenStack Neutron Port: %+v", p)
   348  		if p.Status == "DOWN" || p.Status == "ACTIVE" {
   349  			return p, "ACTIVE", nil
   350  		}
   351  
   352  		return p, p.Status, nil
   353  	}
   354  }
   355  
   356  func waitForNetworkPortDelete(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
   357  	return func() (interface{}, string, error) {
   358  		log.Printf("[DEBUG] Attempting to delete OpenStack Neutron Port %s", portId)
   359  
   360  		p, err := ports.Get(networkingClient, portId).Extract()
   361  		if err != nil {
   362  			errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
   363  			if !ok {
   364  				return p, "ACTIVE", err
   365  			}
   366  			if errCode.Actual == 404 {
   367  				log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
   368  				return p, "DELETED", nil
   369  			}
   370  		}
   371  
   372  		err = ports.Delete(networkingClient, portId).ExtractErr()
   373  		if err != nil {
   374  			errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
   375  			if !ok {
   376  				return p, "ACTIVE", err
   377  			}
   378  			if errCode.Actual == 404 {
   379  				log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
   380  				return p, "DELETED", nil
   381  			}
   382  		}
   383  
   384  		log.Printf("[DEBUG] OpenStack Port %s still active.\n", portId)
   385  		return p, "ACTIVE", nil
   386  	}
   387  }