Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
111
plugins/inputs/upsd/README.md
Normal file
111
plugins/inputs/upsd/README.md
Normal file
|
@ -0,0 +1,111 @@
|
|||
# UPSD Input Plugin
|
||||
|
||||
This plugin reads data of one or more Uninterruptible Power Supplies
|
||||
from an `upsd` daemon using its NUT network protocol.
|
||||
|
||||
## Requirements
|
||||
|
||||
`upsd` should be installed and it's daemon should be running.
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# Monitor UPSes connected via Network UPS Tools
|
||||
[[inputs.upsd]]
|
||||
## A running NUT server to connect to.
|
||||
## IPv6 addresses must be enclosed in brackets (e.g. "[::1]")
|
||||
# server = "127.0.0.1"
|
||||
# port = 3493
|
||||
# username = "user"
|
||||
# password = "password"
|
||||
|
||||
## Force parsing numbers as floats
|
||||
## It is highly recommended to enable this setting to parse numbers
|
||||
## consistently as floats to avoid database conflicts where some numbers are
|
||||
## parsed as integers and others as floats.
|
||||
# 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.
|
||||
## Please attach this information when reporting issues!
|
||||
# log_level = "trace"
|
||||
```
|
||||
|
||||
## Pitfalls
|
||||
|
||||
Please note that field types are automatically determined based on the values.
|
||||
Especially the strings `enabled` and `disabled` are automatically converted to
|
||||
`boolean` values. This might lead to trouble for fields that can contain
|
||||
non-binary values like `enabled`, `disabled` and `muted` as the output field
|
||||
will be `boolean` for the first two values but `string` for the latter. To
|
||||
convert `enabled` and `disabled` values back to `string` for those fields, use
|
||||
the [enum processor][enum_processor] with
|
||||
|
||||
```toml
|
||||
[[processors.enum]]
|
||||
[[processors.enum.mapping]]
|
||||
field = "ups_beeper_status"
|
||||
[processors.enum.mapping.value_mappings]
|
||||
true = "enabled"
|
||||
false = "disabled"
|
||||
```
|
||||
|
||||
Alternatively, you can also map the non-binary value to a `boolean`.
|
||||
|
||||
[enum_processor]: ../../processors/enum/README.md
|
||||
|
||||
## Metrics
|
||||
|
||||
This implementation tries to maintain compatibility with the apcupsd metrics:
|
||||
|
||||
- upsd
|
||||
- tags:
|
||||
- serial
|
||||
- ups_name
|
||||
- model
|
||||
- fields:
|
||||
- status_flags ([status-bits][rfc9271-sec5.1])
|
||||
- input_voltage
|
||||
- load_percent
|
||||
- battery_charge_percent
|
||||
- time_left_ns
|
||||
- output_voltage
|
||||
- internal_temp
|
||||
- battery_voltage
|
||||
- input_frequency
|
||||
- battery_date
|
||||
- nominal_input_voltage
|
||||
- nominal_battery_voltage
|
||||
- nominal_power
|
||||
- firmware
|
||||
|
||||
With the exception of:
|
||||
|
||||
- tags:
|
||||
- status (string representing the set status_flags)
|
||||
- fields:
|
||||
- time_on_battery_ns
|
||||
|
||||
[rfc9271-sec5.1]: https://www.rfc-editor.org/rfc/rfc9271.html#section-5.1
|
||||
|
||||
## Example Output
|
||||
|
||||
```text
|
||||
upsd,serial=AS1231515,ups_name=name1 load_percent=9.7,time_left_ns=9800000,output_voltage=230.4,internal_temp=32.4,battery_voltage=27.4,input_frequency=50.2,input_voltage=230.4,battery_charge_percent=100,status_flags=8i 1490035922000000000
|
||||
```
|
26
plugins/inputs/upsd/sample.conf
Normal file
26
plugins/inputs/upsd/sample.conf
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Monitor UPSes connected via Network UPS Tools
|
||||
[[inputs.upsd]]
|
||||
## A running NUT server to connect to.
|
||||
## IPv6 addresses must be enclosed in brackets (e.g. "[::1]")
|
||||
# server = "127.0.0.1"
|
||||
# port = 3493
|
||||
# username = "user"
|
||||
# password = "password"
|
||||
|
||||
## Force parsing numbers as floats
|
||||
## It is highly recommended to enable this setting to parse numbers
|
||||
## consistently as floats to avoid database conflicts where some numbers are
|
||||
## parsed as integers and others as floats.
|
||||
# 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.
|
||||
## Please attach this information when reporting issues!
|
||||
# log_level = "trace"
|
|
@ -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
|
1
plugins/inputs/upsd/testcases/fake/expected.out
Normal file
1
plugins/inputs/upsd/testcases/fake/expected.out
Normal file
|
@ -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"
|
1
plugins/inputs/upsd/testcases/fake/telegraf.conf
Normal file
1
plugins/inputs/upsd/testcases/fake/telegraf.conf
Normal file
|
@ -0,0 +1 @@
|
|||
[[inputs.upsd]]
|
15
plugins/inputs/upsd/testcases/fake/types.dev
Normal file
15
plugins/inputs/upsd/testcases/fake/types.dev
Normal file
|
@ -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
|
15
plugins/inputs/upsd/testcases/fake/variables.dev
Normal file
15
plugins/inputs/upsd/testcases/fake/variables.dev
Normal file
|
@ -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
|
15
plugins/inputs/upsd/testcases/fake_force_float/types.dev
Normal file
15
plugins/inputs/upsd/testcases/fake_force_float/types.dev
Normal file
|
@ -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
|
15
plugins/inputs/upsd/testcases/fake_force_float/variables.dev
Normal file
15
plugins/inputs/upsd/testcases/fake_force_float/variables.dev
Normal file
|
@ -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
|
284
plugins/inputs/upsd/upsd.go
Normal file
284
plugins/inputs/upsd/upsd.go
Normal file
|
@ -0,0 +1,284 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package upsd
|
||||
|
||||
import (
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
nut "github.com/robbiet480/go.nut"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/filter"
|
||||
"github.com/influxdata/telegraf/internal"
|
||||
"github.com/influxdata/telegraf/internal/choice"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
var (
|
||||
// Define the set of variables _always_ included in a metric
|
||||
mandatoryVariableSet = map[string]bool{
|
||||
"battery.date": true,
|
||||
"battery.mfr.date": true,
|
||||
"battery.runtime": true,
|
||||
"device.model": true,
|
||||
"device.serial": true,
|
||||
"ups.firmware": true,
|
||||
"ups.status": true,
|
||||
}
|
||||
// Define the default field set to add if existing
|
||||
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",
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
defaultAddress = "127.0.0.1"
|
||||
defaultPort = 3493
|
||||
)
|
||||
|
||||
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" deprecated:"1.35.0;use 'log_level' 'trace' instead"`
|
||||
Log telegraf.Logger `toml:"-"`
|
||||
|
||||
filter filter.Filter
|
||||
dumped map[string]bool
|
||||
}
|
||||
|
||||
func (*Upsd) SampleConfig() string {
|
||||
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 {
|
||||
upsList, err := u.fetchVariables(u.Server, u.Port)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if u.Log.Level().Includes(telegraf.Trace) || u.DumpRaw { // for backward compatibility
|
||||
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.Tracef("Variables dump for UPS %q:\n%s\n-----\n%s", name, strings.Join(values, "\n"), strings.Join(types, "\n"))
|
||||
}
|
||||
}
|
||||
for name, variables := range upsList {
|
||||
u.gatherUps(acc, name, variables)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *Upsd) gatherUps(acc telegraf.Accumulator, upsname string, variables []nut.Variable) {
|
||||
metrics := make(map[string]interface{})
|
||||
for _, variable := range variables {
|
||||
name := variable.Name
|
||||
value := variable.Value
|
||||
metrics[name] = value
|
||||
}
|
||||
|
||||
tags := map[string]string{
|
||||
"serial": fmt.Sprintf("%v", metrics["device.serial"]),
|
||||
"ups_name": upsname,
|
||||
// "variables": variables.Status not sure if it's a good idea to provide this
|
||||
"model": fmt.Sprintf("%v", metrics["device.model"]),
|
||||
}
|
||||
|
||||
// For compatibility with the apcupsd plugin's output we map the status string status into a bit-format
|
||||
status := mapStatus(metrics, tags)
|
||||
|
||||
timeLeftS, err := internal.ToFloat64(metrics["battery.runtime"])
|
||||
if err != nil {
|
||||
u.Log.Warnf("Type for 'battery.runtime' is not supported: %v", err)
|
||||
}
|
||||
|
||||
timeLeftNS, err := internal.ToInt64(timeLeftS * 1_000_000_000)
|
||||
if err != nil {
|
||||
u.Log.Warnf("Converting 'battery.runtime' to 'time_left_ns' failed: %v", err)
|
||||
}
|
||||
|
||||
// Add the mandatory information
|
||||
fields := map[string]interface{}{
|
||||
"battery_date": metrics["battery.date"],
|
||||
"battery_mfr_date": metrics["battery.mfr.date"],
|
||||
"status_flags": status,
|
||||
"ups_status": metrics["ups.status"],
|
||||
|
||||
// for compatibility with apcupsd metrics format
|
||||
"time_left_ns": timeLeftNS,
|
||||
}
|
||||
|
||||
// Define the set of mandatory string fields
|
||||
val, err := internal.ToString(metrics["ups.firmware"])
|
||||
if err != nil {
|
||||
acc.AddError(fmt.Errorf("converting ups.firmware=%q failed: %w", metrics["ups.firmware"], err))
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
|
||||
func mapStatus(metrics map[string]interface{}, tags map[string]string) uint64 {
|
||||
status := uint64(0)
|
||||
statusString := fmt.Sprintf("%v", metrics["ups.status"])
|
||||
statuses := strings.Fields(statusString)
|
||||
// Source: 1.3.2 at http://rogerprice.org/NUT/ConfigExamples.A5.pdf
|
||||
// apcupsd bits:
|
||||
// 0 Runtime calibration occurring (Not reported by Smart UPS v/s and BackUPS Pro)
|
||||
// 1 SmartTrim (Not reported by 1st and 2nd generation SmartUPS models)
|
||||
// 2 SmartBoost
|
||||
// 3 On line (this is the normal condition)
|
||||
// 4 On battery
|
||||
// 5 Overloaded output
|
||||
// 6 Battery low
|
||||
// 7 Replace battery
|
||||
if choice.Contains("CAL", statuses) {
|
||||
status |= 1 << 0
|
||||
tags["status_CAL"] = "true"
|
||||
}
|
||||
if choice.Contains("TRIM", statuses) {
|
||||
status |= 1 << 1
|
||||
tags["status_TRIM"] = "true"
|
||||
}
|
||||
if choice.Contains("BOOST", statuses) {
|
||||
status |= 1 << 2
|
||||
tags["status_BOOST"] = "true"
|
||||
}
|
||||
if choice.Contains("OL", statuses) {
|
||||
status |= 1 << 3
|
||||
tags["status_OL"] = "true"
|
||||
}
|
||||
if choice.Contains("OB", statuses) {
|
||||
status |= 1 << 4
|
||||
tags["status_OB"] = "true"
|
||||
}
|
||||
if choice.Contains("OVER", statuses) {
|
||||
status |= 1 << 5
|
||||
tags["status_OVER"] = "true"
|
||||
}
|
||||
if choice.Contains("LB", statuses) {
|
||||
status |= 1 << 6
|
||||
tags["status_LB"] = "true"
|
||||
}
|
||||
if choice.Contains("RB", statuses) {
|
||||
status |= 1 << 7
|
||||
tags["status_RB"] = "true"
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func (u *Upsd) fetchVariables(server string, port int) (map[string][]nut.Variable, error) {
|
||||
client, err := nut.Connect(server, port)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("connect: %w", err)
|
||||
}
|
||||
|
||||
if u.Username != "" && u.Password != "" {
|
||||
_, err = client.Authenticate(u.Username, u.Password)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("auth: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
upsList, err := client.GetUPSList()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getupslist: %w", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_, disconnectErr := client.Disconnect()
|
||||
if disconnectErr != nil {
|
||||
err = fmt.Errorf("disconnect: %w", disconnectErr)
|
||||
}
|
||||
}()
|
||||
|
||||
result := make(map[string][]nut.Variable)
|
||||
for _, ups := range upsList {
|
||||
result[ups.Name] = ups.Variables
|
||||
}
|
||||
|
||||
return result, err
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("upsd", func() telegraf.Input {
|
||||
return &Upsd{
|
||||
Server: defaultAddress,
|
||||
Port: defaultPort,
|
||||
}
|
||||
})
|
||||
}
|
295
plugins/inputs/upsd/upsd_test.go
Normal file
295
plugins/inputs/upsd/upsd_test.go
Normal file
|
@ -0,0 +1,295 @@
|
|||
package upsd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestBadServer(t *testing.T) {
|
||||
// Create and start a server without interactions
|
||||
server := &mockServer{}
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
addr, err := server.listen(ctx)
|
||||
require.NoError(t, err)
|
||||
defer cancel()
|
||||
|
||||
// Setup the plugin
|
||||
plugin := &Upsd{
|
||||
Server: addr.IP.String(),
|
||||
Port: addr.Port,
|
||||
}
|
||||
require.NoError(t, plugin.Init())
|
||||
|
||||
// Do the query
|
||||
var acc testutil.Accumulator
|
||||
require.Error(t, plugin.Gather(&acc))
|
||||
}
|
||||
|
||||
func TestCases(t *testing.T) {
|
||||
// Get all directories in testdata
|
||||
folders, err := os.ReadDir("testcases")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Register the plugin
|
||||
inputs.Add("upsd", func() telegraf.Input {
|
||||
return &Upsd{}
|
||||
})
|
||||
|
||||
for _, f := range folders {
|
||||
// Only handle folders
|
||||
if !f.IsDir() {
|
||||
continue
|
||||
}
|
||||
testcasePath := filepath.Join("testcases", f.Name())
|
||||
configFilename := filepath.Join(testcasePath, "telegraf.conf")
|
||||
expectedFilename := filepath.Join(testcasePath, "expected.out")
|
||||
|
||||
t.Run(f.Name(), func(t *testing.T) {
|
||||
// Prepare the influx parser for expectations
|
||||
parser := &influx.Parser{}
|
||||
require.NoError(t, parser.Init())
|
||||
|
||||
// Read the expected output if any
|
||||
var expected []telegraf.Metric
|
||||
if _, err := os.Stat(expectedFilename); err == nil {
|
||||
var err error
|
||||
expected, err = testutil.ParseMetricsFromFile(expectedFilename, parser)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Setup a server from the input data
|
||||
server, err := setupServer(testcasePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Start the server
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
addr, err := server.listen(ctx)
|
||||
require.NoError(t, err)
|
||||
defer cancel()
|
||||
|
||||
// Configure the plugin
|
||||
cfg := config.NewConfig()
|
||||
require.NoError(t, cfg.LoadConfig(configFilename))
|
||||
require.Len(t, cfg.Inputs, 1)
|
||||
plugin := cfg.Inputs[0].Input.(*Upsd)
|
||||
plugin.Server = addr.IP.String()
|
||||
plugin.Port = addr.Port
|
||||
require.NoError(t, plugin.Init())
|
||||
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type interaction struct {
|
||||
expected string
|
||||
response string
|
||||
}
|
||||
|
||||
type variable struct {
|
||||
name string
|
||||
value string
|
||||
}
|
||||
|
||||
type mockServer struct {
|
||||
protocol []interaction
|
||||
}
|
||||
|
||||
func (s *mockServer) init() {
|
||||
s.protocol = []interaction{
|
||||
{
|
||||
expected: "VER\n",
|
||||
response: "1\n",
|
||||
},
|
||||
{
|
||||
expected: "NETVER\n",
|
||||
response: "1\n",
|
||||
},
|
||||
{
|
||||
expected: "LIST UPS\n",
|
||||
response: "BEGIN LIST UPS\nUPS fake \"fake UPS\"\nEND LIST UPS\n",
|
||||
},
|
||||
{
|
||||
expected: "LIST CLIENT fake\n",
|
||||
response: "BEGIN LIST CLIENT fake\nCLIENT fake 127.0.0.1\nEND LIST CLIENT fake\n",
|
||||
},
|
||||
{
|
||||
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 (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{}
|
||||
ln, err := lc.Listen(ctx, "tcp4", "127.0.0.1:0")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
defer ln.Close()
|
||||
|
||||
for ctx.Err() == nil {
|
||||
func() {
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer conn.Close()
|
||||
err = conn.SetReadDeadline(time.Now().Add(time.Minute))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
in := make([]byte, 128)
|
||||
for _, interaction := range s.protocol {
|
||||
n, err := conn.Read(in)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to read from connection: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
request := in[:n]
|
||||
if !bytes.Equal([]byte(interaction.expected), request) {
|
||||
fmt.Printf("Unexpected request %q, expected %q\n", string(request), interaction.expected)
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := conn.Write([]byte(interaction.response)); err != nil {
|
||||
fmt.Printf("Cannot write answer for request %q: %v\n", string(request), err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Append EOF to end of output bytes
|
||||
if _, err := conn.Write([]byte{0, 0}); err != nil {
|
||||
fmt.Printf("Cannot write EOF: %v\n", err)
|
||||
return
|
||||
}
|
||||
}()
|
||||
}
|
||||
}()
|
||||
|
||||
return ln.Addr().(*net.TCPAddr), nil
|
||||
}
|
||||
|
||||
func setupServer(path string) (*mockServer, error) {
|
||||
// Read the variables
|
||||
varbuf, err := os.ReadFile(filepath.Join(path, "variables.dev"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("reading variables failed: %w", err)
|
||||
}
|
||||
|
||||
// Parse the information into variable names and values (upsc format)
|
||||
variables := make([]variable, 0)
|
||||
scanner := bufio.NewScanner(bytes.NewBuffer(varbuf))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("cannot parse line %s", line)
|
||||
}
|
||||
name := strings.TrimSpace(parts[0])
|
||||
value := strings.TrimSpace(parts[1])
|
||||
variables = append(variables, variable{name, value})
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("processing variables failed: %w", err)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// Parse the information into variable names and values (upsc format)
|
||||
types := make(map[string]string, 0)
|
||||
scanner = bufio.NewScanner(bytes.NewBuffer(typebuf))
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("cannot parse line %s", line)
|
||||
}
|
||||
name := strings.TrimSpace(parts[0])
|
||||
vartype := strings.TrimSpace(parts[1])
|
||||
types[name] = vartype
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("processing variables failed: %w", err)
|
||||
}
|
||||
|
||||
// Setup the server and add the device information
|
||||
server := &mockServer{}
|
||||
server.init()
|
||||
err = server.addVariables(variables, types)
|
||||
return server, err
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue