func()

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
}