// assignMetricMeta assigs metadata to metric using predefined metadata slice func assignMetricMeta(mt *plugin.MetricType, allMetrics []metric) { for _, metricMeta := range allMetrics { if matchSlice(strings.Split(metricMeta.ns, "/"), strings.Split(mt.Namespace().String(), "/")) { mt.Description_ = metricMeta.description break } } }
func TestConfigItems(t *testing.T) { names := []string{"dummy_string", "dummy_bool", "dummy_int", "dummy_float"} Convey("Get values of configuration items with no error", t, func() { Convey("Source: global config", func() { //create global config and add item (different types) cfg := plugin.NewPluginConfigType() cfg.AddItem("dummy_string", ctypes.ConfigValueStr{Value: dummy_str}) cfg.AddItem("dummy_bool", ctypes.ConfigValueBool{Value: dummy_bool}) cfg.AddItem("dummy_int", ctypes.ConfigValueInt{Value: dummy_int}) cfg.AddItem("dummy_float", ctypes.ConfigValueFloat{Value: dummy_float}) result, err := GetConfigItems(cfg, names...) So(err, ShouldBeNil) for _, name := range names { So(result[name], ShouldNotBeEmpty) } }) Convey("Source: metrics config", func() { // create metrics config config := cdata.NewNode() config.AddItem("dummy_string", ctypes.ConfigValueStr{Value: dummy_str}) config.AddItem("dummy_bool", ctypes.ConfigValueBool{Value: dummy_bool}) config.AddItem("dummy_int", ctypes.ConfigValueInt{Value: dummy_int}) config.AddItem("dummy_float", ctypes.ConfigValueFloat{Value: dummy_float}) // create metric and set config metric := plugin.MetricType{} metric.Config_ = config result, err := GetConfigItems(metric, names...) So(err, ShouldBeNil) for _, name := range names { So(result[name], ShouldNotBeEmpty) } }) }) Convey("Try to get values of items from invalid config (unsupported type)", t, func() { invalid_cfg := []string{"invalid", "config", "source"} result, err := GetConfigItems(invalid_cfg, names...) So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) }
// GetMetricTypes returns list of available metric types // It returns error in case retrieval was not successful func (p *Plugin) GetMetricTypes(cfg plugin.ConfigType) ([]plugin.MetricType, error) { if !p.initialized { if err := p.init(cfg.Table()); err != nil { return nil, err } } if err := getStats(p.proc_path, p.stats, p.prevMetricsSum, p.cpuMetricsNumber, p.snapMetricsNames, p.procStatMetricsNames); err != nil { return nil, err } metricTypes := []plugin.MetricType{} namespaces := []string{} prefix := filepath.Join(vendor, fs, pluginName) for cpu, stats := range p.stats { for metric, _ := range stats { namespaces = append(namespaces, prefix+"/"+cpu+"/"+metric) } } // List of terminal metric names mList := make(map[string]bool) for _, namespace := range namespaces { namespace = strings.TrimRight(namespace, string(os.PathSeparator)) metricType := plugin.MetricType{ Namespace_: core.NewNamespace(strings.Split(namespace, string(os.PathSeparator))...)} ns := metricType.Namespace() // CPU metric (aka last element in namespace) mItem := ns[len(ns)-1] // Keep it if not already seen before if !mList[mItem.Value] { mList[mItem.Value] = true metricTypes = append(metricTypes, plugin.MetricType{ Namespace_: core.NewNamespace(strings.Split(prefix, string(os.PathSeparator))...). AddDynamicElement("cpuID", "ID of CPU ('all' for aggregate)"). AddStaticElement(mItem.Value), Description_: "dynamic CPU metric: " + mItem.Value, }) } } return metricTypes, nil }
func createEvent(m plugin.MetricType, config map[string]ctypes.ConfigValue) *raidman.Event { return &raidman.Event{ Host: m.Tags()[core.STD_TAG_PLUGIN_RUNNING_ON], Service: m.Namespace().String(), Metric: m.Data(), } }
func TestGetMetricConfigItem(t *testing.T) { Convey("Get a value of items from Metrics Config with no error", t, func() { // create config config := cdata.NewNode() config.AddItem("dummy_string", ctypes.ConfigValueStr{Value: dummy_str}) config.AddItem("dummy_bool", ctypes.ConfigValueBool{Value: dummy_bool}) config.AddItem("dummy_int", ctypes.ConfigValueInt{Value: dummy_int}) config.AddItem("dummy_float", ctypes.ConfigValueFloat{Value: dummy_float}) // create metric and set config metric := plugin.MetricType{} metric.Config_ = config Convey("string type of item", func() { result, err := GetMetricConfigItem(metric, "dummy_string") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_str) }) Convey("bool type of item", func() { result, err := GetMetricConfigItem(metric, "dummy_bool") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_bool) }) Convey("int type of item", func() { result, err := GetMetricConfigItem(metric, "dummy_int") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_int) }) Convey("float type of item", func() { result, err := GetMetricConfigItem(metric, "dummy_float") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_float) }) }) Convey("Try to get a value of items not defined in Metrics Config", t, func() { config := cdata.NewNode() config.AddItem("foo", ctypes.ConfigValueStr{Value: "foo_val"}) metric := plugin.MetricType{} metric.Config_ = config result, err := GetMetricConfigItem(metric, "foo_not_exist") So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) Convey("No item defined in Metrics Config", t, func() { metric := plugin.MetricType{} metric.Config_ = cdata.NewNode() result, err := GetMetricConfigItem(metric, "foo") So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) }
// GetMetricConfigItem returns value of configuration item specified by `name` defined in Metrics Config // Notes: GetMetricConfigItem() will be helpful to access and get configuration item's value in CollectMetrics() // (Plugin Global Config is merged into Metric Config) func GetMetricConfigItem(metric plugin.MetricType, name string) (interface{}, error) { if metric.Config() != nil && len(metric.Config().Table()) > 0 { if item, ok := metric.Config().Table()[name]; ok { return getConfigItemValue(item) } } return nil, fmt.Errorf("Cannot find %v in Metrics Config", name) }
func TestGetMetricConfigItems(t *testing.T) { Convey("Get values of items from Metrics Config with no error", t, func() { // create config config := cdata.NewNode() config.AddItem("dummy_string", ctypes.ConfigValueStr{Value: dummy_str}) config.AddItem("dummy_bool", ctypes.ConfigValueBool{Value: dummy_bool}) config.AddItem("dummy_int", ctypes.ConfigValueInt{Value: dummy_int}) config.AddItem("dummy_float", ctypes.ConfigValueFloat{Value: dummy_float}) // create metric and set config metric := plugin.MetricType{} metric.Config_ = config names := []string{"dummy_string", "dummy_bool", "dummy_int", "dummy_float"} result, err := GetMetricConfigItems(metric, names) So(err, ShouldBeNil) for _, name := range names { So(result[name], ShouldNotBeEmpty) } }) Convey("Try to get values of items not defined in Metrics config", t, func() { config := cdata.NewNode() config.AddItem("foo", ctypes.ConfigValueStr{Value: "foo_val"}) metric := plugin.MetricType{} metric.Config_ = config names := []string{"foo1", "foo2"} result, err := GetMetricConfigItems(metric, names) So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) Convey("No item defined in Metrics Config", t, func() { metric := plugin.MetricType{} metric.Config_ = cdata.NewNode() names := []string{"foo", "bar"} result, err := GetMetricConfigItems(metric, names) So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) }
// GetMetricConfigItems returns map to values of multiple configuration items defined in Metric Config and specified in 'names' slice // Notes: GetMetricConfigItems() will be helpful to access and get multiple configuration items' values in CollectMetrics() // (Plugin Global Config is merged into Metric Config) func GetMetricConfigItems(metric plugin.MetricType, names []string) (map[string]interface{}, error) { result := make(map[string]interface{}) for _, name := range names { if metric.Config() != nil && len(metric.Config().Table()) > 0 { item, ok := metric.Config().Table()[name] if !ok { return nil, fmt.Errorf("Cannot find %v in Metrics Config", name) } val, err := getConfigItemValue(item) if err != nil { return nil, err } result[name] = val } else { return nil, fmt.Errorf("Cannot find %v in Metrics Config", name) } } return result, nil }
func sendEvent(orgId int64, m *plugin.MetricType) { ns := m.Namespace().Strings() if len(ns) != 4 { log.Printf("Error: invalid event metric. Expected namesapce to be 4 fields.") return } if ns[0] != "worldping" || ns[1] != "event" { log.Printf("Error: invalid event metrics. Metrics hould begin with 'worldping.event'") return } hostname, _ := os.Hostname() id := time.Now().UnixNano() event := &schema.ProbeEvent{ OrgId: orgId, EventType: ns[2], Severity: ns[3], Source: hostname, Timestamp: id / int64(time.Millisecond), Message: m.Data().(string), Tags: m.Tags(), } body, err := msg.CreateProbeEventMsg(event, id, msg.FormatProbeEventMsgp) if err != nil { log.Printf("Error: unable to convert event to ProbeEventMsgp. %s", err) return } sent := false for !sent { if err = PostData("events", Token, body); err != nil { log.Printf("Error: %s", err) time.Sleep(time.Second) } else { sent = true } } }
// function which fills all part of Heka message func setHekaMessageFields(m plugin.MetricType, msg *message.Message) error { mName := make([]string, 0, len(m.Namespace())) var dimField *message.Field var err error // Loop on namespace elements for _, elt := range m.Namespace() { logger.WithField("_block", "setHekaMessageFields").Debug( fmt.Sprintf("Namespace %#+v", elt)) // Dynamic element is not inserted in metric name // but rather added to dimension field if elt.IsDynamic() { dimField, err = addToDimensions(dimField, elt.Name) if err != nil { logger.WithField("_block", "setHekaMessageFields").Error(err) return err } addField(elt.Name, elt.Value, msg) } else { // Static element is concatenated to metric name mName = append(mName, elt.Value) } } // Processing of tags if len(m.Tags()) > 0 { for tag, value := range m.Tags() { logger.WithField("_block", "setHekaMessageFields").Debug( fmt.Sprintf("Adding tag=%s value=%s", tag, value)) dimField, err = addToDimensions(dimField, tag) if err != nil { logger.WithField("_block", "setHekaMessageFields").Error(err) return err } addField(tag, value, msg) } } if dimField != nil { msg.AddField(dimField) } // Handle metric name metricName := strings.Join(mName, ".") // TODO protect access using mutex // for potential race conditions logger.WithField("_block", "setHekaMessageFields").Debug( fmt.Sprintf("Checking metric=%s", metricName)) // Is mapping already stored if val, ok := MetricMappings[metricName]; ok { logger.WithField("_block", "setHekaMessageFields").Debug( fmt.Sprintf("Metric=%s in cache %s", metricName, val)) metricName = val } else { oldMetricName := metricName logger.WithField("_block", "setHekaMessageFields").Debug( fmt.Sprintf("Metric=%s not in cache", metricName)) // Namespace handling for kmapping, vmapping := range globalMappings.Namespace { logger.WithField("_block", "setHekaMessageFields").Debug( fmt.Sprintf("Checking metric=%s against namespace %s (%s)", metricName, kmapping, vmapping)) // Try to see if substitution changes something newMetricName := strings.Replace(metricName, kmapping, vmapping, 1) if strings.Compare(newMetricName, metricName) != 0 { MetricMappings[oldMetricName] = newMetricName logger.WithField("_block", "setHekaMessageFields").Debug( fmt.Sprintf("Changing metric=%s into %s", metricName, newMetricName)) metricName = newMetricName } } // Metrics handling for kmapping, vmapping := range globalMappings.Metrics { logger.WithField("_block", "setHekaMessageFields").Debug( fmt.Sprintf("Checking metric=%s against metric %s (%s)", metricName, kmapping, vmapping)) // Try to see if substitution changes something newMetricName := strings.Replace(metricName, kmapping, vmapping, 1) if strings.Compare(newMetricName, metricName) != 0 { MetricMappings[oldMetricName] = newMetricName logger.WithField("_block", "setHekaMessageFields").Debug( fmt.Sprintf("Changing metric=%s into %s", metricName, newMetricName)) metricName = newMetricName } } } addField("name", metricName, msg) addField("value", getData(m.Data()), msg) addField("timestamp", m.Timestamp().UnixNano(), msg) return nil }
func checkValues(metric plugin.MetricType, mockData []uint64) bool { result := false // get last namespace's item last := len(metric.Namespace()) - 1 // approximation error, acceptance boundary is (+/-) 0.5 approxErr := 0.5 switch metric.Namespace().Strings()[last] { case nLoggedUsers: // take the last set mock data if metric.Data() == mockData[len(mockData)-1] { result = true } case nLoggedUsersMin: if metric.Data() == min(mockData) { result = true } case nLoggedUsersMax: if metric.Data() == max(mockData) { result = true } case nLoggedUsersAvg: if math.Abs(metric.Data().(float64)-avg(mockData)) <= approxErr { result = true } } return result }
// CollectMetrics returns filled mts table with metric values. Limits and quotas // are colllected once per tenant. All hypervisor related statistics are collected // in one call. This method also performs lazy initalization of plugin. Error // is returned if initalization or any of required call failed. func (self *NovaPlugin) CollectMetrics(mts []plugin.MetricType) ([]plugin.MetricType, error) { if len(mts) > 0 { err := self.init(mts[0]) if err != nil { return nil, err } } else { return mts, nil } t := time.Now() limitsFor := map[string]bool{} quotasFor := map[string]bool{} hypervisors := false cluster := false results := make([]plugin.MetricType, len(mts)) for _, mt := range mts { id, group, subgroup, _ := parseName(mt.Namespace().Strings()) if group == GROUP_CLUSTER { cluster = true continue } if group == GROUP_HYPERVISOR { hypervisors = true } else { if subgroup == SUBGROUP_LIMITS { limitsFor[id] = true } else { quotasFor[id] = true } } } cachedLimits := map[string]map[string]interface{}{} for tenant, _ := range limitsFor { limits, err := self.collector.GetLimits(tenant) if err != nil { return nil, fmt.Errorf("cannot read limits for %v: (%v)", tenant, err) } cachedLimits[tenant] = limits } cachedQuotas := map[string]map[string]interface{}{} var tenantIds map[string]string = nil for tenant, _ := range quotasFor { if tenantIds == nil { tenantIds2, err := self.collector.GetTenants() if err != nil { return nil, fmt.Errorf("cannot get tenants list: (%v)", err) } tenantIds = tenantIds2 } quotas, err := self.collector.GetQuotas(tenant, tenantIds[tenant]) if err != nil { return nil, fmt.Errorf("cannot read quotas for %v: (%v)", tenant, err) } cachedQuotas[tenant] = quotas } cachedHypervisor := map[string]map[string]interface{}{} if hypervisors { hStats, err := self.collector.GetHypervisors() if err != nil { return nil, fmt.Errorf("cannot read hypervisors: (%v)", err) } cachedHypervisor = hStats } cachedClusterConfig := map[string]interface{}{} if cluster { cachedClusterConfig = self.collector.GetClusterConfig() } for i, mt := range mts { id, group, subgroup, metric := parseName(mt.Namespace().Strings()) mt := plugin.MetricType{ Namespace_: mt.Namespace(), Timestamp_: t, } if group == GROUP_CLUSTER && id == ID_CONFIG { mt.Data_ = cachedClusterConfig[metric] } else { if group == GROUP_HYPERVISOR { mt.Data_ = cachedHypervisor[id][metric] } else { if subgroup == SUBGROUP_LIMITS { mt.Data_ = cachedLimits[id][metric] } else { mt.Data_ = cachedQuotas[id][metric] } } } results[i] = mt } return results, nil }
func TestCollectMetrics(t *testing.T) { dockerPlg := &docker{ containers: map[string]containerData{}, } Convey("return an error when there is no available container", t, func() { mc := new(ClientMock) mc.On("ListContainersAsMap").Return(nil, errors.New("No docker container found")) dockerPlg.client = mc metrics, err := dockerPlg.CollectMetrics(mockMts) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) So(err.Error(), ShouldEqual, "No docker container found") }) Convey("return an error when cannot get statistics", t, func() { mc := new(ClientMock) mc.On("ListContainersAsMap").Return(mockListOfContainers, nil) mc.On("GetStatsFromContainer").Return(nil, errors.New("Cannot get statistics")) dockerPlg.client = mc metrics, err := dockerPlg.CollectMetrics(mockMts) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) So(err.Error(), ShouldEqual, "Cannot get statistics") }) Convey("successful collect metrics", t, func() { mc := new(ClientMock) mc.On("ListContainersAsMap").Return(mockListOfContainers, nil) mc.On("GetStatsFromContainer").Return(mockStats, nil) dockerPlg.client = mc metrics, err := dockerPlg.CollectMetrics(mockMts) So(err, ShouldBeNil) So(metrics, ShouldNotBeEmpty) // each of collected metrics should have set a version of collector plugin Convey("collected metrics have set plugin version", func() { for _, metric := range metrics { So(metric.Version(), ShouldEqual, VERSION) } }) // collected metrics should not contain an asterisk in a namespace Convey("collected metrics have specified namespace", func() { for _, metric := range metrics { So(metric.Namespace().Strings(), ShouldNotContain, "*") } }) }) Convey("successful collect metrics for specified dynamic metric", t, func() { mc := new(ClientMock) mc.On("ListContainersAsMap").Return(mockListOfContainers, nil) mc.On("GetStatsFromContainer").Return(mockStats, nil) dockerPlg.client = mc Convey("for specific dynamic elements: docker_id", func() { mockMt := plugin.MetricType{ Namespace_: core.NewNamespace(NS_VENDOR, NS_PLUGIN). AddDynamicElement("docker_id", "an id of docker container"). AddStaticElements("stats", "cgroups", "memory_stats", "cache"), } Convey("succefull when specified container exists", func() { Convey("for short docker_id", func() { // specify docker id of requested metric type as a short mockMt.Namespace()[2].Value = mockDockerID metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldBeNil) So(metrics, ShouldNotBeEmpty) So(len(metrics), ShouldEqual, 1) So(metrics[0].Namespace(), ShouldResemble, mockMt.Namespace()) }) Convey("for long docker_id", func() { // specify docker id of requested metric type as a long mockMt.Namespace()[2].Value = mockListOfContainers[mockDockerID].ID metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldBeNil) So(metrics, ShouldNotBeEmpty) So(len(metrics), ShouldEqual, 1) So(metrics[0].Namespace().String(), ShouldEqual, "/intel/docker/"+mockDockerID+"/stats/cgroups/memory_stats/cache") }) Convey("for host of docker_id", func() { // specify docker id of requested metric type mockMt.Namespace()[2].Value = "root" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldBeNil) So(metrics, ShouldNotBeEmpty) So(len(metrics), ShouldEqual, 1) So(metrics[0].Namespace().String(), ShouldEqual, "/intel/docker/root/stats/cgroups/memory_stats/cache") }) }) Convey("return an error when specified docker_id is invalid", func() { Convey("when there is no such container", func() { // specify id (12 chars) of docker container which not exist (it's not returned by ListContainerAsMap()) mockMt.Namespace()[2].Value = "111111111111" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) So(err.Error(), ShouldEqual, "Docker container 111111111111 cannot be found") }) Convey("when specified docker_id has invalid format", func() { mockMt := plugin.MetricType{ Namespace_: core.NewNamespace(NS_VENDOR, NS_PLUGIN). AddDynamicElement("docker_id", "an id of docker container"). AddStaticElements("stats", "cgroups", "memory_stats", "cache"), } // specify requested docker id in invalid way (shorter than 12 chars) mockMt.Namespace()[2].Value = "1" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) So(err.Error(), ShouldEqual, "Docker id 1 is too short (the length of id should equal at least 12)") }) }) }) Convey("for specific dynamic elements: docker_id and cpu_id", func() { mockMt := plugin.MetricType{ Namespace_: core.NewNamespace(NS_VENDOR, NS_PLUGIN). AddDynamicElement("docker_id", "an id of docker container"). AddStaticElements("stats", "cgroups", "cpu_stats", "cpu_usage", "percpu_usage"). AddDynamicElement("cpu_id", "an id of cpu"). AddStaticElement("value"), } // specify docker_id and cpu_id of requested metric type mockMt.Namespace()[2].Value = mockDockerID Convey("successful when specified cpu_id is valid", func() { mockMt.Namespace()[8].Value = "0" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldBeNil) So(metrics, ShouldNotBeEmpty) So(len(metrics), ShouldEqual, 1) So(metrics[0].Namespace(), ShouldResemble, mockMt.Namespace()) }) Convey("return an error when specified cpu_id is invalid", func() { Convey("when cpu_id is out of range", func() { // specify cpu_id which does not exist (out of range) mockMt.Namespace()[8].Value = "100" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) }) Convey("when cpu_id is negative", func() { mockMt.Namespace()[8].Value = "-1" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) }) Convey("when cpu_id is a float", func() { mockMt.Namespace()[8].Value = "1.0" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) }) }) }) Convey("for specific dynamic elements: docker_id and device_name", func() { mockMt := plugin.MetricType{ Namespace_: core.NewNamespace(NS_VENDOR, NS_PLUGIN). AddDynamicElement("docker_id", "an id of docker container"). AddStaticElements("stats", "filesystem"). AddDynamicElement("device_name", "a name of filesystem device"). AddStaticElement("usage"), } mockMt.Namespace()[2].Value = mockDockerID Convey("successful when specified device exists", func() { mockMt.Namespace()[5].Value = "dev1" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldBeNil) So(metrics, ShouldNotBeEmpty) So(len(metrics), ShouldEqual, 1) So(metrics[0].Namespace(), ShouldResemble, mockMt.Namespace()) }) Convey("return an error when specified device is invalid", func() { mockMt.Namespace()[5].Value = "invalid_dev_name" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) So(err.Error(), ShouldEqual, fmt.Sprintf("In metric %s the given device name is invalid (no stats for this device)", mockMt.Namespace().String())) }) }) Convey("for specific dynamic elements: docker_id and network_interface", func() { mockMt := plugin.MetricType{ Namespace_: core.NewNamespace(NS_VENDOR, NS_PLUGIN). AddDynamicElement("docker_id", "an id of docker container"). AddStaticElements("stats", "network"). AddDynamicElement("network_interface", "a name of network interface or 'total' for aggregate"). AddStaticElement("rx_bytes"), } // specify docker_id and device_name of requested metric type mockMt.Namespace()[2].Value = mockDockerID Convey("successful when specified network interface exists", func() { mockMt.Namespace()[5].Value = "eth0" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldBeNil) So(metrics, ShouldNotBeEmpty) So(len(metrics), ShouldEqual, 1) So(metrics[0].Namespace(), ShouldResemble, mockMt.Namespace()) }) Convey("return an error when specified network interface is invalid", func() { mockMt.Namespace()[5].Value = "eth0_invalid" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) So(err.Error(), ShouldEqual, fmt.Sprintf("In metric %s the given network interface is invalid (no stats for this net interface)", mockMt.Namespace().String())) }) }) Convey("for specific dynamic elements: docker_id and label_key", func() { mockMt := plugin.MetricType{ Namespace_: core.NewNamespace(NS_VENDOR, NS_PLUGIN). AddDynamicElement("docker_id", "an id of docker container"). AddStaticElements("spec", "labels"). AddDynamicElement("label_key", "a key of container's label"). AddStaticElement("value"), } // specify docker_id and device_name of requested metric type mockMt.Namespace()[2].Value = mockDockerID Convey("successful when specified label exists", func() { mockMt.Namespace()[5].Value = "lkey1" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldBeNil) So(metrics, ShouldNotBeEmpty) So(len(metrics), ShouldEqual, 1) So(metrics[0].Namespace(), ShouldResemble, mockMt.Namespace()) }) Convey("return an error when specified label is invalid (not exist)", func() { mockMt.Namespace()[5].Value = "lkey1_invalid" metrics, err := dockerPlg.CollectMetrics([]plugin.MetricType{mockMt}) So(err, ShouldNotBeNil) So(metrics, ShouldBeEmpty) So(err.Error(), ShouldEqual, fmt.Sprintf("In metric %s the given label is invalid (no value for this label key)", mockMt.Namespace().String())) }) }) }) }
func gathermts(host string, filter []string) ([]plugin.MetricType, error) { resp, err := http.Get(fmt.Sprintf("%s/metrics", host)) if err != nil { return nil, err } if resp.StatusCode != 200 { return nil, errReqFailed } mtsmap := make(map[string]plugin.MetricType) scanner := bufio.NewScanner(resp.Body) if err != nil { return nil, err } for scanner.Scan() { txt := scanner.Text() if !strings.Contains(txt, "{") && !strings.Contains(txt, "#") { nsslice := strings.Split(txt, " ") mtsmap[nsslice[0]] = plugin.MetricType{ Namespace_: core.NewNamespace("intel", "etcd", nsslice[0]), Data_: nsslice[1], Timestamp_: time.Now(), } } } // No filter given; this was a GetMetricTypes call. if len(filter) == 0 { mts := make([]plugin.MetricType, 0, len(mtsmap)+len(derivatives)) for _, v := range mtsmap { mts = append(mts, v) } for k := range derivatives { mts = append(mts, plugin.MetricType{Namespace_: core.NewNamespace("intel", "etcd", "derivative", k)}) } return mts, nil } // Walk through filter and pluck out metrics. // if we find the requested metric in derivatives, // then derive the value from `from`. mts := make([]plugin.MetricType, 0, len(filter)) for _, f := range filter { from, ok := derivatives[f] if ok { mt := plugin.MetricType{ Namespace_: core.NewNamespace("intel", "etcd", "derivative", f), Timestamp_: time.Now(), } sum, err := strconv.ParseFloat(mtsmap[from[0]].Data_.(string), 64) if err != nil { return nil, err } count, err := strconv.ParseFloat(mtsmap[from[1]].Data_.(string), 64) if err != nil { return nil, err } mt.Data_ = sum / count mts = append(mts, mt) continue } mt, ok := mtsmap[f] if ok { mts = append(mts, mt) } } return mts, nil }
func TestConfigItem(t *testing.T) { Convey("Get a value of item with no error", t, func() { Convey("Source: global config", func() { //create global config and add item (different types) cfg := plugin.NewPluginConfigType() cfg.AddItem("dummy_string", ctypes.ConfigValueStr{Value: dummy_str}) cfg.AddItem("dummy_bool", ctypes.ConfigValueBool{Value: dummy_bool}) cfg.AddItem("dummy_int", ctypes.ConfigValueInt{Value: dummy_int}) cfg.AddItem("dummy_float", ctypes.ConfigValueFloat{Value: dummy_float}) Convey("string type of item", func() { result, err := GetConfigItem(cfg, "dummy_string") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_str) }) Convey("bool type of item", func() { result, err := GetConfigItem(cfg, "dummy_bool") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_bool) }) Convey("int type of item", func() { result, err := GetConfigItem(cfg, "dummy_int") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_int) }) Convey("float type of itemr", func() { result, err := GetConfigItem(cfg, "dummy_float") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_float) }) }) Convey("Source: metrics config", func() { // create metric's config config := cdata.NewNode() config.AddItem("dummy_string", ctypes.ConfigValueStr{Value: dummy_str}) config.AddItem("dummy_bool", ctypes.ConfigValueBool{Value: dummy_bool}) config.AddItem("dummy_int", ctypes.ConfigValueInt{Value: dummy_int}) config.AddItem("dummy_float", ctypes.ConfigValueFloat{Value: dummy_float}) // create metric and set config metric := plugin.MetricType{} metric.Config_ = config Convey("string type of item", func() { result, err := GetConfigItem(metric, "dummy_string") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_str) }) Convey("bool type of item", func() { result, err := GetConfigItem(metric, "dummy_bool") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_bool) }) Convey("int type of item", func() { result, err := GetConfigItem(metric, "dummy_int") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_int) }) Convey("float type of item", func() { result, err := GetConfigItem(metric, "dummy_float") So(err, ShouldBeNil) So(result, ShouldEqual, dummy_float) }) }) }) Convey("Try to get a value of item not defined in config", t, func() { Convey("Source: metrics config", func() { config := cdata.NewNode() config.AddItem("foo", ctypes.ConfigValueStr{Value: "foo_val"}) metric := plugin.MetricType{} metric.Config_ = config result, err := GetMetricConfigItem(metric, "foo_not_exist") So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) Convey("Source: global config", func() { cfg := plugin.NewPluginConfigType() cfg.AddItem("foo", ctypes.ConfigValueStr{Value: "foo"}) names := []string{"foo1", "foo2"} result, err := GetGlobalConfigItems(cfg, names) So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) }) Convey("No item defined in config", t, func() { Convey("Source: metrics config", func() { metric := plugin.MetricType{} metric.Config_ = cdata.NewNode() result, err := GetMetricConfigItem(metric, "foo") So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) Convey("Source: global config", func() { cfg_empty := plugin.NewPluginConfigType() names := []string{"foo", "bar"} result, err := GetGlobalConfigItems(cfg_empty, names) So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) }) Convey("Try to get a value of item from invalid config (unsupported type)", t, func() { invalid_cfg := []string{"invalid", "config", "source"} result, err := GetConfigItem(invalid_cfg, "foo") So(err, ShouldNotBeNil) So(result, ShouldBeNil) }) }
func (p *movingAverageProcessor) calculateMovingAverage(m plugin.MetricType, logger *log.Logger) (float64, error) { namespace := concatNameSpace(m.Namespace().Strings()) switch v := m.Data().(type) { default: logger.Warnln(fmt.Sprintf("Unknown data received: Type %T", v)) return 0.0, nil case int: if _, ok := p.movingAverageMap[namespace]; ok { counter, err := p.getCounter(namespace) counterCurrent := counter % p.movingBufLength err = p.addBufferData(counterCurrent, m.Data(), namespace) if err != nil { return 0.0, err } sum := int(0) //Initial Counter is used to give correct average for initial iterations ie when the buffer is not full initialCounter := 0 for i := 0; i < p.movingBufLength; i++ { if p.getBufferData(i, namespace) != nil { initialCounter++ sum += p.getBufferData(i, namespace).(int) } } movingAvg := float64(sum) / float64(initialCounter) counterCurrent++ p.setCounter(namespace, counterCurrent) return movingAvg, err } else { //Since map doesnot have an entry of this namespace, it's creating an entry for the namespace. //Also m.data value is inserted into 0th position of the buffer because we know that this buffer is being used for the first time p.movingAverageMap[namespace] = newmovingAverage(p.getBufferLength()) err := p.addBufferData(0, m.Data(), namespace) if err != nil { return 0.0, err } sum := p.getBufferData(0, namespace).(int) p.setCounter(namespace, 1) return float64(sum), nil } case float64: if _, ok := p.movingAverageMap[namespace]; ok { counter, err := p.getCounter(namespace) counterCurrent := counter % p.movingBufLength err = p.addBufferData(counterCurrent, m.Data(), namespace) if err != nil { return 0.0, err } sum := float64(0) initialCounter := 0 for i := 0; i < p.movingBufLength; i++ { if p.getBufferData(i, namespace) != nil { initialCounter++ sum += p.getBufferData(i, namespace).(float64) } } movingAvg := float64(sum) / float64(initialCounter) counterCurrent++ p.setCounter(namespace, counterCurrent) return movingAvg, err } p.movingAverageMap[namespace] = newmovingAverage(p.getBufferLength()) err := p.addBufferData(0, m.Data(), namespace) if err != nil { return 0.0, err } sum := p.getBufferData(0, namespace).(float64) p.setCounter(namespace, 1) return float64(sum), nil case float32: if _, ok := p.movingAverageMap[namespace]; ok { counter, err := p.getCounter(namespace) counterCurrent := counter % p.movingBufLength err = p.addBufferData(counterCurrent, m.Data(), namespace) if err != nil { return 0.0, err } sum := float32(0) initialCounter := 0 for i := 0; i < p.movingBufLength; i++ { if p.getBufferData(i, namespace) != nil { initialCounter++ sum += p.getBufferData(i, namespace).(float32) } } movingAvg := float64(sum) / float64(initialCounter) p.setCounter(namespace, counterCurrent) return movingAvg, err } p.movingAverageMap[namespace] = newmovingAverage(p.getBufferLength()) err := p.addBufferData(0, m.Data(), namespace) if err != nil { return 0.0, err } sum := p.getBufferData(0, namespace).(float32) p.setCounter(namespace, 1) return float64(sum), nil case uint32: if _, ok := p.movingAverageMap[namespace]; ok { counter, err := p.getCounter(namespace) counterCurrent := counter % p.movingBufLength err = p.addBufferData(counterCurrent, m.Data(), namespace) if err != nil { return 0.0, err } sum := uint32(0) initialCounter := 0 for i := 0; i < p.movingBufLength; i++ { if p.getBufferData(i, namespace) != nil { initialCounter++ sum += p.getBufferData(i, namespace).(uint32) } } movingAvg := float64(sum) / float64(initialCounter) counterCurrent++ p.setCounter(namespace, counterCurrent) return movingAvg, err } p.movingAverageMap[namespace] = newmovingAverage(p.getBufferLength()) err := p.addBufferData(0, m.Data(), namespace) if err != nil { return 0.0, err } sum := p.getBufferData(0, namespace).(uint32) p.setCounter(namespace, 1) return float64(sum), nil case uint64: if _, ok := p.movingAverageMap[namespace]; ok { counter, err := p.getCounter(namespace) counterCurrent := counter % p.movingBufLength err = p.addBufferData(counterCurrent, m.Data(), namespace) if err != nil { return 0.0, err } sum := uint64(0) initialCounter := 0 for i := 0; i < p.movingBufLength; i++ { if p.getBufferData(i, namespace) != nil { initialCounter++ sum += p.getBufferData(i, namespace).(uint64) } } movingAvg := float64(sum) / float64(initialCounter) counterCurrent++ p.setCounter(namespace, counterCurrent) return movingAvg, err } p.movingAverageMap[namespace] = newmovingAverage(p.getBufferLength()) err := p.addBufferData(0, m.Data(), namespace) if err != nil { return 0.0, err } sum := p.getBufferData(0, namespace).(uint64) p.setCounter(namespace, 1) return float64(sum), nil } }
func TestCollectMetric(t *testing.T) { ns0 := core.NewNamespace("intel", "anothermock", "test") ns1 := core.NewNamespace("intel", "anothermock", "foo") ns2 := core.NewNamespace("intel", "anothermock", "bar") ns3 := core.NewNamespace("intel", "anothermock").AddDynamicElement("host", "name of the host").AddStaticElement("baz") Convey("Testing CollectMetric", t, func() { newPlg := new(AnotherMock) So(newPlg, ShouldNotBeNil) Convey("with 'test' config variable'", func() { node := cdata.NewNode() node.AddItem("test", ctypes.ConfigValueBool{Value: true}) cfg := plugin.ConfigType{ConfigDataNode: node} Convey("testing specific metrics", func() { mTypes := []plugin.MetricType{ plugin.MetricType{Namespace_: ns0, Config_: cfg.ConfigDataNode}, plugin.MetricType{Namespace_: ns1, Config_: cfg.ConfigDataNode}, plugin.MetricType{Namespace_: ns2, Config_: cfg.ConfigDataNode}, } mts, _ := newPlg.CollectMetrics(mTypes) Convey("returned metrics should have data type integer", func() { for _, mt := range mts { _, ok := mt.Data_.(int) So(ok, ShouldBeTrue) } }) }) Convey("testing dynamic metric", func() { mt := plugin.MetricType{Namespace_: ns3, Config_: cfg.ConfigDataNode} Convey("for none specified instance", func() { mts, _ := newPlg.CollectMetrics([]plugin.MetricType{mt}) // there is 10 available hosts (host0, host1, ..., host9) So(len(mts), ShouldEqual, 10) Convey("returned metrics should have data type integer", func() { for _, mt := range mts { _, ok := mt.Data_.(int) So(ok, ShouldBeTrue) } }) Convey("returned metrics should remain dynamic", func() { for _, mt := range mts { isDynamic, _ := mt.Namespace().IsDynamic() So(isDynamic, ShouldBeTrue) } }) }) Convey("for specified instance which is available - host0", func() { mt.Namespace()[2].Value = "host0" mts, _ := newPlg.CollectMetrics([]plugin.MetricType{mt}) // only one metric for this specific hostname should be returned So(len(mts), ShouldEqual, 1) So(mts[0].Namespace().String(), ShouldEqual, "/intel/anothermock/host0/baz") Convey("returned metric should have data type integer", func() { _, ok := mts[0].Data_.(int) So(ok, ShouldBeTrue) }) Convey("returned metric should remain dynamic", func() { isDynamic, _ := mt.Namespace().IsDynamic() So(isDynamic, ShouldBeTrue) }) }) Convey("for specified instance which is not available - host10", func() { mt.Namespace()[2].Value = "host10" mts, err := newPlg.CollectMetrics([]plugin.MetricType{mt}) So(mts, ShouldBeNil) So(err, ShouldNotBeNil) So(err.Error(), ShouldStartWith, "requested hostname `host10` is not available") }) }) }) Convey("without config variables", func() { node := cdata.NewNode() cfg := plugin.ConfigType{ConfigDataNode: node} Convey("testing specific metrics", func() { mTypes := []plugin.MetricType{ plugin.MetricType{Namespace_: ns0, Config_: cfg.ConfigDataNode}, plugin.MetricType{Namespace_: ns1, Config_: cfg.ConfigDataNode}, plugin.MetricType{Namespace_: ns2, Config_: cfg.ConfigDataNode}, } mts, _ := newPlg.CollectMetrics(mTypes) Convey("returned metrics should have data type integer", func() { for _, mt := range mts { _, ok := mt.Data_.(int) So(ok, ShouldBeTrue) } }) }) Convey("testing dynamic metics", func() { mTypes := []plugin.MetricType{ plugin.MetricType{Namespace_: ns3, Config_: cfg.ConfigDataNode}, } mts, _ := newPlg.CollectMetrics(mTypes) Convey("returned metrics should have data type integer", func() { for _, mt := range mts { _, ok := mt.Data_.(int) So(ok, ShouldBeTrue) } }) Convey("returned metrics should remain dynamic", func() { for _, mt := range mts { isDynamic, _ := mt.Namespace().IsDynamic() So(isDynamic, ShouldBeTrue) } }) }) }) }) }