Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1"""Fake scsiutil module""" 

2 

3import glob 

4import os 

5import re 

6 

7from . import f_exceptions 

8from . import util 

9from . import mpath_cli 

10 

11SECTOR_SHIFT = 9 

12 

13def match_dm(s): 

14 regex = re.compile("mapper/") 

15 return regex.search(s, 0) 

16 

17 

18def getdev(path): 

19 realpath = os.path.realpath(path) 

20 if match_dm(realpath): 

21 newpath = realpath.replace("/dev/mapper/","/dev/disk/by-id/scsi-") 

22 else: 

23 newpath = path 

24 return os.path.realpath(newpath).split('/')[-1] 

25 

26 

27def getsize(path): 

28 dev = getdev(path) 

29 sysfs = os.path.join('/sys/block',dev,'size') 

30 size = 0 

31 if os.path.exists(sysfs): 

32 try: 

33 f=open(sysfs, 'r') 

34 size = (int(f.readline()) << SECTOR_SHIFT) 

35 f.close() 

36 except: 

37 pass 

38 return size 

39 

40 

41def get_vendor(device_path): 

42 vendor_command = ['sginfo', '-M', device_path] 

43 (rc, stdout, _) = util.doexec(vendor_command) 

44 if rc != 0: 

45 raise f_exceptions.XenError( 

46 'SCSIRead', 'sginfo failed {}, {}'.format(device_path, rc)) 

47 

48 match = re.search(r'(^|.*\n)Vendor:\s+(.*)[$|\n]', stdout) 

49 if not match: 

50 raise f_exceptions.XenError( 

51 'SCSIRead', 'sginfo no vendor match {}'.format(device_path)) 

52 return match.group(2).strip() 

53 

54 

55def get_serial(device_path): 

56 serial_command = ['sginfo', '-s', device_path] 

57 (rc, stdout, _) = util.doexec(serial_command) 

58 if rc != 0: 

59 raise f_exceptions.XenError( 

60 'SCSIRead', 'sginfo failed {}, {}'.format(device_path, rc)) 

61 

62 match = re.search(r'(^|.*\n)Serial Number\s+\'([^\']*)\'[$|\n]', stdout) 

63 if not match: 

64 raise f_exceptions.XenError( 

65 'SCSIRead', 'sginfo no serial match {}'.format(device_path)) 

66 return match.group(2).strip() 

67 

68 

69def get_devices_by_SCSIid(SCSIid): 

70 devices = os.listdir(os.path.join('/dev/disk/by-scsid', SCSIid)) 

71 if 'mapper' in devices: 

72 devices.remove('mapper') 

73 return devices 

74 

75 

76def get_device_provisioning_mode(SCSIid): 

77 device = get_devices_by_SCSIid(SCSIid)[0] 

78 glob_pattern = os.path.join('/sys', 'block', device, 'device', 'scsi_disk', '*', 'provisioning_mode') 

79 for file in glob.glob(glob_pattern): 79 ↛ exitline 79 didn't return from function 'get_device_provisioning_mode', because the loop on line 79 didn't complete

80 with open(file, 'r') as device_mode: 

81 mode = device_mode.readline().strip() 

82 return mode 

83 

84 

85def device_is_thin_provisioned(SCSIid): 

86 mode = get_device_provisioning_mode(SCSIid) 

87 # Kernel modes are 

88 # [SD_LBP_FULL] = "full", 

89 # [SD_LBP_UNMAP] = "unmap", 

90 # [SD_LBP_WS16] = "writesame_16", 

91 # [SD_LBP_WS10] = "writesame_10", 

92 # [SD_LBP_ZERO] = "writesame_zero", 

93 # [SD_LBP_DISABLE] = "disabled" 

94 return mode in ['unmap', 'writesame_16', 'writesame_10', 'writesame_zero'] 

95 

96 

97def sg_readcap(device): 

98 device = os.path.join('/dev', getdev(device)) 

99 readcapcommand = ['/usr/bin/sg_readcap', '-b', device] 

100 (rc,stdout,stderr) = util.doexec(readcapcommand) 

101 if rc == 6: 

102 # retry one time for "Capacity data has changed" 

103 (rc,stdout,stderr) = util.doexec(readcapcommand) 

104 if rc != 0: 

