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 

6import time 

7import traceback 

8 

9from . import f_exceptions 

10from . import util 

11from . import mpath_cli 

12 

13SECTOR_SHIFT = 9 

14 

15def match_dm(s): 

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

17 return regex.search(s, 0) 

18 

19 

20def getdev(path): 

21 realpath = os.path.realpath(path) 

22 if match_dm(realpath): 

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

24 else: 

25 newpath = path 

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

27 

28 

29def getsize(path): 

30 dev = getdev(path) 

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

32 size = 0 

33 if os.path.exists(sysfs): 

34 try: 

35 f=open(sysfs, 'r') 

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

37 f.close() 

38 except: 

39 pass 

40 return size 

41 

42 

43def get_vendor(device_path): 

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

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

46 if rc != 0: 

47 raise f_exceptions.XenError( 

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

49 

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

51 if not match: 

52 raise f_exceptions.XenError( 

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

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

55 

56 

57def get_serial(device_path): 

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

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

60 if rc != 0: 

61 raise f_exceptions.XenError( 

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

63 

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

65 if not match: 

66 raise f_exceptions.XenError( 

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

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

69 

70 

71def get_devices_by_SCSIid(SCSIid): 

72 id_path = os.path.join('/dev/disk/by-scsid', SCSIid) 

73 if not os.path.exists(id_path): 

74 util.SMlog(f'SCSId Path not found for {id_path}') 

75 return [] 

76 

77 devices = os.listdir(id_path) 

78 if 'mapper' in devices: 

79 devices.remove('mapper') 

80 return devices 

81 

82 

83def get_device_provisioning_mode(SCSIid): 

84 devices = get_devices_by_SCSIid(SCSIid) 

85 if not devices: 

86 # Device can't be found so assume full 

87 return 'full' 

88 

89 device = devices[0] 

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

91 mode = 'full' 

92 for file in glob.glob(glob_pattern): 

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

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

95 

96 return mode 

97 

98 

99def device_is_thin_provisioned(SCSIid): 

100 mode = get_device_provisioning_mode(SCSIid) 

101 # Kernel modes are 

102 # [SD_LBP_FULL] = "full", 

103 # [SD_LBP_UNMAP] = "unmap", 

104 # [SD_LBP_WS16] = "writesame_16", 

105 # [SD_LBP_WS10] = "writesame_10", 

106 # [SD_LBP_ZERO] = "writesame_zero", 

107 # [SD_LBP_DISABLE] = "disabled" 

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

109 

110 

111def sg_return_check(rc, dbg, logmsg, stderr): 

112 """ 

113 Return true if the return code indicates success, false if it is not 

114 success but is retryable, and raise a util.SMException if it is not 

115 retryable using the logmessage and stderr provided. 

116 

117 In the event that a delay is desirable before a retry, that delay is 

118 baked into this function. 

119 """ 

120 if rc == 0: 

121 return True 

122 if rc == 2: 

123 # This is "device not ready", so sleep and try again 

124 util.SMlog(f"{logmsg}: not ready") 

125 time.sleep(1) 

126 return False 

127 if rc == 6: 

128 # retry without a wait for "unit attention". 

129 util.SMlog(f"{logmsg}: unit attention") 

130 return False 

131 if rc == 11: 

132 # Aborted command. Retryable without delay 

133 util.SMlog(f"{logmsg}: command aborted") 

134 return False 

135 if rc == 14: 

136 # Sense miscompare. Retryable without delay until proven otherwise 

137 util.SMlog(f"{logmsg}: sense miscompare") 

138 return False 

139 if rc == 21: 

140 # An error was recovered. This is a success but we would not normally 

141 # expect to see it. Log if it happens. 

142 util.SMlog(f"{logmsg}: recovered error: {stderr}") 

143 return True 

144 if rc == 33: 

145 # Timed out. Retryable without delay 

146 util.SMlog(f"{logmsg}: timed out") 

147 return False 

148 

149 raise f_exceptions.XenError(dbg, f"{logmsg}: RC={rc}, STDERR={stderr}") 

150 

151 

152def sg_readcap(device): 

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

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

155 attempts = 3 

156 succeeded = False 

157 while attempts > 0: 

158 attempts -= 1 

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

160 if sg_return_check( 

161 rc, "SCSIRead", f"scsiutil.sg_readcap({device})", stderr): 

162 succeeded = True 

163 break 

164 

165 if not succeeded: 

166 raise f_exceptions.XenError( 

167 "SCSIRead", f"scsiutil.sg_readcap({device}): too many failures") 

168 

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

170 if not match: 170 ↛ 171line 170 didn't jump to line 171, because the condition on line 170 was never true

171 raise f_exceptions.XenError("SCSIRead", 

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

173 % (device, stdout)) 

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

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

176 

177 

178def refreshdev(pathlist): 

179 """ 

180 Refresh block devices given a path list 

181 """ 

182 for path in pathlist: 

183 dev = getdev(path) 

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

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

186 if os.path.exists(sysfs): 

187 try: 

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

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

190 os.close(f) 

191 except: 

192 pass 

193 

194 

195def refresh_lun_size_by_SCSIid(SCSIid): 

196 """ 

197 Refresh all devices for the SCSIid. 

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

199 """ 

200 def get_primary_device(SCSIid): 

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

202 if os.path.exists(mapperdevice): 

203 return mapperdevice 

204 else: 

205 devices = get_devices_by_SCSIid(SCSIid) 

206 if devices: 

207 return devices[0] 

208 else: 

209 return None 

210 

211 def get_outdated_size_devices(currentcapacity, devices): 

212 devicesthatneedrefresh = [] 

213 for device in devices: 

214 if getsize(device) != currentcapacity: 

215 devicesthatneedrefresh.append(device) 

216 return devicesthatneedrefresh 

217 

218 def refresh_devices_if_needed(primarydevice, SCSIid, currentcapacity): 

219 devices = get_devices_by_SCSIid(SCSIid) 

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

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

222 devicesthatneedrefresh = get_outdated_size_devices(currentcapacity, 

223 devices) 

224 if devicesthatneedrefresh: 

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

226 # if one or multiple devices don't answer 

227 util.timeout_call(10, refreshdev, devicesthatneedrefresh) 

228 if get_outdated_size_devices(currentcapacity, 

229 devicesthatneedrefresh): 

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

231 raise f_exceptions.XenError("DeviceResize", 

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

233 "current capacity." 

234 % devicesthatneedrefresh) 

235 

236 def refresh_mapper_if_needed(primarydevice, SCSIid, currentcapacity): 

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

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

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

240 mpath_cli.resize_map(SCSIid) 

241 if get_outdated_size_devices(currentcapacity, [primarydevice]): 

242 raise f_exceptions.XenError("DeviceResize", 

243 "Failed to get the mapper dev to " 

244 "agree on the current capacity.") 

245 

246 try: 

247 primarydevice = get_primary_device(SCSIid) 

248 if primarydevice: 

249 currentcapacity = sg_readcap(primarydevice) 

250 refresh_devices_if_needed(primarydevice, SCSIid, currentcapacity) 

251 refresh_mapper_if_needed(primarydevice, SCSIid, currentcapacity) 

252 else: 

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

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

255 return True 

256 except: 

257 util.SMlog("Error in scsiutil.refresh_lun_size_by_SCSIid(%s: %s)" % (SCSIid, traceback.format_exc())) 

258 return False