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# 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 

15# 

16# LVM cache (for minimizing the number of lvs commands) 

17# 

18 

19import os 

20import util 

21import lvutil 

22import lvhdutil 

23from lock import Lock 

24from refcounter import RefCounter 

25 

26 

27class LVInfo: 

28 def __init__(self, name): 

29 self.name = name 

30 self.size = 0 

31 self.active = False 

32 self.open = 0 

33 self.readonly = False 

34 self.tags = [] 

35 

36 def toString(self): 

37 return "%s, size=%d, active=%s, open=%s, ro=%s, tags=%s" % \ 

38 (self.name, self.size, self.active, self.open, self.readonly, \ 

39 self.tags) 

40 

41 

42def lazyInit(op): 

43 def wrapper(self, *args): 

44 if not self.initialized: 44 ↛ 48line 44 didn't jump to line 48, because the condition on line 44 was never false

45 util.SMlog("LVMCache: will initialize now") 

46 self.refresh() 

47 #util.SMlog("%s(%s): %s" % (op, args, self.toString())) 

48 try: 

49 ret = op(self, * args) 

50 except KeyError: 

51 util.logException("LVMCache") 

52 util.SMlog("%s(%s): %s" % (op, args, self.toString())) 

53 raise 

54 return ret 

55 return wrapper 

56 

57 

58class LVMCache: 

59 """Per-VG object to store LV information. Can be queried for cached LVM 

60 information and refreshed""" 

61 

62 def __init__(self, vgName, config=None): 

63 """Create a cache for VG vgName, but don't scan the VG yet""" 

64 self.vgName = vgName 

65 self.vgPath = "/dev/%s" % self.vgName 

66 self.config = config 

67 self.lvs = dict() 

68 self.tags = dict() 

69 self.initialized = False 

70 util.SMlog("LVMCache created for %s" % vgName) 

71 

72 def refresh(self): 

73 """Get the LV information for the VG using "lvs" """ 

74 util.SMlog("LVMCache: refreshing") 

75 #cmd = lvutil.cmd_lvm([lvutil.CMD_LVS, "--noheadings", "--units", 

76 # "b", "-o", "+lv_tags", self.vgPath]) 

77 #text = util.pread2(cmd) 

78 

79 cmd = [lvutil.CMD_LVS, "--noheadings", "--units", 

80 "b", "-o", "+lv_tags", self.vgPath] 

81 

82 text = lvutil.cmd_lvm(cmd) 

83 self.lvs.clear() 

84 self.tags.clear() 

85 for line in text.split('\n'): 

86 if not line: 

87 continue 

88 fields = line.split() 

89 lvName = fields[0] 

90 lvInfo = LVInfo(lvName) 

91 lvInfo.size = int(fields[3].replace("B", "")) 

92 lvInfo.active = (fields[2][4] == 'a') 

93 if (fields[2][5] == 'o'): 

94 lvInfo.open = 1 

95 lvInfo.readonly = (fields[2][1] == 'r') 

96 self.lvs[lvName] = lvInfo 

97 if len(fields) >= 5: 

98 tags = fields[4].split(',') 

99 for tag in tags: 

100 self._addTag(lvName, tag) 

101 self.initialized = True 

102 

103 # 

104 # lvutil functions 

105 # 

106 @lazyInit 

107 def create(self, lvName, size, tag=None): 

108 lvutil.create(lvName, size, self.vgName, tag) 

109 lvInfo = LVInfo(lvName) 

110 lvInfo.size = size 

111 lvInfo.active = True 

112 self.lvs[lvName] = lvInfo 

113 if tag: 

114 self._addTag(lvName, tag) 

115 

116 @lazyInit 

117 def remove(self, lvName): 

118 path = self._getPath(lvName) 

119 lvutil.remove(path, self.config) 

120 for tag in self.lvs[lvName].tags: 

121 self._removeTag(lvName, tag) 

122 del self.lvs[lvName] 

123 

124 @lazyInit 

125 def rename(self, lvName, newName): 

126 path = self._getPath(lvName) 

127 lvutil.rename(path, newName) 

128 lvInfo = self.lvs[lvName] 

129 del self.lvs[lvName] 

130 lvInfo.name = newName 

131 self.lvs[newName] = lvInfo 

132 

133 @lazyInit 

134 def setSize(self, lvName, newSize): 

135 path = self._getPath(lvName) 

136 size = self.getSize(lvName) 

137 lvutil.setSize(path, newSize, (newSize < size)) 

138 self.lvs[lvName].size = newSize 

139 

140 @lazyInit 

141 def activate(self, ns, ref, lvName, binary): 

142 lock = Lock(ref, ns) 

143 lock.acquire() 

144 try: 

145 count = RefCounter.get(ref, binary, ns) 

146 if count == 1: 

147 try: 

148 self.activateNoRefcount(lvName) 

149 except util.CommandException: 

150 RefCounter.put(ref, binary, ns) 

151 raise 

152 finally: 

153 lock.release() 

154 

155 @lazyInit 

156 def deactivate(self, ns, ref, lvName, binary): 

157 lock = Lock(ref, ns) 

158 lock.acquire() 

159 try: 

160 count = RefCounter.put(ref, binary, ns) 

161 if count > 0: 

162 return 

163 refreshed = False 

164 while True: 

165 lvInfo = self.getLVInfo(lvName) 

166 if len(lvInfo) != 1: 

