[关闭]
@tony-yin 2018-01-26T23:46:33.000000Z 字数 5554 阅读 992

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

Ceph Raid卡


网上很多获取一块盘是否为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,然后实例化对应的工具类

  1. class RaidCardToolFactory():
  2. RaidCardMap = {
  3. 'SAS2208': MegaraidTool,
  4. 'SAS3008': HBATool,
  5. 'NotSupport': NotSupport
  6. }
  7. def getTool(self):
  8. card_model = self.get_raidcard_model()
  9. tool = self.RaidCardMap[card_model]()
  10. return tool
  11. def get_raidcard_model(self):
  12. card_model = 'NotSupport'
  13. card_info = do_shell("lspci | grep 'LSI Logic'")
  14. if card_info == '':
  15. return card_model
  16. card = card_info.strip().splitlines()[0].split()
  17. if 'RAID bus controller' in card_info:
  18. card_model = card[10] + card[11]
  19. elif 'Attached SCSI controller' in card_info:
  20. card_model = card[10]
  21. 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
  1. class MegaraidTool():
  2. def get_disk_type(self, disk_name):
  3. scsi_info = do_shell("lsscsi | grep {} -w".format(disk_name))
  4. target_id = scsi_info.split()[0].split(":")[2]
  5. serial_nu = scsi_info.split()[3].strip()[2:]
  6. if "LSI" in scsi_info:
  7. disk_type = self.get_ld_type(target_id, serial_nu)
  8. else:
  9. disk_type = self.get_pd_type(target_id)
  10. return disk_type
  11. def get_ld_type(self, target_id, serial_nu):
  12. disk_type = ''
  13. cmd = MEGACLI + ' cfgdsply -aALL -NoLog|grep -E "Product Name|Target Id|Media Type"'
  14. output = do_shell(cmd)
  15. adapters = output.split('Product Name')
  16. for adapter in adapters:
  17. if serial_nu not in adapter:
  18. continue
  19. lines = adapter.split('\n')
  20. for line in lines:
  21. if "Target Id: {}".format(target_id) in line:
  22. index = lines.index(line)
  23. if 'Solid State Device' in lines[index + 1]:
  24. disk_type = "SSD"
  25. else :
  26. disk_type = "HDD"
  27. break
  28. if disk_type != '':
  29. break
  30. return disk_type
  31. def get_pd_type(self, target_id):
  32. disk_type = ''
  33. cmd = MEGACLI + ' pdlist aAll | grep -E "Device Id|Media Type"'
  34. output = do_shell(cmd, force=True)
  35. lines = output.split('\n')
  36. if 'Device Id: {}'.format(target_id) not in lines:
  37. return ''
  38. index = lines.index('Device Id: {}'.format(target_id))
  39. if 'Solid State Device' in lines[index + 1]:
  40. disk_type = "SSD"
  41. else :
  42. disk_type = "HDD"
  43. 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
  1. class HBATool():
  2. def get_disk_type(self, disk_name):
  3. scsi_info = do_shell("lsscsi | grep {} -w".format(disk_name))
  4. if "LSI" in scsi_info:
  5. target_id = scsi_info.split()[0].split(":")[2]
  6. disk_type = self.get_ld_type(target_id)
  7. else:
  8. sas_address = do_cmd('udevadm info --query=symlink --name={}'.format(disk_name))
  9. disk_type = self.get_pd_type(sas_address)
  10. return disk_type
  11. def get_ld_type(self, target_id):
  12. disk_type = ''
  13. controllers = self.get_controllers()
  14. for controller in controllers:
  15. cmd = 'sas3ircu {} display|grep -E "Initiator at ID|Drive Type"'.format(controller)
  16. output = do_shell(cmd)
  17. if 'Initiator at ID #{}'.format(target_id) in output:
  18. lines = output.splitlines()
  19. index = lines.index('Initiator at ID #{}'.format(target_id))
  20. if 'HDD' in lines[index + 1]:
  21. disk_type = 'HDD'
  22. else:
  23. disk_type = 'SSD'
  24. break
  25. return disk_type
  26. def get_pd_type(self, sas_address):
  27. disk_type = ''
  28. controllers = self.get_controllers()
  29. for controller in controllers:
  30. cmd = 'sas3ircu {} display|grep -E "SAS Address|Drive Type"'.format(controller)
  31. output = do_shell(cmd)
  32. lines = output.splitlines()
  33. for i in xrange(0, len(lines), 2):
  34. address = lines[i].split()[-1].replace('-', '')
  35. if address in sas_address:
  36. if 'HDD' in lines[i + 1]:
  37. disk_type = 'HDD'
  38. else:
  39. disk_type = 'SSD'
  40. break
  41. if disk_type != '':
  42. break
  43. return disk_type
  44. def get_controllers(self):
  45. cmd = 'sas3ircu list | awk \'{print $1}\''
  46. list = do_shell(cmd).splitlines()
  47. index = list.index('Index') + 2
  48. controllers = []
  49. for i in range(index, len(list) - 1):
  50. controllers.append(list[i])
  51. return controllers

调用方式

  1. from mcs3.raidcardutils import RaidCardToolFactory
  2. tool = RaidCardToolFactory().getTool()
  3. disk_type = tool.get_disk_type(disk_name)

总结

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

完整代码地址:https://github.com/tony-yin/RaidCardTool/
如果有所帮助的话,帮忙star一下哦 ^_^

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注