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#!/usr/bin/python3 

2 

3# Copyright (C) Citrix Systems Inc. 

4# 

5# This program is free software; you can redistribute it and/or modify 

6# it under the terms of the GNU Lesser General Public License as published 

7# by the Free Software Foundation; version 2.1 only. 

8# 

9# This program is distributed in the hope that it will be useful, 

10# but WITHOUT ANY WARRANTY; without even the implied warranty of 

11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

12# GNU Lesser General Public License for more details. 

13# 

14# You should have received a copy of the GNU Lesser General Public License 

15# along with this program; if not, write to the Free Software Foundation, Inc., 

16# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 

17 

18import util 

19import time 

20import os 

21import sys 

22import re 

23import xs_errors 

24import lock 

25import mpath_cli 

26import mpp_mpathutil 

27import glob 

28import json 

29 

30supported = ['iscsi', 'lvmoiscsi', 'rawhba', 'lvmohba', 'ocfsohba', 'ocfsoiscsi', 'netapp', 'lvmofcoe', 'gfs2'] 

31 

32LOCK_TYPE_HOST = "host" 

33LOCK_NS1 = "mpathcount1" 

34LOCK_NS2 = "mpathcount2" 

35 

36MAPPER_DIR = "/dev/mapper" 

37mpp_path_update = False 

38match_bySCSIid = False 

39mpath_enabled = True 

40SCSIid = 'NOTSUPPLIED' 

41mpp_entry = 'NOTSUPPLIED' 

42 

43 

44def get_dm_major(): 

45 global cached_DM_maj 

46 if not cached_DM_maj: 

47 try: 

48 line = [x for x in open('/proc/devices').readlines() if x.endswith('device-mapper\n')] 

49 cached_DM_maj = int(line[0].split()[0]) 

50 except: 

51 pass 

52 return cached_DM_maj 

53 

54 

55def mpc_exit(session, code): 

56 if session is not None: 

57 try: 

58 session.xenapi.logout() 

59 except: 

60 pass 

61 sys.exit(code) 

62 

63 

64def match_host_id(s): 

65 regex = re.compile("^INSTALLATION_UUID") 

66 return regex.search(s, 0) 

67 

68 

69def get_localhost_uuid(): 

70 filename = '/etc/xensource-inventory' 

71 try: 

72 f = open(filename, 'r') 

73 except: 

74 raise xs_errors.XenError('EIO', \ 

75 opterr="Unable to open inventory file [%s]" % filename) 

76 domid = '' 

77 for line in filter(match_host_id, f.readlines()): 

78 domid = line.split("'")[1] 

79 return domid 

80 

81 

82def match_dmpLUN(s): 

83 regex = re.compile("[0-9]*:[0-9]*:[0-9]*:[0-9]*") 

84 return regex.search(s, 0) 

85 

86 

87def match_pathup(s): 

88 match = re.match(r'.*\d+:\d+:\d+:\d+\s+\S+\s+\S+\s+\S+\s+(\S+)', s) 

89 if match: 89 ↛ 91line 89 didn't jump to line 91, because the condition on line 89 was never false

90 path_status = match.group(1) 

91 if path_status in ['faulty', 'shaky', 'failed']: 

92 return False 

93 return True 

94 

95 

96def _tostring(l): 

97 return str(l) 

98 

99 

100def get_path_count(SCSIid): 

101 count = 0 

102 total = 0 

103 lines = mpath_cli.get_topology(SCSIid) 

104 for line in filter(match_dmpLUN, lines): 

105 total += 1 

106 if match_pathup(line): 

107 count += 1 

108 return (count, total) 

109 

110 

111def get_root_dev_major(): 

112 buf = os.stat('/') 

113 devno = buf.st_dev 

114 return os.major(devno) 

115 

116 

117# @key: key to update 

118# @SCSIid: SCSI id of multipath map 

119# @entry: string representing previous value 

120# @remove: callback to remove key 

121# @add: callback to add key/value pair 

122def update_config(key, SCSIid, entry, remove, add, mpp_path_update=False): 

123 if mpp_path_update: 

124 remove('multipathed') 

125 remove(key) 

126 remove('MPPEnabled') 

127 add('MPPEnabled', 'true') 

128 add('multipathed', 'true') 

129 add(key, str(entry)) 

130 return 

131 

132 path = os.path.join(MAPPER_DIR, SCSIid) 

133 util.SMlog("MPATH: Updating entry for [%s], current: %s" % (SCSIid, entry)) 

134 if os.path.exists(path): 

135 count, total = get_path_count(SCSIid) 

136 max = 0 

137 if len(entry) != 0: 

138 try: 

139 p = entry.strip('[') 

140 p = p.strip(']') 

141 q = p.split(',') 

142 max = int(q[1]) 

143 except: 

144 pass 

145 if total > max: 145 ↛ 147line 145 didn't jump to line 147, because the condition on line 145 was never false

146 max = total 

147 newentry = [count, max] 

148 if str(newentry) != entry: 148 ↛ exitline 148 didn't return from function 'update_config', because the condition on line 148 was never false

149 remove('multipathed') 

150 remove(key) 

151 add('multipathed', 'true') 

152 add(key, str(newentry)) 

153 util.SMlog("MPATH: Set val: %s" % str(newentry)) 

154 else: 

155 util.SMlog('MPATH: device %s gone' % (SCSIid)) 

