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