简介
在上次的文章 Multipath 在 OpenStack 中的 faulty device 的成因及解决 (part 1) 中, 我详细解释了 fault device 的成因, 这篇文章重点介绍下 os-brick 中是如何在并发的情况下, 通过哪些具体的实现避免了 faluty device 的形成.
在讲具体实现前, 有必要提到 Linux 上 SCSI Block device(块设备)地址 (寻址) 的一些细节.
Linux kernel 中通过如下的层次来定位特定的 LUN:
- SCSI adapter number [host]
- channel number [bus]
- id number [target]
- lun [lun]
更多细节可以参考[SCSI Addressing http://www.tldp.org/HOWTO/SCSI-2.4-HOWTO/scsiaddr.html ], 也就是说, 一个 LUN 可以用 [host-bus(channel)-target-lun] 来表示.
Linux 每连接一个 iscsi target,kernel 都会在本地的 /sys/class/iscsi_host/host*/device/session 建立对应的目录结构, 用来表示一个 SCSI 的设备.
$ ls -l /sys/class/iscsi_host/host3/device/session1/
- total 0
- drwxr-xr-x 4 root root 0 Apr 21 21:54 connection1:0
- drwxr-xr-x 3 root root 0 Apr 21 21:54 iscsi_session
- drwxr-xr-x 2 root root 0 Apr 21 21:55 power
- drwxr-xr-x 5 root root 0 Apr 21 21:54 target3:0:0
- -rw-r--r-- 1 root root 4096 Apr 21 21:54 uevent
上面的 3:0:0 就是一个 iSCSI target 所在 host:channel:target
BTW: 如果你看不到如上的目录结构, 你应该先要连接一个 iSCSI target, 下面是我连接的 target:
$ sudo iscsiadm -m session
tcp: [1] 172.17.0.2:3260,1 tgt1 (non-flash)
方案
由于在上篇已经介绍过, os-brick 使用的是连接 (connect_volume) 和断开 (disconnect_volume) 的时候, 分别使用了 multipath -r 和 iscsiadm -m session -R
以上的命令会造成所有的 iSCSI target 对应的 BUS 的所有 LUN 都会被扫描一遍.
os-brick 就对症下药, 根据用户要连接的 target 和 LUN, 缩小扫描范围, 只扫描特定 target 上的特定 LUN.
具体的过程如下:
1. 首先根据用户的输入的 session id 和 LUN id 找到对应的 h-c-t-l(代码 LINK https://github.com/openstack/os-brick/blob/f11695cf0dbfe9bc473915dd03dc9190a7494afd/os_brick/initiator/linuxscsi.py#L549 ):
- def get_hctl(self, session, lun):
- """Given an iSCSI session return the host, channel, target, and lun."""
- glob_str = '/sys/class/iscsi_host/host*/device/session' + session
- paths = glob.glob(glob_str + '/target*')
- if paths:
- __, channel, target = os.path.split(paths[0])[1].split(':')
- # Check if we can get the host
- else:
- target = channel = '-'
- paths = glob.glob(glob_str)
- if not paths:
- LOG.debug('No hctl found on session %s with lun %s', session, lun)
- return None
- # Extract the host number from the path
- host = paths[0][26:paths[0].index('/', 26)]
- res = (host, channel, target, lun)
- LOG.debug('HCTL %s found on session %s with lun %s', res, session, lun)
- return res
上面的参数 session 就是 tcp: [1] 172.17.0.2:3260,1 tgt1 (non-flash) 中的[1],lun 就是要连接的 LUN 的 ID, 一般由 Cinder driver 提供.
对于我的这个 session,LUN=1 对应的 hctl 为:
HCTL ('3', '0', '0', 1) found on session 1 with lun 1
2. 扫描时使用上面的 htcl:(代码 link https://github.com/openstack/os-brick/blob/f11695cf0dbfe9bc473915dd03dc9190a7494afd/os_brick/initiator/linuxscsi.py#L590 )
- def scan_iscsi(self, host, channel='-', target='-', lun='-'):
- """Send an iSCSI scan request given the host and optionally the ctl."""
- LOG.debug('Scanning host %(host)s c: %(channel)s,'
- 't: %(target)s, l: %(lun)s)',
- {'host': host, 'channel': channel,
- 'target': target, 'lun': lun})
- self.echo_scsi_command('/sys/class/scsi_host/host%s/scan' % host,
- '%(c)s %(t)s %(l)s' % {'c': channel,
- 't': target,
- 'l': lun})
在 log 里面会看到类似的 tee 开头的 scsi command, 作用跟 echo '0 0 1' | tee -a /sys/class/scsi_host/host3/scan 一样, 让 kernel 做一个小范围的 host scan.
这样只有用户想要的一个 LUN 会被 scan 出来, 而无关的 LUN 是不会被扫描出来, 从而避免了 fault device 的形成.
参考资料
- [SCSI Addressing]: http://www.tldp.org/HOWTO/SCSI-2.4-HOWTO/scsiaddr.html
- [os-brick]: https://github.com/openstack/os-brick/
- [Refactor iSCSI connect]: https://github.com/openstack/os-brick/commit/56c8665d3d342ce90f5d9433966c0f244063b4c1
来源: https://www.cnblogs.com/sting2me/p/8888420.html