github.com/influxdata/telegraf@v1.30.3/internal/snmp/translator_gosmi_test.go (about) 1 package snmp 2 3 import ( 4 "path/filepath" 5 "testing" 6 7 "github.com/gosnmp/gosnmp" 8 "github.com/stretchr/testify/require" 9 10 "github.com/influxdata/telegraf/testutil" 11 ) 12 13 func getGosmiTr(t *testing.T) Translator { 14 testDataPath, err := filepath.Abs("./testdata/gosmi") 15 require.NoError(t, err) 16 17 tr, err := NewGosmiTranslator([]string{testDataPath}, testutil.Logger{}) 18 require.NoError(t, err) 19 return tr 20 } 21 22 func TestGosmiTranslator(t *testing.T) { 23 var tr Translator 24 var err error 25 26 tr, err = NewGosmiTranslator([]string{"testdata"}, testutil.Logger{}) 27 require.NoError(t, err) 28 require.NotNil(t, tr) 29 } 30 31 func TestFieldInitGosmi(t *testing.T) { 32 tr := getGosmiTr(t) 33 34 translations := []struct { 35 inputOid string 36 inputName string 37 inputConversion string 38 expectedOid string 39 expectedName string 40 expectedConversion string 41 }{ 42 {".1.2.3", "foo", "", ".1.2.3", "foo", ""}, 43 {".iso.2.3", "foo", "", ".1.2.3", "foo", ""}, 44 {".1.0.0.0.1.1", "", "", ".1.0.0.0.1.1", "server", ""}, 45 {"IF-MIB::ifPhysAddress.1", "", "", ".1.3.6.1.2.1.2.2.1.6.1", "ifPhysAddress.1", "hwaddr"}, 46 {"IF-MIB::ifPhysAddress.1", "", "none", ".1.3.6.1.2.1.2.2.1.6.1", "ifPhysAddress.1", "none"}, 47 {"BRIDGE-MIB::dot1dTpFdbAddress.1", "", "", ".1.3.6.1.2.1.17.4.3.1.1.1", "dot1dTpFdbAddress.1", "hwaddr"}, 48 {"TCP-MIB::tcpConnectionLocalAddress.1", "", "", ".1.3.6.1.2.1.6.19.1.2.1", "tcpConnectionLocalAddress.1", "ipaddr"}, 49 {".999", "", "", ".999", ".999", ""}, 50 } 51 52 for _, txl := range translations { 53 f := Field{Oid: txl.inputOid, Name: txl.inputName, Conversion: txl.inputConversion} 54 require.NoError(t, f.Init(tr), "inputOid=%q inputName=%q", txl.inputOid, txl.inputName) 55 56 require.Equal(t, txl.expectedOid, f.Oid, "inputOid=%q inputName=%q inputConversion=%q", txl.inputOid, txl.inputName, txl.inputConversion) 57 require.Equal(t, txl.expectedName, f.Name, "inputOid=%q inputName=%q inputConversion=%q", txl.inputOid, txl.inputName, txl.inputConversion) 58 require.Equal(t, txl.expectedConversion, f.Conversion, "inputOid=%q inputName=%q inputConversion=%q", txl.inputOid, txl.inputName, txl.inputConversion) 59 } 60 } 61 62 func TestTableInitGosmi(t *testing.T) { 63 tbl := Table{ 64 Oid: ".1.3.6.1.2.1.3.1", 65 Fields: []Field{ 66 {Oid: ".999", Name: "foo"}, 67 {Oid: ".1.3.6.1.2.1.3.1.1.1", Name: "atIfIndex", IsTag: true}, 68 {Oid: "RFC1213-MIB::atPhysAddress", Name: "atPhysAddress"}, 69 }, 70 } 71 72 tr := getGosmiTr(t) 73 require.NoError(t, tbl.Init(tr)) 74 75 require.Equal(t, "atTable", tbl.Name) 76 77 require.Len(t, tbl.Fields, 5) 78 79 require.Equal(t, ".999", tbl.Fields[0].Oid) 80 require.Equal(t, "foo", tbl.Fields[0].Name) 81 require.False(t, tbl.Fields[0].IsTag) 82 require.Empty(t, tbl.Fields[0].Conversion) 83 84 require.Equal(t, ".1.3.6.1.2.1.3.1.1.1", tbl.Fields[1].Oid) 85 require.Equal(t, "atIfIndex", tbl.Fields[1].Name) 86 require.True(t, tbl.Fields[1].IsTag) 87 require.Empty(t, tbl.Fields[1].Conversion) 88 89 require.Equal(t, ".1.3.6.1.2.1.3.1.1.2", tbl.Fields[2].Oid) 90 require.Equal(t, "atPhysAddress", tbl.Fields[2].Name) 91 require.False(t, tbl.Fields[2].IsTag) 92 require.Equal(t, "hwaddr", tbl.Fields[2].Conversion) 93 94 require.Equal(t, ".1.3.6.1.2.1.3.1.1.3", tbl.Fields[4].Oid) 95 require.Equal(t, "atNetAddress", tbl.Fields[4].Name) 96 require.True(t, tbl.Fields[4].IsTag) 97 require.Empty(t, tbl.Fields[4].Conversion) 98 } 99 100 // TestTableBuild_walk in snmp_test.go is split into two tests here, 101 // noTranslate and Translate. 102 // 103 // This is only running with gosmi translator but should be valid with 104 // netsnmp too. 105 func TestTableBuild_walk_noTranslate(t *testing.T) { 106 tbl := Table{ 107 Name: "mytable", 108 IndexAsTag: true, 109 Fields: []Field{ 110 { 111 Name: "myfield1", 112 Oid: ".1.0.0.0.1.1", 113 IsTag: true, 114 }, 115 { 116 Name: "myfield2", 117 Oid: ".1.0.0.0.1.2", 118 }, 119 { 120 Name: "myfield3", 121 Oid: ".1.0.0.0.1.3", 122 Conversion: "float", 123 }, 124 { 125 Name: "myfield4", 126 Oid: ".1.0.0.2.1.5", 127 OidIndexSuffix: ".9.9", 128 }, 129 { 130 Name: "myfield5", 131 Oid: ".1.0.0.2.1.5", 132 OidIndexLength: 1, 133 }, 134 }, 135 } 136 137 tb, err := tbl.Build(tsc, true) 138 require.NoError(t, err) 139 require.Equal(t, "mytable", tb.Name) 140 rtr1 := RTableRow{ 141 Tags: map[string]string{ 142 "myfield1": "foo", 143 "index": "0", 144 }, 145 Fields: map[string]interface{}{ 146 "myfield2": 1, 147 "myfield3": float64(0.123), 148 "myfield4": 11, 149 "myfield5": 11, 150 }, 151 } 152 rtr2 := RTableRow{ 153 Tags: map[string]string{ 154 "myfield1": "bar", 155 "index": "1", 156 }, 157 Fields: map[string]interface{}{ 158 "myfield2": 2, 159 "myfield3": float64(0.456), 160 "myfield4": 22, 161 "myfield5": 22, 162 }, 163 } 164 rtr3 := RTableRow{ 165 Tags: map[string]string{ 166 "index": "2", 167 }, 168 Fields: map[string]interface{}{ 169 "myfield2": 0, 170 "myfield3": float64(0.0), 171 }, 172 } 173 rtr4 := RTableRow{ 174 Tags: map[string]string{ 175 "index": "3", 176 }, 177 Fields: map[string]interface{}{ 178 "myfield3": float64(9.999), 179 }, 180 } 181 require.Len(t, tb.Rows, 4) 182 require.Contains(t, tb.Rows, rtr1) 183 require.Contains(t, tb.Rows, rtr2) 184 require.Contains(t, tb.Rows, rtr3) 185 require.Contains(t, tb.Rows, rtr4) 186 } 187 188 func TestTableBuild_walk_Translate(t *testing.T) { 189 tbl := Table{ 190 Name: "atTable", 191 IndexAsTag: true, 192 Fields: []Field{ 193 { 194 Name: "ifIndex", 195 Oid: "1.3.6.1.2.1.3.1.1.1", 196 IsTag: true, 197 }, 198 { 199 Name: "atPhysAddress", 200 Oid: "1.3.6.1.2.1.3.1.1.2", 201 Translate: false, 202 }, 203 { 204 Name: "atNetAddress", 205 Oid: "1.3.6.1.2.1.3.1.1.3", 206 Translate: true, 207 }, 208 }, 209 } 210 211 require.NoError(t, tbl.Init(getGosmiTr(t))) 212 tb, err := tbl.Build(tsc, true) 213 require.NoError(t, err) 214 215 require.Equal(t, "atTable", tb.Name) 216 217 rtr1 := RTableRow{ 218 Tags: map[string]string{ 219 "ifIndex": "foo", 220 "index": "0", 221 }, 222 Fields: map[string]interface{}{ 223 "atPhysAddress": 1, 224 "atNetAddress": "atNetAddress", 225 }, 226 } 227 rtr2 := RTableRow{ 228 Tags: map[string]string{ 229 "ifIndex": "bar", 230 "index": "1", 231 }, 232 Fields: map[string]interface{}{ 233 "atPhysAddress": 2, 234 }, 235 } 236 rtr3 := RTableRow{ 237 Tags: map[string]string{ 238 "index": "2", 239 }, 240 Fields: map[string]interface{}{ 241 "atPhysAddress": 0, 242 }, 243 } 244 245 require.Len(t, tb.Rows, 3) 246 require.Contains(t, tb.Rows, rtr1) 247 require.Contains(t, tb.Rows, rtr2) 248 require.Contains(t, tb.Rows, rtr3) 249 } 250 251 func TestTableBuild_noWalkGosmi(t *testing.T) { 252 tbl := Table{ 253 Name: "mytable", 254 Fields: []Field{ 255 { 256 Name: "myfield1", 257 Oid: ".1.0.0.1.1", 258 IsTag: true, 259 }, 260 { 261 Name: "myfield2", 262 Oid: ".1.0.0.1.2", 263 }, 264 { 265 Name: "myfield3", 266 Oid: ".1.0.0.1.2", 267 IsTag: true, 268 }, 269 { 270 Name: "empty", 271 Oid: ".1.0.0.0.1.1.2", 272 }, 273 { 274 Name: "noexist", 275 Oid: ".1.2.3.4.5", 276 }, 277 }, 278 } 279 280 tb, err := tbl.Build(tsc, false) 281 require.NoError(t, err) 282 283 rtr := RTableRow{ 284 Tags: map[string]string{"myfield1": "baz", "myfield3": "234"}, 285 Fields: map[string]interface{}{"myfield2": 234}, 286 } 287 require.Len(t, tb.Rows, 1) 288 require.Contains(t, tb.Rows, rtr) 289 } 290 291 func TestFieldConvertGosmi(t *testing.T) { 292 testTable := []struct { 293 input interface{} 294 conv string 295 expected interface{} 296 }{ 297 {[]byte("foo"), "", "foo"}, 298 {"0.123", "float", float64(0.123)}, 299 {[]byte("0.123"), "float", float64(0.123)}, 300 {float32(0.123), "float", float64(float32(0.123))}, 301 {float64(0.123), "float", float64(0.123)}, 302 {float64(0.123123123123), "float", float64(0.123123123123)}, 303 {123, "float", float64(123)}, 304 {123, "float(0)", float64(123)}, 305 {123, "float(4)", float64(0.0123)}, 306 {int8(123), "float(3)", float64(0.123)}, 307 {int16(123), "float(3)", float64(0.123)}, 308 {int32(123), "float(3)", float64(0.123)}, 309 {int64(123), "float(3)", float64(0.123)}, 310 {uint(123), "float(3)", float64(0.123)}, 311 {uint8(123), "float(3)", float64(0.123)}, 312 {uint16(123), "float(3)", float64(0.123)}, 313 {uint32(123), "float(3)", float64(0.123)}, 314 {uint64(123), "float(3)", float64(0.123)}, 315 {"123", "int", int64(123)}, 316 {[]byte("123"), "int", int64(123)}, 317 {"123123123123", "int", int64(123123123123)}, 318 {[]byte("123123123123"), "int", int64(123123123123)}, 319 {float32(12.3), "int", int64(12)}, 320 {float64(12.3), "int", int64(12)}, 321 {123, "int", int64(123)}, 322 {int8(123), "int", int64(123)}, 323 {int16(123), "int", int64(123)}, 324 {int32(123), "int", int64(123)}, 325 {int64(123), "int", int64(123)}, 326 {uint(123), "int", int64(123)}, 327 {uint8(123), "int", int64(123)}, 328 {uint16(123), "int", int64(123)}, 329 {uint32(123), "int", int64(123)}, 330 {uint64(123), "int", int64(123)}, 331 {[]byte("abcdef"), "hwaddr", "61:62:63:64:65:66"}, 332 {"abcdef", "hwaddr", "61:62:63:64:65:66"}, 333 {[]byte("abcd"), "ipaddr", "97.98.99.100"}, 334 {"abcd", "ipaddr", "97.98.99.100"}, 335 {[]byte("abcdefghijklmnop"), "ipaddr", "6162:6364:6566:6768:696a:6b6c:6d6e:6f70"}, 336 {[]byte{0x00, 0x09, 0x3E, 0xE3, 0xF6, 0xD5, 0x3B, 0x60}, "hextoint:BigEndian:uint64", uint64(2602423610063712)}, 337 {[]byte{0x00, 0x09, 0x3E, 0xE3}, "hextoint:BigEndian:uint32", uint32(605923)}, 338 {[]byte{0x00, 0x09}, "hextoint:BigEndian:uint16", uint16(9)}, 339 {[]byte{0x00, 0x09, 0x3E, 0xE3, 0xF6, 0xD5, 0x3B, 0x60}, "hextoint:LittleEndian:uint64", uint64(6934371307618175232)}, 340 {[]byte{0x00, 0x09, 0x3E, 0xE3}, "hextoint:LittleEndian:uint32", uint32(3812493568)}, 341 {[]byte{0x00, 0x09}, "hextoint:LittleEndian:uint16", uint16(2304)}, 342 {3, "enum", "testing"}, 343 {3, "enum(1)", "testing(3)"}, 344 } 345 346 for _, tc := range testTable { 347 f := Field{ 348 Name: "test", 349 Conversion: tc.conv, 350 } 351 require.NoError(t, f.Init(getGosmiTr(t))) 352 353 act, err := f.Convert(gosnmp.SnmpPDU{Name: ".1.3.6.1.2.1.2.2.1.8", Value: tc.input}) 354 require.NoError(t, err, "input=%T(%v) conv=%s expected=%T(%v)", tc.input, tc.input, tc.conv, tc.expected, tc.expected) 355 require.EqualValues(t, tc.expected, act, "input=%T(%v) conv=%s expected=%T(%v)", tc.input, tc.input, tc.conv, tc.expected, tc.expected) 356 } 357 } 358 359 func TestTableJoin_walkGosmi(t *testing.T) { 360 tbl := Table{ 361 Name: "mytable", 362 IndexAsTag: true, 363 Fields: []Field{ 364 { 365 Name: "myfield1", 366 Oid: ".1.0.0.3.1.1", 367 IsTag: true, 368 }, 369 { 370 Name: "myfield2", 371 Oid: ".1.0.0.3.1.2", 372 }, 373 { 374 Name: "myfield3", 375 Oid: ".1.0.0.3.1.3", 376 SecondaryIndexTable: true, 377 }, 378 { 379 Name: "myfield4", 380 Oid: ".1.0.0.0.1.1", 381 SecondaryIndexUse: true, 382 IsTag: true, 383 }, 384 { 385 Name: "myfield5", 386 Oid: ".1.0.0.0.1.2", 387 SecondaryIndexUse: true, 388 }, 389 }, 390 } 391 392 require.NoError(t, tbl.Init(getGosmiTr(t))) 393 tb, err := tbl.Build(tsc, true) 394 require.NoError(t, err) 395 396 require.Equal(t, "mytable", tb.Name) 397 rtr1 := RTableRow{ 398 Tags: map[string]string{ 399 "myfield1": "instance", 400 "myfield4": "bar", 401 "index": "10", 402 }, 403 Fields: map[string]interface{}{ 404 "myfield2": 10, 405 "myfield3": 1, 406 "myfield5": 2, 407 }, 408 } 409 rtr2 := RTableRow{ 410 Tags: map[string]string{ 411 "myfield1": "instance2", 412 "index": "11", 413 }, 414 Fields: map[string]interface{}{ 415 "myfield2": 20, 416 "myfield3": 2, 417 "myfield5": 0, 418 }, 419 } 420 rtr3 := RTableRow{ 421 Tags: map[string]string{ 422 "myfield1": "instance3", 423 "index": "12", 424 }, 425 Fields: map[string]interface{}{ 426 "myfield2": 20, 427 "myfield3": 3, 428 }, 429 } 430 require.Len(t, tb.Rows, 3) 431 require.Contains(t, tb.Rows, rtr1) 432 require.Contains(t, tb.Rows, rtr2) 433 require.Contains(t, tb.Rows, rtr3) 434 } 435 436 func TestTableOuterJoin_walkGosmi(t *testing.T) { 437 tbl := Table{ 438 Name: "mytable", 439 IndexAsTag: true, 440 Fields: []Field{ 441 { 442 Name: "myfield1", 443 Oid: ".1.0.0.3.1.1", 444 IsTag: true, 445 }, 446 { 447 Name: "myfield2", 448 Oid: ".1.0.0.3.1.2", 449 }, 450 { 451 Name: "myfield3", 452 Oid: ".1.0.0.3.1.3", 453 SecondaryIndexTable: true, 454 SecondaryOuterJoin: true, 455 }, 456 { 457 Name: "myfield4", 458 Oid: ".1.0.0.0.1.1", 459 SecondaryIndexUse: true, 460 IsTag: true, 461 }, 462 { 463 Name: "myfield5", 464 Oid: ".1.0.0.0.1.2", 465 SecondaryIndexUse: true, 466 }, 467 }, 468 } 469 470 tb, err := tbl.Build(tsc, true) 471 require.NoError(t, err) 472 473 require.Equal(t, "mytable", tb.Name) 474 rtr1 := RTableRow{ 475 Tags: map[string]string{ 476 "myfield1": "instance", 477 "myfield4": "bar", 478 "index": "10", 479 }, 480 Fields: map[string]interface{}{ 481 "myfield2": 10, 482 "myfield3": 1, 483 "myfield5": 2, 484 }, 485 } 486 rtr2 := RTableRow{ 487 Tags: map[string]string{ 488 "myfield1": "instance2", 489 "index": "11", 490 }, 491 Fields: map[string]interface{}{ 492 "myfield2": 20, 493 "myfield3": 2, 494 "myfield5": 0, 495 }, 496 } 497 rtr3 := RTableRow{ 498 Tags: map[string]string{ 499 "myfield1": "instance3", 500 "index": "12", 501 }, 502 Fields: map[string]interface{}{ 503 "myfield2": 20, 504 "myfield3": 3, 505 }, 506 } 507 rtr4 := RTableRow{ 508 Tags: map[string]string{ 509 "index": "Secondary.0", 510 "myfield4": "foo", 511 }, 512 Fields: map[string]interface{}{ 513 "myfield5": 1, 514 }, 515 } 516 require.Len(t, tb.Rows, 4) 517 require.Contains(t, tb.Rows, rtr1) 518 require.Contains(t, tb.Rows, rtr2) 519 require.Contains(t, tb.Rows, rtr3) 520 require.Contains(t, tb.Rows, rtr4) 521 } 522 523 func TestTableJoinNoIndexAsTag_walkGosmi(t *testing.T) { 524 tbl := Table{ 525 Name: "mytable", 526 IndexAsTag: false, 527 Fields: []Field{ 528 { 529 Name: "myfield1", 530 Oid: ".1.0.0.3.1.1", 531 IsTag: true, 532 }, 533 { 534 Name: "myfield2", 535 Oid: ".1.0.0.3.1.2", 536 }, 537 { 538 Name: "myfield3", 539 Oid: ".1.0.0.3.1.3", 540 SecondaryIndexTable: true, 541 }, 542 { 543 Name: "myfield4", 544 Oid: ".1.0.0.0.1.1", 545 SecondaryIndexUse: true, 546 IsTag: true, 547 }, 548 { 549 Name: "myfield5", 550 Oid: ".1.0.0.0.1.2", 551 SecondaryIndexUse: true, 552 }, 553 }, 554 } 555 556 tb, err := tbl.Build(tsc, true) 557 require.NoError(t, err) 558 559 require.Equal(t, "mytable", tb.Name) 560 rtr1 := RTableRow{ 561 Tags: map[string]string{ 562 "myfield1": "instance", 563 "myfield4": "bar", 564 //"index": "10", 565 }, 566 Fields: map[string]interface{}{ 567 "myfield2": 10, 568 "myfield3": 1, 569 "myfield5": 2, 570 }, 571 } 572 rtr2 := RTableRow{ 573 Tags: map[string]string{ 574 "myfield1": "instance2", 575 //"index": "11", 576 }, 577 Fields: map[string]interface{}{ 578 "myfield2": 20, 579 "myfield3": 2, 580 "myfield5": 0, 581 }, 582 } 583 rtr3 := RTableRow{ 584 Tags: map[string]string{ 585 "myfield1": "instance3", 586 //"index": "12", 587 }, 588 Fields: map[string]interface{}{ 589 "myfield2": 20, 590 "myfield3": 3, 591 }, 592 } 593 require.Len(t, tb.Rows, 3) 594 require.Contains(t, tb.Rows, rtr1) 595 require.Contains(t, tb.Rows, rtr2) 596 require.Contains(t, tb.Rows, rtr3) 597 } 598 599 func TestCanNotParse(t *testing.T) { 600 tr := getGosmiTr(t) 601 f := Field{ 602 Oid: "RFC1213-MIB::", 603 } 604 605 require.Error(t, f.Init(tr)) 606 } 607 608 func TestTrapLookup(t *testing.T) { 609 tests := []struct { 610 name string 611 oid string 612 expected MibEntry 613 }{ 614 { 615 name: "Known trap OID", 616 oid: ".1.3.6.1.6.3.1.1.5.1", 617 expected: MibEntry{ 618 MibName: "TGTEST-MIB", 619 OidText: "coldStart", 620 }, 621 }, 622 { 623 name: "Known trap value OID", 624 oid: ".1.3.6.1.2.1.1.3.0", 625 expected: MibEntry{ 626 MibName: "TGTEST-MIB", 627 OidText: "sysUpTimeInstance", 628 }, 629 }, 630 { 631 name: "Unknown enterprise sub-OID", 632 oid: ".1.3.6.1.4.1.0.1.2.3", 633 expected: MibEntry{ 634 MibName: "SNMPv2-SMI", 635 OidText: "enterprises.0.1.2.3", 636 }, 637 }, 638 { 639 name: "Unknown MIB", 640 oid: ".1.999", 641 expected: MibEntry{OidText: "iso.999"}, 642 }, 643 } 644 645 // Load the MIBs 646 getGosmiTr(t) 647 648 for _, tt := range tests { 649 t.Run(tt.name, func(t *testing.T) { 650 // Run the actual test 651 actual, err := TrapLookup(tt.oid) 652 require.NoError(t, err) 653 require.Equal(t, tt.expected, actual) 654 }) 655 } 656 } 657 658 func TestTrapLookupFail(t *testing.T) { 659 tests := []struct { 660 name string 661 oid string 662 expected string 663 }{ 664 { 665 name: "New top level OID", 666 oid: ".3.6.1.3.0", 667 expected: "Could not find node for OID 3.6.1.3.0", 668 }, 669 { 670 name: "Malformed OID", 671 oid: ".1.3.dod.1.3.0", 672 expected: "could not convert OID .1.3.dod.1.3.0: strconv.ParseUint: parsing \"dod\": invalid syntax", 673 }, 674 } 675 676 // Load the MIBs 677 getGosmiTr(t) 678 679 for _, tt := range tests { 680 t.Run(tt.name, func(t *testing.T) { 681 // Run the actual test 682 _, err := TrapLookup(tt.oid) 683 require.EqualError(t, err, tt.expected) 684 }) 685 } 686 }