Skip to content

Commit

Permalink
cmd/scollector: Additional functionality into ExtraHop collector, for…
Browse files Browse the repository at this point in the history
… specifying additional keyed metrics to be collected (#1698)
  • Loading branch information
mhenderson-so authored and kylebrandt committed May 13, 2016
1 parent 951291e commit 3fd1015
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 28 deletions.
82 changes: 71 additions & 11 deletions cmd/scollector/collectors/extrahop.go
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
Expand Up @@ -194,10 +194,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
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
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.

0 comments on commit 3fd1015

Please sign in to comment.