aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVladimir Lavor <vlavor@cisco.com>2021-05-13 11:15:48 +0200
committerVladimir Lavor <vlavor@cisco.com>2021-05-26 09:53:08 +0200
commitba6e92d715c59dc71c4e18e66b262d07578d524b (patch)
treecfea5f53750b52d3eccf7bdb1947936dd7e31ee1
parentc380ee6064379258768fdfe4e9d4ad9138980ec0 (diff)
statsclient: added symlinks
Symlink support based on https://gerrit.fd.io/r/c/vpp/+/31636 Added new stat types `CounterStat` and `CombinedCounterStat` which represent simple/combined value of a single item (interface, node) as a array of values by workers. Example: /if/names NameVector [local0 tap0 tap1] /if/ip6 SimpleCounterVector [[0 9 0] [0 25 3] [0 0 60] [0 0 0]] /interfaces/tap0/ip6 SimpleCounterVector [[9] [25] [0] [0]] /interfaces/tap1/ip6 SimpleCounterVector [[0] [3] [60] [0]] Field `Symlink` added to StatEntry to mark symlink stats. For stats v2 only Change-Id: Iadc825f3c42b05bfc9a91462dba8a0b0068c7cd6 Signed-off-by: Vladimir Lavor <vlavor@cisco.com>
-rw-r--r--adapter/stats_api.go44
-rw-r--r--adapter/statsclient/stat_segment_api.go7
-rw-r--r--adapter/statsclient/statsclient.go6
-rw-r--r--adapter/statsclient/statseg_v1.go5
-rw-r--r--adapter/statsclient/statseg_v2.go80
-rw-r--r--adapter/vppapiclient/stat_client.go10
6 files changed, 127 insertions, 25 deletions
diff --git a/adapter/stats_api.go b/adapter/stats_api.go
index 5b19173..1319d71 100644
--- a/adapter/stats_api.go
+++ b/adapter/stats_api.go
@@ -63,6 +63,7 @@ const (
ErrorIndex StatType = 4
NameVector StatType = 5
Empty StatType = 6
+ Symlink StatType = 7
)
func (d StatType) String() string {
@@ -79,6 +80,8 @@ func (d StatType) String() string {
return "NameVector"
case Empty:
return "Empty"
+ case Symlink:
+ return "Symlink"
}
return fmt.Sprintf("UnknownStatType(%d)", d)
}
@@ -99,8 +102,9 @@ type StatIdentifier struct {
// is defined by Type.
type StatEntry struct {
StatIdentifier
- Type StatType
- Data Stat
+ Type StatType
+ Data Stat
+ Symlink bool
}
// Counter represents simple counter with single value, which is usually packet count.
@@ -129,6 +133,9 @@ type Stat interface {
// IsZero returns true if all of its values equal to zero.
IsZero() bool
+ // Type returns underlying type of a stat
+ Type() StatType
+
// isStat is intentionally unexported to limit implementations of interface to this package,
isStat()
}
@@ -139,13 +146,13 @@ type ScalarStat float64
// ErrorStat represents stat for ErrorIndex. The array represents workers.
type ErrorStat []Counter
-// SimpleCounterStat represents stat for SimpleCounterVector.
+// SimpleCounterStat represents indexed stat for SimpleCounterVector.
// The outer array represents workers and the inner array represents interface/node/.. indexes.
// Values should be aggregated per interface/node for every worker.
// ReduceSimpleCounterStatIndex can be used to reduce specific index.
type SimpleCounterStat [][]Counter
-// CombinedCounterStat represents stat for CombinedCounterVector.
+// CombinedCounterStat represents indexed stat for CombinedCounterVector.
// The outer array represents workers and the inner array represents interface/node/.. indexes.
// Values should be aggregated per interface/node for every worker.
// ReduceCombinedCounterStatIndex can be used to reduce specific index.
@@ -167,6 +174,11 @@ func (EmptyStat) isStat() {}
func (s ScalarStat) IsZero() bool {
return s == 0
}
+
+func (s ScalarStat) Type() StatType {
+ return ScalarIndex
+}
+
func (s ErrorStat) IsZero() bool {
if s == nil {
return true
@@ -178,6 +190,11 @@ func (s ErrorStat) IsZero() bool {
}
return true
}
+
+func (s ErrorStat) Type() StatType {
+ return ErrorIndex
+}
+
func (s SimpleCounterStat) IsZero() bool {
if s == nil {
return true
@@ -191,6 +208,11 @@ func (s SimpleCounterStat) IsZero() bool {
}
return true
}
+
+func (s SimpleCounterStat) Type() StatType {
+ return SimpleCounterVector
+}
+
func (s CombinedCounterStat) IsZero() bool {
if s == nil {
return true
@@ -207,6 +229,11 @@ func (s CombinedCounterStat) IsZero() bool {
}
return true
}
+
+func (s CombinedCounterStat) Type() StatType {
+ return CombinedCounterVector
+}
+
func (s NameStat) IsZero() bool {
if s == nil {
return true
@@ -218,10 +245,19 @@ func (s NameStat) IsZero() bool {
}
return true
}
+
+func (s NameStat) Type() StatType {
+ return NameVector
+}
+
func (s EmptyStat) IsZero() bool {
return true
}
+func (s EmptyStat) Type() StatType {
+ return Empty
+}
+
// ReduceSimpleCounterStatIndex returns reduced SimpleCounterStat s for index i.
func ReduceSimpleCounterStatIndex(s SimpleCounterStat, i int) uint64 {
var val uint64
diff --git a/adapter/statsclient/stat_segment_api.go b/adapter/statsclient/stat_segment_api.go
index 23755a5..2161e6e 100644
--- a/adapter/statsclient/stat_segment_api.go
+++ b/adapter/statsclient/stat_segment_api.go
@@ -46,6 +46,7 @@ const (
statDirErrorIndex = 4
statDirNameVector = 5
statDirEmpty = 6
+ statDirSymlink = 7
)
type (
@@ -75,8 +76,10 @@ type statSegment interface {
GetEpoch() (int64, bool)
// CopyEntryData accepts pointer to a directory segment and returns adapter.Stat
- // based on directory type populated with data
- CopyEntryData(segment dirSegment) adapter.Stat
+ // based on directory type populated with data. The index is an optional parameter
+ // (used by symlinks) returning stats for item on the given index only.
+ // Use ^uint32(0) as an empty index (since 0 is a valid value).
+ CopyEntryData(segment dirSegment, index uint32) adapter.Stat
// UpdateEntryData accepts pointer to a directory segment with data, and stat
// segment to update
diff --git a/adapter/statsclient/statsclient.go b/adapter/statsclient/statsclient.go
index 16af16a..0b16a77 100644
--- a/adapter/statsclient/statsclient.go
+++ b/adapter/statsclient/statsclient.go
@@ -505,13 +505,15 @@ func (sc *StatsClient) getStatEntriesOnIndex(vector dirVector, indexes ...uint32
if len(dirName) == 0 {
return
}
+ d := sc.CopyEntryData(dirPtr, ^uint32(0))
entries = append(entries, adapter.StatEntry{
StatIdentifier: adapter.StatIdentifier{
Index: index,
Name: dirName,
},
- Type: adapter.StatType(dirType),
- Data: sc.CopyEntryData(dirPtr),
+ Type: d.Type(),
+ Data: d,
+ Symlink: adapter.StatType(dirType) == adapter.Symlink,
})
}
return entries, nil
diff --git a/adapter/statsclient/statseg_v1.go b/adapter/statsclient/statseg_v1.go
index e9e8331..efd487e 100644
--- a/adapter/statsclient/statseg_v1.go
+++ b/adapter/statsclient/statseg_v1.go
@@ -93,7 +93,7 @@ func (ss *statSegmentV1) GetEpoch() (int64, bool) {
return sh.epoch, sh.inProgress != 0
}
-func (ss *statSegmentV1) CopyEntryData(segment dirSegment) adapter.Stat {
+func (ss *statSegmentV1) CopyEntryData(segment dirSegment, _ uint32) adapter.Stat {
dirEntry := (*statSegDirectoryEntryV1)(segment)
dirType := adapter.StatType(dirEntry.directoryType)
@@ -214,6 +214,9 @@ func (ss *statSegmentV1) CopyEntryData(segment dirSegment) adapter.Stat {
case statDirEmpty:
// no-op
+ case statDirSymlink:
+ debugf("Symlinks are not supported for stats v1")
+
default:
// TODO: monitor occurrences with metrics
debugf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
diff --git a/adapter/statsclient/statseg_v2.go b/adapter/statsclient/statseg_v2.go
index 10bc5f5..16f1729 100644
--- a/adapter/statsclient/statseg_v2.go
+++ b/adapter/statsclient/statseg_v2.go
@@ -15,6 +15,8 @@
package statsclient
import (
+ "bytes"
+ "encoding/binary"
"sync/atomic"
"unsafe"
@@ -38,6 +40,7 @@ type sharedHeaderV2 struct {
type statSegDirectoryEntryV2 struct {
directoryType dirType
// unionData can represent:
+ // - symlink indexes
// - index
// - value
// - pointer to data
@@ -87,7 +90,7 @@ func (ss *statSegmentV2) GetEpoch() (int64, bool) {
return sh.epoch, sh.inProgress != 0
}
-func (ss *statSegmentV2) CopyEntryData(segment dirSegment) adapter.Stat {
+func (ss *statSegmentV2) CopyEntryData(segment dirSegment, index uint32) adapter.Stat {
dirEntry := (*statSegDirectoryEntryV2)(segment)
if dirEntry.unionData == 0 {
debugf("data value or pointer not defined for %s", dirEntry.name)
@@ -135,11 +138,21 @@ func (ss *statSegmentV2) CopyEntryData(segment dirSegment) adapter.Stat {
continue
}
counterVectorLength := *(*uint32)(vectorLen(counterVector))
- data[i] = make([]adapter.Counter, counterVectorLength)
- for j := uint32(0); j < counterVectorLength; j++ {
- offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
- val := *(*adapter.Counter)(statSegPointer(counterVector, offset))
- data[i][j] = val
+ if index == ^uint32(0) {
+ data[i] = make([]adapter.Counter, counterVectorLength)
+ for j := uint32(0); j < counterVectorLength; j++ {
+ offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
+ data[i][j] = *(*adapter.Counter)(statSegPointer(counterVector, offset))
+ }
+ } else {
+ data[i] = make([]adapter.Counter, 1) // expect single value
+ for j := uint32(0); j < counterVectorLength; j++ {
+ offset := uintptr(j) * unsafe.Sizeof(adapter.Counter(0))
+ if index == j {
+ data[i][0] = *(*adapter.Counter)(statSegPointer(counterVector, offset))
+ break
+ }
+ }
}
}
return adapter.SimpleCounterStat(data)
@@ -160,11 +173,21 @@ func (ss *statSegmentV2) CopyEntryData(segment dirSegment) adapter.Stat {
continue
}
counterVectorLength := *(*uint32)(vectorLen(counterVector))
- data[i] = make([]adapter.CombinedCounter, counterVectorLength)
- for j := uint32(0); j < counterVectorLength; j++ {
- offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
- val := *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
- data[i][j] = val
+ if index == ^uint32(0) {
+ data[i] = make([]adapter.CombinedCounter, counterVectorLength)
+ for j := uint32(0); j < counterVectorLength; j++ {
+ offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
+ data[i][j] = *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
+ }
+ } else {
+ data[i] = make([]adapter.CombinedCounter, 1) // expect single value pair
+ for j := uint32(0); j < counterVectorLength; j++ {
+ offset := uintptr(j) * unsafe.Sizeof(adapter.CombinedCounter{})
+ if index == j {
+ data[i][0] = *(*adapter.CombinedCounter)(statSegPointer(counterVector, offset))
+ break
+ }
+ }
}
}
return adapter.CombinedCounterStat(data)
@@ -205,6 +228,22 @@ func (ss *statSegmentV2) CopyEntryData(segment dirSegment) adapter.Stat {
return adapter.EmptyStat("<none>")
// no-op
+ case statDirSymlink:
+ // prevent recursion loops
+ if index != ^uint32(0) {
+ debugf("received symlink with defined item index")
+ return nil
+ }
+ i1, i2 := ss.getSymlinkIndexes(dirEntry)
+ // use first index to get the stats directory the symlink points to
+ header := ss.loadSharedHeader(ss.sharedHeader)
+ dirVector := ss.adjust(dirVector(&header.dirVector))
+ statSegDir2 := dirSegment(uintptr(dirVector) + uintptr(i1)*unsafe.Sizeof(statSegDirectoryEntryV2{}))
+
+ // retry with actual stats segment and use second index to get
+ // stats for the required item
+ return ss.CopyEntryData(statSegDir2, i2)
+
default:
// TODO: monitor occurrences with metrics
debugf("Unknown type %d for stat entry: %q", dirEntry.directoryType, dirEntry.name)
@@ -349,3 +388,22 @@ func (ss *statSegmentV2) getErrorVector() dirVector {
header := ss.loadSharedHeader(ss.sharedHeader)
return ss.adjust(dirVector(&header.errorVector))
}
+
+func (ss *statSegmentV2) getSymlinkIndexes(dirEntry *statSegDirectoryEntryV2) (index1, index2 uint32) {
+ var b bytes.Buffer
+ if err := binary.Write(&b, binary.LittleEndian, dirEntry.unionData); err != nil {
+ debugf("error getting symlink indexes for %s: %v", dirEntry.name, err)
+ return
+ }
+ if len(b.Bytes()) != 8 {
+ debugf("incorrect symlink union data length for %s: expected 8, got %d", dirEntry.name, len(b.Bytes()))
+ return
+ }
+ for i := range b.Bytes()[:4] {
+ index1 += uint32(b.Bytes()[i]) << (uint32(i) * 8)
+ }
+ for i := range b.Bytes()[4:] {
+ index2 += uint32(b.Bytes()[i+4]) << (uint32(i) * 8)
+ }
+ return
+}
diff --git a/adapter/vppapiclient/stat_client.go b/adapter/vppapiclient/stat_client.go
index 734c158..9de2028 100644
--- a/adapter/vppapiclient/stat_client.go
+++ b/adapter/vppapiclient/stat_client.go
@@ -153,10 +153,10 @@ func (c *statClient) DumpStats(patterns ...string) (stats []adapter.StatEntry, e
vector := make([][]adapter.CombinedCounter, length)
for k := 0; k < length; k++ {
for j := 0; j < int(C.govpp_stat_segment_vec_len(unsafe.Pointer(C.govpp_stat_segment_data_get_combined_counter_index(&v, C.int(k))))); j++ {
- vector[k] = append(vector[k], adapter.CombinedCounter([2]uint64{
+ vector[k] = append(vector[k], [2]uint64{
uint64(C.govpp_stat_segment_data_get_combined_counter_index_packets(&v, C.int(k), C.int(j))),
uint64(C.govpp_stat_segment_data_get_combined_counter_index_bytes(&v, C.int(k), C.int(j))),
- }))
+ })
}
}
stat.Data = adapter.CombinedCounterStat(vector)
@@ -186,15 +186,15 @@ func (c *statClient) DumpStats(patterns ...string) (stats []adapter.StatEntry, e
return stats, nil
}
-func (c *statClient) PrepareDir(prefixes ...string) (*adapter.StatDir, error) {
+func (c *statClient) PrepareDir(_ ...string) (*adapter.StatDir, error) {
return nil, adapter.ErrNotImplemented
}
-func (c *statClient) PrepareDirOnIndex(indexes ...uint32) (*adapter.StatDir, error) {
+func (c *statClient) PrepareDirOnIndex(_ ...uint32) (*adapter.StatDir, error) {
return nil, adapter.ErrNotImplemented
}
-func (c *statClient) UpdateDir(dir *adapter.StatDir) error {
+func (c *statClient) UpdateDir(_ *adapter.StatDir) error {
return adapter.ErrNotImplemented
}