feat: telegraf to merge tables with different indexes (#9241)
This commit is contained in:
parent
6a3b27126a
commit
56398237c4
|
|
@ -201,6 +201,113 @@ One [metric][] is created for each row of the SNMP table.
|
||||||
## Specifies if the value of given field should be snmptranslated
|
## Specifies if the value of given field should be snmptranslated
|
||||||
## by default no field values are translated
|
## by default no field values are translated
|
||||||
# translate = true
|
# translate = true
|
||||||
|
|
||||||
|
## Secondary index table allows to merge data from two tables with
|
||||||
|
## different index that this filed will be used to join them. There can
|
||||||
|
## be only one secondary index table.
|
||||||
|
# secondary_index_table = false
|
||||||
|
|
||||||
|
## This field is using secondary index, and will be later merged with
|
||||||
|
## primary index using SecondaryIndexTable. SecondaryIndexTable and
|
||||||
|
## SecondaryIndexUse are exclusive.
|
||||||
|
# secondary_index_use = false
|
||||||
|
|
||||||
|
## Controls if entries from secondary table should be added or not
|
||||||
|
## if joining index is present or not. I set to true, means that join
|
||||||
|
## is outer, and index is prepended with "Secondary." for missing values
|
||||||
|
## to avoid overlaping indexes from both tables. Can be set per field or
|
||||||
|
## globally with SecondaryIndexTable, global true overrides per field false.
|
||||||
|
# secondary_outer_join = false
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Two Table Join
|
||||||
|
Snmp plugin can join two snmp tables that have different indexes. For this to work one table
|
||||||
|
should have translation field that return index of second table as value. Examples
|
||||||
|
of such fields are:
|
||||||
|
* Cisco portTable with translation field: `CISCO-STACK-MIB::portIfIndex`,
|
||||||
|
which value is IfIndex from ifTable
|
||||||
|
* Adva entityFacilityTable with translation field: `ADVA-FSPR7-MIB::entityFacilityOneIndex`,
|
||||||
|
which value is IfIndex from ifTable
|
||||||
|
* Cisco cpeExtPsePortTable with translation field: `CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortEntPhyIndex`,
|
||||||
|
which value is index from entPhysicalTable
|
||||||
|
|
||||||
|
Such field can be used to translate index to secondary table with `secondary_index_table = true`
|
||||||
|
and all fields from secondary table (with index pointed from translation field), should have added option
|
||||||
|
`secondary_index_use = true`. Telegraf cannot duplicate entries during join so translation
|
||||||
|
must be 1-to-1 (not 1-to-many). To add fields from secondary table with index that is not present
|
||||||
|
in translation table (outer join), there is a second option for translation index `secondary_outer_join = true`.
|
||||||
|
|
||||||
|
###### Example configuration for table joins
|
||||||
|
|
||||||
|
CISCO-POWER-ETHERNET-EXT-MIB table before join:
|
||||||
|
```
|
||||||
|
[[inputs.snmp.table]]
|
||||||
|
name = "ciscoPower"
|
||||||
|
index_as_tag = true
|
||||||
|
|
||||||
|
[[inputs.snmp.table.field]]
|
||||||
|
name = "PortPwrConsumption"
|
||||||
|
oid = "CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortPwrConsumption"
|
||||||
|
|
||||||
|
[[inputs.snmp.table.field]]
|
||||||
|
name = "EntPhyIndex"
|
||||||
|
oid = "CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortEntPhyIndex"
|
||||||
|
```
|
||||||
|
|
||||||
|
Partial result (removed agent_host and host columns from all following outputs in this section):
|
||||||
|
```
|
||||||
|
> ciscoPower,index=1.2 EntPhyIndex=1002i,PortPwrConsumption=6643i 1621460628000000000
|
||||||
|
> ciscoPower,index=1.6 EntPhyIndex=1006i,PortPwrConsumption=10287i 1621460628000000000
|
||||||
|
> ciscoPower,index=1.5 EntPhyIndex=1005i,PortPwrConsumption=8358i 1621460628000000000
|
||||||
|
```
|
||||||
|
|
||||||
|
Note here that EntPhyIndex column carries index from ENTITY-MIB table, config for it:
|
||||||
|
```
|
||||||
|
[[inputs.snmp.table]]
|
||||||
|
name = "entityTable"
|
||||||
|
index_as_tag = true
|
||||||
|
|
||||||
|
[[inputs.snmp.table.field]]
|
||||||
|
name = "EntPhysicalName"
|
||||||
|
oid = "ENTITY-MIB::entPhysicalName"
|
||||||
|
```
|
||||||
|
Partial result:
|
||||||
|
```
|
||||||
|
> entityTable,index=1006 EntPhysicalName="GigabitEthernet1/6" 1621460809000000000
|
||||||
|
> entityTable,index=1002 EntPhysicalName="GigabitEthernet1/2" 1621460809000000000
|
||||||
|
> entityTable,index=1005 EntPhysicalName="GigabitEthernet1/5" 1621460809000000000
|
||||||
|
```
|
||||||
|
|
||||||
|
Now, lets attempt to join these results into one table. EntPhyIndex matches index
|
||||||
|
from second table, and lets convert EntPhysicalName into tag, so second table will
|
||||||
|
only provide tags into result. Configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
[[inputs.snmp.table]]
|
||||||
|
name = "ciscoPowerEntity"
|
||||||
|
index_as_tag = true
|
||||||
|
|
||||||
|
[[inputs.snmp.table.field]]
|
||||||
|
name = "PortPwrConsumption"
|
||||||
|
oid = "CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortPwrConsumption"
|
||||||
|
|
||||||
|
[[inputs.snmp.table.field]]
|
||||||
|
name = "EntPhyIndex"
|
||||||
|
oid = "CISCO-POWER-ETHERNET-EXT-MIB::cpeExtPsePortEntPhyIndex"
|
||||||
|
secondary_index_table = true # enables joining
|
||||||
|
|
||||||
|
[[inputs.snmp.table.field]]
|
||||||
|
name = "EntPhysicalName"
|
||||||
|
oid = "ENTITY-MIB::entPhysicalName"
|
||||||
|
secondary_index_use = true # this tag is indexed from secondary table
|
||||||
|
is_tag = true
|
||||||
|
```
|
||||||
|
|
||||||
|
Result:
|
||||||
|
```
|
||||||
|
> ciscoPowerEntity,EntPhysicalName=GigabitEthernet1/2,index=1.2 EntPhyIndex=1002i,PortPwrConsumption=6643i 1621461148000000000
|
||||||
|
> ciscoPowerEntity,EntPhysicalName=GigabitEthernet1/6,index=1.6 EntPhyIndex=1006i,PortPwrConsumption=10287i 1621461148000000000
|
||||||
|
> ciscoPowerEntity,EntPhysicalName=GigabitEthernet1/5,index=1.5 EntPhyIndex=1005i,PortPwrConsumption=8358i 1621461148000000000
|
||||||
```
|
```
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
|
||||||
|
|
@ -187,11 +187,18 @@ func (t *Table) Init() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
secondaryIndexTablePresent := false
|
||||||
// initialize all the nested fields
|
// initialize all the nested fields
|
||||||
for i := range t.Fields {
|
for i := range t.Fields {
|
||||||
if err := t.Fields[i].init(); err != nil {
|
if err := t.Fields[i].init(); err != nil {
|
||||||
return fmt.Errorf("initializing field %s: %w", t.Fields[i].Name, err)
|
return fmt.Errorf("initializing field %s: %w", t.Fields[i].Name, err)
|
||||||
}
|
}
|
||||||
|
if t.Fields[i].SecondaryIndexTable {
|
||||||
|
if secondaryIndexTablePresent {
|
||||||
|
return fmt.Errorf("only one field can be SecondaryIndexTable")
|
||||||
|
}
|
||||||
|
secondaryIndexTablePresent = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
t.initialized = true
|
t.initialized = true
|
||||||
|
|
@ -252,6 +259,19 @@ type Field struct {
|
||||||
Conversion string
|
Conversion string
|
||||||
// Translate tells if the value of the field should be snmptranslated
|
// Translate tells if the value of the field should be snmptranslated
|
||||||
Translate bool
|
Translate bool
|
||||||
|
// Secondary index table allows to merge data from two tables with different index
|
||||||
|
// that this filed will be used to join them. There can be only one secondary index table.
|
||||||
|
SecondaryIndexTable bool
|
||||||
|
// This field is using secondary index, and will be later merged with primary index
|
||||||
|
// using SecondaryIndexTable. SecondaryIndexTable and SecondaryIndexUse are exclusive.
|
||||||
|
SecondaryIndexUse bool
|
||||||
|
// Controls if entries from secondary table should be added or not if joining
|
||||||
|
// index is present or not. I set to true, means that join is outer, and
|
||||||
|
// index is prepended with "Secondary." for missing values to avoid overlaping
|
||||||
|
// indexes from both tables.
|
||||||
|
// Can be set per field or globally with SecondaryIndexTable, global true overrides
|
||||||
|
// per field false.
|
||||||
|
SecondaryOuterJoin bool
|
||||||
|
|
||||||
initialized bool
|
initialized bool
|
||||||
}
|
}
|
||||||
|
|
@ -278,6 +298,14 @@ func (f *Field) init() error {
|
||||||
//TODO use textual convention conversion from the MIB
|
//TODO use textual convention conversion from the MIB
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if f.SecondaryIndexTable && f.SecondaryIndexUse {
|
||||||
|
return fmt.Errorf("SecondaryIndexTable and UseSecondaryIndex are exclusive")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.SecondaryIndexTable && !f.SecondaryIndexUse && f.SecondaryOuterJoin {
|
||||||
|
return fmt.Errorf("SecondaryOuterJoin set to true, but field is not being used in join")
|
||||||
|
}
|
||||||
|
|
||||||
f.initialized = true
|
f.initialized = true
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -414,6 +442,19 @@ func (s *Snmp) gatherTable(acc telegraf.Accumulator, gs snmpConnection, t Table,
|
||||||
func (t Table) Build(gs snmpConnection, walk bool) (*RTable, error) {
|
func (t Table) Build(gs snmpConnection, walk bool) (*RTable, error) {
|
||||||
rows := map[string]RTableRow{}
|
rows := map[string]RTableRow{}
|
||||||
|
|
||||||
|
//translation table for secondary index (when preforming join on two tables)
|
||||||
|
secIdxTab := make(map[string]string)
|
||||||
|
secGlobalOuterJoin := false
|
||||||
|
for i, f := range t.Fields {
|
||||||
|
if f.SecondaryIndexTable {
|
||||||
|
secGlobalOuterJoin = f.SecondaryOuterJoin
|
||||||
|
if i != 0 {
|
||||||
|
t.Fields[0], t.Fields[i] = t.Fields[i], t.Fields[0]
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
tagCount := 0
|
tagCount := 0
|
||||||
for _, f := range t.Fields {
|
for _, f := range t.Fields {
|
||||||
if f.IsTag {
|
if f.IsTag {
|
||||||
|
|
@ -519,6 +560,16 @@ func (t Table) Build(gs snmpConnection, walk bool) (*RTable, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for idx, v := range ifv {
|
for idx, v := range ifv {
|
||||||
|
if f.SecondaryIndexUse {
|
||||||
|
if newidx, ok := secIdxTab[idx]; ok {
|
||||||
|
idx = newidx
|
||||||
|
} else {
|
||||||
|
if !secGlobalOuterJoin && !f.SecondaryOuterJoin {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
idx = ".Secondary" + idx
|
||||||
|
}
|
||||||
|
}
|
||||||
rtr, ok := rows[idx]
|
rtr, ok := rows[idx]
|
||||||
if !ok {
|
if !ok {
|
||||||
rtr = RTableRow{}
|
rtr = RTableRow{}
|
||||||
|
|
@ -543,6 +594,20 @@ func (t Table) Build(gs snmpConnection, walk bool) (*RTable, error) {
|
||||||
} else {
|
} else {
|
||||||
rtr.Fields[f.Name] = v
|
rtr.Fields[f.Name] = v
|
||||||
}
|
}
|
||||||
|
if f.SecondaryIndexTable {
|
||||||
|
//indexes are stored here with prepending "." so we need to add them if needed
|
||||||
|
var vss string
|
||||||
|
if ok {
|
||||||
|
vss = "." + vs
|
||||||
|
} else {
|
||||||
|
vss = fmt.Sprintf(".%v", v)
|
||||||
|
}
|
||||||
|
if idx[0] == '.' {
|
||||||
|
secIdxTab[vss] = idx
|
||||||
|
} else {
|
||||||
|
secIdxTab[vss] = "." + idx
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,15 @@ var tsc = &testSNMPConnection{
|
||||||
".1.0.0.2.1.5.0.9.9": 11,
|
".1.0.0.2.1.5.0.9.9": 11,
|
||||||
".1.0.0.2.1.5.1.9.9": 22,
|
".1.0.0.2.1.5.1.9.9": 22,
|
||||||
".1.0.0.0.1.6.0": ".1.0.0.0.1.7",
|
".1.0.0.0.1.6.0": ".1.0.0.0.1.7",
|
||||||
|
".1.0.0.3.1.1.10": "instance",
|
||||||
|
".1.0.0.3.1.1.11": "instance2",
|
||||||
|
".1.0.0.3.1.1.12": "instance3",
|
||||||
|
".1.0.0.3.1.2.10": 10,
|
||||||
|
".1.0.0.3.1.2.11": 20,
|
||||||
|
".1.0.0.3.1.2.12": 20,
|
||||||
|
".1.0.0.3.1.3.10": 1,
|
||||||
|
".1.0.0.3.1.3.11": 2,
|
||||||
|
".1.0.0.3.1.3.12": 3,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -960,3 +969,242 @@ func TestSnmpTableCache_hit(t *testing.T) {
|
||||||
assert.Equal(t, []Field{{Name: "d"}}, fields)
|
assert.Equal(t, []Field{{Name: "d"}}, fields)
|
||||||
assert.Equal(t, fmt.Errorf("e"), err)
|
assert.Equal(t, fmt.Errorf("e"), err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTableJoin_walk(t *testing.T) {
|
||||||
|
tbl := Table{
|
||||||
|
Name: "mytable",
|
||||||
|
IndexAsTag: true,
|
||||||
|
Fields: []Field{
|
||||||
|
{
|
||||||
|
Name: "myfield1",
|
||||||
|
Oid: ".1.0.0.3.1.1",
|
||||||
|
IsTag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield2",
|
||||||
|
Oid: ".1.0.0.3.1.2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield3",
|
||||||
|
Oid: ".1.0.0.3.1.3",
|
||||||
|
SecondaryIndexTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield4",
|
||||||
|
Oid: ".1.0.0.0.1.1",
|
||||||
|
SecondaryIndexUse: true,
|
||||||
|
IsTag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield5",
|
||||||
|
Oid: ".1.0.0.0.1.2",
|
||||||
|
SecondaryIndexUse: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tb, err := tbl.Build(tsc, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, tb.Name, "mytable")
|
||||||
|
rtr1 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"myfield1": "instance",
|
||||||
|
"myfield4": "bar",
|
||||||
|
"index": "10",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield2": 10,
|
||||||
|
"myfield3": 1,
|
||||||
|
"myfield5": 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rtr2 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"myfield1": "instance2",
|
||||||
|
"index": "11",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield2": 20,
|
||||||
|
"myfield3": 2,
|
||||||
|
"myfield5": 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rtr3 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"myfield1": "instance3",
|
||||||
|
"index": "12",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield2": 20,
|
||||||
|
"myfield3": 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.Len(t, tb.Rows, 3)
|
||||||
|
assert.Contains(t, tb.Rows, rtr1)
|
||||||
|
assert.Contains(t, tb.Rows, rtr2)
|
||||||
|
assert.Contains(t, tb.Rows, rtr3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTableOuterJoin_walk(t *testing.T) {
|
||||||
|
tbl := Table{
|
||||||
|
Name: "mytable",
|
||||||
|
IndexAsTag: true,
|
||||||
|
Fields: []Field{
|
||||||
|
{
|
||||||
|
Name: "myfield1",
|
||||||
|
Oid: ".1.0.0.3.1.1",
|
||||||
|
IsTag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield2",
|
||||||
|
Oid: ".1.0.0.3.1.2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield3",
|
||||||
|
Oid: ".1.0.0.3.1.3",
|
||||||
|
SecondaryIndexTable: true,
|
||||||
|
SecondaryOuterJoin: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield4",
|
||||||
|
Oid: ".1.0.0.0.1.1",
|
||||||
|
SecondaryIndexUse: true,
|
||||||
|
IsTag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield5",
|
||||||
|
Oid: ".1.0.0.0.1.2",
|
||||||
|
SecondaryIndexUse: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tb, err := tbl.Build(tsc, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, tb.Name, "mytable")
|
||||||
|
rtr1 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"myfield1": "instance",
|
||||||
|
"myfield4": "bar",
|
||||||
|
"index": "10",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield2": 10,
|
||||||
|
"myfield3": 1,
|
||||||
|
"myfield5": 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rtr2 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"myfield1": "instance2",
|
||||||
|
"index": "11",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield2": 20,
|
||||||
|
"myfield3": 2,
|
||||||
|
"myfield5": 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rtr3 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"myfield1": "instance3",
|
||||||
|
"index": "12",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield2": 20,
|
||||||
|
"myfield3": 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rtr4 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"index": "Secondary.0",
|
||||||
|
"myfield4": "foo",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield5": 1,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.Len(t, tb.Rows, 4)
|
||||||
|
assert.Contains(t, tb.Rows, rtr1)
|
||||||
|
assert.Contains(t, tb.Rows, rtr2)
|
||||||
|
assert.Contains(t, tb.Rows, rtr3)
|
||||||
|
assert.Contains(t, tb.Rows, rtr4)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTableJoinNoIndexAsTag_walk(t *testing.T) {
|
||||||
|
tbl := Table{
|
||||||
|
Name: "mytable",
|
||||||
|
IndexAsTag: false,
|
||||||
|
Fields: []Field{
|
||||||
|
{
|
||||||
|
Name: "myfield1",
|
||||||
|
Oid: ".1.0.0.3.1.1",
|
||||||
|
IsTag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield2",
|
||||||
|
Oid: ".1.0.0.3.1.2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield3",
|
||||||
|
Oid: ".1.0.0.3.1.3",
|
||||||
|
SecondaryIndexTable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield4",
|
||||||
|
Oid: ".1.0.0.0.1.1",
|
||||||
|
SecondaryIndexUse: true,
|
||||||
|
IsTag: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "myfield5",
|
||||||
|
Oid: ".1.0.0.0.1.2",
|
||||||
|
SecondaryIndexUse: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
tb, err := tbl.Build(tsc, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, tb.Name, "mytable")
|
||||||
|
rtr1 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"myfield1": "instance",
|
||||||
|
"myfield4": "bar",
|
||||||
|
//"index": "10",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield2": 10,
|
||||||
|
"myfield3": 1,
|
||||||
|
"myfield5": 2,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rtr2 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"myfield1": "instance2",
|
||||||
|
//"index": "11",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield2": 20,
|
||||||
|
"myfield3": 2,
|
||||||
|
"myfield5": 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
rtr3 := RTableRow{
|
||||||
|
Tags: map[string]string{
|
||||||
|
"myfield1": "instance3",
|
||||||
|
//"index": "12",
|
||||||
|
},
|
||||||
|
Fields: map[string]interface{}{
|
||||||
|
"myfield2": 20,
|
||||||
|
"myfield3": 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.Len(t, tb.Rows, 3)
|
||||||
|
assert.Contains(t, tb.Rows, rtr1)
|
||||||
|
assert.Contains(t, tb.Rows, rtr2)
|
||||||
|
assert.Contains(t, tb.Rows, rtr3)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -55,4 +55,43 @@ hostname OBJECT-TYPE
|
||||||
STATUS current
|
STATUS current
|
||||||
::= { testOID 1 1 }
|
::= { testOID 1 1 }
|
||||||
|
|
||||||
|
testSecondaryTable OBJECT-TYPE
|
||||||
|
SYNTAX SEQUENCE OF testSecondaryTableEntry
|
||||||
|
MAX-ACCESS not-accessible
|
||||||
|
STATUS current
|
||||||
|
::= { testOID 3 }
|
||||||
|
|
||||||
|
testSecondaryTableEntry OBJECT-TYPE
|
||||||
|
SYNTAX TestSecondaryTableEntry
|
||||||
|
MAX-ACCESS not-accessible
|
||||||
|
STATUS current
|
||||||
|
INDEX {
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
::= { testSecondaryTable 1 }
|
||||||
|
|
||||||
|
TestSecondaryTableEntry ::=
|
||||||
|
SEQUENCE {
|
||||||
|
instance OCTET STRING,
|
||||||
|
connections INTEGER,
|
||||||
|
testTableIndex INTEGER,
|
||||||
|
}
|
||||||
|
|
||||||
|
instance OBJECT-TYPE
|
||||||
|
SYNTAX OCTET STRING
|
||||||
|
MAX-ACCESS read-only
|
||||||
|
STATUS current
|
||||||
|
::= { testSecondaryTableEntry 1 }
|
||||||
|
|
||||||
|
connections OBJECT-TYPE
|
||||||
|
SYNTAX OCTET STRING
|
||||||
|
MAX-ACCESS read-only
|
||||||
|
STATUS current
|
||||||
|
::= { testSecondaryTableEntry 2 }
|
||||||
|
|
||||||
|
testTableIndex OBJECT-TYPE
|
||||||
|
SYNTAX OCTET STRING
|
||||||
|
MAX-ACCESS read-only
|
||||||
|
STATUS current
|
||||||
|
::= { testSecondaryTableEntry 3 }
|
||||||
END
|
END
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue