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 os 

20import sys 

21import re 

22import xs_errors 

23import mpath_cli 

24 

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

26 

27LOCK_TYPE_HOST = "host" 

28LOCK_NS1 = "mpathcount1" 

29LOCK_NS2 = "mpathcount2" 

30 

31MAPPER_DIR = "/dev/mapper" 

32match_bySCSIid = False 

33mpath_enabled = True 

34SCSIid = 'NOTSUPPLIED' 

35 

36cached_DM_maj = None 

37 

38def get_dm_major(): 

39 global cached_DM_maj 

40 if not cached_DM_maj: 

41 try: 

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

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

44 except: 

45 pass 

46 return cached_DM_maj 

47 

48 

49def mpc_exit(session, code): 

50 if session is not None: 

51 try: 

52 session.xenapi.logout() 

53 except: 

54 pass 

55 sys.exit(code) 

56 

57 

58def match_host_id(s): 

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

60 return regex.search(s, 0) 

61 

62 

63def get_localhost_uuid(): 

64 filename = '/etc/xensource-inventory' 

65 try: 

66 f = open(filename, 'r') 

67 except: 

68 raise xs_errors.XenError('EIO', \ 

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

70 domid = '' 

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

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

73 return domid 

74 

75 

76def match_dmpLUN(s): 

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

78 return regex.search(s, 0) 

79 

80 

81def match_pathup(s): 

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

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

84 path_status = match.group(1) 

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

86 return False 

87 return True 

88 

89 

90def _tostring(l): 

91 return str(l) 

92 

93 

94def get_path_count(SCSIid): 

95 count = 0 

96 total = 0 

97 lines = mpath_cli.get_topology(SCSIid) 

98 for line in filter(match_dmpLUN, lines): 

99 total += 1 

100 if match_pathup(line): 

101 count += 1 

102 return (count, total) 

103 

104 

105def get_root_dev_major(): 

106 buf = os.stat('/') 

107 devno = buf.st_dev 

108 return os.major(devno) 

109 

110 

111# @key: key to update 

112# @SCSIid: SCSI id of multipath map 

113# @entry: string representing previous value 

114# @remove: callback to remove key 

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

116def update_config(key, SCSIid, entry, remove, add): 

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

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

119 if os.path.exists(path): 

120 count, total = get_path_count(SCSIid) 

121 max = 0 

122 if len(entry) != 0: 

123 try: 

124 p = entry.strip('[') 

125 p = p.strip(']') 

126 q = p.split(',') 

127 max = int(q[1]) 

128 except: 

129 pass 

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

131 max = total 

132 newentry = [count, max] 

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

134 remove('multipathed') 

135 remove(key) 

136 add('multipathed', 'true') 

137 add(key, str(newentry)) 

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

139 else: 

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

141 remove('multipathed') 

142 remove(key) 

143 

144 

145def get_SCSIidlist(devconfig, sm_config): 

146 SCSIidlist = [] 

147 if 'SCSIid' in sm_config: 

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

149 elif 'SCSIid' in devconfig: 

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

151 elif 'provider' in devconfig: 

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

153 else: 

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

155 if util._isSCSIid(key): 

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

157 return SCSIidlist 

158 

159 

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

161 if get_root_dev_major() == get_dm_major(): 

162 # Ensure output headers are not in the list 

163 if 'name' in maps: 

164 maps.remove('name') 

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

166 assert(len(maps) > 0) 

167 i = maps[0] 

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

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

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

171 key = "mpath-boot" 

172 if key not in config: 

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

174 else: 

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

176 

177 

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

179 SCSIidlist = get_SCSIidlist(devconfig, sm_config) 

180 if not len(SCSIidlist): 

181 return 

182 for i in SCSIidlist: 

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

184 continue 

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

186 key = "mpath-" + i 

187 if not mpath_enabled: 

188 remove(key) 

189 remove('multipathed') 

190 else: 

191 if key not in config: 

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

193 else: 

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

195 

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

197 try: 

198 session = util.get_localAPI_session() 

199 except: 

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

201 sys.exit(-1) 

202 

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

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

205 try: 

206 if get_root_dev_major() != get_dm_major(): 

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

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

209 mpath_enabled = True 

210 except: 

211 mpath_enabled = False 

212 

213 # Check root disk if multipathed 

214 try: 

215 def _remove(key): 

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

217 

218 

219 def _add(key, val): 

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

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

222 maps = mpath_cli.list_maps() 

223 check_root_disk(config, maps, _remove, _add) 

224 

225 except: 

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

227 mpc_exit(session, -1) 

228 

229 try: 

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

231 except: 

232 mpc_exit(session, -1) 

233 

234 try: 

235 for pbd in pbds: 

236 def remove(key): 

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

238 

239 

240 def add(key, val): 

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

242 record = pbds[pbd] 

243 config = record['other_config'] 

244 SR = record['SR'] 

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

246 if srtype in supported: 

247 devconfig = record["device_config"] 

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

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

250 except: 

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

252 mpc_exit(session, -1) 

253 

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

255 

256 mpc_exit(session, 0)