infi.storagemodel中如何获取存储设备的htcl参数


infi.storagemodel是github上的一个开源的项目,链接如下:
https://github.com/Infinidat/infi.storagemodel

在其中的/infi/storagemodel/linux目录下的LinuxStorageModel(UnixStorageModel)类中,需要获取一个Sysfs()对象,这个对象是在同一个目录下的sysfs.py实现的,Sysfs对象内部有一个hctl对象,这个hctl对象是什么呢?

一、HCTL参数

在linux中,scsi有4个层级的寻址方案:

  • SCSI adapter number [host]
  • channel number [bus]
  • id number [target]
  • lun [lun]

解释:
SCSI适配器编号通常是在计算机内部IO总线上的一个任意的号码,这些适配器通常被叫做HBA(host bus adapter),SCSI适配器编号是由内核指定的,且从0开始增加。
每一个HBA都控制了一个或多个SCSI总线。
每个SCSI总线都连接多个SCSI设备,在SCSI中称HBA为initiator,initiator和通常称之为SCSI设备的targets进行通信。

在lsscsi命令的manpage中,我们可以看到如下的解释,

Generic SCSI devices can also be accessed via the bsg driver in Linux. By default, the bsg driver's device node names are of the form '/dev/bsg/H:C:T:L'.

执行lsscsi可以看到如下显示结果:

[0:0:0:0]    disk    VMware,  VMware Virtual S 1.0   /dev/sda 
[0:0:1:0]    disk    VMware,  VMware Virtual S 1.0   /dev/sdb 
[2:0:0:0]    cd/dvd  NECVMWar VMware IDE CDR10 1.00  /dev/sr0 

前面的[0:0:1:0]四个数字就代表了这个scsi设备的hctl参数,分别是:

  • host: SCSI hosts currently attached to the system.调用lsscsi -H可以看到所有host
  • channel
  • target:
  • lun: 逻辑单元数

二、获取HCTL

获取HCTL可以通过直接调用lsscsi,更为有效的方法是直接给device发送ioctl的请求,在infi.stragemodel中使用的是后者:

跟踪hctl的获取方法,Sysfs类中调用了一个方法:

from infi.sgutils.sg_map import get_hctl_for_sd_device
#dev_path='/dev/sdb'
hctl = get_hctl_for_sd_device(dev_path)

这个方法的实现:

def get_hctl_for_sd_device(device_path):
    from ..ioctl import scsi_ioctl_get_idlun as _ioctl
    #获取一个SCSI_IDLUN对象
    struct = _ioctl(device_path)
    # http://tldp.org/HOWTO/SCSI-Generic-HOWTO/scsi_g_idlun.html
    # "four_in_one" is made up as follows:
    # (scsi_device_id | (lun << 8) | (channel << 16) | (host_no << 24))
    host = (struct.four_in_one >> 24)
    channel = (struct.four_in_one >> 16) & 0xFF
    target = (struct.four_in_one) & 0xFF
    lun = (struct.four_in_one >> 8) & 0xFF
    result = HCTL(host, channel, target, lun)
    return HCTL(host, channel, target, lun)

scsi_ioctl_get_idlun的实现:

def scsi_ioctl_get_idlun(device_path):
    from array import array  
    struct_cls = structures.SCSI_IDLUN     
    size = struct_cls.min_max_sizeof().max 
    buffer = array("B", [0]*size)
    #调用下一个函数,op是固定的0x5382,执行结果回写到buffer中
    result = ioctl(device_path, opcodes.SCSI_IOCTL_GET_IDLUN, buffer)
    struct = struct_cls.create_from_string(buffer)
    #返回SCSI_IDLUN(four_in_one=1, host_unique_id=0)
    return struct

def ioctl(device_path, op_number, buffer=None):
    #打开/dev/sdb,拿到句柄
    fd = os.open(device_path, os.O_RDONLY | os.O_NONBLOCK)
    try:
        from fcntl import ioctl as _ioctl
        args = [fd, op_number,]
        if buffer is not None: 
            args.extend([buffer, True])
        #args=[3, 21378, array('B', [0, 0, 0, 0, 0, 0, 0, 0]), True]
        return _ioctl(*args)
    finally:
        os.close(fd)

scsi_ioctl_get_idlun返回两个参数,four_in_one和host_unique_id,这个four in one的参数其实就是我们需要的hctl参数,返回去看get_hctl_for_sd_device函数是怎么解析这个four in one的:

host = (struct.four_in_one >> 24)
channel = (struct.four_in_one >> 16) & 0xFF
target = (struct.four_in_one) & 0xFF
lun = (struct.four_in_one >> 8) & 0xFF
result = HCTL(host, channel, target, lun)

可以看出这个参数是一个4*8=32位的int型变量,host是第一个8位,channel是第二个8位,lun是第三个8位,target是第4个8位,lsscsi出来的[0:0:1:0]对应的four_in_one参数为:0|0|0|1,这个32位的二进制数的十进制值为1。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,868评论 18 139
  • 总线 计算机的各个功能部件通过总线连接在一起构成完整的计算机系统,总线是多个系统功能部件之间进行数据传送的公共通路...
    罗蓁蓁阅读 4,887评论 0 10
  • 现象描述 在很多场景下,客户会挂载多个数据磁盘到虚拟机上,为了提高性能和易扩展性,会使用到 raid 或者 lvm...
    ITknight阅读 582评论 0 0
  • 第一次读不抱怨的世界是在好几年前了,那个时候并没有意识到自己很爱抱怨。突然有一天,同事跟我说:“米米,这本书你可以...
    米米心臻阅读 350评论 0 0
  • 文 艾米在渔村 今天又下雨了。 整个世界被灰蒙蒙的天空笼罩着,大雨唰唰唰,一阵阵不知疲倦地下着,远远望去,白茫茫的...
    月影清韵阅读 1,318评论 23 104