예제 #1
0
// checkCache checks the cache for metric types.
// returns:
//  - array of metrics that need to be collected
//  - array of metrics that were returned from the cache
func checkCache(mts []core.Metric) ([]plugin.PluginMetricType, []core.Metric) {
	var fromCache []core.Metric
	var metricsToCollect []plugin.PluginMetricType
	for _, mt := range mts {
		if m := metricCache.get(core.JoinNamespace(mt.Namespace()), mt.Version()); m != nil {
			switch metric := m.(type) {
			case core.Metric:
				fromCache = append(fromCache, metric)
			case []core.Metric:
				for _, met := range metric {
					fromCache = append(fromCache, met)
				}
			default:
				log.WithFields(log.Fields{
					"_module": "client",
					"_block":  "checkCache",
				}).Error("unsupported type found in the cache")
			}
		} else {
			mt := plugin.PluginMetricType{
				Namespace_:          mt.Namespace(),
				LastAdvertisedTime_: mt.LastAdvertisedTime(),
				Version_:            mt.Version(),
				Tags_:               mt.Tags(),
				Labels_:             mt.Labels(),
				Config_:             mt.Config(),
			}
			metricsToCollect = append(metricsToCollect, mt)
		}
	}
	return metricsToCollect, fromCache
}
예제 #2
0
파일: metric.go 프로젝트: jeffweiss/snap
func respondWithMetrics(host string, mets []core.CatalogedMetric, w http.ResponseWriter) {
	b := rbody.NewMetricsReturned()

	for _, met := range mets {
		rt := met.Policy().RulesAsTable()
		policies := make([]rbody.PolicyTable, 0, len(rt))
		for _, r := range rt {
			policies = append(policies, rbody.PolicyTable{
				Name:     r.Name,
				Type:     r.Type,
				Default:  r.Default,
				Required: r.Required,
				Minimum:  r.Minimum,
				Maximum:  r.Maximum,
			})
		}
		b = append(b, rbody.Metric{
			Namespace:               core.JoinNamespace(met.Namespace()),
			Version:                 met.Version(),
			LastAdvertisedTimestamp: met.LastAdvertisedTime().Unix(),
			Policy:                  policies,
			Href:                    catalogedMetricURI(host, met),
		})
	}
	sort.Sort(b)
	respond(200, b, w)
}
예제 #3
0
func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	ns := parseNamespace(params.ByName("namespace"))

	var (
		ver int
		err error
	)
	q := r.URL.Query()
	v := q.Get("ver")
	if v == "" {
		ver = -1
	} else {
		ver, err = strconv.Atoi(v)
		if err != nil {
			respond(400, rbody.FromError(err), w)
			return
		}
	}

	if ns[len(ns)-1] == "*" {
		mets, err := s.mm.FetchMetrics(ns[:len(ns)-1], ver)
		if err != nil {
			respond(404, rbody.FromError(err), w)
			return
		}
		respondWithMetrics(r.Host, mets, w)
		return
	}

	mt, err := s.mm.GetMetric(ns, ver)
	if err != nil {
		respond(404, rbody.FromError(err), w)
		return
	}

	b := &rbody.MetricReturned{}
	mb := &rbody.Metric{
		Namespace:               core.JoinNamespace(mt.Namespace()),
		Version:                 mt.Version(),
		LastAdvertisedTimestamp: mt.LastAdvertisedTime().Unix(),
		Href: catalogedMetricURI(r.Host, mt),
	}
	rt := mt.Policy().RulesAsTable()
	policies := make([]rbody.PolicyTable, 0, len(rt))
	for _, r := range rt {
		policies = append(policies, rbody.PolicyTable{
			Name:     r.Name,
			Type:     r.Type,
			Default:  r.Default,
			Required: r.Required,
			Minimum:  r.Minimum,
			Maximum:  r.Maximum,
		})
	}
	mb.Policy = policies
	b.Metric = mb
	respond(200, b, w)
}
예제 #4
0
파일: lru.go 프로젝트: snapbot/snap
// updateCache updates the cache with the given array of metrics.
func (l *lru) UpdateCache(mts []core.Metric) {
	results := []core.Metric{}
	dc := map[string][]core.Metric{}
	for _, mt := range mts {
		if mt.Labels() == nil {
			// cache the individual metric
			l.metricCache.put(core.JoinNamespace(mt.Namespace()), mt.Version(), mt)
			l.logger.Debugf("putting %v:%v in the cache", mt.Namespace(), mt.Version())
		} else {
			// collect the dynamic query results so we can cache
			ns := make([]string, len(mt.Namespace()))
			copy(ns, mt.Namespace())
			for _, label := range mt.Labels() {
				ns[label.Index] = "*"
			}
			if _, ok := dc[core.JoinNamespace(ns)]; !ok {
				dc[core.JoinNamespace(ns)] = []core.Metric{}
			}
			dc[core.JoinNamespace(ns)] = append(dc[core.JoinNamespace(ns)], mt)
			l.metricCache.put(core.JoinNamespace(ns), mt.Version(), dc[core.JoinNamespace(ns)])
			l.logger.Debugf("putting %v:%v in the cache", ns, mt.Version())
		}
		results = append(results, mt)
	}
}
예제 #5
0
func (p *pluginControl) validateMetricTypeSubscription(mt core.RequestedMetric, cd *cdata.ConfigDataNode) (core.Metric, []serror.SnapError) {
	var serrs []serror.SnapError
	controlLogger.WithFields(log.Fields{
		"_block":    "validate-metric-subscription",
		"namespace": mt.Namespace(),
		"version":   mt.Version(),
	}).Info("subscription called on metric")

	m, err := p.metricCatalog.Get(mt.Namespace(), mt.Version())
	if err != nil {
		serrs = append(serrs, serror.New(err, map[string]interface{}{
			"name":    core.JoinNamespace(mt.Namespace()),
			"version": mt.Version(),
		}))
		return nil, serrs
	}

	// No metric found return error.
	if m == nil {
		serrs = append(serrs, serror.New(fmt.Errorf("no metric found cannot subscribe: (%s) version(%d)", mt.Namespace(), mt.Version())))
		return nil, serrs
	}

	m.config = cd

	typ, serr := core.ToPluginType(m.Plugin.TypeName())
	if serr != nil {
		return nil, []serror.SnapError{serror.New(err)}
	}

	// merge global plugin config
	if m.config != nil {
		m.config.Merge(p.Config.Plugins.getPluginConfigDataNode(typ, m.Plugin.Name(), m.Plugin.Version()))
	} else {
		m.config = p.Config.Plugins.getPluginConfigDataNode(typ, m.Plugin.Name(), m.Plugin.Version())
	}

	// When a metric is added to the MetricCatalog, the policy of rules defined by the plugin is added to the metric's policy.
	// If no rules are defined for a metric, we set the metric's policy to an empty ConfigPolicyNode.
	// Checking m.policy for nil will not work, we need to check if rules are nil.
	if m.policy.HasRules() {
		if m.Config() == nil {
			serrs = append(serrs, serror.New(fmt.Errorf("Policy defined for metric, (%s) version (%d), but no config defined in manifest", mt.Namespace(), mt.Version())))
			return nil, serrs
		}
		ncdTable, errs := m.policy.Process(m.Config().Table())
		if errs != nil && errs.HasErrors() {
			for _, e := range errs.Errors() {
				serrs = append(serrs, serror.New(e))
			}
			return nil, serrs
		}
		m.config = cdata.FromTable(*ncdTable)
	}

	return m, serrs
}
예제 #6
0
func (mtt *mttNode) find(ns []string) (*mttNode, serror.SnapError) {
	node, index := mtt.walk(ns)
	if index != len(ns) {
		se := serror.New(errorMetricNotFound(ns))
		se.SetFields(map[string]interface{}{
			"name": core.JoinNamespace(ns),
		})
		return nil, se
	}
	return node, nil
}
예제 #7
0
func (t *TaskWatchHandler) CatchCollection(m []core.Metric) {
	sm := make([]rbody.StreamedMetric, len(m))
	for i, _ := range m {
		sm[i] = rbody.StreamedMetric{
			Namespace: core.JoinNamespace(m[i].Namespace()),
			Data:      m[i].Data(),
			Source:    m[i].Source(),
			Timestamp: m[i].Timestamp(),
		}
	}
	t.mChan <- rbody.StreamedTaskEvent{
		EventType: rbody.TaskWatchMetricEvent,
		Message:   "",
		Event:     sm,
	}
}
예제 #8
0
// Get works like fetch, but only returns the MT at the given node
// and does not gather the node's children.
func (mtt *mttNode) Get(ns []string) ([]*metricType, serror.SnapError) {
	node, err := mtt.find(ns)
	if err != nil {
		return nil, err
	}
	if node.mts == nil {
		se := serror.New(errorMetricNotFound(ns))
		se.SetFields(map[string]interface{}{
			"name": core.JoinNamespace(ns),
		})
		return nil, se
	}
	var mts []*metricType
	for _, mt := range node.mts {
		mts = append(mts, mt)
	}
	return mts, nil
}
예제 #9
0
func (p *pluginControl) gatherCollectors(mts []core.Metric) ([]core.Plugin, []serror.SnapError) {
	var (
		plugins []core.Plugin
		serrs   []serror.SnapError
	)

	// here we resolve and retrieve plugins for each metric type.
	// if the incoming metric type version is < 1, we treat that as
	// latest as with plugins.  The following two loops create a set
	// of plugins with proper versions needed to discern the subscription
	// types.
	colPlugins := make(map[string]*loadedPlugin)
	for _, mt := range mts {
		m, err := p.metricCatalog.Get(mt.Namespace(), mt.Version())
		if err != nil {
			serrs = append(serrs, serror.New(err, map[string]interface{}{
				"name":    core.JoinNamespace(mt.Namespace()),
				"version": mt.Version(),
			}))
			continue
		}
		// if the metric subscription is to version -1, we need to carry
		// that forward in the subscription.
		if mt.Version() < 1 {
			// make a copy of the loadedPlugin and overwrite the version.
			npl := *m.Plugin
			npl.Meta.Version = -1
			colPlugins[npl.Key()] = &npl
		} else {
			colPlugins[m.Plugin.Key()] = m.Plugin
		}
	}
	if len(serrs) > 0 {
		return plugins, serrs
	}

	for _, lp := range colPlugins {
		plugins = append(plugins, lp)
	}

	return plugins, nil
}
예제 #10
0
파일: cache.go 프로젝트: mtanda/snap
func (c *cache) checkCache(mts []core.Metric) (metricsToCollect []core.Metric, fromCache []core.Metric) {
	for _, mt := range mts {
		if m := c.get(core.JoinNamespace(mt.Namespace()), mt.Version()); m != nil {
			switch metric := m.(type) {
			case core.Metric:
				fromCache = append(fromCache, metric)
			case []core.Metric:
				for _, met := range metric {
					fromCache = append(fromCache, met)
				}
			default:
				cacheLog.WithFields(log.Fields{
					"_block": "checkCache",
				}).Error("unsupported type found in the cache")
			}
		} else {
			metricsToCollect = append(metricsToCollect, mt)
		}
	}
	return metricsToCollect, fromCache
}
예제 #11
0
func (mc *metricCatalog) get(ns []string, ver int) (*metricType, serror.SnapError) {
	mts, err := mc.tree.Get(ns)
	if err != nil {
		return nil, err
	}
	if mts == nil {
		return nil, serror.New(errMetricNotFound)
	}
	// a version IS given
	if ver > 0 {
		l, err := getVersion(mts, ver)
		if err != nil {
			se := serror.New(errorMetricNotFound(ns, ver))
			se.SetFields(map[string]interface{}{
				"name":    core.JoinNamespace(ns),
				"version": ver,
			})
			return nil, se
		}
		return l, nil
	}
	// ver is less than or equal to 0 get the latest
	return getLatest(mts), nil
}
예제 #12
0
파일: lru.go 프로젝트: jeffweiss/snap
// checkCache checks the cache for metric types.
// returns:
//  - array of metrics that need to be collected
//  - array of metrics that were returned from the cache
func (l *lru) CheckCache(mts []core.Metric) ([]core.Metric, []core.Metric) {
	var fromCache []core.Metric
	var metricsToCollect []core.Metric
	for _, mt := range mts {
		if m := l.metricCache.get(core.JoinNamespace(mt.Namespace()), mt.Version()); m != nil {
			switch metric := m.(type) {
			case core.Metric:
				fromCache = append(fromCache, metric)
			case []core.Metric:
				for _, met := range metric {
					fromCache = append(fromCache, met)
				}
			default:
				l.logger.WithFields(log.Fields{
					"_module": "client",
					"_block":  "checkCache",
				}).Error("unsupported type found in the cache")
			}
		} else {
			metricsToCollect = append(metricsToCollect, mt)
		}
	}
	return metricsToCollect, fromCache
}
예제 #13
0
// updateCache updates the cache with the given array of metrics.
func updateCache(mts []plugin.PluginMetricType) {
	results := []core.Metric{}
	dc := map[string][]core.Metric{}
	for _, mt := range mts {
		if mt.Labels == nil {
			// cache the individual metric
			metricCache.put(core.JoinNamespace(mt.Namespace_), mt.Version(), mt)
		} else {
			// collect the dynamic query results so we can cache
			ns := make([]string, len(mt.Namespace()))
			copy(ns, mt.Namespace())
			for _, label := range mt.Labels_ {
				ns[label.Index] = "*"
			}
			if _, ok := dc[core.JoinNamespace(ns)]; !ok {
				dc[core.JoinNamespace(ns)] = []core.Metric{}
			}
			dc[core.JoinNamespace(ns)] = append(dc[core.JoinNamespace(ns)], mt)
			metricCache.put(core.JoinNamespace(ns), mt.Version(), dc[core.JoinNamespace(ns)])
		}
		results = append(results, mt)
	}
}
예제 #14
0
func TestCollectDynamicMetrics(t *testing.T) {
	Convey("given a plugin using the native client", t, func() {
		config := NewConfig()
		config.Plugins.All.AddItem("password", ctypes.ConfigValueStr{Value: "testval"})
		c := New(OptSetConfig(config), CacheExpiration(time.Second*1))
		c.Start()
		So(strategy.GlobalCacheExpiration, ShouldResemble, time.Second*1)
		lpe := newListenToPluginEvent()
		c.eventManager.RegisterHandler("Control.PluginLoaded", lpe)
		_, e := load(c, PluginPath)
		Convey("Loading native client plugin", func() {
			Convey("Should not error", func() {
				So(e, ShouldBeNil)
			})
		})
		if e != nil {
			t.FailNow()
		}
		<-lpe.done
		_, e = load(c, JSONRPCPluginPath)
		Convey("Loading JSONRPC client plugin", func() {
			Convey("Should not error", func() {
				So(e, ShouldBeNil)
			})
		})
		if e != nil {
			t.FailNow()
		}
		<-lpe.done
		cd := cdata.NewNode()
		metrics, err := c.metricCatalog.Fetch([]string{})
		So(err, ShouldBeNil)
		So(len(metrics), ShouldEqual, 6)
		m, err := c.metricCatalog.Get([]string{"intel", "mock", "*", "baz"}, 2)
		So(err, ShouldBeNil)
		So(m, ShouldNotBeNil)
		jsonm, err := c.metricCatalog.Get([]string{"intel", "mock", "*", "baz"}, 1)
		So(err, ShouldBeNil)
		So(jsonm, ShouldNotBeNil)
		metric, errs := c.validateMetricTypeSubscription(m, cd)
		So(errs, ShouldBeNil)
		So(metric, ShouldNotBeNil)
		Convey("collects metrics from plugin using native client", func() {
			lp, err := c.pluginManager.get("collector:mock:2")
			So(err, ShouldBeNil)
			So(lp, ShouldNotBeNil)
			pool, errp := c.pluginRunner.AvailablePlugins().getOrCreatePool("collector:mock:2")
			So(errp, ShouldBeNil)
			So(pool, ShouldNotBeNil)
			ttl, err := pool.CacheTTL()
			So(err, ShouldResemble, ErrPoolEmpty)
			So(ttl, ShouldEqual, 0)
			pool.subscribe("1", unboundSubscriptionType)
			err = c.pluginRunner.runPlugin(lp.Details)
			So(err, ShouldBeNil)
			ttl, err = pool.CacheTTL()
			So(err, ShouldBeNil)
			// The minimum TTL advertised by the plugin is 100ms therefore the TTL for the
			// pool should be the global cache expiration
			So(ttl, ShouldEqual, strategy.GlobalCacheExpiration)
			mts, errs := c.CollectMetrics([]core.Metric{m}, time.Now().Add(time.Second*1))
			hits, err := pool.CacheHits(core.JoinNamespace(m.namespace), 2)
			So(err, ShouldBeNil)
			So(hits, ShouldEqual, 0)
			So(errs, ShouldBeNil)
			So(len(mts), ShouldEqual, 10)
			mts, errs = c.CollectMetrics([]core.Metric{m}, time.Now().Add(time.Second*1))
			hits, err = pool.CacheHits(core.JoinNamespace(m.namespace), 2)
			So(err, ShouldBeNil)
			So(hits, ShouldEqual, 1)
			So(errs, ShouldBeNil)
			So(len(mts), ShouldEqual, 10)
			pool.unsubscribe("1")
			Convey("collects metrics from plugin using httpjson client", func() {
				lp, err := c.pluginManager.get("collector:mock:1")
				So(err, ShouldBeNil)
				So(lp, ShouldNotBeNil)
				pool, errp := c.pluginRunner.AvailablePlugins().getOrCreatePool("collector:mock:1")
				So(errp, ShouldBeNil)
				So(pool, ShouldNotBeNil)
				ttl, err := pool.CacheTTL()
				So(err, ShouldResemble, ErrPoolEmpty)
				So(ttl, ShouldEqual, 0)
				pool.subscribe("1", unboundSubscriptionType)
				err = c.pluginRunner.runPlugin(lp.Details)
				So(err, ShouldBeNil)
				ttl, err = pool.CacheTTL()
				So(err, ShouldBeNil)
				So(ttl, ShouldEqual, 1100*time.Millisecond)
				mts, errs := c.CollectMetrics([]core.Metric{jsonm}, time.Now().Add(time.Second*1))
				hits, err := pool.CacheHits(core.JoinNamespace(jsonm.namespace), jsonm.version)
				So(pool.subscriptionCount(), ShouldEqual, 1)
				So(pool.strategy, ShouldNotBeNil)
				So(len(mts), ShouldBeGreaterThan, 0)
				So(err, ShouldBeNil)
				So(hits, ShouldEqual, 0)
				So(errs, ShouldBeNil)
				So(len(mts), ShouldEqual, 10)
				mts, errs = c.CollectMetrics([]core.Metric{jsonm}, time.Now().Add(time.Second*1))
				hits, err = pool.CacheHits(core.JoinNamespace(m.namespace), 1)
				So(err, ShouldBeNil)
				So(hits, ShouldEqual, 1)
				So(errs, ShouldBeNil)
				So(len(mts), ShouldEqual, 10)
				So(pool.AllCacheHits(), ShouldEqual, 1)
				So(pool.AllCacheMisses(), ShouldEqual, 1)
				pool.unsubscribe("1")
				c.Stop()
				time.Sleep(100 * time.Millisecond)
			})
		})
	})
}
예제 #15
0
파일: metrics.go 프로젝트: jeffweiss/snap
func (m *metricType) NamespaceAsString() string {
	return core.JoinNamespace(m.Namespace())
}
예제 #16
0
파일: metrics.go 프로젝트: jeffweiss/snap
func errorMetricNotFound(ns []string, ver ...int) error {
	if len(ver) > 0 {
		return fmt.Errorf("Metric not found: %s (version: %d)", core.JoinNamespace(ns), ver[0])
	}
	return fmt.Errorf("Metric not found: %s", core.JoinNamespace(ns))
}
예제 #17
0
파일: metric.go 프로젝트: jeffweiss/snap
func (s *Server) getMetricsFromTree(w http.ResponseWriter, r *http.Request, params httprouter.Params) {
	namespace := params.ByName("namespace")

	// we land here if the request contains a trailing slash, because it matches the tree
	// lookup URL: /v1/metrics/*namespace.  If the length of the namespace param is 1, we
	// redirect the request to getMetrics.  This results in GET /v1/metrics and
	// GET /v1/metrics/ behaving the same way.
	if len(namespace) <= 1 {
		s.getMetrics(w, r, params)
		return
	}

	ns := parseNamespace(namespace)

	var (
		ver int
		err error
	)
	q := r.URL.Query()
	v := q.Get("ver")

	if ns[len(ns)-1] == "*" {
		if v == "" {
			ver = -1
		} else {
			ver, err = strconv.Atoi(v)
			if err != nil {
				respond(400, rbody.FromError(err), w)
				return
			}
		}

		mets, err := s.mm.FetchMetrics(ns[:len(ns)-1], ver)
		if err != nil {
			respond(404, rbody.FromError(err), w)
			return
		}
		respondWithMetrics(r.Host, mets, w)
		return
	}

	// If no version was given, get all that fall at this namespace.
	if v == "" {
		mts, err := s.mm.GetMetricVersions(ns)
		if err != nil {
			respond(404, rbody.FromError(err), w)
			return
		}
		respondWithMetrics(r.Host, mts, w)
		return
	}

	// if an explicit version is given, get that single one.
	ver, err = strconv.Atoi(v)
	if err != nil {
		respond(400, rbody.FromError(err), w)
		return
	}
	mt, err := s.mm.GetMetric(ns, ver)
	if err != nil {
		respond(404, rbody.FromError(err), w)
		return
	}

	b := &rbody.MetricReturned{}
	mb := &rbody.Metric{
		Namespace:               core.JoinNamespace(mt.Namespace()),
		Version:                 mt.Version(),
		LastAdvertisedTimestamp: mt.LastAdvertisedTime().Unix(),
		Href: catalogedMetricURI(r.Host, mt),
	}
	rt := mt.Policy().RulesAsTable()
	policies := make([]rbody.PolicyTable, 0, len(rt))
	for _, r := range rt {
		policies = append(policies, rbody.PolicyTable{
			Name:     r.Name,
			Type:     r.Type,
			Default:  r.Default,
			Required: r.Required,
			Minimum:  r.Minimum,
			Maximum:  r.Maximum,
		})
	}
	mb.Policy = policies
	b.Metric = mb
	respond(200, b, w)
}
예제 #18
0
파일: metric.go 프로젝트: jeffweiss/snap
func catalogedMetricURI(host string, mt core.CatalogedMetric) string {
	return fmt.Sprintf("%s://%s/v1/metrics%s?ver=%d", protocolPrefix, host, core.JoinNamespace(mt.Namespace()), mt.Version())
}