// 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
}
示例#9
0
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
	}
}
示例#17
0
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)
					}
				})

			})

		})

	})
}