Coverage for drivers/lvmcache.py : 20%

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#
19import os
20import util
21import lvutil
22import lvhdutil
23from lock import Lock
24from refcounter import RefCounter
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 = []
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)
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
58class LVMCache:
59 """Per-VG object to store LV information. Can be queried for cached LVM
60 information and refreshed"""
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)
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)
78 cmd = [lvutil.CMD_LVS, "--noheadings", "--units",
79 "b", "-o", "+lv_tags", self.vgPath]
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
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)
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]
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
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
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()
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()
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
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)
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)
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
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
242 #
243 # cached access
244 #
245 @lazyInit
246 def checkLV(self, lvName):
247 return self.lvs.get(lvName)
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
269 @lazyInit
270 def getSize(self, lvName):
271 return self.lvs[lvName].size
273 @lazyInit
274 def getHidden(self, lvName):
275 return (lvutil.LV_TAG_HIDDEN in self.lvs[lvName].tags)
277 @lazyInit
278 def getTagged(self, tag):
279 lvList = self.tags.get(tag)
280 if not lvList:
281 return []
282 return lvList
284 @lazyInit
285 def is_active(self, lvname):
286 return self.lvs[lvname].active
288 #
289 # private
290 #
291 def _getPath(self, lvName):
292 return os.path.join(self.vgPath, lvName)
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]
301 def _removeTag(self, lvName, tag):
302 self.lvs[lvName].tags.remove(tag)
303 self.tags[tag].remove(lvName)
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