client_test.go 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966
  1. // Copyright The OpenTelemetry Authors
  2. // SPDX-License-Identifier: Apache-2.0
  3. package snmpreceiver // import "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/snmpreceiver"
  4. import (
  5. "errors"
  6. "fmt"
  7. "math"
  8. "strconv"
  9. "strings"
  10. "testing"
  11. "github.com/gosnmp/gosnmp"
  12. "github.com/stretchr/testify/mock"
  13. "github.com/stretchr/testify/require"
  14. "go.opentelemetry.io/collector/component"
  15. "go.opentelemetry.io/collector/component/componenttest"
  16. "go.opentelemetry.io/collector/receiver/scrapererror"
  17. "go.uber.org/zap"
  18. "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/snmpreceiver/internal/mocks"
  19. )
  20. func TestNewClient(t *testing.T) {
  21. testCase := []struct {
  22. desc string
  23. cfg *Config
  24. host component.Host
  25. settings component.TelemetrySettings
  26. logger *zap.Logger
  27. expectError error
  28. }{
  29. {
  30. desc: "Valid v2c configuration",
  31. cfg: &Config{
  32. Version: "v2c",
  33. Endpoint: "udp://localhost:161",
  34. Community: "public",
  35. },
  36. host: componenttest.NewNopHost(),
  37. settings: componenttest.NewNopTelemetrySettings(),
  38. logger: zap.NewNop(),
  39. expectError: nil,
  40. },
  41. {
  42. desc: "Valid v3 configuration",
  43. cfg: &Config{
  44. Version: "v3",
  45. Endpoint: "tcp://localhost:161",
  46. User: "user",
  47. SecurityLevel: "auth_priv",
  48. AuthType: "MD5",
  49. AuthPassword: "authpass",
  50. PrivacyType: "DES",
  51. PrivacyPassword: "privacypass",
  52. },
  53. host: componenttest.NewNopHost(),
  54. settings: componenttest.NewNopTelemetrySettings(),
  55. logger: zap.NewNop(),
  56. expectError: nil,
  57. },
  58. }
  59. for _, tc := range testCase {
  60. t.Run(tc.desc, func(t *testing.T) {
  61. ac, err := newClient(tc.cfg, tc.logger)
  62. if tc.expectError != nil {
  63. require.Nil(t, ac)
  64. require.Contains(t, err.Error(), tc.expectError.Error())
  65. } else {
  66. require.NoError(t, err)
  67. actualClient, ok := ac.(*snmpClient)
  68. require.True(t, ok)
  69. compareConfigToClient(t, actualClient, tc.cfg)
  70. }
  71. })
  72. }
  73. }
  74. func compareConfigToClient(t *testing.T, client *snmpClient, cfg *Config) {
  75. t.Helper()
  76. require.True(t, strings.Contains(cfg.Endpoint, client.client.GetTarget()))
  77. require.True(t, strings.Contains(cfg.Endpoint, strconv.FormatInt(int64(client.client.GetPort()), 10)))
  78. require.True(t, strings.Contains(cfg.Endpoint, client.client.GetTransport()))
  79. switch cfg.Version {
  80. case "v1":
  81. require.Equal(t, gosnmp.Version1, client.client.GetVersion())
  82. require.Equal(t, cfg.Community, client.client.GetCommunity())
  83. case "v2c":
  84. require.Equal(t, gosnmp.Version2c, client.client.GetVersion())
  85. require.Equal(t, cfg.Community, client.client.GetCommunity())
  86. case "v3":
  87. require.Equal(t, gosnmp.Version3, client.client.GetVersion())
  88. securityParams := client.client.GetSecurityParameters().(*gosnmp.UsmSecurityParameters)
  89. require.Equal(t, cfg.User, securityParams.UserName)
  90. switch cfg.SecurityLevel {
  91. case "no_auth_no_priv":
  92. require.Equal(t, gosnmp.NoAuthNoPriv, client.client.GetMsgFlags())
  93. case "auth_no_priv":
  94. require.Equal(t, gosnmp.AuthNoPriv, client.client.GetMsgFlags())
  95. require.Equal(t, cfg.AuthType, securityParams.AuthenticationProtocol)
  96. require.Equal(t, string(cfg.AuthPassword), securityParams.AuthenticationPassphrase)
  97. case "auth_priv":
  98. require.Equal(t, gosnmp.AuthPriv, client.client.GetMsgFlags())
  99. require.Equal(t, cfg.AuthType, securityParams.AuthenticationProtocol.String())
  100. require.Equal(t, string(cfg.AuthPassword), securityParams.AuthenticationPassphrase)
  101. require.Equal(t, cfg.PrivacyType, securityParams.PrivacyProtocol.String())
  102. require.Equal(t, string(cfg.PrivacyPassword), securityParams.PrivacyPassphrase)
  103. }
  104. }
  105. }
  106. func TestConnect(t *testing.T) {
  107. testCases := []struct {
  108. desc string
  109. testFunc func(*testing.T)
  110. }{
  111. {
  112. desc: "Good Connect",
  113. testFunc: func(t *testing.T) {
  114. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  115. mockGoSNMP.On("Connect", mock.Anything).Return(nil)
  116. client := &snmpClient{
  117. logger: &zap.Logger{},
  118. client: mockGoSNMP,
  119. }
  120. err := client.Connect()
  121. require.NoError(t, err)
  122. },
  123. },
  124. {
  125. desc: "Bad Connect",
  126. testFunc: func(t *testing.T) {
  127. connectErr := errors.New("problem connecting")
  128. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  129. mockGoSNMP.On("Connect", mock.Anything).Return(connectErr)
  130. client := &snmpClient{
  131. logger: &zap.Logger{},
  132. client: mockGoSNMP,
  133. }
  134. err := client.Connect()
  135. require.ErrorIs(t, err, connectErr)
  136. },
  137. },
  138. }
  139. for _, tc := range testCases {
  140. t.Run(tc.desc, tc.testFunc)
  141. }
  142. }
  143. func TestClose(t *testing.T) {
  144. testCases := []struct {
  145. desc string
  146. testFunc func(*testing.T)
  147. }{
  148. {
  149. desc: "Good Close",
  150. testFunc: func(t *testing.T) {
  151. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  152. mockGoSNMP.On("Close", mock.Anything).Return(nil)
  153. client := &snmpClient{
  154. logger: &zap.Logger{},
  155. client: mockGoSNMP,
  156. }
  157. err := client.Close()
  158. require.NoError(t, err)
  159. },
  160. },
  161. {
  162. desc: "Bad Close",
  163. testFunc: func(t *testing.T) {
  164. closeErr := errors.New("problem closing")
  165. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  166. mockGoSNMP.On("Close", mock.Anything).Return(closeErr)
  167. client := &snmpClient{
  168. logger: &zap.Logger{},
  169. client: mockGoSNMP,
  170. }
  171. err := client.Close()
  172. require.ErrorIs(t, err, closeErr)
  173. },
  174. },
  175. }
  176. for _, tc := range testCases {
  177. t.Run(tc.desc, tc.testFunc)
  178. }
  179. }
  180. func TestGetScalarData(t *testing.T) {
  181. testCases := []struct {
  182. desc string
  183. testFunc func(*testing.T)
  184. }{
  185. {
  186. desc: "No OIDs does nothing",
  187. testFunc: func(t *testing.T) {
  188. expectedSNMPData := []SNMPData{}
  189. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  190. client := &snmpClient{
  191. logger: zap.NewNop(),
  192. client: mockGoSNMP,
  193. }
  194. var scraperErrors scrapererror.ScrapeErrors
  195. returnedSNMPData := client.GetScalarData([]string{}, &scraperErrors)
  196. require.NoError(t, scraperErrors.Combine())
  197. require.Equal(t, expectedSNMPData, returnedSNMPData)
  198. },
  199. },
  200. {
  201. desc: "GoSNMP Client failures adds error",
  202. testFunc: func(t *testing.T) {
  203. expectedSNMPData := []SNMPData{}
  204. getError := errors.New("Bad GET")
  205. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  206. mockGoSNMP.On("Get", []string{"1"}).Return(nil, getError)
  207. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  208. client := &snmpClient{
  209. logger: zap.NewNop(),
  210. client: mockGoSNMP,
  211. }
  212. var scraperErrors scrapererror.ScrapeErrors
  213. oidSlice := []string{"1"}
  214. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  215. expectedErr := fmt.Errorf("problem with getting scalar data: problem with SNMP GET for OIDs '%v': %w", oidSlice, getError)
  216. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  217. require.Equal(t, expectedSNMPData, returnedSNMPData)
  218. },
  219. },
  220. {
  221. desc: "GoSNMP Client timeout failures tries to reset connection",
  222. testFunc: func(t *testing.T) {
  223. expectedSNMPData := []SNMPData{}
  224. getError := errors.New("request timeout (after 0 retries)")
  225. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  226. mockGoSNMP.On("Get", []string{"1"}).Return(nil, getError)
  227. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  228. mockGoSNMP.On("Close", mock.Anything).Return(nil)
  229. mockGoSNMP.On("Connect", mock.Anything).Return(nil)
  230. client := &snmpClient{
  231. logger: zap.NewNop(),
  232. client: mockGoSNMP,
  233. }
  234. var scraperErrors scrapererror.ScrapeErrors
  235. oidSlice := []string{"1"}
  236. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  237. expectedErr := fmt.Errorf("problem with getting scalar data: problem with SNMP GET for OIDs '%v': %w", oidSlice, getError)
  238. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  239. require.Equal(t, expectedSNMPData, returnedSNMPData)
  240. },
  241. },
  242. {
  243. desc: "GoSNMP Client reset connection fails on connect adds error",
  244. testFunc: func(t *testing.T) {
  245. expectedSNMPData := []SNMPData{}
  246. getError := errors.New("request timeout (after 0 retries)")
  247. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  248. mockGoSNMP.On("Get", []string{"1"}).Return(nil, getError)
  249. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  250. mockGoSNMP.On("Close", mock.Anything).Return(nil)
  251. connectErr := errors.New("can't connect")
  252. mockGoSNMP.On("Connect", mock.Anything).Return(connectErr)
  253. client := &snmpClient{
  254. logger: zap.NewNop(),
  255. client: mockGoSNMP,
  256. }
  257. var scraperErrors scrapererror.ScrapeErrors
  258. oidSlice := []string{"1"}
  259. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  260. expectedErr1 := fmt.Errorf("problem with getting scalar data: problem with SNMP GET for OIDs '%v': %w", oidSlice, getError)
  261. expectedErr2 := fmt.Errorf("problem with getting scalar data: problem connecting while trying to reset connection: %w", connectErr)
  262. expectedErr := fmt.Errorf(expectedErr1.Error() + "; " + expectedErr2.Error())
  263. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  264. require.Equal(t, expectedSNMPData, returnedSNMPData)
  265. },
  266. },
  267. {
  268. desc: "GoSNMP Client partial failures still return successes",
  269. testFunc: func(t *testing.T) {
  270. expectedSNMPData := []SNMPData{
  271. {
  272. oid: "2",
  273. value: int64(1),
  274. valueType: integerVal,
  275. },
  276. }
  277. pdu1 := gosnmp.SnmpPDU{
  278. Value: 1,
  279. Name: "2",
  280. Type: gosnmp.Integer,
  281. }
  282. getError := errors.New("Bad GET")
  283. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  284. mockGoSNMP.On("Get", []string{"1"}).
  285. Return(nil, getError).Once()
  286. mockGoSNMP.On("Get", []string{"2"}).
  287. Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu1}}, nil).Once()
  288. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(1)
  289. client := &snmpClient{
  290. logger: zap.NewNop(),
  291. client: mockGoSNMP,
  292. }
  293. var scraperErrors scrapererror.ScrapeErrors
  294. oidSlice := []string{"1", "2"}
  295. badOIDSlice := []string{"1"}
  296. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  297. expectedErr := fmt.Errorf("problem with getting scalar data: problem with SNMP GET for OIDs '%v': %w", badOIDSlice, getError)
  298. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  299. require.Equal(t, expectedSNMPData, returnedSNMPData)
  300. },
  301. },
  302. {
  303. desc: "GoSNMP Client returned nil value does not return data",
  304. testFunc: func(t *testing.T) {
  305. expectedSNMPData := []SNMPData{}
  306. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  307. pdu := gosnmp.SnmpPDU{
  308. Value: nil,
  309. Name: "1",
  310. Type: gosnmp.Integer,
  311. }
  312. mockGoSNMP.On("Get", []string{"1"}).
  313. Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu}}, nil)
  314. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  315. client := &snmpClient{
  316. logger: zap.NewNop(),
  317. client: mockGoSNMP,
  318. }
  319. var scraperErrors scrapererror.ScrapeErrors
  320. oidSlice := []string{"1"}
  321. badOID := "1"
  322. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  323. expectedErr := fmt.Errorf("problem with getting scalar data: data for OID '%s' not found", badOID)
  324. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  325. require.Equal(t, expectedSNMPData, returnedSNMPData)
  326. },
  327. },
  328. {
  329. desc: "GoSNMP Client returned unsupported type value does not return data",
  330. testFunc: func(t *testing.T) {
  331. expectedSNMPData := []SNMPData{}
  332. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  333. pdu := gosnmp.SnmpPDU{
  334. Value: true,
  335. Name: "1",
  336. Type: gosnmp.Boolean,
  337. }
  338. mockGoSNMP.On("Get", []string{"1"}).
  339. Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu}}, nil)
  340. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  341. client := &snmpClient{
  342. logger: zap.NewNop(),
  343. client: mockGoSNMP,
  344. }
  345. var scraperErrors scrapererror.ScrapeErrors
  346. oidSlice := []string{"1"}
  347. badOID := "1"
  348. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  349. expectedErr := fmt.Errorf("problem with getting scalar data: data for OID '%s' not a supported type", badOID)
  350. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  351. require.Equal(t, expectedSNMPData, returnedSNMPData)
  352. },
  353. },
  354. {
  355. desc: "Large amount of OIDs handled in chunks",
  356. testFunc: func(t *testing.T) {
  357. expectedSNMPData := []SNMPData{
  358. {
  359. oid: "1",
  360. value: int64(1),
  361. valueType: integerVal,
  362. },
  363. {
  364. oid: "2",
  365. value: int64(1),
  366. valueType: integerVal,
  367. },
  368. {
  369. oid: "3",
  370. value: int64(1),
  371. valueType: integerVal,
  372. },
  373. {
  374. oid: "4",
  375. value: int64(1),
  376. valueType: integerVal,
  377. },
  378. }
  379. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  380. pdu1 := gosnmp.SnmpPDU{
  381. Value: 1,
  382. Name: "1",
  383. Type: gosnmp.Integer,
  384. }
  385. pdu2 := gosnmp.SnmpPDU{
  386. Value: 1,
  387. Name: "2",
  388. Type: gosnmp.Integer,
  389. }
  390. pdu3 := gosnmp.SnmpPDU{
  391. Value: 1,
  392. Name: "3",
  393. Type: gosnmp.Integer,
  394. }
  395. pdu4 := gosnmp.SnmpPDU{
  396. Value: 1,
  397. Name: "4",
  398. Type: gosnmp.Integer,
  399. }
  400. mockGoSNMP.On("Get", []string{"1", "2"}).Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu1, pdu2}}, nil)
  401. mockGoSNMP.On("Get", []string{"3", "4"}).Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu3, pdu4}}, nil)
  402. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  403. client := &snmpClient{
  404. logger: zap.NewNop(),
  405. client: mockGoSNMP,
  406. }
  407. var scraperErrors scrapererror.ScrapeErrors
  408. oidSlice := []string{"1", "2", "3", "4"}
  409. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  410. require.NoError(t, scraperErrors.Combine())
  411. require.Equal(t, expectedSNMPData, returnedSNMPData)
  412. },
  413. },
  414. {
  415. desc: "GoSNMP Client float data type properly converted",
  416. testFunc: func(t *testing.T) {
  417. expectedSNMPData := []SNMPData{
  418. {
  419. oid: "1",
  420. value: 1.0,
  421. valueType: floatVal,
  422. },
  423. }
  424. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  425. pdu1 := gosnmp.SnmpPDU{
  426. Value: 1.0,
  427. Name: "1",
  428. Type: gosnmp.OpaqueDouble,
  429. }
  430. mockGoSNMP.On("Get", []string{"1"}).Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu1}}, nil)
  431. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  432. client := &snmpClient{
  433. logger: zap.NewNop(),
  434. client: mockGoSNMP,
  435. }
  436. var scraperErrors scrapererror.ScrapeErrors
  437. oidSlice := []string{"1"}
  438. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  439. require.NoError(t, scraperErrors.Combine())
  440. require.Equal(t, expectedSNMPData, returnedSNMPData)
  441. },
  442. },
  443. {
  444. desc: "GoSNMP Client float data type with bad value adds error",
  445. testFunc: func(t *testing.T) {
  446. expectedSNMPData := []SNMPData{}
  447. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  448. pdu1 := gosnmp.SnmpPDU{
  449. Value: true,
  450. Name: "1",
  451. Type: gosnmp.OpaqueDouble,
  452. }
  453. mockGoSNMP.On("Get", []string{"1"}).Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu1}}, nil)
  454. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  455. client := &snmpClient{
  456. logger: zap.NewNop(),
  457. client: mockGoSNMP,
  458. }
  459. var scraperErrors scrapererror.ScrapeErrors
  460. oidSlice := []string{"1"}
  461. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  462. expectedErr := fmt.Errorf("problem with getting scalar data: data for OID '1' not a supported type")
  463. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  464. require.Equal(t, expectedSNMPData, returnedSNMPData)
  465. },
  466. },
  467. {
  468. desc: "GoSNMP Client float data type with bad string value adds error",
  469. testFunc: func(t *testing.T) {
  470. expectedSNMPData := []SNMPData{}
  471. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  472. pdu1 := gosnmp.SnmpPDU{
  473. Value: "bad",
  474. Name: "1",
  475. Type: gosnmp.OpaqueDouble,
  476. }
  477. mockGoSNMP.On("Get", []string{"1"}).Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu1}}, nil)
  478. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  479. client := &snmpClient{
  480. logger: zap.NewNop(),
  481. client: mockGoSNMP,
  482. }
  483. var scraperErrors scrapererror.ScrapeErrors
  484. oidSlice := []string{"1"}
  485. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  486. expectedErr := fmt.Errorf("problem with getting scalar data: data for OID '1' not a supported type")
  487. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  488. require.Equal(t, expectedSNMPData, returnedSNMPData)
  489. },
  490. },
  491. {
  492. desc: "GoSNMP Client int data type with bad value adds error",
  493. testFunc: func(t *testing.T) {
  494. expectedSNMPData := []SNMPData{}
  495. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  496. pdu1 := gosnmp.SnmpPDU{
  497. Value: float64(math.MaxFloat64),
  498. Name: "1",
  499. Type: gosnmp.Counter64,
  500. }
  501. mockGoSNMP.On("Get", []string{"1"}).Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu1}}, nil)
  502. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  503. client := &snmpClient{
  504. logger: zap.NewNop(),
  505. client: mockGoSNMP,
  506. }
  507. var scraperErrors scrapererror.ScrapeErrors
  508. oidSlice := []string{"1"}
  509. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  510. expectedErr := fmt.Errorf("problem with getting scalar data: data for OID '1' not a supported type")
  511. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  512. require.Equal(t, expectedSNMPData, returnedSNMPData)
  513. },
  514. },
  515. {
  516. desc: "GoSNMP Client string data type properly converted",
  517. testFunc: func(t *testing.T) {
  518. expectedSNMPData := []SNMPData{
  519. {
  520. oid: "1",
  521. value: "test",
  522. valueType: stringVal,
  523. },
  524. }
  525. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  526. pdu1 := gosnmp.SnmpPDU{
  527. Value: []byte("test"),
  528. Name: "1",
  529. Type: gosnmp.OctetString,
  530. }
  531. mockGoSNMP.On("Get", []string{"1"}).Return(&gosnmp.SnmpPacket{Variables: []gosnmp.SnmpPDU{pdu1}}, nil)
  532. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  533. client := &snmpClient{
  534. logger: zap.NewNop(),
  535. client: mockGoSNMP,
  536. }
  537. var scraperErrors scrapererror.ScrapeErrors
  538. oidSlice := []string{"1"}
  539. returnedSNMPData := client.GetScalarData(oidSlice, &scraperErrors)
  540. require.NoError(t, scraperErrors.Combine())
  541. require.Equal(t, expectedSNMPData, returnedSNMPData)
  542. },
  543. },
  544. }
  545. for _, tc := range testCases {
  546. t.Run(tc.desc, tc.testFunc)
  547. }
  548. }
  549. func TestGetIndexedData(t *testing.T) {
  550. testCases := []struct {
  551. desc string
  552. testFunc func(*testing.T)
  553. }{
  554. {
  555. desc: "No OIDs does nothing",
  556. testFunc: func(t *testing.T) {
  557. expectedSNMPData := []SNMPData{}
  558. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  559. client := &snmpClient{
  560. logger: zap.NewNop(),
  561. client: mockGoSNMP,
  562. }
  563. var scraperErrors scrapererror.ScrapeErrors
  564. returnedSNMPData := client.GetIndexedData([]string{}, &scraperErrors)
  565. require.NoError(t, scraperErrors.Combine())
  566. require.Equal(t, expectedSNMPData, returnedSNMPData)
  567. },
  568. },
  569. {
  570. desc: "GoSNMP Client failures adds error",
  571. testFunc: func(t *testing.T) {
  572. expectedSNMPData := []SNMPData{}
  573. walkError := errors.New("Bad WALK")
  574. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  575. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  576. mockGoSNMP.On("BulkWalkAll", "1").Return(nil, walkError)
  577. client := &snmpClient{
  578. logger: zap.NewNop(),
  579. client: mockGoSNMP,
  580. }
  581. var scraperErrors scrapererror.ScrapeErrors
  582. oidSlice := []string{"1"}
  583. returnedSNMPData := client.GetIndexedData(oidSlice, &scraperErrors)
  584. expectedErr := fmt.Errorf("problem with getting indexed data: problem with SNMP WALK for OID '1': %w", walkError)
  585. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  586. require.Equal(t, expectedSNMPData, returnedSNMPData)
  587. },
  588. },
  589. {
  590. desc: "GoSNMP Client timeout failures tries to reset connection",
  591. testFunc: func(t *testing.T) {
  592. expectedSNMPData := []SNMPData{}
  593. walkError := errors.New("request timeout (after 0 retries)")
  594. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  595. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  596. mockGoSNMP.On("BulkWalkAll", "1").Return(nil, walkError)
  597. mockGoSNMP.On("Close", mock.Anything).Return(nil)
  598. mockGoSNMP.On("Connect", mock.Anything).Return(nil)
  599. client := &snmpClient{
  600. logger: zap.NewNop(),
  601. client: mockGoSNMP,
  602. }
  603. var scraperErrors scrapererror.ScrapeErrors
  604. oidSlice := []string{"1"}
  605. returnedSNMPData := client.GetIndexedData(oidSlice, &scraperErrors)
  606. expectedErr := fmt.Errorf("problem with getting indexed data: problem with SNMP WALK for OID '1': %w", walkError)
  607. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  608. require.Equal(t, expectedSNMPData, returnedSNMPData)
  609. },
  610. },
  611. {
  612. desc: "GoSNMP Client reset connection fails on connect adds errors",
  613. testFunc: func(t *testing.T) {
  614. expectedSNMPData := []SNMPData{}
  615. walkError := errors.New("request timeout (after 0 retries)")
  616. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  617. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  618. mockGoSNMP.On("BulkWalkAll", "1").Return(nil, walkError)
  619. mockGoSNMP.On("Close", mock.Anything).Return(nil)
  620. connectErr := errors.New("can't connect")
  621. mockGoSNMP.On("Connect", mock.Anything).Return(connectErr)
  622. client := &snmpClient{
  623. logger: zap.NewNop(),
  624. client: mockGoSNMP,
  625. }
  626. var scraperErrors scrapererror.ScrapeErrors
  627. oidSlice := []string{"1"}
  628. returnedSNMPData := client.GetIndexedData(oidSlice, &scraperErrors)
  629. expectedErr1 := fmt.Errorf("problem with getting indexed data: problem with SNMP WALK for OID '1': %w", walkError)
  630. expectedErr2 := fmt.Errorf("problem with getting indexed data: problem connecting while trying to reset connection: %w", connectErr)
  631. expectedErr := fmt.Errorf(expectedErr1.Error() + "; " + expectedErr2.Error())
  632. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  633. require.Equal(t, expectedSNMPData, returnedSNMPData)
  634. },
  635. },
  636. {
  637. desc: "GoSNMP Client partial failures still returns successes",
  638. testFunc: func(t *testing.T) {
  639. expectedSNMPData := []SNMPData{
  640. {
  641. columnOID: "2",
  642. oid: "2.1",
  643. value: int64(1),
  644. valueType: integerVal,
  645. },
  646. }
  647. pdu1 := gosnmp.SnmpPDU{
  648. Value: 1,
  649. Name: "2.1",
  650. Type: gosnmp.Integer,
  651. }
  652. walkError := errors.New("Bad Walk")
  653. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  654. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  655. mockGoSNMP.On("BulkWalkAll", "1").Return(nil, walkError).Once()
  656. mockGoSNMP.On("BulkWalkAll", "2").Return([]gosnmp.SnmpPDU{pdu1}, nil).Once()
  657. client := &snmpClient{
  658. logger: zap.NewNop(),
  659. client: mockGoSNMP,
  660. }
  661. var scraperErrors scrapererror.ScrapeErrors
  662. oidSlice := []string{"1", "2"}
  663. returnedSNMPData := client.GetIndexedData(oidSlice, &scraperErrors)
  664. expectedErr := fmt.Errorf("problem with getting indexed data: problem with SNMP WALK for OID '1': %w", walkError)
  665. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  666. require.Equal(t, expectedSNMPData, returnedSNMPData)
  667. },
  668. },
  669. {
  670. desc: "GoSNMP Client returned nil value does not return data",
  671. testFunc: func(t *testing.T) {
  672. expectedSNMPData := []SNMPData{}
  673. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  674. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  675. badOID := "1.1"
  676. pdu := gosnmp.SnmpPDU{
  677. Value: nil,
  678. Name: badOID,
  679. Type: gosnmp.Integer,
  680. }
  681. mockGoSNMP.On("BulkWalkAll", "1").Return([]gosnmp.SnmpPDU{pdu}, nil)
  682. client := &snmpClient{
  683. logger: zap.NewNop(),
  684. client: mockGoSNMP,
  685. }
  686. var scraperErrors scrapererror.ScrapeErrors
  687. oidSlice := []string{"1"}
  688. returnedSNMPData := client.GetIndexedData(oidSlice, &scraperErrors)
  689. expectedErr := fmt.Errorf("problem with getting indexed data: data for OID '%s' not found", badOID)
  690. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  691. require.Equal(t, expectedSNMPData, returnedSNMPData)
  692. },
  693. },
  694. {
  695. desc: "GoSNMP Client returned unsupported type value does not return data",
  696. testFunc: func(t *testing.T) {
  697. expectedSNMPData := []SNMPData{}
  698. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  699. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  700. badOID := "1.1"
  701. pdu := gosnmp.SnmpPDU{
  702. Value: true,
  703. Name: badOID,
  704. Type: gosnmp.Boolean,
  705. }
  706. mockGoSNMP.On("BulkWalkAll", "1").Return([]gosnmp.SnmpPDU{pdu}, nil)
  707. client := &snmpClient{
  708. logger: zap.NewNop(),
  709. client: mockGoSNMP,
  710. }
  711. var scraperErrors scrapererror.ScrapeErrors
  712. oidSlice := []string{"1"}
  713. returnedSNMPData := client.GetIndexedData(oidSlice, &scraperErrors)
  714. expectedErr := fmt.Errorf("problem with getting indexed data: data for OID '%s' not a supported type", badOID)
  715. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  716. require.Equal(t, expectedSNMPData, returnedSNMPData)
  717. },
  718. },
  719. {
  720. desc: "Return multiple good values",
  721. testFunc: func(t *testing.T) {
  722. expectedSNMPData := []SNMPData{
  723. {
  724. columnOID: "1",
  725. oid: "1.1",
  726. value: int64(1),
  727. valueType: integerVal,
  728. },
  729. {
  730. columnOID: "1",
  731. oid: "1.2",
  732. value: int64(2),
  733. valueType: integerVal,
  734. },
  735. {
  736. columnOID: "2",
  737. oid: "2.1",
  738. value: int64(3),
  739. valueType: integerVal,
  740. },
  741. {
  742. columnOID: "2",
  743. oid: "2.2",
  744. value: int64(4),
  745. valueType: integerVal,
  746. },
  747. }
  748. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  749. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  750. pdu1 := gosnmp.SnmpPDU{
  751. Value: 1,
  752. Name: "1.1",
  753. Type: gosnmp.Integer,
  754. }
  755. pdu2 := gosnmp.SnmpPDU{
  756. Value: 2,
  757. Name: "1.2",
  758. Type: gosnmp.Integer,
  759. }
  760. pdu3 := gosnmp.SnmpPDU{
  761. Value: 3,
  762. Name: "2.1",
  763. Type: gosnmp.Integer,
  764. }
  765. pdu4 := gosnmp.SnmpPDU{
  766. Value: 4,
  767. Name: "2.2",
  768. Type: gosnmp.Integer,
  769. }
  770. mockGoSNMP.On("BulkWalkAll", "1").Return([]gosnmp.SnmpPDU{pdu1, pdu2}, nil)
  771. mockGoSNMP.On("BulkWalkAll", "2").Return([]gosnmp.SnmpPDU{pdu3, pdu4}, nil)
  772. client := &snmpClient{
  773. logger: zap.NewNop(),
  774. client: mockGoSNMP,
  775. }
  776. var scraperErrors scrapererror.ScrapeErrors
  777. returnedSNMPData := client.GetIndexedData([]string{"1", "2"}, &scraperErrors)
  778. require.NoError(t, scraperErrors.Combine())
  779. require.Equal(t, expectedSNMPData, returnedSNMPData)
  780. },
  781. },
  782. {
  783. desc: "GoSNMP Client float data type properly converted",
  784. testFunc: func(t *testing.T) {
  785. expectedSNMPData := []SNMPData{
  786. {
  787. columnOID: "1",
  788. oid: "1.1",
  789. value: 1.0,
  790. valueType: floatVal,
  791. },
  792. }
  793. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  794. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  795. pdu := gosnmp.SnmpPDU{
  796. Value: 1.0,
  797. Name: "1.1",
  798. Type: gosnmp.OpaqueDouble,
  799. }
  800. mockGoSNMP.On("BulkWalkAll", "1").Return([]gosnmp.SnmpPDU{pdu}, nil)
  801. client := &snmpClient{
  802. logger: zap.NewNop(),
  803. client: mockGoSNMP,
  804. }
  805. var scraperErrors scrapererror.ScrapeErrors
  806. returnedSNMPData := client.GetIndexedData([]string{"1"}, &scraperErrors)
  807. require.NoError(t, scraperErrors.Combine())
  808. require.Equal(t, expectedSNMPData, returnedSNMPData)
  809. },
  810. },
  811. {
  812. desc: "GoSNMP Client float data type with bad value adds error",
  813. testFunc: func(t *testing.T) {
  814. expectedSNMPData := []SNMPData{}
  815. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  816. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  817. pdu := gosnmp.SnmpPDU{
  818. Value: true,
  819. Name: "1.1",
  820. Type: gosnmp.OpaqueDouble,
  821. }
  822. mockGoSNMP.On("BulkWalkAll", "1").Return([]gosnmp.SnmpPDU{pdu}, nil)
  823. client := &snmpClient{
  824. logger: zap.NewNop(),
  825. client: mockGoSNMP,
  826. }
  827. var scraperErrors scrapererror.ScrapeErrors
  828. returnedSNMPData := client.GetIndexedData([]string{"1"}, &scraperErrors)
  829. expectedErr := fmt.Errorf("problem with getting indexed data: data for OID '1.1' not a supported type")
  830. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  831. require.Equal(t, expectedSNMPData, returnedSNMPData)
  832. },
  833. },
  834. {
  835. desc: "GoSNMP Client float data type with bad string value adds error",
  836. testFunc: func(t *testing.T) {
  837. expectedSNMPData := []SNMPData{}
  838. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  839. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  840. pdu := gosnmp.SnmpPDU{
  841. Value: "bad",
  842. Name: "1.1",
  843. Type: gosnmp.OpaqueDouble,
  844. }
  845. mockGoSNMP.On("BulkWalkAll", "1").Return([]gosnmp.SnmpPDU{pdu}, nil)
  846. client := &snmpClient{
  847. logger: zap.NewNop(),
  848. client: mockGoSNMP,
  849. }
  850. var scraperErrors scrapererror.ScrapeErrors
  851. returnedSNMPData := client.GetIndexedData([]string{"1"}, &scraperErrors)
  852. expectedErr := fmt.Errorf("problem with getting indexed data: data for OID '1.1' not a supported type")
  853. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  854. require.Equal(t, expectedSNMPData, returnedSNMPData)
  855. },
  856. },
  857. {
  858. desc: "GoSNMP Client int data type with bad value adds error",
  859. testFunc: func(t *testing.T) {
  860. expectedSNMPData := []SNMPData{}
  861. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  862. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  863. pdu := gosnmp.SnmpPDU{
  864. Value: float64(math.MaxFloat64),
  865. Name: "1.1",
  866. Type: gosnmp.Counter64,
  867. }
  868. mockGoSNMP.On("BulkWalkAll", "1").Return([]gosnmp.SnmpPDU{pdu}, nil)
  869. client := &snmpClient{
  870. logger: zap.NewNop(),
  871. client: mockGoSNMP,
  872. }
  873. var scraperErrors scrapererror.ScrapeErrors
  874. returnedSNMPData := client.GetIndexedData([]string{"1"}, &scraperErrors)
  875. expectedErr := fmt.Errorf("problem with getting indexed data: data for OID '1.1' not a supported type")
  876. require.EqualError(t, scraperErrors.Combine(), expectedErr.Error())
  877. require.Equal(t, expectedSNMPData, returnedSNMPData)
  878. },
  879. },
  880. {
  881. desc: "GoSNMP Client string data type properly converted",
  882. testFunc: func(t *testing.T) {
  883. expectedSNMPData := []SNMPData{
  884. {
  885. columnOID: "1",
  886. oid: "1.1",
  887. value: "test",
  888. valueType: stringVal,
  889. },
  890. }
  891. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  892. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version2c)
  893. pdu := gosnmp.SnmpPDU{
  894. Value: []byte("test"),
  895. Name: "1.1",
  896. Type: gosnmp.OctetString,
  897. }
  898. mockGoSNMP.On("BulkWalkAll", "1").Return([]gosnmp.SnmpPDU{pdu}, nil)
  899. client := &snmpClient{
  900. logger: zap.NewNop(),
  901. client: mockGoSNMP,
  902. }
  903. var scraperErrors scrapererror.ScrapeErrors
  904. returnedSNMPData := client.GetIndexedData([]string{"1"}, &scraperErrors)
  905. require.NoError(t, scraperErrors.Combine())
  906. require.Equal(t, expectedSNMPData, returnedSNMPData)
  907. },
  908. },
  909. {
  910. desc: "GoSNMP Client v1 uses normal Walk function",
  911. testFunc: func(t *testing.T) {
  912. expectedSNMPData := []SNMPData{
  913. {
  914. columnOID: "1",
  915. oid: "1.1",
  916. value: int64(1),
  917. valueType: integerVal,
  918. },
  919. }
  920. mockGoSNMP := new(mocks.MockGoSNMPWrapper)
  921. mockGoSNMP.On("GetVersion", mock.Anything).Return(gosnmp.Version1)
  922. pdu := gosnmp.SnmpPDU{
  923. Value: 1,
  924. Name: "1.1",
  925. Type: gosnmp.Counter32,
  926. }
  927. mockGoSNMP.On("WalkAll", "1").Return([]gosnmp.SnmpPDU{pdu}, nil)
  928. mockGoSNMP.On("GetMaxOids", mock.Anything).Return(2)
  929. client := &snmpClient{
  930. logger: zap.NewNop(),
  931. client: mockGoSNMP,
  932. }
  933. var scraperErrors scrapererror.ScrapeErrors
  934. returnedSNMPData := client.GetIndexedData([]string{"1"}, &scraperErrors)
  935. require.NoError(t, scraperErrors.Combine())
  936. require.Equal(t, expectedSNMPData, returnedSNMPData)
  937. },
  938. },
  939. }
  940. for _, tc := range testCases {
  941. t.Run(tc.desc, tc.testFunc)
  942. }
  943. }