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

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.lvs = dict() 

67 self.tags = dict() 

68 self.initialized = False 

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

70 

71 def refresh(self): 

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

73 util.SMlog("LVMCache: refreshing") 

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

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

76 #text = util.pread2(cmd) 

77 

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

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

80 

81 text = lvutil.cmd_lvm(cmd) 

82 self.lvs.clear() 

83 self.tags.clear() 

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

85 if not line: 

86 continue 

87 fields = line.split() 

88 lvName = fields[0] 

89 lvInfo = LVInfo(lvName) 

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

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

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

93 lvInfo.open = 1 

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

95 self.lvs[lvName] = lvInfo 

96 if len(fields) >= 5: 

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

98 for tag in tags: 

99 self._addTag(lvName, tag) 

100 self.initialized = True 

101 

102 # 

103 # lvutil functions 

104 # 

105 @lazyInit 

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

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

108 lvInfo = LVInfo(lvName) 

109 lvInfo.size = size 

110 lvInfo.active = True 

111 self.lvs[lvName] = lvInfo 

112 if tag: 

113 self._addTag(lvName, tag) 

114 

115 @lazyInit 

116 def remove(self, lvName): 

117 path = self._getPath(lvName) 

118 lvutil.remove(path) 

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

120 self._removeTag(lvName, tag) 

121 del self.lvs[lvName] 

122 

123 @lazyInit 

124 def rename(self, lvName, newName): 

125 path = self._getPath(lvName) 

126 lvutil.rename(path, newName) 

127 lvInfo = self.lvs[lvName] 

128 del self.lvs[lvName] 

129 lvInfo.name = newName 

130 self.lvs[newName] = lvInfo 

131 

132 @lazyInit 

133 def setSize(self, lvName, newSize): 

134 path = self._getPath(lvName) 

135 size = self.getSize(lvName) 

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

137 self.lvs[lvName].size = newSize 

138 

139 @lazyInit 

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

141 lock = Lock(ref, ns) 

142 lock.acquire() 

143 try: 

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

145 if count == 1: 

146 try: 

147 self.activateNoRefcount(lvName) 

148 except util.CommandException: 

149 RefCounter.put(ref, binary, ns) 

150 raise 

151 finally: 

152 lock.release() 

153 

154 @lazyInit 

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

156 lock = Lock(ref, ns) 

157 lock.acquire() 

158 try: 

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

160 if count > 0: 

161 return 

162 refreshed = False 

163 while True: 

164 lvInfo = self.getLVInfo(lvName) 

165 if len(lvInfo) != 1: 

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

167 info = lvInfo[lvName] 

168 if info.open: 

169 if refreshed: 

170 # should never happen in normal conditions but in some 

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

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

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

174 # sync 

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

176 return 

177 # check again in case the cached value is stale 

178 self.refresh() 

179 refreshed = True 

180 else: 

181 break 

182 try: 

183 self.deactivateNoRefcount(lvName) 

184 except util.CommandException: 

185 self.refresh() 

186 if self.getLVInfo(lvName): 

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

188 if lvInfo[lvName].active: 

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

190 RefCounter.get(ref, binary, ns) 

191 raise 

192 else: 

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

194 finally: 

195 lock.release() 

196 

197 @lazyInit 

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

199 path = self._getPath(lvName) 

200 lvutil.activateNoRefcount(path, refresh) 

201 self.lvs[lvName].active = True 

202 

203 @lazyInit 

204 def deactivateNoRefcount(self, lvName): 

205 path = self._getPath(lvName) 

206 if self.checkLV(lvName): 

207 lvutil.deactivateNoRefcount(path) 

208 self.lvs[lvName].active = False 

209 else: 

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

211 lvutil._lvmBugCleanup(path) 

212 

213 @lazyInit 

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

215 path = self._getPath(lvName) 

216 if hidden: 

217 lvutil.setHidden(path) 

218 self._addTag(lvName, lvutil.LV_TAG_HIDDEN) 

219 else: 

220 lvutil.setHidden(path, hidden=False) 

221 self._removeTag(lvName, lvutil.LV_TAG_HIDDEN) 

222 

223 @lazyInit 

224 def setReadonly(self, lvName, readonly): 

225 path = self._getPath(lvName) 

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

227 uuids = util.findall_uuid(path) 

228 ns = lvhdutil.NS_PREFIX_LVM + uuids[0] 

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

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

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

232 lock.acquire() 

233 lvutil.setReadonly(path, readonly) 

234 lock.release() 

235 self.lvs[lvName].readonly = readonly 

236 

237 @lazyInit 

238 def changeOpen(self, lvName, inc): 

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

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

241 

242 # 

243 # cached access 

244 # 

245 @lazyInit 

246 def checkLV(self, lvName): 

247 return self.lvs.get(lvName) 

248 

249 @lazyInit 

250 def getLVInfo(self, lvName=None): 

251 result = dict() 

252 lvs = [] 

253 if lvName is None: 

254 lvs = self.lvs.keys() 

255 elif self.lvs.get(lvName): 

256 lvs = [lvName] 

257 for lvName in lvs: 

258 lvInfo = self.lvs[lvName] 

259 lvutilInfo = lvutil.LVInfo(lvName) 

260 lvutilInfo.size = lvInfo.size 

261 lvutilInfo.active = lvInfo.active 

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

263 lvutilInfo.readonly = lvInfo.readonly 

264 if lvutil.LV_TAG_HIDDEN in lvInfo.tags: 

265 lvutilInfo.hidden = True 

266 result[lvName] = lvutilInfo 

267 return result 

268 

269 @lazyInit 

270 def getSize(self, lvName): 

271 return self.lvs[lvName].size 

272 

273 @lazyInit 

274 def getHidden(self, lvName): 

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

276 

277 @lazyInit 

278 def getTagged(self, tag): 

279 lvList = self.tags.get(tag) 

280 if not lvList: 

281 return [] 

282 return lvList 

283 

284 @lazyInit 

285 def is_active(self, lvname): 

286 return self.lvs[lvname].active 

287 

288 # 

289 # private 

290 # 

291 def _getPath(self, lvName): 

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

293 

294 def _addTag(self, lvName, tag): 

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

296 if self.tags.get(tag): 

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

298 else: 

299 self.tags[tag] = [lvName] 

300 

301 def _removeTag(self, lvName, tag): 

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

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

304 

305 def toString(self): 

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

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

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

309 return result