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 lvutil
20import os
21import util
23from constants import NS_PREFIX_LVM
24from lock import Lock
25from refcounter import RefCounter
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 = []
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)
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
61class LVMCache:
62 """Per-VG object to store LV information. Can be queried for cached LVM
63 information and refreshed"""
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)
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)
82 cmd = [lvutil.CMD_LVS, "--noheadings", "--units",
83 "b", "-o", "+lv_tags", self.vgPath]
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
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)
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]
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
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
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()
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()
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
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)
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)
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
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
246 #
247 # cached access
248 #
249 @lazyInit
250 def checkLV(self, lvName):
251 return self.lvs.get(lvName)
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
273 @lazyInit
274 def getSize(self, lvName):
275 return self.lvs[lvName].size
277 @lazyInit
278 def getHidden(self, lvName):
279 return (lvutil.LV_TAG_HIDDEN in self.lvs[lvName].tags)
281 @lazyInit
282 def getTagged(self, tag):
283 lvList = self.tags.get(tag)
284 if not lvList:
285 return []
286 return lvList
288 @lazyInit
289 def is_active(self, lvname):
290 return self.lvs[lvname].active
292 #
293 # private
294 #
295 def _getPath(self, lvName):
296 return os.path.join(self.vgPath, lvName)
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]
305 def _removeTag(self, lvName, tag):
306 self.lvs[lvName].tags.remove(tag)
307 self.tags[tag].remove(lvName)
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