Skip to content

Commit 68b035b

Browse files
authored
Merge pull request #10 from platform9/feat/networking-extras
feat(networking): ports, floating IPs, and networking lookup data sources
2 parents e77be0a + a00a3d6 commit 68b035b

14 files changed

Lines changed: 1798 additions & 1 deletion

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ All notable changes to this project are documented here. The format is based on
3030
`pcd_images_image_ids` data sources.
3131
- Networking (Neutron v2): resources `pcd_networking_network`, `_subnet`, `_secgroup`,
3232
`_secgroup_rule`, `_router`, `_router_interface`; data sources `pcd_networking_network`,
33-
`_subnet`, `_secgroup`. (Floating IPs, ports, and remaining data sources to follow.)
33+
`_subnet`, `_secgroup`.
34+
- Networking extras: `pcd_networking_port` (fixed IPs, security groups, allowed-address
35+
pairs, tags) and `pcd_networking_floatingip` (allocate from an external network by
36+
`pool` name, associate/disassociate to a port); data sources `pcd_networking_port`,
37+
`_port_ids`, `_router`, `_subnet_ids`, and `_floatingip`. Ports and floating IPs are
38+
code-complete with acceptance tests; see DECISIONS.md for live-validation status.
3439
- Compute (Nova v2): resources `pcd_compute_keypair`, `pcd_compute_flavor`,
3540
`pcd_compute_servergroup` (acceptance-tested); `pcd_compute_instance` (code-complete —
3641
boot verification is blocked on a lab image-library issue, see DECISIONS.md); data

DECISIONS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ yet passable on this lab (reason noted). Generated registry docs are not committ
1919
| Images (DS) | `pcd_images_image`, `_image_ids` | **VALIDATED** |
2020
| Networking | `pcd_networking_network`, `_subnet`, `_secgroup`, `_secgroup_rule`, `_router`, `_router_interface` | **VALIDATED** |
2121
| Networking (DS) | `pcd_networking_network`, `_subnet`, `_secgroup` | **VALIDATED** |
22+
| Networking | `pcd_networking_port` | **PENDING** — code-complete, build/vet/lint/docs clean; acceptance test written (create/update/import). Not yet run live: lab credentials were unavailable in this session. No lab-side blocker expected. |
23+
| Networking | `pcd_networking_floatingip` | **PENDING** — code-complete. Needs an **external network** in the lab (allocation pool); acc test skips unless `PCD_ACC_EXTERNAL_NETWORK` names one. |
24+
| Networking (DS) | `pcd_networking_port`, `_port_ids`, `_router`, `_subnet_ids` | **PENDING** — code-complete; acc test written. Not yet run live (credentials unavailable this session). |
25+
| Networking (DS) | `pcd_networking_floatingip` | **PENDING** — depends on a floating IP existing (see external-network note above). |
2226
| Compute | `pcd_compute_keypair`, `_flavor`, `_servergroup` | **VALIDATED** |
2327
| Compute (DS) | `pcd_compute_flavor`, `_keypair`, `_availability_zones` | **VALIDATED** |
2428
| Compute | `pcd_compute_instance` (boot) | **PENDING** — lab image-library gap: images don't reach the onboarded host's local library → nova returns HTTP 204 for image data. Create/schedule/wait/error-report verified; passes with a library-backed image. |

