Skip to content

Commit

Permalink
update: walk the agent to get all supported oids prior to creating de…
Browse files Browse the repository at this point in the history
…vices
  • Loading branch information
edaniszewski committed May 6, 2020
1 parent 2d31ce9 commit dc1aa1b
Show file tree
Hide file tree
Showing 5 changed files with 187 additions and 75 deletions.
43 changes: 40 additions & 3 deletions pkg/core/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,46 @@ func (c *Client) GetOid(oid string) (*gosnmp.SnmpPDU, error) {
return &data, nil
}

// GetSupportedDevices gets all the OIDs for devices found on the target. This may not
// always be the full set of devices that a MIB defines.
//
// This returns a map of OIDs to empty struct. This map should be used during device creation
// to filter the MIB to only register those devices that a target supports. It is returned
// as a map to make OID lookups easier than iterating over a slice. Presence in the map means
// the device is supported, absence means it is not.
func (c *Client) GetSupportedDevices(rootOid string) (map[string]struct{}, error) {
log.WithFields(log.Fields{
"rootOid": rootOid,
}).Debug("[snmp] getting supported devices for root OIO")

results, err := c.BulkWalkAll(rootOid)
if err != nil {
log.WithError(err).Error("[snmp] failed to bulk walk all")
return nil, err
}

log.WithFields(log.Fields{
"size": len(results),
}).Debug("[snmp] got bulk walk results")

oids := make(map[string]struct{})
for _, r := range results {
oid := r.Name
if strings.HasPrefix(oid, ".") {
oid = oid[1:]
}

log.WithFields(log.Fields{
"name": oid,
"value": r.Value,
"type": r.Type,
}).Debug("[snmp] collecting walk result")
oids[oid] = struct{}{}
}

return oids, nil
}

