Coverage for drivers/mpathcount.py : 59%

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 os
20import sys
21import re
22import xs_errors
23import mpath_cli
25supported = ['iscsi', 'lvmoiscsi', 'rawhba', 'lvmohba', 'ocfsohba', 'ocfsoiscsi', 'netapp', 'lvmofcoe', 'gfs2']
27LOCK_TYPE_HOST = "host"
28LOCK_NS1 = "mpathcount1"
29LOCK_NS2 = "mpathcount2"
31MAPPER_DIR = "/dev/mapper"
32match_bySCSIid = False
33mpath_enabled = True
34SCSIid = 'NOTSUPPLIED'
36cached_DM_maj = None
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
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)
58def match_host_id(s):
59 regex = re.compile("^INSTALLATION_UUID")
60 return regex.search(s, 0)
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
76def match_dmpLUN(s):
77 regex = re.compile("[0-9]*:[0-9]*:[0-9]*:[0-9]*")
78 return regex.search(s, 0)
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
90def _tostring(l):
91 return str(l)
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)
105def get_root_dev_major():
106 buf = os.stat('/')
107 devno = buf.st_dev
108 return os.major(devno)
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)
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
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)
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)
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)
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
213 # Check root disk if multipathed
214 try:
215 def _remove(key):
216 session.xenapi.host.remove_from_other_config(localhost, key)
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)
225 except:
226 util.SMlog("MPATH: Failure updating Host.other-config:mpath-boot db")
227 mpc_exit(session, -1)
229 try:
230 pbds = session.xenapi.PBD.get_all_records_where("field \"host\" = \"%s\"" % localhost)
231 except:
232 mpc_exit(session, -1)
234 try:
235 for pbd in pbds:
236 def remove(key):
237 session.xenapi.PBD.remove_from_other_config(pbd, key)
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)
254 util.SMlog("MPATH: Update done")
256 mpc_exit(session, 0)