in internal/hostmetrics/diskstatsreader/diskstatsreader.go [206:277]
func (r *Reader) readDiskStatsForWindows(ctx context.Context, instanceProps *iipb.InstanceProperties) map[string]*statspb.DiskStats {
diskStats := make(map[string]*statspb.DiskStats)
for _, disk := range instanceProps.GetDisks() {
diskNumber, ok := parseWindowsDiskNumber(disk.GetMapping())
if !ok {
log.CtxLogger(ctx).Infow("Could not get disk number from device mapping", "mapping", disk.GetMapping())
continue
}
// Note: must use separated arguments so the windows go exec does not escape the entire argument list
var args []string
args = append(args, "-command")
args = append(args, "$(Get-Counter")
args = append(args, fmt.Sprintf(`'\PhysicalDisk(%d*)\Avg.`, diskNumber))
args = append(args, "Disk")
args = append(args, "sec/Read').CounterSamples[0].CookedValue;Write-Host")
args = append(args, "';';$(Get-Counter")
args = append(args, fmt.Sprintf(`'\PhysicalDisk(%d*)\Avg.`, diskNumber))
args = append(args, "Disk")
args = append(args, "sec/Write').CounterSamples[0].CookedValue;Write-Host")
args = append(args, "';';$(Get-Counter")
args = append(args, fmt.Sprintf(`'\PhysicalDisk(%d*)\Current`, diskNumber))
args = append(args, "Disk")
args = append(args, "Queue")
args = append(args, "Length').CounterSamples[0].CookedValue")
result := r.execute(ctx, commandlineexecutor.Params{
Executable: "powershell",
Args: args,
})
stdOut := strings.Replace(strings.Replace(result.StdOut, "\n", "", -1), "\r", "", -1)
log.CtxLogger(ctx).Debugw("PowerShell command returned data", "stdout", stdOut, "stderr", result.StdErr, "error", result.Error)
if result.Error != nil {
log.CtxLogger(ctx).Warnw("Could not get stats for disk", "devicename", disk.GetDeviceName(), "mapping", disk.GetMapping(), "number", diskNumber)
continue
}
values := strings.Split(stdOut, ";")
if len(values) != 3 {
log.CtxLogger(ctx).Warnw("Unexpected output format when fetching disk stats", "stdout", stdOut, "devicename", disk.GetDeviceName(), "mapping", disk.GetMapping(), "number", diskNumber, "valueslength", len(values))
continue
}
averageReadResponseTime := int64(metricsformatter.Unavailable)
averageRead, err := strconv.ParseFloat(values[0], 64)
if err != nil {
log.CtxLogger(ctx).Warnw("Could not parse average read response time from output", "output", values[0])
} else {
averageReadResponseTime = int64(math.Round(averageRead * 1000))
}
averageWriteResponseTime := int64(metricsformatter.Unavailable)
averageWrite, err := strconv.ParseFloat(values[1], 64)
if err != nil {
log.CtxLogger(ctx).Warnw("Could not parse average write response time from output", "output", values[1])
} else {
averageWriteResponseTime = int64(math.Round(averageWrite * 1000))
}
queueLength, err := strconv.ParseInt(values[2], 10, 64)
if err != nil {
log.CtxLogger(ctx).Warnw("Could not parse queue length from output", "output", values[2])
queueLength = metricsformatter.Unavailable
}
diskStats[disk.GetMapping()] = &statspb.DiskStats{
DeviceName: disk.GetMapping(),
AverageReadResponseTimeMillis: averageReadResponseTime,
AverageWriteResponseTimeMillis: averageWriteResponseTime,
QueueLength: queueLength,
}
log.CtxLogger(ctx).Debugw("Disk stats", "diskstats", diskStats[disk.GetMapping()])
}
return diskStats
}