Skip to content

Commit

Permalink
Networking; add port data source
Browse files Browse the repository at this point in the history
  • Loading branch information
kayrus committed Jan 6, 2019
1 parent 03068f6 commit 8913a9b
Show file tree
Hide file tree
Showing 7 changed files with 452 additions and 11 deletions.
224 changes: 224 additions & 0 deletions openstack/data_source_openstack_networking_port_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,224 @@
package openstack

import (
"fmt"
"log"
"strconv"
"strings"

"github.com/hashicorp/terraform/helper/schema"

"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
)

func dataSourceNetworkingPortV2() *schema.Resource {
return &schema.Resource{
Read: dataSourceNetworkingPortV2Read,

Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"port_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"description": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"admin_state_up": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},

"network_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"project_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"device_owner": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"mac_address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"device_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"fixed_ip": &schema.Schema{
Type: schema.TypeString,
Optional: true,
},

"all_fixed_ips": &schema.Schema{
Type: schema.TypeList,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
},

"return_ids": &schema.Schema{
Type: schema.TypeBool,
Optional: true,
},
},
}
}

func dataSourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(GetRegion(d, config))

var returnIDs bool
listOpts := ports.ListOpts{}

if v, ok := d.GetOk("return_ids"); ok {
returnIDs = v.(bool)
}

if v, ok := d.GetOk("port_id"); ok {
listOpts.ID = v.(string)
}

if v, ok := d.GetOk("name"); ok {
listOpts.Name = v.(string)
}

if v, ok := d.GetOk("description"); ok {
listOpts.Description = v.(string)
}

if v, ok := d.GetOkExists("admin_state_up"); ok {
asu := v.(bool)
listOpts.AdminStateUp = &asu
}

if v, ok := d.GetOk("network_id"); ok {
listOpts.Status = v.(string)
}

if v, ok := d.GetOk("tenant_id"); ok {
listOpts.TenantID = v.(string)
}

if v, ok := d.GetOk("project_id"); ok {
listOpts.ProjectID = v.(string)
}

if v, ok := d.GetOk("device_owner"); ok {
listOpts.DeviceOwner = v.(string)
}

if v, ok := d.GetOk("mac_address"); ok {
listOpts.MACAddress = v.(string)
}

if v, ok := d.GetOk("device_id"); ok {
listOpts.DeviceID = v.(string)
}

pages, err := ports.List(networkingClient, listOpts).AllPages()
if err != nil {
return fmt.Errorf("Unable to list Ports: %s", err)
}

allPorts, err := ports.ExtractPorts(pages)
if err != nil {
return fmt.Errorf("Unable to retrieve Ports: %s", err)
}

if len(allPorts) == 0 {
return fmt.Errorf("No Port found")
}

// Create a slice of all returned Fixed IPs.
// This will be in the order returned by the API,
// which is usually alpha-numeric.
var port ports.Port
var ports []ports.Port
if v, ok := d.GetOk("fixed_ip"); ok {
for _, p := range allPorts {
var ips = []string{}
for _, ipObject := range p.FixedIPs {
ips = append(ips, ipObject.IPAddress)
if v == ipObject.IPAddress {
ports = append(ports, p)
}
}
if len(ports) > 0 && len(ips) > 0 && returnIDs == false {
d.Set("all_fixed_ips", ips)
break
}
}
if len(ports) == 0 {
return fmt.Errorf("No Port found")
}
} else {
ports = allPorts
}

if len(ports) > 1 && returnIDs {
var portIDs []string

for _, p := range ports {
portIDs = append(portIDs, p.ID)
}

log.Printf("[DEBUG] Retrieved %d Ports %s: %+v", len(ports), strings.Join(portIDs, ","), ports)
d.SetId(strings.Join(portIDs, ","))

return nil
}

if len(ports) > 1 {
return fmt.Errorf("More than one Port found (%d). Try to use 'return_ids = true'", len(ports))
}

if len(ports) == 1 {
port = ports[0]
}

log.Printf("[DEBUG] Retrieved Port %s: %+v", port.ID, port)
d.SetId(port.ID)

d.Set("port_id", port.ID)
d.Set("name", port.Name)
d.Set("description", port.Description)
d.Set("admin_state_up", strconv.FormatBool(port.AdminStateUp))
d.Set("network_id", port.NetworkID)
d.Set("tenant_id", port.TenantID)
d.Set("project_id", port.ProjectID)
d.Set("device_owner", port.DeviceOwner)
d.Set("mac_address", port.MACAddress)
d.Set("device_id", port.DeviceID)
d.Set("region", GetRegion(d, config))

