利用Raid卡工具获取逻辑盘是否为SSD

原文地址: http://www.tony-yin.top/2018/...

网上很多获取一块盘是否为SSD的方式都是不靠谱的,不能覆盖到所有情况。一般我们在操作系统上的硬盘都是虚拟出来的逻辑盘,比如/dev/sda这种,它可能对应一块单独的物理硬盘,也有可能对应的是几块盘组成的raid。我们有时候想获取一块盘的具体信息,比如磁盘类型、插槽号、序列号等等,这时候我们就得借助对应的raid卡工具了,最常见的如Megacli,通过逻辑盘找到对应的物理盘,然后读取信息。

Raid卡简介

所谓raid卡,就是为了更好的统一管理物理硬盘而存在的,在出现单独的raid卡之前,对硬盘做raid操作,需要cpu完成其中的计算操作,这个会很影响其他依赖cpu的应用或进程的性能,后来就将raid卡单独提取出来,并且在其之上存在一个小型cpu供来完成raid相关操作的计算,这其中最常见的raid工具应该非Megacli莫属了。

为什么说最常见的呢?因为raid卡工具对应不同型号的raid卡是不一样,LSI只是一个半导体厂商,负责提供raid芯片,最后还需要集成到服务器厂商的机器上,所以最后的工具还是由厂商决定和提供,也可以理解为特定型号的raid对应各自的工具。

HBA卡简介

近来,又出现了一种HBA卡,只从HBA的英文解释HOST BUS ADAPTER(主机总线适配器)就能看出来,他肯定是给主机用的,一般HBA就是给主机插上后,给主机扩展出更多的接口,来连接外部的设备。大多数讲到HBA卡都是指光纤的HBA卡,给主机提供光纤接口的。也有ISCSIHBA卡,链接ISCSI设备的,从这种功能上说,我们也可以把独立网卡称为HBA卡,通过独立网卡扩展出网口来连接外部网络设备或主机。不过习惯上大部分HBA只是称光纤卡或者iscsi卡。

简而言之,这种HBA卡本身是为了扩展外部连接设备而存在的,但是它具有部分raid功能,与raid卡相比它的优势在于它价格便宜,性价比高;劣势在于虽然具有raid功能,但是都是基础的功能,没有raid卡那么完善。

这篇文章讲 raid卡和 HBA卡讲的挺好的: HBA卡 和 RAID卡

需求和背景

据我所知,这类工具往往是运维人员用的居多,但是往往开发中也会需要用到。本文通过获取逻辑盘对应盘的类型展开描述,并借此讲解获取逻辑盘的一类信息或通过逻辑盘操作对应物理盘。因为这其中的关键就是找到逻辑盘和物理盘之间的对应关系。无论是raid卡工具还是HBA卡工具都是罗列所有硬盘的信息,所以你要从中找到你选择的逻辑盘所对应的便是重中之重。

逻辑盘对应的物理盘可能为单独的硬盘,也可能是raid,单独的可以直接读取硬盘类型,raid的话我们认为只会将同样类型的盘做raid,混合的情况不考虑。

raid卡工具的话,我只对MegacliSas3ircu进行讲解,所以阅读本文前最好有使用以上两个工具的相关经验。首先我会根据目前存在的raid卡类型建立一个map关系,然后通过raid卡类型自动获取对应raid卡工具,每个raid卡都是一个类,然后里面的方法都是为该工具定制化的操作。

获取raid卡工具

目前就考虑两种型号的raid卡,以后有新的再往map里面填充就好了。NotSupport指的是其他不支持型号的raid卡和虚拟机。

do_shell是本人封装的一个在 python中执行 shell命令的方法,大家可以根据自己的情况对该方法进行转换

通过获取的card mode,根据map找到对应的tool,然后实例化对应的工具类

class RaidCardToolFactory():
    RaidCardMap = {
        'SAS2208': MegaraidTool,
        'SAS3008': HBATool,
        'NotSupport': NotSupport
    }
    
    def getTool(self):
        card_model = self.get_raidcard_model()
        tool = self.RaidCardMap[card_model]()
        return tool
        
    def get_raidcard_model(self):
        card_model = 'NotSupport'
        card_info = do_shell("lspci | grep 'LSI Logic'")
        if card_info == '':
            return card_model
        card = card_info.strip().splitlines()[0].split()
        if 'RAID bus controller' in card_info:
            card_model = card[10] + card[11]
        elif 'Attached SCSI controller' in card_info:
            card_model = card[10]
        return card_model

Megaraid工具类

  1. 先通过lsscsi命令获取逻辑盘是否为raid
  2. 如果是raid,那么直接根据lsscsi获取当前逻辑盘的target id,也就是第三个号,然后通过megacli cfgdsply -aALL获取所有raid信息,根据逻辑盘的target id对应物理盘中的Target Id找到对应raid,然后只要获取raid中第一块物理盘的硬盘类型即可,也就是Media Type,具体参见下方API: get_ld_type
  3. 如果不是raid,那么直接根据lsscsi获取当前逻辑盘的target id,也就是第三个号,这边的target id直接对应megacli中每一块单盘中的Device Id字段,所以根据target id匹配megacli pdlist aAll获取磁盘列表的每一项的Device Id便可以找到对应的物理盘,具体参见下方API: get_pd_type
