Skip to content

Commit

Permalink
provider/openstack: Additions to the OpenStack Port resource
Browse files Browse the repository at this point in the history
This commit adds further work to the OpenStack port resource:

* Makes relevant fields computed
* Adds state change functions
* Adds acceptance tests
* Adds Documentation
  • Loading branch information
jtopjian committed Nov 3, 2015
1 parent 7d11b4b commit 312d371
Show file tree
Hide file tree
Showing 6 changed files with 375 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import (
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"

"github.com/rackspace/gophercloud/openstack/compute/v2/extensions/secgroups"
"github.com/rackspace/gophercloud/openstack/compute/v2/servers"
"github.com/rackspace/gophercloud/openstack/networking/v2/extensions/layer3/routers"
"github.com/rackspace/gophercloud/openstack/networking/v2/networks"
"github.com/rackspace/gophercloud/openstack/networking/v2/ports"
"github.com/rackspace/gophercloud/openstack/networking/v2/subnets"
)

Expand Down Expand Up @@ -104,6 +107,81 @@ func TestAccNetworkingV2Network_netstack(t *testing.T) {
})
}

func TestAccNetworkingV2Network_fullstack(t *testing.T) {
region := os.Getenv(OS_REGION_NAME)

var instance servers.Server
var network networks.Network
var port ports.Port
var secgroup secgroups.SecurityGroup
var subnet subnets.Subnet

var testAccNetworkingV2Network_fullstack = fmt.Sprintf(`
resource "openstack_networking_network_v2" "foo" {
region = "%s"
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "foo" {
region = "%s"
name = "subnet_1"
network_id = "${openstack_networking_network_v2.foo.id}"
cidr = "192.168.199.0/24"
ip_version = 4
}
resource "openstack_compute_secgroup_v2" "foo" {
region = "%s"
name = "secgroup_1"
description = "a security group"
rule {
from_port = 22
to_port = 22
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}
}
resource "openstack_networking_port_v2" "foo" {
region = "%s"
name = "port_1"
network_id = "${openstack_networking_network_v2.foo.id}"
admin_state_up = "true"
security_groups = ["${openstack_compute_secgroup_v2.foo.id}"]
depends_on = ["openstack_networking_subnet_v2.foo"]
}
resource "openstack_compute_instance_v2" "foo" {
region = "%s"
name = "terraform-test"
security_groups = ["${openstack_compute_secgroup_v2.foo.name}"]
network {
port = "${openstack_networking_port_v2.foo.id}"
}
}`, region, region, region, region, region)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2NetworkDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2Network_fullstack,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2NetworkExists(t, "openstack_networking_network_v2.foo", &network),
testAccCheckNetworkingV2SubnetExists(t, "openstack_networking_subnet_v2.foo", &subnet),
testAccCheckComputeV2SecGroupExists(t, "openstack_compute_secgroup_v2.foo", &secgroup),
testAccCheckNetworkingV2PortExists(t, "openstack_networking_port_v2.foo", &port),
testAccCheckComputeV2InstanceExists(t, "openstack_compute_instance_v2.foo", &instance),
),
},
},
})
}

func testAccCheckNetworkingV2NetworkDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import (
"fmt"
"log"
"strconv"
"time"

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

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

Expand Down Expand Up @@ -38,26 +42,31 @@ func resourceNetworkingPortV2() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ForceNew: false,
Computed: true,
},
"mac_address": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"tenant_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"device_owner": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
"security_groups": &schema.Schema{
Type: schema.TypeSet,
Optional: true,
ForceNew: false,
Computed: true,
Elem: &schema.Schema{Type: schema.TypeString},
Set: func(v interface{}) int {
return hashcode.String(v.(string))
Expand All @@ -67,6 +76,7 @@ func resourceNetworkingPortV2() *schema.Resource {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
},
}
Expand Down Expand Up @@ -97,6 +107,18 @@ func resourceNetworkingPortV2Create(d *schema.ResourceData, meta interface{}) er
}
log.Printf("[INFO] Network ID: %s", p.ID)

log.Printf("[DEBUG] Waiting for OpenStack Neutron Port (%s) to become available.", p.ID)

stateConf := &resource.StateChangeConf{
Target: "ACTIVE",
Refresh: waitForNetworkPortActive(networkingClient, p.ID),
Timeout: 2 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}

_, err = stateConf.WaitForState()

d.SetId(p.ID)

return resourceNetworkingPortV2Read(d, meta)
Expand Down Expand Up @@ -174,7 +196,16 @@ func resourceNetworkingPortV2Delete(d *schema.ResourceData, meta interface{}) er
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}

err = ports.Delete(networkingClient, d.Id()).ExtractErr()
stateConf := &resource.StateChangeConf{
Pending: []string{"ACTIVE"},
Target: "DELETED",
Refresh: waitForNetworkPortDelete(networkingClient, d.Id()),
Timeout: 2 * time.Minute,
Delay: 5 * time.Second,
MinTimeout: 3 * time.Second,
}

