Skip to content

Commit 654cd1e

Browse files
authored
Add support for dhcp.host
We want to be able to statically assign an IP address to a known MAC address. This `dhcp.host` resource gives us exactly that. It's a fairly easy going resource, so not much here. Like the other resources we've added, we only add the minimum to make it useful. We'll come back and add more in the future. Branch: joneshf/add-support-for-dhcp-host Pull-Request: #131
1 parent 83fa531 commit 654cd1e

File tree

9 files changed

+419
-0
lines changed

9 files changed

+419
-0
lines changed

docs/data-sources/dhcp_host.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "openwrt_dhcp_host Data Source - openwrt"
4+
subcategory: ""
5+
description: |-
6+
Assign a fixed IP address to hosts.
7+
---
8+
9+
# openwrt_dhcp_host (Data Source)
10+
11+
Assign a fixed IP address to hosts.
12+
13+
## Example Usage
14+
15+
```terraform
16+
data "openwrt_dhcp_host" "testing" {
17+
id = "testing"
18+
}
19+
```
20+
21+
<!-- schema generated by tfplugindocs -->
22+
## Schema
23+
24+
### Required
25+
26+
- `id` (String) Name of the section. This name is only used when interacting with UCI directly.
27+
28+
### Read-Only
29+
30+
- `dns` (Boolean) Add static forward and reverse DNS entries for this host.
31+
- `ip` (String) The IP address to be used for this host, or `ignore` to ignore any DHCP request from this host.
32+
- `mac` (String) The hardware address(es) of this host, separated by spaces.
33+
- `name` (String) Hostname to assign.
34+
35+

