// Copyright 2015 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package collector import ( "encoding/json" "fmt" "io/ioutil" "net/http" "regexp" "strconv" "strings" "time" "github.com/google/cadvisor/info/v1" ) type GenericCollector struct { //name of the collector name string //holds information extracted from the config file for a collector configFile Config //holds information necessary to extract metrics info *collectorInfo } type collectorInfo struct { //minimum polling frequency among all metrics minPollingFrequency time.Duration //regular expresssions for all metrics regexps []*regexp.Regexp } //Returns a new collector using the information extracted from the configfile func NewCollector(collectorName string, configFile []byte) (*GenericCollector, error) { var configInJSON Config err := json.Unmarshal(configFile, &configInJSON) if err != nil { return nil, err } //TODO : Add checks for validity of config file (eg : Accurate JSON fields) if len(configInJSON.MetricsConfig) == 0 { return nil, fmt.Errorf("No metrics provided in config") } minPollFrequency := time.Duration(0) regexprs := make([]*regexp.Regexp, len(configInJSON.MetricsConfig)) for ind, metricConfig := range configInJSON.MetricsConfig { // Find the minimum specified polling frequency in metric config. if metricConfig.PollingFrequency != 0 { if minPollFrequency == 0 || metricConfig.PollingFrequency < minPollFrequency { minPollFrequency = metricConfig.PollingFrequency } } regexprs[ind], err = regexp.Compile(metricConfig.Regex) if err != nil { return nil, fmt.Errorf("Invalid regexp %v for metric %v", metricConfig.Regex, metricConfig.Name) } } // Minimum supported polling frequency is 1s. minSupportedFrequency := 1 * time.Second if minPollFrequency < minSupportedFrequency { minPollFrequency = minSupportedFrequency } return &GenericCollector{ name: collectorName, configFile: configInJSON, info: &collectorInfo{ minPollingFrequency: minPollFrequency, regexps: regexprs}, }, nil } //Returns name of the collector func (collector *GenericCollector) Name() string { return collector.name } func (collector *GenericCollector) configToSpec(config MetricConfig) v1.MetricSpec { return v1.MetricSpec{ Name: config.Name, Type: config.MetricType, Format: config.DataType, Units: config.Units, } } func (collector *GenericCollector) GetSpec() []v1.MetricSpec { specs := []v1.MetricSpec{} for _, metricConfig := range collector.configFile.MetricsConfig { spec := collector.configToSpec(metricConfig) specs = append(specs, spec) } return specs } //Returns collected metrics and the next collection time of the collector func (collector *GenericCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) { currentTime := time.Now() nextCollectionTime := currentTime.Add(time.Duration(collector.info.minPollingFrequency)) uri := collector.configFile.Endpoint response, err := http.Get(uri) if err != nil { return nextCollectionTime, nil, err } defer response.Body.Close() pageContent, err := ioutil.ReadAll(response.Body) if err != nil { return nextCollectionTime, nil, err } var errorSlice []error for ind, metricConfig := range collector.configFile.MetricsConfig { matchString := collector.info.regexps[ind].FindStringSubmatch(string(pageContent)) if matchString != nil { if metricConfig.DataType == v1.FloatType { regVal, err := strconv.ParseFloat(strings.TrimSpace(matchString[1]), 64) if err != nil { errorSlice = append(errorSlice, err) } metrics[metricConfig.Name] = []v1.MetricVal{ {FloatValue: regVal, Timestamp: currentTime}, } } else if metricConfig.DataType == v1.IntType { regVal, err := strconv.ParseInt(strings.TrimSpace(matchString[1]), 10, 64) if err != nil { errorSlice = append(errorSlice, err) } metrics[metricConfig.Name] = []v1.MetricVal{ {IntValue: regVal, Timestamp: currentTime}, } } else { errorSlice = append(errorSlice, fmt.Errorf("Unexpected value of 'data_type' for metric '%v' in config ", metricConfig.Name)) } } else { errorSlice = append(errorSlice, fmt.Errorf("No match found for regexp: %v for metric '%v' in config", metricConfig.Regex, metricConfig.Name)) } } return nextCollectionTime, metrics, compileErrors(errorSlice) }