fix(inputs.upsd): Add additional fields to upsd from NUT (#14447)
This commit is contained in:
parent
cbaca43e36
commit
a1eb9f55c6
|
|
@ -33,6 +33,19 @@ See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||||
## consistently as floats to avoid database conflicts where some numbers are
|
## consistently as floats to avoid database conflicts where some numbers are
|
||||||
## parsed as integers and others as floats.
|
## parsed as integers and others as floats.
|
||||||
# force_float = false
|
# force_float = false
|
||||||
|
|
||||||
|
## Collect additional fields if they are available for the UPS
|
||||||
|
## The fields need to be specified as NUT variable names, see
|
||||||
|
## https://networkupstools.org/docs/developer-guide.chunked/apas02.html
|
||||||
|
## Wildcards are accepted.
|
||||||
|
# additional_fields = []
|
||||||
|
|
||||||
|
## Dump information for debugging
|
||||||
|
## Allows to print the raw variables (and corresponding types) as received
|
||||||
|
## from the NUT server ONCE for each UPS. The output is only available when
|
||||||
|
## running Telegraf in debug-mode.
|
||||||
|
## Please attach this information when reporting issues!
|
||||||
|
# dump_raw_variables = false
|
||||||
```
|
```
|
||||||
|
|
||||||
## Metrics
|
## Metrics
|
||||||
|
|
|
||||||
|
|
@ -12,3 +12,16 @@
|
||||||
## consistently as floats to avoid database conflicts where some numbers are
|
## consistently as floats to avoid database conflicts where some numbers are
|
||||||
## parsed as integers and others as floats.
|
## parsed as integers and others as floats.
|
||||||
# force_float = false
|
# force_float = false
|
||||||
|
|
||||||
|
## Collect additional fields if they are available for the UPS
|
||||||
|
## The fields need to be specified as NUT variable names, see
|
||||||
|
## https://networkupstools.org/docs/developer-guide.chunked/apas02.html
|
||||||
|
## Wildcards are accepted.
|
||||||
|
# additional_fields = []
|
||||||
|
|
||||||
|
## Dump information for debugging
|
||||||
|
## Allows to print the raw variables (and corresponding types) as received
|
||||||
|
## from the NUT server ONCE for each UPS. The output is only available when
|
||||||
|
## running Telegraf in debug-mode.
|
||||||
|
## Please attach this information when reporting issues!
|
||||||
|
# dump_raw_variables = false
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
upsd,model=CP900EPFCLCD,serial=0,status_OL=true,ups_name=fake battery_charge_percent=100i,battery_mfr_date="CPS",battery_runtime_low=300i,battery_voltage=24,firmware="",input_transfer_high=260i,input_transfer_low=170i,input_voltage=228,load_percent=13i,nominal_battery_voltage=24i,nominal_input_voltage=230i,nominal_power=540i,output_voltage=228,status_flags=8u,time_left_ns=4020000000000i,ups_delay_shutdown=20i,ups_delay_start=30i,ups_status="OL"
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
[[inputs.upsd]]
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
battery.charge: NUMBER
|
||||||
|
battery.charge.low: STRING
|
||||||
|
battery.charge.warning: NUMBER
|
||||||
|
battery.mfr.date: NUMBER
|
||||||
|
battery.runtime: NUMBER
|
||||||
|
battery.runtime.low: STRING
|
||||||
|
battery.type: NUMBER
|
||||||
|
battery.voltage: NUMBER
|
||||||
|
battery.voltage.nominal: NUMBER
|
||||||
|
device.mfr: NUMBER
|
||||||
|
device.model: NUMBER
|
||||||
|
device.serial: NUMBER
|
||||||
|
device.type: NUMBER
|
||||||
|
driver.debug: NUMBER
|
||||||
|
driver.flag.allow_killpower: NUMBER
|
||||||
|
driver.name: NUMBER
|
||||||
|
driver.parameter.pollfreq: NUMBER
|
||||||
|
driver.parameter.pollinterval: NUMBER
|
||||||
|
driver.parameter.port: NUMBER
|
||||||
|
driver.parameter.product: NUMBER
|
||||||
|
driver.parameter.productid: NUMBER
|
||||||
|
driver.parameter.serial: NUMBER
|
||||||
|
driver.parameter.synchronous: NUMBER
|
||||||
|
driver.parameter.vendor: NUMBER
|
||||||
|
driver.parameter.vendorid: NUMBER
|
||||||
|
driver.state: NUMBER
|
||||||
|
driver.version:
|
||||||
|
driver.version.data: NUMBER
|
||||||
|
driver.version.internal: NUMBER
|
||||||
|
driver.version.usb: NUMBER
|
||||||
|
input.transfer.high: STRING
|
||||||
|
input.transfer.low: STRING
|
||||||
|
input.voltage: NUMBER
|
||||||
|
input.voltage.nominal: NUMBER
|
||||||
|
output.voltage: NUMBER
|
||||||
|
ups.beeper.status: NUMBER
|
||||||
|
ups.delay.shutdown: STRING
|
||||||
|
ups.delay.start: STRING
|
||||||
|
ups.load: NUMBER
|
||||||
|
ups.mfr: NUMBER
|
||||||
|
ups.model: NUMBER
|
||||||
|
ups.productid: NUMBER
|
||||||
|
ups.realpower.nominal: NUMBER
|
||||||
|
ups.serial: NUMBER
|
||||||
|
ups.status: NUMBER
|
||||||
|
ups.test.result: NUMBER
|
||||||
|
ups.timer.shutdown: NUMBER
|
||||||
|
ups.timer.start: NUMBER
|
||||||
|
ups.vendorid: NUMBER
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
battery.charge: 100
|
||||||
|
battery.charge.low: 10
|
||||||
|
battery.charge.warning: 20
|
||||||
|
battery.mfr.date: CPS
|
||||||
|
battery.runtime: 4020
|
||||||
|
battery.runtime.low: 300
|
||||||
|
battery.type: PbAcid
|
||||||
|
battery.voltage: 24.0
|
||||||
|
battery.voltage.nominal: 24
|
||||||
|
device.mfr: CPS
|
||||||
|
device.model: CP900EPFCLCD
|
||||||
|
device.serial: 000000000000
|
||||||
|
device.type: ups
|
||||||
|
driver.debug: 0
|
||||||
|
driver.flag.allow_killpower: 0
|
||||||
|
driver.name: usbhid-ups
|
||||||
|
driver.parameter.pollfreq: 30
|
||||||
|
driver.parameter.pollinterval: 2
|
||||||
|
driver.parameter.port: auto
|
||||||
|
driver.parameter.product: CP900EPFCLCD
|
||||||
|
driver.parameter.productid: 0501
|
||||||
|
driver.parameter.serial: 000000000000
|
||||||
|
driver.parameter.synchronous: auto
|
||||||
|
driver.parameter.vendor: CPS
|
||||||
|
driver.parameter.vendorid: 0764
|
||||||
|
driver.state: quiet
|
||||||
|
driver.version: 2.8.1
|
||||||
|
driver.version.data: CyberPower HID 0.8
|
||||||
|
driver.version.internal: 0.52
|
||||||
|
driver.version.usb: libusb-1.0.26 (API: 0x1000109)
|
||||||
|
input.transfer.high: 260
|
||||||
|
input.transfer.low: 170
|
||||||
|
input.voltage: 228.0
|
||||||
|
input.voltage.nominal: 230
|
||||||
|
output.voltage: 228.0
|
||||||
|
ups.beeper.status: enabled
|
||||||
|
ups.delay.shutdown: 20
|
||||||
|
ups.delay.start: 30
|
||||||
|
ups.load: 13
|
||||||
|
ups.mfr: CPS
|
||||||
|
ups.model: CP900EPFCLCD
|
||||||
|
ups.productid: 0501
|
||||||
|
ups.realpower.nominal: 540
|
||||||
|
ups.serial: 000000000000
|
||||||
|
ups.status: OL
|
||||||
|
ups.test.result: No test initiated
|
||||||
|
ups.timer.shutdown: -60
|
||||||
|
ups.timer.start: -60
|
||||||
|
ups.vendorid: 0764
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
upsd,model=CP900EPFCLCD,serial=0,status_OL=true,ups_name=fake battery_charge_low=10i,battery_charge_percent=100i,battery_charge_warning=20i,battery_mfr_date="CPS",battery_runtime_low=300i,battery_type="PbAcid",battery_voltage=24,firmware="",input_transfer_high=260i,input_transfer_low=170i,input_voltage=228,load_percent=13i,nominal_battery_voltage=24i,nominal_input_voltage=230i,nominal_power=540i,output_voltage=228,status_flags=8u,time_left_ns=4020000000000i,ups_delay_shutdown=20i,ups_delay_start=30i,ups_status="OL" 1704213086754003102
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
[[inputs.upsd]]
|
||||||
|
additional_fields = ["battery.*"]
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
battery.charge: NUMBER
|
||||||
|
battery.charge.low: STRING
|
||||||
|
battery.charge.warning: NUMBER
|
||||||
|
battery.mfr.date: NUMBER
|
||||||
|
battery.runtime: NUMBER
|
||||||
|
battery.runtime.low: STRING
|
||||||
|
battery.type: NUMBER
|
||||||
|
battery.voltage: NUMBER
|
||||||
|
battery.voltage.nominal: NUMBER
|
||||||
|
device.mfr: NUMBER
|
||||||
|
device.model: NUMBER
|
||||||
|
device.serial: NUMBER
|
||||||
|
device.type: NUMBER
|
||||||
|
driver.debug: NUMBER
|
||||||
|
driver.flag.allow_killpower: NUMBER
|
||||||
|
driver.name: NUMBER
|
||||||
|
driver.parameter.pollfreq: NUMBER
|
||||||
|
driver.parameter.pollinterval: NUMBER
|
||||||
|
driver.parameter.port: NUMBER
|
||||||
|
driver.parameter.product: NUMBER
|
||||||
|
driver.parameter.productid: NUMBER
|
||||||
|
driver.parameter.serial: NUMBER
|
||||||
|
driver.parameter.synchronous: NUMBER
|
||||||
|
driver.parameter.vendor: NUMBER
|
||||||
|
driver.parameter.vendorid: NUMBER
|
||||||
|
driver.state: NUMBER
|
||||||
|
driver.version:
|
||||||
|
driver.version.data: NUMBER
|
||||||
|
driver.version.internal: NUMBER
|
||||||
|
driver.version.usb: NUMBER
|
||||||
|
input.transfer.high: STRING
|
||||||
|
input.transfer.low: STRING
|
||||||
|
input.voltage: NUMBER
|
||||||
|
input.voltage.nominal: NUMBER
|
||||||
|
output.voltage: NUMBER
|
||||||
|
ups.beeper.status: NUMBER
|
||||||
|
ups.delay.shutdown: STRING
|
||||||
|
ups.delay.start: STRING
|
||||||
|
ups.load: NUMBER
|
||||||
|
ups.mfr: NUMBER
|
||||||
|
ups.model: NUMBER
|
||||||
|
ups.productid: NUMBER
|
||||||
|
ups.realpower.nominal: NUMBER
|
||||||
|
ups.serial: NUMBER
|
||||||
|
ups.status: NUMBER
|
||||||
|
ups.test.result: NUMBER
|
||||||
|
ups.timer.shutdown: NUMBER
|
||||||
|
ups.timer.start: NUMBER
|
||||||
|
ups.vendorid: NUMBER
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
battery.charge: 100
|
||||||
|
battery.charge.low: 10
|
||||||
|
battery.charge.warning: 20
|
||||||
|
battery.mfr.date: CPS
|
||||||
|
battery.runtime: 4020
|
||||||
|
battery.runtime.low: 300
|
||||||
|
battery.type: PbAcid
|
||||||
|
battery.voltage: 24.0
|
||||||
|
battery.voltage.nominal: 24
|
||||||
|
device.mfr: CPS
|
||||||
|
device.model: CP900EPFCLCD
|
||||||
|
device.serial: 000000000000
|
||||||
|
device.type: ups
|
||||||
|
driver.debug: 0
|
||||||
|
driver.flag.allow_killpower: 0
|
||||||
|
driver.name: usbhid-ups
|
||||||
|
driver.parameter.pollfreq: 30
|
||||||
|
driver.parameter.pollinterval: 2
|
||||||
|
driver.parameter.port: auto
|
||||||
|
driver.parameter.product: CP900EPFCLCD
|
||||||
|
driver.parameter.productid: 0501
|
||||||
|
driver.parameter.serial: 000000000000
|
||||||
|
driver.parameter.synchronous: auto
|
||||||
|
driver.parameter.vendor: CPS
|
||||||
|
driver.parameter.vendorid: 0764
|
||||||
|
driver.state: quiet
|
||||||
|
driver.version: 2.8.1
|
||||||
|
driver.version.data: CyberPower HID 0.8
|
||||||
|
driver.version.internal: 0.52
|
||||||
|
driver.version.usb: libusb-1.0.26 (API: 0x1000109)
|
||||||
|
input.transfer.high: 260
|
||||||
|
input.transfer.low: 170
|
||||||
|
input.voltage: 228.0
|
||||||
|
input.voltage.nominal: 230
|
||||||
|
output.voltage: 228.0
|
||||||
|
ups.beeper.status: enabled
|
||||||
|
ups.delay.shutdown: 20
|
||||||
|
ups.delay.start: 30
|
||||||
|
ups.load: 13
|
||||||
|
ups.mfr: CPS
|
||||||
|
ups.model: CP900EPFCLCD
|
||||||
|
ups.productid: 0501
|
||||||
|
ups.realpower.nominal: 540
|
||||||
|
ups.serial: 000000000000
|
||||||
|
ups.status: OL
|
||||||
|
ups.test.result: No test initiated
|
||||||
|
ups.timer.shutdown: -60
|
||||||
|
ups.timer.start: -60
|
||||||
|
ups.vendorid: 0764
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
upsd,model=CP900EPFCLCD,serial=0,status_OL=true,ups_name=fake battery_charge_low=10i,battery_charge_percent=100i,battery_charge_warning=20i,battery_mfr_date="CPS",battery_runtime_low=300i,battery_type="PbAcid",battery_voltage=24,device_mfr="CPS",device_type="ups",driver_debug=0i,driver_flag_allow_killpower=0i,driver_name="usbhid-ups",driver_parameter_pollfreq=30i,driver_parameter_pollinterval=2i,driver_parameter_port="auto",driver_parameter_product="CP900EPFCLCD",driver_parameter_productid=501i,driver_parameter_serial=0i,driver_parameter_synchronous="auto",driver_parameter_vendor="CPS",driver_parameter_vendorid=764i,driver_state="quiet",driver_version="2.8.1",driver_version_data="CyberPower HID 0.8",driver_version_internal=0.52,driver_version_usb="libusb-1.0.26 (API: 0x1000109)",firmware="",input_transfer_high=260i,input_transfer_low=170i,input_voltage=228,load_percent=13i,nominal_battery_voltage=24i,nominal_input_voltage=230i,nominal_power=540i,output_voltage=228,status_flags=8u,time_left_ns=4020000000000i,ups_beeper_status=true,ups_delay_shutdown=20i,ups_delay_start=30i,ups_mfr="CPS",ups_model="CP900EPFCLCD",ups_productid=501i,ups_serial=0i,ups_status="OL",ups_test_result="No test initiated",ups_timer_shutdown=-60i,ups_timer_start=-60i,ups_vendorid=764i
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
[[inputs.upsd]]
|
||||||
|
additional_fields = ["*"]
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
battery.charge: NUMBER
|
||||||
|
battery.charge.low: STRING
|
||||||
|
battery.charge.warning: NUMBER
|
||||||
|
battery.mfr.date: NUMBER
|
||||||
|
battery.runtime: NUMBER
|
||||||
|
battery.runtime.low: STRING
|
||||||
|
battery.type: NUMBER
|
||||||
|
battery.voltage: NUMBER
|
||||||
|
battery.voltage.nominal: NUMBER
|
||||||
|
device.mfr: NUMBER
|
||||||
|
device.model: NUMBER
|
||||||
|
device.serial: NUMBER
|
||||||
|
device.type: NUMBER
|
||||||
|
driver.debug: NUMBER
|
||||||
|
driver.flag.allow_killpower: NUMBER
|
||||||
|
driver.name: NUMBER
|
||||||
|
driver.parameter.pollfreq: NUMBER
|
||||||
|
driver.parameter.pollinterval: NUMBER
|
||||||
|
driver.parameter.port: NUMBER
|
||||||
|
driver.parameter.product: NUMBER
|
||||||
|
driver.parameter.productid: NUMBER
|
||||||
|
driver.parameter.serial: NUMBER
|
||||||
|
driver.parameter.synchronous: NUMBER
|
||||||
|
driver.parameter.vendor: NUMBER
|
||||||
|
driver.parameter.vendorid: NUMBER
|
||||||
|
driver.state: NUMBER
|
||||||
|
driver.version:
|
||||||
|
driver.version.data: NUMBER
|
||||||
|
driver.version.internal: NUMBER
|
||||||
|
driver.version.usb: NUMBER
|
||||||
|
input.transfer.high: STRING
|
||||||
|
input.transfer.low: STRING
|
||||||
|
input.voltage: NUMBER
|
||||||
|
input.voltage.nominal: NUMBER
|
||||||
|
output.voltage: NUMBER
|
||||||
|
ups.beeper.status: NUMBER
|
||||||
|
ups.delay.shutdown: STRING
|
||||||
|
ups.delay.start: STRING
|
||||||
|
ups.load: NUMBER
|
||||||
|
ups.mfr: NUMBER
|
||||||
|
ups.model: NUMBER
|
||||||
|
ups.productid: NUMBER
|
||||||
|
ups.realpower.nominal: NUMBER
|
||||||
|
ups.serial: NUMBER
|
||||||
|
ups.status: NUMBER
|
||||||
|
ups.test.result: NUMBER
|
||||||
|
ups.timer.shutdown: NUMBER
|
||||||
|
ups.timer.start: NUMBER
|
||||||
|
ups.vendorid: NUMBER
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
battery.charge: 100
|
||||||
|
battery.charge.low: 10
|
||||||
|
battery.charge.warning: 20
|
||||||
|
battery.mfr.date: CPS
|
||||||
|
battery.runtime: 4020
|
||||||
|
battery.runtime.low: 300
|
||||||
|
battery.type: PbAcid
|
||||||
|
battery.voltage: 24.0
|
||||||
|
battery.voltage.nominal: 24
|
||||||
|
device.mfr: CPS
|
||||||
|
device.model: CP900EPFCLCD
|
||||||
|
device.serial: 000000000000
|
||||||
|
device.type: ups
|
||||||
|
driver.debug: 0
|
||||||
|
driver.flag.allow_killpower: 0
|
||||||
|
driver.name: usbhid-ups
|
||||||
|
driver.parameter.pollfreq: 30
|
||||||
|
driver.parameter.pollinterval: 2
|
||||||
|
driver.parameter.port: auto
|
||||||
|
driver.parameter.product: CP900EPFCLCD
|
||||||
|
driver.parameter.productid: 0501
|
||||||
|
driver.parameter.serial: 000000000000
|
||||||
|
driver.parameter.synchronous: auto
|
||||||
|
driver.parameter.vendor: CPS
|
||||||
|
driver.parameter.vendorid: 0764
|
||||||
|
driver.state: quiet
|
||||||
|
driver.version: 2.8.1
|
||||||
|
driver.version.data: CyberPower HID 0.8
|
||||||
|
driver.version.internal: 0.52
|
||||||
|
driver.version.usb: libusb-1.0.26 (API: 0x1000109)
|
||||||
|
input.transfer.high: 260
|
||||||
|
input.transfer.low: 170
|
||||||
|
input.voltage: 228.0
|
||||||
|
input.voltage.nominal: 230
|
||||||
|
output.voltage: 228.0
|
||||||
|
ups.beeper.status: enabled
|
||||||
|
ups.delay.shutdown: 20
|
||||||
|
ups.delay.start: 30
|
||||||
|
ups.load: 13
|
||||||
|
ups.mfr: CPS
|
||||||
|
ups.model: CP900EPFCLCD
|
||||||
|
ups.productid: 0501
|
||||||
|
ups.realpower.nominal: 540
|
||||||
|
ups.serial: 000000000000
|
||||||
|
ups.status: OL
|
||||||
|
ups.test.result: No test initiated
|
||||||
|
ups.timer.shutdown: -60
|
||||||
|
ups.timer.start: -60
|
||||||
|
ups.vendorid: 0764
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
upsd,model=Model\ 12345,serial=ABC123,status_OL=true,ups_name=fake battery_charge_percent=100,battery_mfr_date="2016-07-26",battery_voltage=13.4,firmware="CUSTOM_FIRMWARE",input_voltage=242,load_percent=23,nominal_battery_voltage=24,nominal_input_voltage=230,nominal_power=700i,output_voltage=230,real_power=41,status_flags=8u,time_left_ns=600000000000i,ups_status="OL"
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
[[inputs.upsd]]
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
device.serial: STRING:64
|
||||||
|
device.model: STRING:64
|
||||||
|
input.voltage: NUMBER
|
||||||
|
ups.load: NUMBER
|
||||||
|
battery.charge: NUMBER
|
||||||
|
battery.runtime: NUMBER
|
||||||
|
output.voltage: NUMBER
|
||||||
|
battery.voltage: NUMBER
|
||||||
|
input.voltage.nominal: NUMBER
|
||||||
|
battery.voltage.nominal: NUMBER
|
||||||
|
ups.realpower: NUMBER
|
||||||
|
ups.realpower.nominal: NUMBER
|
||||||
|
ups.firmware: STRING:64
|
||||||
|
battery.mfr.date: STRING:64
|
||||||
|
ups.status: STRING:64
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
device.serial: ABC123
|
||||||
|
device.model: Model 12345
|
||||||
|
input.voltage: 242.0
|
||||||
|
ups.load: 23.0
|
||||||
|
battery.charge: 100.0
|
||||||
|
battery.runtime: 600.00
|
||||||
|
output.voltage: 230.0
|
||||||
|
battery.voltage: 13.4
|
||||||
|
input.voltage.nominal: 230.0
|
||||||
|
battery.voltage.nominal: 24.0
|
||||||
|
ups.realpower: 41.0
|
||||||
|
ups.realpower.nominal: 700
|
||||||
|
ups.firmware: CUSTOM_FIRMWARE
|
||||||
|
battery.mfr.date: 2016-07-26
|
||||||
|
ups.status: OL
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
upsd,model=Model\ 12345,serial=ABC123,status_OL=true,ups_name=fake battery_charge_percent=100,battery_mfr_date="2016-07-26",battery_voltage=13.4,firmware="CUSTOM_FIRMWARE",input_voltage=242,load_percent=23,nominal_battery_voltage=24,nominal_input_voltage=230,nominal_power=700,output_voltage=230,real_power=41,status_flags=8u,time_left_ns=600000000000i,ups_status="OL"
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
[[inputs.upsd]]
|
||||||
|
force_float = true
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
device.serial: STRING:64
|
||||||
|
device.model: STRING:64
|
||||||
|
input.voltage: NUMBER
|
||||||
|
ups.load: NUMBER
|
||||||
|
battery.charge: NUMBER
|
||||||
|
battery.runtime: NUMBER
|
||||||
|
output.voltage: NUMBER
|
||||||
|
battery.voltage: NUMBER
|
||||||
|
input.voltage.nominal: NUMBER
|
||||||
|
battery.voltage.nominal: NUMBER
|
||||||
|
ups.realpower: NUMBER
|
||||||
|
ups.realpower.nominal: NUMBER
|
||||||
|
ups.firmware: STRING:64
|
||||||
|
battery.mfr.date: STRING:64
|
||||||
|
ups.status: STRING:64
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
device.serial: ABC123
|
||||||
|
device.model: Model 12345
|
||||||
|
input.voltage: 242.0
|
||||||
|
ups.load: 23.0
|
||||||
|
battery.charge: 100.0
|
||||||
|
battery.runtime: 600.00
|
||||||
|
output.voltage: 230.0
|
||||||
|
battery.voltage: 13.4
|
||||||
|
input.voltage.nominal: 230.0
|
||||||
|
battery.voltage.nominal: 24.0
|
||||||
|
ups.realpower: 41.0
|
||||||
|
ups.realpower.nominal: 700
|
||||||
|
ups.firmware: CUSTOM_FIRMWARE
|
||||||
|
battery.mfr.date: 2016-07-26
|
||||||
|
ups.status: OL
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
upsd,model=Model\ 12345,serial=ABC123,status_OL=true,ups_name=fake battery_charge_percent=100,battery_mfr_date="2016-07-26",battery_voltage=13.4,firmware="CUSTOM_FIRMWARE",input_voltage=242,load_percent=23,nominal_battery_voltage=24,nominal_input_voltage=230,nominal_power=700,output_voltage=230,real_power=41,status_flags=8u,time_left_ns=600000000000i,ups_status="OL",device_location="Upper floor"
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
[[inputs.upsd]]
|
||||||
|
force_float = true
|
||||||
|
additional_fields = ["*"]
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
device.serial: STRING:64
|
||||||
|
device.model: STRING:64
|
||||||
|
device.location: STRING:64
|
||||||
|
input.voltage: NUMBER
|
||||||
|
ups.load: NUMBER
|
||||||
|
battery.charge: NUMBER
|
||||||
|
battery.runtime: NUMBER
|
||||||
|
output.voltage: NUMBER
|
||||||
|
battery.voltage: NUMBER
|
||||||
|
input.voltage.nominal: NUMBER
|
||||||
|
battery.voltage.nominal: NUMBER
|
||||||
|
ups.realpower: NUMBER
|
||||||
|
ups.realpower.nominal: NUMBER
|
||||||
|
ups.firmware: STRING:64
|
||||||
|
battery.mfr.date: STRING:64
|
||||||
|
ups.status: STRING:64
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
device.serial: ABC123
|
||||||
|
device.model: Model 12345
|
||||||
|
device.location: Upper floor
|
||||||
|
input.voltage: 242.0
|
||||||
|
ups.load: 23.0
|
||||||
|
battery.charge: 100.0
|
||||||
|
battery.runtime: 600.00
|
||||||
|
output.voltage: 230.0
|
||||||
|
battery.voltage: 13.4
|
||||||
|
input.voltage.nominal: 230.0
|
||||||
|
battery.voltage.nominal: 24.0
|
||||||
|
ups.realpower: 41.0
|
||||||
|
ups.realpower.nominal: 700
|
||||||
|
ups.firmware: CUSTOM_FIRMWARE
|
||||||
|
battery.mfr.date: 2016-07-26
|
||||||
|
ups.status: OL
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
nut "github.com/robbiet480/go.nut"
|
nut "github.com/robbiet480/go.nut"
|
||||||
|
|
||||||
"github.com/influxdata/telegraf"
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/filter"
|
||||||
"github.com/influxdata/telegraf/internal"
|
"github.com/influxdata/telegraf/internal"
|
||||||
"github.com/influxdata/telegraf/internal/choice"
|
"github.com/influxdata/telegraf/internal/choice"
|
||||||
"github.com/influxdata/telegraf/plugins/inputs"
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
|
@ -17,37 +18,99 @@ import (
|
||||||
//go:embed sample.conf
|
//go:embed sample.conf
|
||||||
var sampleConfig string
|
var sampleConfig string
|
||||||
|
|
||||||
//See: https://networkupstools.org/docs/developer-guide.chunked/index.html
|
// see: https://networkupstools.org/docs/developer-guide.chunked/index.html
|
||||||
|
|
||||||
const defaultAddress = "127.0.0.1"
|
const defaultAddress = "127.0.0.1"
|
||||||
const defaultPort = 3493
|
const defaultPort = 3493
|
||||||
|
|
||||||
type Upsd struct {
|
// Define the set of variables _always_ included in a metric
|
||||||
Server string `toml:"server"`
|
var mandatoryVariableSet = map[string]bool{
|
||||||
Port int `toml:"port"`
|
"battery.date": true,
|
||||||
Username string `toml:"username"`
|
"battery.mfr.date": true,
|
||||||
Password string `toml:"password"`
|
"battery.runtime": true,
|
||||||
ForceFloat bool `toml:"force_float"`
|
"device.model": true,
|
||||||
|
"device.serial": true,
|
||||||
|
"ups.firmware": true,
|
||||||
|
"ups.status": true,
|
||||||
|
}
|
||||||
|
|
||||||
Log telegraf.Logger `toml:"-"`
|
// Define the default field set to add if existing
|
||||||
|
var defaultFieldSet = map[string]string{
|
||||||
|
"battery.charge": "battery_charge_percent",
|
||||||
|
"battery.runtime.low": "battery_runtime_low",
|
||||||
|
"battery.voltage": "battery_voltage",
|
||||||
|
"input.frequency": "input_frequency",
|
||||||
|
"input.transfer.high": "input_transfer_high",
|
||||||
|
"input.transfer.low": "input_transfer_low",
|
||||||
|
"input.voltage": "input_voltage",
|
||||||
|
"ups.temperature": "internal_temp",
|
||||||
|
"ups.load": "load_percent",
|
||||||
|
"battery.voltage.nominal": "nominal_battery_voltage",
|
||||||
|
"input.voltage.nominal": "nominal_input_voltage",
|
||||||
|
"ups.realpower.nominal": "nominal_power",
|
||||||
|
"output.voltage": "output_voltage",
|
||||||
|
"ups.realpower": "real_power",
|
||||||
|
"ups.delay.shutdown": "ups_delay_shutdown",
|
||||||
|
"ups.delay.start": "ups_delay_start",
|
||||||
|
}
|
||||||
|
|
||||||
|
type Upsd struct {
|
||||||
|
Server string `toml:"server"`
|
||||||
|
Port int `toml:"port"`
|
||||||
|
Username string `toml:"username"`
|
||||||
|
Password string `toml:"password"`
|
||||||
|
ForceFloat bool `toml:"force_float"`
|
||||||
|
Additional []string `toml:"additional_fields"`
|
||||||
|
DumpRaw bool `toml:"dump_raw_variables"`
|
||||||
|
Log telegraf.Logger `toml:"-"`
|
||||||
|
|
||||||
|
filter filter.Filter
|
||||||
|
dumped map[string]bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Upsd) SampleConfig() string {
|
func (*Upsd) SampleConfig() string {
|
||||||
return sampleConfig
|
return sampleConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *Upsd) Init() error {
|
||||||
|
// Compile the additional fields filter
|
||||||
|
f, err := filter.Compile(u.Additional)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("compiling additional_fields filter failed: %w", err)
|
||||||
|
}
|
||||||
|
u.filter = f
|
||||||
|
|
||||||
|
u.dumped = make(map[string]bool)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (u *Upsd) Gather(acc telegraf.Accumulator) error {
|
func (u *Upsd) Gather(acc telegraf.Accumulator) error {
|
||||||
upsList, err := u.fetchVariables(u.Server, u.Port)
|
upsList, err := u.fetchVariables(u.Server, u.Port)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if u.DumpRaw {
|
||||||
|
for name, variables := range upsList {
|
||||||
|
// Only dump the information once per UPS
|
||||||
|
if u.dumped[name] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
values := make([]string, 0, len(variables))
|
||||||
|
types := make([]string, 0, len(variables))
|
||||||
|
for _, v := range variables {
|
||||||
|
values = append(values, fmt.Sprintf("%s: %v", v.Name, v.Value))
|
||||||
|
types = append(types, fmt.Sprintf("%s: %v", v.Name, v.OriginalType))
|
||||||
|
}
|
||||||
|
u.Log.Debugf("Variables dump for UPS %q:\n%s\n-----\n%s", name, strings.Join(values, "\n"), strings.Join(types, "\n"))
|
||||||
|
}
|
||||||
|
}
|
||||||
for name, variables := range upsList {
|
for name, variables := range upsList {
|
||||||
u.gatherUps(acc, name, variables)
|
u.gatherUps(acc, name, variables)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Upsd) gatherUps(acc telegraf.Accumulator, name string, variables []nut.Variable) {
|
func (u *Upsd) gatherUps(acc telegraf.Accumulator, upsname string, variables []nut.Variable) {
|
||||||
metrics := make(map[string]interface{})
|
metrics := make(map[string]interface{})
|
||||||
for _, variable := range variables {
|
for _, variable := range variables {
|
||||||
name := variable.Name
|
name := variable.Name
|
||||||
|
|
@ -57,7 +120,7 @@ func (u *Upsd) gatherUps(acc telegraf.Accumulator, name string, variables []nut.
|
||||||
|
|
||||||
tags := map[string]string{
|
tags := map[string]string{
|
||||||
"serial": fmt.Sprintf("%v", metrics["device.serial"]),
|
"serial": fmt.Sprintf("%v", metrics["device.serial"]),
|
||||||
"ups_name": name,
|
"ups_name": upsname,
|
||||||
//"variables": variables.Status not sure if it's a good idea to provide this
|
//"variables": variables.Status not sure if it's a good idea to provide this
|
||||||
"model": fmt.Sprintf("%v", metrics["device.model"]),
|
"model": fmt.Sprintf("%v", metrics["device.model"]),
|
||||||
}
|
}
|
||||||
|
|
@ -75,54 +138,18 @@ func (u *Upsd) gatherUps(acc telegraf.Accumulator, name string, variables []nut.
|
||||||
u.Log.Warnf("Converting 'battery.runtime' to 'time_left_ns' failed: %v", err)
|
u.Log.Warnf("Converting 'battery.runtime' to 'time_left_ns' failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add the mandatory information
|
||||||
fields := map[string]interface{}{
|
fields := map[string]interface{}{
|
||||||
"battery_date": metrics["battery.date"],
|
"battery_date": metrics["battery.date"],
|
||||||
"battery_mfr_date": metrics["battery.mfr.date"],
|
"battery_mfr_date": metrics["battery.mfr.date"],
|
||||||
"status_flags": status,
|
"status_flags": status,
|
||||||
"ups_status": metrics["ups.status"],
|
"ups_status": metrics["ups.status"],
|
||||||
|
|
||||||
//Compatibility with apcupsd metrics format
|
// for compatibility with apcupsd metrics format
|
||||||
"time_left_ns": timeLeftNS,
|
"time_left_ns": timeLeftNS,
|
||||||
}
|
}
|
||||||
|
|
||||||
floatValues := map[string]string{
|
// Define the set of mandatory string fields
|
||||||
"battery_charge_percent": "battery.charge",
|
|
||||||
"battery_runtime_low": "battery.runtime.low",
|
|
||||||
"battery_voltage": "battery.voltage",
|
|
||||||
"input_frequency": "input.frequency",
|
|
||||||
"input_transfer_high": "input.transfer.high",
|
|
||||||
"input_transfer_low": "input.transfer.low",
|
|
||||||
"input_voltage": "input.voltage",
|
|
||||||
"internal_temp": "ups.temperature",
|
|
||||||
"load_percent": "ups.load",
|
|
||||||
"nominal_battery_voltage": "battery.voltage.nominal",
|
|
||||||
"nominal_input_voltage": "input.voltage.nominal",
|
|
||||||
"nominal_power": "ups.realpower.nominal",
|
|
||||||
"output_voltage": "output.voltage",
|
|
||||||
"real_power": "ups.realpower",
|
|
||||||
"ups_delay_shutdown": "ups.delay.shutdown",
|
|
||||||
"ups_delay_start": "ups.delay.start",
|
|
||||||
}
|
|
||||||
|
|
||||||
for key, rawValue := range floatValues {
|
|
||||||
if metrics[rawValue] == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if !u.ForceFloat {
|
|
||||||
fields[key] = metrics[rawValue]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Force expected float values to actually being float (e.g. if delivered as int)
|
|
||||||
float, err := internal.ToFloat64(metrics[rawValue])
|
|
||||||
if err != nil {
|
|
||||||
acc.AddError(fmt.Errorf("converting %s=%v failed: %w", rawValue, metrics[rawValue], err))
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fields[key] = float
|
|
||||||
}
|
|
||||||
|
|
||||||
val, err := internal.ToString(metrics["ups.firmware"])
|
val, err := internal.ToString(metrics["ups.firmware"])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
acc.AddError(fmt.Errorf("converting ups.firmware=%q failed: %w", metrics["ups.firmware"], err))
|
acc.AddError(fmt.Errorf("converting ups.firmware=%q failed: %w", metrics["ups.firmware"], err))
|
||||||
|
|
@ -130,6 +157,36 @@ func (u *Upsd) gatherUps(acc telegraf.Accumulator, name string, variables []nut.
|
||||||
fields["firmware"] = val
|
fields["firmware"] = val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to gather all default fields and optional field
|
||||||
|
for varname, v := range metrics {
|
||||||
|
// Skip all empty fields and all fields contained in the mandatory set
|
||||||
|
// of fields added above.
|
||||||
|
if v == nil || mandatoryVariableSet[varname] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the name of the default field-set if present and otherwise check
|
||||||
|
// the additional field-set. If none of them contains the variable, we
|
||||||
|
// skip over it
|
||||||
|
var key string
|
||||||
|
if k, found := defaultFieldSet[varname]; found {
|
||||||
|
key = k
|
||||||
|
} else if u.filter != nil && u.filter.Match(varname) {
|
||||||
|
key = strings.ReplaceAll(varname, ".", "_")
|
||||||
|
} else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Force expected float values to actually being float (e.g. if delivered as int)
|
||||||
|
if u.ForceFloat {
|
||||||
|
float, err := internal.ToFloat64(v)
|
||||||
|
if err == nil {
|
||||||
|
v = float
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fields[key] = v
|
||||||
|
}
|
||||||
|
|
||||||
acc.AddFields("upsd", fields, tags)
|
acc.AddFields("upsd", fields, tags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,159 +1,192 @@
|
||||||
package upsd
|
package upsd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/influxdata/telegraf"
|
||||||
|
"github.com/influxdata/telegraf/config"
|
||||||
|
"github.com/influxdata/telegraf/plugins/inputs"
|
||||||
|
"github.com/influxdata/telegraf/plugins/parsers/influx"
|
||||||
"github.com/influxdata/telegraf/testutil"
|
"github.com/influxdata/telegraf/testutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUpsdGather(t *testing.T) {
|
func TestBadServer(t *testing.T) {
|
||||||
nut := &Upsd{}
|
// Create and start a server without interactions
|
||||||
|
server := &mockServer{}
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
addr, err := server.listen(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
var (
|
// Setup the plugin
|
||||||
tests = []struct {
|
plugin := &Upsd{
|
||||||
name string
|
Server: addr.IP.String(),
|
||||||
forceFloat bool
|
Port: addr.Port,
|
||||||
err bool
|
}
|
||||||
tags map[string]string
|
require.NoError(t, plugin.Init())
|
||||||
fields map[string]interface{}
|
|
||||||
out func() []interaction
|
// Do the query
|
||||||
}{
|
var acc testutil.Accumulator
|
||||||
{
|
require.Error(t, plugin.Gather(&acc))
|
||||||
name: "test listening server with output",
|
}
|
||||||
forceFloat: false,
|
|
||||||
err: false,
|
func TestCases(t *testing.T) {
|
||||||
tags: map[string]string{
|
// Get all directories in testdata
|
||||||
"serial": "ABC123",
|
folders, err := os.ReadDir("testcases")
|
||||||
"ups_name": "fake",
|
require.NoError(t, err)
|
||||||
"model": "Model 12345",
|
|
||||||
"status_OL": "true",
|
// Register the plugin
|
||||||
},
|
inputs.Add("upsd", func() telegraf.Input {
|
||||||
fields: map[string]interface{}{
|
return &Upsd{}
|
||||||
"battery_charge_percent": float64(100),
|
})
|
||||||
"battery_date": nil,
|
|
||||||
"battery_mfr_date": "2016-07-26",
|
for _, f := range folders {
|
||||||
"battery_voltage": float64(13.4),
|
// Only handle folders
|
||||||
"firmware": "CUSTOM_FIRMWARE",
|
if !f.IsDir() {
|
||||||
"input_voltage": float64(242),
|
continue
|
||||||
"load_percent": float64(23),
|
|
||||||
"nominal_battery_voltage": float64(24),
|
|
||||||
"nominal_input_voltage": float64(230),
|
|
||||||
"nominal_power": int64(700),
|
|
||||||
"output_voltage": float64(230),
|
|
||||||
"real_power": float64(41),
|
|
||||||
"status_flags": uint64(8),
|
|
||||||
"time_left_ns": int64(600000000000),
|
|
||||||
"ups_status": "OL",
|
|
||||||
},
|
|
||||||
out: genOutput,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "test listening server with output & force floats",
|
|
||||||
forceFloat: true,
|
|
||||||
err: false,
|
|
||||||
tags: map[string]string{
|
|
||||||
"serial": "ABC123",
|
|
||||||
"ups_name": "fake",
|
|
||||||
"model": "Model 12345",
|
|
||||||
"status_OL": "true",
|
|
||||||
},
|
|
||||||
fields: map[string]interface{}{
|
|
||||||
"battery_charge_percent": float64(100),
|
|
||||||
"battery_date": nil,
|
|
||||||
"battery_mfr_date": "2016-07-26",
|
|
||||||
"battery_voltage": float64(13.4),
|
|
||||||
"firmware": "CUSTOM_FIRMWARE",
|
|
||||||
"input_voltage": float64(242),
|
|
||||||
"load_percent": float64(23),
|
|
||||||
"nominal_battery_voltage": float64(24),
|
|
||||||
"nominal_input_voltage": float64(230),
|
|
||||||
"nominal_power": int64(700),
|
|
||||||
"output_voltage": float64(230),
|
|
||||||
"real_power": float64(41),
|
|
||||||
"status_flags": uint64(8),
|
|
||||||
"time_left_ns": int64(600000000000),
|
|
||||||
"ups_status": "OL",
|
|
||||||
},
|
|
||||||
out: genOutput,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
testcasePath := filepath.Join("testcases", f.Name())
|
||||||
|
configFilename := filepath.Join(testcasePath, "telegraf.conf")
|
||||||
|
expectedFilename := filepath.Join(testcasePath, "expected.out")
|
||||||
|
|
||||||
acc testutil.Accumulator
|
t.Run(f.Name(), func(t *testing.T) {
|
||||||
)
|
// Prepare the influx parser for expectations
|
||||||
|
parser := &influx.Parser{}
|
||||||
|
require.NoError(t, parser.Init())
|
||||||
|
|
||||||
for _, tt := range tests {
|
// Read the expected output if any
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
var expected []telegraf.Metric
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
if _, err := os.Stat(expectedFilename); err == nil {
|
||||||
|
var err error
|
||||||
|
expected, err = testutil.ParseMetricsFromFile(expectedFilename, parser)
|
||||||
|
require.NoError(t, err)
|
||||||
|
}
|
||||||
|
|
||||||
lAddr, err := listen(ctx, t, tt.out())
|
// Setup a server from the input data
|
||||||
|
server, err := setupServer(testcasePath)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
nut.Server = (lAddr.(*net.TCPAddr)).IP.String()
|
// Start the server
|
||||||
nut.Port = (lAddr.(*net.TCPAddr)).Port
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
nut.ForceFloat = tt.forceFloat
|
addr, err := server.listen(ctx)
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
err = nut.Gather(&acc)
|
// Configure the plugin
|
||||||
if tt.err {
|
cfg := config.NewConfig()
|
||||||
require.Error(t, err)
|
require.NoError(t, cfg.LoadConfig(configFilename))
|
||||||
} else {
|
require.Len(t, cfg.Inputs, 1)
|
||||||
require.NoError(t, err)
|
plugin := cfg.Inputs[0].Input.(*Upsd)
|
||||||
acc.AssertContainsFields(t, "upsd", tt.fields)
|
plugin.Server = addr.IP.String()
|
||||||
acc.AssertContainsTaggedFields(t, "upsd", tt.fields, tt.tags)
|
plugin.Port = addr.Port
|
||||||
}
|
require.NoError(t, plugin.Init())
|
||||||
cancel()
|
|
||||||
|
var acc testutil.Accumulator
|
||||||
|
require.NoError(t, plugin.Gather(&acc))
|
||||||
|
|
||||||
|
// Check the metric nevertheless as we might get some metrics despite errors.
|
||||||
|
actual := acc.GetTelegrafMetrics()
|
||||||
|
testutil.RequireMetricsEqual(t, expected, actual, testutil.IgnoreTime())
|
||||||
|
acc.Lock()
|
||||||
|
defer acc.Unlock()
|
||||||
|
require.Empty(t, acc.Errors)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUpsdGatherFail(t *testing.T) {
|
type interaction struct {
|
||||||
nut := &Upsd{}
|
Expected string
|
||||||
|
Response string
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
type variable struct {
|
||||||
tests = []struct {
|
Name string
|
||||||
name string
|
Value string
|
||||||
err bool
|
}
|
||||||
tags map[string]string
|
|
||||||
fields map[string]interface{}
|
|
||||||
out func() []interaction
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "test with bad output",
|
|
||||||
err: true,
|
|
||||||
out: genBadOutput,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
acc testutil.Accumulator
|
type mockServer struct {
|
||||||
)
|
protocol []interaction
|
||||||
|
}
|
||||||
|
|
||||||
for _, tt := range tests {
|
func (s *mockServer) init() {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
s.protocol = []interaction{
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
{
|
||||||
|
Expected: "VER\n",
|
||||||
lAddr, err := listen(ctx, t, tt.out())
|
Response: "1\n",
|
||||||
require.NoError(t, err)
|
},
|
||||||
|
{
|
||||||
nut.Server = (lAddr.(*net.TCPAddr)).IP.String()
|
Expected: "NETVER\n",
|
||||||
nut.Port = (lAddr.(*net.TCPAddr)).Port
|
Response: "1\n",
|
||||||
|
},
|
||||||
err = nut.Gather(&acc)
|
{
|
||||||
if tt.err {
|
Expected: "LIST UPS\n",
|
||||||
require.Error(t, err)
|
Response: "BEGIN LIST UPS\nUPS fake \"fake UPS\"\nEND LIST UPS\n",
|
||||||
} else {
|
},
|
||||||
require.NoError(t, err)
|
{
|
||||||
acc.AssertContainsTaggedFields(t, "upsd", tt.fields, tt.tags)
|
Expected: "LIST CLIENT fake\n",
|
||||||
}
|
Response: "BEGIN LIST CLIENT fake\nCLIENT fake 127.0.0.1\nEND LIST CLIENT fake\n",
|
||||||
cancel()
|
},
|
||||||
})
|
{
|
||||||
|
Expected: "LIST CMD fake\n",
|
||||||
|
Response: "BEGIN LIST CMD fake\nEND LIST CMD fake\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Expected: "GET UPSDESC fake\n",
|
||||||
|
Response: "UPSDESC fake \"stub-ups-description\"\n",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Expected: "GET NUMLOGINS fake\n",
|
||||||
|
Response: "NUMLOGINS fake 1\n",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func listen(ctx context.Context, t *testing.T, out []interaction) (net.Addr, error) {
|
func (s *mockServer) addVariables(variables []variable, types map[string]string) error {
|
||||||
|
// Add a VAR entries for the variables
|
||||||
|
values := make([]string, 0, len(variables))
|
||||||
|
for _, v := range variables {
|
||||||
|
values = append(values, fmt.Sprintf("VAR fake %s %q", v.Name, v.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
s.protocol = append(s.protocol, interaction{
|
||||||
|
Expected: "LIST VAR fake\n",
|
||||||
|
Response: "BEGIN LIST VAR fake\n" + strings.Join(values, "\n") + "\nEND LIST VAR fake\n",
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add a description and type interaction for the variable
|
||||||
|
for _, v := range variables {
|
||||||
|
variableType, found := types[v.Name]
|
||||||
|
if !found {
|
||||||
|
return fmt.Errorf("type for variable %q not found", v.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.protocol = append(s.protocol,
|
||||||
|
interaction{
|
||||||
|
Expected: "GET DESC fake " + v.Name + "\n",
|
||||||
|
Response: "DESC fake" + v.Name + " \"No description here\"\n",
|
||||||
|
},
|
||||||
|
interaction{
|
||||||
|
Expected: "GET TYPE fake " + v.Name + "\n",
|
||||||
|
Response: "TYPE fake " + v.Name + " " + variableType + "\n",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *mockServer) listen(ctx context.Context) (*net.TCPAddr, error) {
|
||||||
lc := net.ListenConfig{}
|
lc := net.ListenConfig{}
|
||||||
ln, err := lc.Listen(ctx, "tcp4", "127.0.0.1:0")
|
ln, err := lc.Listen(ctx, "tcp4", "127.0.0.1:0")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -170,131 +203,90 @@ func listen(ctx context.Context, t *testing.T, out []interaction) (net.Addr, err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
require.NoError(t, conn.SetReadDeadline(time.Now().Add(time.Minute)))
|
_ = conn.SetReadDeadline(time.Now().Add(time.Minute))
|
||||||
|
|
||||||
in := make([]byte, 128)
|
in := make([]byte, 128)
|
||||||
for _, interaction := range out {
|
for _, interaction := range s.protocol {
|
||||||
n, err := conn.Read(in)
|
n, err := conn.Read(in)
|
||||||
require.NoError(t, err, "failed to read from connection")
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to read from connection: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
expectedBytes := []byte(interaction.Expected)
|
request := in[:n]
|
||||||
want, got := expectedBytes, in[:n]
|
if !bytes.Equal([]byte(interaction.Expected), request) {
|
||||||
require.Equal(t, want, got)
|
fmt.Printf("Unexpected request %q, expected %q\n", string(request), interaction.Expected)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
_, err = conn.Write([]byte(interaction.Response))
|
if _, err := conn.Write([]byte(interaction.Response)); err != nil {
|
||||||
require.NoError(t, err, "failed to respond to LIST UPS")
|
fmt.Printf("Cannot write answer for request %q: %v\n", string(request), err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Append EOF to end of output bytes
|
// Append EOF to end of output bytes
|
||||||
_, err = conn.Write([]byte{0, 0})
|
if _, err := conn.Write([]byte{0, 0}); err != nil {
|
||||||
require.NoError(t, err, "failed to write EOF")
|
fmt.Printf("Cannot write EOF: %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return ln.Addr(), nil
|
return ln.Addr().(*net.TCPAddr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type interaction struct {
|
func setupServer(path string) (*mockServer, error) {
|
||||||
Expected string
|
// Read the variables
|
||||||
Response string
|
varbuf, err := os.ReadFile(filepath.Join(path, "variables.dev"))
|
||||||
}
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("reading variables failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
func genOutput() []interaction {
|
// Parse the information into variable names and values (upsc format)
|
||||||
m := make([]interaction, 0)
|
variables := make([]variable, 0)
|
||||||
m = append(m,
|
scanner := bufio.NewScanner(bytes.NewBuffer(varbuf))
|
||||||
interaction{
|
for scanner.Scan() {
|
||||||
Expected: "VER\n",
|
line := scanner.Text()
|
||||||
Response: "1\n",
|
parts := strings.SplitN(line, ":", 2)
|
||||||
},
|
if len(parts) != 2 {
|
||||||
interaction{
|
return nil, fmt.Errorf("cannot parse line %s", line)
|
||||||
Expected: "NETVER\n",
|
}
|
||||||
Response: "1\n",
|
name := strings.TrimSpace(parts[0])
|
||||||
},
|
value := strings.TrimSpace(parts[1])
|
||||||
interaction{
|
variables = append(variables, variable{name, value})
|
||||||
Expected: "LIST UPS\n",
|
}
|
||||||
Response: `BEGIN LIST UPS
|
if err := scanner.Err(); err != nil {
|
||||||
UPS fake "fakescription"
|
return nil, fmt.Errorf("processing variables failed: %w", err)
|
||||||
END LIST UPS
|
}
|
||||||
`,
|
|
||||||
},
|
|
||||||
interaction{
|
|
||||||
Expected: "LIST CLIENT fake\n",
|
|
||||||
Response: `BEGIN LIST CLIENT fake
|
|
||||||
CLIENT fake 192.168.1.1
|
|
||||||
END LIST CLIENT fake
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
interaction{
|
|
||||||
Expected: "LIST CMD fake\n",
|
|
||||||
Response: `BEGIN LIST CMD fake
|
|
||||||
END LIST CMD fake
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
interaction{
|
|
||||||
Expected: "GET UPSDESC fake\n",
|
|
||||||
Response: "UPSDESC fake \"stub-ups-description\"\n",
|
|
||||||
},
|
|
||||||
interaction{
|
|
||||||
Expected: "GET NUMLOGINS fake\n",
|
|
||||||
Response: "NUMLOGINS fake 1\n",
|
|
||||||
},
|
|
||||||
interaction{
|
|
||||||
Expected: "LIST VAR fake\n",
|
|
||||||
Response: `BEGIN LIST VAR fake
|
|
||||||
VAR fake device.serial "ABC123"
|
|
||||||
VAR fake device.model "Model 12345"
|
|
||||||
VAR fake input.voltage "242.0"
|
|
||||||
VAR fake ups.load "23.0"
|
|
||||||
VAR fake battery.charge "100.0"
|
|
||||||
VAR fake battery.runtime "600.00"
|
|
||||||
VAR fake output.voltage "230.0"
|
|
||||||
VAR fake battery.voltage "13.4"
|
|
||||||
VAR fake input.voltage.nominal "230.0"
|
|
||||||
VAR fake battery.voltage.nominal "24.0"
|
|
||||||
VAR fake ups.realpower "41.0"
|
|
||||||
VAR fake ups.realpower.nominal "700"
|
|
||||||
VAR fake ups.firmware "CUSTOM_FIRMWARE"
|
|
||||||
VAR fake battery.mfr.date "2016-07-26"
|
|
||||||
VAR fake ups.status "OL"
|
|
||||||
END LIST VAR fake
|
|
||||||
`,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
m = appendVariable(m, "device.serial", "STRING:64")
|
|
||||||
m = appendVariable(m, "device.model", "STRING:64")
|
|
||||||
m = appendVariable(m, "input.voltage", "NUMBER")
|
|
||||||
m = appendVariable(m, "ups.load", "NUMBER")
|
|
||||||
m = appendVariable(m, "battery.charge", "NUMBER")
|
|
||||||
m = appendVariable(m, "battery.runtime", "NUMBER")
|
|
||||||
m = appendVariable(m, "output.voltage", "NUMBER")
|
|
||||||
m = appendVariable(m, "battery.voltage", "NUMBER")
|
|
||||||
m = appendVariable(m, "input.voltage.nominal", "NUMBER")
|
|
||||||
m = appendVariable(m, "battery.voltage.nominal", "NUMBER")
|
|
||||||
m = appendVariable(m, "ups.realpower", "NUMBER")
|
|
||||||
m = appendVariable(m, "ups.realpower.nominal", "NUMBER")
|
|
||||||
m = appendVariable(m, "ups.firmware", "STRING:64")
|
|
||||||
m = appendVariable(m, "battery.mfr.date", "STRING:64")
|
|
||||||
m = appendVariable(m, "ups.status", "STRING:64")
|
|
||||||
|
|
||||||
return m
|
// Read the variable-type mapping
|
||||||
}
|
typebuf, err := os.ReadFile(filepath.Join(path, "types.dev"))
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("reading variables failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
func appendVariable(m []interaction, name string, typ string) []interaction {
|
// Parse the information into variable names and values (upsc format)
|
||||||
m = append(m,
|
types := make(map[string]string, 0)
|
||||||
interaction{
|
scanner = bufio.NewScanner(bytes.NewBuffer(typebuf))
|
||||||
Expected: "GET DESC fake " + name + "\n",
|
for scanner.Scan() {
|
||||||
Response: "DESC fake" + name + " \"No description here\"\n",
|
line := scanner.Text()
|
||||||
},
|
parts := strings.SplitN(line, ":", 2)
|
||||||
interaction{
|
if len(parts) != 2 {
|
||||||
Expected: "GET TYPE fake " + name + "\n",
|
return nil, fmt.Errorf("cannot parse line %s", line)
|
||||||
Response: "TYPE fake " + name + " " + typ + "\n",
|
}
|
||||||
},
|
name := strings.TrimSpace(parts[0])
|
||||||
)
|
vartype := strings.TrimSpace(parts[1])
|
||||||
return m
|
types[name] = vartype
|
||||||
}
|
}
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("processing variables failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
func genBadOutput() []interaction {
|
// Setup the server and add the device information
|
||||||
m := make([]interaction, 0)
|
server := &mockServer{}
|
||||||
return m
|
server.init()
|
||||||
|
err = server.addVariables(variables, types)
|
||||||
|
return server, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue