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