Coverage for drivers/lvutil.py : 44%

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# Miscellaneous LVM utility functions
17#
19import re
20import os
21import errno
22import time
24import lock
25import util
26import xs_errors
27import xml.dom.minidom
28from lvhdutil import VG_LOCATION, VG_PREFIX
29from constants import EXT_PREFIX
30import lvmcache
31import srmetadata
33MDVOLUME_NAME = 'MGT'
34VDI_UUID_TAG_PREFIX = 'vdi_'
35LVM_BIN = os.path.isfile('/sbin/lvdisplay') and '/sbin' or '/usr/sbin'
36CMD_VGS = "vgs"
37CMD_VGCREATE = "vgcreate"
38CMD_VGREMOVE = "vgremove"
39CMD_VGCHANGE = "vgchange"
40CMD_VGEXTEND = "vgextend"
41CMD_PVS = "pvs"
42CMD_PVCREATE = "pvcreate"
43CMD_PVREMOVE = "pvremove"
44CMD_PVRESIZE = "pvresize"
45CMD_LVS = "lvs"
46CMD_LVDISPLAY = "lvdisplay"
47CMD_LVCREATE = "lvcreate"
48CMD_LVREMOVE = "lvremove"
49CMD_LVCHANGE = "lvchange"
50CMD_LVRENAME = "lvrename"
51CMD_LVRESIZE = "lvresize"
52CMD_LVEXTEND = "lvextend"
53CMD_DMSETUP = "/sbin/dmsetup"
55MAX_OPERATION_DURATION = 15
57LVM_SIZE_INCREMENT = 4 * 1024 * 1024
58LV_TAG_HIDDEN = "hidden"
59LVM_FAIL_RETRIES = 10
61MASTER_LVM_CONF = '/etc/lvm/master'
62DEF_LVM_CONF = '/etc/lvm'
64VG_COMMANDS = frozenset({CMD_VGS, CMD_VGCREATE, CMD_VGREMOVE, CMD_VGCHANGE,
65 CMD_VGEXTEND})
66PV_COMMANDS = frozenset({CMD_PVS, CMD_PVCREATE, CMD_PVREMOVE, CMD_PVRESIZE})
67LV_COMMANDS = frozenset({CMD_LVS, CMD_LVDISPLAY, CMD_LVCREATE, CMD_LVREMOVE,
68 CMD_LVCHANGE, CMD_LVRENAME, CMD_LVRESIZE,
69 CMD_LVEXTEND})
70DM_COMMANDS = frozenset({CMD_DMSETUP})
72LVM_COMMANDS = VG_COMMANDS.union(PV_COMMANDS, LV_COMMANDS, DM_COMMANDS)
74LVM_LOCK = 'lvm'
77def extract_vgname(str_in):
78 """Search for and return a VG name
80 Search 'str_in' for a substring in the form of 'VG_XenStorage-<UUID>'.
81 If there are more than one VG names, the first is returned.
83 Input:
84 str_in -- (str) string to search for a VG name
85 in the format specified above.
87 Return:
88 vgname -- if found -> (str)
89 if not found -> None
91 Raise:
92 TypeError
93 """
95 if not util.is_string(str_in):
96 raise TypeError("'str_in' not of type 'str'.")
98 i = str_in.find(VG_PREFIX)
99 prefix = VG_PREFIX
101 if i == -1:
102 i = str_in.find(EXT_PREFIX)
103 prefix = EXT_PREFIX
105 uuid_start = i + len(prefix)
106 re_obj = util.match_uuid(str_in[uuid_start:])
108 if i != -1 and re_obj:
109 return prefix + re_obj.group(0) # vgname
111 return None
113class LvmLockContext(object):
114 """
115 Context manager around the LVM lock.
117 To allow for higher level operations, e.g. VDI snapshot to pre-emptively
118 acquire the lock to encapsulte a set of calls and avoid having to reacquire
119 the locks for each LVM call.
120 """
122 def __init__(self, cmd=None):
123 self.lock = lock.Lock(LVM_LOCK)
124 self.cmd = cmd
125 self.locked = False
127 def __enter__(self):
128 if self.cmd and '--readonly' in self.cmd: 128 ↛ 129line 128 didn't jump to line 129, because the condition on line 128 was never true
129 return
131 self.lock.acquire()
132 self.locked = True
134 def __exit__(self, exc_type, value, traceback):
135 if self.locked: 135 ↛ exitline 135 didn't return from function '__exit__', because the condition on line 135 was never false
136 self.lock.release()
139LVM_RETRY_ERRORS = [
140 "Incorrect checksum in metadata area header"
141]
144def lvmretry(func):
145 def check_exception(exception):
146 retry = False
147 for error in LVM_RETRY_ERRORS:
148 if error in exception.reason:
149 retry = True
150 return retry
152 def decorated(*args, **kwargs):
153 for i in range(LVM_FAIL_RETRIES): 153 ↛ exitline 153 didn't return from function 'decorated', because the loop on line 153 didn't complete
154 try:
155 return func(*args, **kwargs)
156 except util.CommandException as ce:
157 retry = check_exception(ce)
158 if not retry or (i == LVM_FAIL_RETRIES - 1):
159 raise
161 time.sleep(1)
163 decorated.__name__ = func.__name__
164 return decorated
167def cmd_lvm(cmd, pread_func=util.pread2, *args):
168 """ Construct and run the appropriate lvm command.
170 For PV commands, the full path to the device is required.
172 Input:
173 cmd -- (list) lvm command
174 cmd[0] -- (str) lvm command name
175 cmd[1:] -- (str) lvm command parameters
177 pread_func -- (function) the flavor of util.pread to use
178 to execute the lvm command
179 Default: util.pread2()
181 *args -- extra arguments passed to cmd_lvm will be passed
182 to 'pread_func'
184 Return:
185 stdout -- (str) stdout after running the lvm command.
187 Raise:
188 util.CommandException
189 """
191 if type(cmd) is not list: 191 ↛ 192line 191 didn't jump to line 192, because the condition on line 191 was never true
192 util.SMlog("CMD_LVM: Argument 'cmd' not of type 'list'")
193 return None
194 if not len(cmd): 194 ↛ 195line 194 didn't jump to line 195, because the condition on line 194 was never true
195 util.SMlog("CMD_LVM: 'cmd' list is empty")
196 return None
198 lvm_cmd, lvm_args = cmd[0], cmd[1:]
200 if lvm_cmd not in LVM_COMMANDS: 200 ↛ 201line 200 didn't jump to line 201, because the condition on line 200 was never true
201 util.SMlog("CMD_LVM: '{}' is not a valid lvm command".format(lvm_cmd))
202 return None
204 for arg in lvm_args:
205 if not util.is_string(arg): 205 ↛ 206line 205 didn't jump to line 206, because the condition on line 205 was never true
206 util.SMlog("CMD_LVM: Not all lvm arguments are of type 'str'")
207 return None
209 with LvmLockContext(cmd):
210 start_time = time.time()
211 stdout = pread_func([os.path.join(LVM_BIN, lvm_cmd)] + lvm_args, * args)
212 end_time = time.time()
214 if (end_time - start_time > MAX_OPERATION_DURATION): 214 ↛ 215line 214 didn't jump to line 215, because the condition on line 214 was never true
215 util.SMlog("***** Long LVM call of '%s' took %s" % (lvm_cmd, (end_time - start_time)))
217 return stdout
220class LVInfo:
221 name = ""
222 size = 0
223 active = False
224 open = False
225 hidden = False
226 readonly = False
228 def __init__(self, name):
229 self.name = name
231 def toString(self):
232 return "%s, size=%d, active=%s, open=%s, hidden=%s, ro=%s" % \
233 (self.name, self.size, self.active, self.open, self.hidden, \
234 self.readonly)
237def _checkVG(vgname):
238 try:
239 cmd_lvm([CMD_VGS, "--readonly", vgname])
240 return True
241 except:
242 return False
245def _checkPV(pvname):
246 try:
247 cmd_lvm([CMD_PVS, pvname])
248 return True
249 except:
250 return False
253def _checkLV(path):
254 try:
255 cmd_lvm([CMD_LVDISPLAY, path])
256 return True
257 except:
258 return False
261def _getLVsize(path):
262 try:
263 lines = cmd_lvm([CMD_LVDISPLAY, "-c", path]).split(':')
264 return int(lines[6]) * 512
265 except:
266 raise xs_errors.XenError('VDIUnavailable', \
267 opterr='no such VDI %s' % path)
270def _getVGstats(vgname):
271 try:
272 text = cmd_lvm([CMD_VGS, "--noheadings", "--nosuffix",
273 "--units", "b", vgname],
274 pread_func=util.pread).split()
275 size = int(text[5])
276 freespace = int(text[6])
277 utilisation = size - freespace
278 stats = {}
279 stats['physical_size'] = size
280 stats['physical_utilisation'] = utilisation
281 stats['freespace'] = freespace
282 return stats
283 except util.CommandException as inst:
284 raise xs_errors.XenError('VDILoad', \
285 opterr='rvgstats failed error is %d' % inst.code)
286 except ValueError:
287 raise xs_errors.XenError('VDILoad', opterr='rvgstats failed')
290def _getPVstats(dev):
291 try:
292 text = cmd_lvm([CMD_PVS, "--noheadings", "--nosuffix",
293 "--units", "b", dev],
294 pread_func=util.pread).split()
295 size = int(text[4])
296 freespace = int(text[5])
297 utilisation = size - freespace
298 stats = {}
299 stats['physical_size'] = size
300 stats['physical_utilisation'] = utilisation
301 stats['freespace'] = freespace
302 return stats
303 except util.CommandException as inst:
304 raise xs_errors.XenError('VDILoad', \
305 opterr='pvstats failed error is %d' % inst.code)
306 except ValueError:
307 raise xs_errors.XenError('VDILoad', opterr='rvgstats failed')
310# Retrieves the UUID of the SR that corresponds to the specified Physical
311# Volume (pvname). Each element in prefix_list is checked whether it is a
312# prefix of Volume Groups that correspond to the specified PV. If so, the
313# prefix is stripped from the matched VG name and the remainder is returned
314# (effectively the SR UUID). If no match if found, the empty string is
315# returned.
316# E.g.
317# PV VG Fmt Attr PSize PFree
318# /dev/sda4 VG_XenStorage-some-hex-value lvm2 a- 224.74G 223.73G
319# will return "some-hex-value".
320def _get_sr_uuid(pvname, prefix_list):
321 try:
322 return match_VG(cmd_lvm([CMD_PVS, "--noheadings",
323 "-o", "vg_name", pvname]), prefix_list)
324 except:
325 return ""
328# Retrieves the names of the Physical Volumes which are used by the specified
329# Volume Group
330# e.g.
331# PV VG Fmt Attr PSize PFree
332# /dev/sda4 VG_XenStorage-some-hex-value lvm2 a- 224.74G 223.73G
333# will return "/dev/sda4" when given the argument "VG_XenStorage-some-hex-value".
334def get_pv_for_vg(vgname):
335 try:
336 result = cmd_lvm([CMD_PVS, "--noheadings",
337 '-S', 'vg_name=%s' % vgname, '-o', 'name'])
338 return [x.strip() for x in result.splitlines()]
339 except util.CommandException:
340 return []
343# Tries to match any prefix contained in prefix_list in s. If matched, the
344# remainder string is returned, else the empty string is returned. E.g. if s is
345# "VG_XenStorage-some-hex-value" and prefix_list contains "VG_XenStorage-",
346# "some-hex-value" is returned.
347#
348# TODO Items in prefix_list are expected to be found at the beginning of the
349# target string, though if any of them is found inside it a match will be
350# produced. This could be remedied by making the regular expression more
351# specific.
352def match_VG(s, prefix_list):
353 for val in prefix_list:
354 regex = re.compile(val)
355 if regex.search(s, 0):
356 return s.split(val)[1]
357 return ""
360# Retrieves the devices an SR is composed of. A dictionary is returned, indexed
361# by the SR UUID, where each SR UUID is mapped to a comma-separated list of
362# devices. Exceptions are ignored.
363def scan_srlist(prefix, root):
364 VGs = {}
365 for dev in root.split(','):
366 try:
367 sr_uuid = _get_sr_uuid(dev, [prefix]).strip(' \n')
368 if len(sr_uuid):
369 if sr_uuid in VGs:
370 VGs[sr_uuid] += ",%s" % dev
371 else:
372 VGs[sr_uuid] = dev
373 except Exception as e:
374 util.logException("exception (ignored): %s" % e)
375 continue
376 return VGs
379# Converts an SR list to an XML document with the following structure:
380# <SRlist>
381# <SR>
382# <UUID>...</UUID>
383# <Devlist>...</Devlist>
384# <size>...</size>
385# <!-- If includeMetadata is set to True, the following additional nodes
386# are supplied. -->
387# <name_label>...</name_label>
388# <name_description>...</name_description>
389# <pool_metadata_detected>...</pool_metadata_detected>
390# </SR>
391#
392# <SR>...</SR>
393# </SRlist>
394#
395# Arguments:
396# VGs: a dictionary containing the SR UUID to device list mappings
397# prefix: the prefix that if prefixes the SR UUID the VG is produced
398# includeMetadata (optional): include additional information
399def srlist_toxml(VGs, prefix, includeMetadata=False):
400 dom = xml.dom.minidom.Document()
401 element = dom.createElement("SRlist")
402 dom.appendChild(element)
404 for val in VGs:
405 entry = dom.createElement('SR')
406 element.appendChild(entry)
408 subentry = dom.createElement("UUID")
409 entry.appendChild(subentry)
410 textnode = dom.createTextNode(val)
411 subentry.appendChild(textnode)
413 subentry = dom.createElement("Devlist")
414 entry.appendChild(subentry)
415 textnode = dom.createTextNode(VGs[val])
416 subentry.appendChild(textnode)
418 subentry = dom.createElement("size")
419 entry.appendChild(subentry)
420 size = str(_getVGstats(prefix + val)['physical_size'])
421 textnode = dom.createTextNode(size)
422 subentry.appendChild(textnode)
424 if includeMetadata:
425 metadataVDI = None
427 # add SR name_label
428 mdpath = os.path.join(VG_LOCATION, VG_PREFIX + val)
429 mdpath = os.path.join(mdpath, MDVOLUME_NAME)
430 try:
431 mgtVolActivated = False
432 if not os.path.exists(mdpath):
433 # probe happens out of band with attach so this volume
434 # may not have been activated at this point
435 lvmCache = lvmcache.LVMCache(VG_PREFIX + val)
436 lvmCache.activateNoRefcount(MDVOLUME_NAME)
437 mgtVolActivated = True
439 sr_metadata = \
440 srmetadata.LVMMetadataHandler(mdpath, \
441 False).getMetadata()[0]
442 subentry = dom.createElement("name_label")
443 entry.appendChild(subentry)
444 textnode = dom.createTextNode(sr_metadata[srmetadata.NAME_LABEL_TAG])
445 subentry.appendChild(textnode)
447 # add SR description
448 subentry = dom.createElement("name_description")
449 entry.appendChild(subentry)
450 textnode = dom.createTextNode(sr_metadata[srmetadata.NAME_DESCRIPTION_TAG])
451 subentry.appendChild(textnode)
453 # add metadata VDI UUID
454 metadataVDI = srmetadata.LVMMetadataHandler(mdpath, \
455 False).findMetadataVDI()
456 subentry = dom.createElement("pool_metadata_detected")
457 entry.appendChild(subentry)
458 if metadataVDI is not None:
459 subentry.appendChild(dom.createTextNode("true"))
460 else:
461 subentry.appendChild(dom.createTextNode("false"))
462 finally:
463 if mgtVolActivated:
464 # deactivate only if we activated it
465 lvmCache.deactivateNoRefcount(MDVOLUME_NAME)
467 return dom.toprettyxml()
470def _openExclusive(dev, retry):
471 try:
472 return os.open("%s" % dev, os.O_RDWR | os.O_EXCL)
473 except OSError as ose:
474 opened_by = ''
475 if ose.errno == 16:
476 if retry:
477 util.SMlog('Device %s is busy, settle and one shot retry' %
478 dev)
479 util.pread2(['/usr/sbin/udevadm', 'settle'])
480 return _openExclusive(dev, False)
481 else:
482 util.SMlog('Device %s is busy after retry' % dev)
484 util.SMlog('Opening device %s failed with %d' % (dev, ose.errno))
485 raise xs_errors.XenError(
486 'SRInUse', opterr=('Device %s in use, please check your existing '
487 + 'SRs for an instance of this device') % dev)
490def createVG(root, vgname):
491 systemroot = util.getrootdev()
492 rootdev = root.split(',')[0]
494 # Create PVs for each device
495 for dev in root.split(','):
496 if dev in [systemroot, '%s1' % systemroot, '%s2' % systemroot]:
497 raise xs_errors.XenError('Rootdev', \
498 opterr=('Device %s contains core system files, ' \
499 + 'please use another device') % dev)
500 if not os.path.exists(dev):
501 raise xs_errors.XenError('InvalidDev', \
502 opterr=('Device %s does not exist') % dev)
504 f = _openExclusive(dev, True)
505 os.close(f)
506 try:
507 # Overwrite the disk header, try direct IO first
508 cmd = [util.CMD_DD, "if=/dev/zero", "of=%s" % dev, "bs=1M",
509 "count=10", "oflag=direct"]
510 util.pread2(cmd)
511 except util.CommandException as inst:
512 if inst.code == errno.EPERM:
513 try:
514 # Overwrite the disk header, try normal IO
515 cmd = [util.CMD_DD, "if=/dev/zero", "of=%s" % dev,
516 "bs=1M", "count=10"]
517 util.pread2(cmd)
518 except util.CommandException as inst:
519 raise xs_errors.XenError('LVMWrite', \
520 opterr='device %s' % dev)
521 else:
522 raise xs_errors.XenError('LVMWrite', \
523 opterr='device %s' % dev)
525 if not (dev == rootdev):
526 try:
527 cmd_lvm([CMD_PVCREATE, "-ff", "-y", "--metadatasize", "10M", dev])
528 except util.CommandException as inst:
529 raise xs_errors.XenError('LVMPartCreate',
530 opterr='error is %d' % inst.code)
532 # Create VG on first device
533 try:
534 cmd_lvm([CMD_VGCREATE, "--metadatasize", "10M", vgname, rootdev])
535 except:
536 raise xs_errors.XenError('LVMGroupCreate')
538 # Then add any additional devs into the VG
539 for dev in root.split(',')[1:]:
540 try:
541 cmd_lvm([CMD_VGEXTEND, vgname, dev])
542 except util.CommandException as inst:
543 # One of the PV args failed, delete SR
544 try:
545 cmd_lvm([CMD_VGREMOVE, vgname])
546 except:
547 pass
548 raise xs_errors.XenError('LVMGroupCreate')
550 try:
551 cmd_lvm([CMD_VGCHANGE, "-an", vgname])
552 except util.CommandException as inst:
553 raise xs_errors.XenError('LVMUnMount', opterr='errno is %d' % inst.code)
555 # End block
557def removeVG(root, vgname):
558 # Check PVs match VG
559 try:
560 for dev in root.split(','):
561 txt = cmd_lvm([CMD_PVS, dev])
562 if txt.find(vgname) == -1:
563 raise xs_errors.XenError('LVMNoVolume', \
564 opterr='volume is %s' % vgname)
565 except util.CommandException as inst:
566 raise xs_errors.XenError('PVSfailed', \
567 opterr='error is %d' % inst.code)
569 try:
570 cmd_lvm([CMD_VGREMOVE, vgname])
572 for dev in root.split(','):
573 cmd_lvm([CMD_PVREMOVE, dev])
574 except util.CommandException as inst:
575 raise xs_errors.XenError('LVMDelete', \
576 opterr='errno is %d' % inst.code)
579def resizePV(dev):
580 try:
581 cmd_lvm([CMD_PVRESIZE, dev])
582 except util.CommandException as inst:
583 util.SMlog("Failed to grow the PV, non-fatal")
586def setActiveVG(path, active):
587 "activate or deactivate VG 'path'"
588 val = "n"
589 if active:
590 val = "y"
591 text = cmd_lvm([CMD_VGCHANGE, "-a" + val, path])
594@lvmretry
595def create(name, size, vgname, tag=None, size_in_percentage=None):
596 if size_in_percentage:
597 cmd = [CMD_LVCREATE, "-n", name, "-l", size_in_percentage, vgname]
598 else:
599 size_mb = size // (1024 * 1024)
600 cmd = [CMD_LVCREATE, "-n", name, "-L", str(size_mb), vgname]
601 if tag:
602 cmd.extend(["--addtag", tag])
604 cmd.extend(['-W', 'n'])
605 cmd_lvm(cmd)
608def remove(path, config_param=None):
609 # see deactivateNoRefcount()
610 for i in range(LVM_FAIL_RETRIES): 610 ↛ 618line 610 didn't jump to line 618, because the loop on line 610 didn't complete
611 try:
612 _remove(path, config_param)
613 break
614 except util.CommandException as e:
615 if i >= LVM_FAIL_RETRIES - 1:
616 raise
617 util.SMlog("*** lvremove failed on attempt #%d" % i)
618 _lvmBugCleanup(path)
621@lvmretry
622def _remove(path, config_param=None):
623 CONFIG_TAG = "--config"
624 cmd = [CMD_LVREMOVE, "-f", path]
625 if config_param:
626 cmd.extend([CONFIG_TAG, "devices{" + config_param + "}"])
627 ret = cmd_lvm(cmd)
630@lvmretry
631def rename(path, newName):
632 cmd_lvm([CMD_LVRENAME, path, newName], pread_func=util.pread)
635@lvmretry
636def setReadonly(path, readonly):
637 val = "r"
638 if not readonly:
639 val += "w"
640 ret = cmd_lvm([CMD_LVCHANGE, path, "-p", val], pread_func=util.pread)
643def exists(path):
644 (rc, stdout, stderr) = cmd_lvm([CMD_LVS, "--noheadings", path], pread_func=util.doexec)
645 return rc == 0
648@lvmretry
649def setSize(path, size, confirm):
650 sizeMB = size // (1024 * 1024)
651 if confirm:
652 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], util.pread3, "y\n")
653 else:
654 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], pread_func=util.pread)
657@lvmretry
658def setHidden(path, hidden=True):
659 opt = "--addtag"
660 if not hidden:
661 opt = "--deltag"
662 cmd_lvm([CMD_LVCHANGE, opt, LV_TAG_HIDDEN, path])
665@lvmretry
666def _activate(path):
667 cmd = [CMD_LVCHANGE, "-ay", path]
668 cmd_lvm(cmd)
669 if not _checkActive(path):
670 raise util.CommandException(-1, str(cmd), "LV not activated")
673def activateNoRefcount(path, refresh):
674 _activate(path)
675 if refresh: 675 ↛ 677line 675 didn't jump to line 677, because the condition on line 675 was never true
676 # Override slave mode lvm.conf for this command
677 os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF
678 text = cmd_lvm([CMD_LVCHANGE, "--refresh", path])
679 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
680 cmd = [CMD_DMSETUP, "table", mapperDevice]
681 ret = util.pread(cmd)
682 util.SMlog("DM table for %s: %s" % (path, ret.strip()))
683 # Restore slave mode lvm.conf
684 os.environ['LVM_SYSTEM_DIR'] = DEF_LVM_CONF
687def deactivateNoRefcount(path):
688 # LVM has a bug where if an "lvs" command happens to run at the same time
689 # as "lvchange -an", it might hold the device in use and cause "lvchange
690 # -an" to fail. Thus, we need to retry if "lvchange -an" fails. Worse yet,
691 # the race could lead to "lvchange -an" starting to deactivate (removing
692 # the symlink), failing to "dmsetup remove" the device, and still returning
693 # success. Thus, we need to check for the device mapper file existence if
694 # "lvchange -an" returns success.
695 for i in range(LVM_FAIL_RETRIES): 695 ↛ 703line 695 didn't jump to line 703, because the loop on line 695 didn't complete
696 try:
697 _deactivate(path)
698 break
699 except util.CommandException:
700 if i >= LVM_FAIL_RETRIES - 1:
701 raise
702 util.SMlog("*** lvchange -an failed on attempt #%d" % i)
703 _lvmBugCleanup(path)
706@lvmretry
707def _deactivate(path):
708 text = cmd_lvm([CMD_LVCHANGE, "-an", path])
711def _checkActive(path):
712 if util.pathexists(path):
713 return True
715 util.SMlog("_checkActive: %s does not exist!" % path)
716 symlinkExists = os.path.lexists(path)
717 util.SMlog("_checkActive: symlink exists: %s" % symlinkExists)
719 mapperDeviceExists = False
720 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
721 cmd = [CMD_DMSETUP, "status", mapperDevice]
722 try:
723 ret = util.pread2(cmd)
724 mapperDeviceExists = True
725 util.SMlog("_checkActive: %s: %s" % (mapperDevice, ret))
726 except util.CommandException:
727 util.SMlog("_checkActive: device %s does not exist" % mapperDevice)
729 mapperPath = "/dev/mapper/" + mapperDevice
730 mapperPathExists = util.pathexists(mapperPath)
731 util.SMlog("_checkActive: path %s exists: %s" % \
732 (mapperPath, mapperPathExists))
734 if mapperDeviceExists and mapperPathExists and not symlinkExists: 734 ↛ 736line 734 didn't jump to line 736, because the condition on line 734 was never true
735 # we can fix this situation manually here
736 try:
737 util.SMlog("_checkActive: attempt to create the symlink manually.")
738 os.symlink(mapperPath, path)
739 except OSError as e:
740 util.SMlog("ERROR: failed to symlink!")
741 if e.errno != errno.EEXIST:
742 raise
743 if util.pathexists(path):
744 util.SMlog("_checkActive: created the symlink manually")
745 return True
747 return False
750def _lvmBugCleanup(path):
751 # the device should not exist at this point. If it does, this was an LVM
752 # bug, and we manually clean up after LVM here
753 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
754 mapperPath = "/dev/mapper/" + mapperDevice
756 nodeExists = False
757 cmd_st = [CMD_DMSETUP, "status", mapperDevice]
758 cmd_rm = [CMD_DMSETUP, "remove", mapperDevice]
759 cmd_rf = [CMD_DMSETUP, "remove", mapperDevice, "--force"]
761 try:
762 util.pread(cmd_st, expect_rc=1)
763 except util.CommandException as e:
764 if e.code == 0: 764 ↛ 767line 764 didn't jump to line 767, because the condition on line 764 was never false
765 nodeExists = True
767 if not util.pathexists(mapperPath) and not nodeExists:
768 return
770 util.SMlog("_lvmBugCleanup: seeing dm file %s" % mapperPath)
772 # destroy the dm device
773 if nodeExists: 773 ↛ 798line 773 didn't jump to line 798, because the condition on line 773 was never false
774 util.SMlog("_lvmBugCleanup: removing dm device %s" % mapperDevice)
775 for i in range(LVM_FAIL_RETRIES): 775 ↛ 798line 775 didn't jump to line 798, because the loop on line 775 didn't complete
776 try:
777 util.pread2(cmd_rm)
778 break
779 except util.CommandException as e:
780 if i < LVM_FAIL_RETRIES - 1:
781 util.SMlog("Failed on try %d, retrying" % i)
782 try:
783 util.pread(cmd_st, expect_rc=1)
784 util.SMlog("_lvmBugCleanup: dm device {}"
785 " removed".format(mapperDevice)
786 )
787 break
788 except:
789 cmd_rm = cmd_rf
790 time.sleep(1)
791 else:
792 # make sure the symlink is still there for consistency
793 if not os.path.lexists(path): 793 ↛ 796line 793 didn't jump to line 796, because the condition on line 793 was never false
794 os.symlink(mapperPath, path)
795 util.SMlog("_lvmBugCleanup: restored symlink %s" % path)
796 raise e
798 if util.pathexists(mapperPath):
799 os.unlink(mapperPath)
800 util.SMlog("_lvmBugCleanup: deleted devmapper file %s" % mapperPath)
802 # delete the symlink
803 if os.path.lexists(path):
804 os.unlink(path)
805 util.SMlog("_lvmBugCleanup: deleted symlink %s" % path)
808# mdpath is of format /dev/VG-SR-UUID/MGT
809# or in other words /VG_LOCATION/VG_PREFIXSR-UUID/MDVOLUME_NAME
810def ensurePathExists(mdpath):
811 if not os.path.exists(mdpath):
812 vgname = mdpath.split('/')[2]
813 lvmCache = lvmcache.LVMCache(vgname)
814 lvmCache.activateNoRefcount(MDVOLUME_NAME)
817def removeDevMapperEntry(path, strict=True):
818 try:
819 # remove devmapper entry using dmsetup
820 cmd = [CMD_DMSETUP, "remove", path]
821 cmd_lvm(cmd)
822 return True
823 except Exception as e:
824 if not strict:
825 cmd = [CMD_DMSETUP, "status", path]
826 try:
827 util.pread(cmd, expect_rc=1)
828 return True
829 except:
830 pass # Continuining will fail and log the right way
831 ret = util.pread2(["lsof", path])
832 util.SMlog("removeDevMapperEntry: dmsetup remove failed for file %s " \
833 "with error %s, and lsof ret is %s." % (path, str(e), ret))
834 return False