Hide keyboard shortcuts

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# 

20 

21import fileinput 

22import shutil 

23import sys 

24import re 

25import util 

26import lock 

27 

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" 

34 

35 

36class WWIDException(Exception): 

37 def __init__(self, errstr): 

38 self.errstr = errstr 

39 

40 

41def edit_wwid(wwid, remove=False): 

42 """Add a wwid to the list of exceptions or remove if 

43 remove is set to 1. 

44 

45 """ 

46 

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) 

50 

51 conflock = lock.Lock(LOCK_TYPE_HOST, LOCK_NS) 

52 conflock.acquire() 

53 

54 try: 

55 shutil.copy2(CONF_FILE, tmp_file) 

56 except: 

57 util.SMlog("Failed to create temp file %s" % (tmp_file)) 

58 raise 

59 

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 

70 

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) 

78 

79 shutil.move(tmp_file, CONF_FILE) 

80 

81 

82def is_blacklisted(dev): 

83 """This function returns True if the device is blacklisted according 

84 to multipath.conf rules. 

85 

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. 

90 

91 It cannot be used to check the current daemon rules because it 

92 could be running with an old configuration file in memory 

93 

94 Input: 

95 dev -- it is any string accepted by "multipath -c". A full path 

96 is expected. 

97 

98 Return: 

99 bool -- True or False, depending on whether the device is 

100 blacklisted or not. 

101 

102 Raise: 

103 WWIDException 

104 """ 

105 

106 (rc, stdout, stderr) = util.doexec(['/sbin/multipath', '-v3', '-c', dev]) 

107 

108 if "scope is nul" in stdout: 

109 raise WWIDException("WARNING: Could not retrieve device's " 

110 "WWID. Path {} is down".format(dev)) 

111 

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 

119 

120 

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 

128 

129 

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)") 

138 

139 

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 

143 

144 try: 

145 opts, args = getopt.getopt(sys.argv[1:], "fd:w:r") 

146 except getopt.GetoptError: 

147 usage() 

148 sys.exit(1) 

149 

150 remove = False 

151 device = "" 

152 force = False 

153 wwid = "" 

154 

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 

164 

165 if not wwid: 

166 usage() 

167 sys.exit(1) 

168 

169 if not (device or force): 

170 usage() 

171 sys.exit(1) 

172 

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) 

181 

182 sys.exit(0)