Add WLAN resource

This commit is contained in:
Paul Tyng
2019-12-26 08:29:03 -05:00
parent cd0cd18dda
commit 2ef79cfcb0
9 changed files with 560 additions and 52 deletions

View File

@@ -38,6 +38,7 @@ func Provider() terraform.ResourceProvider {
},
ResourcesMap: map[string]*schema.Resource{
"unifi_network": resourceNetwork(),
"unifi_wlan": resourceWLAN(),
},
}
p.ConfigureFunc = configure(p)

153
provider/resource_wlan.go Normal file
View File

@@ -0,0 +1,153 @@
package provider
import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/helper/schema"
"github.com/paultyng/terraform-provider-unifi/unifi"
)
func resourceWLAN() *schema.Resource {
return &schema.Resource{
Create: resourceWLANCreate,
Read: resourceWLANRead,
Update: resourceWLANUpdate,
Delete: resourceWLANDelete,
// TODO: handle site + ID (or name)
// Importer: &schema.ResourceImporter{
// State: schema.ImportStatePassthrough,
// },
Schema: map[string]*schema.Schema{
"site": {
Type: schema.TypeString,
Optional: true,
Default: "default",
ForceNew: true,
},
"name": {
Type: schema.TypeString,
Required: true,
},
"vlan_id": {
Type: schema.TypeInt,
Optional: true,
Default: 1,
},
"passphrase": {
Type: schema.TypeString,
Required: true,
Sensitive: true,
},
},
}
}
func resourceWLANCreate(d *schema.ResourceData, meta interface{}) error {
c := meta.(*client)
site := d.Get("site").(string)
// TODO: allow passing these defaults
wlanGroups, err := c.c.ListWLANGroup(site)
if err != nil {
return err
}
var defaultWLANGroup *unifi.WLANGroup
for _, wg := range wlanGroups {
if wg.HiddenID == "Default" {
defaultWLANGroup = &wg
break
}
}
if defaultWLANGroup == nil {
return fmt.Errorf("unable to find default WLAN group")
}
userGroups, err := c.c.ListUserGroup(site)
if err != nil {
return err
}
var defaultUserGroup *unifi.UserGroup
for _, ug := range userGroups {
if ug.HiddenID == "Default" {
defaultUserGroup = &ug
break
}
}
if defaultUserGroup == nil {
return fmt.Errorf("unable to find default user group")
}
req := &unifi.WLAN{
Name: d.Get("name").(string),
VLAN: fmt.Sprintf("%d", d.Get("vlan_id").(int)),
XPassphrase: d.Get("passphrase").(string),
WLANGroupID: defaultWLANGroup.ID,
UserGroupID: defaultUserGroup.ID,
Enabled: true,
VLANEnabled: true,
WPAEnc: "ccmp",
Security: "wpapsk",
WPAMode: "wpa2",
NameCombineEnabled: true,
GroupRekey: 3600,
DTIMMode: "default",
No2GhzOui: true,
MinrateNaBeaconRateKbps: 6000,
MinrateNaDataRateKbps: 6000,
MinrateNaMgmtRateKbps: 6000,
MinrateNgBeaconRateKbps: 1000,
MinrateNgCckRatesEnabled: true,
MinrateNgDataRateKbps: 1000,
MinrateNgMgmtRateKbps: 1000,
}
resp, err := c.c.CreateWLAN(site, req)
if err != nil {
return err
}
d.SetId(resp.ID)
return nil
}
func resourceWLANRead(d *schema.ResourceData, meta interface{}) error {
c := meta.(*client)
site := d.Get("site").(string)
id := d.Id()
_, err := c.c.GetWLAN(site, id)
if _, ok := err.(*unifi.NotFoundError); ok {
d.SetId("")
return nil
}
if err != nil {
return err
}
return nil
}
func resourceWLANUpdate(d *schema.ResourceData, meta interface{}) error {
panic("not implemented")
}
func resourceWLANDelete(d *schema.ResourceData, meta interface{}) error {
c := meta.(*client)
site := d.Get("site").(string)
id := d.Id()
err := c.c.DeleteWLAN(site, id)
if _, ok := err.(*unifi.NotFoundError); ok {
return nil
}
return err
}

View File