105 raise f_exceptions.XenError("SCSIRead", 

106 "scsiutil.sg_readcap(%s) failed" % (device)) 

107 match = re.search('(^|.*\n)(0x[0-9a-fA-F]+) (0x[0-9a-fA-F]+)\n$', stdout) 

108 if not match: 

109 raise f_exceptions.XenError("SCSIRead", 

110 "scsiutil.sg_readcap(%s) failed to parse: %s" 

111 % (device, stdout)) 

112 (blockcount, blocksize) = match.group(2, 3) 

113 return (int(blockcount, 0) * int(blocksize, 0)) 

114 

115 

116def refreshdev(pathlist): 

117 """ 

118 Refresh block devices given a path list 

119 """ 

120 for path in pathlist: 

121 dev = getdev(path) 

122 util.SMlog("Refreshing size for device %s (%s)" % (path, dev)) 

123 sysfs = os.path.join('/sys/block',dev,'device/rescan') 

124 if os.path.exists(sysfs): 

125 try: 

126 f = os.open(sysfs, os.O_WRONLY) 

127 os.write(f, b'1') 

128 os.close(f) 

129 except: 

130 pass 

131 

132 

133def refresh_lun_size_by_SCSIid(SCSIid): 

134 """ 

135 Refresh all devices for the SCSIid. 

136 Returns True if all known devices and the mapper device are up to date. 

137 """ 

138 def get_primary_device(SCSIid): 

139 mapperdevice = os.path.join('/dev/mapper', SCSIid) 

140 if os.path.exists(mapperdevice): 

141 return mapperdevice 

142 else: 

143 devices = get_devices_by_SCSIid(SCSIid) 

144 if devices: 

145 return devices[0] 

146 else: 

147 return None 

148 

149 def get_outdated_size_devices(currentcapacity, devices): 

150 devicesthatneedrefresh = [] 

151 for device in devices: 

152 if getsize(device) != currentcapacity: 

153 devicesthatneedrefresh.append(device) 

154 return devicesthatneedrefresh 

155 

156 def refresh_devices_if_needed(primarydevice, SCSIid, currentcapacity): 

157 devices = get_devices_by_SCSIid(SCSIid) 

158 if "/dev/mapper/" in primarydevice: 

159 devices = set(devices + mpath_cli.list_paths(SCSIid)) 

160 devicesthatneedrefresh = get_outdated_size_devices(currentcapacity, 

161 devices) 

162 if devicesthatneedrefresh: 

163 # timing out avoids waiting for min(dev_loss_tmo, fast_io_fail_tmo) 

164 # if one or multiple devices don't answer 

165 util.timeout_call(10, refreshdev, devicesthatneedrefresh) 

166 if get_outdated_size_devices(currentcapacity, 

167 devicesthatneedrefresh): 

168 # in this state we shouldn't force resizing the mapper dev 

169 raise f_exceptions.XenError("DeviceResize", 

170 "Failed to get %s to agree on the " 

171 "current capacity." 

172 % devicesthatneedrefresh) 

173 

174 def refresh_mapper_if_needed(primarydevice, SCSIid, currentcapacity): 

175 if ("/dev/mapper/" in primarydevice 

176 and get_outdated_size_devices(currentcapacity, [primarydevice])): 

177 util.SMlog("Resizing multpath map for device %s" % SCSIid) 

178 mpath_cli.resize_map(SCSIid) 

179 if get_outdated_size_devices(currentcapacity, [primarydevice]): 

180 raise f_exceptions.XenError("DeviceResize", 

181 "Failed to get the mapper dev to " 

182 "agree on the current capacity.") 

183 

184 try: 

185 primarydevice = get_primary_device(SCSIid) 

186 if primarydevice: 

187 currentcapacity = sg_readcap(primarydevice) 

188 refresh_devices_if_needed(primarydevice, SCSIid, currentcapacity) 

189 refresh_mapper_if_needed(primarydevice, SCSIid, currentcapacity) 

190 else: 

191 util.SMlog("scsiutil.refresh_lun_size_by_SCSIid(%s) could not " 

192 "find any devices for the SCSIid." % SCSIid) 

193 return True 

194 except: 

195 util.SMlog("Error in scsiutil.refresh_lun_size_by_SCSIid(%s)" % SCSIid) 

196 return False