...

Source file src/github.com/prometheus/procfs/sysfs/class_sas_device.go

Documentation: github.com/prometheus/procfs/sysfs

     1  // Copyright 2022 The Prometheus Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  //go:build linux
    15  // +build linux
    16  
    17  package sysfs
    18  
    19  import (
    20  	"os"
    21  	"path/filepath"
    22  	"regexp"
    23  
    24  	"github.com/prometheus/procfs/internal/util"
    25  )
    26  
    27  const (
    28  	sasDeviceClassPath    = "class/sas_device"
    29  	sasEndDeviceClassPath = "class/sas_end_device"
    30  	sasExpanderClassPath  = "class/sas_expander"
    31  )
    32  
    33  type SASDevice struct {
    34  	Name         string   // /sys/class/sas_device/<Name>
    35  	SASAddress   string   // /sys/class/sas_device/<Name>/sas_address
    36  	SASPhys      []string // /sys/class/sas_device/<Name>/device/phy-*
    37  	SASPorts     []string // /sys/class/sas_device/<Name>/device/ports-*
    38  	BlockDevices []string // /sys/class/sas_device/<Name>/device/target*/*/block/*
    39  }
    40  
    41  type SASDeviceClass map[string]*SASDevice
    42  
    43  var (
    44  	sasTargetDeviceRegexp    = regexp.MustCompile(`^target[0-9:]+$`)
    45  	sasTargetSubDeviceRegexp = regexp.MustCompile(`[0-9]+:.*`)
    46  )
    47  
    48  // sasDeviceClasses reads all of the SAS devices from a specific set
    49  // of /sys/class/sas*/ entries.  The sas_device, sas_end_device, and
    50  // sas_expander classes are all nearly identical and can be handled by the same basic code.
    51  
    52  func (fs FS) parseSASDeviceClass(dir string) (SASDeviceClass, error) {
    53  	path := fs.sys.Path(dir)
    54  
    55  	dirs, err := os.ReadDir(path)
    56  	if err != nil {
    57  		return nil, err
    58  	}
    59  
    60  	sdc := make(SASDeviceClass, len(dirs))
    61  
    62  	for _, d := range dirs {
    63  		device, err := fs.parseSASDevice(d.Name())
    64  		if err != nil {
    65  			return nil, err
    66  		}
    67  
    68  		sdc[device.Name] = device
    69  	}
    70  
    71  	return sdc, nil
    72  }
    73  
    74  // SASDeviceClass parses devices in /sys/class/sas_device.
    75  func (fs FS) SASDeviceClass() (SASDeviceClass, error) {
    76  	return fs.parseSASDeviceClass(sasDeviceClassPath)
    77  }
    78  
    79  // SASEndDeviceClass parses devices in /sys/class/sas_end_device.
    80  // This is a subset of sas_device, and excludes expanders.
    81  func (fs FS) SASEndDeviceClass() (SASDeviceClass, error) {
    82  	return fs.parseSASDeviceClass(sasEndDeviceClassPath)
    83  }
    84  
    85  // SASExpanderClass parses devices in /sys/class/sas_expander.
    86  // This is a subset of sas_device, but only includes expanders.
    87  func (fs FS) SASExpanderClass() (SASDeviceClass, error) {
    88  	return fs.parseSASDeviceClass(sasExpanderClassPath)
    89  }
    90  
    91  // Parse a single sas_device.  This uses /sys/class/sas_device, as
    92  // it's a superset of the other two directories so there's no reason
    93  // to plumb the path through to here.
    94  func (fs FS) parseSASDevice(name string) (*SASDevice, error) {
    95  	device := SASDevice{Name: name}
    96  
    97  	devicepath := fs.sys.Path(filepath.Join(sasDeviceClassPath, name, "device"))
    98  
    99  	dirs, err := os.ReadDir(devicepath)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  
   104  	for _, d := range dirs {
   105  		if sasPhyDeviceRegexp.MatchString(d.Name()) {
   106  			device.SASPhys = append(device.SASPhys, d.Name())
   107  		}
   108  		if sasPortDeviceRegexp.MatchString(d.Name()) {
   109  			device.SASPorts = append(device.SASPorts, d.Name())
   110  		}
   111  	}
   112  
   113  	address := fs.sys.Path(sasDeviceClassPath, name, "sas_address")
   114  	value, err := util.SysReadFile(address)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	device.SASAddress = value
   119  
   120  	device.BlockDevices, err = fs.blockSASDeviceBlockDevices(name)
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  
   125  	return &device, nil
   126  }
   127  
   128  // Identify block devices that map to a specific SAS Device
   129  // This info comes from (for example)
   130  // /sys/class/sas_device/end_device-11:2/device/target11:0:0/11:0:0:0/block/sdp
   131  //
   132  // To find that, we have to look in the device directory for target$X
   133  // subdirs, then specific subdirs of $X, then read from directory
   134  // names in the 'block/' subdirectory under that.  This really
   135  // shouldn't be this hard.
   136  func (fs FS) blockSASDeviceBlockDevices(name string) ([]string, error) {
   137  	var devices []string
   138  
   139  	devicepath := fs.sys.Path(filepath.Join(sasDeviceClassPath, name, "device"))
   140  
   141  	dirs, err := os.ReadDir(devicepath)
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  
   146  	for _, d := range dirs {
   147  		if sasTargetDeviceRegexp.MatchString(d.Name()) {
   148  			targetdir := d.Name()
   149  
   150  			subtargets, err := os.ReadDir(filepath.Join(devicepath, targetdir))
   151  			if err != nil {
   152  				return nil, err
   153  			}
   154  
   155  			for _, targetsubdir := range subtargets {
   156  
   157  				if !sasTargetSubDeviceRegexp.MatchString(targetsubdir.Name()) {
   158  					// need to skip 'power', 'subsys', etc.
   159  					continue
   160  				}
   161  
   162  				blocks, err := os.ReadDir(filepath.Join(devicepath, targetdir, targetsubdir.Name(), "block"))
   163  				if err != nil {
   164  					if os.IsNotExist(err) {
   165  						continue
   166  					}
   167  					return nil, err
   168  				}
   169  
   170  				for _, blockdevice := range blocks {
   171  					devices = append(devices, blockdevice.Name())
   172  				}
   173  			}
   174  		}
   175  	}
   176  
   177  	return devices, nil
   178  }
   179  
   180  // GetByName returns the SASDevice with the provided name.
   181  func (sdc *SASDeviceClass) GetByName(name string) *SASDevice {
   182  	return (*sdc)[name]
   183  }
   184  
   185  // GetByPhy finds the SASDevice that contains the provided PHY name.
   186  func (sdc *SASDeviceClass) GetByPhy(name string) *SASDevice {
   187  	for _, d := range *sdc {
   188  		for _, p := range d.SASPhys {
   189  			if p == name {
   190  				return d
   191  			}
   192  		}
   193  	}
   194  	return nil
   195  }
   196  
   197  // GetByPort finds the SASDevice that contains the provided SAS Port name.
   198  func (sdc *SASDeviceClass) GetByPort(name string) *SASDevice {
   199  	for _, d := range *sdc {
   200  		for _, p := range d.SASPorts {
   201  			if p == name {
   202  				return d
   203  			}
   204  		}
   205  	}
   206  	return nil
   207  }
   208  

View as plain text