// Close the client connection.
func (c *Client) Close() {
if c.Conn != nil {
Expand Down Expand Up @@ -176,8 +216,5 @@ func NewClient(cfg *SnmpTargetConfiguration) (*Client, error) {
},
}

log.WithFields(log.Fields{
"cfg": *c.GoSNMP,
}).Debug("[snmp] created new client")
return c, nil
}
17 changes: 14 additions & 3 deletions pkg/mibs/mib.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,25 +14,27 @@ import (
// MIBs are registered with the SNMP base plugin.
type MIB struct {
Name string
RootOid string
Devices []*SnmpDevice
}

// NewMIB creates a new MIB with the specified devices.
func NewMIB(name string, devices ...*SnmpDevice) *MIB {
func NewMIB(name string, rootOid string, devices ...*SnmpDevice) *MIB {
return &MIB{
Name: name,
RootOid: rootOid,
Devices: devices,
}
}

// String returns a human-readable string, useful for identifying the
// MIB in logs.
func (mib *MIB) String() string {
return fmt.Sprintf("[MIB %s]", mib.Name)
return fmt.Sprintf("[MIB %s (%s)]", mib.Name, mib.RootOid)
}

// LoadDevices loads Synse devices from the SNMP devices defined in the MIB.
func (mib *MIB) LoadDevices(cfg *core.SnmpTargetConfiguration) ([]*sdk.Device, error) {
func (mib *MIB) LoadDevices(cfg *core.SnmpTargetConfiguration, supported map[string]struct{}) ([]*sdk.Device, error) {
if cfg == nil {
return nil, errors.New("cannot load devices with nil SNMP target config")
}
Expand All @@ -44,6 +46,15 @@ func (mib *MIB) LoadDevices(cfg *core.SnmpTargetConfiguration) ([]*sdk.Device, e

var devices []*sdk.Device
for _, d := range mib.Devices {

if _, exists := supported[d.OID]; !exists {
log.WithFields(log.Fields{
"oid": d.OID,
"agent": cfg.Agent,
}).Debug("[snmp] mib device not supported by agent; will not load")
continue
}

device, err := d.ToDevice()
if err != nil {
return nil, err
Expand Down
77 changes: 62 additions & 15 deletions pkg/mibs/mib_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import (
)

func TestNewMIB(t *testing.T) {
mib := NewMIB("name", &SnmpDevice{}, &SnmpDevice{}, &SnmpDevice{})
mib := NewMIB("name", "1.2.3", &SnmpDevice{}, &SnmpDevice{}, &SnmpDevice{})

assert.Equal(t, "name", mib.Name)
assert.Len(t, mib.Devices, 3)
}

func TestMIB_String(t *testing.T) {
mib := NewMIB("name", &SnmpDevice{})
assert.Equal(t, "[MIB name]", mib.String())
mib := NewMIB("name", "1.2.3", &SnmpDevice{})
assert.Equal(t, "[MIB name (1.2.3)]", mib.String())
}

func TestMIB_LoadDevices(t *testing.T) {
Expand Down Expand Up @@ -46,7 +46,13 @@ func TestMIB_LoadDevices(t *testing.T) {
Version: "v3",
Agent: "localhost",
}
devices, err := m.LoadDevices(cfg)
devices, err := m.LoadDevices(
cfg,
map[string]struct{}{
"1.2.3.4": {},
"5.6.7.8": {},
},
)

assert.NoError(t, err)
assert.NotNil(t, devices)
Expand Down Expand Up @@ -101,7 +107,12 @@ func TestMIB_LoadDevices_nilConfig(t *testing.T) {
},
}

devices, err := m.LoadDevices(nil)
devices, err := m.LoadDevices(
nil,
map[string]struct{}{
"1.2.3.4": {},
},
)
assert.Error(t, err)
assert.Nil(t, devices)
}
Expand All @@ -112,11 +123,14 @@ func TestMIB_LoadDevices_noDevices(t *testing.T) {
Devices: []*SnmpDevice{},
}

devices, err := m.LoadDevices(&core.SnmpTargetConfiguration{
MIB: "test-mib",
Version: "v3",
Agent: "localhost",
})
devices, err := m.LoadDevices(
&core.SnmpTargetConfiguration{
MIB: "test-mib",
Version: "v3",
Agent: "localhost",
},
map[string]struct{}{},
)
assert.NoError(t, err)
assert.Empty(t, devices)
}
Expand All @@ -135,11 +149,44 @@ func TestMIB_LoadDevices_deviceError(t *testing.T) {
},
}

devices, err := m.LoadDevices(&core.SnmpTargetConfiguration{
MIB: "test-mib",
Version: "v3",
Agent: "localhost",
})
devices, err := m.LoadDevices(
&core.SnmpTargetConfiguration{
MIB: "test-mib",
Version: "v3",
Agent: "localhost",
},
map[string]struct{}{
"1.2.3.4": {},
},
)
assert.Error(t, err)
assert.Nil(t, devices)
}

func TestMIB_LoadDevices_deviceNotSupported(t *testing.T) {
m := MIB{
Name: "test-mib",
Devices: []*SnmpDevice{
{
OID: "1.2.3.4",
Info: "test device 1",
Type: "temperature",
Handler: "temperature",
Output: "temperature",
},
},
}

devices, err := m.LoadDevices(
&core.SnmpTargetConfiguration{
MIB: "test-mib",
Version: "v3",
Agent: "localhost",
},
map[string]struct{}{
"1.2.3.5": {},
},
)
assert.NoError(t, err)
assert.Empty(t, devices)
}
18 changes: 17 additions & 1 deletion pkg/plugin/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,23 @@ func SnmpDeviceRegistrar(data map[string]interface{}) ([]*sdk.Device, error) {
}).Error("[snmp] specified MIB is not registered with the plugin")
return nil, fmt.Errorf("configured MIB not found")
}
d, err := mib.LoadDevices(config)

// Create an SNMP client for the configured target.
c, err := core.NewClient(config)
if err != nil {
return nil, err
}
if err := c.Connect(); err != nil {
return nil, err
}
defer c.Close()

supportedDevices, err := c.GetSupportedDevices(mib.RootOid)
if err != nil {
return nil, err
}

d, err := mib.LoadDevices(config, supportedDevices)
if err != nil {
log.WithError(err).Error("[snmp] failed to load devices from MIB")
return nil, err
Expand Down
107 changes: 54 additions & 53 deletions pkg/plugin/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/vapor-ware/synse-snmp-base/pkg/mibs"
)

func TestSnmpDeviceIdentifier(t *testing.T) {
Expand Down Expand Up @@ -51,32 +50,33 @@ func TestSnmpDeviceIdentifier_NoAgent(t *testing.T) {
})
}

