Coverage for drivers/mpathcount.py : 62%

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
18from sm_typing import Dict
20import util
21import os
22import sys
23import re
24import xs_errors
25import mpath_cli
26import json
28supported = ['iscsi', 'lvmoiscsi', 'rawhba', 'lvmohba', 'ocfsohba', 'ocfsoiscsi', 'netapp', 'lvmofcoe', 'gfs2']
30LOCK_TYPE_HOST = "host"
31LOCK_NS1 = "mpathcount1"
32LOCK_NS2 = "mpathcount2"
34MAPPER_DIR = "/dev/mapper"
35MPATHS_DIR = "/dev/shm"
36MPATH_FILE_NAME = "/dev/shm/mpath_status"
37match_bySCSIid = False
38mpath_enabled = True
39SCSIid = 'NOTSUPPLIED'
41cached_DM_maj = None
43def get_dm_major():
44 global cached_DM_maj
45 if not cached_DM_maj:
46 try:
47 line = [x for x in open('/proc/devices').readlines() if x.endswith('device-mapper\n')]
48 cached_DM_maj = int(line[0].split()[0])
49 except:
50 pass
51 return cached_DM_maj
54def mpc_exit(session, code):
55 if session is not None:
56 try:
57 session.xenapi.session.logout()
58 except:
59 pass
60 sys.exit(code)
63def match_host_id(s):
64 regex = re.compile("^INSTALLATION_UUID")
65 return regex.search(s, 0)
68def get_localhost_uuid():
69 filename = '/etc/xensource-inventory'
70 try:
71 f = open(filename, 'r')
72 except:
73 raise xs_errors.XenError('EIO', \
74 opterr="Unable to open inventory file [%s]" % filename)
75 domid = ''
76 for line in filter(match_host_id, f.readlines()):
77 domid = line.split("'")[1]
78 return domid
81def match_dmpLUN(s):
82 regex = re.compile("[0-9]*:[0-9]*:[0-9]*:[0-9]*")
83 return regex.search(s, 0)
86def match_pathup(s):
87 match = re.match(r'.*\d+:\d+:\d+:\d+\s+\S+\s+\S+\s+\S+\s+(\S+)', s)
88 if match: 88 ↛ 90line 88 didn't jump to line 90, because the condition on line 88 was never false
89 path_status = match.group(1)
90 if path_status in ['faulty', 'shaky', 'failed']:
91 return False
92 return True
95def _tostring(l):
96 return str(l)
99def get_path_count(SCSIid):
100 count = 0
101 total = 0
102 lines = mpath_cli.get_topology(SCSIid)
103 for line in filter(match_dmpLUN, lines):
104 total += 1
105 if match_pathup(line):
106 count += 1
107 return (count, total)
110def get_root_dev_major():
111 buf = os.stat('/')
112 devno = buf.st_dev
113 return os.major(devno)
116# @key: key to update
117# @SCSIid: SCSI id of multipath map
118# @entry: string representing previous value
119# @remove: callback to remove key
120# @add: callback to add key/value pair
121# @mpath_status: map to record multipath status
122def update_config(key, SCSIid, entry, remove, add, mpath_status=None):
123 path = os.path.join(MAPPER_DIR, SCSIid)
124 util.SMlog("MPATH: Updating entry for [%s], current: %s" % (SCSIid, entry))
125 if os.path.exists(path):
126 count, total = get_path_count(SCSIid)
127 max = 0
128 if len(entry) != 0:
129 try:
130 p = entry.strip('[')
131 p = p.strip(']')
132 q = p.split(',')
133 max = int(q[1])
134 except:
135 pass
136 if total > max: 136 ↛ 138line 136 didn't jump to line 138, because the condition on line 136 was never false
137 max = total
138 newentry = [count, max]
139 if str(newentry) != entry: 139 ↛ 145line 139 didn't jump to line 145, because the condition on line 139 was never false
140 remove('multipathed')
141 remove(key)
142 add('multipathed', 'true')
143 add(key, str(newentry))
144 util.SMlog("MPATH: Set val: %s" % str(newentry))
145 if mpath_status != None: 145 ↛ 146line 145 didn't jump to line 146, because the condition on line 145 was never true
146 mpath_status.update({str(key): f"{count}/{max}"})
147 else:
148 util.SMlog('MPATH: device %s gone' % (SCSIid))
149 remove('multipathed')
150 remove(key)
153def get_SCSIidlist(devconfig, sm_config):
154 SCSIidlist = []
155 if 'SCSIid' in sm_config:
156 SCSIidlist = sm_config['SCSIid'].split(',')
157 elif 'SCSIid' in devconfig:
158 SCSIidlist.append(devconfig['SCSIid'])
159 elif 'provider' in devconfig:
160 SCSIidlist.append(devconfig['ScsiId'])
161 else:
162 for key in sm_config: 162 ↛ 163line 162 didn't jump to line 163, because the loop on line 162 never started
163 if util._isSCSIid(key):
164 SCSIidlist.append(re.sub("^scsi-", "", key))
165 return SCSIidlist
168def check_root_disk(config, maps, remove, add):
169 if get_root_dev_major() == get_dm_major():
170 # Ensure output headers are not in the list
171 if 'name' in maps:
172 maps.remove('name')
173 # first map will always correspond to the root dev, dm-0
174 assert(len(maps) > 0)
175 i = maps[0]
176 if (not match_bySCSIid) or i == SCSIid: 176 ↛ exitline 176 didn't return from function 'check_root_disk', because the condition on line 176 was never false
177 util.SMlog("Matched SCSIid %s, updating " \
178 " Host.other-config:mpath-boot " % i)
179 key = "mpath-boot"
180 if key not in config:
181 update_config(key, i, "", remove, add)
182 else:
183 update_config(key, i, config[key], remove, add)
186def check_devconfig(devconfig, sm_config, config, remove, add, mpath_status=None):
187 SCSIidlist = get_SCSIidlist(devconfig, sm_config)
188 if not len(SCSIidlist):
189 return
190 for i in SCSIidlist:
191 if match_bySCSIid and i != SCSIid: 191 ↛ 192line 191 didn't jump to line 192, because the condition on line 191 was never true
192 continue
193 util.SMlog("Matched SCSIid, updating %s" % i)
194 key = "mpath-" + i
195 if not mpath_enabled:
196 remove(key)
197 remove('multipathed')
198 else:
199 if key not in config:
200 update_config(key, i, "", remove, add, mpath_status)
201 else:
202 update_config(key, i, config[key], remove, add, mpath_status)
204if __name__ == '__main__': 204 ↛ 205line 204 didn't jump to line 205, because the condition on line 204 was never true
205 try:
206 session = util.get_localAPI_session()
207 except:
208 print("Unable to open local XAPI session")
209 sys.exit(-1)
211 localhost = session.xenapi.host.get_by_uuid(get_localhost_uuid())
212 # Check whether multipathing is enabled (either for root dev or SRs)
213 try:
214 if get_root_dev_major() != get_dm_major():
215 hconf = session.xenapi.host.get_other_config(localhost)
216 assert(hconf['multipathing'] == 'true')
217 mpath_enabled = True
218 except:
219 mpath_enabled = False
221 # Check root disk if multipathed
222 try:
223 def _remove(key):
224 session.xenapi.host.remove_from_other_config(localhost, key)
227 def _add(key, val):
228 session.xenapi.host.add_to_other_config(localhost, key, val)
229 config = session.xenapi.host.get_other_config(localhost)
230 maps = mpath_cli.list_maps()
231 check_root_disk(config, maps, _remove, _add)
233 except:
234 util.SMlog("MPATH: Failure updating Host.other-config:mpath-boot db")
235 mpc_exit(session, -1)
237 try:
238 pbds = session.xenapi.PBD.get_all_records_where("field \"host\" = \"%s\"" % localhost)
239 except:
240 mpc_exit(session, -1)
242 try:
243 mpath_status: Dict[str, str] = {}
244 for pbd in pbds:
245 def remove(key):
246 session.xenapi.PBD.remove_from_other_config(pbd, key)
249 def add(key, val):
250 session.xenapi.PBD.add_to_other_config(pbd, key, val)
251 record = pbds[pbd]
252 config = record['other_config']
253 SR = record['SR']
254 srtype = session.xenapi.SR.get_type(SR)
255 if srtype in supported:
256 devconfig = record["device_config"]
257 sm_config = session.xenapi.SR.get_sm_config(SR)
258 check_devconfig(devconfig, sm_config, config, remove, add, mpath_status)
259 mpath_status = mpath_status if mpath_enabled else {}
260 util.atomicFileWrite(MPATH_FILE_NAME, MPATHS_DIR, json.dumps(mpath_status))
261 os.chmod(MPATH_FILE_NAME, 0o0644)
262 except:
263 util.SMlog("MPATH: Failure updating db. %s" % str(sys.exc_info()))
264 mpc_exit(session, -1)
266 util.SMlog("MPATH: Update done")
268 mpc_exit(session, 0)