// TODO: add more attributes from "resource_openstack_networking_port_v2"

return nil
}
136 changes: 136 additions & 0 deletions openstack/data_source_openstack_networking_port_v2_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package openstack

import (
"fmt"
"strings"
"testing"

"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccNetworkingV2PortDataSource_basic(t *testing.T) {
resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2PortDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2PortDataSource_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2PortDataSourceID("data.openstack_networking_port_v2.port"),
resource.TestCheckResourceAttr(
"data.openstack_networking_port_v2.port", "description", "test port"),
),
},
},
})
}

func TestAccNetworkingV2PortDataSource_multiple(t *testing.T) {
var port1, port2 ports.Port

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2PortDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2PortDataSource_multiple,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_1", &port1),
testAccCheckNetworkingV2PortExists("openstack_networking_port_v2.port_2", &port2),
testAccCheckNetworkingV2PortDataSourceMultipleIDs("data.openstack_networking_port_v2.port", &port1, &port2),
),
},
},
})
}

func testAccCheckNetworkingV2PortDataSourceID(n string) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Can't find port data source: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("Port data source ID not set")
}

return nil
}
}

func testAccCheckNetworkingV2PortDataSourceMultipleIDs(n string, port1, port2 *ports.Port) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Can't find share data source: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("Share data source ID not set")
}

portIDs := strings.Split(rs.Primary.ID, ",")
if len(portIDs) != 2 {
return fmt.Errorf("Invalid amount of the detected ports: got %d, want 2", len(portIDs))
}

if !strSliceContains(portIDs, port1.ID) || !strSliceContains(portIDs, port2.ID) {
return fmt.Errorf("Failed to find expected port IDs (%s, %s) in %s", port1.ID, port2.ID, rs.Primary.ID)
}

return nil
}
}

const testAccNetworkingV2PortDataSource_basic = `
resource "openstack_networking_network_v2" "network_1" {
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_port_v2" "port_1" {
name = "port"
description = "test port"
network_id = "${openstack_networking_network_v2.network_1.id}"
admin_state_up = "true"
}
data "openstack_networking_port_v2" "port" {
# depends_on = ["openstack_networking_port_v2.port_1"]
# workaround for the https://github.com/hashicorp/terraform/issues/11806
admin_state_up = "${openstack_networking_port_v2.port_1.admin_state_up}"
name = "port"
}
`

const testAccNetworkingV2PortDataSource_multiple = `
resource "openstack_networking_network_v2" "network_1" {
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_port_v2" "port_1" {
name = "port"
network_id = "${openstack_networking_network_v2.network_1.id}"
admin_state_up = "true"
}
resource "openstack_networking_port_v2" "port_2" {
name = "port"
network_id = "${openstack_networking_network_v2.network_1.id}"
admin_state_up = "true"
}
data "openstack_networking_port_v2" "port" {
# depends_on = ["openstack_networking_port_v2.port_1","openstack_networking_port_v2.port_2"]
# workaround for the https://github.com/hashicorp/terraform/issues/11806
admin_state_up = "${openstack_networking_port_v2.port_1.admin_state_up == "true" ? openstack_networking_port_v2.port_1.admin_state_up : openstack_networking_port_v2.port_2.admin_state_up}"
name = "port"
return_ids = true
}
`
11 changes: 0 additions & 11 deletions openstack/lb_v2_shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -508,14 +508,3 @@ func waitForLBV2L7Rule(lbClient *gophercloud.ServiceClient, parentListener *list

return nil
}

// strSliceContains checks if a given string is contained in a slice
// When anybody asks why Go needs generics, here you go.
func strSliceContains(haystack []string, needle string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}
1 change: 1 addition & 0 deletions openstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ func Provider() terraform.ResourceProvider {
"openstack_networking_subnetpool_v2": dataSourceNetworkingSubnetPoolV2(),
"openstack_networking_floatingip_v2": dataSourceNetworkingFloatingIPV2(),
"openstack_networking_router_v2": dataSourceNetworkingRouterV2(),
"openstack_networking_port_v2": dataSourceNetworkingPortV2(),
},

ResourcesMap: map[string]*schema.Resource{
Expand Down
11 changes: 11 additions & 0 deletions openstack/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,14 @@ func expandToMapStringString(v map[string]interface{}) map[string]string {

return m
}

// strSliceContains checks if a given string is contained in a slice
// When anybody asks why Go needs generics, here you go.
func strSliceContains(haystack []string, needle string) bool {
for _, s := range haystack {
if s == needle {
return true
}
}
return false
}
Loading

0 comments on commit 8913a9b

Please sign in to comment.