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