// Function to check properness of configuration parameters // and set plugin attribute accordingly func (sc *SmartCollector) setProcDevPath(cfg interface{}) error { sc.initializedMutex.Lock() defer sc.initializedMutex.Unlock() if sc.initialized { return nil } procPath, err := config.GetConfigItem(cfg, "proc_path") if err == nil && len(procPath.(string)) > 0 { procPathStats, err := os.Stat(procPath.(string)) if err != nil { return err } if !procPathStats.IsDir() { return errors.New(fmt.Sprintf("%s is not a directory", procPath.(string))) } sc.proc_path = procPath.(string) } devPath, err := config.GetConfigItem(cfg, "dev_path") if err == nil && len(devPath.(string)) > 0 { devPathStats, err := os.Stat(devPath.(string)) if err != nil { return err } if !devPathStats.IsDir() { return errors.New(fmt.Sprintf("%s is not a directory", devPath.(string))) } sc.dev_path = devPath.(string) } if sysUtilProvider == nil { sysUtilProvider = NewSysutilProvider(sc.proc_path, sc.dev_path) } sc.initialized = true return nil }
func getConfig(cfg interface{}) (map[string]string, error) { items := make(map[string]string) var ok bool // Note: although config.GetConfigItems can accept multiple config parameter names, it appears that if // any of those names are missing, GetConfigItems() will `return nil, err`. Since this plugin will work // individually with master or agent (or both), we break this up into two separate lookups and then // test for the existence of the configuration parameter to determine which metric types are available. // We expect the value of "master" in the global config to follow the convention "192.168.99.100:5050" master_cfg, master_err := config.GetConfigItem(cfg, "master") // We expect the value of "agent" in the global config to follow the convention "192.168.99.100:5051" agent_cfg, agent_err := config.GetConfigItem(cfg, "agent") if master_err != nil && agent_err != nil { e := fmt.Errorf("error: no global config specified for 'master' and 'agent'.") log.Error(e) return items, e } items["master"], ok = master_cfg.(string) if !ok { log.Warn("No global config specified for 'master', only 'agent' metrics will be collected.") } items["agent"], ok = agent_cfg.(string) if !ok { log.Warn("No global config specified for 'agent', only 'master' metrics will be collected.") } return items, nil }
// setConfig extracts config item from Global Config or Metric Config, parses its contents (mainly information // about databases and queries) and assigned them to appriopriate DBiPlugin fields func (dbiPlg *DbiPlugin) setConfig(cfg interface{}) error { setFile, err := config.GetConfigItem(cfg, "setfile") if err != nil { // cannot get config item return err } dbiPlg.databases, dbiPlg.queries, err = parser.GetDBItemsFromConfig(setFile.(string)) if err != nil { // cannot parse sql config contents return err } return nil }
// ReadConfig deserializes plugin's configuration from metric or global config // given in cfg. out shoud be pointer to structure. If field of structure has no // tag it's name is used as config key, if it has named tag "c" with values // delimited by commas, first value is used as config key. If second value is // "weak" and field of structure is string, string representation of read value // is written. "weak" is optional for string fields and currently forbidden for // other types. // Returns nil if operation succeeded or relevant error. func ReadConfig(cfg interface{}, out interface{}) error { outStructValue := reflect.ValueOf(out).Elem() outStructType := outStructValue.Type() for i := 0; i < outStructType.NumField(); i++ { field := outStructType.Field(i) tags := strings.Split(field.Tag.Get("c"), ",") tag := strings.TrimSpace(tags[0]) if tag == "" { tag = field.Name } value, err := config.GetConfigItem(cfg, tag) if err != nil { return err } fieldValue := outStructValue.Field(i) if len(tags) > 1 && strings.TrimSpace(tags[1]) == "weak" { if field.Type.Kind() != reflect.String { return fmt.Errorf("field %s has to be string: %s found", field.Name, field.Type) } fieldValue.SetString(fmt.Sprint(value)) } else { if !reflect.TypeOf(value).ConvertibleTo(field.Type) { return fmt.Errorf("cannot assing config attribute %v to field %v: %v is not convertible to %v", tag, field.Name, reflect.TypeOf(value), field.Type, ) } converted := reflect.ValueOf(value).Convert(field.Type) fieldValue.Set(converted) } } return nil }
// CollectMetrics returns list of requested metric values // It returns error in case retrieval was not successful func (c *collector) CollectMetrics(metricTypes []plugin.MetricType) ([]plugin.MetricType, error) { // get admin tenant from configuration. admin tenant is needed for gathering volumes and snapshots metrics at once item, err := config.GetConfigItem(metricTypes[0], "tenant") if err != nil { return nil, err } admin := item.(string) // populate information about all available tenants if len(c.allTenants) == 0 { c.allTenants, err = getTenants(metricTypes[0]) if err != nil { return nil, err } } // iterate over metric types to resolve needed collection calls // for requested tenants collectTenants := str.InitSet() var collectLimits, collectVolumes, collectSnapshots bool for _, metricType := range metricTypes { namespace := metricType.Namespace() if len(namespace) < 6 { return nil, fmt.Errorf("Incorrect namespace lenth. Expected 6 is %d", len(namespace)) } tenant := namespace[3].Value collectTenants.Add(tenant) if str.Contains(namespace.Strings(), "limits") { collectLimits = true } else if str.Contains(namespace.Strings(), "volumes") { collectVolumes = true } else { collectSnapshots = true } } allSnapshots := map[string]types.Snapshots{} allVolumes := map[string]types.Volumes{} // collect volumes and snapshots separately by authenticating to admin { if err := c.authenticate(metricTypes[0], admin); err != nil { return nil, err } provider := c.providers[admin] var done sync.WaitGroup errChn := make(chan error, 2) // Collect volumes if collectVolumes { done.Add(1) go func() { defer done.Done() volumes, err := c.service.GetVolumes(provider) if err != nil { errChn <- err } for tenantId, volumeCount := range volumes { tenantName := c.allTenants[tenantId] allVolumes[tenantName] = volumeCount } }() } // Collect snapshots if collectSnapshots { done.Add(1) go func() { defer done.Done() snapshots, err := c.service.GetSnapshots(provider) if err != nil { errChn <- err } for tenantId, snapshotCount := range snapshots { tenantName := c.allTenants[tenantId] allSnapshots[tenantName] = snapshotCount } }() } done.Wait() close(errChn) if e := <-errChn; e != nil { return nil, e } } // Collect limits per each tenant only if not already collected (plugin lifetime scope) { var done sync.WaitGroup errChn := make(chan error, collectTenants.Size()) for _, tenant := range collectTenants.Elements() { _, found := c.allLimits[tenant] if collectLimits && !found { if err := c.authenticate(metricTypes[0], tenant); err != nil { return nil, err } provider := c.providers[tenant] done.Add(1) go func(p *gophercloud.ProviderClient, t string) { defer done.Done() limits, err := c.service.GetLimits(p) if err != nil { errChn <- err } c.allLimits[t] = limits }(provider, tenant) } } done.Wait() close(errChn) if e := <-errChn; e != nil { return nil, e } } metrics := []plugin.MetricType{} for _, metricType := range metricTypes { namespace := metricType.Namespace().Strings() tenant := namespace[3] // Construct temporary struct to accommodate all gathered metrics metricContainer := struct { S types.Snapshots `json:"snapshots"` V types.Volumes `json:"volumes"` L types.Limits `json:"limits"` }{ allSnapshots[tenant], allVolumes[tenant], c.allLimits[tenant], } // Extract values by namespace from temporary struct and create metrics metric := plugin.MetricType{ Timestamp_: time.Now(), Namespace_: metricType.Namespace(), Data_: ns.GetValueByNamespace(metricContainer, namespace[4:]), } metrics = append(metrics, metric) } return metrics, nil }