@@ -0,0 +1,31 @@
package provider
import (
"testing"
"github.com/hashicorp/terraform-plugin-sdk/helper/resource"
)
func TestAccWLAN_basic(t *testing.T) {
resource.ParallelTest(t, resource.TestCase{
Providers: providers,
// TODO: CheckDestroy: ,
Steps: []resource.TestStep{
{
Config: testAccWLANConfig,
Check: resource.ComposeTestCheckFunc(
// testCheckNetworkExists(t, "name"),
),
},
// importStep("unifi_wlan.test"),
},
})
}
const testAccWLANConfig = `
resource "unifi_wlan" "test" {
name = "foo"
vlan_id = 202
passphrase = "12345678"
}
`

View File

@@ -5,9 +5,12 @@ import (
)
type Network struct {
ID string `json:"_id,omitempty"`
HiddenID string `json:"attr_hidden_id,omitempty"`
NoDelete bool `json:"attr_no_delete,omitempty"`
ID string `json:"_id,omitempty"`
// Hidden bool `json:"attr_hidden,omitempty"`
// HiddenID string `json:"attr_hidden_id,omitempty"`
// NoDelete bool `json:"attr_no_delete,omitempty"`
// NoEdit bool `json:"attr_no_edit,omitempty"`
Purpose string `json:"purpose"` // "corporate"
NetworkGroup string `json:"networkgroup"` // "LAN"
@@ -34,11 +37,9 @@ type Network struct {
IPV6PDStop string `json:"ipv6_pd_stop"` // "::7d1"
}
func (c *Client) ListNetworks(site string) ([]Network, error) {
func (c *Client) ListNetwork(site string) ([]Network, error) {
var respBody struct {
Meta struct {
RC string `json:"rc"`
} `json:"meta"`
Meta meta `json:"meta"`
Data []Network `json:"data"`
}
@@ -51,35 +52,22 @@ func (c *Client) ListNetworks(site string) ([]Network, error) {
}
func (c *Client) GetNetwork(site, id string) (*Network, error) {
list, err := c.ListNetworks(site)
var respBody struct {
Meta meta `json:"meta"`
Data []Network `json:"data"`
}
err := c.do("GET", fmt.Sprintf("s/%s/rest/networkconf/%s", site, id), nil, &respBody)
if err != nil {
return nil, err
}
for _, net := range list {
if net.ID == id {
return &net, nil
}
if len(respBody.Data) != 1 {
return nil, &NotFoundError{}
}
return nil, &NotFoundError{}
// var respBody struct {
// Meta struct {
// RC string `json:"rc"`
// } `json:"meta"`
// Data []Network `json:"data"`
// }
// err := c.do("GET", fmt.Sprintf("s/%s/rest/networkconf/%s", site, id), nil, &respBody)
// if err != nil {
// return nil, err
// }
// if len(respBody.Data) != 1 {
// return nil, &NotFoundError{}
// }
// net := respBody.Data[0]
// return &net, nil
d := respBody.Data[0]
return &d, nil
}
func (c *Client) DeleteNetwork(site, id, name string) error {
@@ -94,15 +82,13 @@ func (c *Client) DeleteNetwork(site, id, name string) error {
return nil
}
func (c *Client) CreateNetwork(site string, network *Network) (*Network, error) {
func (c *Client) CreateNetwork(site string, d *Network) (*Network, error) {
var respBody struct {
Meta struct {
RC string `json:"rc"`
} `json:"meta"`
Meta meta `json:"meta"`
Data []Network `json:"data"`
}
err := c.do("POST", fmt.Sprintf("s/%s/rest/networkconf", site), network, &respBody)
err := c.do("POST", fmt.Sprintf("s/%s/rest/networkconf", site), d, &respBody)
if err != nil {
return nil, err
}
@@ -111,7 +97,7 @@ func (c *Client) CreateNetwork(site string, network *Network) (*Network, error)
return nil, &NotFoundError{}
}
newNetwork := respBody.Data[0]
new := respBody.Data[0]
return &newNetwork, nil
return &new, nil
}

View File

@@ -1,9 +1,12 @@
package unifi
type Site struct {
ID string `json:"_id,omitempty"`
HiddenID string `json:"attr_hidden_id,omitempty"`
NoDelete bool `json:"attr_no_delete,omitempty"`
ID string `json:"_id,omitempty"`
// Hidden bool `json:"attr_hidden,omitempty"`
// HiddenID string `json:"attr_hidden_id,omitempty"`
// NoDelete bool `json:"attr_no_delete,omitempty"`
// NoEdit bool `json:"attr_no_edit,omitempty"`
Name string `json:"name"`
Description string `json:"desc"`
@@ -13,9 +16,7 @@ type Site struct {
func (c *Client) ListSites() ([]Site, error) {
var respBody struct {
Meta struct {
RC string `json:"rc"`
} `json:"meta"`
Meta meta `json:"meta"`
Data []Site `json:"data"`
}

View File

@@ -6,7 +6,6 @@ import (
"encoding/json"
"fmt"
"io"
// "io/ioutil"
"net"
"net/http"
"net/http/cookiejar"
@@ -73,9 +72,14 @@ func (c *Client) Login(user, pass string) error {
}
func (c *Client) do(method, relativeURL string, reqBody interface{}, respBody interface{}) error {
var reqReader io.Reader
var (
reqReader io.Reader
err error
reqBytes []byte
)
if reqBody != nil {
reqBytes, err := json.Marshal(reqBody)
reqBytes, err = json.Marshal(reqBody)
if err != nil {
return err
}
@@ -101,11 +105,12 @@ func (c *Client) do(method, relativeURL string, reqBody interface{}, respBody in
}
if resp.StatusCode != 200 {
// body, _ := ioutil.ReadAll(resp.Body)
//TODO: debug logging?
// fmt.Printf("%s %s\nStatus: %s\n%s", method, url.String(), resp.Status, string(body))
return fmt.Errorf("error from API %s", resp.Status)
fmt.Printf("Request Body:\n%s\n", string(reqBytes))
errBody := struct {
Meta meta `json:"meta"`
}{}
err = json.NewDecoder(resp.Body).Decode(&errBody)
return fmt.Errorf("%s %s (%s) for %s %s", errBody.Meta.RC, errBody.Meta.Message, resp.Status, method, url.String())
}
if respBody == nil || resp.ContentLength == 0 {
@@ -121,3 +126,8 @@ func (c *Client) do(method, relativeURL string, reqBody interface{}, respBody in
return nil
}
type meta struct {
RC string `json:"rc"`
Message string `json:"msg"`
}

33
unifi/user_group.go Normal file
View File

@@ -0,0 +1,33 @@
package unifi
import (
"fmt"
)
type UserGroup struct {
ID string `json:"_id"`
SiteID string `json:"site_id"`
Name string `json:"name"`
//Hidden bool `json:"attr_hidden,omitempty"`
HiddenID string `json:"attr_hidden_id,omitempty"`
NoDelete bool `json:"attr_no_delete,omitempty"`
//NoEdit bool `json:"attr_no_edit,omitempty"`
QOSRateMaxDown int `json:"qos_rate_max_down"`
QOSRateMaxUp int `json:"qos_rate_max_up"`
}
func (c *Client) ListUserGroup(site string) ([]UserGroup, error) {
var respBody struct {
Meta meta `json:"meta"`
Data []UserGroup `json:"data"`
}
err := c.do("GET", fmt.Sprintf("s/%s/rest/usergroup", site), nil, &respBody)
if err != nil {
return nil, err
}
return respBody.Data, nil
}

259
unifi/wlan.go Normal file
View File

@@ -0,0 +1,259 @@
package unifi
import (
"fmt"
)
/*
{
"meta": {
"rc": "ok"
},
"data": [
{
"_id": "5deeabfc439adf048407dcf3",
"enabled": true,
"name": "fred",
"security": "wpapsk",
"wpa_enc": "ccmp",
"wpa_mode": "wpa2",
"x_passphrase": "Liebeskind",
"wlangroup_id": "5d6d8b0c439adf048407dce9",
"name_combine_enabled": true,
"site_id": "5d6d8b07439adf048407dcd9",
"x_iapp_key": "a199e6f225c01127e4135211236f9767",
"minrate_ng_enabled": true,
"minrate_ng_beacon_rate_kbps": 6000,
"minrate_ng_data_rate_kbps": 6000,
"no2ghz_oui": true,
"wep_idx": 1,
"usergroup_id": "5d6d8b0c439adf048407dce8",
"dtim_mode": "default",
"dtim_ng": 1,
"dtim_na": 1,
"minrate_ng_advertising_rates": false,
"minrate_ng_cck_rates_enabled": true,
"minrate_na_enabled": false,
"minrate_na_advertising_rates": false,
"minrate_na_data_rate_kbps": 6000,
"mac_filter_enabled": false,
"mac_filter_policy": "allow",
"mac_filter_list": [],
"bc_filter_enabled": false,
"bc_filter_list": [],
"group_rekey": 3600,
"vlan_enabled": true,
"vlan": "20",
"radius_das_enabled": false,
"schedule": [],
"minrate_ng_mgmt_rate_kbps": 6000,
"minrate_na_mgmt_rate_kbps": 6000,
"minrate_na_beacon_rate_kbps": 6000
},
{
"_id": "5deecfa31e801c052a1a5f5a",
"enabled": true,
"is_guest": true,
"name": "fred-guest",
"security": "wpapsk",
"usergroup_id": "5d6d8b0c439adf048407dce8",
"vlan_enabled": true,
"wlangroup_id": "5d6d8b0c439adf048407dce9",
"x_passphrase": "Enzo and Alice",
"site_id": "5d6d8b07439adf048407dcd9",
"x_iapp_key": "3e6b24dda0d11ce3c097107fc429596e",
"minrate_ng_enabled": true,
"minrate_ng_beacon_rate_kbps": 6000,
"minrate_ng_data_rate_kbps": 6000,
"vlan": "30",
"wep_idx": 1,
"wpa_mode": "wpa2",
"wpa_enc": "ccmp",
"dtim_mode": "default",
"dtim_ng": 1,
"dtim_na": 1,
"minrate_ng_advertising_rates": false,
"minrate_ng_cck_rates_enabled": true,
"minrate_na_enabled": false,
"minrate_na_advertising_rates": false,
"minrate_na_data_rate_kbps": 6000,
"mac_filter_enabled": false,
"mac_filter_policy": "allow",
"mac_filter_list": [],
"name_combine_enabled": true,
"bc_filter_enabled": false,
"bc_filter_list": [],
"group_rekey": 3600,
"radius_das_enabled": false,
"schedule": [],
"minrate_ng_mgmt_rate_kbps": 6000,
"minrate_na_mgmt_rate_kbps": 6000,
"minrate_na_beacon_rate_kbps": 6000
},
{
"_id": "5deed0d51e801c052a1a5f64",
"enabled": true,
"name": "patpat",
"security": "wpapsk",
"usergroup_id": "5d6d8b0c439adf048407dce8",
"wlangroup_id": "5d6d8b0c439adf048407dce9",
"x_passphrase": "forever home 23",
"site_id": "5d6d8b07439adf048407dcd9",
"x_iapp_key": "5aa68d6ab2ddedf45230f7f87bf11921",
"minrate_ng_enabled": true,
"minrate_ng_beacon_rate_kbps": 6000,
"minrate_ng_data_rate_kbps": 6000,
"vlan": "40",
"vlan_enabled": true,
"wep_idx": 1,
"wpa_mode": "wpa2",
"wpa_enc": "ccmp",
"dtim_mode": "default",
"dtim_ng": 1,
"dtim_na": 1,
"minrate_ng_advertising_rates": false,
"minrate_ng_cck_rates_enabled": true,
"minrate_na_enabled": false,
"minrate_na_advertising_rates": false,
"minrate_na_data_rate_kbps": 6000,
"mac_filter_enabled": false,
"mac_filter_policy": "allow",
"mac_filter_list": [],
"name_combine_enabled": true,
"bc_filter_enabled": false,
"bc_filter_list": [],
"group_rekey": 3600,
"radius_das_enabled": false,
"schedule": [],
"minrate_ng_mgmt_rate_kbps": 6000,
"minrate_na_mgmt_rate_kbps": 6000,
"minrate_na_beacon_rate_kbps": 6000
}
]
}
*/
type WLAN struct {
ID string `json:"_id,omitempty"`
SiteID string `json:"site_id,omitempty"`
Enabled bool `json:"enabled"`
Name string `json:"name"`
Security string `json:"security"` // "wpapsk", "wpaeap", "open"
WPAEnc string `json:"wpa_enc"` // "ccmp", "tkip"?
WPAMode string `json:"wpa_mode"` // "wpa2"
XPassphrase string `json:"x_passphrase"`
// create only?
FastRoamingEnabled bool `json:"fast_roaming_enabled,omitempty"`
HideSSID bool `json:"hide_ssid,omitempty"`
IsGuest bool `json:"is_guest,omitempty"`
MulticastEnhanceEnabled bool `json:"mcastenhance_enabled,omitempty"`
RADIUSDasEnabled bool `json:"radius_das_enabled"`
WLANGroupID string `json:"wlangroup_id"`
NameCombineEnabled bool `json:"name_combine_enabled"`
NameCombineSuffix string `json:"name_combine_suffix"`
XIappKey string `json:"x_iapp_key,omitempty"`
No2GhzOui bool `json:"no2ghz_oui"`
WEPIdx int `json:"wep_idx,omitempty"`
UserGroupID string `json:"usergroup_id"`
DTIMMode string `json:"dtim_mode"`
DTIMNg int `json:"dtim_ng,omitempty"`
DTIMNa int `json:"dtim_na,omitempty"`
MinrateNgEnabled bool `json:"minrate_ng_enabled"`
MinrateNgBeaconRateKbps int `json:"minrate_ng_beacon_rate_kbps"`
MinrateNgDataRateKbps int `json:"minrate_ng_data_rate_kbps"`
MinrateNgAdvertisingRates bool `json:"minrate_ng_advertising_rates"`
MinrateNgCckRatesEnabled bool `json:"minrate_ng_cck_rates_enabled"`
MinrateNaEnabled bool `json:"minrate_na_enabled"`
MinrateNaAdvertisingRates bool `json:"minrate_na_advertising_rates"`
MinrateNaDataRateKbps int `json:"minrate_na_data_rate_kbps"`
MinrateNgMgmtRateKbps int `json:"minrate_ng_mgmt_rate_kbps"`
MinrateNaMgmtRateKbps int `json:"minrate_na_mgmt_rate_kbps"`
MinrateNaBeaconRateKbps int `json:"minrate_na_beacon_rate_kbps"`
MACFilterEnabled bool `json:"mac_filter_enabled"`
MACFilterPolicy string `json:"mac_filter_policy,omitempty"`
MACFilterList []string `json:"mac_filter_list,omitempty"`
BroadcastFilterEnabled bool `json:"bc_filter_enabled"`
BroadcastFilterList []string `json:"bc_filter_list,omitempty"`
GroupRekey int `json:"group_rekey"`
VLANEnabled bool `json:"vlan_enabled"`
VLAN string `json:"vlan"`
Schedule []string `json:"schedule"`
}
func (c *Client) ListWLAN(site string) ([]WLAN, error) {
var respBody struct {
Meta meta `json:"meta"`
Data []WLAN `json:"data"`
}
err := c.do("GET", fmt.Sprintf("s/%s/rest/wlanconf", site), nil, &respBody)
if err != nil {
return nil, err
}
return respBody.Data, nil
}
func (c *Client) GetWLAN(site, id string) (*WLAN, error) {
var respBody struct {
Meta meta `json:"meta"`
Data []WLAN `json:"data"`
}
err := c.do("GET", fmt.Sprintf("s/%s/rest/wlanconf/%s", site, id), nil, &respBody)
if err != nil {
return nil, err
}
if len(respBody.Data) != 1 {
return nil, &NotFoundError{}
}
d := respBody.Data[0]
return &d, nil
}
func (c *Client) DeleteWLAN(site, id string) error {
err := c.do("DELETE", fmt.Sprintf("s/%s/rest/wlanconf/%s", site, id), struct{}{}, nil)
if err != nil {
return err
}
return nil
}
func (c *Client) CreateWLAN(site string, d *WLAN) (*WLAN, error) {
if d.Schedule == nil {
d.Schedule = []string{}
}
var respBody struct {
Meta meta `json:"meta"`
Data []WLAN `json:"data"`
}
err := c.do("POST", fmt.Sprintf("s/%s/rest/wlanconf", site), d, &respBody)
if err != nil {
return nil, err
}
if len(respBody.Data) != 1 {
return nil, &NotFoundError{}
}
new := respBody.Data[0]
return &new, nil
}

34
unifi/wlan_group.go Normal file
View File

@@ -0,0 +1,34 @@
package unifi
import (
"fmt"
)
type WLANGroup struct {
ID string `json:"_id"`
SiteID string `json:"site_id"`
Name string `json:"name"`
Hidden bool `json:"attr_hidden,omitempty"`
HiddenID string `json:"attr_hidden_id,omitempty"`
NoDelete bool `json:"attr_no_delete,omitempty"`
NoEdit bool `json:"attr_no_edit,omitempty"`
BSupported bool `json:"b_supported"`
LoadBalanceEnabled bool `json:"loadbalance_enabled"`
PMFMode string `json:"pmf_mode"`
}
func (c *Client) ListWLANGroup(site string) ([]WLANGroup, error) {
var respBody struct {
Meta meta `json:"meta"`
Data []WLANGroup `json:"data"`
}
err := c.do("GET", fmt.Sprintf("s/%s/rest/wlangroup", site), nil, &respBody)
if err != nil {
return nil, err
}
return respBody.Data, nil
}