Skip to content

Commit

Permalink
Merge pull request #38 from browningluke/add_interface_data_source
Browse files Browse the repository at this point in the history
Add interface data source
  • Loading branch information
browningluke authored Oct 29, 2023
2 parents 61a96b3 + e16817d commit 6ecc7d9
Show file tree
Hide file tree
Showing 7 changed files with 387 additions and 3 deletions.
57 changes: 57 additions & 0 deletions docs/data-sources/interface.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
page_title: "opnsense_interface Data Source - terraform-provider-opnsense"
subcategory: Interfaces
description: |-
Interfaces can be used to get configurations of OPNsense interfaces.
---

# opnsense_interface (Data Source)

Interfaces can be used to get configurations of OPNsense interfaces.

<!-- schema generated by tfplugindocs -->
## Schema

### Required

- `device` (String) Name of the interface device.

### Read-Only

- `capabilities` (Set of String) List of capabilities the interface supports.
- `flags` (Set of String) List of flags configured on the interface (equiv. to flags=xxxx in output of ifconfig).
- `groups` (Set of String) List of groups the interface is a member of.
- `ipv4` (Attributes List) (see [below for nested schema](#nestedatt--ipv4))
- `ipv6` (Attributes List) (see [below for nested schema](#nestedatt--ipv6))
- `is_physical` (Boolean) Whether the interface is physical or virtual.
- `macaddr` (String) MAC address assigned to the interface.
- `media` (String) Interface media type settings (see https://man.openbsd.org/ifmedia.4).
- `media_raw` (String) User-friendly interface media type.
- `mtu` (Number) Maximum Transmission Unit for the interface. This is typically 1500 bytes but can vary in some circumstances.
- `options` (Set of String) List of options configured on the interface (equiv. to options=xx in output of ifconfig).
- `status` (String) Status of the interface (e.g. `"active"`).
- `supported_media` (Set of String) List of supported media type settings (see https://man.openbsd.org/ifmedia.4).

<a id="nestedatt--ipv4"></a>
### Nested Schema for `ipv4`

Read-Only:

- `ipaddr` (String) IPv4 address assigned to the interface.
- `subnetbits` (Number) Number of subnet bits (i.e. CIDR).
- `tunnel` (Boolean) Whether IPv4 tunnelling is enabled.


<a id="nestedatt--ipv6"></a>
### Nested Schema for `ipv6`

Read-Only:

- `autoconf` (Boolean) Whether auto-configuration is enabled for the address.
- `deprecated` (Boolean) Whether the address is deprecated.
- `ipaddr` (String) IPv6 address assigned to the interface.
- `link_local` (Boolean) Whether the address is link-local.
- `subnetbits` (Number) Number of subnet bits (i.e. CIDR).
- `tentative` (Boolean) Whether the address is tentative.
- `tunnel` (Boolean) Whether IPv6 tunnelling is enabled.

2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module terraform-provider-opnsense
go 1.20

require (
github.com/browningluke/opnsense-go v0.7.0
github.com/browningluke/opnsense-go v0.8.0
github.com/hashicorp/terraform-plugin-docs v0.14.1
github.com/hashicorp/terraform-plugin-framework v1.2.0
github.com/hashicorp/terraform-plugin-framework-validators v0.10.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgI
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/browningluke/opnsense-go v0.7.0 h1:y6nhqpYnqTvlKx8bZk66n3Bx0mnlR5BfJ8K3sSMW+Jc=
github.com/browningluke/opnsense-go v0.7.0/go.mod h1:u8di5DaP7igyIBl02jjrAbXYKRtEelnRi3Tqp5aj7g0=
github.com/browningluke/opnsense-go v0.8.0 h1:i2d1evSXAWTGbe4O24UJAOydQd89YnG4qDbq3fMXRqk=
github.com/browningluke/opnsense-go v0.8.0/go.mod h1:8TRAG1xjiFzRUEuaosv4mHSXwBJd+R35o6QSEKGVwSA=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
Expand Down
1 change: 1 addition & 0 deletions internal/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ func (p *OPNsenseProvider) DataSources(ctx context.Context) []func() datasource.
return []func() datasource.DataSource{
// Interfaces
service.NewInterfacesVlanDataSource,
service.NewInterfaceDataSource,
// Routes
service.NewRouteDataSource,
// Unbound
Expand Down
77 changes: 77 additions & 0 deletions internal/service/interface_data_source.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package service

import (
"context"
"fmt"
"github.com/browningluke/opnsense-go/pkg/api"
"github.com/browningluke/opnsense-go/pkg/opnsense"
"github.com/hashicorp/terraform-plugin-framework/datasource"
)

// Ensure provider defined types fully satisfy framework interfaces.
var _ datasource.DataSource = &InterfaceDataSource{}

func NewInterfaceDataSource() datasource.DataSource {
return &InterfaceDataSource{}
}

// InterfaceDataSource defines the data source implementation.
type InterfaceDataSource struct {
client opnsense.Client
}

func (d *InterfaceDataSource) Metadata(ctx context.Context, req datasource.MetadataRequest, resp *datasource.MetadataResponse) {
resp.TypeName = req.ProviderTypeName + "_interface"
}

func (d *InterfaceDataSource) Schema(ctx context.Context, req datasource.SchemaRequest, resp *datasource.SchemaResponse) {
resp.Schema = InterfaceDataSourceSchema()
}

func (d *InterfaceDataSource) Configure(ctx context.Context, req datasource.ConfigureRequest, resp *datasource.ConfigureResponse) {
// Prevent panic if the provider has not been configured.
if req.ProviderData == nil {
return
}

apiClient, ok := req.ProviderData.(*api.Client)
if !ok {
resp.Diagnostics.AddError(
"Unexpected Resource Configure Type",
fmt.Sprintf("Expected *opnsense.Client, got: %T. Please report this issue to the provider developers.", req.ProviderData),
)
return
}

d.client = opnsense.NewClient(apiClient)
}

func (d *InterfaceDataSource) Read(ctx context.Context, req datasource.ReadRequest, resp *datasource.ReadResponse) {
var data *InterfaceDataSourceModel

// Read Terraform configuration data into the model
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)

if resp.Diagnostics.HasError() {
return
}

// Get resource from OPNsense API
resource, err := d.client.Diagnostics().GetInterface(ctx, data.Device.ValueString())
if err != nil {
resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to read interface, got error: %s", err))
return
}

// Convert OPNsense struct to TF schema
model, err := convertInterfaceConfigStructToSchema(resource)
if err != nil {
resp.Diagnostics.AddError("Client Error",
fmt.Sprintf("Unable to read interface, got error: %s", err))
return
}

// Save updated data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &model)...)
}
229 changes: 229 additions & 0 deletions internal/service/interface_schema.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
package service

import (
"context"
"github.com/browningluke/opnsense-go/pkg/diagnostics"
"github.com/hashicorp/terraform-plugin-framework/attr"
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
"github.com/hashicorp/terraform-plugin-framework/types"
"terraform-provider-opnsense/internal/tools"
)

type InterfaceDataSourceModel struct {
Device types.String `tfsdk:"device"`
Media types.String `tfsdk:"media"`
MediaRaw types.String `tfsdk:"media_raw"`
MacAddr types.String `tfsdk:"macaddr"`
IsPhysical types.Bool `tfsdk:"is_physical"`
MTU types.Int64 `tfsdk:"mtu"`
Status types.String `tfsdk:"status"`

Flags types.Set `tfsdk:"flags"`
Capabilities types.Set `tfsdk:"capabilities"`
Options types.Set `tfsdk:"options"`
SupportedMedia types.Set `tfsdk:"supported_media"`
Groups types.Set `tfsdk:"groups"`

Ipv4 types.List `tfsdk:"ipv4"`
Ipv6 types.List `tfsdk:"ipv6"`
}

type Ipv4Model struct {
Ipaddr types.String `tfsdk:"ipaddr"`
SubnetBits types.Int64 `tfsdk:"subnetbits"`
Tunnel types.Bool `tfsdk:"tunnel"`
}

type Ipv6Model struct {
Ipaddr types.String `tfsdk:"ipaddr"`
SubnetBits types.Int64 `tfsdk:"subnetbits"`
Tunnel types.Bool `tfsdk:"tunnel"`
Autoconf types.Bool `tfsdk:"autoconf"`
Deprecated types.Bool `tfsdk:"deprecated"`
LinkLocal types.Bool `tfsdk:"link_local"`
Tentative types.Bool `tfsdk:"tentative"`
}

var ipv4AttrTypes = map[string]attr.Type{
"ipaddr": types.StringType,
"subnetbits": types.Int64Type,
"tunnel": types.BoolType,
}

var ipv6AttrTypes = map[string]attr.Type{
"ipaddr": types.StringType,
"subnetbits": types.Int64Type,
"tunnel": types.BoolType,
"autoconf": types.BoolType,
"deprecated": types.BoolType,
"link_local": types.BoolType,
"tentative": types.BoolType,
}

func InterfaceDataSourceSchema() schema.Schema {
return schema.Schema{
MarkdownDescription: "Interfaces can be used to get configurations of OPNsense interfaces.",

Attributes: map[string]schema.Attribute{
"device": schema.StringAttribute{
MarkdownDescription: "Name of the interface device.",
Required: true,
},
"media": schema.StringAttribute{
MarkdownDescription: "Interface media type settings (see https://man.openbsd.org/ifmedia.4).",
Computed: true,
},
"media_raw": schema.StringAttribute{
MarkdownDescription: "User-friendly interface media type.",
Computed: true,
},
"macaddr": schema.StringAttribute{
MarkdownDescription: "MAC address assigned to the interface.",
Computed: true,
},
"is_physical": schema.BoolAttribute{
MarkdownDescription: "Whether the interface is physical or virtual.",
Computed: true,
},
"mtu": schema.Int64Attribute{
MarkdownDescription: "Maximum Transmission Unit for the interface. This is typically 1500 bytes but can vary in some circumstances.",
Computed: true,
},
"status": schema.StringAttribute{
MarkdownDescription: "Status of the interface (e.g. `\"active\"`).",
Computed: true,
},
"flags": schema.SetAttribute{
MarkdownDescription: "List of flags configured on the interface (equiv. to flags=xxxx in output of ifconfig).",
Computed: true,
ElementType: types.StringType,
},
"capabilities": schema.SetAttribute{
MarkdownDescription: "List of capabilities the interface supports.",
Computed: true,
ElementType: types.StringType,
},
"options": schema.SetAttribute{
MarkdownDescription: "List of options configured on the interface (equiv. to options=xx in output of ifconfig).",
Computed: true,
ElementType: types.StringType,
},
"supported_media": schema.SetAttribute{
MarkdownDescription: "List of supported media type settings (see https://man.openbsd.org/ifmedia.4).",
Computed: true,
ElementType: types.StringType,
},
"groups": schema.SetAttribute{
MarkdownDescription: "List of groups the interface is a member of.",
Computed: true,
ElementType: types.StringType,
},
"ipv4": schema.ListNestedAttribute{
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"ipaddr": schema.StringAttribute{
MarkdownDescription: "IPv4 address assigned to the interface.",
Computed: true,
},
"subnetbits": schema.Int64Attribute{
MarkdownDescription: "Number of subnet bits (i.e. CIDR).",
Computed: true,
},
"tunnel": schema.BoolAttribute{
MarkdownDescription: "Whether IPv4 tunnelling is enabled.",
Computed: true,
},
},
}},
"ipv6": schema.ListNestedAttribute{
Computed: true,
NestedObject: schema.NestedAttributeObject{
Attributes: map[string]schema.Attribute{
"ipaddr": schema.StringAttribute{
MarkdownDescription: "IPv6 address assigned to the interface.",
Computed: true,
},
"subnetbits": schema.Int64Attribute{
MarkdownDescription: "Number of subnet bits (i.e. CIDR).",
Computed: true,
},
"tunnel": schema.BoolAttribute{
MarkdownDescription: "Whether IPv6 tunnelling is enabled.",
Computed: true,
},
"autoconf": schema.BoolAttribute{
MarkdownDescription: "Whether auto-configuration is enabled for the address.",
Computed: true,
},
"deprecated": schema.BoolAttribute{
MarkdownDescription: "Whether the address is deprecated.",
Computed: true,
},
"link_local": schema.BoolAttribute{
MarkdownDescription: "Whether the address is link-local.",
Computed: true,
},
"tentative": schema.BoolAttribute{
MarkdownDescription: "Whether the address is tentative.",
Computed: true,
},
},
}},
},
}
}

func convertInterfaceConfigStructToSchema(d *diagnostics.Interface) (*InterfaceDataSourceModel, error) {
model := &InterfaceDataSourceModel{
Device: types.StringValue(d.Device),
Media: types.StringValue(d.Media),
MediaRaw: types.StringValue(d.MediaRaw),
MacAddr: types.StringValue(d.MacAddr),
IsPhysical: types.BoolValue(d.IsPhysical),
MTU: tools.StringToInt64Null(d.MTU),
Status: types.StringValue(d.Status),
Flags: tools.StringSliceToSet(d.Flags),
Capabilities: tools.StringSliceToSet(d.Capabilities),
Options: tools.StringSliceToSet(d.Options),
SupportedMedia: tools.StringSliceToSet(d.SupportedMedia),
Groups: tools.StringSliceToSet(d.Groups),
}

// Creating an empty slice results in `[]` rather than `null` if OPNsense API returned an empty list.
ipv4s := []Ipv4Model{}
for _, elem := range d.Ipv4 {
ipv4s = append(ipv4s, Ipv4Model{
Ipaddr: types.StringValue(elem.IpAddr),
SubnetBits: types.Int64Value(elem.SubnetBits),
Tunnel: types.BoolValue(elem.Tunnel),
})
}

ipv6s := []Ipv6Model{}
for _, elem := range d.Ipv6 {
ipv6s = append(ipv6s, Ipv6Model{
Ipaddr: types.StringValue(elem.IpAddr),
SubnetBits: types.Int64Value(elem.SubnetBits),
Tunnel: types.BoolValue(elem.Tunnel),
Autoconf: types.BoolValue(elem.Autoconf),
Deprecated: types.BoolValue(elem.Deprecated),
LinkLocal: types.BoolValue(elem.LinkLocal),
Tentative: types.BoolValue(elem.Tentative),
})
}

model.Ipv4, _ = types.ListValueFrom(
context.Background(),
types.ObjectType{}.WithAttributeTypes(ipv4AttrTypes),
ipv4s,
)

model.Ipv6, _ = types.ListValueFrom(
context.Background(),
types.ObjectType{}.WithAttributeTypes(ipv6AttrTypes),
ipv6s,
)

return model, nil
}
Loading

0 comments on commit 6ecc7d9

Please sign in to comment.