Coverage for drivers/lvutil.py : 48%
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 traceback
20import re
21import os
22import errno
23import time
25from fairlock import Fairlock
26import util
27import xs_errors
28import xml.dom.minidom
29from constants import EXT_PREFIX, VG_LOCATION, VG_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
113LVM_RETRY_ERRORS = [
114 "Incorrect checksum in metadata area header"
115]
117def calcSizeLV(size: int) -> int:
118 return util.roundup(LVM_SIZE_INCREMENT, size)
121def lvmretry(func):
122 def check_exception(exception):
123 retry = False
124 for error in LVM_RETRY_ERRORS:
125 if error in exception.reason:
126 retry = True
127 return retry
129 def decorated(*args, **kwargs):
130 for i in range(LVM_FAIL_RETRIES): 130 ↛ exitline 130 didn't return from function 'decorated', because the loop on line 130 didn't complete
131 try:
132 return func(*args, **kwargs)
133 except util.CommandException as ce:
134 retry = check_exception(ce)
135 if not retry or (i == LVM_FAIL_RETRIES - 1):
136 raise
138 time.sleep(1)
140 decorated.__name__ = func.__name__
141 return decorated
144def cmd_lvm(cmd, pread_func=util.pread2, *args):
145 """ Construct and run the appropriate lvm command.
147 For PV commands, the full path to the device is required.
149 Input:
150 cmd -- (list) lvm command
151 cmd[0] -- (str) lvm command name
152 cmd[1:] -- (str) lvm command parameters
154 pread_func -- (function) the flavor of util.pread to use
155 to execute the lvm command
156 Default: util.pread2()
158 *args -- extra arguments passed to cmd_lvm will be passed
159 to 'pread_func'
161 Return:
162 stdout -- (str) stdout after running the lvm command.
164 Raise:
165 util.CommandException
166 """
168 if type(cmd) is not list:
169 util.SMlog("CMD_LVM: Argument 'cmd' not of type 'list'")
170 return None
171 if not len(cmd):
172 util.SMlog("CMD_LVM: 'cmd' list is empty")
173 return None
175 lvm_cmd, lvm_args = cmd[0], cmd[1:]
177 if lvm_cmd not in LVM_COMMANDS:
178 util.SMlog("CMD_LVM: '{}' is not a valid lvm command".format(lvm_cmd))
179 return None
181 for arg in lvm_args:
182 if not util.is_string(arg):
183 util.SMlog("CMD_LVM: Not all lvm arguments are of type 'str'")
184 return None
186 with Fairlock("devicemapper"):
187 start_time = time.time()
188 stdout = pread_func([os.path.join(LVM_BIN, lvm_cmd)] + lvm_args, * args)
189 end_time = time.time()
191 if (end_time - start_time > MAX_OPERATION_DURATION):
192 util.SMlog("***** Long LVM call of '%s' took %s" % (lvm_cmd, (end_time - start_time)))
194 return stdout
197class LVInfo:
198 name = ""
199 size = 0
200 active = False
201 open = False
202 hidden = False
203 readonly = False
205 def __init__(self, name):
206 self.name = name
208 def toString(self):
209 return "%s, size=%d, active=%s, open=%s, hidden=%s, ro=%s" % \
210 (self.name, self.size, self.active, self.open, self.hidden, \
211 self.readonly)
214def _checkVG(vgname):
215 try:
216 cmd_lvm([CMD_VGS, "--readonly", vgname])
217 return True
218 except:
219 return False
222def _checkPV(pvname):
223 try:
224 cmd_lvm([CMD_PVS, pvname])
225 return True
226 except:
227 return False
230def _checkLV(path):
231 try:
232 cmd_lvm([CMD_LVDISPLAY, path])
233 return True
234 except:
235 return False
238def _getLVsize(path):
239 try:
240 lines = cmd_lvm([CMD_LVDISPLAY, "-c", path]).split(':')
241 return int(lines[6]) * 512
242 except:
243 raise xs_errors.XenError('VDIUnavailable', \
244 opterr='no such VDI %s' % path)
247def _getVGstats(vgname):
248 try:
249 text = cmd_lvm([CMD_VGS, "--noheadings", "--nosuffix",
250 "--units", "b", vgname],
251 pread_func=util.pread).split()
252 size = int(text[5])
253 freespace = int(text[6])
254 utilisation = size - freespace
255 stats = {}
256 stats['physical_size'] = size
257 stats['physical_utilisation'] = utilisation
258 stats['freespace'] = freespace
259 return stats
260 except util.CommandException as inst:
261 raise xs_errors.XenError('VDILoad', \
262 opterr='rvgstats failed error is %d' % inst.code)
263 except ValueError:
264 raise xs_errors.XenError('VDILoad', opterr='rvgstats failed')
267def _getPVstats(dev):
268 try:
269 text = cmd_lvm([CMD_PVS, "--noheadings", "--nosuffix",
270 "--units", "b", dev],
271 pread_func=util.pread).split()
272 size = int(text[4])
273 freespace = int(text[5])
274 utilisation = size - freespace
275 stats = {}
276 stats['physical_size'] = size
277 stats['physical_utilisation'] = utilisation
278 stats['freespace'] = freespace
279 return stats
280 except util.CommandException as inst:
281 raise xs_errors.XenError('VDILoad', \
282 opterr='pvstats failed error is %d' % inst.code)
283 except ValueError:
284 raise xs_errors.XenError('VDILoad', opterr='rvgstats failed')
287# Retrieves the UUID of the SR that corresponds to the specified Physical
288# Volume (pvname). Each element in prefix_list is checked whether it is a
289# prefix of Volume Groups that correspond to the specified PV. If so, the
290# prefix is stripped from the matched VG name and the remainder is returned
291# (effectively the SR UUID). If no match if found, the empty string is
292# returned.
293# E.g.
294# PV VG Fmt Attr PSize PFree
295# /dev/sda4 VG_XenStorage-some-hex-value lvm2 a- 224.74G 223.73G
296# will return "some-hex-value".
297def _get_sr_uuid(pvname, prefix_list):
298 try:
299 return match_VG(cmd_lvm([CMD_PVS, "--noheadings",
300 "-o", "vg_name", pvname]), prefix_list)
301 except:
302 return ""
305# Retrieves the names of the Physical Volumes which are used by the specified
306# Volume Group
307# e.g.
308# PV VG Fmt Attr PSize PFree
309# /dev/sda4 VG_XenStorage-some-hex-value lvm2 a- 224.74G 223.73G
310# will return "/dev/sda4" when given the argument "VG_XenStorage-some-hex-value".
311def get_pv_for_vg(vgname):
312 try:
313 result = cmd_lvm([CMD_PVS, "--noheadings",
314 '-S', 'vg_name=%s' % vgname, '-o', 'name'])
315 return [x.strip() for x in result.splitlines()]
316 except util.CommandException:
317 return []
320# Tries to match any prefix contained in prefix_list in s. If matched, the
321# remainder string is returned, else the empty string is returned. E.g. if s is
322# "VG_XenStorage-some-hex-value" and prefix_list contains "VG_XenStorage-",
323# "some-hex-value" is returned.
324#
325# TODO Items in prefix_list are expected to be found at the beginning of the
326# target string, though if any of them is found inside it a match will be
327# produced. This could be remedied by making the regular expression more
328# specific.
329def match_VG(s, prefix_list):
330 for val in prefix_list:
331 regex = re.compile(val)
332 if regex.search(s, 0):
333 return s.split(val)[1]
334 return ""
337# Retrieves the devices an SR is composed of. A dictionary is returned, indexed
338# by the SR UUID, where each SR UUID is mapped to a comma-separated list of
339# devices. Exceptions are ignored.
340def scan_srlist(prefix, root):
341 VGs = {}
342 for dev in root.split(','):
343 try:
344 sr_uuid = _get_sr_uuid(dev, [prefix]).strip(' \n')
345 if len(sr_uuid):
346 if sr_uuid in VGs:
347 VGs[sr_uuid] += ",%s" % dev
348 else:
349 VGs[sr_uuid] = dev
350 except Exception as e:
351 util.logException("exception (ignored): %s" % e)
352 continue
353 return VGs
356# Converts an SR list to an XML document with the following structure:
357# <SRlist>
358# <SR>
359# <UUID>...</UUID>
360# <Devlist>...</Devlist>
361# <size>...</size>
362# <!-- If includeMetadata is set to True, the following additional nodes
363# are supplied. -->
364# <name_label>...</name_label>
365# <name_description>...</name_description>
366# <pool_metadata_detected>...</pool_metadata_detected>
367# </SR>
368#
369# <SR>...</SR>
370# </SRlist>
371#
372# Arguments:
373# VGs: a dictionary containing the SR UUID to device list mappings
374# prefix: the prefix that if prefixes the SR UUID the VG is produced
375# includeMetadata (optional): include additional information
376def srlist_toxml(VGs, prefix, includeMetadata=False):
377 dom = xml.dom.minidom.Document()
378 element = dom.createElement("SRlist")
379 dom.appendChild(element)
381 for val in VGs:
382 entry = dom.createElement('SR')
383 element.appendChild(entry)
385 subentry = dom.createElement("UUID")
386 entry.appendChild(subentry)
387 textnode = dom.createTextNode(val)
388 subentry.appendChild(textnode)
390 subentry = dom.createElement("Devlist")
391 entry.appendChild(subentry)
392 textnode = dom.createTextNode(VGs[val])
393 subentry.appendChild(textnode)
395 subentry = dom.createElement("size")
396 entry.appendChild(subentry)
397 size = str(_getVGstats(prefix + val)['physical_size'])
398 textnode = dom.createTextNode(size)
399 subentry.appendChild(textnode)
401 if includeMetadata:
402 metadataVDI = None
404 # add SR name_label
405 mdpath = os.path.join(VG_LOCATION, VG_PREFIX + val)
406 mdpath = os.path.join(mdpath, MDVOLUME_NAME)
407 mgtVolActivated = False
408 try:
409 if not os.path.exists(mdpath):
410 # probe happens out of band with attach so this volume
411 # may not have been activated at this point
412 lvmCache = lvmcache.LVMCache(VG_PREFIX + val)
413 lvmCache.activateNoRefcount(MDVOLUME_NAME)
414 mgtVolActivated = True
416 sr_metadata = \
417 srmetadata.LVMMetadataHandler(mdpath, \
418 False).getMetadata()[0]
419 subentry = dom.createElement("name_label")
420 entry.appendChild(subentry)
421 textnode = dom.createTextNode(sr_metadata[srmetadata.NAME_LABEL_TAG])
422 subentry.appendChild(textnode)
424 # add SR description
425 subentry = dom.createElement("name_description")
426 entry.appendChild(subentry)
427 textnode = dom.createTextNode(sr_metadata[srmetadata.NAME_DESCRIPTION_TAG])
428 subentry.appendChild(textnode)
430 # add metadata VDI UUID
431 metadataVDI = srmetadata.LVMMetadataHandler(mdpath, \
432 False).findMetadataVDI()
433 subentry = dom.createElement("pool_metadata_detected")
434 entry.appendChild(subentry)
435 if metadataVDI is not None:
436 subentry.appendChild(dom.createTextNode("true"))
437 else:
438 subentry.appendChild(dom.createTextNode("false"))
439 finally:
440 if mgtVolActivated:
441 # deactivate only if we activated it
442 lvmCache.deactivateNoRefcount(MDVOLUME_NAME)
444 return dom.toprettyxml()
447def _openExclusive(dev, retry):
448 try:
449 return os.open("%s" % dev, os.O_RDWR | os.O_EXCL)
450 except OSError as ose:
451 opened_by = ''
452 if ose.errno == 16:
453 if retry:
454 util.SMlog('Device %s is busy, settle and one shot retry' %
455 dev)
456 util.pread2(['/usr/sbin/udevadm', 'settle'])
457 return _openExclusive(dev, False)
458 else:
459 util.SMlog('Device %s is busy after retry' % dev)
461 util.SMlog('Opening device %s failed with %d' % (dev, ose.errno))
462 raise xs_errors.XenError(
463 'SRInUse', opterr=('Device %s in use, please check your existing '
464 + 'SRs for an instance of this device') % dev)
467def createVG(root, vgname):
468 systemroot = util.getrootdev()
469 rootdev = root.split(',')[0]
471 # Create PVs for each device
472 for dev in root.split(','):
473 if dev in [systemroot, '%s1' % systemroot, '%s2' % systemroot]:
474 raise xs_errors.XenError('Rootdev', \
475 opterr=('Device %s contains core system files, ' \
476 + 'please use another device') % dev)
477 if not os.path.exists(dev):
478 raise xs_errors.XenError('InvalidDev', \
479 opterr=('Device %s does not exist') % dev)
481 f = _openExclusive(dev, True)
482 os.close(f)
484 # Wipe any fs signature
485 try:
486 util.wipefs(dev)
487 except util.CommandException as inst:
488 raise xs_errors.XenError('WipefsFailure', opterr='device %s' % dev) # from inst
490 if not (dev == rootdev):
491 try:
492 cmd_lvm([CMD_PVCREATE, "-ff", "-y", "--metadatasize", "10M", dev])
493 except util.CommandException as inst:
494 raise xs_errors.XenError('LVMPartCreate',
495 opterr='error is %d' % inst.code)
497 # Create VG on first device
498 try:
499 cmd_lvm([CMD_VGCREATE, "--metadatasize", "10M", vgname, rootdev])
500 except:
501 raise xs_errors.XenError('LVMGroupCreate')
503 # Then add any additional devs into the VG
504 for dev in root.split(',')[1:]:
505 try:
506 cmd_lvm([CMD_VGEXTEND, vgname, dev])
507 except util.CommandException as inst:
508 # One of the PV args failed, delete SR
509 try:
510 cmd_lvm([CMD_VGREMOVE, vgname])
511 except:
512 pass
513 raise xs_errors.XenError('LVMGroupCreate')
515 try:
516 cmd_lvm([CMD_VGCHANGE, "-an", vgname])
517 except util.CommandException as inst:
518 raise xs_errors.XenError('LVMUnMount', opterr='errno is %d' % inst.code)
520 # End block
522def getPVsInVG(vgname):
523 # Get PVs in a specific VG
524 pvs_ret = cmd_lvm([CMD_PVS, '--separator', ' ', '--noheadings', '-o', 'pv_name,vg_name'])
526 # Parse each line to extract PV and VG information
527 # No need to handle exceptions here, return empty list if any error
528 pvs_in_vg = []
529 lines = pvs_ret.strip().split('\n')
530 for line in lines:
531 # To avoid invalid return format
532 parts = line.split()
533 if len(parts) != 2:
534 util.SMlog("Warning: Invalid or empty line in pvs output: %s" % line)
535 continue
536 pv, vg = parts
537 if vg == vgname:
538 pvs_in_vg.append(pv)
540 util.SMlog("PVs in VG %s: %s" % (vgname, pvs_in_vg))
541 return pvs_in_vg
543def removeVG(root, vgname):
544 # Check PVs match VG
545 try:
546 for dev in root.split(','):
547 txt = cmd_lvm([CMD_PVS, dev])
548 if txt.find(vgname) == -1:
549 raise xs_errors.XenError('LVMNoVolume', \
550 opterr='volume is %s' % vgname)
551 except util.CommandException as inst:
552 raise xs_errors.XenError('PVSfailed', \
553 opterr='error is %d' % inst.code)
555 try:
556 # Get PVs in VG before removing the VG
557 devs_in_vg = getPVsInVG(vgname)
558 cmd_lvm([CMD_VGREMOVE, vgname])
560 for dev in devs_in_vg:
561 cmd_lvm([CMD_PVREMOVE, dev])
562 except util.CommandException as inst:
563 raise xs_errors.XenError('LVMDelete', \
564 opterr='errno is %d' % inst.code)
567def resizePV(dev):
568 try:
569 cmd_lvm([CMD_PVRESIZE, dev])
570 except util.CommandException as inst:
571 util.SMlog("Failed to grow the PV, non-fatal")
574def setActiveVG(path, active, config=None):
575 "activate or deactivate VG 'path'"
576 val = "n"
577 if active:
578 val = "y"
579 cmd = [CMD_VGCHANGE, "-a" + val, path]
580 if config:
581 cmd.append("--config")
582 cmd.append(config)
583 cmd_lvm(cmd)
586@lvmretry
587def create(name, size, vgname, tag=None, size_in_percentage=None):
588 if size_in_percentage:
589 cmd = [CMD_LVCREATE, "-n", name, "-l", size_in_percentage, vgname]
590 else:
591 size_mb = size // (1024 * 1024)
592 cmd = [CMD_LVCREATE, "-n", name, "-L", str(size_mb), vgname]
593 if tag:
594 cmd.extend(["--addtag", tag])
596 cmd.extend(['-W', 'n'])
597 cmd_lvm(cmd)
600def remove(path, config_param=None):
601 # see deactivateNoRefcount()
602 for i in range(LVM_FAIL_RETRIES): 602 ↛ 610line 602 didn't jump to line 610, because the loop on line 602 didn't complete
603 try:
604 _remove(path, config_param)
605 break
606 except util.CommandException as e:
607 if i >= LVM_FAIL_RETRIES - 1:
608 raise
609 util.SMlog("*** lvremove failed on attempt #%d" % i)
610 _lvmBugCleanup(path)
613@lvmretry
614def _remove(path, config_param=None):
615 CONFIG_TAG = "--config"
616 cmd = [CMD_LVREMOVE, "-f", path]
617 if config_param:
618 cmd.extend([CONFIG_TAG, "devices{" + config_param + "}"])
619 ret = cmd_lvm(cmd)
622@lvmretry
623def rename(path, newName):
624 cmd_lvm([CMD_LVRENAME, path, newName], pread_func=util.pread)
627@lvmretry
628def setReadonly(path, readonly):
629 val = "r"
630 if not readonly:
631 val += "w"
632 ret = cmd_lvm([CMD_LVCHANGE, path, "-p", val], pread_func=util.pread)
635def exists(path):
636 (rc, stdout, stderr) = cmd_lvm([CMD_LVS, "--noheadings", path], pread_func=util.doexec)
637 return rc == 0
640@lvmretry
641def setSize(path, size, confirm):
642 sizeMB = size // (1024 * 1024)
643 if confirm:
644 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], util.pread3, "y\n")
645 else:
646 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], pread_func=util.pread)
649@lvmretry
650def setHidden(path, hidden=True):
651 opt = "--addtag"
652 if not hidden:
653 opt = "--deltag"
654 cmd_lvm([CMD_LVCHANGE, opt, LV_TAG_HIDDEN, path])
657@lvmretry
658def _activate(path):
659 cmd = [CMD_LVCHANGE, "-ay", path]
660 cmd_lvm(cmd)
661 if not _checkActive(path):
662 raise util.CommandException(-1, str(cmd), "LV not activated")
665def activateNoRefcount(path, refresh):
666 _activate(path)
667 if refresh: 667 ↛ 669line 667 didn't jump to line 669, because the condition on line 667 was never true
668 # Override slave mode lvm.conf for this command
669 os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF
670 text = cmd_lvm([CMD_LVCHANGE, "--refresh", path])
671 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
672 cmd = [CMD_DMSETUP, "table", mapperDevice]
673 with Fairlock("devicemapper"):
674 ret = util.pread(cmd)
675 util.SMlog("DM table for %s: %s" % (path, ret.strip()))
676 # Restore slave mode lvm.conf
677 os.environ['LVM_SYSTEM_DIR'] = DEF_LVM_CONF
680def deactivateNoRefcount(path):
681 # LVM has a bug where if an "lvs" command happens to run at the same time
682 # as "lvchange -an", it might hold the device in use and cause "lvchange
683 # -an" to fail. Thus, we need to retry if "lvchange -an" fails. Worse yet,
684 # the race could lead to "lvchange -an" starting to deactivate (removing
685 # the symlink), failing to "dmsetup remove" the device, and still returning
686 # success. Thus, we need to check for the device mapper file existence if
687 # "lvchange -an" returns success.
688 for i in range(LVM_FAIL_RETRIES): 688 ↛ 696line 688 didn't jump to line 696, because the loop on line 688 didn't complete
689 try:
690 _deactivate(path)
691 break
692 except util.CommandException:
693 if i >= LVM_FAIL_RETRIES - 1:
694 raise
695 util.SMlog("*** lvchange -an failed on attempt #%d" % i)
696 _lvmBugCleanup(path)
699@lvmretry
700def _deactivate(path):
701 # Records what is using the LVM path in case there is an issue.
702 # In most cases this should be nothing.
703 try:
704 (rc, stdout, stderr) = util.doexec(['/usr/sbin/fuser', "-v", path])
705 util.SMlog(f"fuser {path} => {rc} / '{stdout}' / '{stderr}'")
706 except:
707 pass
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 with Fairlock("devicemapper"):
724 ret = util.pread2(cmd)
725 mapperDeviceExists = True
726 util.SMlog("_checkActive: %s: %s" % (mapperDevice, ret))
727 except util.CommandException:
728 util.SMlog("_checkActive: device %s does not exist" % mapperDevice)
730 mapperPath = "/dev/mapper/" + mapperDevice
731 mapperPathExists = util.pathexists(mapperPath)
732 util.SMlog("_checkActive: path %s exists: %s" % \
733 (mapperPath, mapperPathExists))
735 if mapperDeviceExists and mapperPathExists and not symlinkExists: 735 ↛ 737line 735 didn't jump to line 737, because the condition on line 735 was never true
736 # we can fix this situation manually here
737 try:
738 util.SMlog("_checkActive: attempt to create the symlink manually.")
739 os.symlink(mapperPath, path)
740 except OSError as e:
741 util.SMlog("ERROR: failed to symlink!")
742 if e.errno != errno.EEXIST:
743 raise
744 if util.pathexists(path):
745 util.SMlog("_checkActive: created the symlink manually")
746 return True
748 return False
751def _lvmBugCleanup(path):
752 # the device should not exist at this point. If it does, this was an LVM
753 # bug, and we manually clean up after LVM here
754 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
755 mapperPath = "/dev/mapper/" + mapperDevice
757 nodeExists = False
758 cmd_st = [CMD_DMSETUP, "status", mapperDevice]
759 cmd_rm = [CMD_DMSETUP, "remove", mapperDevice]
760 cmd_rf = [CMD_DMSETUP, "remove", mapperDevice, "--force"]
762 try:
763 with Fairlock("devicemapper"):
764 util.pread(cmd_st, expect_rc=1)
765 except util.CommandException as e:
766 if e.code == 0: 766 ↛ 769line 766 didn't jump to line 769, because the condition on line 766 was never false
767 nodeExists = True
769 if not util.pathexists(mapperPath) and not nodeExists:
770 return
772 util.SMlog("_lvmBugCleanup: seeing dm file %s" % mapperPath)
774 # destroy the dm device
775 if nodeExists: 775 ↛ 802line 775 didn't jump to line 802, because the condition on line 775 was never false
776 util.SMlog("_lvmBugCleanup: removing dm device %s" % mapperDevice)
777 for i in range(LVM_FAIL_RETRIES): 777 ↛ 802line 777 didn't jump to line 802, because the loop on line 777 didn't complete
778 try:
779 with Fairlock("devicemapper"):
780 util.pread2(cmd_rm)
781 break
782 except util.CommandException as e:
783 if i < LVM_FAIL_RETRIES - 1:
784 util.SMlog("Failed on try %d, retrying" % i)
785 try:
786 with Fairlock("devicemapper"):
787 util.pread(cmd_st, expect_rc=1)
788 util.SMlog("_lvmBugCleanup: dm device {}"
789 " removed".format(mapperDevice)
790 )
791 break
792 except:
793 cmd_rm = cmd_rf
794 time.sleep(1)
795 else:
796 # make sure the symlink is still there for consistency
797 if not os.path.lexists(path): 797 ↛ 800line 797 didn't jump to line 800, because the condition on line 797 was never false
798 os.symlink(mapperPath, path)
799 util.SMlog("_lvmBugCleanup: restored symlink %s" % path)
800 raise e
802 if util.pathexists(mapperPath):
803 os.unlink(mapperPath)
804 util.SMlog("_lvmBugCleanup: deleted devmapper file %s" % mapperPath)
806 # delete the symlink
807 if os.path.lexists(path):
808 os.unlink(path)
809 util.SMlog("_lvmBugCleanup: deleted symlink %s" % path)
812# mdpath is of format /dev/VG-SR-UUID/MGT
813# or in other words /VG_LOCATION/VG_PREFIXSR-UUID/MDVOLUME_NAME
814def ensurePathExists(mdpath):
815 if not os.path.exists(mdpath):
816 vgname = mdpath.split('/')[2]
817 lvmCache = lvmcache.LVMCache(vgname)
818 lvmCache.activateNoRefcount(MDVOLUME_NAME)
821def removeDevMapperEntry(path, strict=True):
822 try:
823 # remove devmapper entry using dmsetup
824 cmd = [CMD_DMSETUP, "remove", path]
825 cmd_lvm(cmd)
826 return True
827 except Exception as e:
828 if not strict:
829 cmd = [CMD_DMSETUP, "status", path]
830 try:
831 with Fairlock("devicemapper"):
832 util.pread(cmd, expect_rc=1)
833 return True
834 except:
835 pass # Continuining will fail and log the right way
836 ret = util.pread2(["lsof", path])
837 util.SMlog("removeDevMapperEntry: dmsetup remove failed for file %s " \
838 "with error %s, and lsof ret is %s." % (path, str(e), ret))
839 return False