Coverage for drivers/lvmcache.py : 21%

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, 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)
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)
79 cmd = [lvutil.CMD_LVS, "--noheadings", "--units",
80 "b", "-o", "+lv_tags", self.vgPath]
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
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)
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]
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
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
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()
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()
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
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)
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)
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
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
243 #
244 # cached access
245 #
246 @lazyInit
247 def checkLV(self, lvName):
248 return self.lvs.get(lvName)
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
270 @lazyInit
271 def getSize(self, lvName):
272 return self.lvs[lvName].size
274 @lazyInit
275 def getHidden(self, lvName):
276 return (lvutil.LV_TAG_HIDDEN in self.lvs[lvName].tags)
278 @lazyInit
279 def getTagged(self, tag):
280 lvList = self.tags.get(tag)
281 if not lvList:
282 return []
283 return lvList
285 @lazyInit
286 def is_active(self, lvname):
287 return self.lvs[lvname].active
289 #
290 # private
291 #
292 def _getPath(self, lvName):
293 return os.path.join(self.vgPath, lvName)
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]
302 def _removeTag(self, lvName, tag):
303 self.lvs[lvName].tags.remove(tag)
304 self.tags[tag].remove(lvName)
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