Coverage for drivers/mpath_dmp.py : 89%

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# Copyright (C) Citrix Systems Inc.
2#
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU Lesser General Public License as published
5# by the Free Software Foundation; version 2.1 only.
6#
7# This program is distributed in the hope that it will be useful,
8# but WITHOUT ANY WARRANTY; without even the implied warranty of
9# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10# GNU Lesser General Public License for more details.
11#
12# You should have received a copy of the GNU Lesser General Public License
13# along with this program; if not, write to the Free Software Foundation, Inc.,
14# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16import util
17import xs_errors
18import iscsilib
19import mpath_cli
20import os
21import glob
22import time
23import scsiutil
24import wwid_conf
25import errno
27DMPBIN = "/sbin/multipath"
28DEVMAPPERPATH = "/dev/mapper"
29DEVBYIDPATH = "/dev/disk/by-id"
30DEVBYSCSIPATH = "/dev/disk/by-scsibus"
31SYSFS_PATH = '/sys/class/scsi_host'
32MP_INUSEDIR = "/dev/disk/mpInuse"
35def _is_mpath_daemon_running():
36 cmd = ["/sbin/pidof", "-s", "/sbin/multipathd"]
37 (rc, stdout, stderr) = util.doexec(cmd)
38 return (rc == 0)
41def activate_MPdev(sid, dst):
42 try:
43 os.mkdir(MP_INUSEDIR)
44 except OSError as exc:
45 if exc.errno == errno.EEXIST:
46 pass
47 else:
48 raise
49 path = os.path.join(MP_INUSEDIR, sid)
50 cmd = ['ln', '-sf', dst, path]
51 util.pread2(cmd)
52 util.set_scheduler(os.path.realpath(path))
55def deactivate_MPdev(sid):
56 path = os.path.join(MP_INUSEDIR, sid)
57 if os.path.exists(path): 57 ↛ 58line 57 didn't jump to line 58, because the condition on line 57 was never true
58 os.unlink(path)
61def reset(sid, explicit_unmap=False):
62 util.SMlog("Resetting LUN %s" % sid)
63 _resetDMP(sid, explicit_unmap)
66def _resetDMP(sid, explicit_unmap=False):
67# If mpath has been turned on since the sr/vdi was attached, we
68# might be trying to unmap it before the daemon has been started
69# This is unnecessary (and will fail) so just return.
70 deactivate_MPdev(sid)
71 if not _is_mpath_daemon_running(): 71 ↛ 72line 71 didn't jump to line 72, because the condition on line 71 was never true
72 util.SMlog("Warning: Trying to unmap mpath device when multipathd not running")
73 return
75# If the multipath daemon is running, but we were initially plugged
76# with multipathing set to no, there may be no map for us in the multipath
77# tables. In that case, list_paths will return [], but remove_map might
78# throw an exception. Catch it and ignore it.
79 if explicit_unmap: 79 ↛ 85line 79 didn't jump to line 85, because the condition on line 79 was never false
80 util.retry(lambda: util.pread2(['/usr/sbin/multipath', '-f', sid]),
81 maxretry=3, period=4)
82 util.retry(lambda: util.pread2(['/usr/sbin/multipath', '-W']), maxretry=3,
83 period=4)
84 else:
85 mpath_cli.ensure_map_gone(sid)
87 path = "/dev/mapper/%s" % sid
89 if not util.wait_for_nopath(path, 10): 89 ↛ 90line 89 didn't jump to line 90, because the condition on line 89 was never true
90 util.SMlog("MPATH: WARNING - path did not disappear [%s]" % path)
91 else:
92 util.SMlog("MPATH: path disappeared [%s]" % path)
95def refresh(sid, npaths):
96 # Refresh the multipath status
97 util.SMlog("Refreshing LUN %s" % sid)
98 if len(sid):
99 path = DEVBYIDPATH + "/scsi-" + sid
100 if not os.path.exists(path):
101 scsiutil.rescan(scsiutil._genHostList(""))
102 if not util.wait_for_path(path, 60):
103 raise xs_errors.XenError('MultipathDeviceNotAppeared', path)
104 _refresh_DMP(sid, npaths)
105 else:
106 raise xs_errors.XenError('MultipathDeviceNoScsiid')
109def _is_valid_multipath_device(sid):
111 # Check if device is already multipathed
112 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath', '-l', sid])
113 if not stdout + stderr:
114 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath', '-ll', sid])
115 if not stdout + stderr:
116 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath', '-a', sid])
117 if ret < 0: 117 ↛ 118line 117 didn't jump to line 118, because the condition on line 117 was never true
118 util.SMlog("Failed to add {}: wwid could be explicitly "
119 "blacklisted\n Continue with multipath disabled for "
120 "this SR".format(sid))
121 return False
123 by_scsid_path = "/dev/disk/by-scsid/" + sid
124 if os.path.exists(by_scsid_path):
125 devs = os.listdir(by_scsid_path)
126 else:
127 util.SMlog("Device {} is not ready yet, skipping multipath check"
128 .format(by_scsid_path))
129 return False
130 ret = 1
131 # Some paths might be down, check all associated devices
132 for dev in devs:
133 devpath = os.path.join(by_scsid_path, dev)
134 real_path = util.get_real_path(devpath)
135 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath', '-c',
136 real_path])
137 if ret == 0:
138 break
140 if ret == 1:
141 # This is very fragile but it is not a good sign to fail without
142 # any output. At least until multipath 0.4.9, for example,
143 # multipath -c fails without any log if it is able to retrieve the
144 # wwid of the device.
145 # In this case it is better to fail immediately.
146 if not stdout + stderr:
147 # Attempt to cleanup wwids file before raising
148 try:
149 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath',
150 '-w', sid])
151 except OSError:
152 util.SMlog("Error removing {} from wwids file".format(sid))
153 raise xs_errors.XenError('MultipathGenericFailure',
154 '"multipath -c" failed without any'
155 ' output on {}'.format(real_path))
156 util.SMlog("When dealing with {} multipath status returned:\n "
157 "{}{} Continue with multipath disabled for this SR"
158 .format(sid, stdout, stderr))
159 return False
160 return True
163def _refresh_DMP(sid, npaths):
164 if not _is_valid_multipath_device(sid): 164 ↛ 165line 164 didn't jump to line 165, because the condition on line 164 was never true
165 return
166 path = os.path.join(DEVMAPPERPATH, sid)
167 # If the mapper path doesn't exist force a reload in multipath
168 if not os.path.exists(path):
169 util.retry(lambda: util.pread2(
170 ['/usr/sbin/multipath', '-r', sid]),
171 maxretry=3,
172 period=4)
173 util.wait_for_path(path, 30)
174 if not os.path.exists(path):
175 raise xs_errors.XenError('MultipathMapperPathMissing',
176 'Device mapper path {} not found'.format(
177 path))
178 lvm_path = "/dev/disk/by-scsid/" + sid + "/mapper"
179 util.wait_for_path(lvm_path, 30)
180 activate_MPdev(sid, path)
183def activate():
184 util.SMlog("MPATH: multipath activate called")
186 # If we've got no active sessions, and the deamon is already running,
187 # we're ok to restart the daemon
188 if iscsilib.is_iscsi_daemon_running():
189 if not iscsilib._checkAnyTGT():
190 iscsilib.restart_daemon()
192 if not _is_mpath_daemon_running():
193 util.SMlog("Warning: multipath daemon not running. Starting daemon!")
194 cmd = ["service", "multipathd", "start"]
195 util.pread2(cmd)
197 for i in range(0, 120):
198 if mpath_cli.is_working():
199 util.SMlog("MPATH: dm-multipath activated.")
200 return
201 time.sleep(1)
203 util.SMlog("Failed to communicate with the multipath daemon!")
204 raise xs_errors.XenError('MultipathdCommsFailure')
207def deactivate():
208 util.SMlog("MPATH: multipath deactivate called")
210 if _is_mpath_daemon_running(): 210 ↛ 217line 210 didn't jump to line 217, because the condition on line 210 was never false
211 # Flush the multipath nodes
212 for sid in mpath_cli.list_maps():
213 reset(sid, True)
215 # Check the ISCSI daemon doesn't have any active sessions, if not,
216 # restart in the new mode
217 if iscsilib.is_iscsi_daemon_running() and not iscsilib._checkAnyTGT():
218 iscsilib.restart_daemon()
220 util.SMlog("MPATH: multipath deactivated.")
223def path(SCSIid):
224 if _is_valid_multipath_device(SCSIid) and _is_mpath_daemon_running():
225 path = os.path.join(MP_INUSEDIR, SCSIid)
226 return path
227 else:
228 return DEVBYIDPATH + "/scsi-" + SCSIid