github.com/netdata/go.d.plugin@v0.58.1/modules/upsd/collect.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package upsd
     4  
     5  import (
     6  	"errors"
     7  	"strconv"
     8  	"strings"
     9  )
    10  
    11  func (u *Upsd) collect() (map[string]int64, error) {
    12  	if u.conn == nil {
    13  		conn, err := u.establishConnection()
    14  		if err != nil {
    15  			return nil, err
    16  		}
    17  		u.conn = conn
    18  	}
    19  
    20  	upsUnits, err := u.conn.upsUnits()
    21  	if err != nil {
    22  		if !errors.Is(err, errUpsdCommand) {
    23  			_ = u.conn.disconnect()
    24  			u.conn = nil
    25  		}
    26  		return nil, err
    27  	}
    28  
    29  	u.Debugf("found %d UPS units", len(upsUnits))
    30  
    31  	mx := make(map[string]int64)
    32  
    33  	u.collectUPSUnits(mx, upsUnits)
    34  
    35  	return mx, nil
    36  }
    37  
    38  func (u *Upsd) establishConnection() (upsdConn, error) {
    39  	conn := u.newUpsdConn(u.Config)
    40  
    41  	if err := conn.connect(); err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	if u.Username != "" && u.Password != "" {
    46  		if err := conn.authenticate(u.Username, u.Password); err != nil {
    47  			_ = conn.disconnect()
    48  			return nil, err
    49  		}
    50  	}
    51  
    52  	return conn, nil
    53  }
    54  
    55  func (u *Upsd) collectUPSUnits(mx map[string]int64, upsUnits []upsUnit) {
    56  	seen := make(map[string]bool)
    57  
    58  	for _, ups := range upsUnits {
    59  		seen[ups.name] = true
    60  		u.Debugf("collecting metrics UPS '%s'", ups.name)
    61  
    62  		if !u.upsUnits[ups.name] {
    63  			u.upsUnits[ups.name] = true
    64  			u.addUPSCharts(ups)
    65  		}
    66  
    67  		writeVar(mx, ups, varBatteryCharge)
    68  		writeVar(mx, ups, varBatteryRuntime)
    69  		writeVar(mx, ups, varBatteryVoltage)
    70  		writeVar(mx, ups, varBatteryVoltageNominal)
    71  
    72  		writeVar(mx, ups, varInputVoltage)
    73  		writeVar(mx, ups, varInputVoltageNominal)
    74  		writeVar(mx, ups, varInputCurrent)
    75  		writeVar(mx, ups, varInputCurrentNominal)
    76  		writeVar(mx, ups, varInputFrequency)
    77  		writeVar(mx, ups, varInputFrequencyNominal)
    78  
    79  		writeVar(mx, ups, varOutputVoltage)
    80  		writeVar(mx, ups, varOutputVoltageNominal)
    81  		writeVar(mx, ups, varOutputCurrent)
    82  		writeVar(mx, ups, varOutputCurrentNominal)
    83  		writeVar(mx, ups, varOutputFrequency)
    84  		writeVar(mx, ups, varOutputFrequencyNominal)
    85  
    86  		writeVar(mx, ups, varUpsLoad)
    87  		writeVar(mx, ups, varUpsRealPowerNominal)
    88  		writeVar(mx, ups, varUpsTemperature)
    89  		writeUpsLoadUsage(mx, ups)
    90  		writeUpsStatus(mx, ups)
    91  	}
    92  
    93  	for name := range u.upsUnits {
    94  		if !seen[name] {
    95  			delete(u.upsUnits, name)
    96  			u.removeUPSCharts(name)
    97  		}
    98  	}
    99  }
   100  
   101  func writeVar(mx map[string]int64, ups upsUnit, v string) {
   102  	s, ok := ups.vars[v]
   103  	if !ok {
   104  		return
   105  	}
   106  	n, err := strconv.ParseFloat(s, 64)
   107  	if err != nil {
   108  		return
   109  	}
   110  	mx[prefix(ups)+v] = int64(n * varPrecision)
   111  }
   112  
   113  func writeUpsLoadUsage(mx map[string]int64, ups upsUnit) {
   114  	if hasVar(ups.vars, varUpsRealPower) {
   115  		pow, _ := strconv.ParseFloat(ups.vars[varUpsRealPower], 64)
   116  		mx[prefix(ups)+"ups.load.usage"] = int64(pow * varPrecision)
   117  		return
   118  	}
   119  
   120  	if !hasVar(ups.vars, varUpsLoad) || !hasVar(ups.vars, varUpsRealPowerNominal) {
   121  		return
   122  	}
   123  	load, err := strconv.ParseFloat(ups.vars[varUpsLoad], 64)
   124  	if err != nil {
   125  		return
   126  	}
   127  	nomPower, err := strconv.ParseFloat(ups.vars[varUpsRealPowerNominal], 64)
   128  	if err != nil || nomPower == 0 {
   129  		return
   130  	}
   131  	mx[prefix(ups)+"ups.load.usage"] = int64((load / 100 * nomPower) * varPrecision)
   132  }
   133  
   134  // https://networkupstools.org/docs/developer-guide.chunked/ar01s04.html#_status_data
   135  var upsStatuses = map[string]bool{
   136  	"OL":      true,
   137  	"OB":      true,
   138  	"LB":      true,
   139  	"HB":      true,
   140  	"RB":      true,
   141  	"CHRG":    true,
   142  	"DISCHRG": true,
   143  	"BYPASS":  true,
   144  	"CAL":     true,
   145  	"OFF":     true,
   146  	"OVER":    true,
   147  	"TRIM":    true,
   148  	"BOOST":   true,
   149  	"FSD":     true,
   150  }
   151  
   152  func writeUpsStatus(mx map[string]int64, ups upsUnit) {
   153  	if !hasVar(ups.vars, varUpsStatus) {
   154  		return
   155  	}
   156  
   157  	px := prefix(ups) + "ups.status."
   158  
   159  	for st := range upsStatuses {
   160  		mx[px+st] = 0
   161  	}
   162  	mx[px+"other"] = 0
   163  
   164  	for _, st := range strings.Split(ups.vars[varUpsStatus], " ") {
   165  		if _, ok := upsStatuses[st]; ok {
   166  			mx[px+st] = 1
   167  		} else {
   168  			mx[px+"other"] = 1
   169  		}
   170  	}
   171  }
   172  
   173  func hasVar(vars map[string]string, v string) bool {
   174  	_, ok := vars[v]
   175  	return ok
   176  }
   177  
   178  func prefix(ups upsUnit) string {
   179  	return "ups_" + ups.name + "_"
   180  }