Skip to content

Commit

Permalink
Merge pull request #3731 from jtopjian/openstack-networking-port
Browse files Browse the repository at this point in the history
provider/openstack: openstack_networking_port_v2 resource
  • Loading branch information
jtopjian committed Nov 3, 2015
2 parents 1a196af + 312d371 commit e52b6a6
Show file tree
Hide file tree
Showing 7 changed files with 578 additions and 1 deletion.
1 change: 1 addition & 0 deletions builtin/providers/openstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func Provider() terraform.ResourceProvider {
"openstack_networking_network_v2": resourceNetworkingNetworkV2(),
"openstack_networking_subnet_v2": resourceNetworkingSubnetV2(),
"openstack_networking_floatingip_v2": resourceNetworkingFloatingIPV2(),
"openstack_networking_port_v2": resourceNetworkingPortV2(),
"openstack_networking_router_v2": resourceNetworkingRouterV2(),
"openstack_networking_router_interface_v2": resourceNetworkingRouterInterfaceV2(),
"openstack_objectstorage_container_v1": resourceObjectStorageContainerV1(),
Expand Down
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
283 changes: 283 additions & 0 deletions builtin/providers/openstack/resource_openstack_networking_port_v2.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,283 @@
package openstack

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"
)

func resourceNetworkingPortV2() *schema.Resource {
return &schema.Resource{
Create: resourceNetworkingPortV2Create,
Read: resourceNetworkingPortV2Read,
Update: resourceNetworkingPortV2Update,
Delete: resourceNetworkingPortV2Delete,

Schema: map[string]*schema.Schema{
"region": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
DefaultFunc: envDefaultFuncAllowMissing("OS_REGION_NAME"),
},
"name": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: false,
},
"network_id": &schema.Schema{
Type: schema.TypeString,
Required: true,
ForceNew: true,
},
"admin_state_up": &schema.Schema{
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))
},
},
"device_id": &schema.Schema{
Type: schema.TypeString,
Optional: true,
ForceNew: true,
Computed: true,
},
},
}
}

func resourceNetworkingPortV2Create(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}

createOpts := ports.CreateOpts{
Name: d.Get("name").(string),
AdminStateUp: resourcePortAdminStateUpV2(d),
NetworkID: d.Get("network_id").(string),
MACAddress: d.Get("mac_address").(string),
TenantID: d.Get("tenant_id").(string),
DeviceOwner: d.Get("device_owner").(string),
SecurityGroups: resourcePortSecurityGroupsV2(d),
DeviceID: d.Get("device_id").(string),
}

log.Printf("[DEBUG] Create Options: %#v", createOpts)
p, err := ports.Create(networkingClient, createOpts).Extract()
if err != nil {
return fmt.Errorf("Error creating OpenStack Neutron network: %s", err)
}
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)
}

func resourceNetworkingPortV2Read(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}

p, err := ports.Get(networkingClient, d.Id()).Extract()
if err != nil {
return CheckDeleted(d, err, "port")
}

log.Printf("[DEBUG] Retreived Port %s: %+v", d.Id(), p)

d.Set("name", p.Name)
d.Set("admin_state_up", strconv.FormatBool(p.AdminStateUp))
d.Set("network_id", p.NetworkID)
d.Set("mac_address", p.MACAddress)
d.Set("tenant_id", p.TenantID)
d.Set("device_owner", p.DeviceOwner)
d.Set("security_groups", p.SecurityGroups)
d.Set("device_id", p.DeviceID)

return nil
}

func resourceNetworkingPortV2Update(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}

var updateOpts ports.UpdateOpts

if d.HasChange("name") {
updateOpts.Name = d.Get("name").(string)
}

if d.HasChange("admin_state_up") {
updateOpts.AdminStateUp = resourcePortAdminStateUpV2(d)
}

if d.HasChange("device_owner") {
updateOpts.DeviceOwner = d.Get("device_owner").(string)
}

if d.HasChange("security_groups") {
updateOpts.SecurityGroups = resourcePortSecurityGroupsV2(d)
}

if d.HasChange("device_id") {
updateOpts.DeviceID = d.Get("device_id").(string)
}

log.Printf("[DEBUG] Updating Port %s with options: %+v", d.Id(), updateOpts)

_, err = ports.Update(networkingClient, d.Id(), updateOpts).Extract()
if err != nil {
return fmt.Errorf("Error updating OpenStack Neutron Network: %s", err)
}

return resourceNetworkingPortV2Read(d, meta)
}

func resourceNetworkingPortV2Delete(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
networkingClient, err := config.networkingV2Client(d.Get("region").(string))
if err != nil {
return fmt.Errorf("Error creating OpenStack networking client: %s", err)
}

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)
}

d.SetId("")
return nil
}

func resourcePortSecurityGroupsV2(d *schema.ResourceData) []string {
rawSecurityGroups := d.Get("security_groups").(*schema.Set)
groups := make([]string, rawSecurityGroups.Len())
for i, raw := range rawSecurityGroups.List() {
groups[i] = raw.(string)
}
return groups
}

func resourcePortAdminStateUpV2(d *schema.ResourceData) *bool {
value := false

if raw, ok := d.GetOk("admin_state_up"); ok && raw == "true" {
value = true
}

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
}
}
Loading

0 comments on commit e52b6a6

Please sign in to comment.