// 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
}
Exemplo n.º 3
0
// 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
}