// GetQueriedNamespaces returns all matched metrics namespaces for query 'ns' which can contain // an asterisk or tuple (refer to query support) func (mc *metricCatalog) GetQueriedNamespaces(ns core.Namespace) ([]core.Namespace, error) { mc.mutex.Lock() defer mc.mutex.Unlock() // get metric key (might contain wildcard(s)) wkey := ns.Key() return mc.matchedNamespaces(wkey) }
// Remove removes a metricType from the catalog and from matching map func (mc *metricCatalog) Remove(ns core.Namespace) { mc.mutex.Lock() defer mc.mutex.Unlock() mc.tree.Remove(ns.Strings()) // remove all items from map mKey mapped for this 'ns' key := ns.Key() mc.removeMatchedKey(key) }
func traceStat(ns core.Namespace, swagURL string) (*plugin.MetricType, error) { trace := ns.Strings()[4] metric, err := getTrace(trace, swagURL) if err != nil { return nil, err } return &plugin.MetricType{ Namespace_: ns, Data_: metric, Timestamp_: time.Now(), }, nil }
// MatchQuery matches given 'ns' which could contain an asterisk or a tuple and add them to matching map under key 'ns' // The matched metrics namespaces are also returned (as a []core.Namespace) func (mc *metricCatalog) MatchQuery(ns core.Namespace) ([]core.Namespace, error) { mc.mutex.Lock() defer mc.mutex.Unlock() // get metric key (might contain wildcard(s)) wkey := ns.Key() // adding matched namespaces to map mc.addItemToMatchingMap(wkey) return mc.matchedNamespaces(wkey) }
func getDynamicElements(ns core.Namespace, indexes []int) []rbody.DynamicElement { elements := make([]rbody.DynamicElement, 0, len(indexes)) for _, v := range indexes { e := ns.Element(v) elements = append(elements, rbody.DynamicElement{ Index: v, Name: e.Name, Description: e.Description, }) } return elements }
func (mc *metricCatalog) GetPlugin(mns core.Namespace, ver int) (*loadedPlugin, error) { mt, err := mc.tree.GetMetric(mns.Strings(), ver) if err != nil { log.WithFields(log.Fields{ "_module": "control", "_file": "metrics.go,", "_block": "get-plugin", "error": err, }).Error("error getting plugin") return nil, err } return mt.Plugin, nil }
// validateMetricNamespace validates metric namespace in terms of containing not allowed characters and ending with an asterisk func validateMetricNamespace(ns core.Namespace) error { value := "" for _, i := range ns { // A dynamic element requires the name while a static element does not. if i.Name != "" && i.Value != "*" { return errorMetricStaticElementHasName(i.Value, i.Name, ns.String()) } if i.Name == "" && i.Value == "*" { return errorMetricDynamicElementHasNoName(i.Value, ns.String()) } value += i.Value } for _, chars := range notAllowedChars { for _, ch := range chars { if strings.ContainsAny(value, ch) { return errorMetricContainsNotAllowedChars(ns.String()) } } } // plugin should NOT advertise metrics ending with a wildcard if strings.HasSuffix(value, "*") { return errorMetricEndsWithAsterisk(ns.String()) } return nil }
// specifyInstanceOfDynamicMetric returns specified namespace of incoming cataloged metric's namespace // based on requested metric namespace func specifyInstanceOfDynamicMetric(catalogedNamespace core.Namespace, requestedNamespace core.Namespace) core.Namespace { specifiedNamespace := make(core.Namespace, len(catalogedNamespace)) copy(specifiedNamespace, catalogedNamespace) _, indexes := catalogedNamespace.IsDynamic() for _, index := range indexes { if len(requestedNamespace) > index { // use namespace's element of requested metric declared in task manifest // to specify a dynamic instance of the cataloged metric specifiedNamespace[index].Value = requestedNamespace[index].Value } } return specifiedNamespace }
// GetVersions retrieves all versions of a given metric namespace. func (mc *metricCatalog) GetVersions(ns core.Namespace) ([]*metricType, error) { mc.mutex.Lock() defer mc.mutex.Unlock() mts, err := mc.tree.GetVersions(ns.Strings()) if err != nil { log.WithFields(log.Fields{ "_module": "control", "_file": "metrics.go,", "_block": "get-versions", "error": err, }).Error("error getting plugin version") return nil, err } return mts, nil }
// Fetch transactionally retrieves all metrics which fall under namespace ns func (mc *metricCatalog) Fetch(ns core.Namespace) ([]*metricType, error) { mc.mutex.Lock() defer mc.mutex.Unlock() mtsi, err := mc.tree.Fetch(ns.Strings()) if err != nil { log.WithFields(log.Fields{ "_module": "control", "_file": "metrics.go,", "_block": "fetch", "error": err, }).Error("error fetching metrics") return nil, err } return mtsi, nil }
// findTuplesMatches returns all matched combination of queried tuples in incoming namespace, // where a tuple is in the form of `(host0;host1;host3)`. If the incoming namespace: // - does not contain any tuple, return the incoming namespace as the only item in output slice. // - contains a tuple, return the copies of incoming namespace with appropriate values set to namespaces' elements func findTuplesMatches(incomingNs core.Namespace) []core.Namespace { // How it works, exemplary incoming namespace: // "intel", "mock", "(host0;host1)", "(baz;bar)" // // the following 4 namespaces will be returned: // "intel", "mock", "host0", "baz" // "intel", "mock", "host1", "baz" // "intel", "mock", "host0", "bar" // "intel", "mock", "host1", "bar" matchedItems := make(map[int][]string) numOfPossibleCombinations := 1 for index, element := range incomingNs.Strings() { match := []string{} if ok, tupleItems := containsTuple(element); ok { match = tupleItems } else { match = []string{element} } // store matched items under current index of incoming namespace element matchedItems[index] = append(matchedItems[index], match...) // number of possible combinations increases N=len(match) times numOfPossibleCombinations = numOfPossibleCombinations * len(match) } //prepare slice for returned namespaces (results of tuple find) returnedNss := make([]core.Namespace, numOfPossibleCombinations) // initialize each of returned namespaces as a copy of incoming namespace // (copied original value, name and description of their elements) for i := 0; i < numOfPossibleCombinations; i++ { returnedNs := make([]core.NamespaceElement, len(incomingNs.Strings())) copy(returnedNs, incomingNs) returnedNss[i] = returnedNs } // set appropriate value to namespace's elements for index, items := range matchedItems { for i := range returnedNss { // retrieve the matched item (when 'i' exceeds the number of matched items, start from beginning) item := items[i%len(items)] returnedNss[i][index].Value = item } } return returnedNss }
// validateMetricNamespace validates metric namespace in terms of containing not allowed characters and ending with an asterisk func validateMetricNamespace(ns core.Namespace) error { name := "" for _, i := range ns { name += i.Value } for _, chars := range notAllowedChars { for _, ch := range chars { if strings.ContainsAny(name, ch) { return errorMetricContainsNotAllowedChars(ns.String()) } } } // plugin should NOT advertise metrics ending with a wildcard if strings.HasSuffix(name, "*") { return errorMetricEndsWithAsterisk(ns.String()) } return nil }
func memStat(ns core.Namespace, swagURL string) (*plugin.MetricType, error) { memType := ns.Strings()[3] switch { case regexp.MustCompile(`^/` + Vendor + `/` + Name + `/memory/free`).MatchString(ns.String()): metric, err := getMemStat(swagURL, memType) if err != nil { return nil, err } return &plugin.MetricType{ Namespace_: ns, Data_: metric, Timestamp_: time.Now(), }, nil case regexp.MustCompile(`^/` + Vendor + `/` + Name + `/memory/total`).MatchString(ns.String()): metric, err := getMemStat(swagURL, memType) if err != nil { return nil, err } return &plugin.MetricType{ Namespace_: ns, Data_: metric, Timestamp_: time.Now(), }, nil } return nil, fmt.Errorf("Unknown error processing %v", ns) }
// GetMetric retrieves a metric for a given requested namespace and version. // If provided a version of -1 the latest plugin will be returned. func (mc *metricCatalog) GetMetric(requested core.Namespace, version int) (*metricType, error) { mc.mutex.Lock() defer mc.mutex.Unlock() var ns core.Namespace catalogedmt, err := mc.tree.GetMetric(requested.Strings(), version) if err != nil { log.WithFields(log.Fields{ "_module": "control", "_file": "metrics.go,", "_block": "get-metric", "error": err, }).Error("error getting metric") return nil, err } ns = catalogedmt.Namespace() if isDynamic, _ := ns.IsDynamic(); isDynamic { // when namespace is dynamic and the cataloged namespace (e.g. ns=/intel/mock/*/bar) is different than // the requested (e.g. requested=/intel/mock/host0/bar), than specify an instance of dynamic element, // so as a result the dynamic element will have set a value (e.g. ns[2].Value equals "host0") if ns.String() != requested.String() { ns = specifyInstanceOfDynamicMetric(ns, requested) } } returnedmt := &metricType{ Plugin: catalogedmt.Plugin, namespace: ns, version: catalogedmt.Version(), lastAdvertisedTime: catalogedmt.LastAdvertisedTime(), tags: catalogedmt.Tags(), policy: catalogedmt.Plugin.ConfigPolicy.Get(catalogedmt.Namespace().Strings()), config: catalogedmt.Config(), unit: catalogedmt.Unit(), description: catalogedmt.Description(), subscriptions: catalogedmt.SubscriptionCount(), } return returnedmt, nil }
// createMetricNamespace returns metric namespace based on given `ns` which is used as a prefix; all dynamic elements // in the `metricName` are defined based on content of map `dynamicElements` func (creator *nsCreator) createMetricNamespace(ns core.Namespace, metricName string) (core.Namespace, error) { metricName = strings.TrimSpace(metricName) if len(metricName) == 0 { return nil, errors.New("Cannot create metric namespace: empty metric name") } elements := strings.Split(metricName, "/") // check if metricName contains only static elements if !strings.Contains(metricName, "*") { ns = ns.AddStaticElements(elements...) return ns, nil } // when metric name contains dynamic element iterate over elements for index, element := range elements { if element == "*" { // the following element is dynamic dynamicElement, ok := creator.dynamicElements[elements[index-1]] // check if this dynamic element is supported (name and description are available) if !ok { return nil, fmt.Errorf("Unknown dynamic element in metric `%s` under index %d", metricName, index) } // add recognize dynamic element (define its name and description) ns = ns.AddDynamicElement(dynamicElement.name, dynamicElement.description) if len(elements)-1 == index { // in case when an asterisk is the last element, add `value` at the end of ns ns = ns.AddStaticElement("value") } } else { // the following element is static ns = ns.AddStaticElement(element) } } if len(ns) == 0 { return nil, fmt.Errorf("Cannot create metric namespace for metric %s", metricName) } return ns, nil }
// Get retrieves a metric given a namespace and version. // If provided a version of -1 the latest plugin will be returned. func (mc *metricCatalog) Get(ns core.Namespace, version int) (*metricType, error) { mc.mutex.Lock() defer mc.mutex.Unlock() return mc.get(ns.Strings(), version) }
// Remove removes a metricType from the catalog and from matching map func (mc *metricCatalog) Remove(ns core.Namespace) { mc.mutex.Lock() defer mc.mutex.Unlock() mc.tree.Remove(ns.Strings()) }
func parseName(namespace core.Namespace) string { return strings.Join(namespace.Strings()[len(namespacePrefix):], "/") }
// GetVersions retrieves all versions of a given metric namespace. func (mc *metricCatalog) GetVersions(ns core.Namespace) ([]*metricType, error) { mc.mutex.Lock() defer mc.mutex.Unlock() return mc.getVersions(ns.Strings()) }