Skip to content

Commit 3129a7d

Browse files
committed
pdu telemetry
Signed-off-by: Harper, Jason M <[email protected]>
1 parent b078105 commit 3129a7d

File tree

3 files changed

+85
-6
lines changed

3 files changed

+85
-6
lines changed

internal/report/render_html.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1542,11 +1542,31 @@ func gaudiTelemetryTableHTMLRenderer(tableValues TableValues, targetName string)
15421542

15431543
func pduTelemetryTableHTMLRenderer(tableValues TableValues, targetName string) string {
15441544
data := [][]float64{}
1545+
for _, field := range tableValues.Fields[1:] {
1546+
points := []float64{}
1547+
for _, val := range field.Values {
1548+
if val == "" {
1549+
break
1550+
}
1551+
stat, err := strconv.ParseFloat(val, 64)
1552+
if err != nil {
1553+
slog.Error("error parsing stat", slog.String("error", err.Error()))
1554+
return ""
1555+
}
1556+
points = append(points, stat)
1557+
}
1558+
if len(points) > 0 {
1559+
data = append(data, points)
1560+
}
1561+
}
15451562
datasetNames := []string{}
1563+
for _, field := range tableValues.Fields[1:] {
1564+
datasetNames = append(datasetNames, field.Name)
1565+
}
15461566
chartConfig := chartTemplateStruct{
15471567
ID: fmt.Sprintf("%s%d", tableValues.Name, util.RandUint(10000)),
15481568
XaxisText: "Time",
1549-
YaxisText: "Value",
1569+
YaxisText: "Watts",
15501570
TitleText: "",
15511571
DisplayTitle: "false",
15521572
DisplayLegend: "true",

internal/report/table_defs.go

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2768,7 +2768,37 @@ func gaudiTelemetryTableValues(outputs map[string]script.ScriptOutput) []Field {
27682768
}
27692769

27702770
func pduTelemetryTableValues(outputs map[string]script.ScriptOutput) []Field {
2771-
return []Field{}
2771+
// extract PDU fields and their values from PDU telemetry script output
2772+
// output is CSV formatted:
2773+
// Timestamp,ActivePower(W)
2774+
// 18:32:38,123.45
2775+
// 18:32:40,124.10
2776+
// ...
2777+
fields := []Field{}
2778+
reader := csv.NewReader(strings.NewReader(outputs[script.PDUTelemetryScriptName].Stdout))
2779+
records, err := reader.ReadAll()
2780+
if err != nil {
2781+
slog.Error("failed to read PDU telemetry CSV output", slog.String("error", err.Error()))
2782+
return []Field{}
2783+
}
2784+
if len(records) == 0 {
2785+
return []Field{}
2786+
}
2787+
// first row is the header
2788+
for _, header := range records[0] {
2789+
fields = append(fields, Field{Name: header, Values: []string{}})
2790+
}
2791+
// subsequent rows are data
2792+
for _, record := range records[1:] {
2793+
if len(record) != len(fields) {
2794+
slog.Error("unexpected number of fields in PDU telemetry output", slog.Int("expected", len(fields)), slog.Int("got", len(record)))
2795+
return []Field{}
2796+
}
2797+
for i, value := range record {
2798+
fields[i].Values = append(fields[i].Values, value)
2799+
}
2800+
}
2801+
return fields
27722802
}
27732803

27742804
func callStackFrequencyTableValues(outputs map[string]script.ScriptOutput) []Field {

internal/script/script_defs.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1326,10 +1326,39 @@ fi
13261326
NeedsKill: true,
13271327
},
13281328
PDUTelemetryScriptName: {
1329-
Name: PDUTelemetryScriptName,
1330-
ScriptTemplate: ``,
1331-
Superuser: false,
1332-
NeedsKill: false,
1329+
Name: PDUTelemetryScriptName,
1330+
ScriptTemplate: `
1331+
duration={{.Duration}} # total duration in seconds
1332+
interval={{.Interval}} # time between readings in seconds
1333+
pdu="{{.PDUHost}}" # PDU hostname or IP address (must not start with protocol like http://, may include port)
1334+
pdu_ip=$(echo $pdu | awk -F/ '{print $NF}' | awk -F: '{print $1}') # remove http:// or https:// and port if present
1335+
pdu_username="{{.PDUUser}}"
1336+
pdu_password="{{.PDUPassword}}"
1337+
outletgroup="{{.PDUOutlet}}"
1338+
count=$((duration / interval))
1339+
echo "Timestamp,ActivePower(W)"
1340+
for ((i=0; i<count; i++)); do
1341+
timestamp=$(date +%H:%M:%S)
1342+
response=$(no_proxy=$pdu_ip curl --max-time $interval -sS -k -u "$pdu_username:$pdu_password" \
1343+
-d "{'jsonrpc':'2.0','method':'getReading'}" \
1344+
"https://${pdu}/model/outletgroup/${outletgroup}/activePower")
1345+
# Check if curl succeeded
1346+
if [ $? -ne 0 ] || [ -z "$response" ]; then
1347+
echo "ERROR: Failed to retrieve data from PDU" >&2
1348+
exit 1
1349+
fi
1350+
# Try to parse the value using jq
1351+
w=$(echo "$response" | jq -r '.result._ret_.value // empty')
1352+
if [ -z "$w" ]; then
1353+
echo "ERROR: Invalid response format or missing value" >&2
1354+
exit 1
1355+
fi
1356+
echo "$timestamp,$w"
1357+
sleep $interval
1358+
done
1359+
`,
1360+
Superuser: false,
1361+
NeedsKill: false,
13331362
},
13341363
// flamegraph scripts
13351364
CollapsedCallStacksScriptName: {

0 commit comments

Comments
 (0)