_, err = stateConf.WaitForState()
if err != nil {
return fmt.Errorf("Error deleting OpenStack Neutron Network: %s", err)
}
Expand All @@ -201,3 +232,52 @@ func resourcePortAdminStateUpV2(d *schema.ResourceData) *bool {

return &value
}

func waitForNetworkPortActive(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
p, err := ports.Get(networkingClient, portId).Extract()
if err != nil {
return nil, "", err
}

log.Printf("[DEBUG] OpenStack Neutron Port: %+v", p)
if p.Status == "DOWN" || p.Status == "ACTIVE" {
return p, "ACTIVE", nil
}

return p, p.Status, nil
}
}

func waitForNetworkPortDelete(networkingClient *gophercloud.ServiceClient, portId string) resource.StateRefreshFunc {
return func() (interface{}, string, error) {
log.Printf("[DEBUG] Attempting to delete OpenStack Neutron Port %s", portId)

p, err := ports.Get(networkingClient, portId).Extract()
if err != nil {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return p, "ACTIVE", err
}
if errCode.Actual == 404 {
log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
return p, "DELETED", nil
}
}

err = ports.Delete(networkingClient, portId).ExtractErr()
if err != nil {
errCode, ok := err.(*gophercloud.UnexpectedResponseCodeError)
if !ok {
return p, "ACTIVE", err
}
if errCode.Actual == 404 {
log.Printf("[DEBUG] Successfully deleted OpenStack Port %s", portId)
return p, "DELETED", nil
}
}

log.Printf("[DEBUG] OpenStack Port %s still active.\n", portId)
return p, "ACTIVE", nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package openstack

import (
"fmt"
"os"
"testing"

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

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

func TestAccNetworkingV2Port_basic(t *testing.T) {
region := os.Getenv(OS_REGION_NAME)

var network networks.Network
var port ports.Port

var testAccNetworkingV2Port_basic = fmt.Sprintf(`
resource "openstack_networking_network_v2" "foo" {
region = "%s"
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_port_v2" "foo" {
region = "%s"
name = "port_1"
network_id = "${openstack_networking_network_v2.foo.id}"
admin_state_up = "true"
}`, region, region)

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckNetworkingV2PortDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccNetworkingV2Port_basic,
Check: resource.ComposeTestCheckFunc(
testAccCheckNetworkingV2NetworkExists(t, "openstack_networking_network_v2.foo", &network),
testAccCheckNetworkingV2PortExists(t, "openstack_networking_port_v2.foo", &port),
),
},
},
})
}

func testAccCheckNetworkingV2PortDestroy(s *terraform.State) error {
config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2PortDestroy) Error creating OpenStack networking client: %s", err)
}

for _, rs := range s.RootModule().Resources {
if rs.Type != "openstack_networking_port_v2" {
continue
}

_, err := ports.Get(networkingClient, rs.Primary.ID).Extract()
if err == nil {
return fmt.Errorf("Port still exists")
}
}

return nil
}

func testAccCheckNetworkingV2PortExists(t *testing.T, n string, port *ports.Port) resource.TestCheckFunc {
return func(s *terraform.State) error {
rs, ok := s.RootModule().Resources[n]
if !ok {
return fmt.Errorf("Not found: %s", n)
}

if rs.Primary.ID == "" {
return fmt.Errorf("No ID is set")
}

config := testAccProvider.Meta().(*Config)
networkingClient, err := config.networkingV2Client(OS_REGION_NAME)
if err != nil {
return fmt.Errorf("(testAccCheckNetworkingV2PortExists) Error creating OpenStack networking client: %s", err)
}

found, err := ports.Get(networkingClient, rs.Primary.ID).Extract()
if err != nil {
return err
}

if found.ID != rs.Primary.ID {
return fmt.Errorf("Port not found")
}

*port = *found

return nil
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,46 @@ Manages a V2 Neutron network resource within OpenStack.

```
resource "openstack_networking_network_v2" "network_1" {
name = "tf_test_network"
name = "network_1"
admin_state_up = "true"
}
resource "openstack_networking_subnet_v2" "subnet_1" {
name = "subnet_1"
network_id = "${openstack_networking_network_v2.network_1.id}"
cidr = "192.168.199.0/24"
ip_version = 4
}
resource "openstack_compute_secgroup_v2" "secgroup_1" {
name = "secgroup_1"
description = "a security group"
rule {
from_port = 22
to_port = 22
ip_protocol = "tcp"
cidr = "0.0.0.0/0"
}
}
resource "openstack_networking_port_v2" "port_1" {
name = "port_1"
network_id = "${openstack_networking_network_v2.network_1.id}"
admin_state_up = "true"
security_groups = ["${openstack_compute_secgroup_v2.secgroup_1.id}"]
depends_on = ["openstack_networking_subnet_v2.subnet_1"]
}
resource "openstack_compute_instance_v2" "instance_1" {
name = "instance_1"
security_groups = ["${openstack_compute_secgroup_v2.secgroup_1.name}"]
network {
port = "${openstack_networking_port_v2.port_1.id}"
}
}
```

## Argument Reference
Expand Down
Loading

0 comments on commit 312d371

Please sign in to comment.