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 lvutil 

20import os 

21import util 

22 

23from constants import NS_PREFIX_LVM 

24from lock import Lock 

25from refcounter import RefCounter 

26 

27 

28class LVInfo: 

29 def __init__(self, name): 

30 self.name = name 

31 self.vdiType = '' 

32 self.size = 0 

33 self.active = False 

34 self.open = 0 

35 self.readonly = False 

36 self.hidden = False 

37 self.tags = [] 

38 

39 def toString(self): 

40 return "%s, type=%s, size=%d, active=%s, open=%s, ro=%s, hidden=%s, tags=%s" % \ 

41 (self.name, self.vdiType, self.size, self.active, self.open, self.readonly, self.hidden, \ 

42 self.tags) 

43 

44 

45def lazyInit(op): 

46 def wrapper(self, *args): 

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

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

49 self.refresh() 

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

51 try: 

52 ret = op(self, * args) 

53 except KeyError: 

54 util.logException("LVMCache") 

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

56 raise 

57 return ret 

58 return wrapper 

59 

60 

61class LVMCache: 

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

63 information and refreshed""" 

64 

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

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

67 self.vgName = vgName 

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

69 self.config = config 

70 self.lvs = dict() 

71 self.tags = dict() 

72 self.initialized = False 

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

74 

75 def refresh(self): 

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

77 util.SMlog("LVMCache: refreshing") 

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

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

80 #text = util.pread2(cmd) 

81 

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

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

84 

85 text = lvutil.cmd_lvm(cmd) 

86 self.lvs.clear() 

87 self.tags.clear() 

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

89 if not line: 

90 continue 

91 fields = line.split() 

92 lvName = fields[0] 

93 lvInfo = LVInfo(lvName) 

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

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

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

97 lvInfo.open = 1 

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

99 self.lvs[lvName] = lvInfo 

100 if len(fields) >= 5: 

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

102 for tag in tags: 

103 self._addTag(lvName, tag) 

104 self.initialized = True 

105 

106 # 

107 # lvutil functions 

108 # 

109 @lazyInit 

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

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

112 lvInfo = LVInfo(lvName) 

113 lvInfo.size = size 

114 lvInfo.active = True 

115 self.lvs[lvName] = lvInfo 

116 if tag: 

117 self._addTag(lvName, tag) 

118 

119 @lazyInit 

120 def remove(self, lvName): 

121 path = self._getPath(lvName) 

122 lvutil.remove(path, self.config) 

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

124 self._removeTag(lvName, tag) 

125 del self.lvs[lvName] 

126 

127 @lazyInit 

128 def rename(self, lvName, newName): 

129 path = self._getPath(lvName) 

130 lvutil.rename(path, newName) 

131 lvInfo = self.lvs[lvName] 

132 del self.lvs[lvName] 

133 lvInfo.name = newName 

134 self.lvs[newName] = lvInfo 

135 

136 @lazyInit 

137 def setSize(self, lvName, newSize): 

138 path = self._getPath(lvName) 

139 size = self.getSize(lvName) 

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

141 self.lvs[lvName].size = newSize 

142 

143 @lazyInit 

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

145 lock = Lock(ref, ns) 

146 lock.acquire() 

147 try: 

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

149 if count == 1: 

150 try: 

151 self.activateNoRefcount(lvName, True) 

152 except util.CommandException: 

153 RefCounter.put(ref, binary, ns) 

154 raise 

155 finally: 

156 lock.release() 

157 

158 @lazyInit 

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

160 lock = Lock(ref, ns) 

161 lock.acquire() 

162 try: 

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

164 if count > 0: 

165 return 

166 refreshed = False 

167 while True: 

168 lvInfo = self.getLVInfo(lvName) 

169 if len(lvInfo) != 1: 

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

171 info = lvInfo[lvName] 

172 if info.open: 

173 if refreshed: 

174 # should never happen in normal conditions but in some 

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

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

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

178 # sync 

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

180 return 

181 # check again in case the cached value is stale 

182 self.refresh() 

183 refreshed = True 

184 else: 

185 break 

186 try: 

187 self.deactivateNoRefcount(lvName) 

188 except util.CommandException: 

189 self.refresh() 

190 if self.getLVInfo(lvName): 

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

192 if lvInfo[lvName].active: 

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

194 RefCounter.get(ref, binary, ns) 

195 raise 

196 else: 

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

198 finally: 

199 lock.release() 

200 

201 @lazyInit 

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

203 path = self._getPath(lvName) 

204 lvutil.activateNoRefcount(path, refresh) 

205 self.lvs[lvName].active = True 

206 

207 @lazyInit 

208 def deactivateNoRefcount(self, lvName): 

209 path = self._getPath(lvName) 

210 if self.checkLV(lvName): 

211 lvutil.deactivateNoRefcount(path) 

212 self.lvs[lvName].active = False 

213 else: 

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

215 lvutil._lvmBugCleanup(path) 

216 

217 @lazyInit 

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

219 path = self._getPath(lvName) 

220 if hidden: 

221 lvutil.setHidden(path) 

222 self._addTag(lvName, lvutil.LV_TAG_HIDDEN) 

223 else: 

224 lvutil.setHidden(path, hidden=False) 

225 self._removeTag(lvName, lvutil.LV_TAG_HIDDEN) 

226 

227 @lazyInit 

228 def setReadonly(self, lvName, readonly): 

229 path = self._getPath(lvName) 

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

231 uuids = util.findall_uuid(path) 

232 ns = NS_PREFIX_LVM + uuids[0] 

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

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

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

236 lock.acquire() 

237 lvutil.setReadonly(path, readonly) 

238 lock.release() 

239 self.lvs[lvName].readonly = readonly 

240 

241 @lazyInit 

242 def changeOpen(self, lvName, inc): 

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

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

245 

246 # 

247 # cached access 

248 # 

249 @lazyInit 

250 def checkLV(self, lvName): 

251 return self.lvs.get(lvName) 

252 

253 @lazyInit 

254 def getLVInfo(self, lvName=None): 

255 result = dict() 

256 lvs = [] 

257 if lvName is None: 

258 lvs = self.lvs.keys() 

259 elif self.lvs.get(lvName): 

260 lvs = [lvName] 

261 for lvName in lvs: 

262 lvInfo = self.lvs[lvName] 

263 lvutilInfo = lvutil.LVInfo(lvName) 

264 lvutilInfo.size = lvInfo.size 

265 lvutilInfo.active = lvInfo.active 

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

267 lvutilInfo.readonly = lvInfo.readonly 

268 if lvutil.LV_TAG_HIDDEN in lvInfo.tags: 

269 lvutilInfo.hidden = True 

270 result[lvName] = lvutilInfo 

271 return result 

272 

273 @lazyInit 

274 def getSize(self, lvName): 

275 return self.lvs[lvName].size 

276 

277 @lazyInit 

278 def getHidden(self, lvName): 

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

280 

281 @lazyInit 

282 def getTagged(self, tag): 

283 lvList = self.tags.get(tag) 

284 if not lvList: 

285 return [] 

286 return lvList 

287 

288 @lazyInit 

289 def is_active(self, lvname): 

290 return self.lvs[lvname].active 

291 

292 # 

293 # private 

294 # 

295 def _getPath(self, lvName): 

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

297 

298 def _addTag(self, lvName, tag): 

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

300 if self.tags.get(tag): 

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

302 else: 

303 self.tags[tag] = [lvName] 

304 

305 def _removeTag(self, lvName, tag): 

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

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

308 

309 def toString(self): 

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

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

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

313 return result