Coverage for drivers/lvutil.py : 49%
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
25import scsiutil
26from fairlock import Fairlock
27import util
28import xs_errors
29import xml.dom.minidom
30from constants import EXT_PREFIX, VG_LOCATION, VG_PREFIX
31import lvmcache
32import srmetadata
34MDVOLUME_NAME = 'MGT'
35VDI_UUID_TAG_PREFIX = 'vdi_'
36LVM_BIN = os.path.isfile('/sbin/lvdisplay') and '/sbin' or '/usr/sbin'
37CMD_VGS = "vgs"
38CMD_VGCREATE = "vgcreate"
39CMD_VGREMOVE = "vgremove"
40CMD_VGCHANGE = "vgchange"
41CMD_VGEXTEND = "vgextend"
42CMD_PVS = "pvs"
43CMD_PVCREATE = "pvcreate"
44CMD_PVREMOVE = "pvremove"
45CMD_PVRESIZE = "pvresize"
46CMD_LVS = "lvs"
47CMD_LVDISPLAY = "lvdisplay"
48CMD_LVCREATE = "lvcreate"
49CMD_LVREMOVE = "lvremove"
50CMD_LVCHANGE = "lvchange"
51CMD_LVRENAME = "lvrename"
52CMD_LVRESIZE = "lvresize"
53CMD_LVEXTEND = "lvextend"
54CMD_DMSETUP = "/sbin/dmsetup"
56MAX_OPERATION_DURATION = 15
58LVM_SIZE_INCREMENT = 4 * 1024 * 1024
59LV_TAG_HIDDEN = "hidden"
60LVM_FAIL_RETRIES = 10
62MASTER_LVM_CONF = '/etc/lvm/master'
63DEF_LVM_CONF = '/etc/lvm'
65VG_COMMANDS = frozenset({CMD_VGS, CMD_VGCREATE, CMD_VGREMOVE, CMD_VGCHANGE,
66 CMD_VGEXTEND})
67PV_COMMANDS = frozenset({CMD_PVS, CMD_PVCREATE, CMD_PVREMOVE, CMD_PVRESIZE})
68LV_COMMANDS = frozenset({CMD_LVS, CMD_LVDISPLAY, CMD_LVCREATE, CMD_LVREMOVE,
69 CMD_LVCHANGE, CMD_LVRENAME, CMD_LVRESIZE,
70 CMD_LVEXTEND})
71DM_COMMANDS = frozenset({CMD_DMSETUP})
73LVM_COMMANDS = VG_COMMANDS.union(PV_COMMANDS, LV_COMMANDS, DM_COMMANDS)
75LVM_LOCK = 'lvm'
78def extract_vgname(str_in):
79 """Search for and return a VG name
81 Search 'str_in' for a substring in the form of 'VG_XenStorage-<UUID>'.
82 If there are more than one VG names, the first is returned.
84 Input:
85 str_in -- (str) string to search for a VG name
86 in the format specified above.
88 Return:
89 vgname -- if found -> (str)
90 if not found -> None
92 Raise:
93 TypeError
94 """
96 if not util.is_string(str_in):
97 raise TypeError("'str_in' not of type 'str'.")
99 i = str_in.find(VG_PREFIX)
100 prefix = VG_PREFIX
102 if i == -1:
103 i = str_in.find(EXT_PREFIX)
104 prefix = EXT_PREFIX
106 uuid_start = i + len(prefix)
107 re_obj = util.match_uuid(str_in[uuid_start:])
109 if i != -1 and re_obj:
110 return prefix + re_obj.group(0) # vgname
112 return None
114LVM_RETRY_ERRORS = [
115 "Incorrect checksum in metadata area header"
116]
118def calcSizeLV(size: int) -> int:
119 return util.roundup(LVM_SIZE_INCREMENT, size)
122def lvmretry(func):
123 def check_exception(exception):
124 retry = False
125 for error in LVM_RETRY_ERRORS:
126 if error in exception.reason:
127 retry = True
128 return retry
130 def decorated(*args, **kwargs):
131 for i in range(LVM_FAIL_RETRIES): 131 ↛ exitline 131 didn't return from function 'decorated', because the loop on line 131 didn't complete
132 try:
133 return func(*args, **kwargs)
134 except util.CommandException as ce:
135 retry = check_exception(ce)
136 if not retry or (i == LVM_FAIL_RETRIES - 1):
137 raise
139 time.sleep(1)
141 decorated.__name__ = func.__name__
142 return decorated
145def cmd_lvm(cmd, pread_func=util.pread2, *args):
146 """ Construct and run the appropriate lvm command.
148 For PV commands, the full path to the device is required.
150 Input:
151 cmd -- (list) lvm command
152 cmd[0] -- (str) lvm command name
153 cmd[1:] -- (str) lvm command parameters
155 pread_func -- (function) the flavor of util.pread to use
156 to execute the lvm command
157 Default: util.pread2()
159 *args -- extra arguments passed to cmd_lvm will be passed
160 to 'pread_func'
162 Return:
163 stdout -- (str) stdout after running the lvm command.
165 Raise:
166 util.CommandException
167 """
169 if type(cmd) is not list:
170 util.SMlog("CMD_LVM: Argument 'cmd' not of type 'list'")
171 return None
172 if not len(cmd):
173 util.SMlog("CMD_LVM: 'cmd' list is empty")
174 return None
176 lvm_cmd, lvm_args = cmd[0], cmd[1:]
178 if lvm_cmd not in LVM_COMMANDS:
179 util.SMlog("CMD_LVM: '{}' is not a valid lvm command".format(lvm_cmd))
180 return None
182 for arg in lvm_args:
183 if not util.is_string(arg):
184 util.SMlog("CMD_LVM: Not all lvm arguments are of type 'str'")
185 return None
187 with Fairlock("devicemapper"):
188 start_time = time.time()
189 stdout = pread_func([os.path.join(LVM_BIN, lvm_cmd)] + lvm_args, * args)
190 end_time = time.time()
192 if (end_time - start_time > MAX_OPERATION_DURATION):
193 util.SMlog("***** Long LVM call of '%s' took %s" % (lvm_cmd, (end_time - start_time)))
195 return stdout
198class LVInfo:
199 name = ""
200 size = 0
201 active = False
202 open = False
203 hidden = False
204 readonly = False
206 def __init__(self, name):
207 self.name = name
209 def toString(self):
210 return "%s, size=%d, active=%s, open=%s, hidden=%s, ro=%s" % \
211 (self.name, self.size, self.active, self.open, self.hidden, \
212 self.readonly)
215def _checkVG(vgname):
216 try:
217 cmd_lvm([CMD_VGS, "--readonly", vgname])
218 return True
219 except:
220 return False
223def _checkPV(pvname):
224 try:
225 cmd_lvm([CMD_PVS, pvname])
226 return True
227 except:
228 return False
231def _checkLV(path):
232 try:
233 cmd_lvm([CMD_LVDISPLAY, path])
234 return True
235 except:
236 return False
239def _getLVsize(path):
240 try:
241 lines = cmd_lvm([CMD_LVDISPLAY, "-c", path]).split(':')
242 return int(lines[6]) * 512
243 except:
244 raise xs_errors.XenError('VDIUnavailable', \
245 opterr='no such VDI %s' % path)
248def _getVGstats(vgname):
249 try:
250 text = cmd_lvm([CMD_VGS, "--noheadings", "--nosuffix",
251 "--units", "b", vgname],
252 pread_func=util.pread).split()
253 size = int(text[5])
254 freespace = int(text[6])
255 utilisation = size - freespace
256 stats = {}
257 stats['physical_size'] = size
258 stats['physical_utilisation'] = utilisation
259 stats['freespace'] = freespace
260 return stats
261 except util.CommandException as inst:
262 raise xs_errors.XenError('VDILoad', \
263 opterr='rvgstats failed error is %d' % inst.code)
264 except ValueError:
265 raise xs_errors.XenError('VDILoad', opterr='rvgstats failed')
268def _getPVstats(dev):
269 try:
270 text = cmd_lvm([CMD_PVS, "--noheadings", "--nosuffix",
271 "--units", "b", dev],
272 pread_func=util.pread).split()
273 size = int(text[4])
274 freespace = int(text[5])
275 utilisation = size - freespace
276 stats = {}
277 stats['physical_size'] = size
278 stats['physical_utilisation'] = utilisation
279 stats['freespace'] = freespace
280 return stats
281 except util.CommandException as inst:
282 raise xs_errors.XenError('VDILoad', \
283 opterr='pvstats failed error is %d' % inst.code)
284 except ValueError:
285 raise xs_errors.XenError('VDILoad', opterr='rvgstats failed')
288# Retrieves the UUID of the SR that corresponds to the specified Physical
289# Volume (pvname). Each element in prefix_list is checked whether it is a
290# prefix of Volume Groups that correspond to the specified PV. If so, the
291# prefix is stripped from the matched VG name and the remainder is returned
292# (effectively the SR UUID). If no match if found, the empty string is
293# returned.
294# E.g.
295# PV VG Fmt Attr PSize PFree
296# /dev/sda4 VG_XenStorage-some-hex-value lvm2 a- 224.74G 223.73G
297# will return "some-hex-value".
298def _get_sr_uuid(pvname, prefix_list):
299 try:
300 return match_VG(cmd_lvm([CMD_PVS, "--noheadings",
301 "-o", "vg_name", pvname]), prefix_list)
302 except:
303 return ""
306# Retrieves the names of the Physical Volumes which are used by the specified
307# Volume Group
308# e.g.
309# PV VG Fmt Attr PSize PFree
310# /dev/sda4 VG_XenStorage-some-hex-value lvm2 a- 224.74G 223.73G
311# will return "/dev/sda4" when given the argument "VG_XenStorage-some-hex-value".
312def get_pv_for_vg(vgname):
313 try:
314 result = cmd_lvm([CMD_PVS, "--noheadings",
315 '-S', 'vg_name=%s' % vgname, '-o', 'name'])
316 return [x.strip() for x in result.splitlines()]
317 except util.CommandException:
318 return []
321# Tries to match any prefix contained in prefix_list in s. If matched, the
322# remainder string is returned, else the empty string is returned. E.g. if s is
323# "VG_XenStorage-some-hex-value" and prefix_list contains "VG_XenStorage-",
324# "some-hex-value" is returned.
325#
326# TODO Items in prefix_list are expected to be found at the beginning of the
327# target string, though if any of them is found inside it a match will be
328# produced. This could be remedied by making the regular expression more
329# specific.
330def match_VG(s, prefix_list):
331 for val in prefix_list:
332 regex = re.compile(val)
333 if regex.search(s, 0):
334 return s.split(val)[1]
335 return ""
338# Retrieves the devices an SR is composed of. A dictionary is returned, indexed
339# by the SR UUID, where each SR UUID is mapped to a comma-separated list of
340# devices. Exceptions are ignored.
341def scan_srlist(prefix, root):
342 VGs = {}
343 for dev in root.split(','):
344 try:
345 sr_uuid = _get_sr_uuid(dev, [prefix]).strip(' \n')
346 if len(sr_uuid):
347 if sr_uuid in VGs:
348 VGs[sr_uuid] += ",%s" % dev
349 else:
350 VGs[sr_uuid] = dev
351 except Exception as e:
352 util.logException("exception (ignored): %s" % e)
353 continue
354 return VGs
357# Converts an SR list to an XML document with the following structure:
358# <SRlist>
359# <SR>
360# <UUID>...</UUID>
361# <Devlist>...</Devlist>
362# <size>...</size>
363# <!-- If includeMetadata is set to True, the following additional nodes
364# are supplied. -->
365# <name_label>...</name_label>
366# <name_description>...</name_description>
367# <pool_metadata_detected>...</pool_metadata_detected>
368# </SR>
369#
370# <SR>...</SR>
371# </SRlist>
372#
373# Arguments:
374# VGs: a dictionary containing the SR UUID to device list mappings
375# prefix: the prefix that if prefixes the SR UUID the VG is produced
376# includeMetadata (optional): include additional information
377def srlist_toxml(VGs, prefix, includeMetadata=False):
378 dom = xml.dom.minidom.Document()
379 element = dom.createElement("SRlist")
380 dom.appendChild(element)
382 for val in VGs:
383 entry = dom.createElement('SR')
384 element.appendChild(entry)
386 subentry = dom.createElement("UUID")
387 entry.appendChild(subentry)
388 textnode = dom.createTextNode(val)
389 subentry.appendChild(textnode)
391 subentry = dom.createElement("Devlist")
392 entry.appendChild(subentry)
393 textnode = dom.createTextNode(VGs[val])
394 subentry.appendChild(textnode)
396 subentry = dom.createElement("size")
397 entry.appendChild(subentry)
398 size = str(_getVGstats(prefix + val)['physical_size'])
399 textnode = dom.createTextNode(size)
400 subentry.appendChild(textnode)
402 if includeMetadata:
403 metadataVDI = None
405 # add SR name_label
406 mdpath = os.path.join(VG_LOCATION, VG_PREFIX + val)
407 mdpath = os.path.join(mdpath, MDVOLUME_NAME)
408 mgtVolActivated = False
409 try:
410 if not os.path.exists(mdpath):
411 # probe happens out of band with attach so this volume
412 # may not have been activated at this point
413 lvmCache = lvmcache.LVMCache(VG_PREFIX + val)
414 lvmCache.activateNoRefcount(MDVOLUME_NAME)
415 mgtVolActivated = True
417 sr_metadata = \
418 srmetadata.LVMMetadataHandler(mdpath, \
419 False).getMetadata()[0]
420 subentry = dom.createElement("name_label")
421 entry.appendChild(subentry)
422 textnode = dom.createTextNode(sr_metadata[srmetadata.NAME_LABEL_TAG])
423 subentry.appendChild(textnode)
425 # add SR description
426 subentry = dom.createElement("name_description")
427 entry.appendChild(subentry)
428 textnode = dom.createTextNode(sr_metadata[srmetadata.NAME_DESCRIPTION_TAG])
429 subentry.appendChild(textnode)
431 # add metadata VDI UUID
432 metadataVDI = srmetadata.LVMMetadataHandler(mdpath, \
433 False).findMetadataVDI()
434 subentry = dom.createElement("pool_metadata_detected")
435 entry.appendChild(subentry)
436 if metadataVDI is not None:
437 subentry.appendChild(dom.createTextNode("true"))
438 else:
439 subentry.appendChild(dom.createTextNode("false"))
440 finally:
441 if mgtVolActivated:
442 # deactivate only if we activated it
443 lvmCache.deactivateNoRefcount(MDVOLUME_NAME)
445 return dom.toprettyxml()
448def _openExclusive(dev, retry):
449 try:
450 return os.open("%s" % dev, os.O_RDWR | os.O_EXCL)
451 except OSError as ose:
452 opened_by = ''
453 if ose.errno == 16:
454 if retry:
455 util.SMlog('Device %s is busy, settle and one shot retry' %
456 dev)
457 util.pread2(['/usr/sbin/udevadm', 'settle'])
458 return _openExclusive(dev, False)
459 else:
460 util.SMlog('Device %s is busy after retry' % dev)
462 util.SMlog('Opening device %s failed with %d' % (dev, ose.errno))
463 raise xs_errors.XenError(
464 'SRInUse', opterr=('Device %s in use, please check your existing '
465 + 'SRs for an instance of this device') % dev)
468def createVG(root, vgname):
469 systemroot = util.getrootdev()
470 rootdev = root.split(',')[0]
472 # Create PVs for each device
473 for dev in root.split(','):
474 if dev in [systemroot, '%s1' % systemroot, '%s2' % systemroot]:
475 raise xs_errors.XenError('Rootdev', \
476 opterr=('Device %s contains core system files, ' \
477 + 'please use another device') % dev)
478 if not os.path.exists(dev):
479 raise xs_errors.XenError('InvalidDev', \
480 opterr=('Device %s does not exist') % dev)
482 f = _openExclusive(dev, True)
483 os.close(f)
485 # Wipe any fs signature
486 try:
487 util.wipefs(dev)
488 except util.CommandException as inst:
489 raise xs_errors.XenError('WipefsFailure', opterr='device %s' % dev) # from inst
491 if not (dev == rootdev):
492 try:
493 cmd_lvm([CMD_PVCREATE, "-ff", "-y", "--metadatasize", "10M", dev])
494 except util.CommandException as inst:
495 raise xs_errors.XenError('LVMPartCreate',
496 opterr='error is %d' % inst.code)
498 # Create VG on first device
499 try:
500 cmd_lvm([CMD_VGCREATE, "--metadatasize", "10M", vgname, rootdev])
501 except:
502 raise xs_errors.XenError('LVMGroupCreate')
504 # Then add any additional devs into the VG
505 for dev in root.split(',')[1:]:
506 try:
507 cmd_lvm([CMD_VGEXTEND, vgname, dev])
508 except util.CommandException as inst:
509 # One of the PV args failed, delete SR
510 try:
511 cmd_lvm([CMD_VGREMOVE, vgname])
512 except:
513 pass
514 raise xs_errors.XenError('LVMGroupCreate')
516 try:
517 cmd_lvm([CMD_VGCHANGE, "-an", vgname])
518 except util.CommandException as inst:
519 raise xs_errors.XenError('LVMUnMount', opterr='errno is %d' % inst.code)
521 # End block
523def getPVsInVG(vgname):
524 # Get PVs in a specific VG
525 pvs_ret = cmd_lvm([CMD_PVS, '--separator', ' ', '--noheadings', '-o', 'pv_name,vg_name'])
527 # Parse each line to extract PV and VG information
528 # No need to handle exceptions here, return empty list if any error
529 pvs_in_vg = []
530 lines = pvs_ret.strip().split('\n')
531 for line in lines:
532 # To avoid invalid return format
533 parts = line.split()
534 if len(parts) != 2:
535 util.SMlog("Warning: Invalid or empty line in pvs output: %s" % line)
536 continue
537 pv, vg = parts
538 if vg == vgname:
539 pvs_in_vg.append(pv)
541 util.SMlog("PVs in VG %s: %s" % (vgname, pvs_in_vg))
542 return pvs_in_vg
544def removeVG(root, vgname):
545 # Check PVs match VG
546 try:
547 for dev in root.split(','):
548 txt = cmd_lvm([CMD_PVS, dev])
549 if txt.find(vgname) == -1:
550 raise xs_errors.XenError('LVMNoVolume', \
551 opterr='volume is %s' % vgname)
552 except util.CommandException as inst:
553 raise xs_errors.XenError('PVSfailed', \
554 opterr='error is %d' % inst.code)
556 try:
557 # Get PVs in VG before removing the VG
558 devs_in_vg = getPVsInVG(vgname)
559 cmd_lvm([CMD_VGREMOVE, vgname])
561 for dev in devs_in_vg:
562 cmd_lvm([CMD_PVREMOVE, dev])
563 except util.CommandException as inst:
564 raise xs_errors.XenError('LVMDelete', \
565 opterr='errno is %d' % inst.code)
568def resizePV(dev):
569 try:
570 cmd_lvm([CMD_PVRESIZE, dev])
571 except util.CommandException as inst:
572 util.SMlog("Failed to grow the PV, non-fatal")
575def setActiveVG(path, active, config=None):
576 "activate or deactivate VG 'path'"
577 val = "n"
578 if active:
579 val = "y"
580 cmd = [CMD_VGCHANGE, "-a" + val, path]
581 if config:
582 cmd.append("--config")
583 cmd.append(config)
584 cmd_lvm(cmd)
587def checkPVScsiIds(vgname, SCSIid):
588 # Get all the PVs for the specified vgName even if not active
589 cmd = [CMD_PVS, '-a', '--select', f'vgname={vgname}', '--no-headings']
590 text = cmd_lvm(cmd)
591 pv_paths = [x.split()[0] for x in text.splitlines()]
592 for pv_path in pv_paths:
593 pv_scsi_id = scsiutil.getSCSIid(pv_path)
594 if pv_scsi_id != SCSIid:
595 raise xs_errors.XenError(
596 'PVMultiIDs',
597 opterr=f'Found PVs {",".join(pv_paths)} and unexpected '
598 f'SCSI ID {pv_scsi_id}, expected {SCSIid}')
600@lvmretry
601def create(name, size, vgname, tag=None, size_in_percentage=None):
602 if size_in_percentage:
603 cmd = [CMD_LVCREATE, "-n", name, "-l", size_in_percentage, vgname]
604 else:
605 size_mb = size // (1024 * 1024)
606 cmd = [CMD_LVCREATE, "-n", name, "-L", str(size_mb), vgname]
607 if tag:
608 cmd.extend(["--addtag", tag])
610 cmd.extend(['-W', 'n'])
611 cmd_lvm(cmd)
614def remove(path, config_param=None):
615 # see deactivateNoRefcount()
616 for i in range(LVM_FAIL_RETRIES): 616 ↛ 624line 616 didn't jump to line 624, because the loop on line 616 didn't complete
617 try:
618 _remove(path, config_param)
619 break
620 except util.CommandException as e:
621 if i >= LVM_FAIL_RETRIES - 1:
622 raise
623 util.SMlog("*** lvremove failed on attempt #%d" % i)
624 _lvmBugCleanup(path)
627@lvmretry
628def _remove(path, config_param=None):
629 CONFIG_TAG = "--config"
630 cmd = [CMD_LVREMOVE, "-f", path]
631 if config_param:
632 cmd.extend([CONFIG_TAG, "devices{" + config_param + "}"])
633 ret = cmd_lvm(cmd)
636@lvmretry
637def rename(path, newName):
638 cmd_lvm([CMD_LVRENAME, path, newName], pread_func=util.pread)
641@lvmretry
642def setReadonly(path, readonly):
643 val = "r"
644 if not readonly:
645 val += "w"
646 ret = cmd_lvm([CMD_LVCHANGE, path, "-p", val], pread_func=util.pread)
649def exists(path):
650 (rc, stdout, stderr) = cmd_lvm([CMD_LVS, "--noheadings", path], pread_func=util.doexec)
651 return rc == 0
654@lvmretry
655def setSize(path, size, confirm):
656 sizeMB = size // (1024 * 1024)
657 if confirm:
658 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], util.pread3, "y\n")
659 else:
660 cmd_lvm([CMD_LVRESIZE, "-L", str(sizeMB), path], pread_func=util.pread)
663@lvmretry
664def setHidden(path, hidden=True):
665 opt = "--addtag"
666 if not hidden:
667 opt = "--deltag"
668 cmd_lvm([CMD_LVCHANGE, opt, LV_TAG_HIDDEN, path])
671@lvmretry
672def _activate(path):
673 cmd = [CMD_LVCHANGE, "-ay", path]
674 cmd_lvm(cmd)
675 if not _checkActive(path):
676 raise util.CommandException(-1, str(cmd), "LV not activated")
679def activateNoRefcount(path, refresh):
680 _activate(path)
681 if refresh: 681 ↛ 683line 681 didn't jump to line 683, because the condition on line 681 was never true
682 # Override slave mode lvm.conf for this command
683 os.environ['LVM_SYSTEM_DIR'] = MASTER_LVM_CONF
684 text = cmd_lvm([CMD_LVCHANGE, "--refresh", path])
685 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
686 cmd = [CMD_DMSETUP, "table", mapperDevice]
687 with Fairlock("devicemapper"):
688 ret = util.pread(cmd)
689 util.SMlog("DM table for %s: %s" % (path, ret.strip()))
690 # Restore slave mode lvm.conf
691 os.environ['LVM_SYSTEM_DIR'] = DEF_LVM_CONF
694def deactivateNoRefcount(path):
695 # LVM has a bug where if an "lvs" command happens to run at the same time
696 # as "lvchange -an", it might hold the device in use and cause "lvchange
697 # -an" to fail. Thus, we need to retry if "lvchange -an" fails. Worse yet,
698 # the race could lead to "lvchange -an" starting to deactivate (removing
699 # the symlink), failing to "dmsetup remove" the device, and still returning
700 # success. Thus, we need to check for the device mapper file existence if
701 # "lvchange -an" returns success.
702 for i in range(LVM_FAIL_RETRIES): 702 ↛ 710line 702 didn't jump to line 710, because the loop on line 702 didn't complete
703 try:
704 _deactivate(path)
705 break
706 except util.CommandException:
707 if i >= LVM_FAIL_RETRIES - 1:
708 raise
709 util.SMlog("*** lvchange -an failed on attempt #%d" % i)
710 _lvmBugCleanup(path)
713@lvmretry
714def _deactivate(path):
715 text = cmd_lvm([CMD_LVCHANGE, "-an", path])
718def _checkActive(path):
719 if util.pathexists(path):
720 return True
722 util.SMlog("_checkActive: %s does not exist!" % path)
723 symlinkExists = os.path.lexists(path)
724 util.SMlog("_checkActive: symlink exists: %s" % symlinkExists)
726 mapperDeviceExists = False
727 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
728 cmd = [CMD_DMSETUP, "status", mapperDevice]
729 try:
730 with Fairlock("devicemapper"):
731 ret = util.pread2(cmd)
732 mapperDeviceExists = True
733 util.SMlog("_checkActive: %s: %s" % (mapperDevice, ret))
734 except util.CommandException:
735 util.SMlog("_checkActive: device %s does not exist" % mapperDevice)
737 mapperPath = "/dev/mapper/" + mapperDevice
738 mapperPathExists = util.pathexists(mapperPath)
739 util.SMlog("_checkActive: path %s exists: %s" % \
740 (mapperPath, mapperPathExists))
742 if mapperDeviceExists and mapperPathExists and not symlinkExists: 742 ↛ 744line 742 didn't jump to line 744, because the condition on line 742 was never true
743 # we can fix this situation manually here
744 try:
745 util.SMlog("_checkActive: attempt to create the symlink manually.")
746 os.symlink(mapperPath, path)
747 except OSError as e:
748 util.SMlog("ERROR: failed to symlink!")
749 if e.errno != errno.EEXIST:
750 raise
751 if util.pathexists(path):
752 util.SMlog("_checkActive: created the symlink manually")
753 return True
755 return False
758def _lvmBugCleanup(path):
759 # the device should not exist at this point. If it does, this was an LVM
760 # bug, and we manually clean up after LVM here
761 mapperDevice = path[5:].replace("-", "--").replace("/", "-")
762 mapperPath = "/dev/mapper/" + mapperDevice
764 nodeExists = False
765 cmd_st = [CMD_DMSETUP, "status", mapperDevice]
766 cmd_rm = [CMD_DMSETUP, "remove", mapperDevice]
767 cmd_rf = [CMD_DMSETUP, "remove", mapperDevice, "--force"]
769 try:
770 with Fairlock("devicemapper"):
771 util.pread(cmd_st, expect_rc=1)
772 except util.CommandException as e:
773 if e.code == 0: 773 ↛ 776line 773 didn't jump to line 776, because the condition on line 773 was never false
774 nodeExists = True
776 if not util.pathexists(mapperPath) and not nodeExists:
777 return
779 util.SMlog("_lvmBugCleanup: seeing dm file %s" % mapperPath)
781 # destroy the dm device
782 if nodeExists: 782 ↛ 809line 782 didn't jump to line 809, because the condition on line 782 was never false
783 util.SMlog("_lvmBugCleanup: removing dm device %s" % mapperDevice)
784 for i in range(LVM_FAIL_RETRIES): 784 ↛ 809line 784 didn't jump to line 809, because the loop on line 784 didn't complete
785 try:
786 with Fairlock("devicemapper"):
787 util.pread2(cmd_rm)
788 break
789 except util.CommandException as e:
790 if i < LVM_FAIL_RETRIES - 1:
791 util.SMlog("Failed on try %d, retrying" % i)
792 try:
793 with Fairlock("devicemapper"):
794 util.pread(cmd_st, expect_rc=1)
795 util.SMlog("_lvmBugCleanup: dm device {}"
796 " removed".format(mapperDevice)
797 )
798 break
799 except:
800 cmd_rm = cmd_rf
801 time.sleep(1)
802 else:
803 # make sure the symlink is still there for consistency
804 if not os.path.lexists(path): 804 ↛ 807line 804 didn't jump to line 807, because the condition on line 804 was never false
805 os.symlink(mapperPath, path)
806 util.SMlog("_lvmBugCleanup: restored symlink %s" % path)
807 raise e
809 if util.pathexists(mapperPath):
810 os.unlink(mapperPath)
811 util.SMlog("_lvmBugCleanup: deleted devmapper file %s" % mapperPath)
813 # delete the symlink
814 if os.path.lexists(path):
815 os.unlink(path)
816 util.SMlog("_lvmBugCleanup: deleted symlink %s" % path)
819# mdpath is of format /dev/VG-SR-UUID/MGT
820# or in other words /VG_LOCATION/VG_PREFIXSR-UUID/MDVOLUME_NAME
821def ensurePathExists(mdpath):
822 if not os.path.exists(mdpath):
823 vgname = mdpath.split('/')[2]
824 lvmCache = lvmcache.LVMCache(vgname)
825 lvmCache.activateNoRefcount(MDVOLUME_NAME)
828def removeDevMapperEntry(path, strict=True):
829 try:
830 # remove devmapper entry using dmsetup
831 cmd = [CMD_DMSETUP, "remove", path]
832 cmd_lvm(cmd)
833 return True
834 except Exception as e:
835 if not strict:
836 cmd = [CMD_DMSETUP, "status", path]
837 try:
838 with Fairlock("devicemapper"):
839 util.pread(cmd, expect_rc=1)
840 return True
841 except:
842 pass # Continuining will fail and log the right way
843 ret = util.pread2(["lsof", path])
844 util.SMlog("removeDevMapperEntry: dmsetup remove failed for file %s " \
845 "with error %s, and lsof ret is %s." % (path, str(e), ret))
846 return False