- Add Add and Del methods to Client for dynamic host management. - Add RWMutex to Client to protect the devices map. - Add Transport to Config to allow mocking HTTP transport in tests. - Add getDeviceByHost helper to centralize device lookup locking. - Refactor GetAll* methods to snapshot host keys before iteration to avoid concurrent map read/write panic. - Add tests for thread safety and Add/Del functionality.
351 lines
7.0 KiB
Go
351 lines
7.0 KiB
Go
package edgeos
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"sync"
|
|
)
|
|
|
|
// GetInterfaces retrieves the interfaces for a specific device.
|
|
func (c *Client) GetInterfaces(ctx context.Context, host string) ([]Interface, error) {
|
|
d, err := c.getDeviceByHost(host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var out []Interface
|
|
if err := d.do(ctx, "GET", "/api/v1.0/interfaces", nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// GetAllInterfaces retrieves interfaces for all devices.
|
|
func (c *Client) GetAllInterfaces(ctx context.Context) (map[string][]Interface, error) {
|
|
results := make(map[string][]Interface)
|
|
var (
|
|
mu sync.Mutex
|
|
wg sync.WaitGroup
|
|
errs error
|
|
)
|
|
|
|
c.mu.RLock()
|
|
hosts := make([]string, 0, len(c.devices))
|
|
for h := range c.devices {
|
|
hosts = append(hosts, h)
|
|
}
|
|
c.mu.RUnlock()
|
|
|
|
for _, host := range hosts {
|
|
wg.Go(func() {
|
|
res, err := c.GetInterfaces(ctx, host)
|
|
if err != nil {
|
|
mu.Lock()
|
|
errs = errors.Join(errs, err)
|
|
mu.Unlock()
|
|
return
|
|
}
|
|
mu.Lock()
|
|
results[host] = res
|
|
mu.Unlock()
|
|
})
|
|
}
|
|
wg.Wait()
|
|
return results, errs
|
|
}
|
|
|
|
// GetDevice retrieves the device info for a specific device.
|
|
func (c *Client) GetDevice(ctx context.Context, host string) (*Device, error) {
|
|
d, err := c.getDeviceByHost(host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var out Device
|
|
if err := d.do(ctx, "GET", "/api/v1.0/device", nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &out, nil
|
|
}
|
|
|
|
// GetAllDevices retrieves device info for all devices.
|
|
func (c *Client) GetAllDevices(ctx context.Context) (map[string]*Device, error) {
|
|
results := make(map[string]*Device)
|
|
var (
|
|
mu sync.Mutex
|
|
wg sync.WaitGroup
|
|
errs error
|
|
)
|
|
|
|
c.mu.RLock()
|
|
hosts := make([]string, 0, len(c.devices))
|
|
for h := range c.devices {
|
|
hosts = append(hosts, h)
|
|
}
|
|
c.mu.RUnlock()
|
|
|
|
for _, host := range hosts {
|
|
wg.Go(func() {
|
|
res, err := c.GetDevice(ctx, host)
|
|
if err != nil {
|
|
mu.Lock()
|
|
errs = errors.Join(errs, err)
|
|
mu.Unlock()
|
|
return
|
|
}
|
|
mu.Lock()
|
|
results[host] = res
|
|
mu.Unlock()
|
|
})
|
|
}
|
|
wg.Wait()
|
|
return results, errs
|
|
}
|
|
|
|
// GetSystem retrieves the system info for a specific device.
|
|
func (c *Client) GetSystem(ctx context.Context, host string) (*System, error) {
|
|
d, err := c.getDeviceByHost(host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var out System
|
|
if err := d.do(ctx, "GET", "/api/v1.0/system", nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &out, nil
|
|
}
|
|
|
|
// GetAllSystems retrieves system info for all devices.
|
|
func (c *Client) GetAllSystems(ctx context.Context) (map[string]*System, error) {
|
|
results := make(map[string]*System)
|
|
var (
|
|
mu sync.Mutex
|
|
wg sync.WaitGroup
|
|
errs error
|
|
)
|
|
|
|
c.mu.RLock()
|
|
hosts := make([]string, 0, len(c.devices))
|
|
for h := range c.devices {
|
|
hosts = append(hosts, h)
|
|
}
|
|
c.mu.RUnlock()
|
|
|
|
for _, host := range hosts {
|
|
wg.Go(func() {
|
|
res, err := c.GetSystem(ctx, host)
|
|
if err != nil {
|
|
mu.Lock()
|
|
errs = errors.Join(errs, err)
|
|
mu.Unlock()
|
|
return
|
|
}
|
|
mu.Lock()
|
|
results[host] = res
|
|
mu.Unlock()
|
|
})
|
|
}
|
|
wg.Wait()
|
|
return results, errs
|
|
}
|
|
|
|
// GetVLANs retrieves the VLANs for a specific device.
|
|
func (c *Client) GetVLANs(ctx context.Context, host string) (*VLANs, error) {
|
|
d, err := c.getDeviceByHost(host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var out VLANs
|
|
if err := d.do(ctx, "GET", "/api/v1.0/vlans", nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &out, nil
|
|
}
|
|
|
|
// GetAllVLANs retrieves VLANs for all devices.
|
|
func (c *Client) GetAllVLANs(ctx context.Context) (map[string]*VLANs, error) {
|
|
results := make(map[string]*VLANs)
|
|
var (
|
|
mu sync.Mutex
|
|
wg sync.WaitGroup
|
|
errs error
|
|
)
|
|
|
|
c.mu.RLock()
|
|
hosts := make([]string, 0, len(c.devices))
|
|
for h := range c.devices {
|
|
hosts = append(hosts, h)
|
|
}
|
|
c.mu.RUnlock()
|
|
|
|
for _, host := range hosts {
|
|
wg.Go(func() {
|
|
res, err := c.GetVLANs(ctx, host)
|
|
if err != nil {
|
|
mu.Lock()
|
|
errs = errors.Join(errs, err)
|
|
mu.Unlock()
|
|
return
|
|
}
|
|
mu.Lock()
|
|
results[host] = res
|
|
mu.Unlock()
|
|
})
|
|
}
|
|
wg.Wait()
|
|
return results, errs
|
|
}
|
|
|
|
// GetServices retrieves the services for a specific device.
|
|
func (c *Client) GetServices(ctx context.Context, host string) (*Services, error) {
|
|
d, err := c.getDeviceByHost(host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var out Services
|
|
if err := d.do(ctx, "GET", "/api/v1.0/services", nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &out, nil
|
|
}
|
|
|
|
// GetAllServices retrieves services for all devices.
|
|
func (c *Client) GetAllServices(ctx context.Context) (map[string]*Services, error) {
|
|
results := make(map[string]*Services)
|
|
var (
|
|
mu sync.Mutex
|
|
wg sync.WaitGroup
|
|
errs error
|
|
)
|
|
|
|
c.mu.RLock()
|
|
hosts := make([]string, 0, len(c.devices))
|
|
for h := range c.devices {
|
|
hosts = append(hosts, h)
|
|
}
|
|
c.mu.RUnlock()
|
|
|
|
for _, host := range hosts {
|
|
wg.Go(func() {
|
|
res, err := c.GetServices(ctx, host)
|
|
if err != nil {
|
|
mu.Lock()
|
|
errs = errors.Join(errs, err)
|
|
mu.Unlock()
|
|
return
|
|
}
|
|
mu.Lock()
|
|
results[host] = res
|
|
mu.Unlock()
|
|
})
|
|
}
|
|
wg.Wait()
|
|
return results, errs
|
|
}
|
|
|
|
// GetStatistics retrieves the statistics for a specific device.
|
|
func (c *Client) GetStatistics(ctx context.Context, host string) ([]Statistics, error) {
|
|
d, err := c.getDeviceByHost(host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var out []Statistics
|
|
if err := d.do(ctx, "GET", "/api/v1.0/statistics", nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// GetAllStatistics retrieves statistics for all devices.
|
|
func (c *Client) GetAllStatistics(ctx context.Context) (map[string][]Statistics, error) {
|
|
results := make(map[string][]Statistics)
|
|
var (
|
|
mu sync.Mutex
|
|
wg sync.WaitGroup
|
|
errs error
|
|
)
|
|
|
|
c.mu.RLock()
|
|
hosts := make([]string, 0, len(c.devices))
|
|
for h := range c.devices {
|
|
hosts = append(hosts, h)
|
|
}
|
|
c.mu.RUnlock()
|
|
|
|
for _, host := range hosts {
|
|
wg.Go(func() {
|
|
res, err := c.GetStatistics(ctx, host)
|
|
if err != nil {
|
|
mu.Lock()
|
|
errs = errors.Join(errs, err)
|
|
mu.Unlock()
|
|
return
|
|
}
|
|
mu.Lock()
|
|
results[host] = res
|
|
mu.Unlock()
|
|
})
|
|
}
|
|
wg.Wait()
|
|
return results, errs
|
|
}
|
|
|
|
// GetNeighbors retrieves the neighbors for a specific device.
|
|
func (c *Client) GetNeighbors(ctx context.Context, host string) ([]Neighbor, error) {
|
|
d, err := c.getDeviceByHost(host)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var out []Neighbor
|
|
if err := d.do(ctx, "GET", "/api/v1.0/tools/discovery/neighbors", nil, &out); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
// GetAllNeighbors retrieves neighbors for all devices.
|
|
func (c *Client) GetAllNeighbors(ctx context.Context) (map[string][]Neighbor, error) {
|
|
results := make(map[string][]Neighbor)
|
|
var (
|
|
mu sync.Mutex
|
|
wg sync.WaitGroup
|
|
errs error
|
|
)
|
|
|
|
c.mu.RLock()
|
|
hosts := make([]string, 0, len(c.devices))
|
|
for h := range c.devices {
|
|
hosts = append(hosts, h)
|
|
}
|
|
c.mu.RUnlock()
|
|
|
|
for _, host := range hosts {
|
|
wg.Go(func() {
|
|
res, err := c.GetNeighbors(ctx, host)
|
|
if err != nil {
|
|
mu.Lock()
|
|
errs = errors.Join(errs, err)
|
|
mu.Unlock()
|
|
return
|
|
}
|
|
mu.Lock()
|
|
results[host] = res
|
|
mu.Unlock()
|
|
})
|
|
}
|
|
wg.Wait()
|
|
return results, errs
|
|
}
|