156 remove('multipathed') 

157 remove(key) 

158 

159 

160def get_SCSIidlist(devconfig, sm_config): 

161 SCSIidlist = [] 

162 if 'SCSIid' in sm_config: 

163 SCSIidlist = sm_config['SCSIid'].split(',') 

164 elif 'SCSIid' in devconfig: 

165 SCSIidlist.append(devconfig['SCSIid']) 

166 elif 'provider' in devconfig: 

167 SCSIidlist.append(devconfig['ScsiId']) 

168 else: 

169 for key in sm_config: 169 ↛ 170line 169 didn't jump to line 170, because the loop on line 169 never started

170 if util._isSCSIid(key): 

171 SCSIidlist.append(re.sub("^scsi-", "", key)) 

172 return SCSIidlist 

173 

174 

175def check_root_disk(config, maps, remove, add): 

176 if get_root_dev_major() == get_dm_major(): 

177 # Ensure output headers are not in the list 

178 if 'name' in maps: 

179 maps.remove('name') 

180 # first map will always correspond to the root dev, dm-0 

181 assert(len(maps) > 0) 

182 i = maps[0] 

183 if (not match_bySCSIid) or i == SCSIid: 183 ↛ exitline 183 didn't return from function 'check_root_disk', because the condition on line 183 was never false

184 util.SMlog("Matched SCSIid %s, updating " \ 

185 " Host.other-config:mpath-boot " % i) 

186 key = "mpath-boot" 

187 if key not in config: 

188 update_config(key, i, "", remove, add) 

189 else: 

190 update_config(key, i, config[key], remove, add) 

191 

192 

193def check_devconfig(devconfig, sm_config, config, remove, add): 

194 SCSIidlist = get_SCSIidlist(devconfig, sm_config) 

195 if not len(SCSIidlist): 

196 return 

197 for i in SCSIidlist: 

198 if match_bySCSIid and i != SCSIid: 198 ↛ 199line 198 didn't jump to line 199, because the condition on line 198 was never true

199 continue 

200 util.SMlog("Matched SCSIid, updating %s" % i) 

201 key = "mpath-" + i 

202 if not mpath_enabled: 

203 remove(key) 

204 remove('multipathed') 

205 elif mpp_path_update: 205 ↛ 206line 205 didn't jump to line 206, because the condition on line 205 was never true

206 util.SMlog("Matched SCSIid, updating entry %s" % str(mpp_entry)) 

207 update_config(key, i, mpp_entry, remove, add, mpp_path_update) 

208 else: 

209 if key not in config: 

210 update_config(key, i, "", remove, add) 

211 else: 

212 update_config(key, i, config[key], remove, add) 

213 

214if __name__ == '__main__': 214 ↛ 215line 214 didn't jump to line 215, because the condition on line 214 was never true

215 if len(sys.argv) == 3: 

216 match_bySCSIid = True 

217 SCSIid = sys.argv[1] 

218 mpp_path_update = True 

219 mpp_entry = sys.argv[2] 

220 

221 cached_DM_maj = None 

222 

223 try: 

224 session = util.get_localAPI_session() 

225 except: 

226 print("Unable to open local XAPI session") 

227 sys.exit(-1) 

228 

229 localhost = session.xenapi.host.get_by_uuid(get_localhost_uuid()) 

230 # Check whether multipathing is enabled (either for root dev or SRs) 

231 try: 

232 if get_root_dev_major() != get_dm_major(): 

233 hconf = session.xenapi.host.get_other_config(localhost) 

234 assert(hconf['multipathing'] == 'true') 

235 mpath_enabled = True 

236 except: 

237 mpath_enabled = False 

238 

239 # Check root disk if multipathed 

240 try: 

241 def _remove(key): 

242 session.xenapi.host.remove_from_other_config(localhost, key) 

243 

244 

245 def _add(key, val): 

246 session.xenapi.host.add_to_other_config(localhost, key, val) 

247 config = session.xenapi.host.get_other_config(localhost) 

248 maps = mpath_cli.list_maps() 

249 check_root_disk(config, maps, _remove, _add) 

250 

251 except: 

252 util.SMlog("MPATH: Failure updating Host.other-config:mpath-boot db") 

253 mpc_exit(session, -1) 

254 

255 try: 

256 pbds = session.xenapi.PBD.get_all_records_where("field \"host\" = \"%s\"" % localhost) 

257 except: 

258 mpc_exit(session, -1) 

259 

260 try: 

261 for pbd in pbds: 

262 def remove(key): 

263 session.xenapi.PBD.remove_from_other_config(pbd, key) 

264 

265 

266 def add(key, val): 

267 session.xenapi.PBD.add_to_other_config(pbd, key, val) 

268 record = pbds[pbd] 

269 config = record['other_config'] 

270 SR = record['SR'] 

271 srtype = session.xenapi.SR.get_type(SR) 

272 if srtype in supported: 

273 devconfig = record["device_config"] 

274 sm_config = session.xenapi.SR.get_sm_config(SR) 

275 check_devconfig(devconfig, sm_config, config, remove, add) 

276 except: 

277 util.SMlog("MPATH: Failure updating db. %s" % sys.exc_info()) 

278 mpc_exit(session, -1) 

279 

280 util.SMlog("MPATH: Update done") 

281 

282 mpc_exit(session, 0)