167 raise util.SMException("LV info not found for %s" % ref) 

168 info = lvInfo[lvName] 

169 if info.open: 

170 if refreshed: 

171 # should never happen in normal conditions but in some 

172 # failure cases the recovery code may not be able to 

173 # determine what the correct refcount should be, so it 

174 # is not unthinkable that the value might be out of 

175 # sync 

176 util.SMlog("WARNING: deactivate: LV %s open" % lvName) 

177 return 

178 # check again in case the cached value is stale 

179 self.refresh() 

180 refreshed = True 

181 else: 

182 break 

183 try: 

184 self.deactivateNoRefcount(lvName) 

185 except util.CommandException: 

186 self.refresh() 

187 if self.getLVInfo(lvName): 

188 util.SMlog("LV %s could not be deactivated" % lvName) 

189 if lvInfo[lvName].active: 

190 util.SMlog("Reverting the refcount change") 

191 RefCounter.get(ref, binary, ns) 

192 raise 

193 else: 

194 util.SMlog("LV %s not found" % lvName) 

195 finally: 

196 lock.release() 

197 

198 @lazyInit 

199 def activateNoRefcount(self, lvName, refresh=False): 

200 path = self._getPath(lvName) 

201 lvutil.activateNoRefcount(path, refresh) 

202 self.lvs[lvName].active = True 

203 

204 @lazyInit 

205 def deactivateNoRefcount(self, lvName): 

206 path = self._getPath(lvName) 

207 if self.checkLV(lvName): 

208 lvutil.deactivateNoRefcount(path) 

209 self.lvs[lvName].active = False 

210 else: 

211 util.SMlog("LVMCache.deactivateNoRefcount: no LV %s" % lvName) 

212 lvutil._lvmBugCleanup(path) 

213 

214 @lazyInit 

215 def setHidden(self, lvName, hidden=True): 

216 path = self._getPath(lvName) 

217 if hidden: 

218 lvutil.setHidden(path) 

219 self._addTag(lvName, lvutil.LV_TAG_HIDDEN) 

220 else: 

221 lvutil.setHidden(path, hidden=False) 

222 self._removeTag(lvName, lvutil.LV_TAG_HIDDEN) 

223 

224 @lazyInit 

225 def setReadonly(self, lvName, readonly): 

226 path = self._getPath(lvName) 

227 if self.lvs[lvName].readonly != readonly: 

228 uuids = util.findall_uuid(path) 

229 ns = lvhdutil.NS_PREFIX_LVM + uuids[0] 

230 # Taking this lock is needed to avoid a race condition 

231 # with tap-ctl open (which is now taking the same lock) 

232 lock = Lock("lvchange-p", ns) 

233 lock.acquire() 

234 lvutil.setReadonly(path, readonly) 

235 lock.release() 

236 self.lvs[lvName].readonly = readonly 

237 

238 @lazyInit 

239 def changeOpen(self, lvName, inc): 

240 """We don't actually open or close the LV, just mark it in the cache""" 

241 self.lvs[lvName].open += inc 

242 

243 # 

244 # cached access 

245 # 

246 @lazyInit 

247 def checkLV(self, lvName): 

248 return self.lvs.get(lvName) 

249 

250 @lazyInit 

251 def getLVInfo(self, lvName=None): 

252 result = dict() 

253 lvs = [] 

254 if lvName is None: 

255 lvs = self.lvs.keys() 

256 elif self.lvs.get(lvName): 

257 lvs = [lvName] 

258 for lvName in lvs: 

259 lvInfo = self.lvs[lvName] 

260 lvutilInfo = lvutil.LVInfo(lvName) 

261 lvutilInfo.size = lvInfo.size 

262 lvutilInfo.active = lvInfo.active 

263 lvutilInfo.open = (lvInfo.open > 0) 

264 lvutilInfo.readonly = lvInfo.readonly 

265 if lvutil.LV_TAG_HIDDEN in lvInfo.tags: 

266 lvutilInfo.hidden = True 

267 result[lvName] = lvutilInfo 

268 return result 

269 

270 @lazyInit 

271 def getSize(self, lvName): 

272 return self.lvs[lvName].size 

273 

274 @lazyInit 

275 def getHidden(self, lvName): 

276 return (lvutil.LV_TAG_HIDDEN in self.lvs[lvName].tags) 

277 

278 @lazyInit 

279 def getTagged(self, tag): 

280 lvList = self.tags.get(tag) 

281 if not lvList: 

282 return [] 

283 return lvList 

284 

285 @lazyInit 

286 def is_active(self, lvname): 

287 return self.lvs[lvname].active 

288 

289 # 

290 # private 

291 # 

292 def _getPath(self, lvName): 

293 return os.path.join(self.vgPath, lvName) 

294 

295 def _addTag(self, lvName, tag): 

296 self.lvs[lvName].tags.append(tag) 

297 if self.tags.get(tag): 

298 self.tags[tag].append(lvName) 

299 else: 

300 self.tags[tag] = [lvName] 

301 

302 def _removeTag(self, lvName, tag): 

303 self.lvs[lvName].tags.remove(tag) 

304 self.tags[tag].remove(lvName) 

305 

306 def toString(self): 

307 result = "LVM Cache for %s: %d LVs" % (self.vgName, len(self.lvs)) 

308 for lvName, lvInfo in self.lvs.items(): 

309 result += "\n%s" % lvInfo.toString() 

310 return result