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 }