...

Source file src/github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/helper/blockchaos.go

Documentation: github.com/chaos-mesh/chaos-mesh/pkg/chaosdaemon/helper

     1  // Copyright 2022 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package helper
    17  
    18  import (
    19  	"fmt"
    20  	"os"
    21  	"path/filepath"
    22  
    23  	"github.com/moby/sys/mountinfo"
    24  	"github.com/pkg/errors"
    25  	"github.com/spf13/cobra"
    26  )
    27  
    28  var NormalizeVolumeNameCmd = &cobra.Command{
    29  	Use:   "normalize-volume-name [path]",
    30  	Short: "get the device name from the path",
    31  	Long: `Get the device name from the path.
    32  The path could be a directory, a partition, or a block device.
    33  The block device name will be printed out.`,
    34  	Run: func(cmd *cobra.Command, args []string) {
    35  		if len(args) != 1 {
    36  			cmd.Help()
    37  			os.Exit(1)
    38  		}
    39  
    40  		volumePath := args[0]
    41  		deviceName, err := normalizeVolumeName(volumePath)
    42  		if err != nil {
    43  			fmt.Fprintln(os.Stderr, err)
    44  			os.Exit(1)
    45  		}
    46  
    47  		fmt.Println(deviceName)
    48  	},
    49  }
    50  
    51  func normalizeVolumeName(volumePath string) (string, error) {
    52  	// before resolving the soft link the volumePath inside the request have three possible situations:
    53  	// 1. the volumePath is a partition of a block device, e.g. /dev/sda1, or /mnt/disks/ata-CT2000MX500SSD1_2117E599E804-part1
    54  	// 2. the volumePath is a block file path, e.g. /dev/sda, or /mnt/disks/ata-CT2000MX500SSD1_2117E599E804
    55  	// 3. the volumePath is a directory path, e.g. /var/lib/docker/volumes/my-volume
    56  	//
    57  	// if it's a partition of a block device, we need to convert it to the block file path
    58  	// if it's a block device, the client library of chaos-driver can handle it
    59  	// if it's a directory, chaos-daemon should automatically convert it to the corresponding block device name
    60  	// For example, the return value of this function could be: `sda`, `sdb`, `nvme0n1`,
    61  
    62  	volumePath, err := filepath.EvalSymlinks(volumePath)
    63  	if err != nil {
    64  		return "", errors.Wrapf(err, "resolving symlink %s", volumePath)
    65  	}
    66  
    67  	stat, err := os.Stat(volumePath)
    68  	if err != nil {
    69  		return "", errors.Wrapf(err, "getting stat of %s", volumePath)
    70  	}
    71  
    72  	if stat.IsDir() {
    73  		parentMounts, err := mountinfo.GetMounts(mountinfo.ParentsFilter(volumePath))
    74  		if err != nil {
    75  			return "", errors.Wrap(err, "read mountinfo")
    76  		}
    77  
    78  		if len(parentMounts) == 0 {
    79  			return "", errors.Errorf("cannot find the mount point which contains the volume path %s", volumePath)
    80  		}
    81  
    82  		bestMatch := &mountinfo.Info{}
    83  		for _, mount := range parentMounts {
    84  			mount := mount
    85  			if len(mount.Mountpoint) > len(bestMatch.Mountpoint) {
    86  				bestMatch = mount
    87  			}
    88  		}
    89  
    90  		if bestMatch.Source == "none" || len(bestMatch.Source) == 0 {
    91  			return "", errors.Errorf("unknown source of the mount point %v", bestMatch)
    92  		}
    93  		volumePath = bestMatch.Source
    94  	}
    95  
    96  	// now, the `volumePath` is either a partition, or a block device
    97  	volumeName := filepath.Base(volumePath)
    98  
    99  	// volumeName is either a partition (`sda1`, `nvme0n1p1`), or a block device (`sda`, `nvme0n1`)
   100  	if _, err := os.Stat("/sys/block/" + volumeName); errors.Is(err, os.ErrNotExist) {
   101  		// the volumeName is a partition, convert it to the block device name
   102  		partitionSysPath, err := filepath.EvalSymlinks("/sys/class/block/" + volumeName)
   103  		if err != nil {
   104  			return "", errors.Wrapf(err, "resolving symlink %s", "/sys/class/block/"+volumeName)
   105  		}
   106  
   107  		volumeName = filepath.Base(filepath.Dir(partitionSysPath))
   108  	}
   109  	return volumeName, nil
   110  }
   111  
   112  // Manually test has been done for the following situations:
   113  // 1. cdh normalize-volume-name /home, where /home is a simple directory
   114  // 2. cdh normalize-volume-name /dev/vda1
   115  // 3. cdh normalize-volume-name /dev/vda
   116