func findDisksForHANABasePath()

in internal/system/appsdiscovery/apps_discovery.go [1525:1654]


func findDisksForHANABasePath(ctx context.Context, pathName string, globalINIPath string, exec commandlineexecutor.Execute) ([]string, error) {
	// Get paths for desired mounts from global.ini
	p := commandlineexecutor.Params{
		Executable: "grep",
		Args:       []string{pathName, globalINIPath},
	}
	res := exec(ctx, p)
	if res.Error != nil {
		log.CtxLogger(ctx).Infow("Error executing grep", "error", res.Error, "stdOut", res.StdOut, "stdErr", res.StdErr, "exitcode", res.ExitCode)
		return nil, res.Error
	}
	if res.StdOut == "" {
		return nil, errors.New("path not found in global.ini " + pathName)
	}

	// Expected output should be like:
	// basepath_datavolumes = /path/to/mount
	parts := strings.Split(res.StdOut, "=")
	if len(parts) < 2 {
		return nil, errors.New("unable to find path for mount")
	}
	mount := strings.TrimSpace(parts[1])
	// Remove trailing slash if present
	mount = strings.TrimSuffix(mount, "/")
	log.CtxLogger(ctx).Debugw("Found mount", "mount", mount)

	// Find what is mounted to that path.
	p = commandlineexecutor.Params{
		Executable: "lsblk",
		Args:       []string{"--output=NAME,MOUNTPOINTS", "--json"},
	}
	res = exec(ctx, p)
	if res.Error != nil {
		log.CtxLogger(ctx).Infow("Error executing lsblk", "error", res.Error, "stdOut", res.StdOut, "stdErr", res.StdErr, "exitcode", res.ExitCode)
		return nil, res.Error
	}
	// Output is json
	var result lsblk
	err := json.Unmarshal([]byte(res.StdOut), &result)
	if err != nil {
		log.CtxLogger(ctx).Infow("Error unmarshalling lsblk output", "error", err, "stdOut", res.StdOut)
		return nil, err
	}

	var deviceNames []string
	bestMatchLength := 0
	// Find the block device with the best match to mount
	for _, blockDevice := range result.BlockDevices {
		log.CtxLogger(ctx).Debugw("Block device", "blockDevice", blockDevice)
		blockDeviceName, blockMatchLen, err := findMountPointInBlockDevice(ctx, mount, blockDevice)
		if err != nil {
			return nil, err
		}

		if blockDeviceName == "" {
			continue
		}

		if blockMatchLen > bestMatchLength {
			log.CtxLogger(ctx).Debugw("Found better match", "blockDeviceName", blockDeviceName, "blockMatchLen", blockMatchLen, "bestMatchLength", bestMatchLength)
			bestMatchLength = blockMatchLen
			deviceNames = []string{blockDeviceName}
		} else if blockMatchLen == bestMatchLength {
			log.CtxLogger(ctx).Debugw("Found match with same length", "blockDeviceName", blockDeviceName, "blockMatchLen", blockMatchLen, "bestMatchLength", bestMatchLength)
			deviceNames = append(deviceNames, blockDeviceName)
		}
	}
	if len(deviceNames) == 0 {
		return nil, errors.New("unable to find disk for mount")
	}
	log.CtxLogger(ctx).Debugw("Found device name", "deviceName", deviceNames)

	// Find disk name for that device.
	p = commandlineexecutor.Params{
		Executable: "ls",

		Args: []string{"-lart", "/dev/disk/by-id/"},
	}
	res = exec(ctx, p)
	if res.Error != nil {
		log.CtxLogger(ctx).Infow("Error executing ls", "error", res.Error, "stdOut", res.StdOut, "stdErr", res.StdErr, "exitcode", res.ExitCode)
		return nil, res.Error
	}

	// Output will look like:
	// lrwxrwxrwx 1 root root  9 Feb  5 07:32 /dev/disk/by-id/google-persistent-disk-0 -> ../../sda
	// lrwxrwxrwx 1 root root  9 Feb  5 07:32 /dev/disk/by-id/google-sap-posdb00-hana-shared -> ../../sdf
	// lrwxrwxrwx 1 root root  9 Feb  5 07:32 /dev/disk/by-id/google-sap-posdb00-hana-data-0 -> ../../sdc
	// lrwxrwxrwx 1 root root  9 Feb  5 07:32 /dev/disk/by-id/google-sap-posdb00-usr-sap -> ../../sdb

	devicePaths := []string{}
	for _, deviceName := range deviceNames {
		log.CtxLogger(ctx).Debugw("deviceName", "deviceName", deviceName)
		for _, line := range strings.Split(res.StdOut, "\n") {
			parts := strings.Fields(line)
			// Expected parts:
			// 0: permissions
			// 1: links
			// 2: owner
			// 3: group
			// 4: size
			// 5: month
			// 6: day
			// 7: time
			// 8: path
			// 9: ->
			// 10: device
			log.CtxLogger(ctx).Debugw("parts", "parts", parts)
			if len(parts) < 11 {
				continue
			}
			device := parts[10]
			if strings.HasSuffix(device, deviceName) {
				log.CtxLogger(ctx).Debugw("ls output", "line", line)
				log.CtxLogger(ctx).Debugw("Found device name in ls output")
				devicePath := parts[8]

				// Strip up up to the end of /google-
				devicePath = strings.TrimPrefix(devicePath, "/dev/disk/by-id/")
				devicePath = strings.TrimPrefix(devicePath, "google-")
				devicePath = strings.TrimPrefix(devicePath, "scsi-0Google_PersistentDisk_")
				// Maybe need to handle disk partitions
				if !slices.Contains(devicePaths, devicePath) {
					devicePaths = append(devicePaths, devicePath)
				}
			}
		}
	}
	return devicePaths, nil
}