From e15602ccfab29e05ae23cb15a710229ebfad73a7 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Mon, 15 Apr 2019 01:21:24 -0700 Subject: [PATCH 1/3] Add sites support. This changes the interface. --- core/unifi/unidev.go | 10 +++--- core/unifi/unifi.go | 86 ++++++++++++++++++++++++++++---------------- 2 files changed, 62 insertions(+), 34 deletions(-) diff --git a/core/unifi/unidev.go b/core/unifi/unidev.go index cd5d6db4..85f7f86e 100644 --- a/core/unifi/unidev.go +++ b/core/unifi/unidev.go @@ -13,14 +13,16 @@ import ( ) const ( + // SiteList is the path to the api site list. + SiteList string = "/api/self/sites" // ClientPath is Unifi Clients API Path - ClientPath string = "/api/s/default/stat/sta" + ClientPath string = "/api/s/%s/stat/sta" // DevicePath is where we get data about Unifi devices. - DevicePath string = "/api/s/default/stat/device" + DevicePath string = "/api/s/%s/stat/device" // NetworkPath contains network-configuration data. Not really graphable. - NetworkPath string = "/api/s/default/rest/networkconf" + NetworkPath string = "/api/s/%s/rest/networkconf" // UserGroupPath contains usergroup configurations. - UserGroupPath string = "/api/s/default/rest/usergroup" + UserGroupPath string = "/api/s/%s/rest/usergroup" // LoginPath is Unifi Controller Login API Path LoginPath string = "/api/login" ) diff --git a/core/unifi/unifi.go b/core/unifi/unifi.go index b8b9c0e8..8ab4e910 100644 --- a/core/unifi/unifi.go +++ b/core/unifi/unifi.go @@ -2,57 +2,83 @@ package unifi import ( "encoding/json" + "fmt" "io/ioutil" + "strings" "github.com/pkg/errors" ) // GetClients returns a response full of clients' data from the Unifi Controller. -func (u *Unifi) GetClients() (*Clients, error) { - var response struct { - Clients []UCL `json:"data"` +func (u *Unifi) GetClients(sites []string) (*Clients, error) { + var data []UCL + for _, site := range sites { + var response struct { + Data []UCL `json:"data"` + } + clientPath := fmt.Sprintf(ClientPath, site) + if err := u.GetData(clientPath, &response); err != nil { + return nil, err + } + data = append(data, response.Data...) } - req, err := u.UniReq(ClientPath, "") - if err != nil { - return nil, errors.Wrap(err, "c.UniReq(ClientPath)") - } - resp, err := u.Do(req) - if err != nil { - return nil, errors.Wrap(err, "c.Do(req)") - } - defer func() { - _ = resp.Body.Close() - }() - if body, err := ioutil.ReadAll(resp.Body); err != nil { - return nil, errors.Wrap(err, "ioutil.ReadAll(resp.Body)") - } else if err = json.Unmarshal(body, &response); err != nil { - return nil, errors.Wrap(err, "json.Unmarshal([]UCL)") - } - return &Clients{UCLs: response.Clients}, nil + return &Clients{UCLs: data}, nil } // GetDevices returns a response full of devices' data from the Unifi Controller. -func (u *Unifi) GetDevices() (*Devices, error) { - var response struct { - Data []json.RawMessage `json:"data"` +func (u *Unifi) GetDevices(sites []string) (*Devices, error) { + var data []json.RawMessage + for _, site := range sites { + var response struct { + Data []json.RawMessage `json:"data"` + } + devicePath := fmt.Sprintf(DevicePath, site) + if err := u.GetData(devicePath, &response); err != nil { + return nil, err + } + data = append(data, response.Data...) } - req, err := u.UniReq(DevicePath, "") + return u.parseDevices(data), nil +} + +// GetSites returns a list of configured sites on the Unifi controller. +func (u *Unifi) GetSites() ([]string, error) { + var response struct { + Data []struct { + // This is the only field we need. There are others. + Name string `json:"name"` + } `json:"data"` + } + if err := u.GetData(SiteList, &response); err != nil { + return nil, err + } + var output []string + for i := range response.Data { + output = append(output, response.Data[i].Name) + } + u.dLogf("Found %d sites: %v", len(output), strings.Join(output, ",")) + return output, nil +} + +// GetData makes a unifi request and unmarshal the response into a provided pointer. +func (u *Unifi) GetData(methodPath string, v interface{}) error { + req, err := u.UniReq(methodPath, "") if err != nil { - return nil, errors.Wrap(err, "c.UniReq(DevicePath)") + return errors.Wrapf(err, "c.UniReq(%v)", methodPath) } resp, err := u.Do(req) if err != nil { - return nil, errors.Wrap(err, "c.Do(req)") + return errors.Wrapf(err, "c.Do(%v)", methodPath) } defer func() { _ = resp.Body.Close() }() if body, err := ioutil.ReadAll(resp.Body); err != nil { - return nil, errors.Wrap(err, "ioutil.ReadAll(resp.Body)") - } else if err = json.Unmarshal(body, &response); err != nil { - return nil, errors.Wrap(err, "json.Unmarshal([]json.RawMessage)") + return errors.Wrapf(err, "ioutil.ReadAll(%v)", methodPath) + } else if err = json.Unmarshal(body, v); err != nil { + return errors.Wrapf(err, "json.Unmarshal(%v)", methodPath) } - return u.parseDevices(response.Data), nil + return nil } // parseDevices parses the raw JSON from the Unifi Controller into device structures. From f5f4cb0720ed5d9dcf7326e7af12afd66490b4d2 Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Mon, 15 Apr 2019 02:08:58 -0700 Subject: [PATCH 2/3] Add better logs. --- core/unifi/unifi.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/core/unifi/unifi.go b/core/unifi/unifi.go index 8ab4e910..8e42e301 100644 --- a/core/unifi/unifi.go +++ b/core/unifi/unifi.go @@ -16,6 +16,8 @@ func (u *Unifi) GetClients(sites []string) (*Clients, error) { var response struct { Data []UCL `json:"data"` } + u.dLogf("Polling Site '%s' Clients", site) + u.dLogf("Unmarshalling Device Type: ucl") clientPath := fmt.Sprintf(ClientPath, site) if err := u.GetData(clientPath, &response); err != nil { return nil, err @@ -29,6 +31,7 @@ func (u *Unifi) GetClients(sites []string) (*Clients, error) { func (u *Unifi) GetDevices(sites []string) (*Devices, error) { var data []json.RawMessage for _, site := range sites { + u.dLogf("Polling Site '%s' Devices", site) var response struct { Data []json.RawMessage `json:"data"` } @@ -56,7 +59,7 @@ func (u *Unifi) GetSites() ([]string, error) { for i := range response.Data { output = append(output, response.Data[i].Name) } - u.dLogf("Found %d sites: %v", len(output), strings.Join(output, ",")) + u.dLogf("Found %d sites: %s", len(output), strings.Join(output, ",")) return output, nil } @@ -64,19 +67,19 @@ func (u *Unifi) GetSites() ([]string, error) { func (u *Unifi) GetData(methodPath string, v interface{}) error { req, err := u.UniReq(methodPath, "") if err != nil { - return errors.Wrapf(err, "c.UniReq(%v)", methodPath) + return errors.Wrapf(err, "c.UniReq(%s)", methodPath) } resp, err := u.Do(req) if err != nil { - return errors.Wrapf(err, "c.Do(%v)", methodPath) + return errors.Wrapf(err, "c.Do(%s)", methodPath) } defer func() { _ = resp.Body.Close() }() if body, err := ioutil.ReadAll(resp.Body); err != nil { - return errors.Wrapf(err, "ioutil.ReadAll(%v)", methodPath) + return errors.Wrapf(err, "ioutil.ReadAll(%s)", methodPath) } else if err = json.Unmarshal(body, v); err != nil { - return errors.Wrapf(err, "json.Unmarshal(%v)", methodPath) + return errors.Wrapf(err, "json.Unmarshal(%s)", methodPath) } return nil } From 8948b01d418159b6560973bd4c093f92b5f3ee0f Mon Sep 17 00:00:00 2001 From: David Newhall II Date: Mon, 15 Apr 2019 02:26:43 -0700 Subject: [PATCH 3/3] Use new Site type. --- core/unifi/unifi.go | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/core/unifi/unifi.go b/core/unifi/unifi.go index 8e42e301..1cc11bb6 100644 --- a/core/unifi/unifi.go +++ b/core/unifi/unifi.go @@ -10,15 +10,15 @@ import ( ) // GetClients returns a response full of clients' data from the Unifi Controller. -func (u *Unifi) GetClients(sites []string) (*Clients, error) { +func (u *Unifi) GetClients(sites []Site) (*Clients, error) { var data []UCL for _, site := range sites { var response struct { Data []UCL `json:"data"` } - u.dLogf("Polling Site '%s' Clients", site) + u.dLogf("Polling Site '%s' (%s) Clients", site.Name, site.Desc) u.dLogf("Unmarshalling Device Type: ucl") - clientPath := fmt.Sprintf(ClientPath, site) + clientPath := fmt.Sprintf(ClientPath, site.Name) if err := u.GetData(clientPath, &response); err != nil { return nil, err } @@ -28,14 +28,14 @@ func (u *Unifi) GetClients(sites []string) (*Clients, error) { } // GetDevices returns a response full of devices' data from the Unifi Controller. -func (u *Unifi) GetDevices(sites []string) (*Devices, error) { +func (u *Unifi) GetDevices(sites []Site) (*Devices, error) { var data []json.RawMessage for _, site := range sites { - u.dLogf("Polling Site '%s' Devices", site) + u.dLogf("Polling Site '%s' (%s) Devices", site.Name, site.Desc) var response struct { Data []json.RawMessage `json:"data"` } - devicePath := fmt.Sprintf(DevicePath, site) + devicePath := fmt.Sprintf(DevicePath, site.Name) if err := u.GetData(devicePath, &response); err != nil { return nil, err } @@ -44,23 +44,26 @@ func (u *Unifi) GetDevices(sites []string) (*Devices, error) { return u.parseDevices(data), nil } +// Site represents a site's data. There are more pieces to this. +type Site struct { + Name string `json:"name"` + Desc string `json:"desc"` +} + // GetSites returns a list of configured sites on the Unifi controller. -func (u *Unifi) GetSites() ([]string, error) { +func (u *Unifi) GetSites() ([]Site, error) { var response struct { - Data []struct { - // This is the only field we need. There are others. - Name string `json:"name"` - } `json:"data"` + Data []Site `json:"data"` } if err := u.GetData(SiteList, &response); err != nil { return nil, err } - var output []string + var sites []string for i := range response.Data { - output = append(output, response.Data[i].Name) + sites = append(sites, response.Data[i].Name) } - u.dLogf("Found %d sites: %s", len(output), strings.Join(output, ",")) - return output, nil + u.dLogf("Found %d sites: %s", len(sites), strings.Join(sites, ",")) + return response.Data, nil } // GetData makes a unifi request and unmarshal the response into a provided pointer.