class MegaraidTool():
    def get_disk_type(self, disk_name):
        scsi_info = do_shell("lsscsi | grep {} -w".format(disk_name))
        target_id = scsi_info.split()[0].split(":")[2]
        serial_nu = scsi_info.split()[3].strip()[2:]
        if "LSI" in scsi_info:
            disk_type = self.get_ld_type(target_id, serial_nu)
        else:
            disk_type = self.get_pd_type(target_id)
        return disk_type
        
    def get_ld_type(self, target_id, serial_nu):
        disk_type = ''
        cmd = MEGACLI + ' cfgdsply -aALL -NoLog|grep -E "Product Name|Target Id|Media Type"'
        output = do_shell(cmd)
        adapters = output.split('Product Name')
        for adapter in adapters:
            if serial_nu not in adapter:
                continue
            lines = adapter.split('\n')
            for line in lines:
                if "Target Id: {}".format(target_id) in line:
                    index = lines.index(line)
                    if 'Solid State Device' in lines[index + 1]:
                        disk_type = "SSD"
                    else :
                        disk_type = "HDD"
                    break
            if disk_type != '':
                break
        return disk_type
        
    def get_pd_type(self, target_id):
        disk_type = ''
        cmd = MEGACLI + ' pdlist aAll | grep -E "Device Id|Media Type"'
        output = do_shell(cmd, force=True)
        lines = output.split('\n')
        if 'Device Id: {}'.format(target_id) not in lines:
            return ''
        index = lines.index('Device Id: {}'.format(target_id))
        if 'Solid State Device' in lines[index + 1]:
            disk_type = "SSD"
        else :
            disk_type = "HDD"
        return disk_type

HBA工具类

  1. HBA类用的工具是sas3ircu,首先我们需要根据命令sas3ircu list获取所有的controller,然后每次获取信息都需要遍历所有controller
  2. 第一步依旧是判断逻辑盘是否为raid
  3. 如果是raid,获取逻辑盘的target id,与之匹配的是sas3ircu中的Initiator at ID字段,找到对应的raid,然后通过获取其下第一个物理盘的类型,这边类型字段变成了Drive Type,具体参考下方API: get_ld_type
  4. 如果非raid,我匹配的是sas3ircu中的Sas Address字段,那么逻辑盘的Sas Address如何获取呢?这边我用的方式是通过udev获取逻辑盘的symlink,这里面有很多address,而我们需要的是by-path,我这边就简单做了,看sas3ircu每个盘的Sas Address是否被udev获取的symlink包含,如果包含了,那么也就匹配到了,然后直接获取Drive Type字段就可以得到磁盘类型类;具体参考下方API: get_pd_type
class HBATool():
    def get_disk_type(self, disk_name):
        scsi_info = do_shell("lsscsi | grep {} -w".format(disk_name))
        if "LSI" in scsi_info:
            target_id = scsi_info.split()[0].split(":")[2]
            disk_type = self.get_ld_type(target_id)
        else:
            sas_address = do_cmd('udevadm info --query=symlink --name={}'.format(disk_name))
            disk_type = self.get_pd_type(sas_address)
        return disk_type
        
    def get_ld_type(self, target_id):
        disk_type = ''
        controllers = self.get_controllers()
        for controller in controllers:
            cmd = 'sas3ircu {} display|grep -E "Initiator at ID|Drive Type"'.format(controller)
            output = do_shell(cmd)
            if 'Initiator at ID #{}'.format(target_id) in output:
                lines = output.splitlines()
                index = lines.index('Initiator at ID #{}'.format(target_id))
                if 'HDD' in lines[index + 1]:
                    disk_type = 'HDD'
                else:
                    disk_type = 'SSD'
                break
        return disk_type
        
    def get_pd_type(self, sas_address):
        disk_type = ''
        controllers = self.get_controllers()
        for controller in controllers:
            cmd = 'sas3ircu {} display|grep -E "SAS Address|Drive Type"'.format(controller)
            output = do_shell(cmd)
            lines = output.splitlines()
            for i in xrange(0, len(lines), 2):
                address = lines[i].split()[-1].replace('-', '')
                if address in sas_address:
                    if 'HDD' in lines[i + 1]:
                        disk_type = 'HDD'
                    else:
                        disk_type = 'SSD'
                    break
            if disk_type != '':
                break
        return disk_type
        
    def get_controllers(self):
        cmd = 'sas3ircu list | awk \'{print $1}\''
        list = do_shell(cmd).splitlines()
        index = list.index('Index') + 2
        controllers = []
        for i in range(index, len(list) - 1):
            controllers.append(list[i])
        return controllers

调用方式

from mcs3.raidcardutils import RaidCardToolFactory

tool = RaidCardToolFactory().getTool()
disk_type = tool.get_disk_type(disk_name)

总结

其实这其中的关键就是先找到每一块物理盘的唯一标识,然后我们根据工具获取列表中的唯一标识字段,获取逻辑盘对应的信息,就比如上面的Device Id,对应的是逻辑盘的target id

完整代码地址: https://github.com/tony-yin/R...
如果有所帮助的话,帮忙 star一下哦 ^_^
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值