Coverage for drivers/mpath_dmp.py : 91%

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"
31DEVBYMPPPATH = "/dev/disk/by-mpp"
32SYSFS_PATH = '/sys/class/scsi_host'
33MP_INUSEDIR = "/dev/disk/mpInuse"
35MPPGETAIDLNOBIN = "/opt/xensource/bin/xe-get-arrayid-lunnum"
38def _is_mpath_daemon_running():
39 cmd = ["/sbin/pidof", "-s", "/sbin/multipathd"]
40 (rc, stdout, stderr) = util.doexec(cmd)
41 return (rc == 0)
44def activate_MPdev(sid, dst):
45 try:
46 os.mkdir(MP_INUSEDIR)
47 except OSError as exc:
48 if exc.errno == errno.EEXIST:
49 pass
50 else:
51 raise
52 path = os.path.join(MP_INUSEDIR, sid)
53 cmd = ['ln', '-sf', dst, path]
54 util.pread2(cmd)
57def deactivate_MPdev(sid):
58 path = os.path.join(MP_INUSEDIR, sid)
59 if os.path.exists(path): 59 ↛ 60line 59 didn't jump to line 60, because the condition on line 59 was never true
60 os.unlink(path)
63def reset(sid, explicit_unmap=False):
64 util.SMlog("Resetting LUN %s" % sid)
65 _resetDMP(sid, explicit_unmap)
68def _resetDMP(sid, explicit_unmap=False):
69# If mpath has been turned on since the sr/vdi was attached, we
70# might be trying to unmap it before the daemon has been started
71# This is unnecessary (and will fail) so just return.
72 deactivate_MPdev(sid)
73 if not _is_mpath_daemon_running(): 73 ↛ 74line 73 didn't jump to line 74, because the condition on line 73 was never true
74 util.SMlog("Warning: Trying to unmap mpath device when multipathd not running")
75 return
77# If the multipath daemon is running, but we were initially plugged
78# with multipathing set to no, there may be no map for us in the multipath
79# tables. In that case, list_paths will return [], but remove_map might
80# throw an exception. Catch it and ignore it.
81 if explicit_unmap:
82 util.retry(lambda: util.pread2(['/usr/sbin/multipath', '-f', sid]),
83 maxretry=3, period=4)
84 util.retry(lambda: util.pread2(['/usr/sbin/multipath', '-W']), maxretry=3,
85 period=4)
86 else:
87 mpath_cli.ensure_map_gone(sid)
89 path = "/dev/mapper/%s" % sid
91 if not util.wait_for_nopath(path, 10): 91 ↛ 92line 91 didn't jump to line 92, because the condition on line 91 was never true
92 util.SMlog("MPATH: WARNING - path did not disappear [%s]" % path)
93 else:
94 util.SMlog("MPATH: path disappeared [%s]" % path)
97def refresh(sid, npaths):
98 # Refresh the multipath status
99 util.SMlog("Refreshing LUN %s" % sid)
100 if len(sid):
101 path = DEVBYIDPATH + "/scsi-" + sid
102 if not os.path.exists(path):
103 scsiutil.rescan(scsiutil._genHostList(""))
104 if not util.wait_for_path(path, 60):
105 raise xs_errors.XenError('MultipathDeviceNotAppeared', path)
106 _refresh_DMP(sid, npaths)
107 else:
108 raise xs_errors.XenError('MultipathDeviceNoScsiid')
111def _is_valid_multipath_device(sid):
113 # Check if device is already multipathed
114 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath', '-l', sid])
115 if not stdout + stderr:
116 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath', '-ll', sid])
117 if not stdout + stderr:
118 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath', '-a', sid])
119 if ret < 0: 119 ↛ 120line 119 didn't jump to line 120, because the condition on line 119 was never true
120 util.SMlog("Failed to add {}: wwid could be explicitly "
121 "blacklisted\n Continue with multipath disabled for "
122 "this SR".format(sid))
123 return False
125 by_scsid_path = "/dev/disk/by-scsid/" + sid
126 if os.path.exists(by_scsid_path):
127 devs = os.listdir(by_scsid_path)
128 else:
129 util.SMlog("Device {} is not ready yet, skipping multipath check"
130 .format(by_scsid_path))
131 return False
132 ret = 1
133 # Some paths might be down, check all associated devices
134 for dev in devs:
135 devpath = os.path.join(by_scsid_path, dev)
136 real_path = util.get_real_path(devpath)
137 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath', '-c',
138 real_path])
139 if ret == 0:
140 break
142 if ret == 1:
143 # This is very fragile but it is not a good sign to fail without
144 # any output. At least until multipath 0.4.9, for example,
145 # multipath -c fails without any log if it is able to retrieve the
146 # wwid of the device.
147 # In this case it is better to fail immediately.
148 if not stdout + stderr:
149 # Attempt to cleanup wwids file before raising
150 try:
151 (ret, stdout, stderr) = util.doexec(['/usr/sbin/multipath',
152 '-w', sid])
153 except OSError:
154 util.SMlog("Error removing {} from wwids file".format(sid))
155 raise xs_errors.XenError('MultipathGenericFailure',
156 '"multipath -c" failed without any'
157 ' output on {}'.format(real_path))
158 util.SMlog("When dealing with {} multipath status returned:\n "
159 "{}{} Continue with multipath disabled for this SR"
160 .format(sid, stdout, stderr))
161 return False
162 return True
165def _refresh_DMP(sid, npaths):
166 if not _is_valid_multipath_device(sid): 166 ↛ 167line 166 didn't jump to line 167, because the condition on line 166 was never true
167 return
168 path = os.path.join(DEVMAPPERPATH, sid)
169 # If the mapper path doesn't exist force a reload in multipath
170 if not os.path.exists(path):
171 util.retry(lambda: util.pread2(
172 ['/usr/sbin/multipath', '-r', sid]),
173 maxretry=3,
174 period=4)
175 util.wait_for_path(path, 10)
176 if not os.path.exists(path):
177 raise xs_errors.XenError('MultipathMapperPathMissing',
178 'Device mapper path {} not found'.format(
179 path))
180 lvm_path = "/dev/disk/by-scsid/" + sid + "/mapper"
181 util.wait_for_path(lvm_path, 10)
182 activate_MPdev(sid, path)
185def activate():
186 util.SMlog("MPATH: multipath activate called")
188 # If we've got no active sessions, and the deamon is already running,
189 # we're ok to restart the daemon
190 if iscsilib.is_iscsi_daemon_running():
191 if not iscsilib._checkAnyTGT():
192 iscsilib.restart_daemon()
194 if not _is_mpath_daemon_running():
195 util.SMlog("Warning: multipath daemon not running. Starting daemon!")
196 cmd = ["service", "multipathd", "start"]
197 util.pread2(cmd)
199 for i in range(0, 120):
200 if mpath_cli.is_working():
201 util.SMlog("MPATH: dm-multipath activated.")
202 return
203 time.sleep(1)
205 util.SMlog("Failed to communicate with the multipath daemon!")
206 raise xs_errors.XenError('MultipathdCommsFailure')
209def deactivate():
210 util.SMlog("MPATH: multipath deactivate called")
212 if _is_mpath_daemon_running(): 212 ↛ 218line 212 didn't jump to line 218, because the condition on line 212 was never false
213 # Flush the multipath nodes
214 for sid in mpath_cli.list_maps():
215 reset(sid, True)
217 # Disable any active MPP LUN maps (except the root dev)
218 systemroot = os.path.realpath(util.getrootdev())
219 for dev in glob.glob(DEVBYMPPPATH + "/*"):
220 if os.path.realpath(dev) != systemroot:
221 sid = os.path.basename(dev).split('-')[0]
222 reset(sid)
223 else:
224 util.SMlog("MPP: Found root dev node, not resetting")
226 # Check the ISCSI daemon doesn't have any active sessions, if not,
227 # restart in the new mode
228 if iscsilib.is_iscsi_daemon_running() and not iscsilib._checkAnyTGT():
229 iscsilib.restart_daemon()
231 util.SMlog("MPATH: multipath deactivated.")
234def path(SCSIid):
235 if _is_valid_multipath_device(SCSIid) and _is_mpath_daemon_running():
236 path = os.path.join(MP_INUSEDIR, SCSIid)
237 return path
238 else:
239 return DEVBYIDPATH + "/scsi-" + SCSIid