Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provider/openstack: openstack_networking_port_v2 resource #3731

Merged
merged 2 commits into from
Nov 3, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to also allow none-string true? I was surprised that it doesn't work with boolean value

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

admin_state_up is defined as a String in the port resource, but boolean in all other network resources. Fixing this.

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