blob: d36495db52ce622821f44ac584e264e31aec1e1d [file] [log] [blame]
package cache
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import (
"bufio"
"errors"
"fmt"
"io"
"strconv"
"strings"
"github.com/apache/trafficcontrol/lib/go-log"
)
type astatsDataCsv struct {
Ats map[string]interface{}
}
func astatsCsvParseCsv(cacheName string, data io.Reader) (Statistics, map[string]interface{}, error) {
var stats Statistics
var err error
if data == nil {
log.Warnf("Cannot read stats data for cache '%s' - nil data reader", cacheName)
return stats, nil, errors.New("handler got nil reader")
}
var atsData astatsDataCsv
var allData []string
scanner := bufio.NewScanner(data)
for scanner.Scan() {
allData = append(allData, scanner.Text())
}
atsData.Ats = make(map[string]interface{}, len(allData))
for _, line := range allData {
delim := strings.IndexByte(line, ',')
// No delimiter found, skip this line as invalid
if delim < 0 {
continue
}
// Special cases where we just want the string value
if strings.Contains(line[0:delim], "proc.") || strings.Contains(line[0:delim], "inf.name") {
atsData.Ats[line[0:delim]] = line[delim+1:]
} else {
value, err := strconv.ParseFloat(line[delim+1:], 64)
// Skip values that dont parse
if err != nil {
continue
}
atsData.Ats[line[0:delim]] = value
}
}
if len(atsData.Ats) < 1 {
return stats, nil, errors.New("no 'global' data object found in stats_over_http payload")
}
statMap := atsData.Ats
// Handle system specific values and remove them from the map for precomputing to not have issues
if loadAvg, hadLoadAvg := statMap["proc.loadavg"].(string); !hadLoadAvg {
return stats, nil, fmt.Errorf("failed to parse loadavg line for cache '%s': %v", cacheName, "no proc.loadavg in Astats response")
} else if stats.Loadavg, err = LoadavgFromRawLine(loadAvg); err != nil {
return stats, nil, fmt.Errorf("parsing loadavg for cache '%s': %v", cacheName, err)
} else {
delete(statMap, "proc.loadavg")
}
if procNetDev, hadProcNetDev := statMap["proc.net.dev"].(string); !hadProcNetDev {
return stats, nil, fmt.Errorf("failed to parse interface line for cache '%s': %v", cacheName, "no proc.net.dev in Astats response")
} else if err := stats.AddInterfaceFromRawLine(procNetDev); err != nil {
return stats, nil, fmt.Errorf("failed to parse interface line for cache '%s': %v", cacheName, err)
} else {
delete(statMap, "proc.net.dev")
}
if infName, hadinf := statMap["inf.name"].(string); !hadinf {
return stats, nil, fmt.Errorf("failed to parse inf line for cache '%s': %v", cacheName, "no inf.name in Astats response")
} else if inf, err := stats.Interfaces[infName]; !err {
return stats, nil, errors.New("/proc/net/dev line didn't match reported interface line")
} else {
if infSpeed, hadspeed := statMap["inf.speed"].(float64); !hadspeed {
return stats, nil, fmt.Errorf("failed to parse interface speed line for cache '%s': %v", cacheName, "no inf.speed in Astats response")
} else {
inf.Speed = int64(infSpeed)
stats.Interfaces[infName] = inf
delete(statMap, "inf.speed")
delete(statMap, "inf.name")
}
}
// Clean up other non-stats entries
nonStats := []string{
"astatsLoad",
"lastReloadRequest",
"version",
"something",
"lastReload",
"configReloadRequests",
"configReloads",
}
for _, nonStat := range nonStats {
delete(statMap, nonStat)
}
if len(stats.Interfaces) < 1 {
return stats, nil, fmt.Errorf("cache '%s' had no interfaces", cacheName)
}
return stats, statMap, nil
}