docs/resources/dhcp_host.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "openwrt_dhcp_host Resource - openwrt"
4+
subcategory: ""
5+
description: |-
6+
Assign a fixed IP address to hosts.
7+
---
8+
9+
# openwrt_dhcp_host (Resource)
10+
11+
Assign a fixed IP address to hosts.
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "openwrt_dhcp_host" "testing" {
17+
id = "testing"
18+
ip = "192.168.1.50"
19+
mac = "12:34:56:78:90:ab"
20+
name = "testing"
21+
}
22+
```
23+
24+
<!-- schema generated by tfplugindocs -->
25+
## Schema
26+
27+
### Required
28+
29+
- `id` (String) Name of the section. This name is only used when interacting with UCI directly.
30+
31+
### Optional
32+
33+
- `dns` (Boolean) Add static forward and reverse DNS entries for this host.
34+
- `ip` (String) The IP address to be used for this host, or `ignore` to ignore any DHCP request from this host.
35+
- `mac` (String) The hardware address(es) of this host, separated by spaces.
36+
- `name` (String) Hostname to assign.
37+
38+
## Import
39+
40+
Import is supported using the following syntax:
41+
42+
```shell
43+
# Find the Terraform id from LuCI's JSON-RPC API.
44+
# One way to find this information is with `curl` and `jq`:
45+
#
46+
# curl \
47+
# --data '{"id": 0, "method": "foreach", "params": ["dhcp", "host"]}' \
48+
# http://192.168.1.1/cgi-bin/luci/rpc/uci?auth=$AUTH_TOKEN \
49+
# | jq '.result | map({terraformId: .[".name"]})'
50+
#
51+
# This command will output something like:
52+
#
53+
# [
54+
# {
55+
# "terraformId": "cfg123456",
56+
# }
57+
# ]
58+
#
59+
# We'd then use the information to import the appropriate resource:
60+
61+
terraform import openwrt_dhcp_host.this cfg123456
62+
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
data "openwrt_dhcp_host" "testing" {
2+
id = "testing"
3+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Find the Terraform id from LuCI's JSON-RPC API.
2+
# One way to find this information is with `curl` and `jq`:
3+
#
4+
# curl \
5+
# --data '{"id": 0, "method": "foreach", "params": ["dhcp", "host"]}' \
6+
# http://192.168.1.1/cgi-bin/luci/rpc/uci?auth=$AUTH_TOKEN \
7+
# | jq '.result | map({terraformId: .[".name"]})'
8+
#
9+
# This command will output something like:
10+
#
11+
# [
12+
# {
13+
# "terraformId": "cfg123456",
14+
# }
15+
# ]
16+
#
17+
# We'd then use the information to import the appropriate resource:
18+
19+
terraform import openwrt_dhcp_host.this cfg123456
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
resource "openwrt_dhcp_host" "testing" {
2+
id = "testing"
3+
ip = "192.168.1.50"
4+
mac = "12:34:56:78:90:ab"
5+
name = "testing"
6+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//go:build acceptance.test
2+
3+
package host_test
4+
5+
import (
6+
"context"
7+
"fmt"
8+
"log"
9+
"os"
10+
"testing"
11+
12+
"github.com/joneshf/terraform-provider-openwrt/internal/acceptancetest"
13+
"github.com/ory/dockertest/v3"
14+
)
15+
16+
var (
17+
dockerPool *dockertest.Pool
18+
)
19+
20+
func TestMain(m *testing.M) {
21+
var (
22+
code int
23+
err error
24+
tearDown func()
25+
)
26+
ctx := context.Background()
27+
tearDown, dockerPool, err = acceptancetest.Setup(ctx)
28+
defer func() {
29+
tearDown()
30+
os.Exit(code)
31+
}()
32+
if err != nil {
33+
fmt.Printf("Problem setting up tests: %s", err)
34+
code = 1
35+
return
36+
}
37+
38+
log.Printf("Running tests")
39+
code = m.Run()
40+
}

openwrt/dhcp/host/host.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package host
2+
3+
import (
4+
"regexp"
5+
6+
"github.com/hashicorp/terraform-plugin-framework-validators/stringvalidator"
7+
"github.com/hashicorp/terraform-plugin-framework/datasource"
8+
"github.com/hashicorp/terraform-plugin-framework/resource"
9+
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
10+
"github.com/hashicorp/terraform-plugin-framework/types"
11+
"github.com/joneshf/terraform-provider-openwrt/lucirpc"
12+
"github.com/joneshf/terraform-provider-openwrt/openwrt/internal/lucirpcglue"
13+
)
14+
15+
const (
16+
addDNSEntriesAttribute = "dns"
17+
addDNSEntriesAttributeDescription = "Add static forward and reverse DNS entries for this host."
18+
addDNSEntriesUCIOption = "dns"
19+
20+
hostnameAttribute = "name"
21+
hostnameAttributeDescription = "Hostname to assign."
22+
hostnameUCIOption = "name"
23+
24+
ipAddressAttribute = "ip"
25+
ipAddressAttributeDescription = "The IP address to be used for this host, or `ignore` to ignore any DHCP request from this host."
26+
ipAddressUCIOption = "ip"
27+
28+
macAddressAttribute = "mac"
29+
macAddressAttributeDescription = "The hardware address(es) of this host, separated by spaces."
30+
macAddressUCIOption = "mac"
31+
32+
schemaDescription = "Assign a fixed IP address to hosts."
33+
34+
uciConfig = "dhcp"
35+
uciType = "host"
36+
)
37+
38+
var (
39+
addDNSEntriesSchemaAttribute = lucirpcglue.BoolSchemaAttribute[model, lucirpc.Options, lucirpc.Options]{
40+
Description: addDNSEntriesAttributeDescription,
41+
ReadResponse: lucirpcglue.ReadResponseOptionBool(modelSetAddDNSEntries, addDNSEntriesAttribute, addDNSEntriesUCIOption),
42+
ResourceExistence: lucirpcglue.NoValidation,
43+
UpsertRequest: lucirpcglue.UpsertRequestOptionBool(modelGetAddDNSEntries, addDNSEntriesAttribute, addDNSEntriesUCIOption),
44+
}
45+
46+
hostnameSchemaAttribute = lucirpcglue.StringSchemaAttribute[model, lucirpc.Options, lucirpc.Options]{
47+
Description: hostnameAttributeDescription,
48+
ReadResponse: lucirpcglue.ReadResponseOptionString(modelSetHostname, hostnameAttribute, hostnameUCIOption),
49+
ResourceExistence: lucirpcglue.NoValidation,
50+
UpsertRequest: lucirpcglue.UpsertRequestOptionString(modelGetHostname, hostnameAttribute, hostnameUCIOption),
51+
}
52+
53+
ipAddressSchemaAttribute = lucirpcglue.StringSchemaAttribute[model, lucirpc.Options, lucirpc.Options]{
54+
Description: ipAddressAttributeDescription,
55+
ReadResponse: lucirpcglue.ReadResponseOptionString(modelSetIPAddress, ipAddressAttribute, ipAddressUCIOption),
56+
ResourceExistence: lucirpcglue.NoValidation,
57+
UpsertRequest: lucirpcglue.UpsertRequestOptionString(modelGetIPAddress, ipAddressAttribute, ipAddressUCIOption),
58+
Validators: []validator.String{
59+
stringvalidator.Any(
60+
stringvalidator.OneOf(
61+
"ignore",
62+
),
63+
stringvalidator.RegexMatches(
64+
regexp.MustCompile("^([[:digit:]]{1,3}.){3}[[:digit:]]{1,3}$"),
65+
`must be a valid IP address (e.g. "192.168.3.1")`,
66+
),
67+
),
68+
},
69+
}
70+
71+
macAddressSchemaAttribute = lucirpcglue.StringSchemaAttribute[model, lucirpc.Options, lucirpc.Options]{
72+
Description: macAddressAttributeDescription,
73+
ReadResponse: lucirpcglue.ReadResponseOptionString(modelSetMACAddress, macAddressAttribute, macAddressUCIOption),
74+
ResourceExistence: lucirpcglue.NoValidation,
75+
UpsertRequest: lucirpcglue.UpsertRequestOptionString(modelGetMACAddress, macAddressAttribute, macAddressUCIOption),
76+
Validators: []validator.String{
77+
stringvalidator.RegexMatches(
78+
regexp.MustCompile("^([[:xdigit:]][[:xdigit:]]:){5}[[:xdigit:]][[:xdigit:]]$"),
79+
`must be a valid MAC address (e.g. "12:34:56:78:90:ab")`,
80+
),
81+
},
82+
}
83+
84+
schemaAttributes = map[string]lucirpcglue.SchemaAttribute[model, lucirpc.Options, lucirpc.Options]{
85+
addDNSEntriesAttribute: addDNSEntriesSchemaAttribute,
86+
hostnameAttribute: hostnameSchemaAttribute,
87+
ipAddressAttribute: ipAddressSchemaAttribute,
88+
lucirpcglue.IdAttribute: lucirpcglue.IdSchemaAttribute(modelGetId, modelSetId),
89+
macAddressAttribute: macAddressSchemaAttribute,
90+
}
91+
)
92+
93+
func NewDataSource() datasource.DataSource {
94+
return lucirpcglue.NewDataSource(
95+
modelGetId,
96+
schemaAttributes,
97+
schemaDescription,
98+
uciConfig,
99+
uciType,
100+
)
101+
}
102+
103+
func NewResource() resource.Resource {
104+
return lucirpcglue.NewResource(
105+
modelGetId,
106+
schemaAttributes,
107+
schemaDescription,
108+
uciConfig,
109+
uciType,
110+
)
111+
}
112+
113+
type model struct {
114+
AddDNSEntries types.Bool `tfsdk:"dns"`
115+
Hostname types.String `tfsdk:"name"`
116+
Id types.String `tfsdk:"id"`
117+
IPAddress types.String `tfsdk:"ip"`
118+
MACAddress types.String `tfsdk:"mac"`
119+
}
120+
121+
func modelGetAddDNSEntries(m model) types.Bool { return m.AddDNSEntries }
122+
func modelGetHostname(m model) types.String { return m.Hostname }
123+
func modelGetId(m model) types.String { return m.Id }
124+
func modelGetIPAddress(m model) types.String { return m.IPAddress }
125+
func modelGetMACAddress(m model) types.String { return m.MACAddress }
126+
127+
func modelSetAddDNSEntries(m *model, value types.Bool) { m.AddDNSEntries = value }
128+
func modelSetHostname(m *model, value types.String) { m.Hostname = value }
129+
func modelSetId(m *model, value types.String) { m.Id = value }
130+
func modelSetIPAddress(m *model, value types.String) { m.IPAddress = value }
131+
func modelSetMACAddress(m *model, value types.String) { m.MACAddress = value }

0 commit comments

Comments
 (0)