func TestSnmpDeviceRegistrar(t *testing.T) {
defer mibs.Clear()

err := mibs.Register(&mibs.MIB{
Name: "test-mib",
Devices: []*mibs.SnmpDevice{
{
OID: "1.2.3.4",
Info: "test device",
Handler: "read-only",
Type: "temperature",
Output: "temperature",
},
},
})
assert.NoError(t, err)

devices, err := SnmpDeviceRegistrar(map[string]interface{}{
"mib": "test-mib",
"version": "v3",
"agent": "localhost",
"timeout": "1s",
})
assert.NoError(t, err)
assert.Len(t, devices, 1)
}
// TODO (etd): re-implement - need to mock out the snmp client
//func TestSnmpDeviceRegistrar(t *testing.T) {
// defer mibs.Clear()
//
// err := mibs.Register(&mibs.MIB{
// Name: "test-mib",
// Devices: []*mibs.SnmpDevice{
// {
// OID: "1.2.3.4",
// Info: "test device",
// Handler: "read-only",
// Type: "temperature",
// Output: "temperature",
// },
// },
// })
// assert.NoError(t, err)
//
// devices, err := SnmpDeviceRegistrar(map[string]interface{}{
// "mib": "test-mib",
// "version": "v3",
// "agent": "localhost",
// "timeout": "1s",
// })
// assert.NoError(t, err)
// assert.Len(t, devices, 1)
//}

func TestSnmpDeviceRegistrar_FailedConfigLoad(t *testing.T) {
devices, err := SnmpDeviceRegistrar(map[string]interface{}{
Expand Down Expand Up @@ -114,29 +114,30 @@ func TestSnmpDeviceRegistrar_FailedFindMIB(t *testing.T) {
assert.Nil(t, devices)
}

func TestSnmpDeviceRegistrar_FailedDeviceLoad(t *testing.T) {
defer mibs.Clear()

err := mibs.Register(&mibs.MIB{
Name: "test-mib",
Devices: []*mibs.SnmpDevice{
{
OID: "1.2.3.4",
Info: "test device",
Handler: "read-only",
Type: "temperature",
Output: "no-such-output", // load will fail since output doesn't exist
},
},
})
assert.NoError(t, err)

devices, err := SnmpDeviceRegistrar(map[string]interface{}{
"mib": "test-mib",
"version": "v3",
"agent": "localhost",
"timeout": "1s",
})
assert.Error(t, err)
assert.Nil(t, devices)
}
// TODO (etd): re-implement - need to mock out the snmp client
//func TestSnmpDeviceRegistrar_FailedDeviceLoad(t *testing.T) {
// defer mibs.Clear()
//
// err := mibs.Register(&mibs.MIB{
// Name: "test-mib",
// Devices: []*mibs.SnmpDevice{
// {
// OID: "1.2.3.4",
// Info: "test device",
// Handler: "read-only",
// Type: "temperature",
// Output: "no-such-output", // load will fail since output doesn't exist
// },
// },
// })
// assert.NoError(t, err)
//
// devices, err := SnmpDeviceRegistrar(map[string]interface{}{
// "mib": "test-mib",
// "version": "v3",
// "agent": "localhost",
// "timeout": "1s",
// })
// assert.Error(t, err)
// assert.Nil(t, devices)
//}

0 comments on commit dc1aa1b

Please sign in to comment.