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 }