1// Copyright 2015 Google Inc. All Rights Reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package collector 16 17import ( 18 "encoding/json" 19 "fmt" 20 "io/ioutil" 21 "net/http" 22 "regexp" 23 "strconv" 24 "strings" 25 "time" 26 27 "github.com/google/cadvisor/info/v1" 28) 29 30type GenericCollector struct { 31 //name of the collector 32 name string 33 34 //holds information extracted from the config file for a collector 35 configFile Config 36 37 //holds information necessary to extract metrics 38 info *collectorInfo 39} 40 41type collectorInfo struct { 42 //minimum polling frequency among all metrics 43 minPollingFrequency time.Duration 44 45 //regular expresssions for all metrics 46 regexps []*regexp.Regexp 47 48 // Limit for the number of srcaped metrics. If the count is higher, 49 // no metrics will be returned. 50 metricCountLimit int 51} 52 53//Returns a new collector using the information extracted from the configfile 54func NewCollector(collectorName string, configFile []byte, metricCountLimit int) (*GenericCollector, error) { 55 var configInJSON Config 56 err := json.Unmarshal(configFile, &configInJSON) 57 if err != nil { 58 return nil, err 59 } 60 61 //TODO : Add checks for validity of config file (eg : Accurate JSON fields) 62 63 if len(configInJSON.MetricsConfig) == 0 { 64 return nil, fmt.Errorf("No metrics provided in config") 65 } 66 67 minPollFrequency := time.Duration(0) 68 regexprs := make([]*regexp.Regexp, len(configInJSON.MetricsConfig)) 69 70 for ind, metricConfig := range configInJSON.MetricsConfig { 71 // Find the minimum specified polling frequency in metric config. 72 if metricConfig.PollingFrequency != 0 { 73 if minPollFrequency == 0 || metricConfig.PollingFrequency < minPollFrequency { 74 minPollFrequency = metricConfig.PollingFrequency 75 } 76 } 77 78 regexprs[ind], err = regexp.Compile(metricConfig.Regex) 79 if err != nil { 80 return nil, fmt.Errorf("Invalid regexp %v for metric %v", metricConfig.Regex, metricConfig.Name) 81 } 82 } 83 84 // Minimum supported polling frequency is 1s. 85 minSupportedFrequency := 1 * time.Second 86 if minPollFrequency < minSupportedFrequency { 87 minPollFrequency = minSupportedFrequency 88 } 89 90 if len(configInJSON.MetricsConfig) > metricCountLimit { 91 return nil, fmt.Errorf("Too many metrics defined: %d limit: %d", len(configInJSON.MetricsConfig), metricCountLimit) 92 } 93 94 return &GenericCollector{ 95 name: collectorName, 96 configFile: configInJSON, 97 info: &collectorInfo{ 98 minPollingFrequency: minPollFrequency, 99 regexps: regexprs, 100 metricCountLimit: metricCountLimit, 101 }, 102 }, nil 103} 104 105//Returns name of the collector 106func (collector *GenericCollector) Name() string { 107 return collector.name 108} 109 110func (collector *GenericCollector) configToSpec(config MetricConfig) v1.MetricSpec { 111 return v1.MetricSpec{ 112 Name: config.Name, 113 Type: config.MetricType, 114 Format: config.DataType, 115 Units: config.Units, 116 } 117} 118 119func (collector *GenericCollector) GetSpec() []v1.MetricSpec { 120 specs := []v1.MetricSpec{} 121 for _, metricConfig := range collector.configFile.MetricsConfig { 122 spec := collector.configToSpec(metricConfig) 123 specs = append(specs, spec) 124 } 125 return specs 126} 127 128//Returns collected metrics and the next collection time of the collector 129func (collector *GenericCollector) Collect(metrics map[string][]v1.MetricVal) (time.Time, map[string][]v1.MetricVal, error) { 130 currentTime := time.Now() 131 nextCollectionTime := currentTime.Add(time.Duration(collector.info.minPollingFrequency)) 132 133 uri := collector.configFile.Endpoint 134 response, err := http.Get(uri) 135 if err != nil { 136 return nextCollectionTime, nil, err 137 } 138 139 defer response.Body.Close() 140 141 pageContent, err := ioutil.ReadAll(response.Body) 142 if err != nil { 143 return nextCollectionTime, nil, err 144 } 145 146 var errorSlice []error 147 148 for ind, metricConfig := range collector.configFile.MetricsConfig { 149 matchString := collector.info.regexps[ind].FindStringSubmatch(string(pageContent)) 150 if matchString != nil { 151 if metricConfig.DataType == v1.FloatType { 152 regVal, err := strconv.ParseFloat(strings.TrimSpace(matchString[1]), 64) 153 if err != nil { 154 errorSlice = append(errorSlice, err) 155 } 156 metrics[metricConfig.Name] = []v1.MetricVal{ 157 {FloatValue: regVal, Timestamp: currentTime}, 158 } 159 } else if metricConfig.DataType == v1.IntType { 160 regVal, err := strconv.ParseInt(strings.TrimSpace(matchString[1]), 10, 64) 161 if err != nil { 162 errorSlice = append(errorSlice, err) 163 } 164 metrics[metricConfig.Name] = []v1.MetricVal{ 165 {IntValue: regVal, Timestamp: currentTime}, 166 } 167 168 } else { 169 errorSlice = append(errorSlice, fmt.Errorf("Unexpected value of 'data_type' for metric '%v' in config ", metricConfig.Name)) 170 } 171 } else { 172 errorSlice = append(errorSlice, fmt.Errorf("No match found for regexp: %v for metric '%v' in config", metricConfig.Regex, metricConfig.Name)) 173 } 174 } 175 return nextCollectionTime, metrics, compileErrors(errorSlice) 176} 177