Skip to content
This repository was archived by the owner on Feb 13, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 71 additions & 11 deletions cmd/scollector/collectors/extrahop.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,19 @@ import (

const extraHopIntervalSeconds int = 30

var extraHopFilterProtoBy string //What to filter the traffic by. Valid values are "namedprotocols", "toppercent" or "none"
var extraHopTopProtoPerc int //Only log the top % of protocols by volume
var extraHopOtherProtoName string //What name to log the "other" data under.
var extraHopL7Description string //What to append to the end of the L7 description metadata to explain what is and isn't filtered out
var extraHopFilterProtoBy string //What to filter the traffic by. Valid values are "namedprotocols", "toppercent" or "none"
var extraHopTopProtoPerc int //Only log the top % of protocols by volume
var extraHopOtherProtoName string //What name to log the "other" data under.
var extraHopL7Description string //What to append to the end of the L7 description metadata to explain what is and isn't filtered out
var extraHopAdditionalMetrics []string //Other metrics to fetch from Extrahop

//Register a collector for ExtraHop
func ExtraHop(host, apikey, filterby string, filterpercent int) error {
// ExtraHop collection registration
func ExtraHop(host, apikey, filterby string, filterpercent int, customMetrics []string) error {
if host == "" || apikey == "" {
return fmt.Errorf("Empty host or API key for ExtraHop.")
}

extraHopAdditionalMetrics = customMetrics
extraHopFilterProtoBy = filterby
switch filterby { //Set up options
case "toppercent":
Expand Down Expand Up @@ -69,14 +72,50 @@ func c_extrahop(host, apikey string) (opentsdb.MultiDataPoint, error) {
if err := extraHopNetworks(c, &md); err != nil {
return nil, err
}
if err := extraHopGetAdditionalMetrics(c, &md); err != nil {
return nil, err
}

return md, nil
}

/*
This grabs the complex metrics of the L7 traffic from ExtraHop. It is a complex type because the data is not just a simple time series,
the data needs to be tagged with vlan, protocol, etc. We can do the network and vlan tagging ourselves, but the protocol tagging comes
from ExtraHop itself.
*/
func extraHopGetAdditionalMetrics(c *gohop.Client, md *opentsdb.MultiDataPoint) error {
for _, v := range extraHopAdditionalMetrics {
metric, err := gohop.StoEHMetric(v)
if err != nil {
return err
}
ms := []gohop.MetricSpec{ //Build a metric spec to tell ExtraHop what we want to pull out.
{Name: metric.MetricSpecName, CalcType: metric.MetricSpecCalcType, KeyPair: gohop.KeyPair{Key1Regex: "", Key2Regex: "", OpenTSDBKey1: "proto", Key2OpenTSDBKey2: ""}, OpenTSDBMetric: ehMetricNameEscape(v)},
}
mrk, err := c.KeyedMetricQuery(gohop.Cycle30Sec, metric.MetricCategory, metric.ObjectType, -60000, 0, ms, []int64{metric.ObjectId})
if err != nil {
return err
}
for _, a := range mrk.Stats {
for _, b := range a.Values {
for _, d := range b {
if d.Vtype == "tset" {
for _, e := range d.Tset {
*md = append(*md, &opentsdb.DataPoint{
Metric: ehMetricNameEscape(d.Key.Str),
Timestamp: a.Time,
Value: e.Value,
Tags: ehItemNameToTagSet(c, e.Key.Str),
})
}
}
}
}
}
}

return nil
}

// extraHopNetworks grabs the complex metrics of the L7 traffic from ExtraHop. It is a complex type because the data is not just a simple time series,
// the data needs to be tagged with vlan, protocol, etc. We can do the network and vlan tagging ourselves, but the protocol tagging comes
// from ExtraHop itself.
func extraHopNetworks(c *gohop.Client, md *opentsdb.MultiDataPoint) error {
nl, err := c.GetNetworkList(true) //Fetch the network list from ExtraHop, and include VLAN information
if err != nil {
Expand Down Expand Up @@ -170,3 +209,24 @@ func calculateDataCutoff(k gohop.MetricResponseKeyed) map[int64]int64 {
}
return rets
}

func ehItemNameToTagSet(c *gohop.Client, ehName string) opentsdb.TagSet {
thisTagSet := opentsdb.TagSet{"host": strings.ToLower(c.APIUrl.Host)}
if strings.IndexAny(ehName, ",") == 0 {
return thisTagSet
}
nameParts := strings.Split(ehName, ",")
for _, p := range nameParts {
tagParts := strings.Split(p, "=")
if len(tagParts) > 0 {
thisTagSet[tagParts[0]] = tagParts[1]
}
}
return thisTagSet
}

func ehMetricNameEscape(metricName string) string {
metricName = strings.ToLower(metricName)
metricName = strings.Replace(metricName, " ", "_", -1)
return fmt.Sprintf("extrahop.application.%v", metricName)
}
9 changes: 5 additions & 4 deletions cmd/scollector/conf/conf.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,11 @@ type RedisCounters struct {
}

type ExtraHop struct {
Host string
APIKey string
FilterBy string
FilterPercent int
Host string
APIKey string
FilterBy string
FilterPercent int
AdditionalMetrics []string
}

type TagOverride struct {
Expand Down
22 changes: 22 additions & 0 deletions cmd/scollector/doc.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,11 +270,33 @@ accounts for less than 10% of the traffic, it will be dropped. This is OK if you
heavilly dominated by asmall set of protocols, but if you have a fairly even spread of protocols
then this filtering loses its usefulness.

AdditionalMetrics is formatted as such: [object_type].[object_id].[metric_category].[metric_spec_name]

- object_type: is one of: "network", "device", "application", "vlan", "device_group", "activity_group"
- object_id: can be found by querying the ExtraHop API (through the API Explorer) under the endpoint
for the object type. For example, for "application", you would query the "/applications/"
endpoint and locate the ID of the application you want to query.
- metric_category: can be found in the Metric Catalogue for the metric you are wanting to query. e.g. for
custom metrics, this is always "custom_detail"
- metric_spec_name: can be found in the Metric Catalogue for the metric you are wanting to query. e.g. for
custom metrics, this is name you have specified in metricAddDetailCount() function in
a trigger.

For these additional metrics, it is expected that the key for the metric is in a keyvalue, comma seperated pair.
This key will be converted into an OpenTSDB tagset. For example, if you have a key of
"client=192.168.0.1,server=192.168.0.9,port=21441", this will be converted into an OpenTSDB tagset of the same
values.

CAUTION: Do not include unbounded values in your key if you can help it. Putting in something like client IP, or
source/destination port, which are out of your control and specified by people external to your network, could
end up putting millions of different keys into your Bosun instance - something you probably don't want.

[[ExtraHop]]
Host = "extrahop01"
APIkey = "abcdef1234567890"
FilterBy = "toppercent"
FilterPercent = 75
AdditionalMetrics = [ "application.12.custom_detail.my trigger metric" ]

LocalListener (string): local_listener will listen for HTTP request and forward
the request to the configured OpenTSDB host while adding defined tags to
Expand Down
2 changes: 1 addition & 1 deletion cmd/scollector/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ func main() {
}

for _, x := range conf.ExtraHop {
check(collectors.ExtraHop(x.Host, x.APIKey, x.FilterBy, x.FilterPercent))
check(collectors.ExtraHop(x.Host, x.APIKey, x.FilterBy, x.FilterPercent, x.AdditionalMetrics))
}

if err != nil {
Expand Down
51 changes: 39 additions & 12 deletions vendor/github.com/kylebrandt/gohop/gohop.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.