internal/provider/provider.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ func (p *pcdProvider) Resources(_ context.Context) []func() resource.Resource {
5353
networking.NewSecgroupRuleResource,
5454
networking.NewRouterResource,
5555
networking.NewRouterInterfaceResource,
56+
networking.NewPortResource,
57+
networking.NewFloatingIPResource,
5658
compute.NewKeypairResource,
5759
compute.NewInstanceResource,
5860
compute.NewFlavorResource,
@@ -71,7 +73,12 @@ func (p *pcdProvider) DataSources(_ context.Context) []func() datasource.DataSou
7173
images.NewImageIDsDataSource,
7274
networking.NewNetworkDataSource,
7375
networking.NewSubnetDataSource,
76+
networking.NewSubnetIDsDataSource,
7477
networking.NewSecgroupDataSource,
78+
networking.NewPortDataSource,
79+
networking.NewPortIDsDataSource,
80+
networking.NewRouterDataSource,
81+
networking.NewFloatingIPDataSource,
7582
compute.NewFlavorDataSource,
7683
compute.NewKeypairDataSource,
7784
compute.NewAvailabilityZonesDataSource,
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Copyright (c) Platform9 Systems, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
4+
package networking_test
5+
6+
import (
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
10+
11+
"github.com/platform9/terraform-provider-pcd/internal/acctest"
12+
)
13+
14+
// TestAccNetworkingExtraDataSources_basic creates a network, subnet, port, and
15+
// router, then exercises the port, router, subnet_ids, and port_ids data
16+
// sources against them.
17+
func TestAccNetworkingExtraDataSources_basic(t *testing.T) {
18+
resource.Test(t, resource.TestCase{
19+
PreCheck: func() { acctest.PreCheck(t) },
20+
ProtoV6ProviderFactories: acctest.ProtoV6ProviderFactories,
21+
CheckDestroy: resource.ComposeAggregateTestCheckFunc(
22+
testAccCheckPortDestroy(t),
23+
testAccCheckSubnetDestroy(t),
24+
testAccCheckNetworkDestroy(t),
25+
testAccCheckRouterDestroy(t),
26+
),
27+
Steps: []resource.TestStep{
28+
{
29+
Config: testAccNetworkingExtraDataSourcesConfig,
30+
Check: resource.ComposeAggregateTestCheckFunc(
31+
resource.TestCheckResourceAttrPair("data.pcd_networking_port.by_name", "id", "pcd_networking_port.test", "id"),
32+
resource.TestCheckResourceAttrPair("data.pcd_networking_port.by_name", "network_id", "pcd_networking_network.test", "id"),
33+
resource.TestCheckResourceAttrPair("data.pcd_networking_router.by_name", "id", "pcd_networking_router.test", "id"),
34+
resource.TestCheckResourceAttr("data.pcd_networking_subnet_ids.by_network", "ids.#", "1"),
35+
resource.TestCheckResourceAttrPair("data.pcd_networking_subnet_ids.by_network", "ids.0", "pcd_networking_subnet.test", "id"),
36+
resource.TestCheckResourceAttr("data.pcd_networking_port_ids.by_name", "ids.#", "1"),
37+
resource.TestCheckResourceAttrPair("data.pcd_networking_port_ids.by_name", "ids.0", "pcd_networking_port.test", "id"),
38+
),
39+
},
40+
},
41+
})
42+
}
43+
44+
const testAccNetworkingExtraDataSourcesConfig = `
45+
resource "pcd_networking_network" "test" {
46+
name = "tf-acc-dsx-net"
47+
}
48+
49+
resource "pcd_networking_subnet" "test" {
50+
name = "tf-acc-dsx-subnet"
51+
network_id = pcd_networking_network.test.id
52+
cidr = "10.103.0.0/24"
53+
}
54+
55+
resource "pcd_networking_port" "test" {
56+
name = "tf-acc-dsx-port"
57+
network_id = pcd_networking_network.test.id
58+
59+
fixed_ip {
60+
subnet_id = pcd_networking_subnet.test.id
61+
}
62+
}
63+
64+
resource "pcd_networking_router" "test" {
65+
name = "tf-acc-dsx-router"
66+
}
67+
68+
data "pcd_networking_port" "by_name" {
69+
name = pcd_networking_port.test.name
70+
}
71+
72+
data "pcd_networking_router" "by_name" {
73+
name = pcd_networking_router.test.name
74+
}
75+
76+
data "pcd_networking_subnet_ids" "by_network" {
77+
network_id = pcd_networking_network.test.id
78+
79+
depends_on = [pcd_networking_subnet.test]
80+
}
81+
82+
data "pcd_networking_port_ids" "by_name" {
83+
name = pcd_networking_port.test.name
84+
85+
depends_on = [pcd_networking_port.test]
86+
}
87+
`
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright (c) Platform9 Systems, Inc.
2+
// SPDX-License-Identifier: MPL-2.0
3+
//
4+
// Ported from terraform-provider-openstack v3.4.0
5+
// (openstack/data_source_openstack_networking_floatingip_v2.go), adapted for the
6+
// terraform-plugin-framework and PCD.
7+
8+
package networking
9+
10+
import (
11+
"context"
12+
"fmt"
13+
14+
"github.com/gophercloud/gophercloud/v2/openstack/networking/v2/extensions/layer3/floatingips"
15+
"github.com/hashicorp/terraform-plugin-framework/datasource"
16+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
17+
"github.com/hashicorp/terraform-plugin-framework/types"
18+
19+
"github.com/platform9/terraform-provider-pcd/internal/clients"
20+
)
21+
22+
var (
23+
_ datasource.DataSource = (*floatingIPDataSource)(nil)
24+
_ datasource.DataSourceWithConfigure = (*floatingIPDataSource)(nil)
25+
)
26+
27+
// NewFloatingIPDataSource is the factory registered with the provider.
28+
func NewFloatingIPDataSource() datasource.DataSource {
29+
return &floatingIPDataSource{}
30+
}
31+
32+
type floatingIPDataSource struct {
33+
config *clients.Config
34+
}
35+
36+
type floatingIPDataSourceModel struct {
37+
ID types.String `tfsdk:"id"`
38+
Address types.String `tfsdk:"address"`
39+
Description types.String `tfsdk:"description"`
40+
FloatingNetworkID types.String `tfsdk:"floating_network_id"`
41+
PortID types.String `tfsdk:"port_id"`
42+
FixedIP types.String `tfsdk:"fixed_ip"`
43+
Status types.String `tfsdk:"status"`
44+
RouterID types.String `tfsdk:"router_id"`
45+
TenantID types.String `tfsdk:"tenant_id"`
46+
Region types.String `tfsdk:"region"`
47+
}
48+
49+
func (d *floatingIPDataSource) Metadata(_ context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
50+
resp.TypeName = req.ProviderTypeName + "_networking_floatingip"
51+
}
52+
53+
func (d *floatingIPDataSource) Schema(_ context.Context, _ datasource.SchemaRequest, resp *datasource.SchemaResponse) {
54+
resp.Schema = schema.Schema{
55+
MarkdownDescription: "Look up a floating IP by address or filters. Exactly one floating IP must match.",
56+
Attributes: map[string]schema.Attribute{
57+
"id": schema.StringAttribute{Computed: true, MarkdownDescription: "The floating IP ID."},
58+
"address": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Filter by (and report) the floating IP address."},
59+
"description": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Filter by (and report) the description."},
60+
"floating_network_id": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Filter by (and report) the external network ID."},
61+
"port_id": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Filter by (and report) the associated port."},
62+
"fixed_ip": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Filter by (and report) the mapped fixed IP."},
63+
"status": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Filter by (and report) the operational status."},
64+
"router_id": schema.StringAttribute{Computed: true, MarkdownDescription: "The router through which the floating IP is routed."},
65+
"tenant_id": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "Filter by (and report) the owning project."},
66+
"region": schema.StringAttribute{Optional: true, Computed: true, MarkdownDescription: "The region. Defaults to the provider's region."},
67+
},
68+
}
69+
}
70+
71+
func (d *floatingIPDataSource) Configure(_ context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
72+
d.config = configureClient(req.ProviderData, &resp.Diagnostics)
73+
}
74+
75+
func (d *floatingIPDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
76+
var data floatingIPDataSourceModel
77+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
78+
if resp.Diagnostics.HasError() {
79+
return
80+
}
81+
82+
client, err := d.config.NetworkV2Client()
83+
if err != nil {
84+
resp.Diagnostics.AddError("networking: building v2 client", err.Error())
85+
return
86+
}
87+
88+
listOpts := floatingips.ListOpts{
89+
FloatingIP: data.Address.ValueString(),
90+
Description: data.Description.ValueString(),
91+
FloatingNetworkID: data.FloatingNetworkID.ValueString(),
92+
PortID: data.PortID.ValueString(),
93+
FixedIP: data.FixedIP.ValueString(),
94+
Status: data.Status.ValueString(),
95+
TenantID: data.TenantID.ValueString(),
96+
}
97+
pages, err := floatingips.List(client, listOpts).AllPages(ctx)
98+
if err != nil {
99+
resp.Diagnostics.AddError("networking: listing floating IPs", err.Error())
100+
return
101+
}
102+
all, err := floatingips.ExtractFloatingIPs(pages)
103+
if err != nil {
104+
resp.Diagnostics.AddError("networking: extracting floating IPs", err.Error())
105+
return
106+
}
107+
108+
var fip *floatingips.FloatingIP
109+
switch len(all) {
110+
case 0:
111+
resp.Diagnostics.AddError("No floating IP found", "No floating IP matched the given criteria.")
112+
return
113+
case 1:
114+
fip = &all[0]
115+
default:
116+
resp.Diagnostics.AddError("Multiple floating IPs found",
117+
fmt.Sprintf("%d floating IPs matched; refine the filters.", len(all)))
118+
return
119+
}
120+
121+
data.ID = types.StringValue(fip.ID)
122+
data.Address = types.StringValue(fip.FloatingIP)
123+
data.Description = types.StringValue(fip.Description)
124+
data.FloatingNetworkID = types.StringValue(fip.FloatingNetworkID)
125+
data.PortID = types.StringValue(fip.PortID)
126+
data.FixedIP = types.StringValue(fip.FixedIP)
127+
data.Status = types.StringValue(fip.Status)
128+
data.RouterID = types.StringValue(fip.RouterID)
129+
data.TenantID = types.StringValue(fip.TenantID)
130+
if data.Region.IsNull() || data.Region.IsUnknown() {
131+
data.Region = types.StringValue(d.config.Region)
132+
}
133+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
134+
}

0 commit comments

Comments
 (0)