Coverage for drivers/wwid_conf.py : 14%

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
2#
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
17#
18# Manipulation utilities for multipath.conf
19#
21import fileinput
22import shutil
23import sys
24import re
25import util
26import lock
28LOCK_TYPE_HOST = "host"
29LOCK_NS = "multipath.conf"
30# We always expect this file.
31# It must be a regular file pointed by /etc/multipath.conf
32CONF_FILE = "/etc/multipath.xenserver/multipath.conf"
33BELIST_TAG = "blacklist_exceptions"
36class WWIDException(Exception):
37 def __init__(self, errstr):
38 self.errstr = errstr
41def edit_wwid(wwid, remove=False):
42 """Add a wwid to the list of exceptions or remove if
43 remove is set to 1.
45 """
47 tmp_file = CONF_FILE + "~"
48 filt_regex = re.compile(r'^\s*%s\s*{' % BELIST_TAG)
49 wwid_regex = re.compile(r'^\s*wwid\s+\"%s\"' % wwid)
51 conflock = lock.Lock(LOCK_TYPE_HOST, LOCK_NS)
52 conflock.acquire()
54 try:
55 shutil.copy2(CONF_FILE, tmp_file)
56 except:
57 util.SMlog("Failed to create temp file %s" % (tmp_file))
58 raise
60 add_mode = True
61 for line in fileinput.input(tmp_file, inplace=1):
62 if add_mode:
63 print(line, end=' ')
64 else:
65 if wwid_regex.match(line):
66 add_mode = True
67 else:
68 print(line, end=' ')
69 continue
71 if filt_regex.match(line):
72 if remove:
73 # looking for the line to remove
74 add_mode = False
75 continue
76 else:
77 print("\twwid \"%s\"" % wwid)
79 shutil.move(tmp_file, CONF_FILE)
82def is_blacklisted(dev):
83 """This function returns True if the device is blacklisted according
84 to multipath.conf rules.
86 There is also the possibility that the path to the device we are
87 checking is unavailable for some reason. In that case,
88 is_blacklisted() cannot tell if the device is blacklisted or not
89 and a WWIDException is raised.
91 It cannot be used to check the current daemon rules because it
92 could be running with an old configuration file in memory
94 Input:
95 dev -- it is any string accepted by "multipath -c". A full path
96 is expected.
98 Return:
99 bool -- True or False, depending on whether the device is
100 blacklisted or not.
102 Raise:
103 WWIDException
104 """
106 (rc, stdout, stderr) = util.doexec(['/sbin/multipath', '-v3', '-c', dev])
108 if "scope is nul" in stdout:
109 raise WWIDException("WARNING: Could not retrieve device's "
110 "WWID. Path {} is down".format(dev))
112 # If the device is blacklisted according to multipath.conf, the
113 # string "wwid blacklisted" appears in the verbose output.
114 # This is a very fragile mechanism and keeps changing.
115 # What we want is a method to tell immediately if a device is
116 # blacklisted according only to configuration file rules regardless
117 # of daemon in-memory configuration.
118 return "wwid blacklisted" in stdout
121def check_conf_file():
122 (rc, stdout, stderr) = util.doexec(['/sbin/multipath', '-h'])
123 # Ugly way to check for malformed conf file
124 if len(stdout):
125 util.SMlog("Malformed multipath conf file")
126 return 1
127 return 0
130def usage():
131 print("Usage: %s [-r] -d <device path> -w <device wwid>" % sys.argv[0])
132 print("Usage: %s -f [-r] -w <device wwid>" % sys.argv[0])
133 print("\tAdd a device wwid to multipath.conf whitelist")
134 print("\t-r: remove")
135 print("\t-f: if provided the operation will be performed anyway")
136 print("\t otherwise it will be after checking <device> is")
137 print("\t blacklisted (or not)")
140if __name__ == "__main__": 140 ↛ 141line 140 didn't jump to line 141, because the condition on line 140 was never true
141 import getopt
142 from operator import xor
144 try:
145 opts, args = getopt.getopt(sys.argv[1:], "fd:w:r")
146 except getopt.GetoptError:
147 usage()
148 sys.exit(1)
150 remove = False
151 device = ""
152 force = False
153 wwid = ""
155 for o, a in opts:
156 if o == "-r":
157 remove = True
158 elif o == "-d":
159 device = a
160 elif o == "-f":
161 force = True
162 elif o == "-w":
163 wwid = a
165 if not wwid:
166 usage()
167 sys.exit(1)
169 if not (device or force):
170 usage()
171 sys.exit(1)
173 try:
174 if force or xor(remove, is_blacklisted(device)):
175 try:
176 edit_wwid(wwid, remove)
177 except:
178 sys.exit(1)
179 except WWIDException as e:
180 util.SMlog(e.errstr)
182 sys.exit(0)