Coverage for drivers/mpathcount.py : 60%

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
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
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
30supported = ['iscsi', 'lvmoiscsi', 'rawhba', 'lvmohba', 'ocfsohba', 'ocfsoiscsi', 'netapp', 'lvmofcoe', 'gfs2']
32LOCK_TYPE_HOST = "host"
33LOCK_NS1 = "mpathcount1"
34LOCK_NS2 = "mpathcount2"
36MAPPER_DIR = "/dev/mapper"
37mpp_path_update = False
38match_bySCSIid = False
39mpath_enabled = True
40SCSIid = 'NOTSUPPLIED'
41mpp_entry = 'NOTSUPPLIED'
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
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)
64def match_host_id(s):
65 regex = re.compile("^INSTALLATION_UUID")
66 return regex.search(s, 0)
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
82def match_dmpLUN(s):
83 regex = re.compile("[0-9]*:[0-9]*:[0-9]*:[0-9]*")
84 return regex.search(s, 0)
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
96def _tostring(l):
97 return str(l)
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)
111def get_root_dev_major():
112 buf = os.stat('/')
113 devno = buf.st_dev
114 return os.major(devno)
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
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)
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
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)
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)
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]
221 cached_DM_maj = None
223 try:
224 session = util.get_localAPI_session()
225 except:
226 print("Unable to open local XAPI session")
227 sys.exit(-1)
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
239 # Check root disk if multipathed
240 try:
241 def _remove(key):
242 session.xenapi.host.remove_from_other_config(localhost, key)
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)
251 except:
252 util.SMlog("MPATH: Failure updating Host.other-config:mpath-boot db")
253 mpc_exit(session, -1)
255 try:
256 pbds = session.xenapi.PBD.get_all_records_where("field \"host\" = \"%s\"" % localhost)
257 except:
258 mpc_exit(session, -1)
260 try:
261 for pbd in pbds:
262 def remove(key):
263 session.xenapi.PBD.remove_from_other_config(pbd, key)
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)
280 util.SMlog("MPATH: Update done")
282 mpc_exit(session, 0)